pg.ddx.io pgsql-hackers@postgresql.org mailing list archive
help / color / mirror / Atom feedGSoC 2017: Foreign Key Arrays
200+ messages / 19 participants
[nested] [flat]
* GSoC 2017: Foreign Key Arrays
@ 2017-05-22 23:51 Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-05-22 23:51 UTC (permalink / raw)
To: pgsql-hackers; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Alvaro Herrera <alvaro.herrera@2ndquadrant.com>; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
Dear PostgreSQL hacker community,
I am working on Foreign Key Arrays as part of the Google Summer of Code
2017.
I will be logging my progress on this thread as I progress, twice a week
(Mondays and Fridays), so anyone who is willing to comment, please do.
*The Problem*
Foreign Key Arrays were introduced by Marco Nenciarini[1], however, the
proposed patch had some performance issues. Let's assume we have two
tables, table B has a foreign key array that references table A, any change
in table A (INSERT, UPDATE, DELETE) would trigger a referential
integrity check on table B. The current implementation uses sequential
scans to accomplish this. This limits the size of tables using Foreign Key
Arrays to ~100 records which is not practical in real life applications.
*The Proposed Solution*
Ultimately, as proposed by Tom Lane[2], we would like to replace the
sequential scan with a GIN-indexed scan which would greatly enhance the
performance.
To achieve this, introducing a number of new operators is required.
However, for the scope of the project, we will focus on the most basic case
where the Primary Keys are of pseudo-type anyelement and the Foreign Keys
are of pseudo-type anyarray, thus the main operator of concern will be
@>(anyarray,anyelement).
*Progress So Far*
The actual coding begins on 30th of May, till then I will use my time to
research, to settle the technical details of my plan.
- Collected resources about GIN indexing
- http://www.sigaev.ru/gin/README.txt
- https://wiki.postgresql.org/wiki/GIN_generalization
- src\backend\access\gin\README in the repo
- Cloned the git repo found @ https://github.com/postgres/postgres and
identified the main two files I will be concerned with. (I know I may need
to edit other files but these seem to where I will spend most of my summer)
- src/backend/commands/tablecmds.c
- src/backend/utils/ri_triggers.c
*I am yet to identify the files concerned with the GIN opclass. <-- if
anyone can help with this*
- read a little about op classes
- https://www.postgresql.org/docs/9.5/static/indexes-opclass.html
- explored the existing op classes in Postgres
*Next Step*
I still don't have a solid grasp of how I am going to approach creating an
operator, so I would like to experiment till the next report on creating a
very simple operator.
*I have attached the original proposal here.*
[1]
https://www.postgresql.org/message-id/1343842863.5162.4.camel%40greygoo.devise-it.lan
[2] https://www.postgresql.org/message-id/28389.1351094795@sss.pgh.pa.us
Best Regards,
Mark Rofail
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
GSoCproposal2017.pdf (application/pdf, 155.4K) download
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-05-24 15:12 ` Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Robert Haas @ 2017-05-24 15:12 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: pgsql-hackers; Alexander Korotkov <aekorotkov@gmail.com>; Alvaro Herrera <alvaro.herrera@2ndquadrant.com>; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Mon, May 22, 2017 at 7:51 PM, Mark Rofail <markm.rofail@gmail.com> wrote:
> Cloned the git repo found @ https://github.com/postgres/postgres and
> identified the main two files I will be concerned with. (I know I may need
> to edit other files but these seem to where I will spend most of my summer)
>
> src/backend/commands/tablecmds.c
> src/backend/utils/ri_triggers.c
>
> I am yet to identify the files concerned with the GIN opclass. <-- if anyone
> can help with this
There's not only one GIN opclass. You can get a list like this:
select oid, * from pg_opclass where opcmethod = 2742;
Actually, you probably want to look for GIN opfamilies:
rhaas=# select oid, * from pg_opfamily where opfmethod = 2742;
oid | opfmethod | opfname | opfnamespace | opfowner
------+-----------+----------------+--------------+----------
2745 | 2742 | array_ops | 11 | 10
3659 | 2742 | tsvector_ops | 11 | 10
4036 | 2742 | jsonb_ops | 11 | 10
4037 | 2742 | jsonb_path_ops | 11 | 10
(4 rows)
To see which SQL functions are used to implement a particular
opfamily, use the OID from the previous step in a query like this:
rhaas=# select prosrc from pg_amop, pg_operator, pg_proc where
amopfamily = 2745 and amopopr = pg_operator.oid and oprcode =
pg_proc.oid;
prosrc
----------------
array_eq
arrayoverlap
arraycontains
arraycontained
(4 rows)
Then, you can look for those in the source tree. You can also search
for the associated support functions, e.g.:
rhaas=# select distinct amprocnum, prosrc from pg_amproc, pg_proc
where amprocfamily = 2745 and amproc = pg_proc.oid order by 1, 2;
amprocnum | prosrc
-----------+-----------------------
1 | bitcmp
1 | bpcharcmp
1 | btabstimecmp
1 | btboolcmp
1 | btcharcmp
1 | btfloat4cmp
1 | btfloat8cmp
1 | btint2cmp
1 | btint4cmp
1 | btint8cmp
1 | btnamecmp
1 | btoidcmp
1 | btoidvectorcmp
1 | btreltimecmp
1 | bttextcmp
1 | bttintervalcmp
1 | byteacmp
1 | cash_cmp
1 | date_cmp
1 | interval_cmp
1 | macaddr_cmp
1 | network_cmp
1 | numeric_cmp
1 | time_cmp
1 | timestamp_cmp
1 | timetz_cmp
2 | ginarrayextract
3 | ginqueryarrayextract
4 | ginarrayconsistent
6 | ginarraytriconsistent
(30 rows)
You might want to read https://www.postgresql.org/docs/devel/static/xindex.html
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
@ 2017-05-29 23:18 ` Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-05-29 23:18 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: pgsql-hackers; Alexander Korotkov <aekorotkov@gmail.com>; Alvaro Herrera <alvaro.herrera@2ndquadrant.com>; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
>
> rhaas=# select oid, * from pg_opfamily where opfmethod = 2742;
> oid | opfmethod | opfname | opfnamespace | opfowner
> ------+-----------+----------------+--------------+----------
> 2745 | 2742 | array_ops | 11 | 10
> 3659 | 2742 | tsvector_ops | 11 | 10
> 4036 | 2742 | jsonb_ops | 11 | 10
> 4037 | 2742 | jsonb_path_ops | 11 | 10
> (4 rows)
I am particulary intrested in array_ops but I have failed in locating the
code behind it. Where is it reflected in the source code
Best Regards,
Mark Rofail
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-05-30 13:02 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Alexander Korotkov @ 2017-05-30 13:02 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; Alvaro Herrera <alvaro.herrera@2ndquadrant.com>; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
Hi, Mark!
On Tue, May 30, 2017 at 2:18 AM, Mark Rofail <markm.rofail@gmail.com> wrote:
> rhaas=# select oid, * from pg_opfamily where opfmethod = 2742;
>> oid | opfmethod | opfname | opfnamespace | opfowner
>> ------+-----------+----------------+--------------+----------
>> 2745 | 2742 | array_ops | 11 | 10
>> 3659 | 2742 | tsvector_ops | 11 | 10
>> 4036 | 2742 | jsonb_ops | 11 | 10
>> 4037 | 2742 | jsonb_path_ops | 11 | 10
>> (4 rows)
>
> I am particulary intrested in array_ops but I have failed in locating the
> code behind it. Where is it reflected in the source code
>
Let's look what particular opclass is consisting of. Besides records in
pg_opfamily, it also contains records in pg_opclass, pg_amproc and pg_amop.
=# select * from pg_opclass where opcfamily = 2745;
opcmethod | opcname | opcnamespace | opcowner | opcfamily | opcintype |
opcdefault | opckeytype
-----------+-----------+--------------+----------+-----------+-----------+------------+------------
2742 | array_ops | 11 | 10 | 2745 | 2277 |
t | 2283
(1 row)
=# select * from pg_amproc where amprocfamily = 2745;
amprocfamily | amproclefttype | amprocrighttype | amprocnum |
amproc
--------------+----------------+-----------------+-----------+----------------------------
2745 | 2277 | 2277 | 2 |
pg_catalog.ginarrayextract
2745 | 2277 | 2277 | 3 |
ginqueryarrayextract
2745 | 2277 | 2277 | 4 |
ginarrayconsistent
2745 | 2277 | 2277 | 6 |
ginarraytriconsistent
(4 rows)
=# select * from pg_amop where amopfamily = 2745;
amopfamily | amoplefttype | amoprighttype | amopstrategy | amoppurpose |
amopopr | amopmethod | amopsortfamily
------------+--------------+---------------+--------------+-------------+---------+------------+----------------
2745 | 2277 | 2277 | 1 | s |
2750 | 2742 | 0
2745 | 2277 | 2277 | 2 | s |
2751 | 2742 | 0
2745 | 2277 | 2277 | 3 | s |
2752 | 2742 | 0
2745 | 2277 | 2277 | 4 | s |
1070 | 2742 | 0
(4 rows)
These records of system catalog are defined in special headers the source
code:
src/include/catalog/pg_amop.h
src/include/catalog/pg_amproc.h
src/include/catalog/pg_opclass.h
src/include/catalog/pg_opfamily.h
These records are written to system catalog during bootstrap process (see
src/backend/catalog/README).
As you can see pg_amproc records refer some procedures. Those procedures
are actually the majority of source code behind of opclass. Those
procedures are defined in src/backend/access/gin/ginarrayproc.c.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
@ 2017-06-02 22:28 ` Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-06-02 22:28 UTC (permalink / raw)
To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; Alvaro Herrera <alvaro.herrera@2ndquadrant.com>; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
>
> src/include/catalog/pg_amop.h
> src/include/catalog/pg_amproc.h
> src/include/catalog/pg_opclass.h
> src/include/catalog/pg_opfamily.h
>
Thanks to Alexander's reply I have been able to jump from catalog table to
table till I found the function I was looking for.
My goal is to add a new operator (@>(anyarray,anyelement)) to the (array_ops)
op class.
I am going to post the steps I took to locate the procedure, the following
is the trail of tables I followed.
pg_opfamily
pg_opfamily defines operator families.
Link to docs
<https://www.postgresql.org/docs/devel/static/catalog-pg-opfamily.html;
{
opfmethod; /* index access method opfamily is for */
opfname; /* name of this opfamily */
opfnamespace; /* namespace of this opfamily */
opfowner; /* opfamily owner */
}
gin=# select oid, * from pg_opfamily where opfmethod = 2742;
oid | opfmethod | opfname | opfnamespace | opfowner
------+-----------+----------------+--------------+----------
2745 | 2742 | array_ops | 11 | 10
3659 | 2742 | tsvector_ops | 11 | 10
4036 | 2742 | jsonb_ops | 11 | 10
4037 | 2742 | jsonb_path_ops | 11 | 10
(4 rows)
as this table defines operator families I won't need to modify them.
pg_opclass
pg_opclass defines index access method operator classes.
Link to docs
<https://www.postgresql.org/docs/current/static/catalog-pg-opclass.html;
{
opcmethod; /* index access method opclass is for */
opcname; /* name of this opclass */
opcnamespace; /* namespace of this opclass */
opcowner; /* opclass owner */
opcfamily; /* containing operator family */
opcintype; /* type of data indexed by opclass */
opcdefault; /* T if opclass is default for opcintype
opckeytype; /* type of data in index, or InvalidOid */
}
gin=# select * from pg_opclass where opcfamily = 2745;
opcmethod | opcname | opcnamespace | opcowner | opcfamily | opcintype |
opcdefault | opckeytype
-----------+-----------+--------------+----------+----------
-+-----------+------------+------------
2742 | array_ops | 11 | 10 | 2745 | 2277 | t
| 2283
(1 row)
as this table defines operator classes I won't need to modify them.
this led me to pg_amproc
pg_amproc
pg_amproc stores information about support procedures associated with
access method operator families.
Link to docs
<https://www.postgresql.org/docs/devel/static/catalog-pg-amproc.html;
{
amprocfamily; /* the index opfamily this entry is for */
amproclefttype; /* procedure's left input data type */
amprocrighttype; /* procedure's right input data type */
amprocnum[1]; /* support procedure index */
amproc; /* OID of the proc */
}
gin=# select * from pg_amproc where amprocfamily = 2745;
amprocfamily | amproclefttype | amprocrighttype | amprocnum |
amproc
------------------+--------------------+--------------------
-+---------------+----------------------------
2745 | 2277 | 2277
| 2 | pg_catalog.ginarrayextract
2745 | 2277 | 2277
| 3 | ginqueryarrayextract
2745 | 2277 | 2277
| 4 | ginarrayconsistent
2745 | 2277 | 2277
| 6 | ginarraytriconsistent
(4 rows)
[1]amprocnum refers to this table
<https://www.postgresql.org/docs/10/static/xindex.html#xindex-gin-support-table;
as this table defines support procedures I won't need to modify them.
this led me to pg_amop
pg_amop
pg_amop stores information about operators associated with access method
operator families.
Link to docs
<https://www.postgresql.org/docs/devel/static/catalog-pg-amop.html;
{
amopfamily; /* the index opfamily this entry is for */
amoplefttype; /* operator's left input data type */
amoprighttype; /* operator's right input data type */
amopstrategy; /* operator strategy number */
amoppurpose; /* is operator for 's'earch or 'o'rdering? */
amopopr; /* the operator's pg_operator OID */
amopmethod; /* the index access method this entry is for
amopsortfamily; /* ordering opfamily OID, or 0 if search op
}
=# select * from pg_amop where amopfamily = 2745;
amopfamily | amoplefttype | amoprighttype | amopstrategy | amoppurpose |
amopopr | amopmethod | amopsortfamily
------------+--------------+---------------+--------------+-
------------+---------+------------+----------------
2745 | 2277 | 2277 | 1 | s |
2750 | 2742 | 0
2745 | 2277 | 2277 | 2 | s |
2751 | 2742 | 0
2745 | 2277 | 2277 | 3 | s |
2752 | 2742 | 0
2745 | 2277 | 2277 | 4 | s |
1070 | 2742 | 0
(4 rows)
I will need to add a record of my new operator to this table by appending
this line to src/include/catalog/pg_amop.h
DATA(insert (2745 2277 2277 1 s 2750 2742 0 ));
This will result in the following entry
=# select * from pg_amop where amopfamily = 2745;
amopfamily | amoplefttype | amoprighttype | amopstrategy | amoppurpose |
amopopr | amopmethod | amopsortfamily
------------+--------------+---------------+--------------+-
------------+---------+------------+----------------
2745 | 2277 | 2283 | 1 | s |
2750 | 2742 | 0
this led me to pg_operator
pg_operator
pg_operator stores information about operators.
Link to docs
<https://www.postgresql.org/docs/devel/static/catalog-pg-operator.html;
{
oprname; /* name of operator */
oprnamespace; /* OID of namespace containing this oper */
oprowner; /* operator owner */
oprkind; /* 'l', 'r', or 'b' */
oprcanmerge; /* can be used in merge join? */
oprcanhash; /* can be used in hash join? */
oprleft; /* left arg type, or 0 if 'l' oprkind */
oprright; /* right arg type, or 0 if 'r' oprkind */
oprresult; /* result datatype */
oprcom; /* OID of commutator oper, or 0 if none */
oprnegate; /* OID of negator oper, or 0 if none */
oprcode; /* OID of underlying function */
oprrest; /* OID of restriction estimator, or 0 */
oprjoin; /* OID of join estimator, or 0 */
}
postgres=# select * from pg_operator where oid = 2751;
oprname | oprnamespace | oprowner | oprkind | oprcanmerge | oprcanhash |
oprleft | oprright | oprresult | oprcom | oprnegate | oprcode |
oprrest | oprjoin
---------+--------------+----------+---------+-------------+
------------+---------+----------+-----------+--------+-----
------+---------------+--------------+------------------
@> | 11 | 10 | b | f | f |
2277 | 2277 | 16 | 2752 | 0 | arraycontains |
arraycontsel | arraycontjoinsel
(1 row)
I will need to add a record of my new operator to this table by appending
this line to src/include/catalog/pg_operator.h
However, as this is dependent on the procedure I have yet to create there
are still uknown values
DATA(insert OID = <uniqueProcId> ( "@>" PGNSP PGUID b f f 2277 2283 16
2752 0 arraycontainselem ???? ???? ));
DESCR("contains");
#define OID_ARRAY_CONTAINS_OP <uniqueProcId>
This will lead to this entry
oprname | oprnamespace | oprowner | oprkind | oprcanmerge | oprcanhash |
oprleft | oprright | oprresult | oprcom | oprnegate | oprcode |
oprrest | oprjoin
---------+--------------+----------+---------+-------------+
------------+---------+----------+-----------+--------+-----
------+---------------+--------------+------------------
@> | 11 | 10 | b | f | f |
2277 | 2283 | 16 | 2752 | 0 | arraycontainselem |
???? | ????
(1 row)
this led me to pg_proc
pg_proc
pg_proc stores information about functions (or procedures)
Link to docs
<https://www.postgresql.org/docs/devel/static/catalog-pg-proc.html;
{
proname; /* procedure name */
pronamespace; /* OID of namespace containing this proc */
proowner; /* procedure owner */
prolang; /* OID of pg_language entry */
procost; /* estimated execution cost */
prorows; /* estimated # of rows out (if proretset) */
provariadic; /* element type of variadic array, or 0 */
protransform; /* transforms calls to it during planning */
proisagg; /* is it an aggregate? */
proiswindow; /* is it a window function? */
prosecdef; /* security definer */
proleakproof; /* is it a leak-proof function? */
proisstrict; /* strict with respect to NULLs? */
proretset; /* returns a set? */
provolatile; /* see PROVOLATILE_ categories below */
proparallel; /* see PROPARALLEL_ categories below */
pronargs; /* number of arguments */
pronargdefaults; /* number of arguments with defaults */
prorettype; /* OID of result type */
proargtypes; /* parameter types (excludes OUT params) */
proallargtypes[1]; /* all param types (NULL if IN only) */
proargmodes[1]; /* parameter modes (NULL if IN only) */
proargnames[1]; /* parameter names (NULL if no names) */
proargdefaults; /* list of expression trees for argument
protrftypes[1]; /* types for which to apply transforms */
prosrc; /* procedure source text */
probin; /* secondary procedure info (can be NULL) */
proconfig[1]; /* procedure-local GUC settings */
proacl[1]; /* access permissions */
}
postgres=# select * from pg_proc where oid = 2748;
proname | pronamespace | proowner | prolang | procost | prorows |
provariadic | protransform | proisagg | proiswindow | prosecdef |
proleakproof | proisstrict | proretset | provolatile | proparallel | pron
args | pronargdefaults | prorettype | proargtypes | proallargtypes |
proargmodes | proargnames | proargdefaults | protrftypes | prosrc |
probin | proconfig | proacl
---------------+--------------+----------+---------+--------
-+---------+-------------+--------------+----------+--------
-----+-----------+--------------+-------------+-----------+-
------------+-------------+-----
-----+-----------------+------------+-------------+---------
-------+-------------+-------------+----------------+-------
------+---------------+--------+-----------+--------
arraycontains | 11 | 10 | 12 | 1 | 0 |
0 | - | f | f | f | f
| t | f | i | s |
2 | 0 | 16 | 2277 2277 | |
| | | | arraycontains |
| |
(1 row)
I have yet to study this table thoroughly.
This finally led me to the arraycontains procedure in src/backend/utils/adt/
arrayfuncs.c
Datum
arraycontains(PG_FUNCTION_ARGS)
{
AnyArrayType *array1 = PG_GETARG_ANY_ARRAY(0);
AnyArrayType *array2 = PG_GETARG_ANY_ARRAY(1);
Oid collation = PG_GET_COLLATION();
bool result;
result = array_contain_compare(array2, array1, collation, true,
&fcinfo->flinfo->fn_extra);
/* Avoid leaking memory when handed toasted input. */
AARR_FREE_IF_COPY(array1, 0);
AARR_FREE_IF_COPY(array2, 1);
PG_RETURN_BOOL(result);
}
This corrosponds to the operator @<(anyarray, anyarray) which is the
generalised form of my proposed operator @<(anyarray, anyelement).
Studying the syntax will help me produce a function that follows the
postgres style rules.
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-06-10 22:35 ` Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-06-10 22:35 UTC (permalink / raw)
To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; Alvaro Herrera <alvaro.herrera@2ndquadrant.com>; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
• After finding the arraycontains function, I implemented
arraycontainselem that corresponds to the operator @<(anyarray,
anyelem)
◦ Please read the attached patch file to view my progress.
• In addition to src/backend/utils/adt/arrayfuncs.c where I
implemented arraycontainselem.
◦ I also edited pg_amop (src/include/catalog/pg_amop.h) since
it stores information about operators associated with access method
operator families.
+DATA(insert ( 2745 2277 2283 2 s 2753 2742 0 ));
{
2745: Oid amopfamily; (denotes gin array_ops)
277: Oid amoplefttype; (denotes anyaray)
2283: Oid amoprighttype; (denotes anyelem)
5: int16 amopstrategy; /* operator strategy number */ (denotes the new
startegy that is yet to be created)
's': char amoppurpose; (denotes 's' for search)
2753: Oid amopopr; (denotes the new operator Oid)
2742: Oid amopmethod;(denotes gin)
0: Oid amopsortfamily; (0 since search operator)
}
◦ And pg_operator (src/include/catalog/pg_operator.h) since it
stores information about operators.
+DATA(insert OID = 2753 ( "@>" PGNSP PGUID b f f 2277 2283 16 0 0
arraycontainselem 0 0 ));
{
"@>": NameData oprname; /* name of operator */
Oid oprnamespace; /* OID of namespace containing this oper */
Oid oprowner; /* operator owner */
'b': char oprkind; /* 'l', 'r', or 'b' */ (denotes infix)
'f': bool oprcanmerge; /* can be used in merge join? */
'f': bool oprcanhash; /* can be used in hash join? */
277: Oid oprleft; (denotes anyaray)
2283: Oid oprright; (denotes anyelem)
16: Oid oprresult; (denotes boolean)
0: Oid oprcom; /* OID of commutator oper, or 0 if none */ (needs to be
revisited)
0: Oid oprnegate; /* OID of negator oper, or 0 if none */ (needs to be
revisited)
arraycontainselem: regproc oprcode; /* OID of underlying function */
0: regproc oprrest; /* OID of restriction estimator, or 0 */
0: regproc oprjoin; /* OID of join estimator, or 0 */
}
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
elemOperatorV2.patch (text/x-patch, 3.6K) download
diff --git a/src/backend/access/gin/ginarrayproc.c b/src/backend/access/gin/ginarrayproc.c
index cc7435e030..14fedc8066 100644
--- a/src/backend/access/gin/ginarrayproc.c
+++ b/src/backend/access/gin/ginarrayproc.c
@@ -24,6 +24,7 @@
#define GinContainsStrategy 2
#define GinContainedStrategy 3
#define GinEqualStrategy 4
+#define GinContainsElemStrategy 5
/*
@@ -110,7 +111,8 @@ ginqueryarrayextract(PG_FUNCTION_ARGS)
case GinOverlapStrategy:
*searchMode = GIN_SEARCH_MODE_DEFAULT;
break;
+ case GinContainsElemStrategy:
case GinContainsStrategy:
if (nelems > 0)
*searchMode = GIN_SEARCH_MODE_DEFAULT;
else /* everything contains the empty set */
@@ -171,6 +173,7 @@ ginarrayconsistent(PG_FUNCTION_ARGS)
}
}
break;
+ case GinContainsElemStrategy:
case GinContainsStrategy:
/* result is not lossy */
*recheck = false;
@@ -258,7 +261,8 @@ ginarraytriconsistent(PG_FUNCTION_ARGS)
}
}
break;
- case GinContainsStrategy:
+ case GinContainsElemStrategy:
case GinContainsStrategy:
/* must have all elements in check[] true, and no nulls */
res = GIN_TRUE;
for (i = 0; i < nkeys; i++)
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index d9c8aa569c..e1ff6d33b5 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -4215,6 +4215,40 @@ arraycontains(PG_FUNCTION_ARGS)
}
Datum
+arraycontainselem(PG_FUNCTION_ARGS)
+{
+ Datum *elem = PG_GETARG_DATUM(0);
+ AnyArrayType *array1;
+ AnyArrayType *array2 = PG_GETARG_ANY_ARRAY(1);
+ Oid collation = PG_GET_COLLATION();
+ bool result;
+
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ int nelems;
+
+ /* we have one element */
+ nelems= 1;
+
+ /* get required info about the element type */
+ get_typlenbyvalalign(ARR_ELEMTYPE(array),
+ &typlen, &typbyval, &typalign);
+
+ /* now build the array */
+ array1 = construct_array(&elem, nelems,collation, &typlen, &typbyval, &typalign);
+
+ result = array_contain_compare(array2, array1, collation, true,
+ &fcinfo->flinfo->fn_extra);
+
+ /* Avoid leaking memory when handed toasted input. */
+ PG_FREE_IF_COPY(elem,0);
+ AARR_FREE_IF_COPY(array, 1);
+
+ PG_RETURN_BOOL(result);
+}
+
+Datum
arraycontained(PG_FUNCTION_ARGS)
{
AnyArrayType *array1 = PG_GETARG_ANY_ARRAY(0);
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index da0228de6b..2da9002577 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -687,6 +687,8 @@ DATA(insert ( 2595 718 600 15 o 3291 783 1970 ));
*/
DATA(insert ( 2745 2277 2277 1 s 2750 2742 0 ));
DATA(insert ( 2745 2277 2277 2 s 2751 2742 0 ));
+//TODO link the operator's pg_operator OID
+DATA(insert ( 2745 2277 2283 5 s 2753 2742 0 ));
DATA(insert ( 2745 2277 2277 3 s 2752 2742 0 ));
DATA(insert ( 2745 2277 2277 4 s 1070 2742 0 ));
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index ccbb17efec..626a0b1c49 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1567,6 +1567,9 @@ DESCR("overlaps");
DATA(insert OID = 2751 ( "@>" PGNSP PGUID b f f 2277 2277 16 2752 0 arraycontains arraycontsel arraycontjoinsel ));
DESCR("contains");
#define OID_ARRAY_CONTAINS_OP 2751
+DATA(insert OID = 2753 ( "@>" PGNSP PGUID b f f 2277 2283 16 2753 0 arraycontainselem 0 0 ));
+DESCR("containselem");
+#define OID_ARRAY_CONTAINS_ELEM_OP 2753
DATA(insert OID = 2752 ( "<@" PGNSP PGUID b f f 2277 2277 16 2751 0 arraycontained arraycontsel arraycontjoinsel ));
DESCR("is contained by");
#define OID_ARRAY_CONTAINED_OP 2752
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-06-17 21:41 ` Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-06-17 21:41 UTC (permalink / raw)
To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; Alvaro Herrera <alvaro.herrera@2ndquadrant.com>; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
*Updates till now:*
- added a record to pg_proc (src/include/catalog/pg_proc.h)
- modified opr_sanity regression check expected results
- implemented a low-level function called `array_contains_elem` as an
equivalent to `array_contain_compare` but accepts anyelement instead of
anyarray as the right operand. This is more efficient than constructing an
array and then immediately deconstructing it.
*Questions:*
- I'd like to check that anyelem and anyarray have the same element
type. but anyelem is obtained from PG_FUNCTION_ARGS as a Datum. How can
I make such a check?
Best Regards,
Mark Rofail
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
elemOperatorV3.patch (text/x-patch, 7.6K) download
diff --git a/src/backend/access/gin/ginarrayproc.c b/src/backend/access/gin/ginarrayproc.c
index cc7435e030..214aac8fba 100644
--- a/src/backend/access/gin/ginarrayproc.c
+++ b/src/backend/access/gin/ginarrayproc.c
@@ -24,6 +24,7 @@
#define GinContainsStrategy 2
#define GinContainedStrategy 3
#define GinEqualStrategy 4
+#define GinContainsElemStrategy 5
/*
@@ -43,7 +44,7 @@ ginarrayextract(PG_FUNCTION_ARGS)
bool *nulls;
int nelems;
- get_typlenbyvalalign(ARR_ELEMTYPE(array),
+ get_typlenbyvalalign(ARR_ELEMTYPE(array),
&elmlen, &elmbyval, &elmalign);
deconstruct_array(array,
@@ -110,7 +111,8 @@ ginqueryarrayextract(PG_FUNCTION_ARGS)
case GinOverlapStrategy:
*searchMode = GIN_SEARCH_MODE_DEFAULT;
break;
- case GinContainsStrategy:
+ case GinContainsElemStrategy:
+ case GinContainsStrategy:
if (nelems > 0)
*searchMode = GIN_SEARCH_MODE_DEFAULT;
else /* everything contains the empty set */
@@ -171,6 +173,7 @@ ginarrayconsistent(PG_FUNCTION_ARGS)
}
}
break;
+ case GinContainsElemStrategy:
case GinContainsStrategy:
/* result is not lossy */
*recheck = false;
@@ -258,7 +261,8 @@ ginarraytriconsistent(PG_FUNCTION_ARGS)
}
}
break;
- case GinContainsStrategy:
+ case GinContainsElemStrategy:
+ case GinContainsStrategy:
/* must have all elements in check[] true, and no nulls */
res = GIN_TRUE;
for (i = 0; i < nkeys; i++)
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index d9c8aa569c..8009ab5acb 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -4232,6 +4232,107 @@ arraycontained(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(result);
}
+/*
+ * array_contains_elem : checks an array for a spefific element
+ */
+static bool
+array_contains_elem(AnyArrayType *array, Datum elem, Oid collation,
+ void **fn_extra)
+{
+ Oid element_type = AARR_ELEMTYPE(array);
+ TypeCacheEntry *typentry;
+ int nelems;
+ int typlen;
+ bool typbyval;
+ char typalign;
+ int i;
+ array_iter it1;
+ FunctionCallInfoData locfcinfo;
+
+ /*
+ * We arrange to look up the equality function only once per series of
+ * calls, assuming the element type doesn't change underneath us. The
+ * typcache is used so that we have no memory leakage when being used as
+ * an index support function.
+ */
+ typentry = (TypeCacheEntry *)*fn_extra;
+ if (typentry == NULL ||
+ typentry->type_id != element_type)
+ {
+ typentry = lookup_type_cache(element_type,
+ TYPECACHE_EQ_OPR_FINFO);
+ if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(element_type))));
+ *fn_extra = (void *)typentry;
+ }
+ typlen = typentry->typlen;
+ typbyval = typentry->typbyval;
+ typalign = typentry->typalign;
+
+ /*
+ * Apply the comparison operator to each pair of array elements.
+ */
+ InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+ collation, NULL, NULL);
+
+ /* Loop over source data */
+ nelems = ArrayGetNItems(AARR_NDIM(array), AARR_DIMS(array));
+ array_iter_setup(&it1, array);
+
+ for (i = 0; i < nelems; i++)
+ {
+ Datum elt1;
+ bool isnull;
+ bool oprresult;
+
+ /* Get element, checking for NULL */
+ elt1 = array_iter_next(&it1, &isnull, i, typlen, typbyval, typalign);
+
+ /*
+ * We assume that the comparison operator is strict, so a NULL can't
+ * match anything. XXX this diverges from the "NULL=NULL" behavior of
+ * array_eq, should we act like that?
+ */
+ if (isnull)
+ continue;
+
+ /*
+ * Apply the operator to the element pair
+ */
+ locfcinfo.arg[0] = elt1;
+ locfcinfo.arg[1] = elem;
+ locfcinfo.argnull[0] = false;
+ locfcinfo.argnull[1] = false;
+ locfcinfo.isnull = false;
+ oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+ if (oprresult)
+ return true;
+ }
+
+ return false;
+}
+
+Datum
+arraycontainselem(PG_FUNCTION_ARGS)
+{
+ AnyArrayType *array = PG_GETARG_ANY_ARRAY(0);
+ Datum elem = PG_GETARG_DATUM(1);
+ Oid collation = PG_GET_COLLATION();
+ bool result;
+
+ result = array_contains_elem(array, elem, collation,
+ &fcinfo->flinfo->fn_extra);
+
+ /* Avoid leaking memory when handed toasted input. */
+ AARR_FREE_IF_COPY(array, 0);
+ PG_FREE_IF_COPY( &elem, 1);
+
+ PG_RETURN_BOOL(result);
+}
+
/*-----------------------------------------------------------------------------
* Array iteration functions
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index da0228de6b..4967a388e8 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -689,6 +689,7 @@ DATA(insert ( 2745 2277 2277 1 s 2750 2742 0 ));
DATA(insert ( 2745 2277 2277 2 s 2751 2742 0 ));
DATA(insert ( 2745 2277 2277 3 s 2752 2742 0 ));
DATA(insert ( 2745 2277 2277 4 s 1070 2742 0 ));
+DATA(insert ( 2745 2277 2283 5 s 6108 2742 0 ));
/*
* btree enum_ops
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index ccbb17efec..cc56d61bfb 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1570,6 +1570,8 @@ DESCR("contains");
DATA(insert OID = 2752 ( "<@" PGNSP PGUID b f f 2277 2277 16 2751 0 arraycontained arraycontsel arraycontjoinsel ));
DESCR("is contained by");
#define OID_ARRAY_CONTAINED_OP 2752
+DATA(insert OID = 6108 ( "@>" PGNSP PGUID b f f 2277 2283 16 0 0 arraycontainselem arraycontsel arraycontjoinsel ));
+DESCR("containselem");
/* capturing operators to preserve pre-8.3 behavior of text concatenation */
DATA(insert OID = 2779 ( "||" PGNSP PGUID b f f 25 2776 25 0 0 textanycat - - ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 6c44def6e6..66ba68e9ef 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4308,6 +4308,7 @@ DESCR("GIN array support (obsolete)");
DATA(insert OID = 2747 ( arrayoverlap PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arrayoverlap _null_ _null_ _null_ ));
DATA(insert OID = 2748 ( arraycontains PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arraycontains _null_ _null_ _null_ ));
DATA(insert OID = 2749 ( arraycontained PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arraycontained _null_ _null_ _null_ ));
+DATA(insert OID = 6109 ( arraycontainselem PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2283" _null_ _null_ _null_ _null_ _null_ arraycontainselem _null_ _null_ _null_ ));
/* BRIN minmax */
DATA(insert OID = 3383 ( brin_minmax_opcinfo PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_opcinfo _null_ _null_ _null_ ));
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 1d7629f84e..dc98a8c8f9 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1801,6 +1801,7 @@ ORDER BY 1, 2, 3;
2742 | 2 | @@@
2742 | 3 | <@
2742 | 4 | =
+ 2742 | 5 | @>
2742 | 7 | @>
2742 | 9 | ?
2742 | 10 | ?|
@@ -1868,7 +1869,7 @@ ORDER BY 1, 2, 3;
4000 | 25 | <<=
4000 | 26 | >>
4000 | 27 | >>=
-(121 rows)
+(122 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-06-18 20:16 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Alexander Korotkov @ 2017-06-18 20:16 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; Alvaro Herrera <alvaro.herrera@2ndquadrant.com>; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Sun, Jun 18, 2017 at 12:41 AM, Mark Rofail <markm.rofail@gmail.com>
wrote:
> *Questions:*
>
> - I'd like to check that anyelem and anyarray have the same element
> type. but anyelem is obtained from PG_FUNCTION_ARGS as a Datum. How
> can I make such a check?
>
>
As I know, it's implicitly checked during query analyze stage. You don't
have to implement your own check inside function implementation.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
@ 2017-06-19 22:02 ` Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-06-19 22:02 UTC (permalink / raw)
To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; Alvaro Herrera <alvaro.herrera@2ndquadrant.com>; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
Okay, so major breakthrough.
*Updates:*
- The operator @>(anyarray, anyelement) is now functional
- The segmentation fault was due to applying PG_FREE_IF_COPY on a
datum when it should only be applied on TOASTed inputs
- The only problem now is if for example you apply the operator as
follows '{AAAAAAAAAA646'}' @> 'AAAAAAAAAA646' it maps to @>(anyarray,
anyarray) since 'AAAAAAAAAA646' is interpreted as char[] instead of Text
- Added some regression tests (src/test/regress/sql/arrays.sql) and
their results(src/test/regress/expected/arrays.out)
- wokred on the new GIN strategy, I don't think it would vary much from
GinContainsStrategy.
*What I plan to do:*
- I need to start working on the Referential Integrity code but I don't
where to start
Best Regards,
Mark Rofail
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
elemOperatorV3_1.patch (text/x-patch, 16.3K) download
diff --git a/src/backend/access/gin/ginarrayproc.c b/src/backend/access/gin/ginarrayproc.c
index cc7435e030..a1b3f53ed9 100644
--- a/src/backend/access/gin/ginarrayproc.c
+++ b/src/backend/access/gin/ginarrayproc.c
@@ -24,6 +24,7 @@
#define GinContainsStrategy 2
#define GinContainedStrategy 3
#define GinEqualStrategy 4
+#define GinContainsElemStrategy 5
/*
@@ -110,6 +111,11 @@ ginqueryarrayextract(PG_FUNCTION_ARGS)
case GinOverlapStrategy:
*searchMode = GIN_SEARCH_MODE_DEFAULT;
break;
+ case GinContainsElemStrategy:
+ /* only items that match the queried element
+ are considered candidate */
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+ break;
case GinContainsStrategy:
if (nelems > 0)
*searchMode = GIN_SEARCH_MODE_DEFAULT;
@@ -171,6 +177,7 @@ ginarrayconsistent(PG_FUNCTION_ARGS)
}
}
break;
+ case GinContainsElemStrategy:
case GinContainsStrategy:
/* result is not lossy */
*recheck = false;
@@ -258,7 +265,8 @@ ginarraytriconsistent(PG_FUNCTION_ARGS)
}
}
break;
- case GinContainsStrategy:
+ case GinContainsElemStrategy:
+ case GinContainsStrategy:
/* must have all elements in check[] true, and no nulls */
res = GIN_TRUE;
for (i = 0; i < nkeys; i++)
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index d9c8aa569c..c563aa564e 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -4232,6 +4232,117 @@ arraycontained(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(result);
}
+/*
+ * array_contains_elem : checks an array for a spefific element
+ */
+static bool
+array_contains_elem(AnyArrayType *array, Datum elem, Oid element_type,
+ bool element_isnull, Oid collation, void **fn_extra)
+{
+ Oid arr_type = AARR_ELEMTYPE(array);
+ TypeCacheEntry *typentry;
+ int nelems;
+ int typlen;
+ bool typbyval;
+ char typalign;
+ int i;
+ array_iter it1;
+ FunctionCallInfoData locfcinfo;
+
+ if (arr_type != element_type)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot compare different element types")));
+
+ if (element_isnull)
+ return false;
+
+ /*
+ * We arrange to look up the equality function only once per series of
+ * calls, assuming the element type doesn't change underneath us. The
+ * typcache is used so that we have no memory leakage when being used as
+ * an index support function.
+ */
+ typentry = (TypeCacheEntry *)*fn_extra;
+ if (typentry == NULL ||
+ typentry->type_id != arr_type)
+ {
+ typentry = lookup_type_cache(arr_type,
+ TYPECACHE_EQ_OPR_FINFO);
+ if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(arr_type))));
+ *fn_extra = (void *)typentry;
+ }
+ typlen = typentry->typlen;
+ typbyval = typentry->typbyval;
+ typalign = typentry->typalign;
+
+ /*
+ * Apply the comparison operator to each pair of array elements.
+ */
+ InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+ collation, NULL, NULL);
+
+ /* Loop over source data */
+ nelems = ArrayGetNItems(AARR_NDIM(array), AARR_DIMS(array));
+ array_iter_setup(&it1, array);
+
+ for (i = 0; i < nelems; i++)
+ {
+ Datum elt1;
+ bool isnull;
+ bool oprresult;
+
+ /* Get element, checking for NULL */
+ elt1 = array_iter_next(&it1, &isnull, i, typlen, typbyval, typalign);
+
+ /*
+ * We assume that the comparison operator is strict, so a NULL can't
+ * match anything. XXX this diverges from the "NULL=NULL" behavior of
+ * array_eq, should we act like that?
+ */
+ if (isnull)
+ continue;
+
+ /*
+ * Apply the operator to the element pair
+ */
+ locfcinfo.arg[0] = elt1;
+ locfcinfo.arg[1] = elem;
+ locfcinfo.argnull[0] = false;
+ locfcinfo.argnull[1] = false;
+ locfcinfo.isnull = false;
+ oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+ if (oprresult)
+ return true;
+ }
+
+ return false;
+}
+
+Datum
+arraycontainselem(PG_FUNCTION_ARGS)
+{
+ AnyArrayType *array = PG_GETARG_ANY_ARRAY(0);
+ Datum elem = PG_GETARG_DATUM(1);
+ Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ Oid collation = PG_GET_COLLATION();
+ bool element_isnull = PG_ARGISNULL(1);
+ bool result;
+
+ result = array_contains_elem(array, elem, element_type,
+ element_isnull, collation,
+ &fcinfo->flinfo->fn_extra);
+
+ /* Avoid leaking memory when handed toasted input. */
+ AARR_FREE_IF_COPY(array, 0);
+
+ PG_RETURN_BOOL(result);
+}
+
/*-----------------------------------------------------------------------------
* Array iteration functions
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index da0228de6b..4967a388e8 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -689,6 +689,7 @@ DATA(insert ( 2745 2277 2277 1 s 2750 2742 0 ));
DATA(insert ( 2745 2277 2277 2 s 2751 2742 0 ));
DATA(insert ( 2745 2277 2277 3 s 2752 2742 0 ));
DATA(insert ( 2745 2277 2277 4 s 1070 2742 0 ));
+DATA(insert ( 2745 2277 2283 5 s 6108 2742 0 ));
/*
* btree enum_ops
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index ccbb17efec..cc56d61bfb 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1570,6 +1570,8 @@ DESCR("contains");
DATA(insert OID = 2752 ( "<@" PGNSP PGUID b f f 2277 2277 16 2751 0 arraycontained arraycontsel arraycontjoinsel ));
DESCR("is contained by");
#define OID_ARRAY_CONTAINED_OP 2752
+DATA(insert OID = 6108 ( "@>" PGNSP PGUID b f f 2277 2283 16 0 0 arraycontainselem arraycontsel arraycontjoinsel ));
+DESCR("containselem");
/* capturing operators to preserve pre-8.3 behavior of text concatenation */
DATA(insert OID = 2779 ( "||" PGNSP PGUID b f f 25 2776 25 0 0 textanycat - - ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 6c44def6e6..66ba68e9ef 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4308,6 +4308,7 @@ DESCR("GIN array support (obsolete)");
DATA(insert OID = 2747 ( arrayoverlap PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arrayoverlap _null_ _null_ _null_ ));
DATA(insert OID = 2748 ( arraycontains PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arraycontains _null_ _null_ _null_ ));
DATA(insert OID = 2749 ( arraycontained PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arraycontained _null_ _null_ _null_ ));
+DATA(insert OID = 6109 ( arraycontainselem PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2283" _null_ _null_ _null_ _null_ _null_ arraycontainselem _null_ _null_ _null_ ));
/* BRIN minmax */
DATA(insert OID = 3383 ( brin_minmax_opcinfo PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_opcinfo _null_ _null_ _null_ ));
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index c730563f03..f9932a5335 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -737,6 +737,17 @@ SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(6 rows)
+SELECT * FROM array_op_test WHERE i @> 32 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+ 98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
+ 100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
+(6 rows)
+
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -761,6 +772,19 @@ SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
(8 rows)
+SELECT * FROM array_op_test WHERE i @> 17 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
+ 53 | {38,17} | {AAAAAAAAAAA21658}
+ 65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+(8 rows)
+
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -942,6 +966,11 @@ SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
-------+---+---
(0 rows)
+SELECT * FROM array_op_test WHERE i @> NULL ORDER BY seqno;
+ seqno | i | t
+-------+---+---
+(0 rows)
+
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
seqno | i | t
-------+---+---
@@ -962,6 +991,15 @@ SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
(4 rows)
+SELECT * FROM array_op_test WHERE t @> 'AAAAAAAA72908' ORDER BY seqno;
+ seqno | i | t
+-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
+ 22 | {11,6,56,62,53,30} | {AAAAAAAA72908}
+ 45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}
+ 72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+(4 rows)
+
SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
seqno | i | t
-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
@@ -979,6 +1017,14 @@ SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
96 | {23,97,43} | {AAAAAAAAAA646,A87088}
(3 rows)
+SELECT * FROM array_op_test WHERE t @> 'AAAAAAAAAA646' ORDER BY seqno;
+ seqno | i | t
+-------+------------------+--------------------------------------------------------------------
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+ 96 | {23,97,43} | {AAAAAAAAAA646,A87088}
+(3 rows)
+
SELECT * FROM array_op_test WHERE t && '{AAAAAAAAAA646}' ORDER BY seqno;
seqno | i | t
-------+------------------+--------------------------------------------------------------------
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 1d7629f84e..dc98a8c8f9 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1801,6 +1801,7 @@ ORDER BY 1, 2, 3;
2742 | 2 | @@@
2742 | 3 | <@
2742 | 4 | =
+ 2742 | 5 | @>
2742 | 7 | @>
2742 | 9 | ?
2742 | 10 | ?|
@@ -1868,7 +1869,7 @@ ORDER BY 1, 2, 3;
4000 | 25 | <<=
4000 | 26 | >>
4000 | 27 | >>=
-(121 rows)
+(122 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 25dd4e2c6d..39189ce6e3 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -313,8 +313,10 @@ SELECT ARRAY[0,0] || ARRAY[1,1] || ARRAY[2,2] AS "{0,0,1,1,2,2}";
SELECT 0 || ARRAY[1,2] || 3 AS "{0,1,2,3}";
SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @> 32 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @> 17 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{32,17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32,17}' ORDER BY seqno;
@@ -325,12 +327,15 @@ SELECT * FROM array_op_test WHERE i && '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @> NULL ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @> 'AAAAAAAA72908' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @> 'AAAAAAAAAA646' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno;
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-06-19 22:19 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Alvaro Herrera @ 2017-06-19 22:19 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
Mark Rofail wrote:
> Okay, so major breakthrough.
>
> *Updates:*
>
> - The operator @>(anyarray, anyelement) is now functional
> - The segmentation fault was due to applying PG_FREE_IF_COPY on a
> datum when it should only be applied on TOASTed inputs
> - The only problem now is if for example you apply the operator as
> follows '{AAAAAAAAAA646'}' @> 'AAAAAAAAAA646' it maps to @>(anyarray,
> anyarray) since 'AAAAAAAAAA646' is interpreted as char[] instead of Text
> - Added some regression tests (src/test/regress/sql/arrays.sql) and
> their results(src/test/regress/expected/arrays.out)
> - wokred on the new GIN strategy, I don't think it would vary much from
> GinContainsStrategy.
OK, that's great.
> *What I plan to do:*
>
> - I need to start working on the Referential Integrity code but I don't
> where to start
You need to study the old patch posted by Marco Nenciarini.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2017-06-25 23:26 ` Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-06-25 23:26 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
*What I did:*
- read into the old patch but couldn't apply it since it's quite old. It
needs to be rebased and that's what I am working on. It's a lot of work.
- incomplete patch can be found attached here
*Bugs*
- problem with the @>(anyarray, anyelement) opertator: if for example,
you apply the operator as follows '{AAAAAAAAAA646'}' @> 'AAAAAAAAAA646' it
maps to @>(anyarray, anyarray) since 'AAAAAAAAAA646' is interpreted as
char[] instead of Text
*Suggestion:*
- since I needed to check if the Datum was null and its type, I had to
do it in the arraycontainselem and pass it as a parameter to the underlying
function array_contains_elem. I'm proposing to introduce a new struct like
ArrayType, but ElementType along all with brand new MACROs to make dealing
with anyelement easier in any polymorphic context.
Best Regards,
Mark Rofail
On Tue, Jun 20, 2017 at 12:19 AM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:
> Mark Rofail wrote:
> > Okay, so major breakthrough.
> >
> > *Updates:*
> >
> > - The operator @>(anyarray, anyelement) is now functional
> > - The segmentation fault was due to applying PG_FREE_IF_COPY on a
> > datum when it should only be applied on TOASTed inputs
> > - The only problem now is if for example you apply the operator as
> > follows '{AAAAAAAAAA646'}' @> 'AAAAAAAAAA646' it maps to
> @>(anyarray,
> > anyarray) since 'AAAAAAAAAA646' is interpreted as char[] instead
> of Text
> > - Added some regression tests (src/test/regress/sql/arrays.sql) and
> > their results(src/test/regress/expected/arrays.out)
> > - wokred on the new GIN strategy, I don't think it would vary much
> from
> > GinContainsStrategy.
>
> OK, that's great.
>
> > *What I plan to do:*
> >
> > - I need to start working on the Referential Integrity code but I
> don't
> > where to start
>
> You need to study the old patch posted by Marco Nenciarini.
>
> --
> Álvaro Herrera https://www.2ndQuadrant.com/
> PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
incomplete-Array-ELEMENT-foreign-key-v2-REBASED-ddb5fdc.patch (text/x-patch, 25.8K) download
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ea655a10a8..712f631e88 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2288,6 +2288,14 @@ SCRAM-SHA-256$<replaceable><iteration count></>:<replaceable><salt><
</row>
<row>
+ <entry><structfield>confiselement</structfield></entry>
+ <entry><type>bool</type></entry>
+ <entry></entry>
+ <entry>If a foreign key, is it an array <literal>ELEMENT</literal>
+ foreign key?</entry>
+ </row>
+
+ <row>
<entry><structfield>coninhcount</structfield></entry>
<entry><type>int4</type></entry>
<entry></entry>
@@ -2324,6 +2332,18 @@ SCRAM-SHA-256$<replaceable><iteration count></>:<replaceable><salt><
</row>
<row>
+ <entry><structfield>confelement</structfield></entry>
+ <entry><type>bool[]</type></entry>
+ <entry></entry>
+ <entry>
+ If a foreign key, list of booleans expressing which columns
+ are array <literal>ELEMENT</literal> columns; see
+ <xref linkend="sql-createtable-element-foreign-key-constraints">
+ for details
+ </entry>
+ </row>
+
+ <row>
<entry><structfield>conpfeqop</structfield></entry>
<entry><type>oid[]</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</></entry>
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index b05a9c2150..c1c847bc7e 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -881,7 +881,112 @@ CREATE TABLE order_items (
<xref linkend="sql-createtable">.
</para>
</sect2>
-
+
+ <sect2 id="ddl-constraints-element-fk">
+ <title>Array ELEMENT Foreign Keys</title>
+
+ <indexterm>
+ <primary>ELEMENT foreign key</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>Array ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>referential integrity</primary>
+ </indexterm>
+
+ <para>
+ Another option you have with foreign keys is to use a
+ referencing column which is an array of elements with
+ the same type (or a compatible one) as the referenced
+ column in the related table. This feature is called
+ <firstterm>array element foreign key</firstterm> and is implemented
+ in PostgreSQL with <firstterm>ELEMENT foreign key constraints</firstterm>,
+ as described in the following example:
+
+ <programlisting>
+ CREATE TABLE drivers (
+ driver_id integer PRIMARY KEY,
+ first_name text,
+ last_name text,
+ ...
+ );
+
+ CREATE TABLE races (
+ race_id integer PRIMARY KEY,
+ title text,
+ race_day DATE,
+ ...
+ final_positions integer[] <emphasis>ELEMENT REFERENCES drivers</emphasis>
+ );
+ </programlisting>
+
+ The above example uses an array (<literal>final_positions</literal>)
+ to store the results of a race: for each of its elements
+ a referential integrity check is enforced on the
+ <literal>drivers</literal> table.
+ Note that <literal>ELEMENT REFERENCES</literal> is an extension
+ of PostgreSQL and it is not included in the SQL standard.
+ </para>
+
+ <para>
+ Even though the most common use case for array <literal>ELEMENT</literal>
+ foreign keys is on a single column key, you can define an <quote>array
+ <literal>ELEMENT</literal> foreign key constraint</quote> on a group
+ of columns. As the following example shows, it must be written in table
+ constraint form:
+
+ <programlisting>
+ CREATE TABLE available_moves (
+ kind text,
+ move text,
+ description text,
+ PRIMARY KEY (kind, move)
+ );
+
+ CREATE TABLE paths (
+ description text,
+ kind text,
+ moves text[],
+ <emphasis>FOREIGN KEY (kind, ELEMENT moves) REFERENCES available_moves (kind, move)</emphasis>
+ );
+
+ INSERT INTO available_moves VALUES ('relative', 'LN', 'look north');
+ INSERT INTO available_moves VALUES ('relative', 'RL', 'rotate left');
+ INSERT INTO available_moves VALUES ('relative', 'RR', 'rotate right');
+ INSERT INTO available_moves VALUES ('relative', 'MF', 'move forward');
+ INSERT INTO available_moves VALUES ('absolute', 'N', 'move north');
+ INSERT INTO available_moves VALUES ('absolute', 'S', 'move south');
+ INSERT INTO available_moves VALUES ('absolute', 'E', 'move east');
+ INSERT INTO available_moves VALUES ('absolute', 'W', 'move west');
+
+ INSERT INTO paths VALUES ('L-shaped path', 'relative', '{LN, RL, MF, RR, MF, MF}');
+ INSERT INTO paths VALUES ('L-shaped path', 'absolute', '{W, N, N}');
+ </programlisting>
+
+ On top of standard foreign key requirements,
+ array <literal>ELEMENT</literal> foreign key constraints
+ require that the referencing column is an array of a compatible
+ type of the corresponding referenced column.
+ </para>
+
+ <para>
+ For more detailed information on array <literal>ELEMENT</literal>
+ foreign key options and special cases, please refer to the documentation
+ for <xref linkend="sql-createtable-foreign-key"> and
+ <xref linkend="sql-createtable-element-foreign-key-constraints">.
+ </para>
+
+ </sect2>
+
<sect2 id="ddl-constraints-exclusion">
<title>Exclusion Constraints</title>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index b15c19d3d0..1d7749ce38 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -65,7 +65,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ] |
UNIQUE <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="PARAMETER">index_parameters</replaceable> |
- REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
+ [ELEMENT] REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -76,7 +76,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
UNIQUE ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ] |
- FOREIGN KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> [, ... ] ) ]
+ FOREIGN KEY ( [ELEMENT] <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> [, ... ] ) ]
[ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -779,10 +779,11 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</listitem>
</varlistentry>
- <varlistentry>
+ <varlistentry id="sql-createtable-foreign-key" xreflabel="FOREIGN KEY">
<term><literal>REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
- <term><literal>FOREIGN KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] )
+ <term>
+ <literal>FOREIGN KEY ( [ELEMENT] <replaceable class="parameter">column</replaceable> [, ... ] )
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] ) ]
[ MATCH <replaceable class="parameter">matchtype</replaceable> ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ]
@@ -806,6 +807,19 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</para>
<para>
+ In case the column name <replaceable class="parameter">column</replaceable>
+ is prepended with the <literal>ELEMENT</literal> keyword and <replaceable
+ class="parameter">column</replaceable> is an array of elements compatible
+ with the corresponding <replaceable class="parameter">refcolumn</replaceable>
+ in <replaceable class="parameter">reftable</replaceable>, an
+ array <literal>ELEMENT</literal> foreign key constraint is put in place
+ (see <xref linkend="sql-createtable-element-foreign-key-constraints">
+ for more information).
+ Multi-column keys with more than one <literal>ELEMENT</literal> column
+ are currently not allowed.
+ </para>
+
+ <para>
A value inserted into the referencing column(s) is matched against the
values of the referenced table and referenced columns using the
given match type. There are three match types: <literal>MATCH
@@ -868,7 +882,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
<para>
Delete any rows referencing the deleted row, or update the
values of the referencing column(s) to the new values of the
- referenced columns, respectively.
+ referenced columns, respectively. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -877,7 +892,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
<term><literal>SET NULL</literal></term>
<listitem>
<para>
- Set the referencing column(s) to null.
+ Set the referencing column(s) to null. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -889,7 +905,9 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
Set the referencing column(s) to their default values.
(There must be a row in the referenced table matching the default
values, if they are not null, or the operation will fail.)
- </para>
+ Currently not supported with array <literal>ELEMENT</literal>
+ foreign keys.
+ </para>
</listitem>
</varlistentry>
</variablelist>
@@ -904,6 +922,60 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</listitem>
</varlistentry>
+ <varlistentry id="sql-createtable-element-foreign-key-constraints" xreflabel="ELEMENT REFERENCES">
+ <term><literal>ELEMENT REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
+ <listitem>
+ <para>
+ The <literal>ELEMENT REFERENCES</literal> definition specifies
+ an <quote>array <literal>ELEMENT</literal> foreign key</quote>,
+ a special kind of foreign key
+ constraint requiring the referencing column to be an array of elements
+ of the same type (or a compatible one) as the referenced column
+ in the referenced table. The value of each element of the
+ <replaceable class="parameter">refcolumn</replaceable> array
+ will be matched against some row of <replaceable
+ class="parameter">reftable</replaceable>.
+ </para>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys are an extension
+ of PostgreSQL and are not included in the SQL standard.
+ </para>
+
+ <para>
+ Even with <literal>ELEMENT</literal> foreign keys, modifications
+ in the referenced column can trigger actions to be performed on
+ the referencing array.
+ Similarly to standard foreign keys, you can specify these
+ actions using the <literal>ON DELETE</literal> and
+ <literal>ON UPDATE</literal> clauses.
+ However, only the two following actions for each clause are
+ currently allowed:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>NO ACTION</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints. This is the default action.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RESTRICT</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>DEFERRABLE</literal></term>
<term><literal>NOT DEFERRABLE</literal></term>
@@ -1843,6 +1915,16 @@ CREATE TABLE cities_ab_10000_to_100000
</refsect2>
<refsect2>
+ <title id="sql-createtable-foreign-key-arrays">Array <literal>ELEMENT</literal> Foreign Keys</title>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys and the
+ <literal>ELEMENT REFERENCES</literal> clause
+ are a <productname>PostgreSQL</productname> extension.
+ </para>
+ </refsect2>
+
+ <refsect2>
<title><literal>PARTITION BY</> Clause</title>
<para>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index a376b99f1e..a25ccd084c 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2099,7 +2099,9 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr,
NULL,
NULL,
0,
- ' ',
+ false,
+ NULL,
+ ' ',
' ',
' ',
NULL, /* not an exclusion constraint */
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 027abd56b0..a2e8baba55 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1212,6 +1212,8 @@ index_constraint_create(Relation heapRelation,
NULL,
NULL,
0,
+ false,
+ NULL,
' ',
' ',
' ',
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index 98bcfa08c6..0c731b1085 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -1226,7 +1226,9 @@ CREATE VIEW referential_constraints AS
WHEN 'd' THEN 'SET DEFAULT'
WHEN 'r' THEN 'RESTRICT'
WHEN 'a' THEN 'NO ACTION' END
- AS character_data) AS delete_rule
+ AS character_data) AS delete_rule,
+
+ con.confiselement AS is_element
FROM (pg_namespace ncon
INNER JOIN pg_constraint con ON ncon.oid = con.connamespace
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 1336c46d3f..c99b7c6cad 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -63,6 +63,8 @@ CreateConstraintEntry(const char *constraintName,
const Oid *ppEqOp,
const Oid *ffEqOp,
int foreignNKeys,
+ bool confisElement,
+ const bool *foreignElement,
char foreignUpdateType,
char foreignDeleteType,
char foreignMatchType,
@@ -82,6 +84,7 @@ CreateConstraintEntry(const char *constraintName,
Datum values[Natts_pg_constraint];
ArrayType *conkeyArray;
ArrayType *confkeyArray;
+ ArrayType *confelementArray;
ArrayType *conpfeqopArray;
ArrayType *conppeqopArray;
ArrayType *conffeqopArray;
@@ -177,6 +180,7 @@ CreateConstraintEntry(const char *constraintName,
values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount);
values[Anum_pg_constraint_connoinherit - 1] = BoolGetDatum(conNoInherit);
+ values[Anum_pg_constraint_coniselement - 1] = BoolGetDatum(confisElement);
if (conkeyArray)
values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
@@ -188,6 +192,11 @@ CreateConstraintEntry(const char *constraintName,
else
nulls[Anum_pg_constraint_confkey - 1] = true;
+ if (confelementArray)
+ values[Anum_pg_constraint_confelement - 1] = PointerGetDatum(confelementArray);
+ else
+ nulls[Anum_pg_constraint_confelement - 1] = true;
+
if (conpfeqopArray)
values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
else
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7d9c769b06..03dfa2ea5e 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -41,6 +41,7 @@
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
+#include "catalog/pg_operator.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@@ -6995,6 +6996,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Relation pkrel;
int16 pkattnum[INDEX_MAX_KEYS];
int16 fkattnum[INDEX_MAX_KEYS];
+ bool fkattelement[INDEX_MAX_KEYS];
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
Oid opclasses[INDEX_MAX_KEYS];
@@ -7082,6 +7084,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
*/
MemSet(pkattnum, 0, sizeof(pkattnum));
MemSet(fkattnum, 0, sizeof(fkattnum));
+ MemSet(fkattelement, 0, sizeof(fkattelement));
MemSet(pktypoid, 0, sizeof(pktypoid));
MemSet(fktypoid, 0, sizeof(fktypoid));
MemSet(opclasses, 0, sizeof(opclasses));
@@ -7094,6 +7097,39 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
fkattnum, fktypoid);
/*
+ * If an array ELEMENT FK, decode the content of
+ * the fk_element_attrs array.
+ */
+ if (fkconstraint->fk_is_element)
+ {
+ ListCell *l;
+ int attnum;
+ bool element_found = false;
+
+ attnum = 0;
+ foreach(l, fkconstraint->fk_element_attrs)
+ {
+ if (lfirst_int(l)) {
+
+ /*
+ * Currently, the ELEMENT flag cannot be set on more than
+ * one column.
+ */
+ if (element_found) {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("array ELEMENT foreign keys support only one ELEMENT column")));
+ }
+
+ fkattelement[attnum] = true;
+ element_found = true;
+ }
+ attnum++;
+ }
+
+ }
+
+ /*
* If the attribute list for the referenced table was omitted, lookup the
* definition of the primary key and use it. Otherwise, validate the
* supplied attribute list. In either case, discover the index OID and
@@ -7141,6 +7177,22 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
old_check_ok = (fkconstraint->old_conpfeqop != NIL);
Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
+ /* Enforce array ELEMENT foreign key restrictions */
+ if (fkconstraint->fk_is_element)
+ {
+ /*
+ * Array ELEMENT foreign keys support only NO ACTION and
+ * RESTRICT actions
+ */
+ if ((fkconstraint->fk_upd_action != FKCONSTR_ACTION_NOACTION
+ && fkconstraint->fk_upd_action != FKCONSTR_ACTION_RESTRICT)
+ || (fkconstraint->fk_del_action != FKCONSTR_ACTION_NOACTION
+ && fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("array ELEMENT foreign keys only support NO ACTION and RESTRICT actions")));
+ }
+
for (i = 0; i < numpks; i++)
{
Oid pktype = pktypoid[i];
@@ -7156,6 +7208,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid ffeqop;
int16 eqstrategy;
Oid pfeqop_right;
+ Oid fk_element_type = InvalidOid;
/* We need several fields out of the pg_opclass entry */
cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
@@ -7189,6 +7242,31 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
eqstrategy, opcintype, opcintype, opfamily);
+ if (fkattelement[i])
+ {
+ /*
+ * For every array ELEMENT FK, look if an equality operator that
+ * takes exactly the FK element type exists. Assume we should
+ * look through any domain here.
+ */
+ fk_element_type = get_base_element_type(fktype);
+ if (!OidIsValid(fk_element_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has type %s which is not an array type.",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktype))));
+
+ pfeqop = get_opfamily_member(opfamily, opcintype, fk_element_type,
+ eqstrategy);
+ pfeqop_right = fk_element_type;
+ ffeqop = ARRAY_EQ_OP;
+ }
+ else
+ {
+
/*
* Are there equality operators that take exactly the FK type? Assume
* we should look through any domain here.
@@ -7208,6 +7286,26 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
/* keep compiler quiet */
pfeqop_right = InvalidOid;
ffeqop = InvalidOid;
+ /*
+ * Are there equality operators that take exactly the FK type?
+ * Assume we should look through any domain here.
+ */
+ fktyped = getBaseType(fktype);
+
+ pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
+ eqstrategy);
+ if (OidIsValid(pfeqop))
+ {
+ pfeqop_right = fktyped;
+ ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
+ eqstrategy);
+ }
+ else
+ {
+ /* keep compiler quiet */
+ pfeqop_right = InvalidOid;
+ ffeqop = InvalidOid;
+ }
}
if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
@@ -7225,25 +7323,46 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid target_typeids[2];
input_typeids[0] = pktype;
- input_typeids[1] = fktype;
+ if (fkattelement[i])
+ input_typeids[1] = fk_element_type;
+ else
+ input_typeids[1] = fktype;
target_typeids[0] = opcintype;
target_typeids[1] = opcintype;
if (can_coerce_type(2, input_typeids, target_typeids,
COERCION_IMPLICIT))
{
- pfeqop = ffeqop = ppeqop;
+ pfeqop = ppeqop;
pfeqop_right = opcintype;
++ /*
++ * In case of an array ELEMENT FK, the ffeqop must be left
++ * untouched; otherwise we use the primary equality operator.
++ */
++ if (!fkattelement[i])
++ ffeqop = ppeqop;
}
}
+ /*
+ * In case of an array ELEMENT FK, make sure TYPECACHE_EQ_OPR exists
+ * for the FK element_type and it is compatible with pfeqop
+ */
+ if (fkattelement[i] && OidIsValid(pfeqop))
+ {
+ TypeCacheEntry *typentry = lookup_type_cache(fk_element_type,
+ TYPECACHE_EQ_OPR);
+ if (!OidIsValid(typentry->eq_opr)
+ || !equality_ops_are_compatible(typentry->eq_opr, pfeqop))
+ /* Error: incompatible operators */
+ pfeqop = InvalidOid;
+ }
+
if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("foreign key constraint \"%s\" "
- "cannot be implemented",
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
fkconstraint->conname),
- errdetail("Key columns \"%s\" and \"%s\" "
- "are of incompatible types: %s and %s.",
+ errdetail("Key columns \"%s\" and \"%s\" are of incompatible types: %s and %s.",
strVal(list_nth(fkconstraint->fk_attrs, i)),
strVal(list_nth(fkconstraint->pk_attrs, i)),
format_type_be(fktype),
@@ -7274,8 +7393,16 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
* column types to the right (foreign) operand type of the pfeqop.
* We may assume that pg_constraint.conkey is not changing.
*/
- old_fktype = tab->oldDesc->attrs[fkattnum[i] - 1]->atttypid;
- new_fktype = fktype;
+ if (fkattelement[i])
+ {
+ old_fktype = get_base_element_type(tab->oldDesc->attrs[fkattnum[i] - 1]->atttypid);
+ new_fktype = fk_element_type;
+ }
+ else
+ {
+ old_fktype = tab->oldDesc->attrs[fkattnum[i] - 1]->atttypid;
+ new_fktype = fktype;
+ }
old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
&old_castfunc);
new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
@@ -7345,6 +7472,8 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
ppeqoperators,
ffeqoperators,
numpks,
+ fkconstraint->fk_is_element,
+ fkattelement,
fkconstraint->fk_upd_action,
fkconstraint->fk_del_action,
fkconstraint->fk_matchtype,
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-06-26 16:44 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Alexander Korotkov @ 2017-06-26 16:44 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Mon, Jun 26, 2017 at 2:26 AM, Mark Rofail <markm.rofail@gmail.com> wrote:
> *What I did:*
>
>
>
> - read into the old patch but couldn't apply it since it's quite old.
> It needs to be rebased and that's what I am working on. It's a lot of
> work.
> - incomplete patch can be found attached here
>
> Have you met any particular problem here? Or is it just a lot of
mechanical work?
*Bugs*
>
> - problem with the @>(anyarray, anyelement) opertator: if for example,
> you apply the operator as follows '{AAAAAAAAAA646'}' @> 'AAAAAAAAAA646' it
> maps to @>(anyarray, anyarray) since 'AAAAAAAAAA646' is interpreted as
> char[] instead of Text
>
> I don't think it is bug. When types are not specified explicitly, then
optimizer do its best on guessing them. Sometimes results are
counterintuitive to user. But that is not bug, it's probably a room for
improvement. And I don't think this improvement should be subject of this
GSoC. Anyway, array FK code should use explicit type cast, and then you
wouldn't meet this problem.
On the other hand, you could just choose another operator name for
arraycontainselem.
Then such problem probably wouldn't occur.
*Suggestion:*
>
> - since I needed to check if the Datum was null and its type, I had to
> do it in the arraycontainselem and pass it as a parameter to the underlying
> function array_contains_elem. I'm proposing to introduce a new struct like
> ArrayType, but ElementType along all with brand new MACROs to make dealing
> with anyelement easier in any polymorphic context.
>
> You don't need to do explicit check for nulls, because arraycontainselem
is marked as strict function. Executor never pass null inputs to your
function if its declared as strict. See evaluate_function().
Also, during query planning it's checked that all polymorphic are
consistent between each other. See
https://www.postgresql.org/docs/devel/static/extend-type-system.html#extend-types-polymorphic
and check_generic_type_consistency() for details.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
@ 2017-06-28 21:49 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-06-28 21:49 UTC (permalink / raw)
To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Mon, Jun 26, 2017 at 6:44 PM, Alexander Korotkov <aekorotkov@gmail.com>
wrote:
> Have you met any particular problem here? Or is it just a lot of
> mechanical work?
>
Just A LOT of mechanictal work, thankfully. The patch is now rebased and
all regress tests have passed (even the element_foreign_key). Please find
the patch below !
I don't think it is bug.
>
That's good news !
*What I plan to do next *
- study ri_triggers.c (src/backend/utils/adt/ri_triggers.c) since this
is where the new RI code will reside
Best Regards,
Mark Rofail
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
Array-ELEMENT-foreign-key-v2-REBASED-f0256c7.patch (text/x-patch, 109.0K) download
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ea655a10a8..5c32a4f20a 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2299,6 +2299,14 @@ SCRAM-SHA-256$<replaceable><iteration count></>:<replaceable><salt><
</row>
<row>
+ <entry><structfield>confiselement</structfield></entry>
+ <entry><type>bool</type></entry>
+ <entry></entry>
+ <entry>If a foreign key, is it an array <literal>ELEMENT</literal>
+ foreign key?</entry>
+ </row>
+
+ <row>
<entry><structfield>connoinherit</structfield></entry>
<entry><type>bool</type></entry>
<entry></entry>
@@ -2324,6 +2332,18 @@ SCRAM-SHA-256$<replaceable><iteration count></>:<replaceable><salt><
</row>
<row>
+ <entry><structfield>confelement</structfield></entry>
+ <entry><type>bool[]</type></entry>
+ <entry></entry>
+ <entry>
+ If a foreign key, list of booleans expressing which columns
+ are array <literal>ELEMENT</literal> columns; see
+ <xref linkend="sql-createtable-element-foreign-key-constraints">
+ for details
+ </entry>
+ </row>
+
+ <row>
<entry><structfield>conpfeqop</structfield></entry>
<entry><type>oid[]</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</></entry>
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index b05a9c2150..c1c847bc7e 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -881,7 +881,112 @@ CREATE TABLE order_items (
<xref linkend="sql-createtable">.
</para>
</sect2>
-
+
+ <sect2 id="ddl-constraints-element-fk">
+ <title>Array ELEMENT Foreign Keys</title>
+
+ <indexterm>
+ <primary>ELEMENT foreign key</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>Array ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>referential integrity</primary>
+ </indexterm>
+
+ <para>
+ Another option you have with foreign keys is to use a
+ referencing column which is an array of elements with
+ the same type (or a compatible one) as the referenced
+ column in the related table. This feature is called
+ <firstterm>array element foreign key</firstterm> and is implemented
+ in PostgreSQL with <firstterm>ELEMENT foreign key constraints</firstterm>,
+ as described in the following example:
+
+ <programlisting>
+ CREATE TABLE drivers (
+ driver_id integer PRIMARY KEY,
+ first_name text,
+ last_name text,
+ ...
+ );
+
+ CREATE TABLE races (
+ race_id integer PRIMARY KEY,
+ title text,
+ race_day DATE,
+ ...
+ final_positions integer[] <emphasis>ELEMENT REFERENCES drivers</emphasis>
+ );
+ </programlisting>
+
+ The above example uses an array (<literal>final_positions</literal>)
+ to store the results of a race: for each of its elements
+ a referential integrity check is enforced on the
+ <literal>drivers</literal> table.
+ Note that <literal>ELEMENT REFERENCES</literal> is an extension
+ of PostgreSQL and it is not included in the SQL standard.
+ </para>
+
+ <para>
+ Even though the most common use case for array <literal>ELEMENT</literal>
+ foreign keys is on a single column key, you can define an <quote>array
+ <literal>ELEMENT</literal> foreign key constraint</quote> on a group
+ of columns. As the following example shows, it must be written in table
+ constraint form:
+
+ <programlisting>
+ CREATE TABLE available_moves (
+ kind text,
+ move text,
+ description text,
+ PRIMARY KEY (kind, move)
+ );
+
+ CREATE TABLE paths (
+ description text,
+ kind text,
+ moves text[],
+ <emphasis>FOREIGN KEY (kind, ELEMENT moves) REFERENCES available_moves (kind, move)</emphasis>
+ );
+
+ INSERT INTO available_moves VALUES ('relative', 'LN', 'look north');
+ INSERT INTO available_moves VALUES ('relative', 'RL', 'rotate left');
+ INSERT INTO available_moves VALUES ('relative', 'RR', 'rotate right');
+ INSERT INTO available_moves VALUES ('relative', 'MF', 'move forward');
+ INSERT INTO available_moves VALUES ('absolute', 'N', 'move north');
+ INSERT INTO available_moves VALUES ('absolute', 'S', 'move south');
+ INSERT INTO available_moves VALUES ('absolute', 'E', 'move east');
+ INSERT INTO available_moves VALUES ('absolute', 'W', 'move west');
+
+ INSERT INTO paths VALUES ('L-shaped path', 'relative', '{LN, RL, MF, RR, MF, MF}');
+ INSERT INTO paths VALUES ('L-shaped path', 'absolute', '{W, N, N}');
+ </programlisting>
+
+ On top of standard foreign key requirements,
+ array <literal>ELEMENT</literal> foreign key constraints
+ require that the referencing column is an array of a compatible
+ type of the corresponding referenced column.
+ </para>
+
+ <para>
+ For more detailed information on array <literal>ELEMENT</literal>
+ foreign key options and special cases, please refer to the documentation
+ for <xref linkend="sql-createtable-foreign-key"> and
+ <xref linkend="sql-createtable-element-foreign-key-constraints">.
+ </para>
+
+ </sect2>
+
<sect2 id="ddl-constraints-exclusion">
<title>Exclusion Constraints</title>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index b15c19d3d0..229876d735 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -65,7 +65,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ] |
UNIQUE <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="PARAMETER">index_parameters</replaceable> |
- REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
+ [ELEMENT] REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -76,7 +76,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
UNIQUE ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ] |
- FOREIGN KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> [, ... ] ) ]
+ FOREIGN KEY ( [ELEMENT] <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> [, ... ] ) ]
[ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -779,10 +779,11 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</listitem>
</varlistentry>
- <varlistentry>
+ <varlistentry id="sql-createtable-foreign-key" xreflabel="FOREIGN KEY">
<term><literal>REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
- <term><literal>FOREIGN KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] )
+ <term>
+ <literal>FOREIGN KEY ( [ELEMENT] <replaceable class="parameter">column_name</replaceable> [, ... ] )
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] ) ]
[ MATCH <replaceable class="parameter">matchtype</replaceable> ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ]
@@ -806,6 +807,19 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</para>
<para>
+ In case the column name <replaceable class="parameter">column</replaceable>
+ is prepended with the <literal>ELEMENT</literal> keyword and <replaceable
+ class="parameter">column</replaceable> is an array of elements compatible
+ with the corresponding <replaceable class="parameter">refcolumn</replaceable>
+ in <replaceable class="parameter">reftable</replaceable>, an
+ array <literal>ELEMENT</literal> foreign key constraint is put in place
+ (see <xref linkend="sql-createtable-element-foreign-key-constraints">
+ for more information).
+ Multi-column keys with more than one <literal>ELEMENT</literal> column
+ are currently not allowed.
+ </para>
+
+ <para>
A value inserted into the referencing column(s) is matched against the
values of the referenced table and referenced columns using the
given match type. There are three match types: <literal>MATCH
@@ -868,7 +882,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
<para>
Delete any rows referencing the deleted row, or update the
values of the referencing column(s) to the new values of the
- referenced columns, respectively.
+ referenced columns, respectively. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -877,7 +892,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
<term><literal>SET NULL</literal></term>
<listitem>
<para>
- Set the referencing column(s) to null.
+ Set the referencing column(s) to null. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -889,6 +905,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
Set the referencing column(s) to their default values.
(There must be a row in the referenced table matching the default
values, if they are not null, or the operation will fail.)
+ Currently not supported with array <literal>ELEMENT</literal>
+ foreign keys.
</para>
</listitem>
</varlistentry>
@@ -904,6 +922,61 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</listitem>
</varlistentry>
+ <varlistentry id="sql-createtable-element-foreign-key-constraints" xreflabel="ELEMENT REFERENCES">
+ <term><literal>ELEMENT REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
+
+ <listitem>
+ <para>
+ The <literal>ELEMENT REFERENCES</literal> definition specifies
+ an <quote>array <literal>ELEMENT</literal> foreign key</quote>,
+ a special kind of foreign key
+ constraint requiring the referencing column to be an array of elements
+ of the same type (or a compatible one) as the referenced column
+ in the referenced table. The value of each element of the
+ <replaceable class="parameter">refcolumn</replaceable> array
+ will be matched against some row of <replaceable
+ class="parameter">reftable</replaceable>.
+ </para>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys are an extension
+ of PostgreSQL and are not included in the SQL standard.
+ </para>
+
+ <para>
+ Even with <literal>ELEMENT</literal> foreign keys, modifications
+ in the referenced column can trigger actions to be performed on
+ the referencing array.
+ Similarly to standard foreign keys, you can specify these
+ actions using the <literal>ON DELETE</literal> and
+ <literal>ON UPDATE</literal> clauses.
+ However, only the two following actions for each clause are
+ currently allowed:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>NO ACTION</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints. This is the default action.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RESTRICT</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>DEFERRABLE</literal></term>
<term><literal>NOT DEFERRABLE</literal></term>
@@ -1859,7 +1932,16 @@ CREATE TABLE cities_ab_10000_to_100000
<productname>PostgreSQL</productname> extension.
</para>
</refsect2>
-
+
+ <refsect2>
+ <title id="sql-createtable-foreign-key-arrays">Array <literal>ELEMENT</literal> Foreign Keys</title>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys and the
+ <literal>ELEMENT REFERENCES</literal> clause
+ are a <productname>PostgreSQL</productname> extension.
+ </para>
+ </refsect2>
</refsect1>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index a376b99f1e..a25ccd084c 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2099,7 +2099,9 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr,
NULL,
NULL,
0,
- ' ',
+ false,
+ NULL,
+ ' ',
' ',
' ',
NULL, /* not an exclusion constraint */
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 027abd56b0..a2e8baba55 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1212,6 +1212,8 @@ index_constraint_create(Relation heapRelation,
NULL,
NULL,
0,
+ false,
+ NULL,
' ',
' ',
' ',
diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql
index 98bcfa08c6..0c731b1085 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -1226,7 +1226,9 @@ CREATE VIEW referential_constraints AS
WHEN 'd' THEN 'SET DEFAULT'
WHEN 'r' THEN 'RESTRICT'
WHEN 'a' THEN 'NO ACTION' END
- AS character_data) AS delete_rule
+ AS character_data) AS delete_rule,
+
+ con.confiselement AS is_element
FROM (pg_namespace ncon
INNER JOIN pg_constraint con ON ncon.oid = con.connamespace
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 1336c46d3f..b96294f6a6 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -63,6 +63,8 @@ CreateConstraintEntry(const char *constraintName,
const Oid *ppEqOp,
const Oid *ffEqOp,
int foreignNKeys,
+ bool confisElement,
+ const bool *foreignElement,
char foreignUpdateType,
char foreignDeleteType,
char foreignMatchType,
@@ -82,6 +84,7 @@ CreateConstraintEntry(const char *constraintName,
Datum values[Natts_pg_constraint];
ArrayType *conkeyArray;
ArrayType *confkeyArray;
+ ArrayType *confelementArray;
ArrayType *conpfeqopArray;
ArrayType *conppeqopArray;
ArrayType *conffeqopArray;
@@ -132,10 +135,15 @@ CreateConstraintEntry(const char *constraintName,
fkdatums[i] = ObjectIdGetDatum(ffEqOp[i]);
conffeqopArray = construct_array(fkdatums, foreignNKeys,
OIDOID, sizeof(Oid), true, 'i');
+ for (i = 0; i < foreignNKeys; i++)
+ fkdatums[i] = BoolGetDatum(foreignElement[i]);
+ confelementArray = construct_array(fkdatums, foreignNKeys,
+ BOOLOID, 1, true, 'c');
}
else
{
confkeyArray = NULL;
+ confelementArray = NULL;
conpfeqopArray = NULL;
conppeqopArray = NULL;
conffeqopArray = NULL;
@@ -177,6 +185,7 @@ CreateConstraintEntry(const char *constraintName,
values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
values[Anum_pg_constraint_coninhcount - 1] = Int32GetDatum(conInhCount);
values[Anum_pg_constraint_connoinherit - 1] = BoolGetDatum(conNoInherit);
+ values[Anum_pg_constraint_coniselement - 1] = BoolGetDatum(confisElement);
if (conkeyArray)
values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
@@ -188,6 +197,11 @@ CreateConstraintEntry(const char *constraintName,
else
nulls[Anum_pg_constraint_confkey - 1] = true;
+ if (confelementArray)
+ values[Anum_pg_constraint_confelement - 1] = PointerGetDatum(confelementArray);
+ else
+ nulls[Anum_pg_constraint_confelement - 1] = true;
+
if (conpfeqopArray)
values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
else
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7d9c769b06..25951c89c7 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -41,6 +41,7 @@
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
+#include "catalog/pg_operator.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@@ -6995,6 +6996,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Relation pkrel;
int16 pkattnum[INDEX_MAX_KEYS];
int16 fkattnum[INDEX_MAX_KEYS];
+ bool fkattelement[INDEX_MAX_KEYS];
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
Oid opclasses[INDEX_MAX_KEYS];
@@ -7082,6 +7084,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
*/
MemSet(pkattnum, 0, sizeof(pkattnum));
MemSet(fkattnum, 0, sizeof(fkattnum));
+ MemSet(fkattelement, 0, sizeof(fkattelement));
MemSet(pktypoid, 0, sizeof(pktypoid));
MemSet(fktypoid, 0, sizeof(fktypoid));
MemSet(opclasses, 0, sizeof(opclasses));
@@ -7094,6 +7097,39 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
fkattnum, fktypoid);
/*
+ * If an array ELEMENT FK, decode the content of
+ * the fk_element_attrs array.
+ */
+ if (fkconstraint->fk_is_element)
+ {
+ ListCell *l;
+ int attnum;
+ bool element_found = false;
+
+ attnum = 0;
+ foreach(l, fkconstraint->fk_element_attrs)
+ {
+ if (lfirst_int(l)) {
+
+ /*
+ * Currently, the ELEMENT flag cannot be set on more than
+ * one column.
+ */
+ if (element_found) {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("array ELEMENT foreign keys support only one ELEMENT column")));
+ }
+
+ fkattelement[attnum] = true;
+ element_found = true;
+ }
+ attnum++;
+ }
+
+ }
+
+ /*
* If the attribute list for the referenced table was omitted, lookup the
* definition of the primary key and use it. Otherwise, validate the
* supplied attribute list. In either case, discover the index OID and
@@ -7140,6 +7176,22 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
*/
old_check_ok = (fkconstraint->old_conpfeqop != NIL);
Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
+
+ /* Enforce array ELEMENT foreign key restrictions */
+ if (fkconstraint->fk_is_element)
+ {
+ /*
+ * Array ELEMENT foreign keys support only NO ACTION and
+ * RESTRICT actions
+ */
+ if ((fkconstraint->fk_upd_action != FKCONSTR_ACTION_NOACTION
+ && fkconstraint->fk_upd_action != FKCONSTR_ACTION_RESTRICT)
+ || (fkconstraint->fk_del_action != FKCONSTR_ACTION_NOACTION
+ && fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("array ELEMENT foreign keys only support NO ACTION and RESTRICT actions")));
+ }
for (i = 0; i < numpks; i++)
{
@@ -7156,6 +7208,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid ffeqop;
int16 eqstrategy;
Oid pfeqop_right;
+ Oid fk_element_type = InvalidOid;
/* We need several fields out of the pg_opclass entry */
cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
@@ -7189,26 +7242,51 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
eqstrategy, opcintype, opcintype, opfamily);
- /*
- * Are there equality operators that take exactly the FK type? Assume
- * we should look through any domain here.
- */
- fktyped = getBaseType(fktype);
-
- pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
- eqstrategy);
- if (OidIsValid(pfeqop))
- {
- pfeqop_right = fktyped;
- ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
- eqstrategy);
- }
- else
- {
- /* keep compiler quiet */
- pfeqop_right = InvalidOid;
- ffeqop = InvalidOid;
- }
+ if (fkattelement[i])
+ {
+ /*
+ * For every array ELEMENT FK, look if an equality operator that
+ * takes exactly the FK element type exists. Assume we should
+ * look through any domain here.
+ */
+ fk_element_type = get_base_element_type(fktype);
+ if (!OidIsValid(fk_element_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has type %s which is not an array type.",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktype))));
+
+ pfeqop = get_opfamily_member(opfamily, opcintype, fk_element_type,
+ eqstrategy);
+ pfeqop_right = fk_element_type;
+ ffeqop = ARRAY_EQ_OP;
+ }
+ else
+ {
+ /*
+ * Are there equality operators that take exactly the FK type?
+ * Assume we should look through any domain here.
+ */
+ fktyped = getBaseType(fktype);
+
+ pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
+ eqstrategy);
+ if (OidIsValid(pfeqop))
+ {
+ pfeqop_right = fktyped;
+ ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
+ eqstrategy);
+ }
+ else
+ {
+ /* keep compiler quiet */
+ pfeqop_right = InvalidOid;
+ ffeqop = InvalidOid;
+ }
+ }
if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
{
@@ -7225,25 +7303,46 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid target_typeids[2];
input_typeids[0] = pktype;
- input_typeids[1] = fktype;
+ if (fkattelement[i])
+ input_typeids[1] = fk_element_type;
+ else
+ input_typeids[1] = fktype;
target_typeids[0] = opcintype;
target_typeids[1] = opcintype;
if (can_coerce_type(2, input_typeids, target_typeids,
COERCION_IMPLICIT))
{
- pfeqop = ffeqop = ppeqop;
+ pfeqop = ppeqop;
pfeqop_right = opcintype;
+ /*
+ * In case of an array ELEMENT FK, the ffeqop must be left
+ * untouched; otherwise we use the primary equality operator.
+ */
+ if (!fkattelement[i])
+ ffeqop = ppeqop;
}
}
+ /*
+ * In case of an array ELEMENT FK, make sure TYPECACHE_EQ_OPR exists
+ * for the FK element_type and it is compatible with pfeqop
+ */
+ if (fkattelement[i] && OidIsValid(pfeqop))
+ {
+ TypeCacheEntry *typentry = lookup_type_cache(fk_element_type,
+ TYPECACHE_EQ_OPR);
+ if (!OidIsValid(typentry->eq_opr)
+ || !equality_ops_are_compatible(typentry->eq_opr, pfeqop))
+ /* Error: incompatible operators */
+ pfeqop = InvalidOid;
+ }
+
if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("foreign key constraint \"%s\" "
- "cannot be implemented",
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
fkconstraint->conname),
- errdetail("Key columns \"%s\" and \"%s\" "
- "are of incompatible types: %s and %s.",
+ errdetail("Key columns \"%s\" and \"%s\" are of incompatible types: %s and %s.",
strVal(list_nth(fkconstraint->fk_attrs, i)),
strVal(list_nth(fkconstraint->pk_attrs, i)),
format_type_be(fktype),
@@ -7274,8 +7373,16 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
* column types to the right (foreign) operand type of the pfeqop.
* We may assume that pg_constraint.conkey is not changing.
*/
- old_fktype = tab->oldDesc->attrs[fkattnum[i] - 1]->atttypid;
- new_fktype = fktype;
+ if (fkattelement[i])
+ {
+ old_fktype = get_base_element_type(tab->oldDesc->attrs[fkattnum[i] - 1]->atttypid);
+ new_fktype = fk_element_type;
+ }
+ else
+ {
+ old_fktype = tab->oldDesc->attrs[fkattnum[i] - 1]->atttypid;
+ new_fktype = fktype;
+ }
old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
&old_castfunc);
new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
@@ -7345,6 +7452,8 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
ppeqoperators,
ffeqoperators,
numpks,
+ fkconstraint->fk_is_element,
+ fkattelement,
fkconstraint->fk_upd_action,
fkconstraint->fk_del_action,
fkconstraint->fk_matchtype,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 45d1f515eb..c6ba754c87 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -603,6 +603,8 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
NULL,
NULL,
0,
+ false,
+ NULL,
' ',
' ',
' ',
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index c2fc59d1aa..16479fa83b 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -3076,6 +3076,8 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
NULL,
NULL,
0,
+ false,
+ NULL,
' ',
' ',
' ',
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 67ac8145a0..0e4361230c 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2847,6 +2847,8 @@ _copyConstraint(const Constraint *from)
COPY_SCALAR_FIELD(fk_upd_action);
COPY_SCALAR_FIELD(fk_del_action);
COPY_NODE_FIELD(old_conpfeqop);
+ COPY_SCALAR_FIELD(fk_is_element);
+ COPY_NODE_FIELD(fk_element_attrs);
COPY_SCALAR_FIELD(old_pktable_oid);
COPY_SCALAR_FIELD(skip_validation);
COPY_SCALAR_FIELD(initially_valid);
@@ -2868,6 +2870,17 @@ _copyDefElem(const DefElem *from)
return newnode;
}
+static ForeignKeyColumnElem *
+_copyForeignKeyColumnElem(const ForeignKeyColumnElem *from)
+{
+ ForeignKeyColumnElem *newnode = makeNode(ForeignKeyColumnElem);
+
+ COPY_NODE_FIELD(name);
+ COPY_SCALAR_FIELD(element);
+
+ return newnode;
+}
+
static LockingClause *
_copyLockingClause(const LockingClause *from)
{
@@ -5456,6 +5469,9 @@ copyObjectImpl(const void *from)
case T_DefElem:
retval = _copyDefElem(from);
break;
+ case T_ForeignKeyColumnElem:
+ retval = _copyForeignKeyColumnElem(from);
+ break;
case T_LockingClause:
retval = _copyLockingClause(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 91d64b7331..c121a0a5cb 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2579,6 +2579,8 @@ _equalConstraint(const Constraint *a, const Constraint *b)
COMPARE_SCALAR_FIELD(fk_upd_action);
COMPARE_SCALAR_FIELD(fk_del_action);
COMPARE_NODE_FIELD(old_conpfeqop);
+ COMPARE_SCALAR_FIELD(fk_is_element);
+ COMPARE_NODE_FIELD(fk_element_attrs);
COMPARE_SCALAR_FIELD(old_pktable_oid);
COMPARE_SCALAR_FIELD(skip_validation);
COMPARE_SCALAR_FIELD(initially_valid);
@@ -2599,6 +2601,16 @@ _equalDefElem(const DefElem *a, const DefElem *b)
}
static bool
+_equalForeignKeyColumnElem(const ForeignKeyColumnElem *a,
+ const ForeignKeyColumnElem *b)
+ {
+ COMPARE_NODE_FIELD(name);
+ COMPARE_SCALAR_FIELD(element);
+
+ return true;
+}
+
+static bool
_equalLockingClause(const LockingClause *a, const LockingClause *b)
{
COMPARE_NODE_FIELD(lockedRels);
@@ -3606,6 +3618,9 @@ equal(const void *a, const void *b)
case T_DefElem:
retval = _equalDefElem(a, b);
break;
+ case T_ForeignKeyColumnElem:
+ retval = _equalForeignKeyColumnElem(a, b);
+ break;
case T_LockingClause:
retval = _equalLockingClause(a, b);
break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 3a23f0bb16..3fa2b399b3 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2726,6 +2726,15 @@ _outTableLikeClause(StringInfo str, const TableLikeClause *node)
}
static void
+_outForeignKeyColumnElem(StringInfo str, const ForeignKeyColumnElem *node)
+{
+ WRITE_NODE_TYPE("FOREIGNKEYCOLUMNELEM");
+
+ WRITE_NODE_FIELD(name);
+ WRITE_BOOL_FIELD(element);
+}
+
+static void
_outLockingClause(StringInfo str, const LockingClause *node)
{
WRITE_NODE_TYPE("LOCKINGCLAUSE");
@@ -3467,6 +3476,8 @@ _outConstraint(StringInfo str, const Constraint *node)
WRITE_CHAR_FIELD(fk_upd_action);
WRITE_CHAR_FIELD(fk_del_action);
WRITE_NODE_FIELD(old_conpfeqop);
+ WRITE_BOOL_FIELD(fk_is_element);
+ WRITE_NODE_FIELD(fk_element_attrs);
WRITE_OID_FIELD(old_pktable_oid);
WRITE_BOOL_FIELD(skip_validation);
WRITE_BOOL_FIELD(initially_valid);
@@ -4175,6 +4186,9 @@ outNode(StringInfo str, const void *obj)
case T_TableLikeClause:
_outTableLikeClause(str, obj);
break;
+ case T_ForeignKeyColumnElem:
+ _outForeignKeyColumnElem(str, obj);
+ break;
case T_LockingClause:
_outLockingClause(str, obj);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 0f3998ff89..16c71ad07d 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -392,7 +392,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
execute_param_clause using_clause returning_clause
opt_enum_val_list enum_val_list table_func_column_list
create_generic_options alter_generic_options
- relation_expr_list dostmt_opt_list
+ relation_expr_list dostmt_opt_list foreignKeyColumnList
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
@@ -466,7 +466,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> def_arg columnElem where_clause where_or_current_clause
a_expr b_expr c_expr AexprConst indirection_el opt_slice_bound
columnref in_expr having_clause func_table xmltable array_expr
- ExclusionWhereClause operator_def_arg
+ ExclusionWhereClause foreignKeyColumnElem operator_def_arg
%type <list> rowsfrom_item rowsfrom_list opt_col_def_list
%type <boolean> opt_ordinality
%type <list> ExclusionConstraintList ExclusionConstraintElem
@@ -622,7 +622,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
DOUBLE_P DROP
- EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
+ EACH ELEMENT ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
EXTENSION EXTERNAL EXTRACT
@@ -766,6 +766,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%left JOIN CROSS LEFT FULL RIGHT INNER_P NATURAL
/* kluge to keep xml_whitespace_option from causing shift/reduce conflicts */
%right PRESERVE STRIP_P
+%nonassoc ELEMENT
%%
@@ -3435,6 +3436,24 @@ ColConstraintElem:
n->fk_matchtype = $4;
n->fk_upd_action = (char) ($5 >> 8);
n->fk_del_action = (char) ($5 & 0xFF);
+ n->fk_is_element = false;
+ n->skip_validation = false;
+ n->initially_valid = true;
+ $$ = (Node *)n;
+ }
+ | ELEMENT REFERENCES qualified_name opt_column_list
+ key_match key_actions
+ {
+ Constraint *n = makeNode(Constraint);
+ n->contype = CONSTR_FOREIGN;
+ n->location = @1;
+ n->pktable = $3;
+ n->fk_attrs = NIL;
+ n->pk_attrs = $4;
+ n->fk_matchtype = $5;
+ n->fk_upd_action = (char) ($6 >> 8);
+ n->fk_del_action = (char) ($6 & 0xFF);
+ n->fk_is_element = true;
n->skip_validation = false;
n->initially_valid = true;
$$ = (Node *)n;
@@ -3625,8 +3644,9 @@ ConstraintElem:
NULL, yyscanner);
$$ = (Node *)n;
}
- | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
- opt_column_list key_match key_actions ConstraintAttributeSpec
+ | FOREIGN KEY '(' foreignKeyColumnList ')' REFERENCES
+ qualified_name opt_column_list key_match key_actions
+ ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_FOREIGN;
@@ -3649,7 +3669,31 @@ ConstraintElem:
opt_no_inherit: NO INHERIT { $$ = TRUE; }
| /* EMPTY */ { $$ = FALSE; }
;
-
+
+foreignKeyColumnList:
+ foreignKeyColumnElem
+ { $$ = list_make1($1); }
+ | foreignKeyColumnList ',' foreignKeyColumnElem
+ { $$ = lappend($1, $3); }
+ ;
+
+foreignKeyColumnElem:
+ ELEMENT ColId
+ {
+ ForeignKeyColumnElem *n = makeNode(ForeignKeyColumnElem);
+ n->name = (Node *) makeString($2);
+ n->element = true;
+ $$ = (Node *) n;
+ }
+ | ColId
+ {
+ ForeignKeyColumnElem *n = makeNode(ForeignKeyColumnElem);
+ n->name = (Node *) makeString($1);
+ n->element = false;
+ $$ = (Node *) n;
+ }
+ ;
+
opt_column_list:
'(' columnList ')' { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; }
@@ -14676,6 +14720,7 @@ unreserved_keyword:
| DOUBLE_P
| DROP
| EACH
+ | ELEMENT
| ENABLE_P
| ENCODING
| ENCRYPTED
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index ee5f3a3a52..128ed3b92a 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -743,6 +743,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
* list of FK constraints to be processed later.
*/
constraint->fk_attrs = list_make1(makeString(column->colname));
+ constraint->fk_element_attrs =
+ list_make1_int(constraint->fk_is_element);
cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
break;
@@ -856,6 +858,33 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
break;
case CONSTR_FOREIGN:
+ /*
+ * Split the content of foreignKeyColumnList
+ * in two separate list. One list of fields
+ * and one list of boolean values.
+ */
+ {
+ ListCell *i;
+ List *old_fk_attrs = constraint->fk_attrs;
+
+ constraint->fk_attrs = NIL;
+ constraint->fk_is_element = false;
+ constraint->fk_element_attrs = NIL;
+ foreach (i, old_fk_attrs)
+ {
+ ForeignKeyColumnElem *elem =
+ (ForeignKeyColumnElem *)lfirst(i);
+
+ Assert(IsA(elem, ForeignKeyColumnElem));
+ constraint->fk_attrs =
+ lappend(constraint->fk_attrs, elem->name);
+ constraint->fk_is_element |= elem->element;
+ constraint->fk_element_attrs =
+ lappend_int(constraint->fk_element_attrs,
+ elem->element);
+ }
+ }
+
if (cxt->isforeign)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index c2891e6fa1..c09277ef3a 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -115,12 +115,15 @@ typedef struct RI_ConstraintInfo
NameData conname; /* name of the FK constraint */
Oid pk_relid; /* referenced relation */
Oid fk_relid; /* referencing relation */
+ bool confiselement; /* is an array ELEMENT FK */
char confupdtype; /* foreign key's ON UPDATE action */
char confdeltype; /* foreign key's ON DELETE action */
char confmatchtype; /* foreign key's match type */
int nkeys; /* number of key columns */
int16 pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */
int16 fk_attnums[RI_MAX_NUMKEYS]; /* attnums of referencing cols */
+ bool fk_element_atts[RI_MAX_NUMKEYS]; /* referencing cols is
+ * an array ELEMENT FK */
Oid pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = FK) */
Oid pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = PK) */
Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK = FK) */
@@ -204,7 +207,8 @@ static void ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype);
+ const char *rightop, Oid rightoptype,
+ bool is_array);
static void ri_add_cast_to(StringInfo buf, Oid typid);
static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
static int ri_NullCheck(HeapTuple tup,
@@ -395,6 +399,7 @@ RI_FKey_check(TriggerData *trigdata)
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
StringInfoData querybuf;
+ StringInfoData countbuf;
char pkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
char paramname[16];
@@ -407,12 +412,24 @@ RI_FKey_check(TriggerData *trigdata)
* FOR KEY SHARE OF x
* The type id's for the $ parameters are those of the
* corresponding FK attributes.
+ *
+ * In case of an array ELEMENT foreign key, the previous query is used
+ * to count the number of matching rows and see if every combination
+ * is actually referenced.
+ * The wrapping query is
+ * SELECT 1 WHERE 1 *
+ * (SELECT count(DISTINCT y) FROM UNNEST($1) y WHERE y IS NOT NULL)
+ * [ * ...] = (SELECT count(*) FROM (<QUERY>) z)
* ----------
*/
initStringInfo(&querybuf);
quoteRelationName(pkrelname, pk_rel);
appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
querysep = "WHERE";
+ if (riinfo->confiselement) {
+ initStringInfo(&countbuf);
+ appendStringInfo(&countbuf, "SELECT 1 WHERE 1");
+ }
for (i = 0; i < riinfo->nkeys; i++)
{
Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
@@ -421,18 +438,40 @@ RI_FKey_check(TriggerData *trigdata)
quoteOneName(attname,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
sprintf(paramname, "$%d", i + 1);
+ /*
+ * In case of an array ELEMENT foreign key, we check that every
+ * DISTINCT NOT NULL value in the array is present in the PK
+ * table.
+ */
+ if (riinfo->fk_element_atts[i])
+ {
+ appendStringInfo(&countbuf,
+ " * (SELECT count(DISTINCT y) FROM UNNEST(%s) y WHERE y IS NOT NULL)",
+ paramname);
+ }
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pf_eq_oprs[i],
- paramname, fk_type);
+ paramname, fk_type,
+ riinfo->fk_element_atts[i]);
querysep = "AND";
queryoids[i] = fk_type;
}
appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
- /* Prepare and save the plan */
- qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
- &qkey, fk_rel, pk_rel, true);
+ if (riinfo->confiselement) {
+ appendStringInfo(&countbuf,
+ " = (SELECT count(*) FROM (%s) z)",
+ querybuf.data);
+
+ /* Prepare and save the plan for array ELEMENT foreign keys */
+ qplan = ri_PlanCheck(countbuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
+ }
+ else
+ /* Prepare and save the plan */
+ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
}
/*
@@ -559,7 +598,8 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pp_eq_oprs[i],
- paramname, pk_type);
+ paramname, pk_type,
+ false);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -751,7 +791,8 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_element_atts[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -974,7 +1015,8 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_element_atts[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1130,7 +1172,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_element_atts[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1309,7 +1352,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ false);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1475,7 +1519,8 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_element_atts[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1651,7 +1696,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_element_atts[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1817,7 +1863,8 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_element_atts[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2008,7 +2055,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_element_atts[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2327,6 +2375,14 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
* For MATCH FULL:
* (fk.keycol1 IS NOT NULL [OR ...])
*
+ * In case of an array ELEMENT column, relname is replaced with the
+ * following subquery:
+ *
+ * SELECT unnest("keycol1") k1, "keycol1" ak1 [, ...]
+ * FROM ONLY "public"."fk"
+ *
+ * where all the columns are renamed in order to prevent name collisions.
+ *
* We attach COLLATE clauses to the operators when comparing columns
* that have different collations.
*----------
@@ -2338,15 +2394,46 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
{
quoteOneName(fkattname,
RIAttName(fk_rel, riinfo->fk_attnums[i]));
- appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
+ if (riinfo->confiselement)
+ if (riinfo->fk_element_atts[i])
+ appendStringInfo(&querybuf, "%sfk.ak%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.k%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
sep = ", ";
}
quoteRelationName(pkrelname, pk_rel);
quoteRelationName(fkrelname, fk_rel);
- appendStringInfo(&querybuf,
- " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
- fkrelname, pkrelname);
+
+ if (riinfo->confiselement)
+ {
+ sep = "";
+ appendStringInfo(&querybuf,
+ " FROM (SELECT ");
+ for (i = 0; i < riinfo->nkeys; i++)
+ {
+ quoteOneName(fkattname,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->fk_element_atts[i])
+ appendStringInfo(&querybuf, "%sunnest(%s) k%d, %s ak%d",
+ sep, fkattname, i + 1, fkattname, i + 1);
+ else
+ appendStringInfo(&querybuf, "%s%s k%d", sep, fkattname,
+ i + 1);
+ sep = ", ";
+ }
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s) fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
+ }
+ else
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
strcpy(pkattname, "pk.");
strcpy(fkattname, "fk.");
@@ -2360,12 +2447,16 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
quoteOneName(pkattname + 3,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
- quoteOneName(fkattname + 3,
+ if (riinfo->confiselement)
+ sprintf(fkattname + 3, "k%d", i + 1);
+ else
+ quoteOneName(fkattname + 3,
RIAttName(fk_rel, riinfo->fk_attnums[i]));
ri_GenerateQual(&querybuf, sep,
pkattname, pk_type,
riinfo->pf_eq_oprs[i],
- fkattname, fk_type);
+ fkattname, fk_type,
+ false);
if (pk_coll != fk_coll)
ri_GenerateQualCollation(&querybuf, pk_coll);
sep = "AND";
@@ -2381,7 +2472,10 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
sep = "";
for (i = 0; i < riinfo->nkeys; i++)
{
- quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->confiselement)
+ sprintf(fkattname, "k%d", i + 1);
+ else
+ quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
appendStringInfo(&querybuf,
"%sfk.%s IS NOT NULL",
sep, fkattname);
@@ -2570,7 +2664,8 @@ ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype)
+ const char *rightop, Oid rightoptype,
+ bool is_array)
{
HeapTuple opertup;
Form_pg_operator operform;
@@ -2591,9 +2686,23 @@ ri_GenerateQual(StringInfo buf,
ri_add_cast_to(buf, operform->oprleft);
appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
appendStringInfoString(buf, oprname);
- appendStringInfo(buf, ") %s", rightop);
- if (rightoptype != operform->oprright)
- ri_add_cast_to(buf, operform->oprright);
+ /*
+ * If rightoptype is an array of leftoptype check equality using ANY().
+ * Needed for array support in foreign keys.
+ */
+ if (is_array)
+ {
+ appendStringInfo(buf, ") ANY (%s", rightop);
+ if (rightoptype != get_array_type (operform->oprright))
+ ri_add_cast_to(buf, get_array_type (operform->oprright));
+ appendStringInfo(buf, ")");
+ }
+ else
+ {
+ appendStringInfo(buf, ") %s", rightop);
+ if (rightoptype != operform->oprright)
+ ri_add_cast_to(buf, operform->oprright);
+ }
ReleaseSysCache(opertup);
}
@@ -2841,6 +2950,7 @@ ri_LoadConstraintInfo(Oid constraintOid)
riinfo->confupdtype = conForm->confupdtype;
riinfo->confdeltype = conForm->confdeltype;
riinfo->confmatchtype = conForm->confmatchtype;
+ riinfo->confiselement = conForm->confiselement;
/*
* We expect the arrays to be 1-D arrays of the right types; verify that.
@@ -2878,6 +2988,23 @@ ri_LoadConstraintInfo(Oid constraintOid)
if ((Pointer) arr != DatumGetPointer(adatum))
pfree(arr); /* free de-toasted copy, if any */
+ adatum = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confelement, &isNull);
+ if (isNull)
+ elog(ERROR, "null confelement for constraint %u", constraintOid);
+ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
+ numkeys = ARR_DIMS(arr)[0];
+ if (ARR_NDIM(arr) != 1 ||
+ numkeys != riinfo->nkeys ||
+ numkeys > RI_MAX_NUMKEYS ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != BOOLOID)
+ elog(ERROR, "confelement is not a 1-D boolean array");
+ memcpy(riinfo->fk_element_atts, ARR_DATA_PTR(arr),
+ numkeys * sizeof(int16));
+ if ((Pointer) arr != DatumGetPointer(adatum))
+ pfree(arr); /* free de-toasted copy, if any */
+
adatum = SysCacheGetAttr(CONSTROID, tup,
Anum_pg_constraint_conpfeqop, &isNull);
if (isNull)
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 18d9e27d1e..6f0368a523 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -314,6 +314,9 @@ static char *pg_get_viewdef_worker(Oid viewoid,
static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
static void decompile_column_index_array(Datum column_index_array, Oid relId,
StringInfo buf);
+static void decompile_fk_column_index_array(Datum column_index_array,
+ Datum element_array,
+ Oid relId, StringInfo buf);
static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
const Oid *excludeOps,
@@ -1876,6 +1879,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
case CONSTRAINT_FOREIGN:
{
Datum val;
+ Datum element;
bool isnull;
const char *string;
@@ -1888,11 +1892,20 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
if (isnull)
elog(ERROR, "null conkey for constraint %u",
constraintId);
-
- decompile_column_index_array(val, conForm->conrelid, &buf);
+ element = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confelement,
+ &isnull);
+ if (isnull)
+ elog(ERROR, "null confelement for constraint %u",
+ constraintId);
+
+ decompile_fk_column_index_array(val, element,
+ conForm->conrelid, &buf);
+
+ appendStringInfo(&buf, ") REFERENCES ");
/* add foreign relation name */
- appendStringInfo(&buf, ") REFERENCES %s(",
+ appendStringInfo(&buf, "%s(",
generate_relation_name(conForm->confrelid,
NIL));
@@ -2179,6 +2192,54 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
}
+/*
+ * Convert an int16[] Datum and an bool[] Datum into a comma-separated
+ * list of column names for the indicated relation prefixed by
+ * an optional ELEMENT keyword; append the list to buf.
+ *
+ * The two arrays must have the same cardinality.
+ */
+static void
+decompile_fk_column_index_array(Datum column_index_array,
+ Datum element_array,
+ Oid relId, StringInfo buf)
+{
+ Datum *keys;
+ int nKeys;
+ Datum *bools;
+ int nBools;
+ int j;
+
+ /* Extract data from array of int16 */
+ deconstruct_array(DatumGetArrayTypeP(column_index_array),
+ INT2OID, 2, true, 's',
+ &keys, NULL, &nKeys);
+
+ /* Extract data from array of bool */
+ deconstruct_array(DatumGetArrayTypeP(element_array),
+ BOOLOID, 1, true, 'c',
+ &bools, NULL, &nBools);
+
+ if (nKeys != nBools)
+ elog(ERROR, "wrong confelement cardinality");
+
+ for (j = 0; j < nKeys; j++)
+ {
+ char *colName;
+ char *element;
+
+ colName = get_relid_attribute_name(relId, DatumGetInt16(keys[j]));
+ element = DatumGetBool(bools[j])?"ELEMENT ":"";
+
+ if (j == 0)
+ appendStringInfo(buf, "%s%s", element,
+ quote_identifier(colName));
+ else
+ appendStringInfo(buf, ", %s%s", element,
+ quote_identifier(colName));
+ }
+}
+
/* ----------
* get_expr - Decompile an expression tree
*
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index ec035d8434..177254c217 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -90,6 +90,9 @@ CATALOG(pg_constraint,2606)
/* Has a local definition and cannot be inherited */
bool connoinherit;
+ /* true if an array ELEMENT foreign key */
+ bool confiselement;
+
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/*
@@ -104,6 +107,12 @@ CATALOG(pg_constraint,2606)
int16 confkey[1];
/*
+ * If a foreign key, true if array ELEMENT foreign key for each column of
+ * the constraint
+ */
+ bool confelement[1];
+
+ /*
* If a foreign key, the OIDs of the PK = FK equality operators for each
* column of the constraint
*/
@@ -150,7 +159,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* compiler constants for pg_constraint
* ----------------
*/
-#define Natts_pg_constraint 24
+#define Natts_pg_constraint 26
#define Anum_pg_constraint_conname 1
#define Anum_pg_constraint_connamespace 2
#define Anum_pg_constraint_contype 3
@@ -167,14 +176,16 @@ typedef FormData_pg_constraint *Form_pg_constraint;
#define Anum_pg_constraint_conislocal 14
#define Anum_pg_constraint_coninhcount 15
#define Anum_pg_constraint_connoinherit 16
-#define Anum_pg_constraint_conkey 17
-#define Anum_pg_constraint_confkey 18
-#define Anum_pg_constraint_conpfeqop 19
-#define Anum_pg_constraint_conppeqop 20
-#define Anum_pg_constraint_conffeqop 21
-#define Anum_pg_constraint_conexclop 22
-#define Anum_pg_constraint_conbin 23
-#define Anum_pg_constraint_consrc 24
+#define Anum_pg_constraint_coniselement 17
+#define Anum_pg_constraint_conkey 18
+#define Anum_pg_constraint_confkey 19
+#define Anum_pg_constraint_confelement 20
+#define Anum_pg_constraint_conpfeqop 21
+#define Anum_pg_constraint_conppeqop 22
+#define Anum_pg_constraint_conffeqop 23
+#define Anum_pg_constraint_conexclop 24
+#define Anum_pg_constraint_conbin 25
+#define Anum_pg_constraint_consrc 26
/* ----------------
* initial contents of pg_constraint
diff --git a/src/include/catalog/pg_constraint_fn.h b/src/include/catalog/pg_constraint_fn.h
index a4c46897ed..4aa074f9c3 100644
--- a/src/include/catalog/pg_constraint_fn.h
+++ b/src/include/catalog/pg_constraint_fn.h
@@ -44,6 +44,8 @@ extern Oid CreateConstraintEntry(const char *constraintName,
const Oid *ppEqOp,
const Oid *ffEqOp,
int foreignNKeys,
+ bool confisElement,
+ const bool *foreignElement,
char foreignUpdateType,
char foreignDeleteType,
char foreignMatchType,
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 01527399b8..6cff3ba4b3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -461,6 +461,7 @@ typedef enum NodeTag
T_InferClause,
T_OnConflictClause,
T_CommonTableExpr,
+ T_ForeignKeyColumnElem,
T_RoleSpec,
T_TriggerTransition,
T_PartitionElem,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 1d96169d34..62843df063 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -723,6 +723,21 @@ typedef struct DefElem
} DefElem;
/*
+ * ForeignKeyColumnElem - foreign key column (used in foreign key
+ * constraint)
+ *
+ * For a foreign key attribute, 'name' is the name of the table column to
+ * index, and element is true if it is an array ELEMENT fk.
+*/
+typedef struct ForeignKeyColumnElem
+{
+ NodeTag type;
+ Node *name; /* name of the column, or NULL */
+ bool element; /* true if an array ELEMENT foreign key */
+
+} ForeignKeyColumnElem;
+
+ /*
* LockingClause - raw representation of FOR [NO KEY] UPDATE/[KEY] SHARE
* options
*
@@ -2098,6 +2113,8 @@ typedef struct Constraint
char fk_upd_action; /* ON UPDATE action */
char fk_del_action; /* ON DELETE action */
List *old_conpfeqop; /* pg_constraint.conpfeqop of my former self */
+ bool fk_is_element; /* is array ELEMENT foreign key? */
+ List *fk_element_attrs; /* array ELEMENT FK attrs */
Oid old_pktable_oid; /* pg_constraint.confrelid of my former
* self */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f50e45e886..d3f4803006 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -140,6 +140,7 @@ PG_KEYWORD("domain", DOMAIN_P, UNRESERVED_KEYWORD)
PG_KEYWORD("double", DOUBLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD)
PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD)
+PG_KEYWORD("element", ELEMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("else", ELSE, RESERVED_KEYWORD)
PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD)
diff --git a/src/test/regress/expected/element_foreign_key.out b/src/test/regress/expected/element_foreign_key.out
new file mode 100644
index 0000000000..bfe9ebe20f
--- /dev/null
+++ b/src/test/regress/expected/element_foreign_key.out
@@ -0,0 +1,591 @@
+-- ELEMENT FK CONSTRAINTS
+--
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (ELEMENT ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (ELEMENT ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (ELEMENT ftest1) REFERENCES PKTABLEFORARRAY;
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fkarray"
+DETAIL: Key (ftest1)=({10,1}) is not present in table "pktableforarray".
+DROP TABLE FKTABLEFORARRAY;
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][] ELEMENT REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL ELEMENT REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4,6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL,4,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+ERROR: insert or update on table "fktableforarraymdim" violates foreign key constraint "fktableforarraymdim_ftest1_fkey"
+DETAIL: Key (ftest1)=({{1,2},{6,NULL}}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+ERROR: null value in column "ftest1" violates not-null constraint
+DETAIL: Failing row contains (null, 21).
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+ {1} | 4
+(11 rows)
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+ERROR: array ELEMENT foreign keys only support NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[] ELEMENT REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({D}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({A,B,D}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(A) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(B) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[] ELEMENT REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using FLOAT keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float PRIMARY KEY, ptest2 text );
+-- FAILS because equality operator are incompatible
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY, ftest2 int );
+ERROR: foreign key constraint "fktableforarray_ftest1_fkey" cannot be implemented
+DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: integer[] and double precision.
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, ELEMENT fid2) REFERENCES PKTABLEFORARRAY);
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(A, {A,B,C}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(B, {A}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Test ELEMENT foreign keys with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[] ELEMENT REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,99)"}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,1)","(2010,1)"}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2010,99)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2011,1)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+-- Check FK with cast
+CREATE TABLE PKTABLEFORARRAY (c smallint PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (c int[] ELEMENT REFERENCES PKTABLEFORARRAY);
+INSERT INTO PKTABLEFORARRAY VALUES (1), (2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,2}');
+UPDATE PKTABLEFORARRAY SET c = 3 WHERE c = 2;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_c_fkey" on table "fktableforarray"
+DETAIL: Key (c)=(2) is still referenced from table "fktableforarray".
+DELETE FROM PKTABLEFORARRAY WHERE c = 1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_c_fkey" on table "fktableforarray"
+DETAIL: Key (c)=(1) is still referenced from table "fktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({0,1}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({2,1}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, ELEMENT y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(2, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(20, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, ELEMENT y) REFERENCES DIM1(x, y);
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (ELEMENT x, ELEMENT y) REFERENCES DIM1(x, y)
+);
+ERROR: array ELEMENT foreign keys support only one ELEMENT column
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (ELEMENT x, ELEMENT y) REFERENCES DIM1(x, y);
+ERROR: array ELEMENT foreign keys support only one ELEMENT column
+DROP TABLE F1;
+-- Cleanup
+DROP TABLE DIM1;
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(ELEMENT x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "x2_x1_fkey"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(ELEMENT x1, x2) REFERENCES x1; -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "fk_const"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+DROP TABLE x1;
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3] ELEMENT REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{100,100,100},{NULL,NULL,20},{7,8,10}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (ELEMENT SLOTS) REFERENCES DIM1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (ELEMENT SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 23692615f9..72674fc3f2 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -103,7 +103,7 @@ test: publication subscription
# ----------
# Another group of parallel tests
# ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
+test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass element_foreign_key
# ----------
# Another group of parallel tests
# NB: temp.sql does a reconnect which transiently uses 2 connections,
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 5e8b7e94c4..e56038579a 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -141,6 +141,7 @@ test: amutils
test: select_views
test: portals_p2
test: foreign_key
+test: element_foreign_key
test: cluster
test: dependency
test: guc
diff --git a/src/test/regress/sql/element_foreign_key.sql b/src/test/regress/sql/element_foreign_key.sql
new file mode 100644
index 0000000000..103a25b197
--- /dev/null
+++ b/src/test/regress/sql/element_foreign_key.sql
@@ -0,0 +1,452 @@
+-- ELEMENT FK CONSTRAINTS
+--
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (ELEMENT ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (ELEMENT ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (ELEMENT ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][] ELEMENT REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL ELEMENT REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[] ELEMENT REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[] ELEMENT REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using FLOAT keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float PRIMARY KEY, ptest2 text );
+-- FAILS because equality operator are incompatible
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, ELEMENT fid2) REFERENCES PKTABLEFORARRAY);
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Test ELEMENT foreign keys with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[] ELEMENT REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+
+-- Check FK with cast
+CREATE TABLE PKTABLEFORARRAY (c smallint PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (c int[] ELEMENT REFERENCES PKTABLEFORARRAY);
+INSERT INTO PKTABLEFORARRAY VALUES (1), (2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,2}');
+UPDATE PKTABLEFORARRAY SET c = 3 WHERE c = 2;
+DELETE FROM PKTABLEFORARRAY WHERE c = 1;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, ELEMENT y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+
+
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, ELEMENT y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (ELEMENT x, ELEMENT y) REFERENCES DIM1(x, y)
+);
+
+
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (ELEMENT x, ELEMENT y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+-- Cleanup
+DROP TABLE DIM1;
+
+
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(ELEMENT x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(ELEMENT x1, x2) REFERENCES x1; -- FAILS
+DROP TABLE x2;
+DROP TABLE x1;
+
+
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3] ELEMENT REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+DROP TABLE F1;
+
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (ELEMENT SLOTS) REFERENCES DIM1; -- FAILS
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (ELEMENT SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+DROP TABLE F1;
+
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[] ELEMENT REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
\ No newline at end of file
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-03 20:34 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Alvaro Herrera @ 2017-07-03 20:34 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
Mark Rofail wrote:
> On Mon, Jun 26, 2017 at 6:44 PM, Alexander Korotkov <aekorotkov@gmail.com>
> wrote:
>
> > Have you met any particular problem here? Or is it just a lot of
> > mechanical work?
> >
>
> Just A LOT of mechanictal work, thankfully. The patch is now rebased and
> all regress tests have passed (even the element_foreign_key). Please find
> the patch below !
Great!
> *What I plan to do next *
>
> - study ri_triggers.c (src/backend/utils/adt/ri_triggers.c) since this
> is where the new RI code will reside
Any news?
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2017-07-04 01:06 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-07-04 01:06 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
I was unaware that there was a ver3 patch:
https://www.postgresql.org/message-id/28617.1351095467%40sss.pgh.pa.us
I rebased this also (rebased version attached here).
There were considerable changes in syntax between v2 and v3, and different
approaches in the implementations, so I have to restudy ri_triggers.c but
at least the old patch gave me a good idea of what's going on.
As for the *limitations *of the patch:
1. Only one "ELEMENT" column allowed in a multi-column key
- - e.g. FOREIGN KEY (c1, ELEMENT c2, ELEMENT c3) REFERENCES t1 (u1,
u2, u3) will throw an error
2. Supported actions:
- - NO ACTION
- - RESTRICT
3. The use of count(distinct y) in the SQL statements if the referencing
column is an array. Since its equality operator is different from the PK
unique index equality operator this leads to a broken statement
- regression=# create table ff (f1 float8 primary key);
CREATE TABLE
regression=# create table cc (f1 numeric references ff);
CREATE TABLE
regression=# create table cc2 (f1 numeric[], foreign key(each element
of f1) references ff);
ERROR: foreign key constraint "cc2_f1_fkey" cannot be implemented
DETAIL: Key column "f1" has element type numeric which does not have
a default btree operator class that's compatible with class "float8_ops".
4. undesirable dependency on default opclass semantics in the patch,
which is that it supposes it can use array_eq() to detect whether or not
the referencing column has changed. But I think that can be fixed without
undue pain by providing a refactored version of array_eq() that can be told
which element-comparison function to use
5. fatal performance issues. If you issue any UPDATE or DELETE against
the PK table, you get a query like this for checking to see if the RI
constraint would be violated:
SELECT 1 FROM ONLY fktable x WHERE $1 = ANY (fkcol) FOR SHARE OF x;
6. cross-type FKs are unsupported
These are the limitations I gathered from the previous mailing list:
https://www.postgresql.org/message-id/flat/1343842863.5162.4.camel%40greygoo.devise-it.lan#134384286...
I am pretty sure other limitations will arise.
I am confident that between the time the patch was implemented(2012) and
now postgres has grown considerably, the array functions are now more
robust and will help in resolving many issues.
I would like to point out that Limitation #5 is the first limitation we
should eliminate as it deems the feature unbeneficial.
I would like to thank Marco Nenciarini, Gabriele, Gianni and Tom Lane, for
their hard work in the previous patches and anyone else I forgot.
As for limitations for the anyarray @> anyelem operator's *limitations*:
1. since anyarray @< anyarray and anyarray @> anyelem have the same
symbol when a statemnt like this is executed '{AAAAAAAAAA646'}' @>
'AAAAAAAAAA646' it's mapped to anyarray @< anyarray instead of anyarray @>
anyelem
- but as Alexander pointed out
On Mon, Jun 26, 2017 at 6:44 PM, Alexander Korotkov <aekorotkov@gmail.com>
wrote:
> When types are not specified explicitly, then optimizer do its best on
> guessing them. Sometimes results are counterintuitive to user. But that
> is not bug, it's probably a room for improvement. And I don't think this
> improvement should be subject of this GSoC. Anyway, array FK code should
> use explicit type cast, and then you wouldn't meet this problem.
*What I plan to do next: *
>
- located the SQL statements triggered at any insert or update and will
now "convert" them to use GIN. However, NO ACTION and RESTRICT are the
only actions supported right now
so that's how I will spend the next week.
Best Regards,
Mark Rofail
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
Array-ELEMENT-foreign-key-v3-REBASED-42794d6.patch (text/x-patch, 111.7K) download
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ea655a10a8..b4aefd7aa3 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2324,7 +2324,17 @@ SCRAM-SHA-256$<replaceable><iteration count></>:<replaceable><salt><
</row>
<row>
- <entry><structfield>conpfeqop</structfield></entry>
+ <entry><structfield>confreftype</structfield></entry>
+ <entry><type>char[]</type></entry>
+ <entry></entry>
+ <entry>If a foreign key, the reference semantics for each column:
+ <literal>p</> = plain (simple equality),
+ <literal>e</> = each element of referencing array must have a match
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>conpfeqop</structfield></entry>
<entry><type>oid[]</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</></entry>
<entry>If a foreign key, list of the equality operators for PK = FK comparisons</entry>
@@ -2369,6 +2379,12 @@ SCRAM-SHA-256$<replaceable><iteration count></>:<replaceable><salt><
</table>
<para>
+ When <structfield>confreftype</structfield> indicates array-vs-scalar
+ foreign key reference semantics, the equality operators listed in
+ <structfield>conpfeqop</structfield> etc are for the array's element type.
+ </para>
+
+ <para>
In the case of an exclusion constraint, <structfield>conkey</structfield>
is only useful for constraint elements that are simple column references.
For other cases, a zero appears in <structfield>conkey</structfield>
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index b05a9c2150..75da196334 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -882,6 +882,111 @@ CREATE TABLE order_items (
</para>
</sect2>
+ <sect2 id="ddl-constraints-element-fk">
+ <title>Array ELEMENT Foreign Keys</title>
+
+ <indexterm>
+ <primary>ELEMENT foreign key</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>Array ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>referential integrity</primary>
+ </indexterm>
+
+ <para>
+ Another option you have with foreign keys is to use a
+ referencing column which is an array of elements with
+ the same type (or a compatible one) as the referenced
+ column in the related table. This feature is called
+ <firstterm>array element foreign key</firstterm> and is implemented
+ in PostgreSQL with <firstterm>ELEMENT foreign key constraints</firstterm>,
+ as described in the following example:
+
+<programlisting>
+CREATE TABLE drivers (
+ driver_id integer PRIMARY KEY,
+ first_name text,
+ last_name text,
+ ...
+);
+
+CREATE TABLE races (
+ race_id integer PRIMARY KEY,
+ title text,
+ race_day DATE,
+ ...
+ final_positions integer[] <emphasis>ELEMENT REFERENCES drivers</emphasis>
+);
+</programlisting>
+
+ The above example uses an array (<literal>final_positions</literal>)
+ to store the results of a race: for each of its elements
+ a referential integrity check is enforced on the
+ <literal>drivers</literal> table.
+ Note that <literal>ELEMENT REFERENCES</literal> is an extension
+ of PostgreSQL and it is not included in the SQL standard.
+ </para>
+
+ <para>
+ Even though the most common use case for array <literal>ELEMENT</literal>
+ foreign keys is on a single column key, you can define an <quote>array
+ <literal>ELEMENT</literal> foreign key constraint</quote> on a group
+ of columns. As the following example shows, it must be written in table
+ constraint form:
+
+<programlisting>
+CREATE TABLE available_moves (
+ kind text,
+ move text,
+ description text,
+ PRIMARY KEY (kind, move)
+);
+
+CREATE TABLE paths (
+ description text,
+ kind text,
+ moves text[],
+ <emphasis>FOREIGN KEY (kind, ELEMENT moves) REFERENCES available_moves (kind, move)</emphasis>
+);
+
+INSERT INTO available_moves VALUES ('relative', 'LN', 'look north');
+INSERT INTO available_moves VALUES ('relative', 'RL', 'rotate left');
+INSERT INTO available_moves VALUES ('relative', 'RR', 'rotate right');
+INSERT INTO available_moves VALUES ('relative', 'MF', 'move forward');
+INSERT INTO available_moves VALUES ('absolute', 'N', 'move north');
+INSERT INTO available_moves VALUES ('absolute', 'S', 'move south');
+INSERT INTO available_moves VALUES ('absolute', 'E', 'move east');
+INSERT INTO available_moves VALUES ('absolute', 'W', 'move west');
+
+INSERT INTO paths VALUES ('L-shaped path', 'relative', '{LN, RL, MF, RR, MF, MF}');
+INSERT INTO paths VALUES ('L-shaped path', 'absolute', '{W, N, N}');
+</programlisting>
+
+ On top of standard foreign key requirements,
+ array <literal>ELEMENT</literal> foreign key constraints
+ require that the referencing column is an array of a compatible
+ type of the corresponding referenced column.
+ </para>
+
+ <para>
+ For more detailed information on array <literal>ELEMENT</literal>
+ foreign key options and special cases, please refer to the documentation
+ for <xref linkend="sql-createtable-foreign-key"> and
+ <xref linkend="sql-createtable-element-foreign-key-constraints">.
+ </para>
+
+ </sect2>
+
<sect2 id="ddl-constraints-exclusion">
<title>Exclusion Constraints</title>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index b15c19d3d0..c3fe34888e 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -65,7 +65,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ] |
UNIQUE <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="PARAMETER">index_parameters</replaceable> |
- REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
+ [ELEMENT] REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -76,7 +76,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
UNIQUE ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ] |
- FOREIGN KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> [, ... ] ) ]
+ FOREIGN KEY ( [ELEMENT] <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> [, ... ] ) ]
[ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -779,10 +779,10 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</listitem>
</varlistentry>
- <varlistentry>
+ <varlistentry id="sql-createtable-foreign-key" xreflabel="FOREIGN KEY">
<term><literal>REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
- <term><literal>FOREIGN KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] )
+ <term><literal>FOREIGN KEY ( [ELEMENT] <replaceable class="parameter">column_name</replaceable> [, ... ] )
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] ) ]
[ MATCH <replaceable class="parameter">matchtype</replaceable> ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ]
@@ -806,6 +806,19 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</para>
<para>
+ In case the column name <replaceable class="parameter">column</replaceable>
+ is prepended with the <literal>ELEMENT</literal> keyword and <replaceable
+ class="parameter">column</replaceable> is an array of elements compatible
+ with the corresponding <replaceable class="parameter">refcolumn</replaceable>
+ in <replaceable class="parameter">reftable</replaceable>, an
+ array <literal>ELEMENT</literal> foreign key constraint is put in place
+ (see <xref linkend="sql-createtable-element-foreign-key-constraints">
+ for more information).
+ Multi-column keys with more than one <literal>ELEMENT</literal> column
+ are currently not allowed.
+ </para>
+
+ <para>
A value inserted into the referencing column(s) is matched against the
values of the referenced table and referenced columns using the
given match type. There are three match types: <literal>MATCH
@@ -868,7 +881,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
<para>
Delete any rows referencing the deleted row, or update the
values of the referencing column(s) to the new values of the
- referenced columns, respectively.
+ referenced columns, respectively. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -877,7 +891,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
<term><literal>SET NULL</literal></term>
<listitem>
<para>
- Set the referencing column(s) to null.
+ Set the referencing column(s) to null. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -889,6 +904,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
Set the referencing column(s) to their default values.
(There must be a row in the referenced table matching the default
values, if they are not null, or the operation will fail.)
+ Currently not supported with array <literal>ELEMENT</literal>
+ foreign keys.
</para>
</listitem>
</varlistentry>
@@ -905,6 +922,61 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</varlistentry>
<varlistentry>
+ <varlistentry id="sql-createtable-element-foreign-key-constraints" xreflabel="ELEMENT REFERENCES">
+ <term><literal>ELEMENT REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
+
+ <listitem>
+ <para>
+ The <literal>ELEMENT REFERENCES</literal> definition specifies
+ an <quote>array <literal>ELEMENT</literal> foreign key</quote>,
+ a special kind of foreign key
+ constraint requiring the referencing column to be an array of elements
+ of the same type (or a compatible one) as the referenced column
+ in the referenced table. The value of each element of the
+ <replaceable class="parameter">refcolumn</replaceable> array
+ will be matched against some row of <replaceable
+ class="parameter">reftable</replaceable>.
+ </para>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys are an extension
+ of PostgreSQL and are not included in the SQL standard.
+ </para>
+
+ <para>
+ Even with <literal>ELEMENT</literal> foreign keys, modifications
+ in the referenced column can trigger actions to be performed on
+ the referencing array.
+ Similarly to standard foreign keys, you can specify these
+ actions using the <literal>ON DELETE</literal> and
+ <literal>ON UPDATE</literal> clauses.
+ However, only the two following actions for each clause are
+ currently allowed:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>NO ACTION</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints. This is the default action.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RESTRICT</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ </listitem>
+ </varlistentry>
+
<term><literal>DEFERRABLE</literal></term>
<term><literal>NOT DEFERRABLE</literal></term>
<listitem>
@@ -1843,6 +1915,16 @@ CREATE TABLE cities_ab_10000_to_100000
</refsect2>
<refsect2>
+ <title id="sql-createtable-foreign-key-arrays">Array <literal>ELEMENT</literal> Foreign Keys</title>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys and the
+ <literal>ELEMENT REFERENCES</literal> clause
+ are a <productname>PostgreSQL</productname> extension.
+ </para>
+ </refsect2>
+
+ <refsect2>
<title><literal>PARTITION BY</> Clause</title>
<para>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index a376b99f1e..3bd0b772a4 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2098,6 +2098,7 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 027abd56b0..d95ac7d594 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1211,6 +1211,7 @@ index_constraint_create(Relation heapRelation,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 1336c46d3f..c197cec11a 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -59,6 +59,7 @@ CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
@@ -82,6 +83,7 @@ CreateConstraintEntry(const char *constraintName,
Datum values[Natts_pg_constraint];
ArrayType *conkeyArray;
ArrayType *confkeyArray;
+ ArrayType *confreftypeArray;
ArrayType *conpfeqopArray;
ArrayType *conppeqopArray;
ArrayType *conffeqopArray;
@@ -119,7 +121,11 @@ CreateConstraintEntry(const char *constraintName,
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = Int16GetDatum(foreignKey[i]);
confkeyArray = construct_array(fkdatums, foreignNKeys,
- INT2OID, 2, true, 's');
+ INT2OID, sizeof(int16), true, 's');
+ for (i = 0; i < foreignNKeys; i++)
+ fkdatums[i] = CharGetDatum(foreignRefType[i]);
+ confreftypeArray = construct_array(fkdatums, foreignNKeys,
+ CHAROID, sizeof(char), true, 'c');
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
conpfeqopArray = construct_array(fkdatums, foreignNKeys,
@@ -136,6 +142,7 @@ CreateConstraintEntry(const char *constraintName,
else
{
confkeyArray = NULL;
+ confreftypeArray = NULL;
conpfeqopArray = NULL;
conppeqopArray = NULL;
conffeqopArray = NULL;
@@ -188,6 +195,11 @@ CreateConstraintEntry(const char *constraintName,
else
nulls[Anum_pg_constraint_confkey - 1] = true;
+ if (confreftypeArray)
+ values[Anum_pg_constraint_confreftype - 1] = PointerGetDatum(confreftypeArray);
+ else
+ nulls[Anum_pg_constraint_confreftype - 1] = true;
+
if (conpfeqopArray)
values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
else
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index bb00858ad1..dc18fd1eae 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -6995,6 +6995,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Relation pkrel;
int16 pkattnum[INDEX_MAX_KEYS];
int16 fkattnum[INDEX_MAX_KEYS];
+ char fkreftypes[INDEX_MAX_KEYS];
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
Oid opclasses[INDEX_MAX_KEYS];
@@ -7002,10 +7003,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid ppeqoperators[INDEX_MAX_KEYS];
Oid ffeqoperators[INDEX_MAX_KEYS];
int i;
+ ListCell *lc;
int numfks,
numpks;
Oid indexOid;
Oid constrOid;
+ bool has_array;
bool old_check_ok;
ObjectAddress address;
ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
@@ -7082,6 +7085,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
*/
MemSet(pkattnum, 0, sizeof(pkattnum));
MemSet(fkattnum, 0, sizeof(fkattnum));
+ MemSet(fkreftypes, 0, sizeof(fkreftypes));
MemSet(pktypoid, 0, sizeof(pktypoid));
MemSet(fktypoid, 0, sizeof(fktypoid));
MemSet(opclasses, 0, sizeof(opclasses));
@@ -7094,6 +7098,50 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
fkattnum, fktypoid);
/*
+ * Validate the reference semantics codes, too, and convert list to array
+ * format to pass to CreateConstraintEntry.
+ */
+ Assert(list_length(fkconstraint->fk_reftypes) == numfks);
+ has_array = false;
+ i = 0;
+ foreach(lc, fkconstraint->fk_reftypes)
+ {
+ char reftype = lfirst_int(lc);
+
+ switch (reftype)
+ {
+ case FKCONSTR_REF_PLAIN:
+ /* OK, nothing to do */
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ /* At most one FK column can be an array reference */
+ if (has_array)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign keys support only one array column")));
+ has_array = true;
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d", (int) reftype);
+ break;
+ }
+ fkreftypes[i] = reftype;
+ i++;
+ }
+
+ /* Array foreign keys support only NO ACTION and RESTRICT actions */
+ if (has_array)
+ {
+ if ((fkconstraint->fk_upd_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_upd_action != FKCONSTR_ACTION_RESTRICT) ||
+ (fkconstraint->fk_del_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("array foreign keys support only NO ACTION and RESTRICT actions")));
+ }
+
+ /*
* If the attribute list for the referenced table was omitted, lookup the
* definition of the primary key and use it. Otherwise, validate the
* supplied attribute list. In either case, discover the index OID and
@@ -7179,6 +7227,65 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
eqstrategy = BTEqualStrategyNumber;
/*
+ * If this is an array foreign key, we must look up the operators for
+ * the array element type, not the array type itself.
+ */
+ if (fkreftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ Oid elemopclass;
+
+ /* We look through any domain here */
+ fktype = get_base_element_type(fktype);
+ if (!OidIsValid(fktype))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has type %s which is not an array type.",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktypoid[i]))));
+
+ /*
+ * For the moment, we must also insist that the array's element
+ * type have a default btree opclass that is in the index's
+ * opfamily. This is necessary because ri_triggers.c relies on
+ * COUNT(DISTINCT x) on the element type, as well as on array_eq()
+ * on the array type, and we need those operations to have the
+ * same notion of equality that we're using otherwise.
+ *
+ * XXX this restriction is pretty annoying, considering the effort
+ * that's been put into the rest of the RI mechanisms to make them
+ * work with nondefault equality operators. In particular, it
+ * means that the cast-to-PK-datatype code path isn't useful for
+ * array-to-scalar references.
+ */
+ elemopclass = GetDefaultOpClass(fktype, BTREE_AM_OID);
+ if (!OidIsValid(elemopclass) ||
+ get_opclass_family(elemopclass) != opfamily)
+ {
+ /* Get the index opclass's name for the error message. */
+ char *opcname;
+
+ cla_ht = SearchSysCache1(CLAOID,
+ ObjectIdGetDatum(opclasses[i]));
+ if (!HeapTupleIsValid(cla_ht))
+ elog(ERROR, "cache lookup failed for opclass %u",
+ opclasses[i]);
+ cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
+ opcname = pstrdup(NameStr(cla_tup->opcname));
+ ReleaseSysCache(cla_ht);
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has element type %s which does not have a default btree operator class that's compatible with class \"%s\".",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktype),
+ opcname)));
+ }
+ }
+
+ /*
* There had better be a primary equality operator for the index.
* We'll use it for PK = PK comparisons.
*/
@@ -7239,14 +7346,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("foreign key constraint \"%s\" "
- "cannot be implemented",
- fkconstraint->conname),
- errdetail("Key columns \"%s\" and \"%s\" "
- "are of incompatible types: %s and %s.",
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key columns \"%s\" and \"%s\" are of incompatible types: %s and %s.",
strVal(list_nth(fkconstraint->fk_attrs, i)),
strVal(list_nth(fkconstraint->pk_attrs, i)),
- format_type_be(fktype),
+ format_type_be(fktypoid[i]),
format_type_be(pktype))));
if (old_check_ok)
@@ -7275,6 +7380,13 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
* We may assume that pg_constraint.conkey is not changing.
*/
old_fktype = tab->oldDesc->attrs[fkattnum[i] - 1]->atttypid;
+ if (fkreftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ old_fktype = get_base_element_type(old_fktype);
+ /* this shouldn't happen ... */
+ if (!OidIsValid(old_fktype))
+ elog(ERROR, "old foreign key column is not an array");
+ }
new_fktype = fktype;
old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
&old_castfunc);
@@ -7317,7 +7429,6 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
new_castfunc == old_castfunc &&
(!IsPolymorphicType(pfeqop_right) ||
new_fktype == old_fktype));
-
}
pfeqoperators[i] = pfeqop;
@@ -7341,6 +7452,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
indexOid,
RelationGetRelid(pkrel),
pkattnum,
+ fkreftypes,
pfeqoperators,
ppeqoperators,
ffeqoperators,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index b502941b08..cb017cbe14 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -635,6 +635,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
@@ -1006,6 +1007,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
char fk_matchtype = FKCONSTR_MATCH_SIMPLE;
List *fk_attrs = NIL;
List *pk_attrs = NIL;
+ List *fk_reftypes = NIL;
StringInfoData buf;
int funcnum;
OldTriggerInfo *info = NULL;
@@ -1035,7 +1037,10 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
if (i % 2)
fk_attrs = lappend(fk_attrs, arg);
else
- pk_attrs = lappend(pk_attrs, arg);
+ {
+ pk_attrs = lappend(pk_attrs, arg);
+ fk_reftypes = lappend_int(fk_reftypes, FKCONSTR_REF_PLAIN);
+ }
}
/* Prepare description of constraint for use in messages */
@@ -1174,6 +1179,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
fkcon->conname = constr_name;
fkcon->fk_attrs = fk_attrs;
fkcon->pk_attrs = pk_attrs;
+ fkcon->fk_reftypes = fk_reftypes;
fkcon->fk_matchtype = fk_matchtype;
switch (info->funcoids[0])
{
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index c2fc59d1aa..4f089d53a9 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -3075,6 +3075,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 67ac8145a0..e84c226f95 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2843,6 +2843,7 @@ _copyConstraint(const Constraint *from)
COPY_NODE_FIELD(pktable);
COPY_NODE_FIELD(fk_attrs);
COPY_NODE_FIELD(pk_attrs);
+ COPY_NODE_FIELD(fk_reftypes);
COPY_SCALAR_FIELD(fk_matchtype);
COPY_SCALAR_FIELD(fk_upd_action);
COPY_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 91d64b7331..f11017a711 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2575,6 +2575,7 @@ _equalConstraint(const Constraint *a, const Constraint *b)
COMPARE_NODE_FIELD(pktable);
COMPARE_NODE_FIELD(fk_attrs);
COMPARE_NODE_FIELD(pk_attrs);
+ COMPARE_NODE_FIELD(fk_reftypes);
COMPARE_SCALAR_FIELD(fk_matchtype);
COMPARE_SCALAR_FIELD(fk_upd_action);
COMPARE_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 3a23f0bb16..070e50994c 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3463,6 +3463,7 @@ _outConstraint(StringInfo str, const Constraint *node)
WRITE_NODE_FIELD(pktable);
WRITE_NODE_FIELD(fk_attrs);
WRITE_NODE_FIELD(pk_attrs);
+ WRITE_NODE_FIELD(fk_reftypes);
WRITE_CHAR_FIELD(fk_matchtype);
WRITE_CHAR_FIELD(fk_upd_action);
WRITE_CHAR_FIELD(fk_del_action);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 0f3998ff89..6a302b243c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -126,6 +126,13 @@ typedef struct ImportQual
List *table_names;
} ImportQual;
+/* Private struct for the result of foreign_key_column_elem production */
+typedef struct FKColElem
+{
+ Node *name; /* name of the column (a String) */
+ char reftype; /* FKCONSTR_REF_xxx code */
+} FKColElem;
+
/* ConstraintAttributeSpec yields an integer bitmask of these flags: */
#define CAS_NOT_DEFERRABLE 0x01
#define CAS_DEFERRABLE 0x02
@@ -183,6 +190,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
static void SplitColQualList(List *qualList,
List **constraintList, CollateClause **collClause,
core_yyscan_t yyscanner);
+static void SplitFKColElems(List *fkcolelems, List **names, List **reftypes);
static void processCASbits(int cas_bits, int location, const char *constrType,
bool *deferrable, bool *initdeferred, bool *not_valid,
bool *no_inherit, core_yyscan_t yyscanner);
@@ -233,6 +241,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
A_Indices *aind;
ResTarget *target;
struct PrivTarget *privtarget;
+ struct FKColElem *fkcolelem;
AccessPriv *accesspriv;
struct ImportQual *importqual;
InsertStmt *istmt;
@@ -358,6 +367,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <accesspriv> privilege
%type <list> privileges privilege_list
%type <privtarget> privilege_target
+%type <fkcolelem> foreign_key_column_elem
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
%type <ival> defacl_privilege_target
@@ -392,7 +402,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
execute_param_clause using_clause returning_clause
opt_enum_val_list enum_val_list table_func_column_list
create_generic_options alter_generic_options
- relation_expr_list dostmt_opt_list
+ relation_expr_list dostmt_opt_list foreign_key_column_list
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
@@ -622,8 +632,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
DOUBLE_P DROP
- EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
- EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
+ EACH ELEMENT ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE
+ EVENT EXCEPT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
EXTENSION EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FILTER FIRST_P FLOAT_P FOLLOWING FOR
@@ -3427,14 +3437,16 @@ ColConstraintElem:
| REFERENCES qualified_name opt_column_list key_match key_actions
{
Constraint *n = makeNode(Constraint);
- n->contype = CONSTR_FOREIGN;
- n->location = @1;
- n->pktable = $2;
- n->fk_attrs = NIL;
- n->pk_attrs = $3;
- n->fk_matchtype = $4;
- n->fk_upd_action = (char) ($5 >> 8);
- n->fk_del_action = (char) ($5 & 0xFF);
+ n->contype = CONSTR_FOREIGN;
+ n->location = @1;
+ n->pktable = $2;
+ /* fk_attrs will be filled in by parse analysis */
+ n->fk_attrs = NIL;
+ n->pk_attrs = $3;
+ n->fk_reftypes = list_make1_int(FKCONSTR_REF_PLAIN);
+ n->fk_matchtype = $4;
+ n->fk_upd_action = (char) ($5 >> 8);
+ n->fk_del_action = (char) ($5 & 0xFF);
n->skip_validation = false;
n->initially_valid = true;
$$ = (Node *)n;
@@ -3625,14 +3637,15 @@ ConstraintElem:
NULL, yyscanner);
$$ = (Node *)n;
}
- | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
- opt_column_list key_match key_actions ConstraintAttributeSpec
+ | FOREIGN KEY '(' foreign_key_column_list ')' REFERENCES
+ qualified_name opt_column_list key_match key_actions
+ ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_FOREIGN;
n->location = @1;
+ SplitFKColElems($4, &n->fk_attrs, &n->fk_reftypes);
n->pktable = $7;
- n->fk_attrs = $4;
n->pk_attrs = $8;
n->fk_matchtype = $9;
n->fk_upd_action = (char) ($10 >> 8);
@@ -3665,7 +3678,30 @@ columnElem: ColId
$$ = (Node *) makeString($1);
}
;
-
+ foreign_key_column_list:
+ foreign_key_column_elem
+ { $$ = list_make1($1); }
+ | foreign_key_column_list ',' foreign_key_column_elem
+ { $$ = lappend($1, $3); }
+ ;
+
+ foreign_key_column_elem:
+ ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($1);
+ n->reftype = FKCONSTR_REF_PLAIN;
+ $$ = n;
+ }
+ | EACH ELEMENT OF ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($4);
+ n->reftype = FKCONSTR_REF_EACH_ELEMENT;
+ $$ = n;
+ }
+ ;
+
key_match: MATCH FULL
{
$$ = FKCONSTR_MATCH_FULL;
@@ -14676,6 +14712,7 @@ unreserved_keyword:
| DOUBLE_P
| DROP
| EACH
+ | ELEMENT
| ENABLE_P
| ENCODING
| ENCRYPTED
@@ -15793,6 +15830,23 @@ SplitColQualList(List *qualList,
*constraintList = qualList;
}
+/* Split a list of FKColElem structs into separate name and reftype lists */
+static void
+SplitFKColElems(List *fkcolelems, List **names, List **reftypes)
+{
+ ListCell *lc;
+
+ *names = NIL;
+ *reftypes = NIL;
+ foreach(lc, fkcolelems)
+ {
+ FKColElem *fkcolelem = (FKColElem *) lfirst(lc);
+
+ *names = lappend(*names, fkcolelem->name);
+ *reftypes = lappend_int(*reftypes, fkcolelem->reftype);
+ }
+}
+
/*
* Process result of ConstraintAttributeSpec, and set appropriate bool flags
* in the output command node. Pass NULL for any flags the particular
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index ee5f3a3a52..1542cb0fa4 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -743,6 +743,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
* list of FK constraints to be processed later.
*/
constraint->fk_attrs = list_make1(makeString(column->colname));
+ /* grammar should have set fk_reftypes */
+ Assert(list_length(constraint->fk_reftypes) == 1);
cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
break;
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index c2891e6fa1..3a25ba52f3 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -118,9 +118,11 @@ typedef struct RI_ConstraintInfo
char confupdtype; /* foreign key's ON UPDATE action */
char confdeltype; /* foreign key's ON DELETE action */
char confmatchtype; /* foreign key's match type */
+ bool has_array; /* true if any reftype is EACH_ELEMENT */
int nkeys; /* number of key columns */
int16 pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */
int16 fk_attnums[RI_MAX_NUMKEYS]; /* attnums of referencing cols */
+ char fk_reftypes[RI_MAX_NUMKEYS]; /* reference semantics */
Oid pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = FK) */
Oid pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = PK) */
Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK = FK) */
@@ -204,7 +206,8 @@ static void ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype);
+ const char *rightop, Oid rightoptype,
+ char fkreftype);
static void ri_add_cast_to(StringInfo buf, Oid typid);
static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
static int ri_NullCheck(HeapTuple tup,
@@ -395,6 +398,7 @@ RI_FKey_check(TriggerData *trigdata)
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
StringInfoData querybuf;
+ StringInfoData countbuf;
char pkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
char paramname[16];
@@ -407,12 +411,22 @@ RI_FKey_check(TriggerData *trigdata)
* FOR KEY SHARE OF x
* The type id's for the $ parameters are those of the
* corresponding FK attributes.
+ *
+ * In case of an array ELEMENT foreign key, the previous query is used
+ * to count the number of matching rows and see if every combination
+ * is actually referenced.
+ * The wrapping query is
+ * SELECT 1 WHERE
+ * (SELECT count(DISTINCT y) FROM unnest($1) y)
+ * = (SELECT count(*) FROM (<QUERY>) z)
* ----------
*/
initStringInfo(&querybuf);
quoteRelationName(pkrelname, pk_rel);
appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
querysep = "WHERE";
+ initStringInfo(&countbuf);
+ appendStringInfo(&countbuf, "SELECT 1 WHERE ");
for (i = 0; i < riinfo->nkeys; i++)
{
Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
@@ -421,18 +435,41 @@ RI_FKey_check(TriggerData *trigdata)
quoteOneName(attname,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
sprintf(paramname, "$%d", i + 1);
+
+ /*
+ * In case of an array ELEMENT foreign key, we check that each
+ * distinct non-null value in the array is present in the PK
+ * table.
+ */
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&countbuf,
+ "(SELECT pg_catalog.count(DISTINCT y) FROM pg_catalog.unnest(%s) y)",
+ paramname);
+
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pf_eq_oprs[i],
- paramname, fk_type);
+ paramname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = fk_type;
}
appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
- /* Prepare and save the plan */
- qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
- &qkey, fk_rel, pk_rel, true);
+ if (riinfo->has_array)
+ {
+ appendStringInfo(&countbuf,
+ " OPERATOR(pg_catalog.=) (SELECT pg_catalog.count(*) FROM (%s) z)",
+ querybuf.data);
+
+ /* Prepare and save the plan for array ELEMENT foreign keys */
+ qplan = ri_PlanCheck(countbuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
+ }
+ else
+ /* Prepare and save the plan */
+ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
}
/*
@@ -559,7 +596,8 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pp_eq_oprs[i],
- paramname, pk_type);
+ paramname, pk_type,
+ FKCONSTR_REF_PLAIN);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -751,7 +789,8 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -974,7 +1013,8 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1130,7 +1170,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1309,7 +1350,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1475,7 +1517,8 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1651,7 +1694,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1817,7 +1861,8 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2008,7 +2053,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2327,6 +2373,14 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
* For MATCH FULL:
* (fk.keycol1 IS NOT NULL [OR ...])
*
+ * In case of an array ELEMENT column, relname is replaced with the
+ * following subquery:
+ *
+ * SELECT unnest("keycol1") k1, "keycol1" ak1 [, ...]
+ * FROM ONLY "public"."fk"
+ *
+ * where all the columns are renamed in order to prevent name collisions.
+ *
* We attach COLLATE clauses to the operators when comparing columns
* that have different collations.
*----------
@@ -2338,15 +2392,46 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
{
quoteOneName(fkattname,
RIAttName(fk_rel, riinfo->fk_attnums[i]));
- appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
+ if (riinfo->has_array)
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%sfk.ak%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.k%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
sep = ", ";
}
quoteRelationName(pkrelname, pk_rel);
quoteRelationName(fkrelname, fk_rel);
- appendStringInfo(&querybuf,
- " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
- fkrelname, pkrelname);
+
+ if (riinfo->has_array)
+ {
+ sep = "";
+ appendStringInfo(&querybuf,
+ " FROM (SELECT ");
+ for (i = 0; i < riinfo->nkeys; i++)
+ {
+ quoteOneName(fkattname,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%spg_catalog.unnest(%s) k%d, %s ak%d",
+ sep, fkattname, i + 1, fkattname, i + 1);
+ else
+ appendStringInfo(&querybuf, "%s%s k%d", sep, fkattname,
+ i + 1);
+ sep = ", ";
+ }
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s) fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
+ }
+ else
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
strcpy(pkattname, "pk.");
strcpy(fkattname, "fk.");
@@ -2360,12 +2445,16 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
quoteOneName(pkattname + 3,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
- quoteOneName(fkattname + 3,
- RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname + 3, "k%d", i + 1);
+ else
+ quoteOneName(fkattname + 3,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
ri_GenerateQual(&querybuf, sep,
pkattname, pk_type,
riinfo->pf_eq_oprs[i],
- fkattname, fk_type);
+ fkattname, fk_type,
+ FKCONSTR_REF_PLAIN);
if (pk_coll != fk_coll)
ri_GenerateQualCollation(&querybuf, pk_coll);
sep = "AND";
@@ -2381,7 +2470,10 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
sep = "";
for (i = 0; i < riinfo->nkeys; i++)
{
- quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname, "k%d", i + 1);
+ else
+ quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
appendStringInfo(&querybuf,
"%sfk.%s IS NOT NULL",
sep, fkattname);
@@ -2557,25 +2649,29 @@ quoteRelationName(char *buffer, Relation rel)
/*
* ri_GenerateQual --- generate a WHERE clause equating two variables
*
- * The idea is to append " sep leftop op rightop" to buf. The complexity
- * comes from needing to be sure that the parser will select the desired
- * operator. We always name the operator using OPERATOR(schema.op) syntax
- * (readability isn't a big priority here), so as to avoid search-path
- * uncertainties. We have to emit casts too, if either input isn't already
- * the input type of the operator; else we are at the mercy of the parser's
- * heuristics for ambiguous-operator resolution.
- */
+ * The idea is to append " sep leftop op rightop" to buf, or if fkreftype is
+ * FKCONSTR_REF_EACH_ELEMENT, append " sep leftop op ANY(rightop)" to buf.
+ *
+ * The complexity comes from needing to be sure that the parser will select
+ * the desired operator. We always name the operator using
+ * OPERATOR(schema.op) syntax (readability isn't a big priority here), so as
+ * to avoid search-path uncertainties. We have to emit casts too, if either
+ * input isn't already the input type of the operator; else we are at the
+ * mercy of the parser's heuristics for ambiguous-operator resolution.
+ */
static void
ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype)
+ const char *rightop, Oid rightoptype,
+ char fkreftype)
{
HeapTuple opertup;
Form_pg_operator operform;
char *oprname;
char *nspname;
+ Oid oprright;
opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opoid));
if (!HeapTupleIsValid(opertup))
@@ -2586,14 +2682,32 @@ ri_GenerateQual(StringInfo buf,
nspname = get_namespace_name(operform->oprnamespace);
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ oprright = get_array_type(operform->oprright);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprright))));
+ }
+ else
+ oprright = operform->oprright;
+
appendStringInfo(buf, " %s %s", sep, leftop);
if (leftoptype != operform->oprleft)
ri_add_cast_to(buf, operform->oprleft);
- appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
- appendStringInfoString(buf, oprname);
- appendStringInfo(buf, ") %s", rightop);
- if (rightoptype != operform->oprright)
- ri_add_cast_to(buf, operform->oprright);
+
+ appendStringInfo(buf, " OPERATOR(%s.%s) ",
+ quote_identifier(nspname), oprname);
+
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfoString(buf, "ANY (");
+ appendStringInfoString(buf, rightop);
+ if (rightoptype != oprright)
+ ri_add_cast_to(buf, oprright);
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfoChar(buf, ')');
ReleaseSysCache(opertup);
}
@@ -2801,6 +2915,7 @@ ri_LoadConstraintInfo(Oid constraintOid)
bool isNull;
ArrayType *arr;
int numkeys;
+ int i;
/*
* On the first call initialize the hashtable
@@ -2879,6 +2994,20 @@ ri_LoadConstraintInfo(Oid constraintOid)
pfree(arr); /* free de-toasted copy, if any */
adatum = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype, &isNull);
+ if (isNull)
+ elog(ERROR, "null confreftype for constraint %u", constraintOid);
+ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
+ if (ARR_NDIM(arr) != 1 ||
+ ARR_DIMS(arr)[0] != numkeys ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != CHAROID)
+ elog(ERROR, "confreftype is not a 1-D char array");
+ memcpy(riinfo->fk_reftypes, ARR_DATA_PTR(arr), numkeys * sizeof(char));
+ if ((Pointer) arr != DatumGetPointer(adatum))
+ pfree(arr); /* free de-toasted copy, if any */
+
+ adatum = SysCacheGetAttr(CONSTROID, tup,
Anum_pg_constraint_conpfeqop, &isNull);
if (isNull)
elog(ERROR, "null conpfeqop for constraint %u", constraintOid);
@@ -2921,6 +3050,24 @@ ri_LoadConstraintInfo(Oid constraintOid)
if ((Pointer) arr != DatumGetPointer(adatum))
pfree(arr); /* free de-toasted copy, if any */
+ /*
+ * Fix up some stuff for array foreign keys. We need a has_array flag
+ * indicating whether there's an array foreign key, and we want to set
+ * ff_eq_oprs[i] to array_eq() for array columns, because that's what
+ * makes sense for ri_KeysEqual, and we have no other use for ff_eq_oprs
+ * in this module. (If we did, substituting the array comparator at the
+ * call point in ri_KeysEqual might be more appropriate.)
+ */
+ riinfo->has_array = false;
+ for (i = 0; i < numkeys; i++)
+ {
+ if (riinfo->fk_reftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ riinfo->has_array = true;
+ riinfo->ff_eq_oprs[i] = ARRAY_EQ_OP;
+ }
+ }
+
ReleaseSysCache(tup);
/*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 18d9e27d1e..ab433bb8d6 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -314,6 +314,9 @@ static char *pg_get_viewdef_worker(Oid viewoid,
static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
static void decompile_column_index_array(Datum column_index_array, Oid relId,
StringInfo buf);
+static void decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf);
static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
const Oid *excludeOps,
@@ -1875,7 +1878,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
{
case CONSTRAINT_FOREIGN:
{
- Datum val;
+ Datum colindexes;
+ Datum reftypes;
bool isnull;
const char *string;
@@ -1883,13 +1887,21 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
appendStringInfoString(&buf, "FOREIGN KEY (");
/* Fetch and build referencing-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_conkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_conkey,
+ &isnull);
if (isnull)
elog(ERROR, "null conkey for constraint %u",
constraintId);
+ reftypes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype,
+ &isnull);
+ if (isnull)
+ elog(ERROR, "null confreftype for constraint %u",
+ constraintId);
- decompile_column_index_array(val, conForm->conrelid, &buf);
+ decompile_fk_column_index_array(colindexes, reftypes,
+ conForm->conrelid, &buf);
/* add foreign relation name */
appendStringInfo(&buf, ") REFERENCES %s(",
@@ -1897,13 +1909,15 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
NIL));
/* Fetch and build referenced-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_confkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confkey,
+ &isnull);
if (isnull)
elog(ERROR, "null confkey for constraint %u",
constraintId);
- decompile_column_index_array(val, conForm->confrelid, &buf);
+ decompile_column_index_array(colindexes,
+ conForm->confrelid, &buf);
appendStringInfoChar(&buf, ')');
@@ -2178,6 +2192,66 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
}
}
+ /*
+ * Convert an int16[] Datum and a char[] Datum into a comma-separated
+ * list of column names for the indicated relation, prefixed by appropriate
+ * keywords depending on the foreign key reference semantics indicated by
+ * the char[] entries. Append the text to buf.
+ */
+ static void
+ decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf)
+ {
+ Datum *keys;
+ int nKeys;
+ Datum *reftypes;
+ int nReftypes;
+ int j;
+
+ /* Extract data from array of int16 */
+ deconstruct_array(DatumGetArrayTypeP(column_index_array),
+ INT2OID, sizeof(int16), true, 's',
+ &keys, NULL, &nKeys);
+
+ /* Extract data from array of char */
+ deconstruct_array(DatumGetArrayTypeP(fk_reftype_array),
+ CHAROID, sizeof(char), true, 'c',
+ &reftypes, NULL, &nReftypes);
+
+ if (nKeys != nReftypes)
+ elog(ERROR, "wrong confreftype cardinality");
+
+ for (j = 0; j < nKeys; j++)
+ {
+ char *colName;
+ const char *prefix;
+
+ colName = get_relid_attribute_name(relId, DatumGetInt16(keys[j]));
+
+ switch (DatumGetChar(reftypes[j]))
+ {
+ case FKCONSTR_REF_PLAIN:
+ prefix = "";
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ prefix = "EACH ELEMENT OF ";
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d",
+ (int) DatumGetChar(reftypes[j]));
+ prefix = NULL; /* keep compiler quiet */
+ break;
+ }
+
+ if (j == 0)
+ appendStringInfo(buf, "%s%s", prefix,
+ quote_identifier(colName));
+ else
+ appendStringInfo(buf, ", %s%s", prefix,
+ quote_identifier(colName));
+ }
+}
/* ----------
* get_expr - Decompile an expression tree
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index ec035d8434..bea6f82404 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -104,8 +104,16 @@ CATALOG(pg_constraint,2606)
int16 confkey[1];
/*
+ * If a foreign key, the reference semantics for each column
+ */
+ char confreftype[1];
+
+ /*
* If a foreign key, the OIDs of the PK = FK equality operators for each
* column of the constraint
+ *
+ * Note: for array foreign keys, all these operators are for the array's
+ * element type.
*/
Oid conpfeqop[1];
@@ -150,7 +158,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* compiler constants for pg_constraint
* ----------------
*/
-#define Natts_pg_constraint 24
+#define Natts_pg_constraint 25
#define Anum_pg_constraint_conname 1
#define Anum_pg_constraint_connamespace 2
#define Anum_pg_constraint_contype 3
@@ -169,12 +177,13 @@ typedef FormData_pg_constraint *Form_pg_constraint;
#define Anum_pg_constraint_connoinherit 16
#define Anum_pg_constraint_conkey 17
#define Anum_pg_constraint_confkey 18
-#define Anum_pg_constraint_conpfeqop 19
-#define Anum_pg_constraint_conppeqop 20
-#define Anum_pg_constraint_conffeqop 21
-#define Anum_pg_constraint_conexclop 22
-#define Anum_pg_constraint_conbin 23
-#define Anum_pg_constraint_consrc 24
+#define Anum_pg_constraint_confreftype 19
+#define Anum_pg_constraint_conpfeqop 20
+#define Anum_pg_constraint_conppeqop 21
+#define Anum_pg_constraint_conffeqop 22
+#define Anum_pg_constraint_conexclop 23
+#define Anum_pg_constraint_conbin 24
+#define Anum_pg_constraint_consrc 25
/* ----------------
* initial contents of pg_constraint
@@ -195,7 +204,9 @@ typedef FormData_pg_constraint *Form_pg_constraint;
/*
* Valid values for confupdtype and confdeltype are the FKCONSTR_ACTION_xxx
* constants defined in parsenodes.h. Valid values for confmatchtype are
- * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h.
+ * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h. Valid values
+ * for elements of confreftype[] are the FKCONSTR_REF_xxx constants defined
+ * in parsenodes.h.
*/
#endif /* PG_CONSTRAINT_H */
diff --git a/src/include/catalog/pg_constraint_fn.h b/src/include/catalog/pg_constraint_fn.h
index a4c46897ed..06f4313bae 100644
--- a/src/include/catalog/pg_constraint_fn.h
+++ b/src/include/catalog/pg_constraint_fn.h
@@ -40,6 +40,7 @@ extern Oid CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 1d96169d34..28323fc99c 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2059,6 +2059,10 @@ typedef enum ConstrType /* types of constraints */
#define FKCONSTR_MATCH_PARTIAL 'p'
#define FKCONSTR_MATCH_SIMPLE 's'
+ /* Foreign key column reference semantics codes */
+ #define FKCONSTR_REF_PLAIN 'p'
+ #define FKCONSTR_REF_EACH_ELEMENT 'e'
+
typedef struct Constraint
{
NodeTag type;
@@ -2094,6 +2098,7 @@ typedef struct Constraint
RangeVar *pktable; /* Primary key table */
List *fk_attrs; /* Attributes of foreign key */
List *pk_attrs; /* Corresponding attrs in PK table */
+ List *fk_reftypes; /* Per-column reference semantics (int List) */
char fk_matchtype; /* FULL, PARTIAL, SIMPLE */
char fk_upd_action; /* ON UPDATE action */
char fk_del_action; /* ON DELETE action */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f50e45e886..d3f4803006 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -140,6 +140,7 @@ PG_KEYWORD("domain", DOMAIN_P, UNRESERVED_KEYWORD)
PG_KEYWORD("double", DOUBLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD)
PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD)
+PG_KEYWORD("element", ELEMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("else", ELSE, RESERVED_KEYWORD)
PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD)
diff --git a/src/test/regress/expected/element_foreign_key.out b/src/test/regress/expected/element_foreign_key.out
new file mode 100644
index 0000000000..b62f53e729
--- /dev/null
+++ b/src/test/regress/expected/element_foreign_key.out
@@ -0,0 +1,590 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fkarray"
+DETAIL: Key (ftest1)=({10,1}) is not present in table "pktableforarray".
+DROP TABLE FKTABLEFORARRAY;
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4,6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL,4,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+ERROR: insert or update on table "fktableforarraymdim" violates foreign key constraint "fktableforarraymdim_ftest1_fkey"
+DETAIL: Key (ftest1)=({{1,2},{6,NULL}}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+ERROR: null value in column "ftest1" violates not-null constraint
+DETAIL: Failing row contains (null, 21).
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+ {1} | 4
+(11 rows)
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({D}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({A,B,D}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(A) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(B) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ERROR: foreign key constraint "fktableforarray_ftest1_fkey" cannot be implemented
+DETAIL: Key column "ftest1" has element type integer which does not have a default btree operator class that's compatible with class "float8_ops".
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(A, {A,B,C}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(B, {A}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Test ELEMENT foreign keys with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,99)"}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,1)","(2010,1)"}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2010,99)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2011,1)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+-- Check FK with cast
+CREATE TABLE PKTABLEFORARRAY (c smallint PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (c int[], FOREIGN KEY (EACH ELEMENT OF c) REFERENCES PKTABLEFORARRAY);
+INSERT INTO PKTABLEFORARRAY VALUES (1), (2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,2}');
+UPDATE PKTABLEFORARRAY SET c = 3 WHERE c = 2;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_c_fkey" on table "fktableforarray"
+DETAIL: Key (c)=(2) is still referenced from table "fktableforarray".
+DELETE FROM PKTABLEFORARRAY WHERE c = 1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_c_fkey" on table "fktableforarray"
+DETAIL: Key (c)=(1) is still referenced from table "fktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({0,1}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({2,1}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(2, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(20, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+ERROR: foreign keys support only one array column
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: foreign keys support only one array column
+DROP TABLE F1;
+-- Cleanup
+DROP TABLE DIM1;
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "x2_x1_fkey"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "fk_const"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+DROP TABLE x1;
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{100,100,100},{NULL,NULL,20},{7,8,10}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 23692615f9..3ecd258f28 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -103,7 +103,7 @@ test: publication subscription
# ----------
# Another group of parallel tests
# ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
+test: select_views portals_p2 foreign_key element_foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
# ----------
# Another group of parallel tests
# NB: temp.sql does a reconnect which transiently uses 2 connections,
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 5e8b7e94c4..e56038579a 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -141,6 +141,7 @@ test: amutils
test: select_views
test: portals_p2
test: foreign_key
+test: element_foreign_key
test: cluster
test: dependency
test: guc
diff --git a/src/test/regress/sql/element_foreign_key.sql b/src/test/regress/sql/element_foreign_key.sql
new file mode 100644
index 0000000000..8c1e4d9601
--- /dev/null
+++ b/src/test/regress/sql/element_foreign_key.sql
@@ -0,0 +1,452 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Test ELEMENT foreign keys with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+
+-- Check FK with cast
+CREATE TABLE PKTABLEFORARRAY (c smallint PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (c int[], FOREIGN KEY (EACH ELEMENT OF c) REFERENCES PKTABLEFORARRAY);
+INSERT INTO PKTABLEFORARRAY VALUES (1), (2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,2}');
+UPDATE PKTABLEFORARRAY SET c = 3 WHERE c = 2;
+DELETE FROM PKTABLEFORARRAY WHERE c = 1;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+
+
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+
+
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+-- Cleanup
+DROP TABLE DIM1;
+
+
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+DROP TABLE x2;
+DROP TABLE x1;
+
+
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+DROP TABLE F1;
+
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+DROP TABLE F1;
+
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-06 02:02 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-07-06 02:02 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>; Tom Lane <tgl@sss.pgh.pa.us>
To make the queries fired by the RI triggers GIN indexed. We need to ‒ as
Tom Lane has previously suggested[1] ‒ to replace the query
SELECT 1 FROM ONLY fktable x WHERE $1 = ANY (fkcol) FOR SHARE OF x;
with
SELECT 1 FROM ONLY fktable x WHERE ARRAY[$1] <@ fkcol FOR SHARE OF x;
but since we have @<(anyarray, anyelement) it can be improved to
SELECT 1 FROM ONLY fktable x WHERE $1 @> fkcol FOR SHARE OF x;
and the piece of code responsible for all of this is ri_GenerateQual in
ri_triggers.c.
How to accomplish that is the next step. I don't know if we should hardcode
the "@>" symbol or if we just index the fk table then ri_GenerateQual would
be able to find the operator on it's own.
*What I plan to do:*
- study how to index the fk table upon its creation. I suspect this can
be done in tablecmds.c
*Questions:*
- how can you programmatically in C index a table?
[1] https://www.postgresql.org/message-id/28389.1351094795%40sss.pgh.pa.us
Best Regards,
Mark Rofail
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
GIN-fk-RI-code-v1.patch (text/x-patch, 1.3K) download
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 3a25ba52f3..0045f64c9e 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -2650,7 +2650,7 @@ quoteRelationName(char *buffer, Relation rel)
* ri_GenerateQual --- generate a WHERE clause equating two variables
*
* The idea is to append " sep leftop op rightop" to buf, or if fkreftype is
- * FKCONSTR_REF_EACH_ELEMENT, append " sep leftop op ANY(rightop)" to buf.
+ * FKCONSTR_REF_EACH_ELEMENT, append " sep leftop <@ rightop" to buf.
*
* The complexity comes from needing to be sure that the parser will select
* the desired operator. We always name the operator using
@@ -2697,17 +2697,10 @@ ri_GenerateQual(StringInfo buf,
appendStringInfo(buf, " %s %s", sep, leftop);
if (leftoptype != operform->oprleft)
ri_add_cast_to(buf, operform->oprleft);
-
- appendStringInfo(buf, " OPERATOR(%s.%s) ",
- quote_identifier(nspname), oprname);
-
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- appendStringInfoString(buf, "ANY (");
+ appendStringInfo(buf, " @> ");
appendStringInfoString(buf, rightop);
if (rightoptype != oprright)
ri_add_cast_to(buf, oprright);
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- appendStringInfoChar(buf, ')');
ReleaseSysCache(opertup);
}
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-08 23:35 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-07-08 23:35 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
* What I am working on*
- since we want to create an index on the referencing column, I am
working on firing a 'CREATE INDEX' query programatically right after the
'CREATE TABLE' query
- The problem I ran into is how to specify my Strategy (
GinContainsElemStrategy) within the CREATE INDEX query. For
example: CREATE
INDEX ON fktable USING gin (fkcolumn array_ops)
Where does the strategy number fit?
- The patch is attached here, is the approach I took to creating an
index programmatically, correct?
Best Regard,
Mark Rofail
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
GIN-fk-RI-code-v1.1.patch (text/x-patch, 2.9K) download
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index dc18fd1eae..085b63aa98 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7139,6 +7139,31 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
ereport(ERROR,
(errcode(ERRCODE_INVALID_FOREIGN_KEY),
errmsg("array foreign keys support only NO ACTION and RESTRICT actions")));
+
+ IndexStmt *stmt = makeNode(IndexStmt);
+ stmt->unique = false; /* is index unique? Nope, should allow duplicates*/
+ stmt->concurrent = false; /* should this be a concurrent index build? we want
+ to lock out writes on the table until it's done. */
+ stmt->idxname = NULL; /* let the idxname be generated */
+ stmt->relation = /* relation name */;
+ stmt->accessMethod = "gin"; /* name of access method: GIN */
+ stmt->indexParams = /* column name + */"array_ops";
+ stmt->options = NULL;
+ stmt->tableSpace = NULL; /* NULL for default */
+ stmt->whereClause = NULL;
+ stmt->excludeOpNames = NIL;
+ stmt->idxcomment = NULL;
+ stmt->indexOid = InvalidOid;
+ stmt->oldNode = InvalidOid; /* relfilenode of existing storage, if any: None*/
+ stmt->primary = false; /* is index a primary key? Nope */
+ stmt->isconstraint = false; /* is it for a pkey/unique constraint? Nope */
+ stmt->deferrable = false;
+ stmt->initdeferred = false;
+ stmt->transformed = false;
+ stmt->if_not_exists = false; /* just do nothing if index already exists? Nope
+ (this shouldn't happen)*/
+
+ ATExecAddIndex(tab, rel, stmt, true, lockmode);
}
/*
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 3a25ba52f3..0045f64c9e 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -2650,7 +2650,7 @@ quoteRelationName(char *buffer, Relation rel)
* ri_GenerateQual --- generate a WHERE clause equating two variables
*
* The idea is to append " sep leftop op rightop" to buf, or if fkreftype is
- * FKCONSTR_REF_EACH_ELEMENT, append " sep leftop op ANY(rightop)" to buf.
+ * FKCONSTR_REF_EACH_ELEMENT, append " sep leftop <@ rightop" to buf.
*
* The complexity comes from needing to be sure that the parser will select
* the desired operator. We always name the operator using
@@ -2697,17 +2697,10 @@ ri_GenerateQual(StringInfo buf,
appendStringInfo(buf, " %s %s", sep, leftop);
if (leftoptype != operform->oprleft)
ri_add_cast_to(buf, operform->oprleft);
-
- appendStringInfo(buf, " OPERATOR(%s.%s) ",
- quote_identifier(nspname), oprname);
-
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- appendStringInfoString(buf, "ANY (");
+ appendStringInfo(buf, " @> ");
appendStringInfoString(buf, rightop);
if (rightoptype != oprright)
ri_add_cast_to(buf, oprright);
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- appendStringInfoChar(buf, ')');
ReleaseSysCache(opertup);
}
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-09 00:38 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Alexander Korotkov @ 2017-07-09 00:38 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Sun, Jul 9, 2017 at 2:35 AM, Mark Rofail <markm.rofail@gmail.com> wrote:
> * What I am working on*
>
> - since we want to create an index on the referencing column, I am
> working on firing a 'CREATE INDEX' query programatically right after
> the 'CREATE TABLE' query
> - The problem I ran into is how to specify my Strategy (
> GinContainsElemStrategy) within the CREATE INDEX query. For
> example: CREATE INDEX ON fktable USING gin (fkcolumn array_ops)
> Where does the strategy number fit?
> - The patch is attached here, is the approach I took to creating an
> index programmatically, correct?
>
>
Could you, please, specify idea of what you're implementing in more
detail? AFACS, you're going to automatically create GIN indexes on FK
array columns. However, if we don't do this for regular columns, why
should we do for array columns? For me that sounds like a separate feature
which should be implemented for both regular and array FK columns.
Regarding your questions. If you need to create index supporting given
operator, you shouldn't take care about strategy number. Strategy number
makes sense only in opclass internals. You just need to specify opclass
which support your operator. In principle, you can find all of them in
pg_amop table. Alternatively you can just stick to GIN array_ops.
In general the approach you create index looks OK. It's OK to manually
create DDL node and execute it. As you can see, this is done in many other
places of backend code.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
@ 2017-07-09 10:11 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-07-09 10:11 UTC (permalink / raw)
To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Sun, Jul 9, 2017 at 2:38 AM, Alexander Korotkov <aekorotkov@gmail.com>
wrote:
> Could you, please, specify idea of what you're implementing in more
> detail?
>
Ultimatley we would like an indexed scan instead of a sequential scan, so I
thought we needed to index the FK array columns first.
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-09 17:42 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Alexander Korotkov @ 2017-07-09 17:42 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Sun, Jul 9, 2017 at 1:11 PM, Mark Rofail <markm.rofail@gmail.com> wrote:
> On Sun, Jul 9, 2017 at 2:38 AM, Alexander Korotkov <aekorotkov@gmail.com>
> wrote:
>
>> Could you, please, specify idea of what you're implementing in more
>> detail?
>>
>
> Ultimatley we would like an indexed scan instead of a sequential scan, so
> I thought we needed to index the FK array columns first.
>
Indeed, this is right.
But look how that works for regular FK. When you declare a FK, you
necessary need unique index on referenced column(s). However, index on
referencing columns(s) is not required. Without index on referencing
column(s), row delete in referenced table and update of referenced column
are expensive because requires sequential scan of referencing table. Users
are encouraged to index referencing column(s) to accelerate queries
produced by RI triggers. [1]
According to this, it's unclear why array FKs should behave differently.
We may document that GIN index is required to accelerate RI queries for
array FKs. And users are encouraged to manually define them.
It's also possible to define new option when index on referencing column(s)
would be created automatically. But I think this option should work the
same way for regular FKs and array FKs.
1.
https://www.postgresql.org/docs/current/static/ddl-constraints.html#DDL-CONSTRAINTS-FK
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
@ 2017-07-11 22:26 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:27 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 2 replies; 200+ messages in thread
From: Mark Rofail @ 2017-07-11 22:26 UTC (permalink / raw)
To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Sun, Jul 9, 2017 at 7:42 PM, Alexander Korotkov <aekorotkov@gmail.com>
wrote:
> We may document that GIN index is required to accelerate RI queries for
> array FKs. And users are encouraged to manually define them.
> It's also possible to define new option when index on referencing
> column(s) would be created automatically. But I think this option should
> work the same way for regular FKs and array FKs.
>
I just thought because GIN index is suited for composite elements, it would
be appropriate for array FKs.
So we should leave it to the user ? I think tht would be fine too.
*What I did *
- now the RI checks utilise the @>(anyarray, anyelement)
- however there's a small problem:
operator does not exist: integer[] @> smallint
I assume that external casting would be required here. But how can I
downcast smallint to integer or interger to numeric automatically ?
*What I plan to do*
- work on the above mentioned buy/limitation
- otherwise, I think this concludes limitation #5
fatal performance issues. If you issue any UPDATE or DELETE against the PK
> table, you get a query like this for checking to see if the RI constraint
> would be violated:
SELECT 1 FROM ONLY fktable x WHERE $1 = ANY (fkcol) FOR SHARE OF x;.
or is there anything remaining ?
Best Regards,
Mark Rofail
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-11 22:27 ` Mark Rofail <markm.rofail@gmail.com>
1 sibling, 0 replies; 200+ messages in thread
From: Mark Rofail @ 2017-07-11 22:27 UTC (permalink / raw)
To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
here are the modifications to ri_triggers.c
On Wed, Jul 12, 2017 at 12:26 AM, Mark Rofail <markm.rofail@gmail.com>
wrote:
>
> *What I did *
>
> - now the RI checks utilise the @>(anyarray, anyelement)
>
> Best Regards,
> Mark Rofail
>
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
GIN-fk-RI-code-v1.3.patch (text/x-patch, 2.0K) download
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 3a25ba52f3..2d2b8e6a4f 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -2650,7 +2650,7 @@ quoteRelationName(char *buffer, Relation rel)
* ri_GenerateQual --- generate a WHERE clause equating two variables
*
* The idea is to append " sep leftop op rightop" to buf, or if fkreftype is
- * FKCONSTR_REF_EACH_ELEMENT, append " sep leftop op ANY(rightop)" to buf.
+ * FKCONSTR_REF_EACH_ELEMENT, append " sep leftop <@ rightop" to buf.
*
* The complexity comes from needing to be sure that the parser will select
* the desired operator. We always name the operator using
@@ -2694,21 +2694,34 @@ ri_GenerateQual(StringInfo buf,
else
oprright = operform->oprright;
- appendStringInfo(buf, " %s %s", sep, leftop);
- if (leftoptype != operform->oprleft)
- ri_add_cast_to(buf, operform->oprleft);
-
- appendStringInfo(buf, " OPERATOR(%s.%s) ",
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT){
+ appendStringInfo(buf, " %s %s", sep, rightop);
+
+ if (rightoptype != oprright)
+ ri_add_cast_to(buf, oprright);
+
+ appendStringInfo(buf, " @> ");
+
+ appendStringInfoString(buf, leftop);
+
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+ }
+ else{
+ appendStringInfo(buf, " %s %s", sep, leftop);
+
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+
+ appendStringInfo(buf, " OPERATOR(%s.%s) ",
quote_identifier(nspname), oprname);
-
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- appendStringInfoString(buf, "ANY (");
- appendStringInfoString(buf, rightop);
- if (rightoptype != oprright)
- ri_add_cast_to(buf, oprright);
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- appendStringInfoChar(buf, ')');
+ appendStringInfoString(buf, rightop);
+
+ if (rightoptype != oprright)
+ ri_add_cast_to(buf, oprright);
+ }
+
ReleaseSysCache(opertup);
}
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-11 22:53 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
1 sibling, 1 reply; 200+ messages in thread
From: Alvaro Herrera @ 2017-07-11 22:53 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
Mark Rofail wrote:
> - now the RI checks utilise the @>(anyarray, anyelement)
> - however there's a small problem:
> operator does not exist: integer[] @> smallint
> I assume that external casting would be required here. But how can I
> downcast smallint to integer or interger to numeric automatically ?
We have one opclass for each type combination -- int4 to int2, int4 to
int4, int4 to int8, etc. You just need to add the new strategy to all
the opclasses.
BTW now that we've gone through this a little further, it's starting to
look like a mistake to me to use the same @> operator for (anyarray,
anyelement) than we use for (anyarray, anyarray). I have the feeling
we'd do better by having some other operator for this purpose -- dunno,
maybe @>> or @>. ... whatever you think is reasonable and not already
in use. Unless there is some other reason to pick @> for this purpose.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2017-07-12 12:30 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-07-12 12:30 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Wed, Jul 12, 2017 at 12:53 AM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:
>
> We have one opclass for each type combination -- int4 to int2, int4 to
> int4, int4 to int8, etc. You just need to add the new strategy to all
> the opclasses.
>
Can you clarify this solution ? I think another solution would be external
casting
BTW now that we've gone through this a little further, it's starting to
> look like a mistake to me to use the same @> operator for (anyarray,
> anyelement) than we use for (anyarray, anyarray).
I agree. Changed to @>>
Best Regards,
Mark Rofail
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-14 22:04 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-07-14 22:04 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Wed, Jul 12, 2017 at 2:30 PM, Mark Rofail <markm.rofail@gmail.com> wrote:
> On Wed, Jul 12, 2017 at 12:53 AM, Alvaro Herrera <alvherre@2ndquadrant.com
> > wrote:
>>
>> We have one opclass for each type combination -- int4 to int2, int4 to
>> int4, int4 to int8, etc. You just need to add the new strategy to all
>> the opclasses.
>>
>
> Can you clarify this solution ? I think another solution would be external
> casting
>
>>
>> If external casting is to be used. If for example the two types in
question are smallint and integer. Would a function get_common_type(Oid
leftopr, Oid rightopr) be useful ?, that given the two types return the
"common" type between the two in this case integer.
Best Regards,
Mark Rofail
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-14 22:40 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Alvaro Herrera @ 2017-07-14 22:40 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
Mark Rofail wrote:
> On Wed, Jul 12, 2017 at 2:30 PM, Mark Rofail <markm.rofail@gmail.com> wrote:
>
> > On Wed, Jul 12, 2017 at 12:53 AM, Alvaro Herrera <alvherre@2ndquadrant.com
> > > wrote:
> >>
> >> We have one opclass for each type combination -- int4 to int2, int4 to
> >> int4, int4 to int8, etc. You just need to add the new strategy to all
> >> the opclasses.
> >>
> >
> > Can you clarify this solution ? I think another solution would be external
> > casting
> >
> If external casting is to be used. If for example the two types in
> question are smallint and integer. Would a function get_common_type(Oid
> leftopr, Oid rightopr) be useful ?, that given the two types return the
> "common" type between the two in this case integer.
Do you mean adding cast decorators to the query constructed by
ri_triggers.c? That looks like an inferior solution. What problem do
you see with adding more rows to the opclass?
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2017-07-17 23:24 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-17 23:47 ` Re: GSoC 2017: Foreign Key Arrays Enrique Meneses <emmeneses@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
0 siblings, 2 replies; 200+ messages in thread
From: Mark Rofail @ 2017-07-17 23:24 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Wed, Jul 12, 2017 at 12:53 AM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:
>
> We have one opclass for each type combination -- int4 to int2, int4 to
> int4, int4 to int8, etc. You just need to add the new strategy to all
> the opclasses.
I tried this approach by manually declaring the operator multiple of times
in pg_amop.h (src/include/catalog/pg_amop.h)
so instead of the polymorphic declaration
DATA(insert ( 2745 2277 2283 5 s 6108 2742 0 )); /* anyarray @>> anyelem
*/
multiple declarations were used, for example for int4[] :
DATA(insert ( 2745 1007 20 5 s 6108 2742 0 )); /* int4[] @>> int8 */
DATA(insert ( 2745 1007 23 5 s 6108 2742 0 )); /* int4[] @>> int4 */
DATA(insert ( 2745 1007 21 5 s 6108 2742 0 )); /* int4[] @>> int2 */
DATA(insert ( 2745 1007 1700 5 s 6108 2742 0 ));/* int4[] @>> numeric */
However, make check produced:
could not create unique index "pg_amop_opr_fam_index"
Key (amopopr, amoppurpose, amopfamily)=(6108, s, 2745) is duplicated.
Am I implementing this the wrong way or do we need to look for another
approach?
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
elemOperatorV3.4.2.patch (text/x-patch, 20.1K) download
diff --git a/src/backend/access/gin/ginarrayproc.c b/src/backend/access/gin/ginarrayproc.c
index a5238c3af5..9d6447923d 100644
--- a/src/backend/access/gin/ginarrayproc.c
+++ b/src/backend/access/gin/ginarrayproc.c
@@ -24,6 +24,7 @@
#define GinContainsStrategy 2
#define GinContainedStrategy 3
#define GinEqualStrategy 4
+#define GinContainsElemStrategy 5
/*
@@ -43,7 +44,7 @@ ginarrayextract(PG_FUNCTION_ARGS)
bool *nulls;
int nelems;
- get_typlenbyvalalign(ARR_ELEMTYPE(array),
+ get_typlenbyvalalign(ARR_ELEMTYPE(array),
&elmlen, &elmbyval, &elmalign);
deconstruct_array(array,
@@ -110,6 +111,11 @@ ginqueryarrayextract(PG_FUNCTION_ARGS)
case GinOverlapStrategy:
*searchMode = GIN_SEARCH_MODE_DEFAULT;
break;
+ case GinContainsElemStrategy:
+ /* only items that match the queried element
+ are considered candidate */
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+ break;
case GinContainsStrategy:
if (nelems > 0)
*searchMode = GIN_SEARCH_MODE_DEFAULT;
@@ -171,6 +177,7 @@ ginarrayconsistent(PG_FUNCTION_ARGS)
}
}
break;
+ case GinContainsElemStrategy:
case GinContainsStrategy:
/* result is not lossy */
*recheck = false;
@@ -258,7 +265,8 @@ ginarraytriconsistent(PG_FUNCTION_ARGS)
}
}
break;
- case GinContainsStrategy:
+ case GinContainsElemStrategy:
+ case GinContainsStrategy:
/* must have all elements in check[] true, and no nulls */
res = GIN_TRUE;
for (i = 0; i < nkeys; i++)
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 34dadd6e19..8c9eb0c676 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -4232,6 +4232,117 @@ arraycontained(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(result);
}
+/*
+ * array_contains_elem : checks an array for a spefific element
+ */
+static bool
+array_contains_elem(AnyArrayType *array, Datum elem, Oid element_type,
+ bool element_isnull, Oid collation, void **fn_extra)
+{
+ Oid arr_type = AARR_ELEMTYPE(array);
+ TypeCacheEntry *typentry;
+ int nelems;
+ int typlen;
+ bool typbyval;
+ char typalign;
+ int i;
+ array_iter it1;
+ FunctionCallInfoData locfcinfo;
+
+ if (arr_type != element_type)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot compare different element types")));
+
+ if (element_isnull)
+ return false;
+
+ /*
+ * We arrange to look up the equality function only once per series of
+ * calls, assuming the element type doesn't change underneath us. The
+ * typcache is used so that we have no memory leakage when being used as
+ * an index support function.
+ */
+ typentry = (TypeCacheEntry *)*fn_extra;
+ if (typentry == NULL ||
+ typentry->type_id != arr_type)
+ {
+ typentry = lookup_type_cache(arr_type,
+ TYPECACHE_EQ_OPR_FINFO);
+ if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(arr_type))));
+ *fn_extra = (void *)typentry;
+ }
+ typlen = typentry->typlen;
+ typbyval = typentry->typbyval;
+ typalign = typentry->typalign;
+
+ /*
+ * Apply the comparison operator to each pair of array elements.
+ */
+ InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+ collation, NULL, NULL);
+
+ /* Loop over source data */
+ nelems = ArrayGetNItems(AARR_NDIM(array), AARR_DIMS(array));
+ array_iter_setup(&it1, array);
+
+ for (i = 0; i < nelems; i++)
+ {
+ Datum elt1;
+ bool isnull;
+ bool oprresult;
+
+ /* Get element, checking for NULL */
+ elt1 = array_iter_next(&it1, &isnull, i, typlen, typbyval, typalign);
+
+ /*
+ * We assume that the comparison operator is strict, so a NULL can't
+ * match anything. XXX this diverges from the "NULL=NULL" behavior of
+ * array_eq, should we act like that?
+ */
+ if (isnull)
+ continue;
+
+ /*
+ * Apply the operator to the element pair
+ */
+ locfcinfo.arg[0] = elt1;
+ locfcinfo.arg[1] = elem;
+ locfcinfo.argnull[0] = false;
+ locfcinfo.argnull[1] = false;
+ locfcinfo.isnull = false;
+ oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+ if (oprresult)
+ return true;
+ }
+
+ return false;
+}
+
+Datum
+arraycontainselem(PG_FUNCTION_ARGS)
+{
+ AnyArrayType *array = PG_GETARG_ANY_ARRAY(0);
+ Datum elem = PG_GETARG_DATUM(1);
+ Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ Oid collation = PG_GET_COLLATION();
+ bool element_isnull = PG_ARGISNULL(1);
+ bool result;
+
+ result = array_contains_elem(array, elem, element_type,
+ element_isnull, collation,
+ &fcinfo->flinfo->fn_extra);
+
+ /* Avoid leaking memory when handed toasted input. */
+ AARR_FREE_IF_COPY(array, 0);
+
+ PG_RETURN_BOOL(result);
+}
+
/*-----------------------------------------------------------------------------
* Array iteration functions
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 3a25ba52f3..9e7d66df7e 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -2650,7 +2650,7 @@ quoteRelationName(char *buffer, Relation rel)
* ri_GenerateQual --- generate a WHERE clause equating two variables
*
* The idea is to append " sep leftop op rightop" to buf, or if fkreftype is
- * FKCONSTR_REF_EACH_ELEMENT, append " sep leftop op ANY(rightop)" to buf.
+ * FKCONSTR_REF_EACH_ELEMENT, append " sep leftop @>> rightop" to buf.
*
* The complexity comes from needing to be sure that the parser will select
* the desired operator. We always name the operator using
@@ -2694,21 +2694,34 @@ ri_GenerateQual(StringInfo buf,
else
oprright = operform->oprright;
- appendStringInfo(buf, " %s %s", sep, leftop);
- if (leftoptype != operform->oprleft)
- ri_add_cast_to(buf, operform->oprleft);
-
- appendStringInfo(buf, " OPERATOR(%s.%s) ",
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT){
+ appendStringInfo(buf, " %s %s", sep, rightop);
+
+ if (rightoptype != oprright)
+ ri_add_cast_to(buf, oprright);
+
+ appendStringInfo(buf, " @>> ");
+
+ appendStringInfoString(buf, leftop);
+
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+ }
+ else{
+ appendStringInfo(buf, " %s %s", sep, leftop);
+
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+
+ appendStringInfo(buf, " OPERATOR(%s.%s) ",
quote_identifier(nspname), oprname);
-
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- appendStringInfoString(buf, "ANY (");
- appendStringInfoString(buf, rightop);
- if (rightoptype != oprright)
- ri_add_cast_to(buf, oprright);
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- appendStringInfoChar(buf, ')');
+ appendStringInfoString(buf, rightop);
+
+ if (rightoptype != oprright)
+ ri_add_cast_to(buf, oprright);
+ }
+
ReleaseSysCache(opertup);
}
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index f850be490a..5649ecd22d 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -689,7 +689,31 @@ DATA(insert ( 2745 2277 2277 1 s 2750 2742 0 ));
DATA(insert ( 2745 2277 2277 2 s 2751 2742 0 ));
DATA(insert ( 2745 2277 2277 3 s 2752 2742 0 ));
DATA(insert ( 2745 2277 2277 4 s 1070 2742 0 ));
-
+// DATA(insert ( 2745 2277 2283 5 s 6108 2742 0 )); /* anyarray @>> anyelem */
+DATA(insert ( 2745 1000 16 5 s 6108 2742 0 )); /* bool[] @>> bool */
+DATA(insert ( 2745 1001 17 5 s 6108 2742 0 )); /* char[] @>> char */
+DATA(insert ( 2745 1003 18 5 s 6108 2742 0 )); /* name[] @>> name */
+DATA(insert ( 2745 1231 20 5 s 6108 2742 0 )); /* numeric[] @>> int8 */
+DATA(insert ( 2745 1231 23 5 s 6108 2742 0 )); /* numeric[] @>> int4 */
+DATA(insert ( 2745 1231 21 5 s 6108 2742 0 )); /* numeric[] @>> int2 */
+DATA(insert ( 2745 1231 1700 5 s 6108 2742 0 ));/* numeric[] @>> numeric */
+DATA(insert ( 2745 1016 20 5 s 6108 2742 0 )); /* int8[] @>> int8 */
+DATA(insert ( 2745 1016 23 5 s 6108 2742 0 )); /* int8[] @>> int4 */
+DATA(insert ( 2745 1016 21 5 s 6108 2742 0 )); /* int8[] @>> int2 */
+DATA(insert ( 2745 1016 1700 5 s 6108 2742 0 ));/* int8[] @>> numeric */
+DATA(insert ( 2745 1007 20 5 s 6108 2742 0 )); /* int4[] @>> int8 */
+DATA(insert ( 2745 1007 23 5 s 6108 2742 0 )); /* int4[] @>> int4 */
+DATA(insert ( 2745 1007 21 5 s 6108 2742 0 )); /* int4[] @>> int2 */
+DATA(insert ( 2745 1007 1700 5 s 6108 2742 0 ));/* int4[] @>> numeric */
+DATA(insert ( 2745 1005 20 5 s 6108 2742 0 )); /* int2[] @>> int8 */
+DATA(insert ( 2745 1005 23 5 s 6108 2742 0 )); /* int2[] @>> int4 */
+DATA(insert ( 2745 1005 21 5 s 6108 2742 0 )); /* int2[] @>> int2 */
+DATA(insert ( 2745 1005 1700 5 s 6108 2742 0 ));/* int2[] @>> numeric */
+DATA(insert ( 2745 1021 700 5 s 6108 2742 0 )); /* float4[] @>> float4 */
+DATA(insert ( 2745 1021 701 5 s 6108 2742 0 )); /* float4[] @>> float8 */
+DATA(insert ( 2745 1022 700 5 s 6108 2742 0 )); /* float8[] @>> float4 */
+DATA(insert ( 2745 1022 701 5 s 6108 2742 0 )); /* float8[] @>> float8 */
+
/*
* btree enum_ops
*/
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index ffabc2003b..8a9d616d29 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1570,6 +1570,8 @@ DESCR("contains");
DATA(insert OID = 2752 ( "<@" PGNSP PGUID b f f 2277 2277 16 2751 0 arraycontained arraycontsel arraycontjoinsel ));
DESCR("is contained by");
#define OID_ARRAY_CONTAINED_OP 2752
+DATA(insert OID = 6108 ( "@>>" PGNSP PGUID b f f 2277 2283 16 0 0 arraycontainselem arraycontsel arraycontjoinsel ));
+DESCR("containselem");
/* capturing operators to preserve pre-8.3 behavior of text concatenation */
DATA(insert OID = 2779 ( "||" PGNSP PGUID b f f 25 2776 25 0 0 textanycat - - ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 8b33b4e0ea..01b207d363 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4308,6 +4308,7 @@ DESCR("GIN array support (obsolete)");
DATA(insert OID = 2747 ( arrayoverlap PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arrayoverlap _null_ _null_ _null_ ));
DATA(insert OID = 2748 ( arraycontains PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arraycontains _null_ _null_ _null_ ));
DATA(insert OID = 2749 ( arraycontained PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arraycontained _null_ _null_ _null_ ));
+DATA(insert OID = 6109 ( arraycontainselem PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2283" _null_ _null_ _null_ _null_ _null_ arraycontainselem _null_ _null_ _null_ ));
/* BRIN minmax */
DATA(insert OID = 3383 ( brin_minmax_opcinfo PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_opcinfo _null_ _null_ _null_ ));
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index c730563f03..0a9eff4d04 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -737,6 +737,17 @@ SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(6 rows)
+SELECT * FROM array_op_test WHERE i @>> 32 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+ 98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
+ 100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
+(6 rows)
+
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -761,6 +772,19 @@ SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
(8 rows)
+SELECT * FROM array_op_test WHERE i @>> 17 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
+ 53 | {38,17} | {AAAAAAAAAAA21658}
+ 65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+(8 rows)
+
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -942,6 +966,11 @@ SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
-------+---+---
(0 rows)
+SELECT * FROM array_op_test WHERE i @>> NULL ORDER BY seqno;
+ seqno | i | t
+-------+---+---
+(0 rows)
+
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
seqno | i | t
-------+---+---
@@ -953,6 +982,15 @@ SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
101 | {} | {}
(1 row)
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAA72908' ORDER BY seqno;
+ seqno | i | t
+-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
+ 22 | {11,6,56,62,53,30} | {AAAAAAAA72908}
+ 45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}
+ 72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+(4 rows)
+
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
seqno | i | t
-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
@@ -971,6 +1009,14 @@ SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
(4 rows)
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAAAA646' ORDER BY seqno;
+ seqno | i | t
+-------+------------------+--------------------------------------------------------------------
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+ 96 | {23,97,43} | {AAAAAAAAAA646,A87088}
+(3 rows)
+
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
seqno | i | t
-------+------------------+--------------------------------------------------------------------
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index fcf8bd7565..25ef369951 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1811,6 +1811,7 @@ ORDER BY 1, 2, 3;
2742 | 2 | @@@
2742 | 3 | <@
2742 | 4 | =
+ 2742 | 5 | @>>
2742 | 7 | @>
2742 | 9 | ?
2742 | 10 | ?|
@@ -1878,7 +1879,7 @@ ORDER BY 1, 2, 3;
4000 | 25 | <<=
4000 | 26 | >>
4000 | 27 | >>=
-(121 rows)
+(122 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 25dd4e2c6d..f6fb507bdd 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -313,8 +313,10 @@ SELECT ARRAY[0,0] || ARRAY[1,1] || ARRAY[2,2] AS "{0,0,1,1,2,2}";
SELECT 0 || ARRAY[1,2] || 3 AS "{0,1,2,3}";
SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> 32 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> 17 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{32,17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32,17}' ORDER BY seqno;
@@ -325,11 +327,14 @@ SELECT * FROM array_op_test WHERE i && '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> NULL ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAA72908' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAAAA646' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno;
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-17 23:47 ` Enrique Meneses <emmeneses@gmail.com>
1 sibling, 0 replies; 200+ messages in thread
From: Enrique Meneses @ 2017-07-17 23:47 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
There is a generic definition for any array added as part of
https://commitfest.postgresql.org/10/708/ (it may be the reason for the
duplicate error). I am not sure what your change is but I would review the
above just in case. There is also a defect with a misleading error that is
still being triggered for UUID arrays.
Enrique
On Mon, Jul 17, 2017 at 4:25 PM Mark Rofail <markm.rofail@gmail.com> wrote:
> On Wed, Jul 12, 2017 at 12:53 AM, Alvaro Herrera <alvherre@2ndquadrant.com
> > wrote:
>>
>> We have one opclass for each type combination -- int4 to int2, int4 to
>> int4, int4 to int8, etc. You just need to add the new strategy to all
>> the opclasses.
>
>
> I tried this approach by manually declaring the operator multiple of
> times in pg_amop.h (src/include/catalog/pg_amop.h)
>
> so instead of the polymorphic declaration
> DATA(insert ( 2745 2277 2283 5 s 6108 2742 0 )); /* anyarray @>>
> anyelem */
>
> multiple declarations were used, for example for int4[] :
> DATA(insert ( 2745 1007 20 5 s 6108 2742 0 )); /* int4[] @>> int8 */
> DATA(insert ( 2745 1007 23 5 s 6108 2742 0 )); /* int4[] @>> int4 */
> DATA(insert ( 2745 1007 21 5 s 6108 2742 0 )); /* int4[] @>> int2 */
> DATA(insert ( 2745 1007 1700 5 s 6108 2742 0 ));/* int4[] @>> numeric */
>
> However, make check produced:
> could not create unique index "pg_amop_opr_fam_index"
> Key (amopopr, amoppurpose, amopfamily)=(6108, s, 2745) is duplicated.
>
> Am I implementing this the wrong way or do we need to look for another
> approach?
>
>
> --
> Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-hackers
>
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-18 17:43 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 17:48 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
1 sibling, 2 replies; 200+ messages in thread
From: Alexander Korotkov @ 2017-07-18 17:43 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Tue, Jul 18, 2017 at 2:24 AM, Mark Rofail <markm.rofail@gmail.com> wrote:
> On Wed, Jul 12, 2017 at 12:53 AM, Alvaro Herrera <alvherre@2ndquadrant.com
> > wrote:
>>
>> We have one opclass for each type combination -- int4 to int2, int4 to
>> int4, int4 to int8, etc. You just need to add the new strategy to all
>> the opclasses.
>
>
> I tried this approach by manually declaring the operator multiple of
> times in pg_amop.h (src/include/catalog/pg_amop.h)
>
> so instead of the polymorphic declaration
> DATA(insert ( 2745 2277 2283 5 s 6108 2742 0 )); /* anyarray @>>
> anyelem */
>
> multiple declarations were used, for example for int4[] :
> DATA(insert ( 2745 1007 20 5 s 6108 2742 0 )); /* int4[] @>> int8 */
> DATA(insert ( 2745 1007 23 5 s 6108 2742 0 )); /* int4[] @>> int4 */
> DATA(insert ( 2745 1007 21 5 s 6108 2742 0 )); /* int4[] @>> int2 */
> DATA(insert ( 2745 1007 1700 5 s 6108 2742 0 ));/* int4[] @>> numeric */
>
> However, make check produced:
> could not create unique index "pg_amop_opr_fam_index"
> Key (amopopr, amoppurpose, amopfamily)=(6108, s, 2745) is duplicated.
>
> Am I implementing this the wrong way or do we need to look for another
> approach?
>
The problem is that you need to have not only opclass entries for the
operators, but also operators themselves. I.e. separate operators for
int4[] @>> int8, int4[] @>> int4, int4[] @>> int2, int4[] @>> numeric. You
tried to add multiple pg_amop rows for single operator and consequently get
unique index violation.
Alvaro, do you think we need to define all these operators? I'm not sure.
If even we need it, I think we shouldn't do this during this GSoC. What
particular shortcomings do you see in explicit cast in RI triggers queries?
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
@ 2017-07-18 17:48 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
1 sibling, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-07-18 17:48 UTC (permalink / raw)
To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; David Steele <david@pgmasters.net>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; pgsql-hackers
On Tue, 18 Jul 2017 at 7:43 pm, Alexander Korotkov <aekorotkov@gmail.com>
wrote:
> On T upue, Jul 18, 2017 at 2:24 AM, Mark Rofail <markm.rofail@gmail.com>
> wrote:
>
>> On Wed, Jul 12, 2017 at 12:53 AM, Alvaro Herrera <
>> alvherre@2ndquadrant.com> wrote:
>>>
>>> We have one opclass for each type combination -- int4 to int2, int4 to
>>> int4, int4 to int8, etc. You just need to add the new strategy to all
>>> the opclasses.
>>
>>
>> I tried this approach by manually declaring the operator multiple of
>> times in pg_amop.h (src/include/catalog/pg_amop.h)
>>
>> so instead of the polymorphic declaration
>> DATA(insert ( 2745 2277 2283 5 s 6108 2742 0 )); /* anyarray @>>
>> anyelem */
>>
>> multiple declarations were used, for example for int4[] :
>> DATA(insert ( 2745 1007 20 5 s 6108 2742 0 )); /* int4[] @>> int8 */
>> DATA(insert ( 2745 1007 23 5 s 6108 2742 0 )); /* int4[] @>> int4 */
>> DATA(insert ( 2745 1007 21 5 s 6108 2742 0 )); /* int4[] @>> int2 */
>> DATA(insert ( 2745 1007 1700 5 s 6108 2742 0 ));/* int4[] @>> numeric
>> */
>>
>> However, make check produced:
>> could not create unique index "pg_amop_opr_fam_index"
>> Key (amopopr, amoppurpose, amopfamily)=(6108, s, 2745) is duplicated.
>>
>> Am I implementing this the wrong way or do we need to look for another
>> approach?
>>
>
> The problem is that you need to have not only opclass entries for the
> operators, but also operators themselves. I.e. separate operators for
> int4[] @>> int8, int4[] @>> int4, int4[] @>> int2, int4[] @>> numeric. You
> tried to add multiple pg_amop rows for single operator and consequently get
> unique index violation.
>
> Alvaro, do you think we need to define all these operators? I'm not
> sure. If even we need it, I think
> ------
> Alexander Korotkov
> Postgres Professional: http://www.postgrespro.com
> The Russian Postgres Company
>
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 17:48 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-18 17:51 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-18 21:13 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-07-18 17:51 UTC (permalink / raw)
To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; David Steele <david@pgmasters.net>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; pgsql-hackers
On Tue, 18 Jul 2017 at 7:43 pm, Alexander Korotkov <aekorotkov@gmail.com>
wrote:
> separate operators for int4[] @>> int8, int4[] @>> int4, int4[] @>> int2,
> int4[] @>> numeric.
>
My only comment on the separate operators is its high maintenance. Any new
datatype introduced a corresponding operator should be created.
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 17:48 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-18 21:13 ` Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 0 replies; 200+ messages in thread
From: Alvaro Herrera @ 2017-07-18 21:13 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; David Steele <david@pgmasters.net>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; pgsql-hackers
Mark Rofail wrote:
> On Tue, 18 Jul 2017 at 7:43 pm, Alexander Korotkov <aekorotkov@gmail.com>
> wrote:
>
> > separate operators for int4[] @>> int8, int4[] @>> int4, int4[] @>> int2,
> > int4[] @>> numeric.
> >
>
> My only comment on the separate operators is its high maintenance. Any new
> datatype introduced a corresponding operator should be created.
Yes.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
@ 2017-07-18 21:14 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
1 sibling, 1 reply; 200+ messages in thread
From: Alvaro Herrera @ 2017-07-18 21:14 UTC (permalink / raw)
To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Mark Rofail <markm.rofail@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
Alexander Korotkov wrote:
> The problem is that you need to have not only opclass entries for the
> operators, but also operators themselves. I.e. separate operators for
> int4[] @>> int8, int4[] @>> int4, int4[] @>> int2, int4[] @>> numeric. You
> tried to add multiple pg_amop rows for single operator and consequently get
> unique index violation.
>
> Alvaro, do you think we need to define all these operators? I'm not sure.
> If even we need it, I think we shouldn't do this during this GSoC. What
> particular shortcomings do you see in explicit cast in RI triggers queries?
I'm probably confused. Why did we add an operator and not a support
procedure? I think we should have added rows in pg_amproc, not
pg_amproc. I'm very tired right now so I may be speaking nonsense.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2017-07-19 11:56 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-19 12:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
0 siblings, 2 replies; 200+ messages in thread
From: Mark Rofail @ 2017-07-19 11:56 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Tue, Jul 18, 2017 at 11:14 PM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:
>
> Why did we add an operator and not a support
> procedure?
I thought the support procedures were constant within an opclass. They
implement the mandotary function required of an opclass. I don't see why we
would need to implement new ones since they already deal with the lefthand
operand which is the refrencing coloumn and is always an array so anyarray
would suffice.
Also the support procedure don't interact with the left and right operands
simultanously. And we want to target the combinations of int4[] @>> int8,
int4[] @>> int4, int4[] @>> int2, int4[] @>> numeric.
So I think implementing operators is the way to go.
Best Regards,
Mark Rofail.
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-19 12:08 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-19 17:28 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
1 sibling, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-07-19 12:08 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
*To summarise,* the options we have to solve the limitation of the
@>(anyarray , anyelement) where it produces the following error: operator
does not exist: integer[] @> smallint
*Option 1: *Multiple Operators
Have separate operators for every combination of datatypes instead of a
single polymorphic definition (i.e int4[] @>> int8, int4[] @>> int4, int4[]
@>> int2, int4[] @>> numeric.)
Drawback: High maintenance.
*Option 2: *Explicit casting
Where we compare the datatype of the 2 operands and cast with the
appropriate datatype
Drawback: figuring out the appropriate cast may require considerable
computation
*Option 3:* Unsafe Polymorphic datatypes
This a little out there. But since @>(anyarray, anyelement) have to resolve
to the same datatype. How about defining new datatypes without this
constraint? Where we handle the datatypes ourselves? It would ve something
like @>(unsafeAnyarray, unsafeAnyelement).
Drawback: a lot of defensive programming has to be implemented to guard
against any exception.
*Another thing*
Until this is settled, another thing I have to go through is performance
testing. To provide evidence that all we did actually enhances the
performance of the RI checks. How can I go about this?
Best Regards,
Mark Rofail
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 12:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-19 17:28 ` Robert Haas <robertmhaas@gmail.com>
2017-07-19 18:29 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Robert Haas @ 2017-07-19 17:28 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Alexander Korotkov <aekorotkov@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Wed, Jul 19, 2017 at 8:08 AM, Mark Rofail <markm.rofail@gmail.com> wrote:
> To summarise, the options we have to solve the limitation of the @>(anyarray
> , anyelement) where it produces the following error: operator does not
> exist: integer[] @> smallint
Why do we have to solve that limitation?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 12:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 17:28 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
@ 2017-07-19 18:29 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-19 19:02 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-07-19 18:29 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Alexander Korotkov <aekorotkov@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Wed, Jul 19, 2017 at 7:28 PM, Robert Haas <robertmhaas@gmail.com> wrote:
> Why do we have to solve that limitation?
Since the regress test labled element_foreing_key fails now that I made the
RI queries utilise @(anyarray, anyelement), that means it's not functioning
as it is meant to be.
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 12:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 17:28 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-19 18:29 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-19 19:02 ` Robert Haas <robertmhaas@gmail.com>
0 siblings, 0 replies; 200+ messages in thread
From: Robert Haas @ 2017-07-19 19:02 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Alexander Korotkov <aekorotkov@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Wed, Jul 19, 2017 at 2:29 PM, Mark Rofail <markm.rofail@gmail.com> wrote:
> On Wed, Jul 19, 2017 at 7:28 PM, Robert Haas <robertmhaas@gmail.com> wrote:
>>
>> Why do we have to solve that limitation?
>
> Since the regress test labled element_foreing_key fails now that I made the
> RI queries utilise @(anyarray, anyelement), that means it's not functioning
> as it is meant to be.
Well, if this is a new test introduced by the patch, you could also
just change the test. Off-hand, I'm not sure that it's very important
to make the case work where the types don't match between the
referenced table and the referencing table, which is what you seem to
be talking about here. But maybe I'm misunderstanding the situation.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-19 20:08 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 20:46 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
1 sibling, 2 replies; 200+ messages in thread
From: Alvaro Herrera @ 2017-07-19 20:08 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
Mark Rofail wrote:
> On Tue, Jul 18, 2017 at 11:14 PM, Alvaro Herrera <alvherre@2ndquadrant.com>
> wrote:
> >
> > Why did we add an operator and not a support
> > procedure?
>
> I thought the support procedures were constant within an opclass.
Uhh ... I apologize but I think I was barking at the wrong tree. I was
thinking that it mattered that the opclass mechanism was able to
determine whether some array @>> some element, but that's not true: it's
the queries in ri_triggers.c, which have no idea about opclasses.
(I tihnk we would have wanted to use to opclasses in order to find out
what operator to use in the first place, if ri_triggers.c was already
using that general idea; but in reality it's already using hardcoded
operator names, so it doesn't matter.)
I'm not entirely sure what's the best way to deal with the polymorphic
problem, but on the other hand as Robert says downthread maybe we
shouldn't be solving it at this stage anyway. So let's step back a bit,
get a patch that works for the case where the types match on both sides
of the FK, then we review that patch; if all is well, we can discuss the
other problem as a stretch goal.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2017-07-19 20:46 ` Mark Rofail <markm.rofail@gmail.com>
1 sibling, 0 replies; 200+ messages in thread
From: Mark Rofail @ 2017-07-19 20:46 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Wed, Jul 19, 2017 at 10:08 PM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:
> So let's step back a bit,
> get a patch that works for the case where the types match on both sides
> of the FK, then we review that patch; if all is well, we can discuss the
> other problem as a stretch goal.
Agreed. This should be a future improvment.
I think the next step should be testing the performnce before/after the
modifiactions.
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
@ 2017-07-21 13:56 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
1 sibling, 1 reply; 200+ messages in thread
From: Alexander Korotkov @ 2017-07-21 13:56 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Mark Rofail <markm.rofail@gmail.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Wed, Jul 19, 2017 at 11:08 PM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:
> I'm not entirely sure what's the best way to deal with the polymorphic
> problem, but on the other hand as Robert says downthread maybe we
> shouldn't be solving it at this stage anyway. So let's step back a bit,
> get a patch that works for the case where the types match on both sides
> of the FK, then we review that patch; if all is well, we can discuss the
> other problem as a stretch goal.
+1
Regular FK functionality have type restrictions based on btree opfamilies
and implicit casts. Array FK should not necessary have the same type
restrictions. Also, we don't necessary need to make those restrictions as
soft as possible during this GSoC project.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
@ 2017-07-22 21:50 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-07-22 21:50 UTC (permalink / raw)
To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Alvaro Herrera <alvherre@2ndquadrant.com>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
Okay, so I'm trying to test the performance of Foreign Key Array for 3
scenarios: Original Patch, After My Modifications and After My
Modifications with GIN index on the referencing column.
I have attached the sql test file here. It contains about 10k row
insertions.
However, there is a bug that prevented me from testing the third scenario,
I assume there's an issue of incompatible types problem since the right
operand type is anyelement and the supporting procedures expect anyarray.
I am working on debugging it right now.
But if it comes to it, should I introduce a new opclass specifically for
anyelement or add new supporting procedures to the old opclass ? .
I have also attached the results for the first 2 scenarios, however, the
third scenario is the most important one since it's what the project is all
about.
Also, this is kind of interesting. Upon reviewing the results
SELECT 1 FROM ONLY fktable x WHERE $1 @> fkcol FOR SHARE OF x;
produces worse results than the original
SELECT 1 FROM ONLY fktable x WHERE $1 = ANY (fkcol) FOR SHARE OF x;
so personally I don't think we should leave creating a GIN index up to the
user, it should be automatically generated instead.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
element_foreign_key_performance.sql (application/sql, 627.8K) download
original_patch_results.out (application/octet-stream, 643.1K) download
modified_results.out (application/octet-stream, 643.1K) download
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-23 02:41 ` Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Robert Haas @ 2017-07-23 02:41 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
On Sat, Jul 22, 2017 at 5:50 PM, Mark Rofail <markm.rofail@gmail.com> wrote:
> so personally I don't think we should leave creating a GIN index up to the
> user, it should be automatically generated instead.
I can certainly understand why you feel that way, but trying to do
that in your patch is just going to get your patch rejected. We don't
want array foreign keys to have different behavior than regular
foreign keys, and regular foreign keys don't do this automatically.
We could change that, but I suspect it would cause us some pretty
serious problems with upgrades from older versions with the existing
behavior to newer versions with the revised behavior.
There are other problems, too. Suppose the user creates the foreign
key and then drops the associated index; then, they run pg_dump. Will
restoring the dump recreate the index? If so, then you've broken
dump/restore, because now it doesn't actually recreate the original
state of the database. You might think of fixing this by not letting
the index be dropped, but that's problematic too, because a
fairly-standard way of removing index bloat is to create a new index
with the "concurrently" flag and then drop the old one. Another
problem entirely is that the auto-generated index will need to have an
auto-generated name, and that name might happen to conflict with the
name of some other object that already exists in the database, which
doesn't initially seem like a problem because you can just generate a
different name instead; indeed, we already do such things. But the
thorny point is that you have to preserve whatever name you choose --
and the linkage to the array foreign key that caused it to be created
-- across a dump/restore cycle; otherwise you'll have cases where
conflicting names cause failures. I doubt this is a comprehensive
list of things that might go wrong; it's intended as an illustrative
list, not an exhaustive one.
This is a jumbo king-sized can of worms, and even a very experienced
contributor would likely find it extremely difficult to sort all of
the problems that would result from a change in this area.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
@ 2017-07-24 20:50 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-07-24 20:50 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
It certainly is, thank you for the heads up. I included a note to encourage
the user to index the referencing column instead.
On Sun, Jul 23, 2017 at 4:41 AM, Robert Haas <robertmhaas@gmail.com> wrote:
>
> This is a jumbo king-sized can of worms, and even a very experienced
> contributor would likely find it extremely difficult to sort all of
> the problems that would result from a change in this area.
Best Regards,
Mark Rofail
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-24 20:51 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-07-24 20:51 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
>
> However, there is a bug that prevented me from testing the third scenario,
> I assume there's an issue of incompatible types problem since the right
> operand type is anyelement and the supporting procedures expect anyarray.
> I am working on debugging it right now.
>
I have also solved the bug that prevented me from performance testing the
New Patch with the Index in place.
Here is a summary of the results:
A- Original Patch
DELETE Average Execution time = 3.508 ms
UPDATE Average Execution time = 3.239 ms
B- New Patch
DELETE Average Execution time = 4.970 ms
UPDATE Average Execution time = 4.170 ms
C- With Index
DELETE Average Execution time = 0.169 ms
UPDATE Average Execution time = 0.147 ms
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-24 21:08 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-07-24 21:08 UTC (permalink / raw)
To: Robert Haas <robertmhaas@gmail.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>
Here is the new Patch with the bug fixes and the New Patch with the Index
in place performance results.
I just want to point this out because I still can't believe the numbers. In
reference to the old patch:
The new patch without the index suffers a 41.68% slow down, while the new
patch with the index has a 95.18% speed up!
Best Regards,
Mark Rofail
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
elemOperatorV4.patch (text/x-patch, 21.3K) download
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index c3fe34888e..828e5a6c07 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -816,6 +816,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
for more information).
Multi-column keys with more than one <literal>ELEMENT</literal> column
are currently not allowed.
+
+ It is advisable to index the refrencing column using GIN index as it considerably enhances the performance.
</para>
<para>
diff --git a/src/backend/access/gin/ginarrayproc.c b/src/backend/access/gin/ginarrayproc.c
index a5238c3af5..416ed60b0c 100644
--- a/src/backend/access/gin/ginarrayproc.c
+++ b/src/backend/access/gin/ginarrayproc.c
@@ -24,6 +24,7 @@
#define GinContainsStrategy 2
#define GinContainedStrategy 3
#define GinEqualStrategy 4
+#define GinContainsElemStrategy 5
/*
@@ -43,7 +44,7 @@ ginarrayextract(PG_FUNCTION_ARGS)
bool *nulls;
int nelems;
- get_typlenbyvalalign(ARR_ELEMTYPE(array),
+ get_typlenbyvalalign(ARR_ELEMTYPE(array),
&elmlen, &elmbyval, &elmalign);
deconstruct_array(array,
@@ -79,7 +80,8 @@ Datum
ginqueryarrayextract(PG_FUNCTION_ARGS)
{
/* Make copy of array input to ensure it doesn't disappear while in use */
- ArrayType *array = PG_GETARG_ARRAYTYPE_P_COPY(0);
+ ArrayType *array;
+ Datum elem;
int32 *nkeys = (int32 *) PG_GETARG_POINTER(1);
StrategyNumber strategy = PG_GETARG_UINT16(2);
@@ -94,45 +96,64 @@ ginqueryarrayextract(PG_FUNCTION_ARGS)
bool *nulls;
int nelems;
- get_typlenbyvalalign(ARR_ELEMTYPE(array),
- &elmlen, &elmbyval, &elmalign);
+ if (strategy == GinContainsElemStrategy)
+ {
+ /* only items that match the queried element
+ are considered candidate */
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+
+ elem = PG_GETARG_DATUM(0);
+ PG_RETURN_POINTER(elem);
+ }
+ else
+ {
+ array = PG_GETARG_ARRAYTYPE_P_COPY(0);
- deconstruct_array(array,
- ARR_ELEMTYPE(array),
- elmlen, elmbyval, elmalign,
- &elems, &nulls, &nelems);
+ get_typlenbyvalalign(ARR_ELEMTYPE(array),
+ &elmlen, &elmbyval, &elmalign);
- *nkeys = nelems;
- *nullFlags = nulls;
+ deconstruct_array(array,
+ ARR_ELEMTYPE(array),
+ elmlen, elmbyval, elmalign,
+ &elems, &nulls, &nelems);
- switch (strategy)
- {
- case GinOverlapStrategy:
- *searchMode = GIN_SEARCH_MODE_DEFAULT;
- break;
- case GinContainsStrategy:
- if (nelems > 0)
+ *nkeys = nelems;
+ *nullFlags = nulls;
+
+ switch (strategy)
+ {
+ case GinOverlapStrategy:
*searchMode = GIN_SEARCH_MODE_DEFAULT;
- else /* everything contains the empty set */
- *searchMode = GIN_SEARCH_MODE_ALL;
- break;
- case GinContainedStrategy:
- /* empty set is contained in everything */
- *searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
- break;
- case GinEqualStrategy:
- if (nelems > 0)
+ break;
+ case GinContainsElemStrategy:
+ /* only items that match the queried element
+ are considered candidate */
*searchMode = GIN_SEARCH_MODE_DEFAULT;
- else
+ break;
+ case GinContainsStrategy:
+ if (nelems > 0)
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+ else /* everything contains the empty set */
+ *searchMode = GIN_SEARCH_MODE_ALL;
+ break;
+ case GinContainedStrategy:
+ /* empty set is contained in everything */
*searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
- break;
- default:
- elog(ERROR, "ginqueryarrayextract: unknown strategy number: %d",
- strategy);
+ break;
+ case GinEqualStrategy:
+ if (nelems > 0)
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+ else
+ *searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
+ break;
+ default:
+ elog(ERROR, "ginqueryarrayextract: unknown strategy number: %d",
+ strategy);
}
/* we should not free array, elems[i] points into it */
PG_RETURN_POINTER(elems);
+ }
}
/*
@@ -171,6 +192,7 @@ ginarrayconsistent(PG_FUNCTION_ARGS)
}
}
break;
+ case GinContainsElemStrategy:
case GinContainsStrategy:
/* result is not lossy */
*recheck = false;
@@ -258,7 +280,8 @@ ginarraytriconsistent(PG_FUNCTION_ARGS)
}
}
break;
- case GinContainsStrategy:
+ case GinContainsElemStrategy:
+ case GinContainsStrategy:
/* must have all elements in check[] true, and no nulls */
res = GIN_TRUE;
for (i = 0; i < nkeys; i++)
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 34dadd6e19..8c9eb0c676 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -4232,6 +4232,117 @@ arraycontained(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(result);
}
+/*
+ * array_contains_elem : checks an array for a spefific element
+ */
+static bool
+array_contains_elem(AnyArrayType *array, Datum elem, Oid element_type,
+ bool element_isnull, Oid collation, void **fn_extra)
+{
+ Oid arr_type = AARR_ELEMTYPE(array);
+ TypeCacheEntry *typentry;
+ int nelems;
+ int typlen;
+ bool typbyval;
+ char typalign;
+ int i;
+ array_iter it1;
+ FunctionCallInfoData locfcinfo;
+
+ if (arr_type != element_type)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot compare different element types")));
+
+ if (element_isnull)
+ return false;
+
+ /*
+ * We arrange to look up the equality function only once per series of
+ * calls, assuming the element type doesn't change underneath us. The
+ * typcache is used so that we have no memory leakage when being used as
+ * an index support function.
+ */
+ typentry = (TypeCacheEntry *)*fn_extra;
+ if (typentry == NULL ||
+ typentry->type_id != arr_type)
+ {
+ typentry = lookup_type_cache(arr_type,
+ TYPECACHE_EQ_OPR_FINFO);
+ if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(arr_type))));
+ *fn_extra = (void *)typentry;
+ }
+ typlen = typentry->typlen;
+ typbyval = typentry->typbyval;
+ typalign = typentry->typalign;
+
+ /*
+ * Apply the comparison operator to each pair of array elements.
+ */
+ InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+ collation, NULL, NULL);
+
+ /* Loop over source data */
+ nelems = ArrayGetNItems(AARR_NDIM(array), AARR_DIMS(array));
+ array_iter_setup(&it1, array);
+
+ for (i = 0; i < nelems; i++)
+ {
+ Datum elt1;
+ bool isnull;
+ bool oprresult;
+
+ /* Get element, checking for NULL */
+ elt1 = array_iter_next(&it1, &isnull, i, typlen, typbyval, typalign);
+
+ /*
+ * We assume that the comparison operator is strict, so a NULL can't
+ * match anything. XXX this diverges from the "NULL=NULL" behavior of
+ * array_eq, should we act like that?
+ */
+ if (isnull)
+ continue;
+
+ /*
+ * Apply the operator to the element pair
+ */
+ locfcinfo.arg[0] = elt1;
+ locfcinfo.arg[1] = elem;
+ locfcinfo.argnull[0] = false;
+ locfcinfo.argnull[1] = false;
+ locfcinfo.isnull = false;
+ oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+ if (oprresult)
+ return true;
+ }
+
+ return false;
+}
+
+Datum
+arraycontainselem(PG_FUNCTION_ARGS)
+{
+ AnyArrayType *array = PG_GETARG_ANY_ARRAY(0);
+ Datum elem = PG_GETARG_DATUM(1);
+ Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ Oid collation = PG_GET_COLLATION();
+ bool element_isnull = PG_ARGISNULL(1);
+ bool result;
+
+ result = array_contains_elem(array, elem, element_type,
+ element_isnull, collation,
+ &fcinfo->flinfo->fn_extra);
+
+ /* Avoid leaking memory when handed toasted input. */
+ AARR_FREE_IF_COPY(array, 0);
+
+ PG_RETURN_BOOL(result);
+}
+
/*-----------------------------------------------------------------------------
* Array iteration functions
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 3a25ba52f3..9e7d66df7e 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -2650,7 +2650,7 @@ quoteRelationName(char *buffer, Relation rel)
* ri_GenerateQual --- generate a WHERE clause equating two variables
*
* The idea is to append " sep leftop op rightop" to buf, or if fkreftype is
- * FKCONSTR_REF_EACH_ELEMENT, append " sep leftop op ANY(rightop)" to buf.
+ * FKCONSTR_REF_EACH_ELEMENT, append " sep leftop @>> rightop" to buf.
*
* The complexity comes from needing to be sure that the parser will select
* the desired operator. We always name the operator using
@@ -2694,21 +2694,34 @@ ri_GenerateQual(StringInfo buf,
else
oprright = operform->oprright;
- appendStringInfo(buf, " %s %s", sep, leftop);
- if (leftoptype != operform->oprleft)
- ri_add_cast_to(buf, operform->oprleft);
-
- appendStringInfo(buf, " OPERATOR(%s.%s) ",
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT){
+ appendStringInfo(buf, " %s %s", sep, rightop);
+
+ if (rightoptype != oprright)
+ ri_add_cast_to(buf, oprright);
+
+ appendStringInfo(buf, " @>> ");
+
+ appendStringInfoString(buf, leftop);
+
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+ }
+ else{
+ appendStringInfo(buf, " %s %s", sep, leftop);
+
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+
+ appendStringInfo(buf, " OPERATOR(%s.%s) ",
quote_identifier(nspname), oprname);
-
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- appendStringInfoString(buf, "ANY (");
- appendStringInfoString(buf, rightop);
- if (rightoptype != oprright)
- ri_add_cast_to(buf, oprright);
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- appendStringInfoChar(buf, ')');
+ appendStringInfoString(buf, rightop);
+
+ if (rightoptype != oprright)
+ ri_add_cast_to(buf, oprright);
+ }
+
ReleaseSysCache(opertup);
}
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index f850be490a..55a43d0b90 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -689,7 +689,8 @@ DATA(insert ( 2745 2277 2277 1 s 2750 2742 0 ));
DATA(insert ( 2745 2277 2277 2 s 2751 2742 0 ));
DATA(insert ( 2745 2277 2277 3 s 2752 2742 0 ));
DATA(insert ( 2745 2277 2277 4 s 1070 2742 0 ));
-
+DATA(insert ( 2745 2277 2283 5 s 6108 2742 0 )); /* anyarray @>> anyelem */
+
/*
* btree enum_ops
*/
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index ffabc2003b..8a9d616d29 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1570,6 +1570,8 @@ DESCR("contains");
DATA(insert OID = 2752 ( "<@" PGNSP PGUID b f f 2277 2277 16 2751 0 arraycontained arraycontsel arraycontjoinsel ));
DESCR("is contained by");
#define OID_ARRAY_CONTAINED_OP 2752
+DATA(insert OID = 6108 ( "@>>" PGNSP PGUID b f f 2277 2283 16 0 0 arraycontainselem arraycontsel arraycontjoinsel ));
+DESCR("containselem");
/* capturing operators to preserve pre-8.3 behavior of text concatenation */
DATA(insert OID = 2779 ( "||" PGNSP PGUID b f f 25 2776 25 0 0 textanycat - - ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 8b33b4e0ea..01b207d363 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4308,6 +4308,7 @@ DESCR("GIN array support (obsolete)");
DATA(insert OID = 2747 ( arrayoverlap PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arrayoverlap _null_ _null_ _null_ ));
DATA(insert OID = 2748 ( arraycontains PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arraycontains _null_ _null_ _null_ ));
DATA(insert OID = 2749 ( arraycontained PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arraycontained _null_ _null_ _null_ ));
+DATA(insert OID = 6109 ( arraycontainselem PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2283" _null_ _null_ _null_ _null_ _null_ arraycontainselem _null_ _null_ _null_ ));
/* BRIN minmax */
DATA(insert OID = 3383 ( brin_minmax_opcinfo PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_opcinfo _null_ _null_ _null_ ));
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index c730563f03..0a9eff4d04 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -737,6 +737,17 @@ SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(6 rows)
+SELECT * FROM array_op_test WHERE i @>> 32 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+ 98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
+ 100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
+(6 rows)
+
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -761,6 +772,19 @@ SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
(8 rows)
+SELECT * FROM array_op_test WHERE i @>> 17 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
+ 53 | {38,17} | {AAAAAAAAAAA21658}
+ 65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+(8 rows)
+
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -942,6 +966,11 @@ SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
-------+---+---
(0 rows)
+SELECT * FROM array_op_test WHERE i @>> NULL ORDER BY seqno;
+ seqno | i | t
+-------+---+---
+(0 rows)
+
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
seqno | i | t
-------+---+---
@@ -953,6 +982,15 @@ SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
101 | {} | {}
(1 row)
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAA72908' ORDER BY seqno;
+ seqno | i | t
+-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
+ 22 | {11,6,56,62,53,30} | {AAAAAAAA72908}
+ 45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}
+ 72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+(4 rows)
+
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
seqno | i | t
-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
@@ -971,6 +1009,14 @@ SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
(4 rows)
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAAAA646' ORDER BY seqno;
+ seqno | i | t
+-------+------------------+--------------------------------------------------------------------
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+ 96 | {23,97,43} | {AAAAAAAAAA646,A87088}
+(3 rows)
+
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
seqno | i | t
-------+------------------+--------------------------------------------------------------------
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index fcf8bd7565..25ef369951 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1811,6 +1811,7 @@ ORDER BY 1, 2, 3;
2742 | 2 | @@@
2742 | 3 | <@
2742 | 4 | =
+ 2742 | 5 | @>>
2742 | 7 | @>
2742 | 9 | ?
2742 | 10 | ?|
@@ -1878,7 +1879,7 @@ ORDER BY 1, 2, 3;
4000 | 25 | <<=
4000 | 26 | >>
4000 | 27 | >>=
-(121 rows)
+(122 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 25dd4e2c6d..f6fb507bdd 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -313,8 +313,10 @@ SELECT ARRAY[0,0] || ARRAY[1,1] || ARRAY[2,2] AS "{0,0,1,1,2,2}";
SELECT 0 || ARRAY[1,2] || 3 AS "{0,1,2,3}";
SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> 32 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> 17 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{32,17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32,17}' ORDER BY seqno;
@@ -325,11 +327,14 @@ SELECT * FROM array_op_test WHERE i && '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> NULL ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAA72908' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAAAA646' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno;
modified_with_index_results.out (application/octet-stream, 643.1K) download
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-24 21:25 ` Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Erik Rijkers @ 2017-07-24 21:25 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <aekorotkov@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers@postgresql.org, David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>; pgsql-hackers-owner@postgresql.org
On 2017-07-24 23:08, Mark Rofail wrote:
> Here is the new Patch with the bug fixes and the New Patch with the
> Index
> in place performance results.
>
> I just want to point this out because I still can't believe the
> numbers. In
> reference to the old patch:
> The new patch without the index suffers a 41.68% slow down, while the
> new
> patch with the index has a 95.18% speed up!
> [elemOperatorV4.patch]
This patch doesn't apply to HEAD at the moment ( e2c8100e6072936 ).
Can you have a look?
thanks,
Erik Rijkers
patching file doc/src/sgml/ref/create_table.sgml
Hunk #1 succeeded at 816 with fuzz 3.
patching file src/backend/access/gin/ginarrayproc.c
patching file src/backend/utils/adt/arrayfuncs.c
patching file src/backend/utils/adt/ri_triggers.c
Hunk #1 FAILED at 2650.
Hunk #2 FAILED at 2694.
2 out of 2 hunks FAILED -- saving rejects to file
src/backend/utils/adt/ri_triggers.c.rej
patching file src/include/catalog/pg_amop.h
patching file src/include/catalog/pg_operator.h
patching file src/include/catalog/pg_proc.h
patching file src/test/regress/expected/arrays.out
patching file src/test/regress/expected/opr_sanity.out
patching file src/test/regress/sql/arrays.sql
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
@ 2017-07-24 21:31 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-07-24 21:31 UTC (permalink / raw)
To: Erik Rijkers <er@xs4all.nl>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <aekorotkov@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers@postgresql.org, David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>; pgsql-hackers-owner@postgresql.org
On Mon, Jul 24, 2017 at 11:25 PM, Erik Rijkers <er@xs4all.nl> wrote:
>
> This patch doesn't apply to HEAD at the moment ( e2c8100e6072936 ).
>
My bad, I should have mentioned that the patch is dependant on the original
patch.
Here is a *unified* patch that I just tested.
I would appreciate it if you could review it.
Best Regards,
Mark Rofail
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
Array-ELEMENT-foreign-key-v4.patch (text/x-patch, 124.2K) download
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ea655a10a8..0d3a4c31d2 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2324,7 +2324,16 @@ SCRAM-SHA-256$<replaceable><iteration count></>:<replaceable><salt><
</row>
<row>
- <entry><structfield>conpfeqop</structfield></entry>
+ <entry><structfield>confreftype</structfield></entry>
+ <entry><type>char[]</type></entry>
+ <entry></entry>
+ <entry>If a foreign key, the reference semantics for each column:
+ <literal>p</> = plain (simple equality),
+ <literal>e</> = each element of referencing array must have a match
+ </entry>
+ </row>
+
+ <row> <entry><structfield>conpfeqop</structfield></entry>
<entry><type>oid[]</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</></entry>
<entry>If a foreign key, list of the equality operators for PK = FK comparisons</entry>
@@ -2369,6 +2378,12 @@ SCRAM-SHA-256$<replaceable><iteration count></>:<replaceable><salt><
</table>
<para>
+ When <structfield>confreftype</structfield> indicates array-vs-scalar
+ foreign key reference semantics, the equality operators listed in
+ <structfield>conpfeqop</structfield> etc are for the array's element type.
+ </para>
+
+ <para>
In the case of an exclusion constraint, <structfield>conkey</structfield>
is only useful for constraint elements that are simple column references.
For other cases, a zero appears in <structfield>conkey</structfield>
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index b05a9c2150..75da196334 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -882,6 +882,111 @@ CREATE TABLE order_items (
</para>
</sect2>
+ <sect2 id="ddl-constraints-element-fk">
+ <title>Array ELEMENT Foreign Keys</title>
+
+ <indexterm>
+ <primary>ELEMENT foreign key</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>Array ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>referential integrity</primary>
+ </indexterm>
+
+ <para>
+ Another option you have with foreign keys is to use a
+ referencing column which is an array of elements with
+ the same type (or a compatible one) as the referenced
+ column in the related table. This feature is called
+ <firstterm>array element foreign key</firstterm> and is implemented
+ in PostgreSQL with <firstterm>ELEMENT foreign key constraints</firstterm>,
+ as described in the following example:
+
+<programlisting>
+CREATE TABLE drivers (
+ driver_id integer PRIMARY KEY,
+ first_name text,
+ last_name text,
+ ...
+);
+
+CREATE TABLE races (
+ race_id integer PRIMARY KEY,
+ title text,
+ race_day DATE,
+ ...
+ final_positions integer[] <emphasis>ELEMENT REFERENCES drivers</emphasis>
+);
+</programlisting>
+
+ The above example uses an array (<literal>final_positions</literal>)
+ to store the results of a race: for each of its elements
+ a referential integrity check is enforced on the
+ <literal>drivers</literal> table.
+ Note that <literal>ELEMENT REFERENCES</literal> is an extension
+ of PostgreSQL and it is not included in the SQL standard.
+ </para>
+
+ <para>
+ Even though the most common use case for array <literal>ELEMENT</literal>
+ foreign keys is on a single column key, you can define an <quote>array
+ <literal>ELEMENT</literal> foreign key constraint</quote> on a group
+ of columns. As the following example shows, it must be written in table
+ constraint form:
+
+<programlisting>
+CREATE TABLE available_moves (
+ kind text,
+ move text,
+ description text,
+ PRIMARY KEY (kind, move)
+);
+
+CREATE TABLE paths (
+ description text,
+ kind text,
+ moves text[],
+ <emphasis>FOREIGN KEY (kind, ELEMENT moves) REFERENCES available_moves (kind, move)</emphasis>
+);
+
+INSERT INTO available_moves VALUES ('relative', 'LN', 'look north');
+INSERT INTO available_moves VALUES ('relative', 'RL', 'rotate left');
+INSERT INTO available_moves VALUES ('relative', 'RR', 'rotate right');
+INSERT INTO available_moves VALUES ('relative', 'MF', 'move forward');
+INSERT INTO available_moves VALUES ('absolute', 'N', 'move north');
+INSERT INTO available_moves VALUES ('absolute', 'S', 'move south');
+INSERT INTO available_moves VALUES ('absolute', 'E', 'move east');
+INSERT INTO available_moves VALUES ('absolute', 'W', 'move west');
+
+INSERT INTO paths VALUES ('L-shaped path', 'relative', '{LN, RL, MF, RR, MF, MF}');
+INSERT INTO paths VALUES ('L-shaped path', 'absolute', '{W, N, N}');
+</programlisting>
+
+ On top of standard foreign key requirements,
+ array <literal>ELEMENT</literal> foreign key constraints
+ require that the referencing column is an array of a compatible
+ type of the corresponding referenced column.
+ </para>
+
+ <para>
+ For more detailed information on array <literal>ELEMENT</literal>
+ foreign key options and special cases, please refer to the documentation
+ for <xref linkend="sql-createtable-foreign-key"> and
+ <xref linkend="sql-createtable-element-foreign-key-constraints">.
+ </para>
+
+ </sect2>
+
<sect2 id="ddl-constraints-exclusion">
<title>Exclusion Constraints</title>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index e9c2c49533..0228fbe941 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -65,7 +65,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ] |
UNIQUE <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="PARAMETER">index_parameters</replaceable> |
- REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
+ [ELEMENT] REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -76,7 +76,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
UNIQUE ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ] |
- FOREIGN KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> [, ... ] ) ]
+ FOREIGN KEY ( [ELEMENT] <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> [, ... ] ) ]
[ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -812,10 +812,10 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</listitem>
</varlistentry>
- <varlistentry>
+ <varlistentry id="sql-createtable-foreign-key" xreflabel="FOREIGN KEY">
<term><literal>REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
- <term><literal>FOREIGN KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] )
+ <term><literal>FOREIGN KEY ( [ELEMENT] <replaceable class="parameter">column_name</replaceable> [, ... ] )
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] ) ]
[ MATCH <replaceable class="parameter">matchtype</replaceable> ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ]
@@ -839,6 +839,21 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</para>
<para>
+ In case the column name <replaceable class="parameter">column</replaceable>
+ is prepended with the <literal>ELEMENT</literal> keyword and <replaceable
+ class="parameter">column</replaceable> is an array of elements compatible
+ with the corresponding <replaceable class="parameter">refcolumn</replaceable>
+ in <replaceable class="parameter">reftable</replaceable>, an
+ array <literal>ELEMENT</literal> foreign key constraint is put in place
+ (see <xref linkend="sql-createtable-element-foreign-key-constraints">
+ for more information).
+ Multi-column keys with more than one <literal>ELEMENT</literal> column
+ are currently not allowed.
+
+ It is advisable to index the refrencing column using GIN index as it considerably enhances the performance.
+ </para>
+
+ <para>
A value inserted into the referencing column(s) is matched against the
values of the referenced table and referenced columns using the
given match type. There are three match types: <literal>MATCH
@@ -901,7 +916,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
<para>
Delete any rows referencing the deleted row, or update the
values of the referencing column(s) to the new values of the
- referenced columns, respectively.
+ referenced columns, respectively. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -910,7 +926,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
<term><literal>SET NULL</literal></term>
<listitem>
<para>
- Set the referencing column(s) to null.
+ Set the referencing column(s) to null. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -922,6 +939,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
Set the referencing column(s) to their default values.
(There must be a row in the referenced table matching the default
values, if they are not null, or the operation will fail.)
+ Currently not supported with array <literal>ELEMENT</literal>
+ foreign keys.
</para>
</listitem>
</varlistentry>
@@ -938,6 +957,61 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</varlistentry>
<varlistentry>
+ <varlistentry id="sql-createtable-element-foreign-key-constraints" xreflabel="ELEMENT REFERENCES">
+ <term><literal>ELEMENT REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
+
+ <listitem>
+ <para>
+ The <literal>ELEMENT REFERENCES</literal> definition specifies
+ an <quote>array <literal>ELEMENT</literal> foreign key</quote>,
+ a special kind of foreign key
+ constraint requiring the referencing column to be an array of elements
+ of the same type (or a compatible one) as the referenced column
+ in the referenced table. The value of each element of the
+ <replaceable class="parameter">refcolumn</replaceable> array
+ will be matched against some row of <replaceable
+ class="parameter">reftable</replaceable>.
+ </para>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys are an extension
+ of PostgreSQL and are not included in the SQL standard.
+ </para>
+
+ <para>
+ Even with <literal>ELEMENT</literal> foreign keys, modifications
+ in the referenced column can trigger actions to be performed on
+ the referencing array.
+ Similarly to standard foreign keys, you can specify these
+ actions using the <literal>ON DELETE</literal> and
+ <literal>ON UPDATE</literal> clauses.
+ However, only the two following actions for each clause are
+ currently allowed:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>NO ACTION</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints. This is the default action.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RESTRICT</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ </listitem>
+ </varlistentry>
+
<term><literal>DEFERRABLE</literal></term>
<term><literal>NOT DEFERRABLE</literal></term>
<listitem>
@@ -1876,6 +1950,16 @@ CREATE TABLE cities_ab_10000_to_100000
</refsect2>
<refsect2>
+ <title id="sql-createtable-foreign-key-arrays">Array <literal>ELEMENT</literal> Foreign Keys</title>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys and the
+ <literal>ELEMENT REFERENCES</literal> clause
+ are a <productname>PostgreSQL</productname> extension.
+ </para>
+ </refsect2>
+
+ <refsect2>
<title><literal>PARTITION BY</> Clause</title>
<para>
diff --git a/src/backend/access/gin/ginarrayproc.c b/src/backend/access/gin/ginarrayproc.c
index a5238c3af5..416ed60b0c 100644
--- a/src/backend/access/gin/ginarrayproc.c
+++ b/src/backend/access/gin/ginarrayproc.c
@@ -24,6 +24,7 @@
#define GinContainsStrategy 2
#define GinContainedStrategy 3
#define GinEqualStrategy 4
+#define GinContainsElemStrategy 5
/*
@@ -43,7 +44,7 @@ ginarrayextract(PG_FUNCTION_ARGS)
bool *nulls;
int nelems;
- get_typlenbyvalalign(ARR_ELEMTYPE(array),
+ get_typlenbyvalalign(ARR_ELEMTYPE(array),
&elmlen, &elmbyval, &elmalign);
deconstruct_array(array,
@@ -79,7 +80,8 @@ Datum
ginqueryarrayextract(PG_FUNCTION_ARGS)
{
/* Make copy of array input to ensure it doesn't disappear while in use */
- ArrayType *array = PG_GETARG_ARRAYTYPE_P_COPY(0);
+ ArrayType *array;
+ Datum elem;
int32 *nkeys = (int32 *) PG_GETARG_POINTER(1);
StrategyNumber strategy = PG_GETARG_UINT16(2);
@@ -94,45 +96,64 @@ ginqueryarrayextract(PG_FUNCTION_ARGS)
bool *nulls;
int nelems;
- get_typlenbyvalalign(ARR_ELEMTYPE(array),
- &elmlen, &elmbyval, &elmalign);
+ if (strategy == GinContainsElemStrategy)
+ {
+ /* only items that match the queried element
+ are considered candidate */
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+
+ elem = PG_GETARG_DATUM(0);
+ PG_RETURN_POINTER(elem);
+ }
+ else
+ {
+ array = PG_GETARG_ARRAYTYPE_P_COPY(0);
- deconstruct_array(array,
- ARR_ELEMTYPE(array),
- elmlen, elmbyval, elmalign,
- &elems, &nulls, &nelems);
+ get_typlenbyvalalign(ARR_ELEMTYPE(array),
+ &elmlen, &elmbyval, &elmalign);
- *nkeys = nelems;
- *nullFlags = nulls;
+ deconstruct_array(array,
+ ARR_ELEMTYPE(array),
+ elmlen, elmbyval, elmalign,
+ &elems, &nulls, &nelems);
- switch (strategy)
- {
- case GinOverlapStrategy:
- *searchMode = GIN_SEARCH_MODE_DEFAULT;
- break;
- case GinContainsStrategy:
- if (nelems > 0)
+ *nkeys = nelems;
+ *nullFlags = nulls;
+
+ switch (strategy)
+ {
+ case GinOverlapStrategy:
*searchMode = GIN_SEARCH_MODE_DEFAULT;
- else /* everything contains the empty set */
- *searchMode = GIN_SEARCH_MODE_ALL;
- break;
- case GinContainedStrategy:
- /* empty set is contained in everything */
- *searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
- break;
- case GinEqualStrategy:
- if (nelems > 0)
+ break;
+ case GinContainsElemStrategy:
+ /* only items that match the queried element
+ are considered candidate */
*searchMode = GIN_SEARCH_MODE_DEFAULT;
- else
+ break;
+ case GinContainsStrategy:
+ if (nelems > 0)
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+ else /* everything contains the empty set */
+ *searchMode = GIN_SEARCH_MODE_ALL;
+ break;
+ case GinContainedStrategy:
+ /* empty set is contained in everything */
*searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
- break;
- default:
- elog(ERROR, "ginqueryarrayextract: unknown strategy number: %d",
- strategy);
+ break;
+ case GinEqualStrategy:
+ if (nelems > 0)
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+ else
+ *searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
+ break;
+ default:
+ elog(ERROR, "ginqueryarrayextract: unknown strategy number: %d",
+ strategy);
}
/* we should not free array, elems[i] points into it */
PG_RETURN_POINTER(elems);
+ }
}
/*
@@ -171,6 +192,7 @@ ginarrayconsistent(PG_FUNCTION_ARGS)
}
}
break;
+ case GinContainsElemStrategy:
case GinContainsStrategy:
/* result is not lossy */
*recheck = false;
@@ -258,7 +280,8 @@ ginarraytriconsistent(PG_FUNCTION_ARGS)
}
}
break;
- case GinContainsStrategy:
+ case GinContainsElemStrategy:
+ case GinContainsStrategy:
/* must have all elements in check[] true, and no nulls */
res = GIN_TRUE;
for (i = 0; i < nkeys; i++)
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index a376b99f1e..3bd0b772a4 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2098,6 +2098,7 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index d25b39bb54..4e0d0bdda0 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1211,6 +1211,7 @@ index_constraint_create(Relation heapRelation,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 1336c46d3f..c197cec11a 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -59,6 +59,7 @@ CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
@@ -82,6 +83,7 @@ CreateConstraintEntry(const char *constraintName,
Datum values[Natts_pg_constraint];
ArrayType *conkeyArray;
ArrayType *confkeyArray;
+ ArrayType *confreftypeArray;
ArrayType *conpfeqopArray;
ArrayType *conppeqopArray;
ArrayType *conffeqopArray;
@@ -119,7 +121,11 @@ CreateConstraintEntry(const char *constraintName,
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = Int16GetDatum(foreignKey[i]);
confkeyArray = construct_array(fkdatums, foreignNKeys,
- INT2OID, 2, true, 's');
+ INT2OID, sizeof(int16), true, 's');
+ for (i = 0; i < foreignNKeys; i++)
+ fkdatums[i] = CharGetDatum(foreignRefType[i]);
+ confreftypeArray = construct_array(fkdatums, foreignNKeys,
+ CHAROID, sizeof(char), true, 'c');
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
conpfeqopArray = construct_array(fkdatums, foreignNKeys,
@@ -136,6 +142,7 @@ CreateConstraintEntry(const char *constraintName,
else
{
confkeyArray = NULL;
+ confreftypeArray = NULL;
conpfeqopArray = NULL;
conppeqopArray = NULL;
conffeqopArray = NULL;
@@ -188,6 +195,11 @@ CreateConstraintEntry(const char *constraintName,
else
nulls[Anum_pg_constraint_confkey - 1] = true;
+ if (confreftypeArray)
+ values[Anum_pg_constraint_confreftype - 1] = PointerGetDatum(confreftypeArray);
+ else
+ nulls[Anum_pg_constraint_confreftype - 1] = true;
+
if (conpfeqopArray)
values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
else
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index bb00858ad1..634a8cd97a 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -6995,6 +6995,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Relation pkrel;
int16 pkattnum[INDEX_MAX_KEYS];
int16 fkattnum[INDEX_MAX_KEYS];
+ char fkreftypes[INDEX_MAX_KEYS];
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
Oid opclasses[INDEX_MAX_KEYS];
@@ -7002,10 +7003,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid ppeqoperators[INDEX_MAX_KEYS];
Oid ffeqoperators[INDEX_MAX_KEYS];
int i;
+ ListCell *lc;
int numfks,
numpks;
Oid indexOid;
Oid constrOid;
+ bool has_array;
bool old_check_ok;
ObjectAddress address;
ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
@@ -7082,6 +7085,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
*/
MemSet(pkattnum, 0, sizeof(pkattnum));
MemSet(fkattnum, 0, sizeof(fkattnum));
+ MemSet(fkreftypes, 0, sizeof(fkreftypes));
MemSet(pktypoid, 0, sizeof(pktypoid));
MemSet(fktypoid, 0, sizeof(fktypoid));
MemSet(opclasses, 0, sizeof(opclasses));
@@ -7094,6 +7098,50 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
fkattnum, fktypoid);
/*
+ * Validate the reference semantics codes, too, and convert list to array
+ * format to pass to CreateConstraintEntry.
+ */
+ Assert(list_length(fkconstraint->fk_reftypes) == numfks);
+ has_array = false;
+ i = 0;
+ foreach(lc, fkconstraint->fk_reftypes)
+ {
+ char reftype = lfirst_int(lc);
+
+ switch (reftype)
+ {
+ case FKCONSTR_REF_PLAIN:
+ /* OK, nothing to do */
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ /* At most one FK column can be an array reference */
+ if (has_array)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign keys support only one array column")));
+ has_array = true;
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d", (int) reftype);
+ break;
+ }
+ fkreftypes[i] = reftype;
+ i++;
+ }
+
+ /* Array foreign keys support only NO ACTION and RESTRICT actions */
+ if (has_array)
+ {
+ if ((fkconstraint->fk_upd_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_upd_action != FKCONSTR_ACTION_RESTRICT) ||
+ (fkconstraint->fk_del_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("array foreign keys support only NO ACTION and RESTRICT actions")));
+ }
+
+ /*
* If the attribute list for the referenced table was omitted, lookup the
* definition of the primary key and use it. Otherwise, validate the
* supplied attribute list. In either case, discover the index OID and
@@ -7179,6 +7227,65 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
eqstrategy = BTEqualStrategyNumber;
/*
+ * If this is an array foreign key, we must look up the operators for
+ * the array element type, not the array type itself.
+ */
+ if (fkreftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ Oid elemopclass;
+
+ /* We look through any domain here */
+ fktype = get_base_element_type(fktype);
+ if (!OidIsValid(fktype))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has type %s which is not an array type.",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktypoid[i]))));
+
+ /*
+ * For the moment, we must also insist that the array's element
+ * type have a default btree opclass that is in the index's
+ * opfamily. This is necessary because ri_triggers.c relies on
+ * COUNT(DISTINCT x) on the element type, as well as on array_eq()
+ * on the array type, and we need those operations to have the
+ * same notion of equality that we're using otherwise.
+ *
+ * XXX this restriction is pretty annoying, considering the effort
+ * that's been put into the rest of the RI mechanisms to make them
+ * work with nondefault equality operators. In particular, it
+ * means that the cast-to-PK-datatype code path isn't useful for
+ * array-to-scalar references.
+ */
+ elemopclass = GetDefaultOpClass(fktype, BTREE_AM_OID);
+ if (!OidIsValid(elemopclass) ||
+ get_opclass_family(elemopclass) != opfamily)
+ {
+ /* Get the index opclass's name for the error message. */
+ char *opcname;
+
+ cla_ht = SearchSysCache1(CLAOID,
+ ObjectIdGetDatum(opclasses[i]));
+ if (!HeapTupleIsValid(cla_ht))
+ elog(ERROR, "cache lookup failed for opclass %u",
+ opclasses[i]);
+ cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
+ opcname = pstrdup(NameStr(cla_tup->opcname));
+ ReleaseSysCache(cla_ht);
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has element type %s which does not have a default btree operator class that's compatible with class \"%s\".",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktype),
+ opcname)));
+ }
+ }
+
+ /*
* There had better be a primary equality operator for the index.
* We'll use it for PK = PK comparisons.
*/
@@ -7239,14 +7346,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("foreign key constraint \"%s\" "
- "cannot be implemented",
- fkconstraint->conname),
- errdetail("Key columns \"%s\" and \"%s\" "
- "are of incompatible types: %s and %s.",
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key columns \"%s\" and \"%s\" are of incompatible types: %s and %s.",
strVal(list_nth(fkconstraint->fk_attrs, i)),
strVal(list_nth(fkconstraint->pk_attrs, i)),
- format_type_be(fktype),
+ format_type_be(fktypoid[i]),
format_type_be(pktype))));
if (old_check_ok)
@@ -7275,6 +7380,13 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
* We may assume that pg_constraint.conkey is not changing.
*/
old_fktype = tab->oldDesc->attrs[fkattnum[i] - 1]->atttypid;
+ if (fkreftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ old_fktype = get_base_element_type(old_fktype);
+ /* this shouldn't happen ... */
+ if (!OidIsValid(old_fktype))
+ elog(ERROR, "old foreign key column is not an array");
+ }
new_fktype = fktype;
old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
&old_castfunc);
@@ -7341,6 +7453,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
indexOid,
RelationGetRelid(pkrel),
pkattnum,
+ fkreftypes,
pfeqoperators,
ppeqoperators,
ffeqoperators,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index b502941b08..cb017cbe14 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -635,6 +635,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
@@ -1006,6 +1007,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
char fk_matchtype = FKCONSTR_MATCH_SIMPLE;
List *fk_attrs = NIL;
List *pk_attrs = NIL;
+ List *fk_reftypes = NIL;
StringInfoData buf;
int funcnum;
OldTriggerInfo *info = NULL;
@@ -1035,7 +1037,10 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
if (i % 2)
fk_attrs = lappend(fk_attrs, arg);
else
- pk_attrs = lappend(pk_attrs, arg);
+ {
+ pk_attrs = lappend(pk_attrs, arg);
+ fk_reftypes = lappend_int(fk_reftypes, FKCONSTR_REF_PLAIN);
+ }
}
/* Prepare description of constraint for use in messages */
@@ -1174,6 +1179,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
fkcon->conname = constr_name;
fkcon->fk_attrs = fk_attrs;
fkcon->pk_attrs = pk_attrs;
+ fkcon->fk_reftypes = fk_reftypes;
fkcon->fk_matchtype = fk_matchtype;
switch (info->funcoids[0])
{
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index c2fc59d1aa..4f089d53a9 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -3075,6 +3075,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 45a04b0b27..01238bfd71 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2843,6 +2843,7 @@ _copyConstraint(const Constraint *from)
COPY_NODE_FIELD(pktable);
COPY_NODE_FIELD(fk_attrs);
COPY_NODE_FIELD(pk_attrs);
+ COPY_NODE_FIELD(fk_reftypes);
COPY_SCALAR_FIELD(fk_matchtype);
COPY_SCALAR_FIELD(fk_upd_action);
COPY_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 8d92c03633..d35f3d1d9a 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2575,6 +2575,7 @@ _equalConstraint(const Constraint *a, const Constraint *b)
COMPARE_NODE_FIELD(pktable);
COMPARE_NODE_FIELD(fk_attrs);
COMPARE_NODE_FIELD(pk_attrs);
+ COMPARE_NODE_FIELD(fk_reftypes);
COMPARE_SCALAR_FIELD(fk_matchtype);
COMPARE_SCALAR_FIELD(fk_upd_action);
COMPARE_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 379d92a2b0..119d100b37 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3490,6 +3490,7 @@ _outConstraint(StringInfo str, const Constraint *node)
WRITE_NODE_FIELD(pktable);
WRITE_NODE_FIELD(fk_attrs);
WRITE_NODE_FIELD(pk_attrs);
+ WRITE_NODE_FIELD(fk_reftypes);
WRITE_CHAR_FIELD(fk_matchtype);
WRITE_CHAR_FIELD(fk_upd_action);
WRITE_CHAR_FIELD(fk_del_action);
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 9f37f1b920..163719c7a8 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -743,6 +743,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
* list of FK constraints to be processed later.
*/
constraint->fk_attrs = list_make1(makeString(column->colname));
+ /* grammar should have set fk_reftypes */
+ Assert(list_length(constraint->fk_reftypes) == 1);
cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
break;
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 34dadd6e19..8c9eb0c676 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -4232,6 +4232,117 @@ arraycontained(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(result);
}
+/*
+ * array_contains_elem : checks an array for a spefific element
+ */
+static bool
+array_contains_elem(AnyArrayType *array, Datum elem, Oid element_type,
+ bool element_isnull, Oid collation, void **fn_extra)
+{
+ Oid arr_type = AARR_ELEMTYPE(array);
+ TypeCacheEntry *typentry;
+ int nelems;
+ int typlen;
+ bool typbyval;
+ char typalign;
+ int i;
+ array_iter it1;
+ FunctionCallInfoData locfcinfo;
+
+ if (arr_type != element_type)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot compare different element types")));
+
+ if (element_isnull)
+ return false;
+
+ /*
+ * We arrange to look up the equality function only once per series of
+ * calls, assuming the element type doesn't change underneath us. The
+ * typcache is used so that we have no memory leakage when being used as
+ * an index support function.
+ */
+ typentry = (TypeCacheEntry *)*fn_extra;
+ if (typentry == NULL ||
+ typentry->type_id != arr_type)
+ {
+ typentry = lookup_type_cache(arr_type,
+ TYPECACHE_EQ_OPR_FINFO);
+ if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(arr_type))));
+ *fn_extra = (void *)typentry;
+ }
+ typlen = typentry->typlen;
+ typbyval = typentry->typbyval;
+ typalign = typentry->typalign;
+
+ /*
+ * Apply the comparison operator to each pair of array elements.
+ */
+ InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+ collation, NULL, NULL);
+
+ /* Loop over source data */
+ nelems = ArrayGetNItems(AARR_NDIM(array), AARR_DIMS(array));
+ array_iter_setup(&it1, array);
+
+ for (i = 0; i < nelems; i++)
+ {
+ Datum elt1;
+ bool isnull;
+ bool oprresult;
+
+ /* Get element, checking for NULL */
+ elt1 = array_iter_next(&it1, &isnull, i, typlen, typbyval, typalign);
+
+ /*
+ * We assume that the comparison operator is strict, so a NULL can't
+ * match anything. XXX this diverges from the "NULL=NULL" behavior of
+ * array_eq, should we act like that?
+ */
+ if (isnull)
+ continue;
+
+ /*
+ * Apply the operator to the element pair
+ */
+ locfcinfo.arg[0] = elt1;
+ locfcinfo.arg[1] = elem;
+ locfcinfo.argnull[0] = false;
+ locfcinfo.argnull[1] = false;
+ locfcinfo.isnull = false;
+ oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+ if (oprresult)
+ return true;
+ }
+
+ return false;
+}
+
+Datum
+arraycontainselem(PG_FUNCTION_ARGS)
+{
+ AnyArrayType *array = PG_GETARG_ANY_ARRAY(0);
+ Datum elem = PG_GETARG_DATUM(1);
+ Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ Oid collation = PG_GET_COLLATION();
+ bool element_isnull = PG_ARGISNULL(1);
+ bool result;
+
+ result = array_contains_elem(array, elem, element_type,
+ element_isnull, collation,
+ &fcinfo->flinfo->fn_extra);
+
+ /* Avoid leaking memory when handed toasted input. */
+ AARR_FREE_IF_COPY(array, 0);
+
+ PG_RETURN_BOOL(result);
+}
+
/*-----------------------------------------------------------------------------
* Array iteration functions
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index c2891e6fa1..9e7d66df7e 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -118,9 +118,11 @@ typedef struct RI_ConstraintInfo
char confupdtype; /* foreign key's ON UPDATE action */
char confdeltype; /* foreign key's ON DELETE action */
char confmatchtype; /* foreign key's match type */
+ bool has_array; /* true if any reftype is EACH_ELEMENT */
int nkeys; /* number of key columns */
int16 pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */
int16 fk_attnums[RI_MAX_NUMKEYS]; /* attnums of referencing cols */
+ char fk_reftypes[RI_MAX_NUMKEYS]; /* reference semantics */
Oid pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = FK) */
Oid pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = PK) */
Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK = FK) */
@@ -204,7 +206,8 @@ static void ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype);
+ const char *rightop, Oid rightoptype,
+ char fkreftype);
static void ri_add_cast_to(StringInfo buf, Oid typid);
static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
static int ri_NullCheck(HeapTuple tup,
@@ -395,6 +398,7 @@ RI_FKey_check(TriggerData *trigdata)
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
StringInfoData querybuf;
+ StringInfoData countbuf;
char pkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
char paramname[16];
@@ -407,12 +411,22 @@ RI_FKey_check(TriggerData *trigdata)
* FOR KEY SHARE OF x
* The type id's for the $ parameters are those of the
* corresponding FK attributes.
+ *
+ * In case of an array ELEMENT foreign key, the previous query is used
+ * to count the number of matching rows and see if every combination
+ * is actually referenced.
+ * The wrapping query is
+ * SELECT 1 WHERE
+ * (SELECT count(DISTINCT y) FROM unnest($1) y)
+ * = (SELECT count(*) FROM (<QUERY>) z)
* ----------
*/
initStringInfo(&querybuf);
quoteRelationName(pkrelname, pk_rel);
appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
querysep = "WHERE";
+ initStringInfo(&countbuf);
+ appendStringInfo(&countbuf, "SELECT 1 WHERE ");
for (i = 0; i < riinfo->nkeys; i++)
{
Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
@@ -421,18 +435,41 @@ RI_FKey_check(TriggerData *trigdata)
quoteOneName(attname,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
sprintf(paramname, "$%d", i + 1);
+
+ /*
+ * In case of an array ELEMENT foreign key, we check that each
+ * distinct non-null value in the array is present in the PK
+ * table.
+ */
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&countbuf,
+ "(SELECT pg_catalog.count(DISTINCT y) FROM pg_catalog.unnest(%s) y)",
+ paramname);
+
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pf_eq_oprs[i],
- paramname, fk_type);
+ paramname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = fk_type;
}
appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
- /* Prepare and save the plan */
- qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
- &qkey, fk_rel, pk_rel, true);
+ if (riinfo->has_array)
+ {
+ appendStringInfo(&countbuf,
+ " OPERATOR(pg_catalog.=) (SELECT pg_catalog.count(*) FROM (%s) z)",
+ querybuf.data);
+
+ /* Prepare and save the plan for array ELEMENT foreign keys */
+ qplan = ri_PlanCheck(countbuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
+ }
+ else
+ /* Prepare and save the plan */
+ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
}
/*
@@ -559,7 +596,8 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pp_eq_oprs[i],
- paramname, pk_type);
+ paramname, pk_type,
+ FKCONSTR_REF_PLAIN);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -751,7 +789,8 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -974,7 +1013,8 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1130,7 +1170,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1309,7 +1350,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1475,7 +1517,8 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1651,7 +1694,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1817,7 +1861,8 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2008,7 +2053,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2327,6 +2373,14 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
* For MATCH FULL:
* (fk.keycol1 IS NOT NULL [OR ...])
*
+ * In case of an array ELEMENT column, relname is replaced with the
+ * following subquery:
+ *
+ * SELECT unnest("keycol1") k1, "keycol1" ak1 [, ...]
+ * FROM ONLY "public"."fk"
+ *
+ * where all the columns are renamed in order to prevent name collisions.
+ *
* We attach COLLATE clauses to the operators when comparing columns
* that have different collations.
*----------
@@ -2338,15 +2392,46 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
{
quoteOneName(fkattname,
RIAttName(fk_rel, riinfo->fk_attnums[i]));
- appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
+ if (riinfo->has_array)
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%sfk.ak%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.k%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
sep = ", ";
}
quoteRelationName(pkrelname, pk_rel);
quoteRelationName(fkrelname, fk_rel);
- appendStringInfo(&querybuf,
- " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
- fkrelname, pkrelname);
+
+ if (riinfo->has_array)
+ {
+ sep = "";
+ appendStringInfo(&querybuf,
+ " FROM (SELECT ");
+ for (i = 0; i < riinfo->nkeys; i++)
+ {
+ quoteOneName(fkattname,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%spg_catalog.unnest(%s) k%d, %s ak%d",
+ sep, fkattname, i + 1, fkattname, i + 1);
+ else
+ appendStringInfo(&querybuf, "%s%s k%d", sep, fkattname,
+ i + 1);
+ sep = ", ";
+ }
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s) fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
+ }
+ else
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
strcpy(pkattname, "pk.");
strcpy(fkattname, "fk.");
@@ -2360,12 +2445,16 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
quoteOneName(pkattname + 3,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
- quoteOneName(fkattname + 3,
- RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname + 3, "k%d", i + 1);
+ else
+ quoteOneName(fkattname + 3,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
ri_GenerateQual(&querybuf, sep,
pkattname, pk_type,
riinfo->pf_eq_oprs[i],
- fkattname, fk_type);
+ fkattname, fk_type,
+ FKCONSTR_REF_PLAIN);
if (pk_coll != fk_coll)
ri_GenerateQualCollation(&querybuf, pk_coll);
sep = "AND";
@@ -2381,7 +2470,10 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
sep = "";
for (i = 0; i < riinfo->nkeys; i++)
{
- quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname, "k%d", i + 1);
+ else
+ quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
appendStringInfo(&querybuf,
"%sfk.%s IS NOT NULL",
sep, fkattname);
@@ -2557,25 +2649,29 @@ quoteRelationName(char *buffer, Relation rel)
/*
* ri_GenerateQual --- generate a WHERE clause equating two variables
*
- * The idea is to append " sep leftop op rightop" to buf. The complexity
- * comes from needing to be sure that the parser will select the desired
- * operator. We always name the operator using OPERATOR(schema.op) syntax
- * (readability isn't a big priority here), so as to avoid search-path
- * uncertainties. We have to emit casts too, if either input isn't already
- * the input type of the operator; else we are at the mercy of the parser's
- * heuristics for ambiguous-operator resolution.
- */
+ * The idea is to append " sep leftop op rightop" to buf, or if fkreftype is
+ * FKCONSTR_REF_EACH_ELEMENT, append " sep leftop @>> rightop" to buf.
+ *
+ * The complexity comes from needing to be sure that the parser will select
+ * the desired operator. We always name the operator using
+ * OPERATOR(schema.op) syntax (readability isn't a big priority here), so as
+ * to avoid search-path uncertainties. We have to emit casts too, if either
+ * input isn't already the input type of the operator; else we are at the
+ * mercy of the parser's heuristics for ambiguous-operator resolution.
+ */
static void
ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype)
+ const char *rightop, Oid rightoptype,
+ char fkreftype)
{
HeapTuple opertup;
Form_pg_operator operform;
char *oprname;
char *nspname;
+ Oid oprright;
opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opoid));
if (!HeapTupleIsValid(opertup))
@@ -2586,15 +2682,46 @@ ri_GenerateQual(StringInfo buf,
nspname = get_namespace_name(operform->oprnamespace);
- appendStringInfo(buf, " %s %s", sep, leftop);
- if (leftoptype != operform->oprleft)
- ri_add_cast_to(buf, operform->oprleft);
- appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
- appendStringInfoString(buf, oprname);
- appendStringInfo(buf, ") %s", rightop);
- if (rightoptype != operform->oprright)
- ri_add_cast_to(buf, operform->oprright);
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ oprright = get_array_type(operform->oprright);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprright))));
+ }
+ else
+ oprright = operform->oprright;
+
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT){
+ appendStringInfo(buf, " %s %s", sep, rightop);
+
+ if (rightoptype != oprright)
+ ri_add_cast_to(buf, oprright);
+ appendStringInfo(buf, " @>> ");
+
+ appendStringInfoString(buf, leftop);
+
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+ }
+ else{
+ appendStringInfo(buf, " %s %s", sep, leftop);
+
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+
+ appendStringInfo(buf, " OPERATOR(%s.%s) ",
+ quote_identifier(nspname), oprname);
+
+ appendStringInfoString(buf, rightop);
+
+ if (rightoptype != oprright)
+ ri_add_cast_to(buf, oprright);
+ }
+
ReleaseSysCache(opertup);
}
@@ -2801,6 +2928,7 @@ ri_LoadConstraintInfo(Oid constraintOid)
bool isNull;
ArrayType *arr;
int numkeys;
+ int i;
/*
* On the first call initialize the hashtable
@@ -2879,6 +3007,20 @@ ri_LoadConstraintInfo(Oid constraintOid)
pfree(arr); /* free de-toasted copy, if any */
adatum = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype, &isNull);
+ if (isNull)
+ elog(ERROR, "null confreftype for constraint %u", constraintOid);
+ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
+ if (ARR_NDIM(arr) != 1 ||
+ ARR_DIMS(arr)[0] != numkeys ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != CHAROID)
+ elog(ERROR, "confreftype is not a 1-D char array");
+ memcpy(riinfo->fk_reftypes, ARR_DATA_PTR(arr), numkeys * sizeof(char));
+ if ((Pointer) arr != DatumGetPointer(adatum))
+ pfree(arr); /* free de-toasted copy, if any */
+
+ adatum = SysCacheGetAttr(CONSTROID, tup,
Anum_pg_constraint_conpfeqop, &isNull);
if (isNull)
elog(ERROR, "null conpfeqop for constraint %u", constraintOid);
@@ -2921,6 +3063,24 @@ ri_LoadConstraintInfo(Oid constraintOid)
if ((Pointer) arr != DatumGetPointer(adatum))
pfree(arr); /* free de-toasted copy, if any */
+ /*
+ * Fix up some stuff for array foreign keys. We need a has_array flag
+ * indicating whether there's an array foreign key, and we want to set
+ * ff_eq_oprs[i] to array_eq() for array columns, because that's what
+ * makes sense for ri_KeysEqual, and we have no other use for ff_eq_oprs
+ * in this module. (If we did, substituting the array comparator at the
+ * call point in ri_KeysEqual might be more appropriate.)
+ */
+ riinfo->has_array = false;
+ for (i = 0; i < numkeys; i++)
+ {
+ if (riinfo->fk_reftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ riinfo->has_array = true;
+ riinfo->ff_eq_oprs[i] = ARRAY_EQ_OP;
+ }
+ }
+
ReleaseSysCache(tup);
/*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index d83377d1d8..680623534c 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -314,6 +314,9 @@ static char *pg_get_viewdef_worker(Oid viewoid,
static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
static void decompile_column_index_array(Datum column_index_array, Oid relId,
StringInfo buf);
+static void decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf);
static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
const Oid *excludeOps,
@@ -1882,7 +1885,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
{
case CONSTRAINT_FOREIGN:
{
- Datum val;
+ Datum colindexes;
+ Datum reftypes;
bool isnull;
const char *string;
@@ -1890,13 +1894,21 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
appendStringInfoString(&buf, "FOREIGN KEY (");
/* Fetch and build referencing-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_conkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_conkey,
+ &isnull);
if (isnull)
elog(ERROR, "null conkey for constraint %u",
constraintId);
+ reftypes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype,
+ &isnull);
+ if (isnull)
+ elog(ERROR, "null confreftype for constraint %u",
+ constraintId);
- decompile_column_index_array(val, conForm->conrelid, &buf);
+ decompile_fk_column_index_array(colindexes, reftypes,
+ conForm->conrelid, &buf);
/* add foreign relation name */
appendStringInfo(&buf, ") REFERENCES %s(",
@@ -1904,13 +1916,15 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
NIL));
/* Fetch and build referenced-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_confkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confkey,
+ &isnull);
if (isnull)
elog(ERROR, "null confkey for constraint %u",
constraintId);
- decompile_column_index_array(val, conForm->confrelid, &buf);
+ decompile_column_index_array(colindexes,
+ conForm->confrelid, &buf);
appendStringInfoChar(&buf, ')');
@@ -2185,6 +2199,66 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
}
}
+ /*
+ * Convert an int16[] Datum and a char[] Datum into a comma-separated
+ * list of column names for the indicated relation, prefixed by appropriate
+ * keywords depending on the foreign key reference semantics indicated by
+ * the char[] entries. Append the text to buf.
+ */
+ static void
+ decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf)
+ {
+ Datum *keys;
+ int nKeys;
+ Datum *reftypes;
+ int nReftypes;
+ int j;
+
+ /* Extract data from array of int16 */
+ deconstruct_array(DatumGetArrayTypeP(column_index_array),
+ INT2OID, sizeof(int16), true, 's',
+ &keys, NULL, &nKeys);
+
+ /* Extract data from array of char */
+ deconstruct_array(DatumGetArrayTypeP(fk_reftype_array),
+ CHAROID, sizeof(char), true, 'c',
+ &reftypes, NULL, &nReftypes);
+
+ if (nKeys != nReftypes)
+ elog(ERROR, "wrong confreftype cardinality");
+
+ for (j = 0; j < nKeys; j++)
+ {
+ char *colName;
+ const char *prefix;
+
+ colName = get_relid_attribute_name(relId, DatumGetInt16(keys[j]));
+
+ switch (DatumGetChar(reftypes[j]))
+ {
+ case FKCONSTR_REF_PLAIN:
+ prefix = "";
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ prefix = "EACH ELEMENT OF ";
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d",
+ (int) DatumGetChar(reftypes[j]));
+ prefix = NULL; /* keep compiler quiet */
+ break;
+ }
+
+ if (j == 0)
+ appendStringInfo(buf, "%s%s", prefix,
+ quote_identifier(colName));
+ else
+ appendStringInfo(buf, ", %s%s", prefix,
+ quote_identifier(colName));
+ }
+}
/* ----------
* get_expr - Decompile an expression tree
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index f850be490a..55a43d0b90 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -689,7 +689,8 @@ DATA(insert ( 2745 2277 2277 1 s 2750 2742 0 ));
DATA(insert ( 2745 2277 2277 2 s 2751 2742 0 ));
DATA(insert ( 2745 2277 2277 3 s 2752 2742 0 ));
DATA(insert ( 2745 2277 2277 4 s 1070 2742 0 ));
-
+DATA(insert ( 2745 2277 2283 5 s 6108 2742 0 )); /* anyarray @>> anyelem */
+
/*
* btree enum_ops
*/
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index ec035d8434..bea6f82404 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -104,8 +104,16 @@ CATALOG(pg_constraint,2606)
int16 confkey[1];
/*
+ * If a foreign key, the reference semantics for each column
+ */
+ char confreftype[1];
+
+ /*
* If a foreign key, the OIDs of the PK = FK equality operators for each
* column of the constraint
+ *
+ * Note: for array foreign keys, all these operators are for the array's
+ * element type.
*/
Oid conpfeqop[1];
@@ -150,7 +158,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* compiler constants for pg_constraint
* ----------------
*/
-#define Natts_pg_constraint 24
+#define Natts_pg_constraint 25
#define Anum_pg_constraint_conname 1
#define Anum_pg_constraint_connamespace 2
#define Anum_pg_constraint_contype 3
@@ -169,12 +177,13 @@ typedef FormData_pg_constraint *Form_pg_constraint;
#define Anum_pg_constraint_connoinherit 16
#define Anum_pg_constraint_conkey 17
#define Anum_pg_constraint_confkey 18
-#define Anum_pg_constraint_conpfeqop 19
-#define Anum_pg_constraint_conppeqop 20
-#define Anum_pg_constraint_conffeqop 21
-#define Anum_pg_constraint_conexclop 22
-#define Anum_pg_constraint_conbin 23
-#define Anum_pg_constraint_consrc 24
+#define Anum_pg_constraint_confreftype 19
+#define Anum_pg_constraint_conpfeqop 20
+#define Anum_pg_constraint_conppeqop 21
+#define Anum_pg_constraint_conffeqop 22
+#define Anum_pg_constraint_conexclop 23
+#define Anum_pg_constraint_conbin 24
+#define Anum_pg_constraint_consrc 25
/* ----------------
* initial contents of pg_constraint
@@ -195,7 +204,9 @@ typedef FormData_pg_constraint *Form_pg_constraint;
/*
* Valid values for confupdtype and confdeltype are the FKCONSTR_ACTION_xxx
* constants defined in parsenodes.h. Valid values for confmatchtype are
- * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h.
+ * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h. Valid values
+ * for elements of confreftype[] are the FKCONSTR_REF_xxx constants defined
+ * in parsenodes.h.
*/
#endif /* PG_CONSTRAINT_H */
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index ffabc2003b..8a9d616d29 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1570,6 +1570,8 @@ DESCR("contains");
DATA(insert OID = 2752 ( "<@" PGNSP PGUID b f f 2277 2277 16 2751 0 arraycontained arraycontsel arraycontjoinsel ));
DESCR("is contained by");
#define OID_ARRAY_CONTAINED_OP 2752
+DATA(insert OID = 6108 ( "@>>" PGNSP PGUID b f f 2277 2283 16 0 0 arraycontainselem arraycontsel arraycontjoinsel ));
+DESCR("containselem");
/* capturing operators to preserve pre-8.3 behavior of text concatenation */
DATA(insert OID = 2779 ( "||" PGNSP PGUID b f f 25 2776 25 0 0 textanycat - - ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 8b33b4e0ea..01b207d363 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4308,6 +4308,7 @@ DESCR("GIN array support (obsolete)");
DATA(insert OID = 2747 ( arrayoverlap PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arrayoverlap _null_ _null_ _null_ ));
DATA(insert OID = 2748 ( arraycontains PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arraycontains _null_ _null_ _null_ ));
DATA(insert OID = 2749 ( arraycontained PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arraycontained _null_ _null_ _null_ ));
+DATA(insert OID = 6109 ( arraycontainselem PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2283" _null_ _null_ _null_ _null_ _null_ arraycontainselem _null_ _null_ _null_ ));
/* BRIN minmax */
DATA(insert OID = 3383 ( brin_minmax_opcinfo PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_opcinfo _null_ _null_ _null_ ));
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 5f2a4a75da..7662ef8018 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2067,6 +2067,10 @@ typedef enum ConstrType /* types of constraints */
#define FKCONSTR_MATCH_PARTIAL 'p'
#define FKCONSTR_MATCH_SIMPLE 's'
+ /* Foreign key column reference semantics codes */
+ #define FKCONSTR_REF_PLAIN 'p'
+ #define FKCONSTR_REF_EACH_ELEMENT 'e'
+
typedef struct Constraint
{
NodeTag type;
@@ -2102,6 +2106,7 @@ typedef struct Constraint
RangeVar *pktable; /* Primary key table */
List *fk_attrs; /* Attributes of foreign key */
List *pk_attrs; /* Corresponding attrs in PK table */
+ List *fk_reftypes; /* Per-column reference semantics (int List) */
char fk_matchtype; /* FULL, PARTIAL, SIMPLE */
char fk_upd_action; /* ON UPDATE action */
char fk_del_action; /* ON DELETE action */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f50e45e886..d3f4803006 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -140,6 +140,7 @@ PG_KEYWORD("domain", DOMAIN_P, UNRESERVED_KEYWORD)
PG_KEYWORD("double", DOUBLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD)
PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD)
+PG_KEYWORD("element", ELEMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("else", ELSE, RESERVED_KEYWORD)
PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD)
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index c730563f03..0a9eff4d04 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -737,6 +737,17 @@ SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(6 rows)
+SELECT * FROM array_op_test WHERE i @>> 32 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+ 98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
+ 100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
+(6 rows)
+
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -761,6 +772,19 @@ SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
(8 rows)
+SELECT * FROM array_op_test WHERE i @>> 17 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
+ 53 | {38,17} | {AAAAAAAAAAA21658}
+ 65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+(8 rows)
+
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -942,6 +966,11 @@ SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
-------+---+---
(0 rows)
+SELECT * FROM array_op_test WHERE i @>> NULL ORDER BY seqno;
+ seqno | i | t
+-------+---+---
+(0 rows)
+
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
seqno | i | t
-------+---+---
@@ -953,6 +982,15 @@ SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
101 | {} | {}
(1 row)
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAA72908' ORDER BY seqno;
+ seqno | i | t
+-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
+ 22 | {11,6,56,62,53,30} | {AAAAAAAA72908}
+ 45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}
+ 72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+(4 rows)
+
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
seqno | i | t
-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
@@ -971,6 +1009,14 @@ SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
(4 rows)
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAAAA646' ORDER BY seqno;
+ seqno | i | t
+-------+------------------+--------------------------------------------------------------------
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+ 96 | {23,97,43} | {AAAAAAAAAA646,A87088}
+(3 rows)
+
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
seqno | i | t
-------+------------------+--------------------------------------------------------------------
diff --git a/src/test/regress/expected/element_foreign_key.out b/src/test/regress/expected/element_foreign_key.out
new file mode 100644
index 0000000000..b62f53e729
--- /dev/null
+++ b/src/test/regress/expected/element_foreign_key.out
@@ -0,0 +1,590 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fkarray"
+DETAIL: Key (ftest1)=({10,1}) is not present in table "pktableforarray".
+DROP TABLE FKTABLEFORARRAY;
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4,6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL,4,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+ERROR: insert or update on table "fktableforarraymdim" violates foreign key constraint "fktableforarraymdim_ftest1_fkey"
+DETAIL: Key (ftest1)=({{1,2},{6,NULL}}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+ERROR: null value in column "ftest1" violates not-null constraint
+DETAIL: Failing row contains (null, 21).
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+ {1} | 4
+(11 rows)
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({D}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({A,B,D}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(A) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(B) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ERROR: foreign key constraint "fktableforarray_ftest1_fkey" cannot be implemented
+DETAIL: Key column "ftest1" has element type integer which does not have a default btree operator class that's compatible with class "float8_ops".
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(A, {A,B,C}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(B, {A}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Test ELEMENT foreign keys with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,99)"}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,1)","(2010,1)"}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2010,99)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2011,1)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+-- Check FK with cast
+CREATE TABLE PKTABLEFORARRAY (c smallint PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (c int[], FOREIGN KEY (EACH ELEMENT OF c) REFERENCES PKTABLEFORARRAY);
+INSERT INTO PKTABLEFORARRAY VALUES (1), (2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,2}');
+UPDATE PKTABLEFORARRAY SET c = 3 WHERE c = 2;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_c_fkey" on table "fktableforarray"
+DETAIL: Key (c)=(2) is still referenced from table "fktableforarray".
+DELETE FROM PKTABLEFORARRAY WHERE c = 1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_c_fkey" on table "fktableforarray"
+DETAIL: Key (c)=(1) is still referenced from table "fktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({0,1}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({2,1}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(2, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(20, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+ERROR: foreign keys support only one array column
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: foreign keys support only one array column
+DROP TABLE F1;
+-- Cleanup
+DROP TABLE DIM1;
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "x2_x1_fkey"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "fk_const"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+DROP TABLE x1;
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{100,100,100},{NULL,NULL,20},{7,8,10}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index fcf8bd7565..25ef369951 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1811,6 +1811,7 @@ ORDER BY 1, 2, 3;
2742 | 2 | @@@
2742 | 3 | <@
2742 | 4 | =
+ 2742 | 5 | @>>
2742 | 7 | @>
2742 | 9 | ?
2742 | 10 | ?|
@@ -1878,7 +1879,7 @@ ORDER BY 1, 2, 3;
4000 | 25 | <<=
4000 | 26 | >>
4000 | 27 | >>=
-(121 rows)
+(122 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index eefdeeacae..d88ff3772c 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -103,7 +103,7 @@ test: publication subscription
# ----------
# Another group of parallel tests
# ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
+test: select_views portals_p2 foreign_key element_foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
# ----------
# Another group of parallel tests
# NB: temp.sql does a reconnect which transiently uses 2 connections,
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 76b0de30a7..9cb8638b4e 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -140,6 +140,7 @@ test: amutils
test: select_views
test: portals_p2
test: foreign_key
+test: element_foreign_key
test: cluster
test: dependency
test: guc
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 25dd4e2c6d..f6fb507bdd 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -313,8 +313,10 @@ SELECT ARRAY[0,0] || ARRAY[1,1] || ARRAY[2,2] AS "{0,0,1,1,2,2}";
SELECT 0 || ARRAY[1,2] || 3 AS "{0,1,2,3}";
SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> 32 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> 17 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{32,17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32,17}' ORDER BY seqno;
@@ -325,11 +327,14 @@ SELECT * FROM array_op_test WHERE i && '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> NULL ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAA72908' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAAAA646' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno;
diff --git a/src/test/regress/sql/element_foreign_key.sql b/src/test/regress/sql/element_foreign_key.sql
new file mode 100644
index 0000000000..8c1e4d9601
--- /dev/null
+++ b/src/test/regress/sql/element_foreign_key.sql
@@ -0,0 +1,452 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Test ELEMENT foreign keys with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+
+-- Check FK with cast
+CREATE TABLE PKTABLEFORARRAY (c smallint PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (c int[], FOREIGN KEY (EACH ELEMENT OF c) REFERENCES PKTABLEFORARRAY);
+INSERT INTO PKTABLEFORARRAY VALUES (1), (2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,2}');
+UPDATE PKTABLEFORARRAY SET c = 3 WHERE c = 2;
+DELETE FROM PKTABLEFORARRAY WHERE c = 1;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+
+
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+
+
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+-- Cleanup
+DROP TABLE DIM1;
+
+
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+DROP TABLE x2;
+DROP TABLE x1;
+
+
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+DROP TABLE F1;
+
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+DROP TABLE F1;
+
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-24 21:44 ` Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Erik Rijkers @ 2017-07-24 21:44 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <aekorotkov@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers@postgresql.org, David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>; pgsql-hackers-owner@postgresql.org
On 2017-07-24 23:31, Mark Rofail wrote:
> On Mon, Jul 24, 2017 at 11:25 PM, Erik Rijkers <er@xs4all.nl> wrote:
>>
>> This patch doesn't apply to HEAD at the moment ( e2c8100e6072936 ).
>>
>
> My bad, I should have mentioned that the patch is dependant on the
> original
> patch.
> Here is a *unified* patch that I just tested.
Thanks. Apply is now good, but I get this error when compiling:
ELEMENT' not present in UNRESERVED_KEYWORD section of gram.y
make[4]: *** [gram.c] Error 1
make[3]: *** [parser/gram.h] Error 2
make[2]: *** [../../src/include/parser/gram.h] Error 2
make[1]: *** [all-common-recurse] Error 2
make: *** [all-src-recurse] Error 2
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
@ 2017-07-24 22:16 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-07-24 22:16 UTC (permalink / raw)
To: Erik Rijkers <er@xs4all.nl>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <aekorotkov@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers@postgresql.org, David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>; pgsql-hackers-owner@postgresql.org
On Mon, Jul 24, 2017 at 11:44 PM, Erik Rijkers <er@xs4all.nl> wrote:
>
> Thanks. Apply is now good, but I get this error when compiling:
>
Well, this is embarrassing, okay, I cloned the repo and tested the fixed
patch and here it is.
Thanks for your patience.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
Array-ELEMENT-foreign-key-v4 (fixed).patch (text/x-patch, 127.5K) download
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ea655a10a8..b4aefd7aa3 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2324,7 +2324,17 @@ SCRAM-SHA-256$<replaceable><iteration count></>:<replaceable><salt><
</row>
<row>
- <entry><structfield>conpfeqop</structfield></entry>
+ <entry><structfield>confreftype</structfield></entry>
+ <entry><type>char[]</type></entry>
+ <entry></entry>
+ <entry>If a foreign key, the reference semantics for each column:
+ <literal>p</> = plain (simple equality),
+ <literal>e</> = each element of referencing array must have a match
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>conpfeqop</structfield></entry>
<entry><type>oid[]</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</></entry>
<entry>If a foreign key, list of the equality operators for PK = FK comparisons</entry>
@@ -2369,6 +2379,12 @@ SCRAM-SHA-256$<replaceable><iteration count></>:<replaceable><salt><
</table>
<para>
+ When <structfield>confreftype</structfield> indicates array-vs-scalar
+ foreign key reference semantics, the equality operators listed in
+ <structfield>conpfeqop</structfield> etc are for the array's element type.
+ </para>
+
+ <para>
In the case of an exclusion constraint, <structfield>conkey</structfield>
is only useful for constraint elements that are simple column references.
For other cases, a zero appears in <structfield>conkey</structfield>
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index b05a9c2150..75da196334 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -882,6 +882,111 @@ CREATE TABLE order_items (
</para>
</sect2>
+ <sect2 id="ddl-constraints-element-fk">
+ <title>Array ELEMENT Foreign Keys</title>
+
+ <indexterm>
+ <primary>ELEMENT foreign key</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>Array ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>referential integrity</primary>
+ </indexterm>
+
+ <para>
+ Another option you have with foreign keys is to use a
+ referencing column which is an array of elements with
+ the same type (or a compatible one) as the referenced
+ column in the related table. This feature is called
+ <firstterm>array element foreign key</firstterm> and is implemented
+ in PostgreSQL with <firstterm>ELEMENT foreign key constraints</firstterm>,
+ as described in the following example:
+
+<programlisting>
+CREATE TABLE drivers (
+ driver_id integer PRIMARY KEY,
+ first_name text,
+ last_name text,
+ ...
+);
+
+CREATE TABLE races (
+ race_id integer PRIMARY KEY,
+ title text,
+ race_day DATE,
+ ...
+ final_positions integer[] <emphasis>ELEMENT REFERENCES drivers</emphasis>
+);
+</programlisting>
+
+ The above example uses an array (<literal>final_positions</literal>)
+ to store the results of a race: for each of its elements
+ a referential integrity check is enforced on the
+ <literal>drivers</literal> table.
+ Note that <literal>ELEMENT REFERENCES</literal> is an extension
+ of PostgreSQL and it is not included in the SQL standard.
+ </para>
+
+ <para>
+ Even though the most common use case for array <literal>ELEMENT</literal>
+ foreign keys is on a single column key, you can define an <quote>array
+ <literal>ELEMENT</literal> foreign key constraint</quote> on a group
+ of columns. As the following example shows, it must be written in table
+ constraint form:
+
+<programlisting>
+CREATE TABLE available_moves (
+ kind text,
+ move text,
+ description text,
+ PRIMARY KEY (kind, move)
+);
+
+CREATE TABLE paths (
+ description text,
+ kind text,
+ moves text[],
+ <emphasis>FOREIGN KEY (kind, ELEMENT moves) REFERENCES available_moves (kind, move)</emphasis>
+);
+
+INSERT INTO available_moves VALUES ('relative', 'LN', 'look north');
+INSERT INTO available_moves VALUES ('relative', 'RL', 'rotate left');
+INSERT INTO available_moves VALUES ('relative', 'RR', 'rotate right');
+INSERT INTO available_moves VALUES ('relative', 'MF', 'move forward');
+INSERT INTO available_moves VALUES ('absolute', 'N', 'move north');
+INSERT INTO available_moves VALUES ('absolute', 'S', 'move south');
+INSERT INTO available_moves VALUES ('absolute', 'E', 'move east');
+INSERT INTO available_moves VALUES ('absolute', 'W', 'move west');
+
+INSERT INTO paths VALUES ('L-shaped path', 'relative', '{LN, RL, MF, RR, MF, MF}');
+INSERT INTO paths VALUES ('L-shaped path', 'absolute', '{W, N, N}');
+</programlisting>
+
+ On top of standard foreign key requirements,
+ array <literal>ELEMENT</literal> foreign key constraints
+ require that the referencing column is an array of a compatible
+ type of the corresponding referenced column.
+ </para>
+
+ <para>
+ For more detailed information on array <literal>ELEMENT</literal>
+ foreign key options and special cases, please refer to the documentation
+ for <xref linkend="sql-createtable-foreign-key"> and
+ <xref linkend="sql-createtable-element-foreign-key-constraints">.
+ </para>
+
+ </sect2>
+
<sect2 id="ddl-constraints-exclusion">
<title>Exclusion Constraints</title>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index e9c2c49533..0228fbe941 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -65,7 +65,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ] |
UNIQUE <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="PARAMETER">index_parameters</replaceable> |
- REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
+ [ELEMENT] REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -76,7 +76,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
UNIQUE ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ] |
- FOREIGN KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> [, ... ] ) ]
+ FOREIGN KEY ( [ELEMENT] <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> [, ... ] ) ]
[ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -812,10 +812,10 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</listitem>
</varlistentry>
- <varlistentry>
+ <varlistentry id="sql-createtable-foreign-key" xreflabel="FOREIGN KEY">
<term><literal>REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
- <term><literal>FOREIGN KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] )
+ <term><literal>FOREIGN KEY ( [ELEMENT] <replaceable class="parameter">column_name</replaceable> [, ... ] )
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] ) ]
[ MATCH <replaceable class="parameter">matchtype</replaceable> ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ]
@@ -839,6 +839,21 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</para>
<para>
+ In case the column name <replaceable class="parameter">column</replaceable>
+ is prepended with the <literal>ELEMENT</literal> keyword and <replaceable
+ class="parameter">column</replaceable> is an array of elements compatible
+ with the corresponding <replaceable class="parameter">refcolumn</replaceable>
+ in <replaceable class="parameter">reftable</replaceable>, an
+ array <literal>ELEMENT</literal> foreign key constraint is put in place
+ (see <xref linkend="sql-createtable-element-foreign-key-constraints">
+ for more information).
+ Multi-column keys with more than one <literal>ELEMENT</literal> column
+ are currently not allowed.
+
+ It is advisable to index the refrencing column using GIN index as it considerably enhances the performance.
+ </para>
+
+ <para>
A value inserted into the referencing column(s) is matched against the
values of the referenced table and referenced columns using the
given match type. There are three match types: <literal>MATCH
@@ -901,7 +916,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
<para>
Delete any rows referencing the deleted row, or update the
values of the referencing column(s) to the new values of the
- referenced columns, respectively.
+ referenced columns, respectively. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -910,7 +926,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
<term><literal>SET NULL</literal></term>
<listitem>
<para>
- Set the referencing column(s) to null.
+ Set the referencing column(s) to null. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -922,6 +939,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
Set the referencing column(s) to their default values.
(There must be a row in the referenced table matching the default
values, if they are not null, or the operation will fail.)
+ Currently not supported with array <literal>ELEMENT</literal>
+ foreign keys.
</para>
</listitem>
</varlistentry>
@@ -938,6 +957,61 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</varlistentry>
<varlistentry>
+ <varlistentry id="sql-createtable-element-foreign-key-constraints" xreflabel="ELEMENT REFERENCES">
+ <term><literal>ELEMENT REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
+
+ <listitem>
+ <para>
+ The <literal>ELEMENT REFERENCES</literal> definition specifies
+ an <quote>array <literal>ELEMENT</literal> foreign key</quote>,
+ a special kind of foreign key
+ constraint requiring the referencing column to be an array of elements
+ of the same type (or a compatible one) as the referenced column
+ in the referenced table. The value of each element of the
+ <replaceable class="parameter">refcolumn</replaceable> array
+ will be matched against some row of <replaceable
+ class="parameter">reftable</replaceable>.
+ </para>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys are an extension
+ of PostgreSQL and are not included in the SQL standard.
+ </para>
+
+ <para>
+ Even with <literal>ELEMENT</literal> foreign keys, modifications
+ in the referenced column can trigger actions to be performed on
+ the referencing array.
+ Similarly to standard foreign keys, you can specify these
+ actions using the <literal>ON DELETE</literal> and
+ <literal>ON UPDATE</literal> clauses.
+ However, only the two following actions for each clause are
+ currently allowed:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>NO ACTION</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints. This is the default action.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RESTRICT</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ </listitem>
+ </varlistentry>
+
<term><literal>DEFERRABLE</literal></term>
<term><literal>NOT DEFERRABLE</literal></term>
<listitem>
@@ -1876,6 +1950,16 @@ CREATE TABLE cities_ab_10000_to_100000
</refsect2>
<refsect2>
+ <title id="sql-createtable-foreign-key-arrays">Array <literal>ELEMENT</literal> Foreign Keys</title>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys and the
+ <literal>ELEMENT REFERENCES</literal> clause
+ are a <productname>PostgreSQL</productname> extension.
+ </para>
+ </refsect2>
+
+ <refsect2>
<title><literal>PARTITION BY</> Clause</title>
<para>
diff --git a/src/backend/access/gin/ginarrayproc.c b/src/backend/access/gin/ginarrayproc.c
index a5238c3af5..416ed60b0c 100644
--- a/src/backend/access/gin/ginarrayproc.c
+++ b/src/backend/access/gin/ginarrayproc.c
@@ -24,6 +24,7 @@
#define GinContainsStrategy 2
#define GinContainedStrategy 3
#define GinEqualStrategy 4
+#define GinContainsElemStrategy 5
/*
@@ -43,7 +44,7 @@ ginarrayextract(PG_FUNCTION_ARGS)
bool *nulls;
int nelems;
- get_typlenbyvalalign(ARR_ELEMTYPE(array),
+ get_typlenbyvalalign(ARR_ELEMTYPE(array),
&elmlen, &elmbyval, &elmalign);
deconstruct_array(array,
@@ -79,7 +80,8 @@ Datum
ginqueryarrayextract(PG_FUNCTION_ARGS)
{
/* Make copy of array input to ensure it doesn't disappear while in use */
- ArrayType *array = PG_GETARG_ARRAYTYPE_P_COPY(0);
+ ArrayType *array;
+ Datum elem;
int32 *nkeys = (int32 *) PG_GETARG_POINTER(1);
StrategyNumber strategy = PG_GETARG_UINT16(2);
@@ -94,45 +96,64 @@ ginqueryarrayextract(PG_FUNCTION_ARGS)
bool *nulls;
int nelems;
- get_typlenbyvalalign(ARR_ELEMTYPE(array),
- &elmlen, &elmbyval, &elmalign);
+ if (strategy == GinContainsElemStrategy)
+ {
+ /* only items that match the queried element
+ are considered candidate */
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+
+ elem = PG_GETARG_DATUM(0);
+ PG_RETURN_POINTER(elem);
+ }
+ else
+ {
+ array = PG_GETARG_ARRAYTYPE_P_COPY(0);
- deconstruct_array(array,
- ARR_ELEMTYPE(array),
- elmlen, elmbyval, elmalign,
- &elems, &nulls, &nelems);
+ get_typlenbyvalalign(ARR_ELEMTYPE(array),
+ &elmlen, &elmbyval, &elmalign);
- *nkeys = nelems;
- *nullFlags = nulls;
+ deconstruct_array(array,
+ ARR_ELEMTYPE(array),
+ elmlen, elmbyval, elmalign,
+ &elems, &nulls, &nelems);
- switch (strategy)
- {
- case GinOverlapStrategy:
- *searchMode = GIN_SEARCH_MODE_DEFAULT;
- break;
- case GinContainsStrategy:
- if (nelems > 0)
+ *nkeys = nelems;
+ *nullFlags = nulls;
+
+ switch (strategy)
+ {
+ case GinOverlapStrategy:
*searchMode = GIN_SEARCH_MODE_DEFAULT;
- else /* everything contains the empty set */
- *searchMode = GIN_SEARCH_MODE_ALL;
- break;
- case GinContainedStrategy:
- /* empty set is contained in everything */
- *searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
- break;
- case GinEqualStrategy:
- if (nelems > 0)
+ break;
+ case GinContainsElemStrategy:
+ /* only items that match the queried element
+ are considered candidate */
*searchMode = GIN_SEARCH_MODE_DEFAULT;
- else
+ break;
+ case GinContainsStrategy:
+ if (nelems > 0)
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+ else /* everything contains the empty set */
+ *searchMode = GIN_SEARCH_MODE_ALL;
+ break;
+ case GinContainedStrategy:
+ /* empty set is contained in everything */
*searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
- break;
- default:
- elog(ERROR, "ginqueryarrayextract: unknown strategy number: %d",
- strategy);
+ break;
+ case GinEqualStrategy:
+ if (nelems > 0)
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+ else
+ *searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
+ break;
+ default:
+ elog(ERROR, "ginqueryarrayextract: unknown strategy number: %d",
+ strategy);
}
/* we should not free array, elems[i] points into it */
PG_RETURN_POINTER(elems);
+ }
}
/*
@@ -171,6 +192,7 @@ ginarrayconsistent(PG_FUNCTION_ARGS)
}
}
break;
+ case GinContainsElemStrategy:
case GinContainsStrategy:
/* result is not lossy */
*recheck = false;
@@ -258,7 +280,8 @@ ginarraytriconsistent(PG_FUNCTION_ARGS)
}
}
break;
- case GinContainsStrategy:
+ case GinContainsElemStrategy:
+ case GinContainsStrategy:
/* must have all elements in check[] true, and no nulls */
res = GIN_TRUE;
for (i = 0; i < nkeys; i++)
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index a376b99f1e..3bd0b772a4 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2098,6 +2098,7 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index d25b39bb54..4e0d0bdda0 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1211,6 +1211,7 @@ index_constraint_create(Relation heapRelation,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 1336c46d3f..c197cec11a 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -59,6 +59,7 @@ CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
@@ -82,6 +83,7 @@ CreateConstraintEntry(const char *constraintName,
Datum values[Natts_pg_constraint];
ArrayType *conkeyArray;
ArrayType *confkeyArray;
+ ArrayType *confreftypeArray;
ArrayType *conpfeqopArray;
ArrayType *conppeqopArray;
ArrayType *conffeqopArray;
@@ -119,7 +121,11 @@ CreateConstraintEntry(const char *constraintName,
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = Int16GetDatum(foreignKey[i]);
confkeyArray = construct_array(fkdatums, foreignNKeys,
- INT2OID, 2, true, 's');
+ INT2OID, sizeof(int16), true, 's');
+ for (i = 0; i < foreignNKeys; i++)
+ fkdatums[i] = CharGetDatum(foreignRefType[i]);
+ confreftypeArray = construct_array(fkdatums, foreignNKeys,
+ CHAROID, sizeof(char), true, 'c');
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
conpfeqopArray = construct_array(fkdatums, foreignNKeys,
@@ -136,6 +142,7 @@ CreateConstraintEntry(const char *constraintName,
else
{
confkeyArray = NULL;
+ confreftypeArray = NULL;
conpfeqopArray = NULL;
conppeqopArray = NULL;
conffeqopArray = NULL;
@@ -188,6 +195,11 @@ CreateConstraintEntry(const char *constraintName,
else
nulls[Anum_pg_constraint_confkey - 1] = true;
+ if (confreftypeArray)
+ values[Anum_pg_constraint_confreftype - 1] = PointerGetDatum(confreftypeArray);
+ else
+ nulls[Anum_pg_constraint_confreftype - 1] = true;
+
if (conpfeqopArray)
values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
else
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index bb00858ad1..dc18fd1eae 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -6995,6 +6995,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Relation pkrel;
int16 pkattnum[INDEX_MAX_KEYS];
int16 fkattnum[INDEX_MAX_KEYS];
+ char fkreftypes[INDEX_MAX_KEYS];
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
Oid opclasses[INDEX_MAX_KEYS];
@@ -7002,10 +7003,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid ppeqoperators[INDEX_MAX_KEYS];
Oid ffeqoperators[INDEX_MAX_KEYS];
int i;
+ ListCell *lc;
int numfks,
numpks;
Oid indexOid;
Oid constrOid;
+ bool has_array;
bool old_check_ok;
ObjectAddress address;
ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
@@ -7082,6 +7085,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
*/
MemSet(pkattnum, 0, sizeof(pkattnum));
MemSet(fkattnum, 0, sizeof(fkattnum));
+ MemSet(fkreftypes, 0, sizeof(fkreftypes));
MemSet(pktypoid, 0, sizeof(pktypoid));
MemSet(fktypoid, 0, sizeof(fktypoid));
MemSet(opclasses, 0, sizeof(opclasses));
@@ -7094,6 +7098,50 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
fkattnum, fktypoid);
/*
+ * Validate the reference semantics codes, too, and convert list to array
+ * format to pass to CreateConstraintEntry.
+ */
+ Assert(list_length(fkconstraint->fk_reftypes) == numfks);
+ has_array = false;
+ i = 0;
+ foreach(lc, fkconstraint->fk_reftypes)
+ {
+ char reftype = lfirst_int(lc);
+
+ switch (reftype)
+ {
+ case FKCONSTR_REF_PLAIN:
+ /* OK, nothing to do */
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ /* At most one FK column can be an array reference */
+ if (has_array)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign keys support only one array column")));
+ has_array = true;
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d", (int) reftype);
+ break;
+ }
+ fkreftypes[i] = reftype;
+ i++;
+ }
+
+ /* Array foreign keys support only NO ACTION and RESTRICT actions */
+ if (has_array)
+ {
+ if ((fkconstraint->fk_upd_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_upd_action != FKCONSTR_ACTION_RESTRICT) ||
+ (fkconstraint->fk_del_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("array foreign keys support only NO ACTION and RESTRICT actions")));
+ }
+
+ /*
* If the attribute list for the referenced table was omitted, lookup the
* definition of the primary key and use it. Otherwise, validate the
* supplied attribute list. In either case, discover the index OID and
@@ -7179,6 +7227,65 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
eqstrategy = BTEqualStrategyNumber;
/*
+ * If this is an array foreign key, we must look up the operators for
+ * the array element type, not the array type itself.
+ */
+ if (fkreftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ Oid elemopclass;
+
+ /* We look through any domain here */
+ fktype = get_base_element_type(fktype);
+ if (!OidIsValid(fktype))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has type %s which is not an array type.",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktypoid[i]))));
+
+ /*
+ * For the moment, we must also insist that the array's element
+ * type have a default btree opclass that is in the index's
+ * opfamily. This is necessary because ri_triggers.c relies on
+ * COUNT(DISTINCT x) on the element type, as well as on array_eq()
+ * on the array type, and we need those operations to have the
+ * same notion of equality that we're using otherwise.
+ *
+ * XXX this restriction is pretty annoying, considering the effort
+ * that's been put into the rest of the RI mechanisms to make them
+ * work with nondefault equality operators. In particular, it
+ * means that the cast-to-PK-datatype code path isn't useful for
+ * array-to-scalar references.
+ */
+ elemopclass = GetDefaultOpClass(fktype, BTREE_AM_OID);
+ if (!OidIsValid(elemopclass) ||
+ get_opclass_family(elemopclass) != opfamily)
+ {
+ /* Get the index opclass's name for the error message. */
+ char *opcname;
+
+ cla_ht = SearchSysCache1(CLAOID,
+ ObjectIdGetDatum(opclasses[i]));
+ if (!HeapTupleIsValid(cla_ht))
+ elog(ERROR, "cache lookup failed for opclass %u",
+ opclasses[i]);
+ cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
+ opcname = pstrdup(NameStr(cla_tup->opcname));
+ ReleaseSysCache(cla_ht);
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has element type %s which does not have a default btree operator class that's compatible with class \"%s\".",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktype),
+ opcname)));
+ }
+ }
+
+ /*
* There had better be a primary equality operator for the index.
* We'll use it for PK = PK comparisons.
*/
@@ -7239,14 +7346,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("foreign key constraint \"%s\" "
- "cannot be implemented",
- fkconstraint->conname),
- errdetail("Key columns \"%s\" and \"%s\" "
- "are of incompatible types: %s and %s.",
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key columns \"%s\" and \"%s\" are of incompatible types: %s and %s.",
strVal(list_nth(fkconstraint->fk_attrs, i)),
strVal(list_nth(fkconstraint->pk_attrs, i)),
- format_type_be(fktype),
+ format_type_be(fktypoid[i]),
format_type_be(pktype))));
if (old_check_ok)
@@ -7275,6 +7380,13 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
* We may assume that pg_constraint.conkey is not changing.
*/
old_fktype = tab->oldDesc->attrs[fkattnum[i] - 1]->atttypid;
+ if (fkreftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ old_fktype = get_base_element_type(old_fktype);
+ /* this shouldn't happen ... */
+ if (!OidIsValid(old_fktype))
+ elog(ERROR, "old foreign key column is not an array");
+ }
new_fktype = fktype;
old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
&old_castfunc);
@@ -7317,7 +7429,6 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
new_castfunc == old_castfunc &&
(!IsPolymorphicType(pfeqop_right) ||
new_fktype == old_fktype));
-
}
pfeqoperators[i] = pfeqop;
@@ -7341,6 +7452,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
indexOid,
RelationGetRelid(pkrel),
pkattnum,
+ fkreftypes,
pfeqoperators,
ppeqoperators,
ffeqoperators,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index b502941b08..cb017cbe14 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -635,6 +635,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
@@ -1006,6 +1007,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
char fk_matchtype = FKCONSTR_MATCH_SIMPLE;
List *fk_attrs = NIL;
List *pk_attrs = NIL;
+ List *fk_reftypes = NIL;
StringInfoData buf;
int funcnum;
OldTriggerInfo *info = NULL;
@@ -1035,7 +1037,10 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
if (i % 2)
fk_attrs = lappend(fk_attrs, arg);
else
- pk_attrs = lappend(pk_attrs, arg);
+ {
+ pk_attrs = lappend(pk_attrs, arg);
+ fk_reftypes = lappend_int(fk_reftypes, FKCONSTR_REF_PLAIN);
+ }
}
/* Prepare description of constraint for use in messages */
@@ -1174,6 +1179,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
fkcon->conname = constr_name;
fkcon->fk_attrs = fk_attrs;
fkcon->pk_attrs = pk_attrs;
+ fkcon->fk_reftypes = fk_reftypes;
fkcon->fk_matchtype = fk_matchtype;
switch (info->funcoids[0])
{
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index c2fc59d1aa..4f089d53a9 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -3075,6 +3075,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 45a04b0b27..01238bfd71 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2843,6 +2843,7 @@ _copyConstraint(const Constraint *from)
COPY_NODE_FIELD(pktable);
COPY_NODE_FIELD(fk_attrs);
COPY_NODE_FIELD(pk_attrs);
+ COPY_NODE_FIELD(fk_reftypes);
COPY_SCALAR_FIELD(fk_matchtype);
COPY_SCALAR_FIELD(fk_upd_action);
COPY_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 8d92c03633..d35f3d1d9a 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2575,6 +2575,7 @@ _equalConstraint(const Constraint *a, const Constraint *b)
COMPARE_NODE_FIELD(pktable);
COMPARE_NODE_FIELD(fk_attrs);
COMPARE_NODE_FIELD(pk_attrs);
+ COMPARE_NODE_FIELD(fk_reftypes);
COMPARE_SCALAR_FIELD(fk_matchtype);
COMPARE_SCALAR_FIELD(fk_upd_action);
COMPARE_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 379d92a2b0..119d100b37 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3490,6 +3490,7 @@ _outConstraint(StringInfo str, const Constraint *node)
WRITE_NODE_FIELD(pktable);
WRITE_NODE_FIELD(fk_attrs);
WRITE_NODE_FIELD(pk_attrs);
+ WRITE_NODE_FIELD(fk_reftypes);
WRITE_CHAR_FIELD(fk_matchtype);
WRITE_CHAR_FIELD(fk_upd_action);
WRITE_CHAR_FIELD(fk_del_action);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 4b1ce09c44..d1df75dd6e 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -126,6 +126,13 @@ typedef struct ImportQual
List *table_names;
} ImportQual;
+/* Private struct for the result of foreign_key_column_elem production */
+typedef struct FKColElem
+{
+ Node *name; /* name of the column (a String) */
+ char reftype; /* FKCONSTR_REF_xxx code */
+} FKColElem;
+
/* ConstraintAttributeSpec yields an integer bitmask of these flags: */
#define CAS_NOT_DEFERRABLE 0x01
#define CAS_DEFERRABLE 0x02
@@ -183,6 +190,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
static void SplitColQualList(List *qualList,
List **constraintList, CollateClause **collClause,
core_yyscan_t yyscanner);
+static void SplitFKColElems(List *fkcolelems, List **names, List **reftypes);
static void processCASbits(int cas_bits, int location, const char *constrType,
bool *deferrable, bool *initdeferred, bool *not_valid,
bool *no_inherit, core_yyscan_t yyscanner);
@@ -233,6 +241,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
A_Indices *aind;
ResTarget *target;
struct PrivTarget *privtarget;
+ struct FKColElem *fkcolelem;
AccessPriv *accesspriv;
struct ImportQual *importqual;
InsertStmt *istmt;
@@ -358,6 +367,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <accesspriv> privilege
%type <list> privileges privilege_list
%type <privtarget> privilege_target
+%type <fkcolelem> foreign_key_column_elem
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
%type <ival> defacl_privilege_target
@@ -392,7 +402,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
execute_param_clause using_clause returning_clause
opt_enum_val_list enum_val_list table_func_column_list
create_generic_options alter_generic_options
- relation_expr_list dostmt_opt_list
+ relation_expr_list dostmt_opt_list foreign_key_column_list
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
@@ -622,8 +632,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
DOUBLE_P DROP
- EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
- EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
+ EACH ELEMENT ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE
+ EVENT EXCEPT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
EXTENSION EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FILTER FIRST_P FLOAT_P FOLLOWING FOR
@@ -3437,14 +3447,16 @@ ColConstraintElem:
| REFERENCES qualified_name opt_column_list key_match key_actions
{
Constraint *n = makeNode(Constraint);
- n->contype = CONSTR_FOREIGN;
- n->location = @1;
- n->pktable = $2;
- n->fk_attrs = NIL;
- n->pk_attrs = $3;
- n->fk_matchtype = $4;
- n->fk_upd_action = (char) ($5 >> 8);
- n->fk_del_action = (char) ($5 & 0xFF);
+ n->contype = CONSTR_FOREIGN;
+ n->location = @1;
+ n->pktable = $2;
+ /* fk_attrs will be filled in by parse analysis */
+ n->fk_attrs = NIL;
+ n->pk_attrs = $3;
+ n->fk_reftypes = list_make1_int(FKCONSTR_REF_PLAIN);
+ n->fk_matchtype = $4;
+ n->fk_upd_action = (char) ($5 >> 8);
+ n->fk_del_action = (char) ($5 & 0xFF);
n->skip_validation = false;
n->initially_valid = true;
$$ = (Node *)n;
@@ -3635,14 +3647,15 @@ ConstraintElem:
NULL, yyscanner);
$$ = (Node *)n;
}
- | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
- opt_column_list key_match key_actions ConstraintAttributeSpec
+ | FOREIGN KEY '(' foreign_key_column_list ')' REFERENCES
+ qualified_name opt_column_list key_match key_actions
+ ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_FOREIGN;
n->location = @1;
+ SplitFKColElems($4, &n->fk_attrs, &n->fk_reftypes);
n->pktable = $7;
- n->fk_attrs = $4;
n->pk_attrs = $8;
n->fk_matchtype = $9;
n->fk_upd_action = (char) ($10 >> 8);
@@ -3675,7 +3688,30 @@ columnElem: ColId
$$ = (Node *) makeString($1);
}
;
-
+ foreign_key_column_list:
+ foreign_key_column_elem
+ { $$ = list_make1($1); }
+ | foreign_key_column_list ',' foreign_key_column_elem
+ { $$ = lappend($1, $3); }
+ ;
+
+ foreign_key_column_elem:
+ ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($1);
+ n->reftype = FKCONSTR_REF_PLAIN;
+ $$ = n;
+ }
+ | EACH ELEMENT OF ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($4);
+ n->reftype = FKCONSTR_REF_EACH_ELEMENT;
+ $$ = n;
+ }
+ ;
+
key_match: MATCH FULL
{
$$ = FKCONSTR_MATCH_FULL;
@@ -14686,6 +14722,7 @@ unreserved_keyword:
| DOUBLE_P
| DROP
| EACH
+ | ELEMENT
| ENABLE_P
| ENCODING
| ENCRYPTED
@@ -15803,6 +15840,23 @@ SplitColQualList(List *qualList,
*constraintList = qualList;
}
+/* Split a list of FKColElem structs into separate name and reftype lists */
+static void
+SplitFKColElems(List *fkcolelems, List **names, List **reftypes)
+{
+ ListCell *lc;
+
+ *names = NIL;
+ *reftypes = NIL;
+ foreach(lc, fkcolelems)
+ {
+ FKColElem *fkcolelem = (FKColElem *) lfirst(lc);
+
+ *names = lappend(*names, fkcolelem->name);
+ *reftypes = lappend_int(*reftypes, fkcolelem->reftype);
+ }
+}
+
/*
* Process result of ConstraintAttributeSpec, and set appropriate bool flags
* in the output command node. Pass NULL for any flags the particular
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 9f37f1b920..163719c7a8 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -743,6 +743,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
* list of FK constraints to be processed later.
*/
constraint->fk_attrs = list_make1(makeString(column->colname));
+ /* grammar should have set fk_reftypes */
+ Assert(list_length(constraint->fk_reftypes) == 1);
cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
break;
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 34dadd6e19..8c9eb0c676 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -4232,6 +4232,117 @@ arraycontained(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(result);
}
+/*
+ * array_contains_elem : checks an array for a spefific element
+ */
+static bool
+array_contains_elem(AnyArrayType *array, Datum elem, Oid element_type,
+ bool element_isnull, Oid collation, void **fn_extra)
+{
+ Oid arr_type = AARR_ELEMTYPE(array);
+ TypeCacheEntry *typentry;
+ int nelems;
+ int typlen;
+ bool typbyval;
+ char typalign;
+ int i;
+ array_iter it1;
+ FunctionCallInfoData locfcinfo;
+
+ if (arr_type != element_type)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot compare different element types")));
+
+ if (element_isnull)
+ return false;
+
+ /*
+ * We arrange to look up the equality function only once per series of
+ * calls, assuming the element type doesn't change underneath us. The
+ * typcache is used so that we have no memory leakage when being used as
+ * an index support function.
+ */
+ typentry = (TypeCacheEntry *)*fn_extra;
+ if (typentry == NULL ||
+ typentry->type_id != arr_type)
+ {
+ typentry = lookup_type_cache(arr_type,
+ TYPECACHE_EQ_OPR_FINFO);
+ if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(arr_type))));
+ *fn_extra = (void *)typentry;
+ }
+ typlen = typentry->typlen;
+ typbyval = typentry->typbyval;
+ typalign = typentry->typalign;
+
+ /*
+ * Apply the comparison operator to each pair of array elements.
+ */
+ InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+ collation, NULL, NULL);
+
+ /* Loop over source data */
+ nelems = ArrayGetNItems(AARR_NDIM(array), AARR_DIMS(array));
+ array_iter_setup(&it1, array);
+
+ for (i = 0; i < nelems; i++)
+ {
+ Datum elt1;
+ bool isnull;
+ bool oprresult;
+
+ /* Get element, checking for NULL */
+ elt1 = array_iter_next(&it1, &isnull, i, typlen, typbyval, typalign);
+
+ /*
+ * We assume that the comparison operator is strict, so a NULL can't
+ * match anything. XXX this diverges from the "NULL=NULL" behavior of
+ * array_eq, should we act like that?
+ */
+ if (isnull)
+ continue;
+
+ /*
+ * Apply the operator to the element pair
+ */
+ locfcinfo.arg[0] = elt1;
+ locfcinfo.arg[1] = elem;
+ locfcinfo.argnull[0] = false;
+ locfcinfo.argnull[1] = false;
+ locfcinfo.isnull = false;
+ oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+ if (oprresult)
+ return true;
+ }
+
+ return false;
+}
+
+Datum
+arraycontainselem(PG_FUNCTION_ARGS)
+{
+ AnyArrayType *array = PG_GETARG_ANY_ARRAY(0);
+ Datum elem = PG_GETARG_DATUM(1);
+ Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ Oid collation = PG_GET_COLLATION();
+ bool element_isnull = PG_ARGISNULL(1);
+ bool result;
+
+ result = array_contains_elem(array, elem, element_type,
+ element_isnull, collation,
+ &fcinfo->flinfo->fn_extra);
+
+ /* Avoid leaking memory when handed toasted input. */
+ AARR_FREE_IF_COPY(array, 0);
+
+ PG_RETURN_BOOL(result);
+}
+
/*-----------------------------------------------------------------------------
* Array iteration functions
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index c2891e6fa1..9e7d66df7e 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -118,9 +118,11 @@ typedef struct RI_ConstraintInfo
char confupdtype; /* foreign key's ON UPDATE action */
char confdeltype; /* foreign key's ON DELETE action */
char confmatchtype; /* foreign key's match type */
+ bool has_array; /* true if any reftype is EACH_ELEMENT */
int nkeys; /* number of key columns */
int16 pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */
int16 fk_attnums[RI_MAX_NUMKEYS]; /* attnums of referencing cols */
+ char fk_reftypes[RI_MAX_NUMKEYS]; /* reference semantics */
Oid pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = FK) */
Oid pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = PK) */
Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK = FK) */
@@ -204,7 +206,8 @@ static void ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype);
+ const char *rightop, Oid rightoptype,
+ char fkreftype);
static void ri_add_cast_to(StringInfo buf, Oid typid);
static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
static int ri_NullCheck(HeapTuple tup,
@@ -395,6 +398,7 @@ RI_FKey_check(TriggerData *trigdata)
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
StringInfoData querybuf;
+ StringInfoData countbuf;
char pkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
char paramname[16];
@@ -407,12 +411,22 @@ RI_FKey_check(TriggerData *trigdata)
* FOR KEY SHARE OF x
* The type id's for the $ parameters are those of the
* corresponding FK attributes.
+ *
+ * In case of an array ELEMENT foreign key, the previous query is used
+ * to count the number of matching rows and see if every combination
+ * is actually referenced.
+ * The wrapping query is
+ * SELECT 1 WHERE
+ * (SELECT count(DISTINCT y) FROM unnest($1) y)
+ * = (SELECT count(*) FROM (<QUERY>) z)
* ----------
*/
initStringInfo(&querybuf);
quoteRelationName(pkrelname, pk_rel);
appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
querysep = "WHERE";
+ initStringInfo(&countbuf);
+ appendStringInfo(&countbuf, "SELECT 1 WHERE ");
for (i = 0; i < riinfo->nkeys; i++)
{
Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
@@ -421,18 +435,41 @@ RI_FKey_check(TriggerData *trigdata)
quoteOneName(attname,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
sprintf(paramname, "$%d", i + 1);
+
+ /*
+ * In case of an array ELEMENT foreign key, we check that each
+ * distinct non-null value in the array is present in the PK
+ * table.
+ */
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&countbuf,
+ "(SELECT pg_catalog.count(DISTINCT y) FROM pg_catalog.unnest(%s) y)",
+ paramname);
+
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pf_eq_oprs[i],
- paramname, fk_type);
+ paramname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = fk_type;
}
appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
- /* Prepare and save the plan */
- qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
- &qkey, fk_rel, pk_rel, true);
+ if (riinfo->has_array)
+ {
+ appendStringInfo(&countbuf,
+ " OPERATOR(pg_catalog.=) (SELECT pg_catalog.count(*) FROM (%s) z)",
+ querybuf.data);
+
+ /* Prepare and save the plan for array ELEMENT foreign keys */
+ qplan = ri_PlanCheck(countbuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
+ }
+ else
+ /* Prepare and save the plan */
+ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
}
/*
@@ -559,7 +596,8 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pp_eq_oprs[i],
- paramname, pk_type);
+ paramname, pk_type,
+ FKCONSTR_REF_PLAIN);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -751,7 +789,8 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -974,7 +1013,8 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1130,7 +1170,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1309,7 +1350,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1475,7 +1517,8 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1651,7 +1694,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1817,7 +1861,8 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2008,7 +2053,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2327,6 +2373,14 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
* For MATCH FULL:
* (fk.keycol1 IS NOT NULL [OR ...])
*
+ * In case of an array ELEMENT column, relname is replaced with the
+ * following subquery:
+ *
+ * SELECT unnest("keycol1") k1, "keycol1" ak1 [, ...]
+ * FROM ONLY "public"."fk"
+ *
+ * where all the columns are renamed in order to prevent name collisions.
+ *
* We attach COLLATE clauses to the operators when comparing columns
* that have different collations.
*----------
@@ -2338,15 +2392,46 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
{
quoteOneName(fkattname,
RIAttName(fk_rel, riinfo->fk_attnums[i]));
- appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
+ if (riinfo->has_array)
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%sfk.ak%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.k%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
sep = ", ";
}
quoteRelationName(pkrelname, pk_rel);
quoteRelationName(fkrelname, fk_rel);
- appendStringInfo(&querybuf,
- " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
- fkrelname, pkrelname);
+
+ if (riinfo->has_array)
+ {
+ sep = "";
+ appendStringInfo(&querybuf,
+ " FROM (SELECT ");
+ for (i = 0; i < riinfo->nkeys; i++)
+ {
+ quoteOneName(fkattname,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%spg_catalog.unnest(%s) k%d, %s ak%d",
+ sep, fkattname, i + 1, fkattname, i + 1);
+ else
+ appendStringInfo(&querybuf, "%s%s k%d", sep, fkattname,
+ i + 1);
+ sep = ", ";
+ }
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s) fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
+ }
+ else
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
strcpy(pkattname, "pk.");
strcpy(fkattname, "fk.");
@@ -2360,12 +2445,16 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
quoteOneName(pkattname + 3,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
- quoteOneName(fkattname + 3,
- RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname + 3, "k%d", i + 1);
+ else
+ quoteOneName(fkattname + 3,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
ri_GenerateQual(&querybuf, sep,
pkattname, pk_type,
riinfo->pf_eq_oprs[i],
- fkattname, fk_type);
+ fkattname, fk_type,
+ FKCONSTR_REF_PLAIN);
if (pk_coll != fk_coll)
ri_GenerateQualCollation(&querybuf, pk_coll);
sep = "AND";
@@ -2381,7 +2470,10 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
sep = "";
for (i = 0; i < riinfo->nkeys; i++)
{
- quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname, "k%d", i + 1);
+ else
+ quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
appendStringInfo(&querybuf,
"%sfk.%s IS NOT NULL",
sep, fkattname);
@@ -2557,25 +2649,29 @@ quoteRelationName(char *buffer, Relation rel)
/*
* ri_GenerateQual --- generate a WHERE clause equating two variables
*
- * The idea is to append " sep leftop op rightop" to buf. The complexity
- * comes from needing to be sure that the parser will select the desired
- * operator. We always name the operator using OPERATOR(schema.op) syntax
- * (readability isn't a big priority here), so as to avoid search-path
- * uncertainties. We have to emit casts too, if either input isn't already
- * the input type of the operator; else we are at the mercy of the parser's
- * heuristics for ambiguous-operator resolution.
- */
+ * The idea is to append " sep leftop op rightop" to buf, or if fkreftype is
+ * FKCONSTR_REF_EACH_ELEMENT, append " sep leftop @>> rightop" to buf.
+ *
+ * The complexity comes from needing to be sure that the parser will select
+ * the desired operator. We always name the operator using
+ * OPERATOR(schema.op) syntax (readability isn't a big priority here), so as
+ * to avoid search-path uncertainties. We have to emit casts too, if either
+ * input isn't already the input type of the operator; else we are at the
+ * mercy of the parser's heuristics for ambiguous-operator resolution.
+ */
static void
ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype)
+ const char *rightop, Oid rightoptype,
+ char fkreftype)
{
HeapTuple opertup;
Form_pg_operator operform;
char *oprname;
char *nspname;
+ Oid oprright;
opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opoid));
if (!HeapTupleIsValid(opertup))
@@ -2586,15 +2682,46 @@ ri_GenerateQual(StringInfo buf,
nspname = get_namespace_name(operform->oprnamespace);
- appendStringInfo(buf, " %s %s", sep, leftop);
- if (leftoptype != operform->oprleft)
- ri_add_cast_to(buf, operform->oprleft);
- appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
- appendStringInfoString(buf, oprname);
- appendStringInfo(buf, ") %s", rightop);
- if (rightoptype != operform->oprright)
- ri_add_cast_to(buf, operform->oprright);
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ oprright = get_array_type(operform->oprright);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprright))));
+ }
+ else
+ oprright = operform->oprright;
+
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT){
+ appendStringInfo(buf, " %s %s", sep, rightop);
+
+ if (rightoptype != oprright)
+ ri_add_cast_to(buf, oprright);
+ appendStringInfo(buf, " @>> ");
+
+ appendStringInfoString(buf, leftop);
+
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+ }
+ else{
+ appendStringInfo(buf, " %s %s", sep, leftop);
+
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+
+ appendStringInfo(buf, " OPERATOR(%s.%s) ",
+ quote_identifier(nspname), oprname);
+
+ appendStringInfoString(buf, rightop);
+
+ if (rightoptype != oprright)
+ ri_add_cast_to(buf, oprright);
+ }
+
ReleaseSysCache(opertup);
}
@@ -2801,6 +2928,7 @@ ri_LoadConstraintInfo(Oid constraintOid)
bool isNull;
ArrayType *arr;
int numkeys;
+ int i;
/*
* On the first call initialize the hashtable
@@ -2879,6 +3007,20 @@ ri_LoadConstraintInfo(Oid constraintOid)
pfree(arr); /* free de-toasted copy, if any */
adatum = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype, &isNull);
+ if (isNull)
+ elog(ERROR, "null confreftype for constraint %u", constraintOid);
+ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
+ if (ARR_NDIM(arr) != 1 ||
+ ARR_DIMS(arr)[0] != numkeys ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != CHAROID)
+ elog(ERROR, "confreftype is not a 1-D char array");
+ memcpy(riinfo->fk_reftypes, ARR_DATA_PTR(arr), numkeys * sizeof(char));
+ if ((Pointer) arr != DatumGetPointer(adatum))
+ pfree(arr); /* free de-toasted copy, if any */
+
+ adatum = SysCacheGetAttr(CONSTROID, tup,
Anum_pg_constraint_conpfeqop, &isNull);
if (isNull)
elog(ERROR, "null conpfeqop for constraint %u", constraintOid);
@@ -2921,6 +3063,24 @@ ri_LoadConstraintInfo(Oid constraintOid)
if ((Pointer) arr != DatumGetPointer(adatum))
pfree(arr); /* free de-toasted copy, if any */
+ /*
+ * Fix up some stuff for array foreign keys. We need a has_array flag
+ * indicating whether there's an array foreign key, and we want to set
+ * ff_eq_oprs[i] to array_eq() for array columns, because that's what
+ * makes sense for ri_KeysEqual, and we have no other use for ff_eq_oprs
+ * in this module. (If we did, substituting the array comparator at the
+ * call point in ri_KeysEqual might be more appropriate.)
+ */
+ riinfo->has_array = false;
+ for (i = 0; i < numkeys; i++)
+ {
+ if (riinfo->fk_reftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ riinfo->has_array = true;
+ riinfo->ff_eq_oprs[i] = ARRAY_EQ_OP;
+ }
+ }
+
ReleaseSysCache(tup);
/*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index d83377d1d8..680623534c 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -314,6 +314,9 @@ static char *pg_get_viewdef_worker(Oid viewoid,
static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
static void decompile_column_index_array(Datum column_index_array, Oid relId,
StringInfo buf);
+static void decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf);
static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
const Oid *excludeOps,
@@ -1882,7 +1885,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
{
case CONSTRAINT_FOREIGN:
{
- Datum val;
+ Datum colindexes;
+ Datum reftypes;
bool isnull;
const char *string;
@@ -1890,13 +1894,21 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
appendStringInfoString(&buf, "FOREIGN KEY (");
/* Fetch and build referencing-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_conkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_conkey,
+ &isnull);
if (isnull)
elog(ERROR, "null conkey for constraint %u",
constraintId);
+ reftypes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype,
+ &isnull);
+ if (isnull)
+ elog(ERROR, "null confreftype for constraint %u",
+ constraintId);
- decompile_column_index_array(val, conForm->conrelid, &buf);
+ decompile_fk_column_index_array(colindexes, reftypes,
+ conForm->conrelid, &buf);
/* add foreign relation name */
appendStringInfo(&buf, ") REFERENCES %s(",
@@ -1904,13 +1916,15 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
NIL));
/* Fetch and build referenced-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_confkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confkey,
+ &isnull);
if (isnull)
elog(ERROR, "null confkey for constraint %u",
constraintId);
- decompile_column_index_array(val, conForm->confrelid, &buf);
+ decompile_column_index_array(colindexes,
+ conForm->confrelid, &buf);
appendStringInfoChar(&buf, ')');
@@ -2185,6 +2199,66 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
}
}
+ /*
+ * Convert an int16[] Datum and a char[] Datum into a comma-separated
+ * list of column names for the indicated relation, prefixed by appropriate
+ * keywords depending on the foreign key reference semantics indicated by
+ * the char[] entries. Append the text to buf.
+ */
+ static void
+ decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf)
+ {
+ Datum *keys;
+ int nKeys;
+ Datum *reftypes;
+ int nReftypes;
+ int j;
+
+ /* Extract data from array of int16 */
+ deconstruct_array(DatumGetArrayTypeP(column_index_array),
+ INT2OID, sizeof(int16), true, 's',
+ &keys, NULL, &nKeys);
+
+ /* Extract data from array of char */
+ deconstruct_array(DatumGetArrayTypeP(fk_reftype_array),
+ CHAROID, sizeof(char), true, 'c',
+ &reftypes, NULL, &nReftypes);
+
+ if (nKeys != nReftypes)
+ elog(ERROR, "wrong confreftype cardinality");
+
+ for (j = 0; j < nKeys; j++)
+ {
+ char *colName;
+ const char *prefix;
+
+ colName = get_relid_attribute_name(relId, DatumGetInt16(keys[j]));
+
+ switch (DatumGetChar(reftypes[j]))
+ {
+ case FKCONSTR_REF_PLAIN:
+ prefix = "";
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ prefix = "EACH ELEMENT OF ";
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d",
+ (int) DatumGetChar(reftypes[j]));
+ prefix = NULL; /* keep compiler quiet */
+ break;
+ }
+
+ if (j == 0)
+ appendStringInfo(buf, "%s%s", prefix,
+ quote_identifier(colName));
+ else
+ appendStringInfo(buf, ", %s%s", prefix,
+ quote_identifier(colName));
+ }
+}
/* ----------
* get_expr - Decompile an expression tree
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index f850be490a..55a43d0b90 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -689,7 +689,8 @@ DATA(insert ( 2745 2277 2277 1 s 2750 2742 0 ));
DATA(insert ( 2745 2277 2277 2 s 2751 2742 0 ));
DATA(insert ( 2745 2277 2277 3 s 2752 2742 0 ));
DATA(insert ( 2745 2277 2277 4 s 1070 2742 0 ));
-
+DATA(insert ( 2745 2277 2283 5 s 6108 2742 0 )); /* anyarray @>> anyelem */
+
/*
* btree enum_ops
*/
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index ec035d8434..bea6f82404 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -104,8 +104,16 @@ CATALOG(pg_constraint,2606)
int16 confkey[1];
/*
+ * If a foreign key, the reference semantics for each column
+ */
+ char confreftype[1];
+
+ /*
* If a foreign key, the OIDs of the PK = FK equality operators for each
* column of the constraint
+ *
+ * Note: for array foreign keys, all these operators are for the array's
+ * element type.
*/
Oid conpfeqop[1];
@@ -150,7 +158,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* compiler constants for pg_constraint
* ----------------
*/
-#define Natts_pg_constraint 24
+#define Natts_pg_constraint 25
#define Anum_pg_constraint_conname 1
#define Anum_pg_constraint_connamespace 2
#define Anum_pg_constraint_contype 3
@@ -169,12 +177,13 @@ typedef FormData_pg_constraint *Form_pg_constraint;
#define Anum_pg_constraint_connoinherit 16
#define Anum_pg_constraint_conkey 17
#define Anum_pg_constraint_confkey 18
-#define Anum_pg_constraint_conpfeqop 19
-#define Anum_pg_constraint_conppeqop 20
-#define Anum_pg_constraint_conffeqop 21
-#define Anum_pg_constraint_conexclop 22
-#define Anum_pg_constraint_conbin 23
-#define Anum_pg_constraint_consrc 24
+#define Anum_pg_constraint_confreftype 19
+#define Anum_pg_constraint_conpfeqop 20
+#define Anum_pg_constraint_conppeqop 21
+#define Anum_pg_constraint_conffeqop 22
+#define Anum_pg_constraint_conexclop 23
+#define Anum_pg_constraint_conbin 24
+#define Anum_pg_constraint_consrc 25
/* ----------------
* initial contents of pg_constraint
@@ -195,7 +204,9 @@ typedef FormData_pg_constraint *Form_pg_constraint;
/*
* Valid values for confupdtype and confdeltype are the FKCONSTR_ACTION_xxx
* constants defined in parsenodes.h. Valid values for confmatchtype are
- * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h.
+ * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h. Valid values
+ * for elements of confreftype[] are the FKCONSTR_REF_xxx constants defined
+ * in parsenodes.h.
*/
#endif /* PG_CONSTRAINT_H */
diff --git a/src/include/catalog/pg_constraint_fn.h b/src/include/catalog/pg_constraint_fn.h
index a4c46897ed..06f4313bae 100644
--- a/src/include/catalog/pg_constraint_fn.h
+++ b/src/include/catalog/pg_constraint_fn.h
@@ -40,6 +40,7 @@ extern Oid CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index ffabc2003b..8a9d616d29 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1570,6 +1570,8 @@ DESCR("contains");
DATA(insert OID = 2752 ( "<@" PGNSP PGUID b f f 2277 2277 16 2751 0 arraycontained arraycontsel arraycontjoinsel ));
DESCR("is contained by");
#define OID_ARRAY_CONTAINED_OP 2752
+DATA(insert OID = 6108 ( "@>>" PGNSP PGUID b f f 2277 2283 16 0 0 arraycontainselem arraycontsel arraycontjoinsel ));
+DESCR("containselem");
/* capturing operators to preserve pre-8.3 behavior of text concatenation */
DATA(insert OID = 2779 ( "||" PGNSP PGUID b f f 25 2776 25 0 0 textanycat - - ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 8b33b4e0ea..01b207d363 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4308,6 +4308,7 @@ DESCR("GIN array support (obsolete)");
DATA(insert OID = 2747 ( arrayoverlap PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arrayoverlap _null_ _null_ _null_ ));
DATA(insert OID = 2748 ( arraycontains PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arraycontains _null_ _null_ _null_ ));
DATA(insert OID = 2749 ( arraycontained PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arraycontained _null_ _null_ _null_ ));
+DATA(insert OID = 6109 ( arraycontainselem PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2283" _null_ _null_ _null_ _null_ _null_ arraycontainselem _null_ _null_ _null_ ));
/* BRIN minmax */
DATA(insert OID = 3383 ( brin_minmax_opcinfo PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_opcinfo _null_ _null_ _null_ ));
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 5f2a4a75da..7662ef8018 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2067,6 +2067,10 @@ typedef enum ConstrType /* types of constraints */
#define FKCONSTR_MATCH_PARTIAL 'p'
#define FKCONSTR_MATCH_SIMPLE 's'
+ /* Foreign key column reference semantics codes */
+ #define FKCONSTR_REF_PLAIN 'p'
+ #define FKCONSTR_REF_EACH_ELEMENT 'e'
+
typedef struct Constraint
{
NodeTag type;
@@ -2102,6 +2106,7 @@ typedef struct Constraint
RangeVar *pktable; /* Primary key table */
List *fk_attrs; /* Attributes of foreign key */
List *pk_attrs; /* Corresponding attrs in PK table */
+ List *fk_reftypes; /* Per-column reference semantics (int List) */
char fk_matchtype; /* FULL, PARTIAL, SIMPLE */
char fk_upd_action; /* ON UPDATE action */
char fk_del_action; /* ON DELETE action */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f50e45e886..d3f4803006 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -140,6 +140,7 @@ PG_KEYWORD("domain", DOMAIN_P, UNRESERVED_KEYWORD)
PG_KEYWORD("double", DOUBLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD)
PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD)
+PG_KEYWORD("element", ELEMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("else", ELSE, RESERVED_KEYWORD)
PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD)
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index c730563f03..0a9eff4d04 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -737,6 +737,17 @@ SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(6 rows)
+SELECT * FROM array_op_test WHERE i @>> 32 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+ 98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
+ 100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
+(6 rows)
+
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -761,6 +772,19 @@ SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
(8 rows)
+SELECT * FROM array_op_test WHERE i @>> 17 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
+ 53 | {38,17} | {AAAAAAAAAAA21658}
+ 65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+(8 rows)
+
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -942,6 +966,11 @@ SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
-------+---+---
(0 rows)
+SELECT * FROM array_op_test WHERE i @>> NULL ORDER BY seqno;
+ seqno | i | t
+-------+---+---
+(0 rows)
+
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
seqno | i | t
-------+---+---
@@ -953,6 +982,15 @@ SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
101 | {} | {}
(1 row)
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAA72908' ORDER BY seqno;
+ seqno | i | t
+-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
+ 22 | {11,6,56,62,53,30} | {AAAAAAAA72908}
+ 45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}
+ 72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+(4 rows)
+
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
seqno | i | t
-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
@@ -971,6 +1009,14 @@ SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
(4 rows)
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAAAA646' ORDER BY seqno;
+ seqno | i | t
+-------+------------------+--------------------------------------------------------------------
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+ 96 | {23,97,43} | {AAAAAAAAAA646,A87088}
+(3 rows)
+
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
seqno | i | t
-------+------------------+--------------------------------------------------------------------
diff --git a/src/test/regress/expected/element_foreign_key.out b/src/test/regress/expected/element_foreign_key.out
new file mode 100644
index 0000000000..586eccab6e
--- /dev/null
+++ b/src/test/regress/expected/element_foreign_key.out
@@ -0,0 +1,553 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fkarray"
+DETAIL: Key (ftest1)=({10,1}) is not present in table "pktableforarray".
+DROP TABLE FKTABLEFORARRAY;
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4,6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL,4,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+ERROR: insert or update on table "fktableforarraymdim" violates foreign key constraint "fktableforarraymdim_ftest1_fkey"
+DETAIL: Key (ftest1)=({{1,2},{6,NULL}}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+ERROR: null value in column "ftest1" violates not-null constraint
+DETAIL: Failing row contains (null, 21).
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+ {1} | 4
+(11 rows)
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({D}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({A,B,D}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(A) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(B) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ERROR: foreign key constraint "fktableforarray_ftest1_fkey" cannot be implemented
+DETAIL: Key column "ftest1" has element type integer which does not have a default btree operator class that's compatible with class "float8_ops".
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(A, {A,B,C}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(B, {A}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Test ELEMENT foreign keys with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,99)"}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,1)","(2010,1)"}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2010,99)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2011,1)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({0,1}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({2,1}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(2, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(20, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+ERROR: foreign keys support only one array column
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: foreign keys support only one array column
+DROP TABLE F1;
+-- Cleanup
+DROP TABLE DIM1;
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "x2_x1_fkey"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "fk_const"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+DROP TABLE x1;
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{100,100,100},{NULL,NULL,20},{7,8,10}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index fcf8bd7565..25ef369951 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1811,6 +1811,7 @@ ORDER BY 1, 2, 3;
2742 | 2 | @@@
2742 | 3 | <@
2742 | 4 | =
+ 2742 | 5 | @>>
2742 | 7 | @>
2742 | 9 | ?
2742 | 10 | ?|
@@ -1878,7 +1879,7 @@ ORDER BY 1, 2, 3;
4000 | 25 | <<=
4000 | 26 | >>
4000 | 27 | >>=
-(121 rows)
+(122 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index eefdeeacae..d88ff3772c 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -103,7 +103,7 @@ test: publication subscription
# ----------
# Another group of parallel tests
# ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
+test: select_views portals_p2 foreign_key element_foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
# ----------
# Another group of parallel tests
# NB: temp.sql does a reconnect which transiently uses 2 connections,
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 76b0de30a7..9cb8638b4e 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -140,6 +140,7 @@ test: amutils
test: select_views
test: portals_p2
test: foreign_key
+test: element_foreign_key
test: cluster
test: dependency
test: guc
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 25dd4e2c6d..f6fb507bdd 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -313,8 +313,10 @@ SELECT ARRAY[0,0] || ARRAY[1,1] || ARRAY[2,2] AS "{0,0,1,1,2,2}";
SELECT 0 || ARRAY[1,2] || 3 AS "{0,1,2,3}";
SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> 32 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> 17 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{32,17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32,17}' ORDER BY seqno;
@@ -325,11 +327,14 @@ SELECT * FROM array_op_test WHERE i && '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> NULL ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAA72908' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAAAA646' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno;
diff --git a/src/test/regress/sql/element_foreign_key.sql b/src/test/regress/sql/element_foreign_key.sql
new file mode 100644
index 0000000000..8d5363efbf
--- /dev/null
+++ b/src/test/regress/sql/element_foreign_key.sql
@@ -0,0 +1,415 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Test ELEMENT foreign keys with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+
+
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+
+
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+-- Cleanup
+DROP TABLE DIM1;
+
+
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+DROP TABLE x2;
+DROP TABLE x1;
+
+
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+DROP TABLE F1;
+
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+DROP TABLE F1;
+
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-27 00:31 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 17:15 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
0 siblings, 2 replies; 200+ messages in thread
From: Mark Rofail @ 2017-07-27 00:31 UTC (permalink / raw)
To: Erik Rijkers <er@xs4all.nl>; +Cc: Robert Haas <robertmhaas@gmail.com>; Alexander Korotkov <aekorotkov@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers@postgresql.org, David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>; pgsql-hackers-owner@postgresql.org
I have written some benchmark test.
With two tables a PK table with 5 rows and an FK table with growing row
count.
Once triggering an RI check
at 10 rows,
100 rows,
1,000 rows,
10,000 rows,
100,000 rows and
1,000,000 rows
Please find the graph with the findings attached below
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
FK Benchmark.jpg (image/jpeg, 404.3K) download
view image
FK Benchmark.jpg (image/jpeg, 404.3K) download
view image
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-27 10:54 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
1 sibling, 1 reply; 200+ messages in thread
From: Alexander Korotkov @ 2017-07-27 10:54 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>; pgsql-hackers-owner@postgresql.org
On Thu, Jul 27, 2017 at 3:31 AM, Mark Rofail <markm.rofail@gmail.com> wrote:
> I have written some benchmark test.
>
> With two tables a PK table with 5 rows and an FK table with growing row
> count.
>
> Once triggering an RI check
> at 10 rows,
> 100 rows,
> 1,000 rows,
> 10,000 rows,
> 100,000 rows and
> 1,000,000 rows
>
How many rows of FK table were referencing the PK table row you're
updating/deleting.
I wonder how may RI trigger work so fast if it has to do some job besides
index search with no results?
I think we should also vary the number of referencing rows.
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
@ 2017-07-27 12:07 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-07-27 12:07 UTC (permalink / raw)
To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>; pgsql-hackers-owner@postgresql.org
On Thu, Jul 27, 2017 at 12:54 PM, Alexander Korotkov <aekorotkov@gmail.com>
wrote:
>
> How many rows of FK table were referencing the PK table row you're
> updating/deleting.
> I wonder how may RI trigger work so fast if it has to do some job besides
> index search with no results?
>
The problem here is that the only to option for the foreign key arrays are
NO ACTION and RESTRICT which don't allow me to update/delete a refrenced
row in the PK Table. the EXPLAIN ANALYZE only tells me that this violates
the FK constraint.
So we have two options. Either implement CASCADE or if there's a
configration for EXPLAIN to show costs even if it violates the FK
constraints.
> I think we should also vary the number of referencing rows.
>
The x axis is the number if refrencing rows in the FK table
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-07-27 17:30 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 19:15 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 2 replies; 200+ messages in thread
From: Alexander Korotkov @ 2017-07-27 17:30 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>; pgsql-hackers-owner@postgresql.org
On Thu, Jul 27, 2017 at 3:07 PM, Mark Rofail <markm.rofail@gmail.com> wrote:
> On Thu, Jul 27, 2017 at 12:54 PM, Alexander Korotkov <aekorotkov@gmail.com
> > wrote:
>>
>> How many rows of FK table were referencing the PK table row you're
>> updating/deleting.
>> I wonder how may RI trigger work so fast if it has to do some job besides
>> index search with no results?
>>
> The problem here is that the only to option for the foreign key arrays are
> NO ACTION and RESTRICT which don't allow me to update/delete a refrenced
> row in the PK Table. the EXPLAIN ANALYZE only tells me that this violates
> the FK constraint.
>
> So we have two options. Either implement CASCADE or if there's a
> configration for EXPLAIN to show costs even if it violates the FK
> constraints.
>
Oh, ok. I missed that.
Could you remind me why don't we have DELETE CASCADE? I understand that
UPDATE CASCADE is problematic because it's unclear which way should we
delete elements from array. But what about DELETE CASCADE?
------
Alexander Korotkov
Postgres Professional: http://www.postgrespro.com
The Russian Postgres Company
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
@ 2017-07-27 19:15 ` Mark Rofail <markm.rofail@gmail.com>
1 sibling, 0 replies; 200+ messages in thread
From: Mark Rofail @ 2017-07-27 19:15 UTC (permalink / raw)
To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>; pgsql-hackers-owner@postgresql.org
On Thu, Jul 27, 2017 at 7:30 PM, Alexander Korotkov <aekorotkov@gmail.com>
wrote:
> Oh, ok. I missed that.
>>
> Could you remind me why don't we have DELETE CASCADE? I understand that
> UPDATE CASCADE is problematic because it's unclear which way should we
> delete elements from array. But what about DELETE CASCADE?
>
Honestly, I didn't touch that part of the patch. It's very interesting
though, I think it would be great to spend the rest of GSoC in it.
Off the top of my head though, there's many ways to go about DELETE
CASCADE. You could only delete the member of the referencing array or the
whole array. I think there's a lot of options the user might want to
consider and it's hard to generalize to DELETE CASCADE. Maybe new grammar
would be introduced here ?|
Best Regards,
Mark Rofail
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
@ 2017-08-19 17:49 ` Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
1 sibling, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-08-19 17:49 UTC (permalink / raw)
To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>; pgsql-hackers-owner@postgresql.org
I have a concern that after supporting UPDATE/DELETE CASCADE, the
performance would drop.
On Thu, Jul 27, 2017 at 12:54 PM, Alexander Korotkov <aekorotkov@gmail.com>
wrote:
>
> I wonder how may RI trigger work so fast if it has to do some job besides
> index search with no results?
>
Best Regards,
Mark Rofail
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-08-21 13:43 ` Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-08-21 13:43 UTC (permalink / raw)
To: Alexander Korotkov <aekorotkov@gmail.com>; +Cc: Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; Alvaro Herrera <alvherre@2ndquadrant.com>; pgsql-hackers; David Steele <david@pgmasters.net>; Stephen Frost <sfrost@snowman.net>; pgsql-hackers-owner@postgresql.org
Okay... There are several developments...
1) Whenever there was an index on the Foreign Key Array column and I issue
the following query "SELECT * FROM FKTABLEFORARRAY WHERE ftest1 @>> 5;" I
wouldn't get any results, even though it works without an index.
This confirmed my suspicion that there was something wrong with the GIN
index, the results were too good to be true in the comparison here
<https://www.postgresql.org/message-id/CAJvoCuv6ufngfZBPNq4icCy%3Dx6NpqVVyyPaio97ZHcZVvAC%2BZg%40mail...;.
It's almost O(1);
Turns out the index search wasn't performed at all. When I fixed this bug,
the performance changed. And the new benchmark graph is attached below.
2) About coercion, coercion should only be allowed one way not the other.
Since casting the FK column defeats the index and performance drops
drastically. I don't want to disallow it though. Should I leave it up to
the user?
3) As of patch v4.4 DELETE CASCADE is now supported. The rest of the
actions, however, will require array manipulation.
Once the updates are confirmed, I'll update the limitation checklist.
Best Regards,
Mark Rofail
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
FK Benchmark2.jpg (image/jpeg, 454.0K) download
view image
Array-ELEMENT-foreign-key-v4.4.patch (text/x-patch, 138.6K) download
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ef7054cf26..f16638dcda 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2322,7 +2322,17 @@ SCRAM-SHA-256$<replaceable><iteration count></>:<replaceable><salt><
</row>
<row>
- <entry><structfield>conpfeqop</structfield></entry>
+ <entry><structfield>confreftype</structfield></entry>
+ <entry><type>char[]</type></entry>
+ <entry></entry>
+ <entry>If a foreign key, the reference semantics for each column:
+ <literal>p</> = plain (simple equality),
+ <literal>e</> = each element of referencing array must have a match
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>conpfeqop</structfield></entry>
<entry><type>oid[]</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</></entry>
<entry>If a foreign key, list of the equality operators for PK = FK comparisons</entry>
@@ -2367,6 +2377,12 @@ SCRAM-SHA-256$<replaceable><iteration count></>:<replaceable><salt><
</table>
<para>
+ When <structfield>confreftype</structfield> indicates array-vs-scalar
+ foreign key reference semantics, the equality operators listed in
+ <structfield>conpfeqop</structfield> etc are for the array's element type.
+ </para>
+
+ <para>
In the case of an exclusion constraint, <structfield>conkey</structfield>
is only useful for constraint elements that are simple column references.
For other cases, a zero appears in <structfield>conkey</structfield>
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index b05a9c2150..75da196334 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -882,6 +882,111 @@ CREATE TABLE order_items (
</para>
</sect2>
+ <sect2 id="ddl-constraints-element-fk">
+ <title>Array ELEMENT Foreign Keys</title>
+
+ <indexterm>
+ <primary>ELEMENT foreign key</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>Array ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>referential integrity</primary>
+ </indexterm>
+
+ <para>
+ Another option you have with foreign keys is to use a
+ referencing column which is an array of elements with
+ the same type (or a compatible one) as the referenced
+ column in the related table. This feature is called
+ <firstterm>array element foreign key</firstterm> and is implemented
+ in PostgreSQL with <firstterm>ELEMENT foreign key constraints</firstterm>,
+ as described in the following example:
+
+<programlisting>
+CREATE TABLE drivers (
+ driver_id integer PRIMARY KEY,
+ first_name text,
+ last_name text,
+ ...
+);
+
+CREATE TABLE races (
+ race_id integer PRIMARY KEY,
+ title text,
+ race_day DATE,
+ ...
+ final_positions integer[] <emphasis>ELEMENT REFERENCES drivers</emphasis>
+);
+</programlisting>
+
+ The above example uses an array (<literal>final_positions</literal>)
+ to store the results of a race: for each of its elements
+ a referential integrity check is enforced on the
+ <literal>drivers</literal> table.
+ Note that <literal>ELEMENT REFERENCES</literal> is an extension
+ of PostgreSQL and it is not included in the SQL standard.
+ </para>
+
+ <para>
+ Even though the most common use case for array <literal>ELEMENT</literal>
+ foreign keys is on a single column key, you can define an <quote>array
+ <literal>ELEMENT</literal> foreign key constraint</quote> on a group
+ of columns. As the following example shows, it must be written in table
+ constraint form:
+
+<programlisting>
+CREATE TABLE available_moves (
+ kind text,
+ move text,
+ description text,
+ PRIMARY KEY (kind, move)
+);
+
+CREATE TABLE paths (
+ description text,
+ kind text,
+ moves text[],
+ <emphasis>FOREIGN KEY (kind, ELEMENT moves) REFERENCES available_moves (kind, move)</emphasis>
+);
+
+INSERT INTO available_moves VALUES ('relative', 'LN', 'look north');
+INSERT INTO available_moves VALUES ('relative', 'RL', 'rotate left');
+INSERT INTO available_moves VALUES ('relative', 'RR', 'rotate right');
+INSERT INTO available_moves VALUES ('relative', 'MF', 'move forward');
+INSERT INTO available_moves VALUES ('absolute', 'N', 'move north');
+INSERT INTO available_moves VALUES ('absolute', 'S', 'move south');
+INSERT INTO available_moves VALUES ('absolute', 'E', 'move east');
+INSERT INTO available_moves VALUES ('absolute', 'W', 'move west');
+
+INSERT INTO paths VALUES ('L-shaped path', 'relative', '{LN, RL, MF, RR, MF, MF}');
+INSERT INTO paths VALUES ('L-shaped path', 'absolute', '{W, N, N}');
+</programlisting>
+
+ On top of standard foreign key requirements,
+ array <literal>ELEMENT</literal> foreign key constraints
+ require that the referencing column is an array of a compatible
+ type of the corresponding referenced column.
+ </para>
+
+ <para>
+ For more detailed information on array <literal>ELEMENT</literal>
+ foreign key options and special cases, please refer to the documentation
+ for <xref linkend="sql-createtable-foreign-key"> and
+ <xref linkend="sql-createtable-element-foreign-key-constraints">.
+ </para>
+
+ </sect2>
+
<sect2 id="ddl-constraints-exclusion">
<title>Exclusion Constraints</title>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index e9c2c49533..90de1ca102 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -65,7 +65,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ] |
UNIQUE <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="PARAMETER">index_parameters</replaceable> |
- REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
+ [ELEMENT] REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -76,7 +76,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
UNIQUE ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ] |
- FOREIGN KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> [, ... ] ) ]
+ FOREIGN KEY ( [ELEMENT] <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> [, ... ] ) ]
[ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -812,10 +812,10 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</listitem>
</varlistentry>
- <varlistentry>
+ <varlistentry id="sql-createtable-foreign-key" xreflabel="FOREIGN KEY">
<term><literal>REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
- <term><literal>FOREIGN KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] )
+ <term><literal>FOREIGN KEY ( [ELEMENT] <replaceable class="parameter">column_name</replaceable> [, ... ] )
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] ) ]
[ MATCH <replaceable class="parameter">matchtype</replaceable> ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ]
@@ -839,6 +839,21 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</para>
<para>
+ In case the column name <replaceable class="parameter">column</replaceable>
+ is prepended with the <literal>ELEMENT</literal> keyword and <replaceable
+ class="parameter">column</replaceable> is an array of elements compatible
+ with the corresponding <replaceable class="parameter">refcolumn</replaceable>
+ in <replaceable class="parameter">reftable</replaceable>, an
+ array <literal>ELEMENT</literal> foreign key constraint is put in place
+ (see <xref linkend="sql-createtable-element-foreign-key-constraints">
+ for more information).
+ Multi-column keys with more than one <literal>ELEMENT</literal> column
+ are currently not allowed.
+
+ It is advisable to index the refrencing column using GIN index as it considerably enhances the performance.
+ </para>
+
+ <para>
A value inserted into the referencing column(s) is matched against the
values of the referenced table and referenced columns using the
given match type. There are three match types: <literal>MATCH
@@ -901,7 +916,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
<para>
Delete any rows referencing the deleted row, or update the
values of the referencing column(s) to the new values of the
- referenced columns, respectively.
+ referenced columns, respectively. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -910,7 +926,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
<term><literal>SET NULL</literal></term>
<listitem>
<para>
- Set the referencing column(s) to null.
+ Set the referencing column(s) to null. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -922,6 +939,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
Set the referencing column(s) to their default values.
(There must be a row in the referenced table matching the default
values, if they are not null, or the operation will fail.)
+ Currently not supported with array <literal>ELEMENT</literal>
+ foreign keys.
</para>
</listitem>
</varlistentry>
@@ -937,6 +956,61 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</listitem>
</varlistentry>
+ <varlistentry id="sql-createtable-element-foreign-key-constraints" xreflabel="ELEMENT REFERENCES">
+ <term><literal>ELEMENT REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
+
+ <listitem>
+ <para>
+ The <literal>ELEMENT REFERENCES</literal> definition specifies
+ an <quote>array <literal>ELEMENT</literal> foreign key</quote>,
+ a special kind of foreign key
+ constraint requiring the referencing column to be an array of elements
+ of the same type (or a compatible one) as the referenced column
+ in the referenced table. The value of each element of the
+ <replaceable class="parameter">refcolumn</replaceable> array
+ will be matched against some row of <replaceable
+ class="parameter">reftable</replaceable>.
+ </para>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys are an extension
+ of PostgreSQL and are not included in the SQL standard.
+ </para>
+
+ <para>
+ Even with <literal>ELEMENT</literal> foreign keys, modifications
+ in the referenced column can trigger actions to be performed on
+ the referencing array.
+ Similarly to standard foreign keys, you can specify these
+ actions using the <literal>ON DELETE</literal> and
+ <literal>ON UPDATE</literal> clauses.
+ However, only the two following actions for each clause are
+ currently allowed:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>NO ACTION</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints. This is the default action.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RESTRICT</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>DEFERRABLE</literal></term>
<term><literal>NOT DEFERRABLE</literal></term>
@@ -1876,6 +1950,16 @@ CREATE TABLE cities_ab_10000_to_100000
</refsect2>
<refsect2>
+ <title id="sql-createtable-foreign-key-arrays">Array <literal>ELEMENT</literal> Foreign Keys</title>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys and the
+ <literal>ELEMENT REFERENCES</literal> clause
+ are a <productname>PostgreSQL</productname> extension.
+ </para>
+ </refsect2>
+
+ <refsect2>
<title><literal>PARTITION BY</> Clause</title>
<para>
diff --git a/src/backend/access/gin/ginarrayproc.c b/src/backend/access/gin/ginarrayproc.c
index a5238c3af5..4246f6f704 100644
--- a/src/backend/access/gin/ginarrayproc.c
+++ b/src/backend/access/gin/ginarrayproc.c
@@ -24,6 +24,7 @@
#define GinContainsStrategy 2
#define GinContainedStrategy 3
#define GinEqualStrategy 4
+#define GinContainsElemStrategy 5
/*
@@ -43,7 +44,7 @@ ginarrayextract(PG_FUNCTION_ARGS)
bool *nulls;
int nelems;
- get_typlenbyvalalign(ARR_ELEMTYPE(array),
+ get_typlenbyvalalign(ARR_ELEMTYPE(array),
&elmlen, &elmbyval, &elmalign);
deconstruct_array(array,
@@ -79,7 +80,8 @@ Datum
ginqueryarrayextract(PG_FUNCTION_ARGS)
{
/* Make copy of array input to ensure it doesn't disappear while in use */
- ArrayType *array = PG_GETARG_ARRAYTYPE_P_COPY(0);
+ ArrayType *array;
+ Datum elem;
int32 *nkeys = (int32 *) PG_GETARG_POINTER(1);
StrategyNumber strategy = PG_GETARG_UINT16(2);
@@ -94,45 +96,77 @@ ginqueryarrayextract(PG_FUNCTION_ARGS)
bool *nulls;
int nelems;
- get_typlenbyvalalign(ARR_ELEMTYPE(array),
- &elmlen, &elmbyval, &elmalign);
+ if (strategy == GinContainsElemStrategy)
+ {
+ /*
+ * since this function return a pointer to a
+ * deconstructed array, there is nothing to do if
+ * operand is a single element except to return
+ * as is and configure the searchmode
+ */
- deconstruct_array(array,
- ARR_ELEMTYPE(array),
- elmlen, elmbyval, elmalign,
- &elems, &nulls, &nelems);
+ /*
+ * only items that match the queried element
+ * are considered candidate
+ */
- *nkeys = nelems;
- *nullFlags = nulls;
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+
+ elem = PG_GETARG_DATUM(0);
+ *nkeys = 1;
+ *nullFlags = PG_ARGISNULL(0);
- switch (strategy)
+ PG_RETURN_POINTER(elem);
+ }
+ else
{
- case GinOverlapStrategy:
- *searchMode = GIN_SEARCH_MODE_DEFAULT;
- break;
- case GinContainsStrategy:
- if (nelems > 0)
+ array = PG_GETARG_ARRAYTYPE_P_COPY(0);
+
+ get_typlenbyvalalign(ARR_ELEMTYPE(array),
+ &elmlen, &elmbyval, &elmalign);
+
+ deconstruct_array(array,
+ ARR_ELEMTYPE(array),
+ elmlen, elmbyval, elmalign,
+ &elems, &nulls, &nelems);
+
+ *nkeys = nelems;
+ *nullFlags = nulls;
+
+ switch (strategy)
+ {
+ case GinOverlapStrategy:
*searchMode = GIN_SEARCH_MODE_DEFAULT;
- else /* everything contains the empty set */
- *searchMode = GIN_SEARCH_MODE_ALL;
- break;
- case GinContainedStrategy:
- /* empty set is contained in everything */
- *searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
- break;
- case GinEqualStrategy:
- if (nelems > 0)
+ break;
+ case GinContainsElemStrategy:
+ /* only items that match the queried element
+ are considered candidate */
*searchMode = GIN_SEARCH_MODE_DEFAULT;
- else
+ break;
+ case GinContainsStrategy:
+ if (nelems > 0)
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+ else /* everything contains the empty set */
+ *searchMode = GIN_SEARCH_MODE_ALL;
+ break;
+ case GinContainedStrategy:
+ /* empty set is contained in everything */
*searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
- break;
- default:
- elog(ERROR, "ginqueryarrayextract: unknown strategy number: %d",
- strategy);
+ break;
+ case GinEqualStrategy:
+ if (nelems > 0)
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+ else
+ *searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
+ break;
+ default:
+ elog(ERROR, "ginqueryarrayextract: unknown strategy number: %d",
+ strategy);
}
/* we should not free array, elems[i] points into it */
PG_RETURN_POINTER(elems);
+ }
}
/*
@@ -171,6 +205,7 @@ ginarrayconsistent(PG_FUNCTION_ARGS)
}
}
break;
+ case GinContainsElemStrategy:
case GinContainsStrategy:
/* result is not lossy */
*recheck = false;
@@ -258,7 +293,8 @@ ginarraytriconsistent(PG_FUNCTION_ARGS)
}
}
break;
- case GinContainsStrategy:
+ case GinContainsElemStrategy:
+ case GinContainsStrategy:
/* must have all elements in check[] true, and no nulls */
res = GIN_TRUE;
for (i = 0; i < nkeys; i++)
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 7ceea7a741..62189838e1 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -178,7 +178,21 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
if (i < nUserQueryValues)
{
/* set up normal entry using extractQueryFn's outputs */
- queryKey = queryValues[i];
+
+ if (strategy == 5 && nQueryValues == 1)
+ {
+ /*
+ * strategy 5 is GinContainsElemStrategy.
+ * we are sure the strategy is GinContainsElemStrategy,
+ * so take the datum directly without accessing an array
+ */
+ queryKey = queryValues;
+ }
+ else
+ {
+ queryKey = queryValues[i];
+ }
+
queryCategory = queryCategories[i];
isPartialMatch =
(ginstate->canPartialMatch[attnum - 1] && partial_matches)
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 45ee9ac8b9..3fe6b1b247 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2100,6 +2100,7 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c7b2f031f0..db7978ec45 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1213,6 +1213,7 @@ index_constraint_create(Relation heapRelation,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 1336c46d3f..c197cec11a 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -59,6 +59,7 @@ CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
@@ -82,6 +83,7 @@ CreateConstraintEntry(const char *constraintName,
Datum values[Natts_pg_constraint];
ArrayType *conkeyArray;
ArrayType *confkeyArray;
+ ArrayType *confreftypeArray;
ArrayType *conpfeqopArray;
ArrayType *conppeqopArray;
ArrayType *conffeqopArray;
@@ -119,7 +121,11 @@ CreateConstraintEntry(const char *constraintName,
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = Int16GetDatum(foreignKey[i]);
confkeyArray = construct_array(fkdatums, foreignNKeys,
- INT2OID, 2, true, 's');
+ INT2OID, sizeof(int16), true, 's');
+ for (i = 0; i < foreignNKeys; i++)
+ fkdatums[i] = CharGetDatum(foreignRefType[i]);
+ confreftypeArray = construct_array(fkdatums, foreignNKeys,
+ CHAROID, sizeof(char), true, 'c');
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
conpfeqopArray = construct_array(fkdatums, foreignNKeys,
@@ -136,6 +142,7 @@ CreateConstraintEntry(const char *constraintName,
else
{
confkeyArray = NULL;
+ confreftypeArray = NULL;
conpfeqopArray = NULL;
conppeqopArray = NULL;
conffeqopArray = NULL;
@@ -188,6 +195,11 @@ CreateConstraintEntry(const char *constraintName,
else
nulls[Anum_pg_constraint_confkey - 1] = true;
+ if (confreftypeArray)
+ values[Anum_pg_constraint_confreftype - 1] = PointerGetDatum(confreftypeArray);
+ else
+ nulls[Anum_pg_constraint_confreftype - 1] = true;
+
if (conpfeqopArray)
values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
else
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 0f08245a67..d14e6372e8 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7021,6 +7021,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Relation pkrel;
int16 pkattnum[INDEX_MAX_KEYS];
int16 fkattnum[INDEX_MAX_KEYS];
+ char fkreftypes[INDEX_MAX_KEYS];
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
Oid opclasses[INDEX_MAX_KEYS];
@@ -7028,10 +7029,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid ppeqoperators[INDEX_MAX_KEYS];
Oid ffeqoperators[INDEX_MAX_KEYS];
int i;
+ ListCell *lc;
int numfks,
numpks;
Oid indexOid;
Oid constrOid;
+ bool has_array;
bool old_check_ok;
ObjectAddress address;
ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
@@ -7108,6 +7111,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
*/
MemSet(pkattnum, 0, sizeof(pkattnum));
MemSet(fkattnum, 0, sizeof(fkattnum));
+ MemSet(fkreftypes, 0, sizeof(fkreftypes));
MemSet(pktypoid, 0, sizeof(pktypoid));
MemSet(fktypoid, 0, sizeof(fktypoid));
MemSet(opclasses, 0, sizeof(opclasses));
@@ -7120,6 +7124,51 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
fkattnum, fktypoid);
/*
+ * Validate the reference semantics codes, too, and convert list to array
+ * format to pass to CreateConstraintEntry.
+ */
+ Assert(list_length(fkconstraint->fk_reftypes) == numfks);
+ has_array = false;
+ i = 0;
+ foreach(lc, fkconstraint->fk_reftypes)
+ {
+ char reftype = lfirst_int(lc);
+
+ switch (reftype)
+ {
+ case FKCONSTR_REF_PLAIN:
+ /* OK, nothing to do */
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ /* At most one FK column can be an array reference */
+ if (has_array)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign keys support only one array column")));
+ has_array = true;
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d", (int) reftype);
+ break;
+ }
+ fkreftypes[i] = reftype;
+ i++;
+ }
+
+ /* Array foreign keys support only NO ACTION and RESTRICT actions */
+ if (has_array)
+ {
+ if ((fkconstraint->fk_upd_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_upd_action != FKCONSTR_ACTION_RESTRICT) ||
+ (fkconstraint->fk_del_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_CASCADE))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions")));
+ }
+
+ /*
* If the attribute list for the referenced table was omitted, lookup the
* definition of the primary key and use it. Otherwise, validate the
* supplied attribute list. In either case, discover the index OID and
@@ -7205,6 +7254,65 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
eqstrategy = BTEqualStrategyNumber;
/*
+ * If this is an array foreign key, we must look up the operators for
+ * the array element type, not the array type itself.
+ */
+ if (fkreftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ Oid elemopclass;
+
+ /* We look through any domain here */
+ fktype = get_base_element_type(fktype);
+ if (!OidIsValid(fktype))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has type %s which is not an array type.",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktypoid[i]))));
+
+ /*
+ * For the moment, we must also insist that the array's element
+ * type have a default btree opclass that is in the index's
+ * opfamily. This is necessary because ri_triggers.c relies on
+ * COUNT(DISTINCT x) on the element type, as well as on array_eq()
+ * on the array type, and we need those operations to have the
+ * same notion of equality that we're using otherwise.
+ *
+ * XXX this restriction is pretty annoying, considering the effort
+ * that's been put into the rest of the RI mechanisms to make them
+ * work with nondefault equality operators. In particular, it
+ * means that the cast-to-PK-datatype code path isn't useful for
+ * array-to-scalar references.
+ */
+ elemopclass = GetDefaultOpClass(fktype, BTREE_AM_OID);
+ if (!OidIsValid(elemopclass) ||
+ get_opclass_family(elemopclass) != opfamily)
+ {
+ /* Get the index opclass's name for the error message. */
+ char *opcname;
+
+ cla_ht = SearchSysCache1(CLAOID,
+ ObjectIdGetDatum(opclasses[i]));
+ if (!HeapTupleIsValid(cla_ht))
+ elog(ERROR, "cache lookup failed for opclass %u",
+ opclasses[i]);
+ cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
+ opcname = pstrdup(NameStr(cla_tup->opcname));
+ ReleaseSysCache(cla_ht);
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has element type %s which does not have a default btree operator class that's compatible with class \"%s\".",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktype),
+ opcname)));
+ }
+ }
+
+ /*
* There had better be a primary equality operator for the index.
* We'll use it for PK = PK comparisons.
*/
@@ -7265,14 +7373,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("foreign key constraint \"%s\" "
- "cannot be implemented",
- fkconstraint->conname),
- errdetail("Key columns \"%s\" and \"%s\" "
- "are of incompatible types: %s and %s.",
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key columns \"%s\" and \"%s\" are of incompatible types: %s and %s.",
strVal(list_nth(fkconstraint->fk_attrs, i)),
strVal(list_nth(fkconstraint->pk_attrs, i)),
- format_type_be(fktype),
+ format_type_be(fktypoid[i]),
format_type_be(pktype))));
if (old_check_ok)
@@ -7303,6 +7409,13 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
* We may assume that pg_constraint.conkey is not changing.
*/
old_fktype = attr->atttypid;
+ if (fkreftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ old_fktype = get_base_element_type(old_fktype);
+ /* this shouldn't happen ... */
+ if (!OidIsValid(old_fktype))
+ elog(ERROR, "old foreign key column is not an array");
+ }
new_fktype = fktype;
old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
&old_castfunc);
@@ -7345,7 +7458,6 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
new_castfunc == old_castfunc &&
(!IsPolymorphicType(pfeqop_right) ||
new_fktype == old_fktype));
-
}
pfeqoperators[i] = pfeqop;
@@ -7369,6 +7481,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
indexOid,
RelationGetRelid(pkrel),
pkattnum,
+ fkreftypes,
pfeqoperators,
ppeqoperators,
ffeqoperators,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index da0850bfd6..68f06930f9 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -635,6 +635,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
@@ -1006,6 +1007,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
char fk_matchtype = FKCONSTR_MATCH_SIMPLE;
List *fk_attrs = NIL;
List *pk_attrs = NIL;
+ List *fk_reftypes = NIL;
StringInfoData buf;
int funcnum;
OldTriggerInfo *info = NULL;
@@ -1035,7 +1037,10 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
if (i % 2)
fk_attrs = lappend(fk_attrs, arg);
else
- pk_attrs = lappend(pk_attrs, arg);
+ {
+ pk_attrs = lappend(pk_attrs, arg);
+ fk_reftypes = lappend_int(fk_reftypes, FKCONSTR_REF_PLAIN);
+ }
}
/* Prepare description of constraint for use in messages */
@@ -1174,6 +1179,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
fkcon->conname = constr_name;
fkcon->fk_attrs = fk_attrs;
fkcon->pk_attrs = pk_attrs;
+ fkcon->fk_reftypes = fk_reftypes;
fkcon->fk_matchtype = fk_matchtype;
switch (info->funcoids[0])
{
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7ed16aeff4..8639854f4d 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -3092,6 +3092,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 72041693df..544a69d0da 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2845,6 +2845,7 @@ _copyConstraint(const Constraint *from)
COPY_NODE_FIELD(pktable);
COPY_NODE_FIELD(fk_attrs);
COPY_NODE_FIELD(pk_attrs);
+ COPY_NODE_FIELD(fk_reftypes);
COPY_SCALAR_FIELD(fk_matchtype);
COPY_SCALAR_FIELD(fk_upd_action);
COPY_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 8d92c03633..d35f3d1d9a 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2575,6 +2575,7 @@ _equalConstraint(const Constraint *a, const Constraint *b)
COMPARE_NODE_FIELD(pktable);
COMPARE_NODE_FIELD(fk_attrs);
COMPARE_NODE_FIELD(pk_attrs);
+ COMPARE_NODE_FIELD(fk_reftypes);
COMPARE_SCALAR_FIELD(fk_matchtype);
COMPARE_SCALAR_FIELD(fk_upd_action);
COMPARE_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 5ce3c7c599..1a5b535877 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3490,6 +3490,7 @@ _outConstraint(StringInfo str, const Constraint *node)
WRITE_NODE_FIELD(pktable);
WRITE_NODE_FIELD(fk_attrs);
WRITE_NODE_FIELD(pk_attrs);
+ WRITE_NODE_FIELD(fk_reftypes);
WRITE_CHAR_FIELD(fk_matchtype);
WRITE_CHAR_FIELD(fk_upd_action);
WRITE_CHAR_FIELD(fk_del_action);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7d0de99baf..1974625722 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -126,6 +126,13 @@ typedef struct ImportQual
List *table_names;
} ImportQual;
+/* Private struct for the result of foreign_key_column_elem production */
+typedef struct FKColElem
+{
+ Node *name; /* name of the column (a String) */
+ char reftype; /* FKCONSTR_REF_xxx code */
+} FKColElem;
+
/* ConstraintAttributeSpec yields an integer bitmask of these flags: */
#define CAS_NOT_DEFERRABLE 0x01
#define CAS_DEFERRABLE 0x02
@@ -183,6 +190,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
static void SplitColQualList(List *qualList,
List **constraintList, CollateClause **collClause,
core_yyscan_t yyscanner);
+static void SplitFKColElems(List *fkcolelems, List **names, List **reftypes);
static void processCASbits(int cas_bits, int location, const char *constrType,
bool *deferrable, bool *initdeferred, bool *not_valid,
bool *no_inherit, core_yyscan_t yyscanner);
@@ -233,6 +241,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
A_Indices *aind;
ResTarget *target;
struct PrivTarget *privtarget;
+ struct FKColElem *fkcolelem;
AccessPriv *accesspriv;
struct ImportQual *importqual;
InsertStmt *istmt;
@@ -358,6 +367,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <accesspriv> privilege
%type <list> privileges privilege_list
%type <privtarget> privilege_target
+%type <fkcolelem> foreign_key_column_elem
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
%type <ival> defacl_privilege_target
@@ -392,7 +402,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
execute_param_clause using_clause returning_clause
opt_enum_val_list enum_val_list table_func_column_list
create_generic_options alter_generic_options
- relation_expr_list dostmt_opt_list
+ relation_expr_list dostmt_opt_list foreign_key_column_list
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
@@ -622,8 +632,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
DOUBLE_P DROP
- EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
- EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
+ EACH ELEMENT ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE
+ EVENT EXCEPT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
EXTENSION EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FILTER FIRST_P FLOAT_P FOLLOWING FOR
@@ -3406,14 +3416,16 @@ ColConstraintElem:
| REFERENCES qualified_name opt_column_list key_match key_actions
{
Constraint *n = makeNode(Constraint);
- n->contype = CONSTR_FOREIGN;
- n->location = @1;
- n->pktable = $2;
- n->fk_attrs = NIL;
- n->pk_attrs = $3;
- n->fk_matchtype = $4;
- n->fk_upd_action = (char) ($5 >> 8);
- n->fk_del_action = (char) ($5 & 0xFF);
+ n->contype = CONSTR_FOREIGN;
+ n->location = @1;
+ n->pktable = $2;
+ /* fk_attrs will be filled in by parse analysis */
+ n->fk_attrs = NIL;
+ n->pk_attrs = $3;
+ n->fk_reftypes = list_make1_int(FKCONSTR_REF_PLAIN);
+ n->fk_matchtype = $4;
+ n->fk_upd_action = (char) ($5 >> 8);
+ n->fk_del_action = (char) ($5 & 0xFF);
n->skip_validation = false;
n->initially_valid = true;
$$ = (Node *)n;
@@ -3604,14 +3616,15 @@ ConstraintElem:
NULL, yyscanner);
$$ = (Node *)n;
}
- | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
- opt_column_list key_match key_actions ConstraintAttributeSpec
+ | FOREIGN KEY '(' foreign_key_column_list ')' REFERENCES
+ qualified_name opt_column_list key_match key_actions
+ ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_FOREIGN;
n->location = @1;
+ SplitFKColElems($4, &n->fk_attrs, &n->fk_reftypes);
n->pktable = $7;
- n->fk_attrs = $4;
n->pk_attrs = $8;
n->fk_matchtype = $9;
n->fk_upd_action = (char) ($10 >> 8);
@@ -3644,7 +3657,30 @@ columnElem: ColId
$$ = (Node *) makeString($1);
}
;
-
+ foreign_key_column_list:
+ foreign_key_column_elem
+ { $$ = list_make1($1); }
+ | foreign_key_column_list ',' foreign_key_column_elem
+ { $$ = lappend($1, $3); }
+ ;
+
+ foreign_key_column_elem:
+ ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($1);
+ n->reftype = FKCONSTR_REF_PLAIN;
+ $$ = n;
+ }
+ | EACH ELEMENT OF ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($4);
+ n->reftype = FKCONSTR_REF_EACH_ELEMENT;
+ $$ = n;
+ }
+ ;
+
key_match: MATCH FULL
{
$$ = FKCONSTR_MATCH_FULL;
@@ -14655,6 +14691,7 @@ unreserved_keyword:
| DOUBLE_P
| DROP
| EACH
+ | ELEMENT
| ENABLE_P
| ENCODING
| ENCRYPTED
@@ -15772,6 +15809,23 @@ SplitColQualList(List *qualList,
*constraintList = qualList;
}
+/* Split a list of FKColElem structs into separate name and reftype lists */
+static void
+SplitFKColElems(List *fkcolelems, List **names, List **reftypes)
+{
+ ListCell *lc;
+
+ *names = NIL;
+ *reftypes = NIL;
+ foreach(lc, fkcolelems)
+ {
+ FKColElem *fkcolelem = (FKColElem *) lfirst(lc);
+
+ *names = lappend(*names, fkcolelem->name);
+ *reftypes = lappend_int(*reftypes, fkcolelem->reftype);
+ }
+}
+
/*
* Process result of ConstraintAttributeSpec, and set appropriate bool flags
* in the output command node. Pass NULL for any flags the particular
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index e95cee1ebf..fc320a988f 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1320,6 +1320,90 @@ select_common_type(ParseState *pstate, List *exprs, const char *context,
}
/*
+ * look at select_common_type(); in parse_coerce.c(src/backend/parser/parse_coerce.c)
+ *
+ * select_common_type_2args()
+ * Determine the common supertype of a list of input expressions.
+ * 'leftOid': left operand's type
+ * 'rightOid': left operand's type
+ */
+Oid select_common_type_2args(Oid leftOid, Oid rightOid)
+{
+ TYPCATEGORY leftcategory;
+ TYPCATEGORY rightcategory;
+ bool leftispreferred;
+ bool rightispreferred;
+
+ Assert(leftOid != InvalidOid);
+ Assert(rightOid != InvalidOid);
+
+ /*
+ * If input types are valid and exactly the same, just pick that type.
+ */
+ if (leftOid != UNKNOWNOID)
+ {
+ if (rightOid == leftOid)
+ return leftOid;
+ }
+
+ /*
+ * Nope, so set up for the full algorithm.
+ */
+ leftOid = getBaseType(leftOid);
+ rightOid = getBaseType(rightOid);
+ get_type_category_preferred(leftOid, &leftcategory, &leftispreferred);
+ get_type_category_preferred(rightOid, &rightcategory, &rightispreferred);
+
+ if (rightOid != UNKNOWNOID && rightOid != leftOid)
+ {
+ if (rightcategory != leftcategory)
+ {
+ /* both types in different categories? then not much hope... */
+ return InvalidOid;
+ }
+
+ if (can_coerce_type(1, &rightOid, &leftOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left */
+ if (!can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can not coerce to it implicitly right to left, thus coercion only works one way */
+ return leftOid;
+ }
+ else
+ {
+ /* coercion works both ways, then decide depending on preferred flag */
+ if (leftispreferred)
+ return leftOid;
+ else
+ rightOid;
+ }
+ }
+ else{
+ /* can not coerce to it implicitly right to left */
+ if (can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left, thus coercion only works one way */
+ return rightOid;
+ }
+ else{
+ /* can not coerce either way */
+ return InvalidOid;
+ }
+ }
+ }
+
+ /*
+ * If all the inputs were UNKNOWN type --- ie, unknown-type literals ---
+ * then resolve as type TEXT.
+ */
+ if (leftOid == UNKNOWNOID)
+ leftOid = TEXTOID;
+
+ return leftOid;
+}
+
+/*
* coerce_to_common_type()
* Coerce an expression to the given type.
*
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 20586797cc..4da64b9385 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -744,6 +744,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
* list of FK constraints to be processed later.
*/
constraint->fk_attrs = list_make1(makeString(column->colname));
+ /* grammar should have set fk_reftypes */
+ Assert(list_length(constraint->fk_reftypes) == 1);
cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
break;
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 34dadd6e19..e13e75fba7 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -4232,6 +4232,111 @@ arraycontained(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(result);
}
+/*
+ * array_contains_elem : checks an array for a spefific element
+ */
+static bool
+array_contains_elem(AnyArrayType *array, Datum elem,
+ Oid collation, void **fn_extra)
+{
+ Oid arr_type = AARR_ELEMTYPE(array);
+ TypeCacheEntry *typentry;
+ int nelems;
+ int typlen;
+ bool typbyval;
+ char typalign;
+ int i;
+ array_iter it1;
+ FunctionCallInfoData locfcinfo;
+
+ /*
+ * We arrange to look up the equality function only once per series of
+ * calls, assuming the element type doesn't change underneath us. The
+ * typcache is used so that we have no memory leakage when being used as
+ * an index support function.
+ */
+ typentry = (TypeCacheEntry *)*fn_extra;
+ if (typentry == NULL ||
+ typentry->type_id != arr_type)
+ {
+ typentry = lookup_type_cache(arr_type,
+ TYPECACHE_EQ_OPR_FINFO);
+ if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(arr_type))));
+ *fn_extra = (void *)typentry;
+ }
+ typlen = typentry->typlen;
+ typbyval = typentry->typbyval;
+ typalign = typentry->typalign;
+
+ /*
+ * Apply the comparison operator to each pair of array elements.
+ */
+ InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+ collation, NULL, NULL);
+
+ /* Loop over source data */
+ nelems = ArrayGetNItems(AARR_NDIM(array), AARR_DIMS(array));
+ array_iter_setup(&it1, array);
+
+ for (i = 0; i < nelems; i++)
+ {
+ Datum elt1;
+ bool isnull;
+ bool oprresult;
+
+ /* Get element, checking for NULL */
+ elt1 = array_iter_next(&it1, &isnull, i, typlen, typbyval, typalign);
+
+ /*
+ * We assume that the comparison operator is strict, so a NULL can't
+ * match anything. XXX this diverges from the "NULL=NULL" behavior of
+ * array_eq, should we act like that?
+ */
+ if (isnull)
+ continue;
+
+ /*
+ * Apply the operator to the element pair
+ */
+ locfcinfo.arg[0] = elt1;
+ locfcinfo.arg[1] = elem;
+ locfcinfo.argnull[0] = false;
+ locfcinfo.argnull[1] = false;
+ locfcinfo.isnull = false;
+ oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+ if (oprresult)
+ return true;
+ }
+
+ return false;
+}
+
+Datum
+arraycontainselem(PG_FUNCTION_ARGS)
+{
+ AnyArrayType *array = PG_GETARG_ANY_ARRAY(0);
+ Datum elem = PG_GETARG_DATUM(1);
+ Oid collation = PG_GET_COLLATION();
+ bool result;
+
+ /* we don't need to check if the elem is null or if
+ the elem datatype and array datatype match since this
+ is handled within internal calls already (a property
+ of polymorphic functions) */
+
+ result = array_contains_elem(array, elem, collation,
+ &fcinfo->flinfo->fn_extra);
+
+ /* Avoid leaking memory when handed toasted input. */
+ AARR_FREE_IF_COPY(array, 0);
+
+ PG_RETURN_BOOL(result);
+}
+
/*-----------------------------------------------------------------------------
* Array iteration functions
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index c2891e6fa1..9efebe48f6 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -118,9 +118,11 @@ typedef struct RI_ConstraintInfo
char confupdtype; /* foreign key's ON UPDATE action */
char confdeltype; /* foreign key's ON DELETE action */
char confmatchtype; /* foreign key's match type */
+ bool has_array; /* true if any reftype is EACH_ELEMENT */
int nkeys; /* number of key columns */
int16 pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */
int16 fk_attnums[RI_MAX_NUMKEYS]; /* attnums of referencing cols */
+ char fk_reftypes[RI_MAX_NUMKEYS]; /* reference semantics */
Oid pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = FK) */
Oid pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = PK) */
Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK = FK) */
@@ -204,7 +206,8 @@ static void ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype);
+ const char *rightop, Oid rightoptype,
+ char fkreftype);
static void ri_add_cast_to(StringInfo buf, Oid typid);
static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
static int ri_NullCheck(HeapTuple tup,
@@ -395,6 +398,7 @@ RI_FKey_check(TriggerData *trigdata)
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
StringInfoData querybuf;
+ StringInfoData countbuf;
char pkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
char paramname[16];
@@ -407,12 +411,22 @@ RI_FKey_check(TriggerData *trigdata)
* FOR KEY SHARE OF x
* The type id's for the $ parameters are those of the
* corresponding FK attributes.
+ *
+ * In case of an array ELEMENT foreign key, the previous query is used
+ * to count the number of matching rows and see if every combination
+ * is actually referenced.
+ * The wrapping query is
+ * SELECT 1 WHERE
+ * (SELECT count(DISTINCT y) FROM unnest($1) y)
+ * = (SELECT count(*) FROM (<QUERY>) z)
* ----------
*/
initStringInfo(&querybuf);
quoteRelationName(pkrelname, pk_rel);
appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
querysep = "WHERE";
+ initStringInfo(&countbuf);
+ appendStringInfo(&countbuf, "SELECT 1 WHERE ");
for (i = 0; i < riinfo->nkeys; i++)
{
Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
@@ -421,18 +435,41 @@ RI_FKey_check(TriggerData *trigdata)
quoteOneName(attname,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
sprintf(paramname, "$%d", i + 1);
+
+ /*
+ * In case of an array ELEMENT foreign key, we check that each
+ * distinct non-null value in the array is present in the PK
+ * table.
+ */
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&countbuf,
+ "(SELECT pg_catalog.count(DISTINCT y) FROM pg_catalog.unnest(%s) y)",
+ paramname);
+
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pf_eq_oprs[i],
- paramname, fk_type);
+ paramname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = fk_type;
}
appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
- /* Prepare and save the plan */
- qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
- &qkey, fk_rel, pk_rel, true);
+ if (riinfo->has_array)
+ {
+ appendStringInfo(&countbuf,
+ " OPERATOR(pg_catalog.=) (SELECT pg_catalog.count(*) FROM (%s) z)",
+ querybuf.data);
+
+ /* Prepare and save the plan for array ELEMENT foreign keys */
+ qplan = ri_PlanCheck(countbuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
+ }
+ else
+ /* Prepare and save the plan */
+ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
}
/*
@@ -559,7 +596,8 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pp_eq_oprs[i],
- paramname, pk_type);
+ paramname, pk_type,
+ FKCONSTR_REF_PLAIN);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -751,7 +789,8 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -974,7 +1013,8 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1130,7 +1170,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1309,7 +1350,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1475,7 +1517,8 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1651,7 +1694,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1817,7 +1861,8 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2008,7 +2053,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2327,6 +2373,14 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
* For MATCH FULL:
* (fk.keycol1 IS NOT NULL [OR ...])
*
+ * In case of an array ELEMENT column, relname is replaced with the
+ * following subquery:
+ *
+ * SELECT unnest("keycol1") k1, "keycol1" ak1 [, ...]
+ * FROM ONLY "public"."fk"
+ *
+ * where all the columns are renamed in order to prevent name collisions.
+ *
* We attach COLLATE clauses to the operators when comparing columns
* that have different collations.
*----------
@@ -2338,15 +2392,46 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
{
quoteOneName(fkattname,
RIAttName(fk_rel, riinfo->fk_attnums[i]));
- appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
+ if (riinfo->has_array)
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%sfk.ak%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.k%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
sep = ", ";
}
quoteRelationName(pkrelname, pk_rel);
quoteRelationName(fkrelname, fk_rel);
- appendStringInfo(&querybuf,
- " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
- fkrelname, pkrelname);
+
+ if (riinfo->has_array)
+ {
+ sep = "";
+ appendStringInfo(&querybuf,
+ " FROM (SELECT ");
+ for (i = 0; i < riinfo->nkeys; i++)
+ {
+ quoteOneName(fkattname,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%spg_catalog.unnest(%s) k%d, %s ak%d",
+ sep, fkattname, i + 1, fkattname, i + 1);
+ else
+ appendStringInfo(&querybuf, "%s%s k%d", sep, fkattname,
+ i + 1);
+ sep = ", ";
+ }
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s) fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
+ }
+ else
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
strcpy(pkattname, "pk.");
strcpy(fkattname, "fk.");
@@ -2360,12 +2445,16 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
quoteOneName(pkattname + 3,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
- quoteOneName(fkattname + 3,
- RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname + 3, "k%d", i + 1);
+ else
+ quoteOneName(fkattname + 3,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
ri_GenerateQual(&querybuf, sep,
pkattname, pk_type,
riinfo->pf_eq_oprs[i],
- fkattname, fk_type);
+ fkattname, fk_type,
+ FKCONSTR_REF_PLAIN);
if (pk_coll != fk_coll)
ri_GenerateQualCollation(&querybuf, pk_coll);
sep = "AND";
@@ -2381,7 +2470,10 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
sep = "";
for (i = 0; i < riinfo->nkeys; i++)
{
- quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname, "k%d", i + 1);
+ else
+ quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
appendStringInfo(&querybuf,
"%sfk.%s IS NOT NULL",
sep, fkattname);
@@ -2557,20 +2649,23 @@ quoteRelationName(char *buffer, Relation rel)
/*
* ri_GenerateQual --- generate a WHERE clause equating two variables
*
- * The idea is to append " sep leftop op rightop" to buf. The complexity
- * comes from needing to be sure that the parser will select the desired
- * operator. We always name the operator using OPERATOR(schema.op) syntax
- * (readability isn't a big priority here), so as to avoid search-path
- * uncertainties. We have to emit casts too, if either input isn't already
- * the input type of the operator; else we are at the mercy of the parser's
- * heuristics for ambiguous-operator resolution.
- */
+ * The idea is to append " sep leftop op rightop" to buf, or if fkreftype is
+ * FKCONSTR_REF_EACH_ELEMENT, append " sep rightop @>> leftop" to buf.
+ *
+ * The complexity comes from needing to be sure that the parser will select
+ * the desired operator. We always name the operator using
+ * OPERATOR(schema.op) syntax (readability isn't a big priority here), so as
+ * to avoid search-path uncertainties. We have to emit casts too, if either
+ * input isn't already the input type of the operator; else we are at the
+ * mercy of the parser's heuristics for ambiguous-operator resolution.
+ */
static void
ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype)
+ const char *rightop, Oid rightoptype,
+ char fkreftype)
{
HeapTuple opertup;
Form_pg_operator operform;
@@ -2586,15 +2681,77 @@ ri_GenerateQual(StringInfo buf,
nspname = get_namespace_name(operform->oprnamespace);
- appendStringInfo(buf, " %s %s", sep, leftop);
- if (leftoptype != operform->oprleft)
- ri_add_cast_to(buf, operform->oprleft);
- appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
- appendStringInfoString(buf, oprname);
- appendStringInfo(buf, ") %s", rightop);
- if (rightoptype != operform->oprright)
- ri_add_cast_to(buf, operform->oprright);
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ Oid oprright;
+ Oid oprleft;
+ Oid oprcommon;
+
+ /*
+ * since the @>> operator has the array as
+ * the first operand and the element as the second
+ * operand we switch their places in the query
+ * i.e opright is oprleft and vice versar
+ */
+
+ /* get array type of refrenced element*/
+ oprright = get_array_type(operform->oprleft);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprleft))));
+
+ /* get array type of refrencing element*/
+ oprleft = get_array_type(operform->oprright);
+ if (!OidIsValid(oprleft))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprright))));
+
+ /* compare the two array types and try to find
+ a common supertype */
+ oprcommon = select_common_type_2args(oprleft, oprright);
+ if (!OidIsValid(oprcommon))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find common type between %s and %s",
+ format_type_be(oprleft), format_type_be(oprright))));
+
+ oprright = get_base_element_type(oprcommon);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find element type for data type %s",
+ format_type_be(oprcommon))));
+
+ appendStringInfo(buf, " %s %s", sep, rightop);
+ if (oprleft != oprcommon)
+ ri_add_cast_to(buf, oprcommon);
+ appendStringInfo(buf, " OPERATOR(pg_catalog.@>>) ");
+
+ appendStringInfoString(buf, leftop);
+ if (operform->oprleft != oprright)
+ ri_add_cast_to(buf, oprright);
+ }
+ else
+ {
+ appendStringInfo(buf, " %s %s", sep, leftop);
+
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+
+ appendStringInfo(buf, " OPERATOR(%s.%s) ",
+ quote_identifier(nspname), oprname);
+
+ appendStringInfoString(buf, rightop);
+
+ if (rightoptype != operform->oprright)
+ ri_add_cast_to(buf, operform->oprright);
+ }
+
ReleaseSysCache(opertup);
}
@@ -2801,6 +2958,7 @@ ri_LoadConstraintInfo(Oid constraintOid)
bool isNull;
ArrayType *arr;
int numkeys;
+ int i;
/*
* On the first call initialize the hashtable
@@ -2879,6 +3037,20 @@ ri_LoadConstraintInfo(Oid constraintOid)
pfree(arr); /* free de-toasted copy, if any */
adatum = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype, &isNull);
+ if (isNull)
+ elog(ERROR, "null confreftype for constraint %u", constraintOid);
+ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
+ if (ARR_NDIM(arr) != 1 ||
+ ARR_DIMS(arr)[0] != numkeys ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != CHAROID)
+ elog(ERROR, "confreftype is not a 1-D char array");
+ memcpy(riinfo->fk_reftypes, ARR_DATA_PTR(arr), numkeys * sizeof(char));
+ if ((Pointer) arr != DatumGetPointer(adatum))
+ pfree(arr); /* free de-toasted copy, if any */
+
+ adatum = SysCacheGetAttr(CONSTROID, tup,
Anum_pg_constraint_conpfeqop, &isNull);
if (isNull)
elog(ERROR, "null conpfeqop for constraint %u", constraintOid);
@@ -2921,6 +3093,24 @@ ri_LoadConstraintInfo(Oid constraintOid)
if ((Pointer) arr != DatumGetPointer(adatum))
pfree(arr); /* free de-toasted copy, if any */
+ /*
+ * Fix up some stuff for array foreign keys. We need a has_array flag
+ * indicating whether there's an array foreign key, and we want to set
+ * ff_eq_oprs[i] to array_eq() for array columns, because that's what
+ * makes sense for ri_KeysEqual, and we have no other use for ff_eq_oprs
+ * in this module. (If we did, substituting the array comparator at the
+ * call point in ri_KeysEqual might be more appropriate.)
+ */
+ riinfo->has_array = false;
+ for (i = 0; i < numkeys; i++)
+ {
+ if (riinfo->fk_reftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ riinfo->has_array = true;
+ riinfo->ff_eq_oprs[i] = ARRAY_EQ_OP;
+ }
+ }
+
ReleaseSysCache(tup);
/*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 43646d2c4f..4662cf5d56 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -314,6 +314,9 @@ static char *pg_get_viewdef_worker(Oid viewoid,
static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
static void decompile_column_index_array(Datum column_index_array, Oid relId,
StringInfo buf);
+static void decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf);
static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
const Oid *excludeOps,
@@ -1882,7 +1885,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
{
case CONSTRAINT_FOREIGN:
{
- Datum val;
+ Datum colindexes;
+ Datum reftypes;
bool isnull;
const char *string;
@@ -1890,13 +1894,21 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
appendStringInfoString(&buf, "FOREIGN KEY (");
/* Fetch and build referencing-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_conkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_conkey,
+ &isnull);
if (isnull)
elog(ERROR, "null conkey for constraint %u",
constraintId);
+ reftypes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype,
+ &isnull);
+ if (isnull)
+ elog(ERROR, "null confreftype for constraint %u",
+ constraintId);
- decompile_column_index_array(val, conForm->conrelid, &buf);
+ decompile_fk_column_index_array(colindexes, reftypes,
+ conForm->conrelid, &buf);
/* add foreign relation name */
appendStringInfo(&buf, ") REFERENCES %s(",
@@ -1904,13 +1916,15 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
NIL));
/* Fetch and build referenced-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_confkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confkey,
+ &isnull);
if (isnull)
elog(ERROR, "null confkey for constraint %u",
constraintId);
- decompile_column_index_array(val, conForm->confrelid, &buf);
+ decompile_column_index_array(colindexes,
+ conForm->confrelid, &buf);
appendStringInfoChar(&buf, ')');
@@ -2185,6 +2199,66 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
}
}
+ /*
+ * Convert an int16[] Datum and a char[] Datum into a comma-separated
+ * list of column names for the indicated relation, prefixed by appropriate
+ * keywords depending on the foreign key reference semantics indicated by
+ * the char[] entries. Append the text to buf.
+ */
+ static void
+ decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf)
+ {
+ Datum *keys;
+ int nKeys;
+ Datum *reftypes;
+ int nReftypes;
+ int j;
+
+ /* Extract data from array of int16 */
+ deconstruct_array(DatumGetArrayTypeP(column_index_array),
+ INT2OID, sizeof(int16), true, 's',
+ &keys, NULL, &nKeys);
+
+ /* Extract data from array of char */
+ deconstruct_array(DatumGetArrayTypeP(fk_reftype_array),
+ CHAROID, sizeof(char), true, 'c',
+ &reftypes, NULL, &nReftypes);
+
+ if (nKeys != nReftypes)
+ elog(ERROR, "wrong confreftype cardinality");
+
+ for (j = 0; j < nKeys; j++)
+ {
+ char *colName;
+ const char *prefix;
+
+ colName = get_relid_attribute_name(relId, DatumGetInt16(keys[j]));
+
+ switch (DatumGetChar(reftypes[j]))
+ {
+ case FKCONSTR_REF_PLAIN:
+ prefix = "";
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ prefix = "EACH ELEMENT OF ";
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d",
+ (int) DatumGetChar(reftypes[j]));
+ prefix = NULL; /* keep compiler quiet */
+ break;
+ }
+
+ if (j == 0)
+ appendStringInfo(buf, "%s%s", prefix,
+ quote_identifier(colName));
+ else
+ appendStringInfo(buf, ", %s%s", prefix,
+ quote_identifier(colName));
+ }
+}
/* ----------
* get_expr - Decompile an expression tree
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index f850be490a..55a43d0b90 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -689,7 +689,8 @@ DATA(insert ( 2745 2277 2277 1 s 2750 2742 0 ));
DATA(insert ( 2745 2277 2277 2 s 2751 2742 0 ));
DATA(insert ( 2745 2277 2277 3 s 2752 2742 0 ));
DATA(insert ( 2745 2277 2277 4 s 1070 2742 0 ));
-
+DATA(insert ( 2745 2277 2283 5 s 6108 2742 0 )); /* anyarray @>> anyelem */
+
/*
* btree enum_ops
*/
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index ec035d8434..bea6f82404 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -104,8 +104,16 @@ CATALOG(pg_constraint,2606)
int16 confkey[1];
/*
+ * If a foreign key, the reference semantics for each column
+ */
+ char confreftype[1];
+
+ /*
* If a foreign key, the OIDs of the PK = FK equality operators for each
* column of the constraint
+ *
+ * Note: for array foreign keys, all these operators are for the array's
+ * element type.
*/
Oid conpfeqop[1];
@@ -150,7 +158,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* compiler constants for pg_constraint
* ----------------
*/
-#define Natts_pg_constraint 24
+#define Natts_pg_constraint 25
#define Anum_pg_constraint_conname 1
#define Anum_pg_constraint_connamespace 2
#define Anum_pg_constraint_contype 3
@@ -169,12 +177,13 @@ typedef FormData_pg_constraint *Form_pg_constraint;
#define Anum_pg_constraint_connoinherit 16
#define Anum_pg_constraint_conkey 17
#define Anum_pg_constraint_confkey 18
-#define Anum_pg_constraint_conpfeqop 19
-#define Anum_pg_constraint_conppeqop 20
-#define Anum_pg_constraint_conffeqop 21
-#define Anum_pg_constraint_conexclop 22
-#define Anum_pg_constraint_conbin 23
-#define Anum_pg_constraint_consrc 24
+#define Anum_pg_constraint_confreftype 19
+#define Anum_pg_constraint_conpfeqop 20
+#define Anum_pg_constraint_conppeqop 21
+#define Anum_pg_constraint_conffeqop 22
+#define Anum_pg_constraint_conexclop 23
+#define Anum_pg_constraint_conbin 24
+#define Anum_pg_constraint_consrc 25
/* ----------------
* initial contents of pg_constraint
@@ -195,7 +204,9 @@ typedef FormData_pg_constraint *Form_pg_constraint;
/*
* Valid values for confupdtype and confdeltype are the FKCONSTR_ACTION_xxx
* constants defined in parsenodes.h. Valid values for confmatchtype are
- * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h.
+ * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h. Valid values
+ * for elements of confreftype[] are the FKCONSTR_REF_xxx constants defined
+ * in parsenodes.h.
*/
#endif /* PG_CONSTRAINT_H */
diff --git a/src/include/catalog/pg_constraint_fn.h b/src/include/catalog/pg_constraint_fn.h
index a4c46897ed..06f4313bae 100644
--- a/src/include/catalog/pg_constraint_fn.h
+++ b/src/include/catalog/pg_constraint_fn.h
@@ -40,6 +40,7 @@ extern Oid CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index ffabc2003b..8a9d616d29 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1570,6 +1570,8 @@ DESCR("contains");
DATA(insert OID = 2752 ( "<@" PGNSP PGUID b f f 2277 2277 16 2751 0 arraycontained arraycontsel arraycontjoinsel ));
DESCR("is contained by");
#define OID_ARRAY_CONTAINED_OP 2752
+DATA(insert OID = 6108 ( "@>>" PGNSP PGUID b f f 2277 2283 16 0 0 arraycontainselem arraycontsel arraycontjoinsel ));
+DESCR("containselem");
/* capturing operators to preserve pre-8.3 behavior of text concatenation */
DATA(insert OID = 2779 ( "||" PGNSP PGUID b f f 25 2776 25 0 0 textanycat - - ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 8b33b4e0ea..01b207d363 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4308,6 +4308,7 @@ DESCR("GIN array support (obsolete)");
DATA(insert OID = 2747 ( arrayoverlap PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arrayoverlap _null_ _null_ _null_ ));
DATA(insert OID = 2748 ( arraycontains PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arraycontains _null_ _null_ _null_ ));
DATA(insert OID = 2749 ( arraycontained PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arraycontained _null_ _null_ _null_ ));
+DATA(insert OID = 6109 ( arraycontainselem PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2283" _null_ _null_ _null_ _null_ _null_ arraycontainselem _null_ _null_ _null_ ));
/* BRIN minmax */
DATA(insert OID = 3383 ( brin_minmax_opcinfo PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_opcinfo _null_ _null_ _null_ ));
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 5f2a4a75da..7662ef8018 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2067,6 +2067,10 @@ typedef enum ConstrType /* types of constraints */
#define FKCONSTR_MATCH_PARTIAL 'p'
#define FKCONSTR_MATCH_SIMPLE 's'
+ /* Foreign key column reference semantics codes */
+ #define FKCONSTR_REF_PLAIN 'p'
+ #define FKCONSTR_REF_EACH_ELEMENT 'e'
+
typedef struct Constraint
{
NodeTag type;
@@ -2102,6 +2106,7 @@ typedef struct Constraint
RangeVar *pktable; /* Primary key table */
List *fk_attrs; /* Attributes of foreign key */
List *pk_attrs; /* Corresponding attrs in PK table */
+ List *fk_reftypes; /* Per-column reference semantics (int List) */
char fk_matchtype; /* FULL, PARTIAL, SIMPLE */
char fk_upd_action; /* ON UPDATE action */
char fk_del_action; /* ON DELETE action */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f50e45e886..d3f4803006 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -140,6 +140,7 @@ PG_KEYWORD("domain", DOMAIN_P, UNRESERVED_KEYWORD)
PG_KEYWORD("double", DOUBLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD)
PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD)
+PG_KEYWORD("element", ELEMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("else", ELSE, RESERVED_KEYWORD)
PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index 06f65293cb..f5bad57b70 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -68,6 +68,7 @@ extern int parser_coercion_errposition(ParseState *pstate,
extern Oid select_common_type(ParseState *pstate, List *exprs,
const char *context, Node **which_expr);
+extern Oid select_common_type_2args(Oid ptype, Oid ntype);
extern Node *coerce_to_common_type(ParseState *pstate, Node *node,
Oid targetTypeId,
const char *context);
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index c730563f03..0a9eff4d04 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -737,6 +737,17 @@ SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(6 rows)
+SELECT * FROM array_op_test WHERE i @>> 32 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+ 98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
+ 100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
+(6 rows)
+
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -761,6 +772,19 @@ SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
(8 rows)
+SELECT * FROM array_op_test WHERE i @>> 17 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
+ 53 | {38,17} | {AAAAAAAAAAA21658}
+ 65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+(8 rows)
+
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -942,6 +966,11 @@ SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
-------+---+---
(0 rows)
+SELECT * FROM array_op_test WHERE i @>> NULL ORDER BY seqno;
+ seqno | i | t
+-------+---+---
+(0 rows)
+
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
seqno | i | t
-------+---+---
@@ -953,6 +982,15 @@ SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
101 | {} | {}
(1 row)
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAA72908' ORDER BY seqno;
+ seqno | i | t
+-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
+ 22 | {11,6,56,62,53,30} | {AAAAAAAA72908}
+ 45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}
+ 72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+(4 rows)
+
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
seqno | i | t
-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
@@ -971,6 +1009,14 @@ SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
(4 rows)
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAAAA646' ORDER BY seqno;
+ seqno | i | t
+-------+------------------+--------------------------------------------------------------------
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+ 96 | {23,97,43} | {AAAAAAAAAA646,A87088}
+(3 rows)
+
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
seqno | i | t
-------+------------------+--------------------------------------------------------------------
diff --git a/src/test/regress/expected/element_foreign_key.out b/src/test/regress/expected/element_foreign_key.out
new file mode 100644
index 0000000000..c0ebed4bb4
--- /dev/null
+++ b/src/test/regress/expected/element_foreign_key.out
@@ -0,0 +1,599 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fkarray"
+DETAIL: Key (ftest1)=({10,1}) is not present in table "pktableforarray".
+DROP TABLE FKTABLEFORARRAY;
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4,6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL,4,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+ERROR: insert or update on table "fktableforarraymdim" violates foreign key constraint "fktableforarraymdim_ftest1_fkey"
+DETAIL: Key (ftest1)=({{1,2},{6,NULL}}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+ERROR: null value in column "ftest1" violates not-null constraint
+DETAIL: Failing row contains (null, 21).
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+ {1} | 4
+(11 rows)
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({D}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({A,B,D}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(A) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(B) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ERROR: foreign key constraint "fktableforarray_ftest1_fkey" cannot be implemented
+DETAIL: Key column "ftest1" has element type integer which does not have a default btree operator class that's compatible with class "float8_ops".
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(A, {A,B,C}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(B, {A}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Test ELEMENT foreign keys with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,99)"}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,1)","(2010,1)"}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2010,99)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2011,1)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({0,1}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({2,1}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(2, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(20, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+ERROR: foreign keys support only one array column
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: foreign keys support only one array column
+DROP TABLE F1;
+-- Cleanup
+DROP TABLE DIM1;
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "x2_x1_fkey"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "fk_const"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+DROP TABLE x1;
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{100,100,100},{NULL,NULL,20},{7,8,10}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index fcf8bd7565..25ef369951 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1811,6 +1811,7 @@ ORDER BY 1, 2, 3;
2742 | 2 | @@@
2742 | 3 | <@
2742 | 4 | =
+ 2742 | 5 | @>>
2742 | 7 | @>
2742 | 9 | ?
2742 | 10 | ?|
@@ -1878,7 +1879,7 @@ ORDER BY 1, 2, 3;
4000 | 25 | <<=
4000 | 26 | >>
4000 | 27 | >>=
-(121 rows)
+(122 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index eefdeeacae..825c4894ce 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -103,7 +103,7 @@ test: publication subscription
# ----------
# Another group of parallel tests
# ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
+test: select_views portals_p2 foreign_key element_foreign_key element_foreign_key_performance cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
# ----------
# Another group of parallel tests
# NB: temp.sql does a reconnect which transiently uses 2 connections,
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 76b0de30a7..090db8eef1 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -140,6 +140,8 @@ test: amutils
test: select_views
test: portals_p2
test: foreign_key
+test: element_foreign_key
+test: element_foreign_key_performance
test: cluster
test: dependency
test: guc
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 25dd4e2c6d..f6fb507bdd 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -313,8 +313,10 @@ SELECT ARRAY[0,0] || ARRAY[1,1] || ARRAY[2,2] AS "{0,0,1,1,2,2}";
SELECT 0 || ARRAY[1,2] || 3 AS "{0,1,2,3}";
SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> 32 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> 17 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{32,17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32,17}' ORDER BY seqno;
@@ -325,11 +327,14 @@ SELECT * FROM array_op_test WHERE i && '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> NULL ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAA72908' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAAAA646' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno;
diff --git a/src/test/regress/sql/element_foreign_key.sql b/src/test/regress/sql/element_foreign_key.sql
new file mode 100644
index 0000000000..31f5b81e4c
--- /dev/null
+++ b/src/test/regress/sql/element_foreign_key.sql
@@ -0,0 +1,505 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Test ELEMENT foreign keys with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+
+
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+
+
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+-- Cleanup
+DROP TABLE DIM1;
+
+
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+DROP TABLE x2;
+DROP TABLE x1;
+
+
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+DROP TABLE F1;
+
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+DROP TABLE F1;
+
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
+
+-- EACH-ELEMENT FK PERFORMANCE
+-- Define PKTABLEFORARRAYDELETE
+CREATE TABLE PKTABLEFORARRAYDELETE ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAYDELETE
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (5, 'Test5');
+
+-- Define FKTABLEFORARRAYDELETE
+CREATE TABLE FKTABLEFORARRAYDELETE ( ftest1 int[],
+ ftest2 int,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYDELETE
+ ON DELETE CASCADE
+ ON UPDATE NO ACTION
+ );
+
+-- Create index on FKTABLEFORARRAYDELETE
+CREATE INDEX ON FKTABLEFORARRAYDELETE USING gin (ftest1);
+
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,5,3}', 10);
+
+SELECT * FROM FKTABLEFORARRAYDELETE;
+SELECT * FROM FKTABLEFORARRAYDELETE WHERE ftest1 @>> 5;
+
+EXPLAIN ANALYZE DELETE FROM PKTABLEFORARRAYDELETE WHERE ptest1 = 5;
+
+SELECT * FROM FKTABLEFORARRAYDELETE;
\ No newline at end of file
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-08-23 17:01 ` Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-08-23 17:01 UTC (permalink / raw)
To: Alexander Korotkov <aekorotkov@gmail.com>; Stephen Frost <sfrost@snowman.net>; David Steele <david@pgmasters.net>; Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; pgsql-hackers-owner@postgresql.org, Tom Lane <tgl@sss.pgh.pa.us>; Hans-Jürgen Schönig <hs@cybertec.at>
GSoC 2017 has come to a close. These three months have proved
extremely beneficial, it was my first interaction with an open source
community and hopefully not my last.
In short, this patch allows each element in an array to act as a foreign
key, with the following syntax:
CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
CREATE TABLE FKTABLEFORARRAY ( ftest1 int, FOREIGN KEY (EACH ELEMENT OF
ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
The initial patch was written by Marco Nenciarini, Gabriele Bartolini and
Gianni Ciolli, and modified by Tom Lan.[2]
The GSoC proposal was to overcome the performance issues that appear
whenever an UPDATE/DELETE was performed on the PK table; this fires a
Referential Integrity check on the FK table using sequential scan, which is
responsible for the performance issues.
We planned on replacing the sequential scan with an indexed scan under GIN
and to do that we introduced a new operator anyarray @>> anyelem that
returns true if the element is present in the array.
I'm proud to say that we have realised our initial goal, overcoming the
performance issues produced on RI checks for Foreign Key Arrays. Outlined
here [1].
The benchmarking test showed exactly how much rewarding the use of the GIN
index has proven to be.[3]
Having accomplished the initial goals, I compiled a comprehensive
limitation check list and started to work on each limitation.
Here's a summary of Foreign Key Arrays limitations:
The limitations of the patch:
- Supported actions:
✔ UPDATE/DELETE NO ACTION
✔ UPDATE/DELETE RESTRICT
✔ DELETE CASCADE
✗ UPDATE CASCADE
✗ UPDATE/DELETE SET NULL
✗ UPDATE/DELETE SET DEFAULT
✗ Only one "ELEMENT" column allowed in a multi-column key
✗ undesirable dependency on default opclass semantics in the patch, which
is that it supposes it can use array_eq() to detect whether or not the
referencing column has changed. But I think that can be fixed without
undue pain by providing a refactored version of array_eq() that can be told
which element-comparison function to use
-- Attempted limitations
✗ presupposes that count(distinct y) has exactly the same notion of
equality that the PK unique index has. In reality, count(distinct) will
fall back to the default btree opclass for the array element type.
-- Resolved limitations
✔ fatal performance issues. If you issue any UPDATE or DELETE against the
PK table, you get a query like this for checking to see if the RI
constraint would be violated:
SELECT 1 FROM ONLY fktable x WHERE $1 = ANY (fkcol) FOR SHARE OF x;
Changed into SELECT 1 FROM ONLY fktable x WHERE $1 @> fkcol FOR SHARE OF x;
✔ coercion is now supported.
CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF
ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
✔ supported DELTE CASCADE action
--
------------------------------------------------------------------------------------------------------------------------
The final patch v5 is attached here.
I also attached a diff file to highlight my changes to the old rebased
patch v3
Thank you, everyone, the Postgres community for your support.
Best Regards,
Mark Rofail
--
------------------------------------------------------------------------------------------------------------------------
[1]
https://www.postgresql.org/message-id/CAJvoCuv=EeXMs7My-8AKFf1WmvXO+M_ngUEP9B=7Xaxr4EqFeg@mail.gmail...
[2]
https://www.postgresql.org/message-id/1343842863.5162.4.camel%40greygoo.devise-it.lan
[3]
https://www.postgresql.org/message-id/CAJvoCusMuLnYZUbwTBKt%2Bp6bB9GwiTqF95OsQFHXixJj3LkxVQ%40mail.g...
diff --git a/home/mark/Dev/GSoC 2017/New Patches/Array-ELEMENT-foreign-key-v3-REBASED-42794d6.patch b/home/mark/Dev/GSoC 2017/Github repo/postgresql/Array-ELEMENT-foreign-key-v4.5.patch
index b125541d09..248a4c4623 100644
--- a/home/mark/Dev/GSoC 2017/New Patches/Array-ELEMENT-foreign-key-v3-REBASED-42794d6.patch
+++ b/home/mark/Dev/GSoC 2017/Github repo/postgresql/Array-ELEMENT-foreign-key-v4.5.patch
@@ -1,8 +1,8 @@
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
ref/create_table.sgml
@@ -199,13 +199,29 @@ index b15c19d3d0..c3fe34888e 100644
+
+ It is advisable to index the refrencing column using GIN index as it considerably enhances the performance.
+ Also concerning coercion while using the GIN index:
+
+ <programlisting>
+ CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+ CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ </programlisting>
+ This syntax is fine since this will cast ptest1 to int4 upon RI checks, however,
+
+ <programlisting>
+ CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+ CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ </programlisting>
+ This syntax will cast ftest1 to int4 upon RI checks, thus defeating the purpose of the index.
+
diff --git a/src/backend/access/gin/ginarrayproc.c b/src/backend/access/gin/ginarrayproc.c
index a5238c3af5..2587cf9ccf 100644
--- a/src/backend/access/gin/ginarrayproc.c
+++ b/src/backend/access/gin/ginarrayproc.c
@@ -24,6 +24,7 @@
#define GinContainsStrategy 2
#define GinContainedStrategy 3
#define GinEqualStrategy 4
+#define GinContainsElemStrategy 5
@@ -79,7 +80,7 @@ Datum
ginqueryarrayextract(PG_FUNCTION_ARGS)
{
/* Make copy of array input to ensure it doesn't disappear while in use */
- ArrayType *array = PG_GETARG_ARRAYTYPE_P_COPY(0);
+ ArrayType *array;
int32 *nkeys = (int32 *) PG_GETARG_POINTER(1);
StrategyNumber strategy = PG_GETARG_UINT16(2);
@@ -94,19 +95,35 @@ ginqueryarrayextract(PG_FUNCTION_ARGS)
bool *nulls;
int nelems;
- get_typlenbyvalalign(ARR_ELEMTYPE(array),
- &elmlen, &elmbyval, &elmalign);
+ if (strategy != GinContainsElemStrategy){
+ array = PG_GETARG_ARRAYTYPE_P_COPY(0);
- deconstruct_array(array,
- ARR_ELEMTYPE(array),
- elmlen, elmbyval, elmalign,
- &elems, &nulls, &nelems);
+ get_typlenbyvalalign(ARR_ELEMTYPE(array),
+ &elmlen, &elmbyval, &elmalign);
- *nkeys = nelems;
- *nullFlags = nulls;
+ deconstruct_array(array,
+ ARR_ELEMTYPE(array),
+ elmlen, elmbyval, elmalign,
+ &elems, &nulls, &nelems);
- switch (strategy)
- {
+ *nkeys = nelems;
+ *nullFlags = nulls;
+ }
+ else{
+ /*
+ * since this function return a pointer to a
+ * deconstructed array, there is nothing to do if
+ * operand is a single element except to return
+ * as is and configure the searchmode
+ */
+
+ elems = PG_GETARG_POINTER(0);
+ *nkeys = 1;
+ *nullFlags = PG_ARGISNULL(0);
+ }
+
+ switch (strategy)
+ {
case GinOverlapStrategy:
*searchMode = GIN_SEARCH_MODE_DEFAULT;
break;
@@ -126,6 +143,14 @@ ginqueryarrayextract(PG_FUNCTION_ARGS)
else
*searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
break;
+ case GinContainsElemStrategy:
+ /*
+ * only items that match the queried element
+ * are considered candidate
+ */
+
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+ break;
default:
elog(ERROR, "ginqueryarrayextract: unknown strategy number: %d",
strategy);
@@ -171,6 +196,7 @@ ginarrayconsistent(PG_FUNCTION_ARGS)
}
}
break;
+ case GinContainsElemStrategy:
case GinContainsStrategy:
/* result is not lossy */
*recheck = false;
@@ -258,7 +284,8 @@ ginarraytriconsistent(PG_FUNCTION_ARGS)
}
}
break;
- case GinContainsStrategy:
+ case GinContainsElemStrategy:
+ case GinContainsStrategy:
/* must have all elements in check[] true, and no nulls */
res = GIN_TRUE;
for (i = 0; i < nkeys; i++)
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 7ceea7a741..ca8390a147 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -178,7 +178,22 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
if (i < nUserQueryValues)
{
/* set up normal entry using extractQueryFn's outputs */
- queryKey = queryValues[i];
+
+ if (strategy == 5 && nQueryValues == 1)
+ {
+ /*
+ * strategy 5 is GinContainsElemStrategy.
+ * we are sure the strategy is GinContainsElemStrategy,
+ * so take the datum directly without accessing an array
+ * since it's an element not an array
+ */
+ queryKey = queryValues;
+ }
+ else
+ {
+ queryKey = queryValues[i];
+ }
+
queryCategory = queryCategories[i];
isPartialMatch =
(ginstate->canPartialMatch[attnum - 1] && partial_matches)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -458,89 +602,90 @@ index bb00858ad1..dc18fd1eae 100644
i++;
}
- /* Array foreign keys support only NO ACTION and RESTRICT actions */
+ /* Array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions */
if (has_array)
{
if ((fkconstraint->fk_upd_action != FKCONSTR_ACTION_NOACTION &&
fkconstraint->fk_upd_action != FKCONSTR_ACTION_RESTRICT) ||
(fkconstraint->fk_del_action != FKCONSTR_ACTION_NOACTION &&
- fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT))
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_CASCADE))
ereport(ERROR,
(errcode(ERRCODE_INVALID_FOREIGN_KEY),
- errmsg("array foreign keys support only NO ACTION and RESTRICT actions")));
+ errmsg("array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions")));
}
- /*
+ /*
* If the attribute list for the referenced table was omitted, lookup the
* definition of the primary key and use it. Otherwise, validate the
* supplied attribute list. In either case, discover the index OID and
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index e95cee1ebf..cf273e7c03 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1320,6 +1320,90 @@ select_common_type(ParseState *pstate, List *exprs, const char *context,
}
/*
+ * look at select_common_type(); in parse_coerce.c(src/backend/parser/parse_coerce.c)
+ *
+ * select_common_type_2args()
+ * Determine the common supertype of a list of input expressions.
+ * 'leftOid': left operand's type
+ * 'rightOid': left operand's type
+ */
+Oid select_common_type_2args(Oid leftOid, Oid rightOid)
+{
+ TYPCATEGORY leftcategory;
+ TYPCATEGORY rightcategory;
+ bool leftispreferred;
+ bool rightispreferred;
+
+ Assert(leftOid != InvalidOid);
+ Assert(rightOid != InvalidOid);
+
+ /*
+ * If input types are valid and exactly the same, just pick that type.
+ */
+ if (leftOid != UNKNOWNOID)
+ {
+ if (rightOid == leftOid)
+ return leftOid;
+ }
+
+ /*
+ * Nope, so set up for the full algorithm.
+ */
+ leftOid = getBaseType(leftOid);
+ rightOid = getBaseType(rightOid);
+ get_type_category_preferred(leftOid, &leftcategory, &leftispreferred);
+ get_type_category_preferred(rightOid, &rightcategory, &rightispreferred);
+
+ if (rightOid != UNKNOWNOID && rightOid != leftOid)
+ {
+ if (rightcategory != leftcategory)
+ {
+ /* both types in different categories? then not much hope... */
+ return InvalidOid;
+ }
+
+ if (can_coerce_type(1, &rightOid, &leftOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left */
+ if (!can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can not coerce to it implicitly right to left, thus coercion only works one way */
+ return leftOid;
+ }
+ else
+ {
+ /* coercion works both ways, then decide depending on preferred flag */
+ if (leftispreferred)
+ return leftOid;
+ else
+ rightOid;
+ }
+ }
+ else{
+ /* can not coerce to it implicitly right to left */
+ if (can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left, thus coercion only works one way */
+ return rightOid;
+ }
+ else{
+ /* can not coerce either way */
+ return InvalidOid;
+ }
+ }
+ }
+
+ /*
+ * If all the inputs were UNKNOWN type --- ie, unknown-type literals ---
+ * then resolve as type TEXT.
+ */
+ if (leftOid == UNKNOWNOID)
+ leftOid = TEXTOID;
+
+ return leftOid;
+}
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 34dadd6e19..b2db8baffd 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -4232,6 +4232,112 @@ arraycontained(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(result);
}
+/*
+ * array_contains_elem : checks an array for a spefific element
+ * adapted from array_contain_compare() for containment of a single element
+ */
+static bool
+array_contains_elem(AnyArrayType *array, Datum elem,
+ Oid collation, void **fn_extra)
+{
+ Oid arr_type = AARR_ELEMTYPE(array);
+ TypeCacheEntry *typentry;
+ int nelems;
+ int typlen;
+ bool typbyval;
+ char typalign;
+ int i;
+ array_iter it1;
+ FunctionCallInfoData locfcinfo;
+
+ /*
+ * We arrange to look up the equality function only once per series of
+ * calls, assuming the element type doesn't change underneath us. The
+ * typcache is used so that we have no memory leakage when being used as
+ * an index support function.
+ */
+ typentry = (TypeCacheEntry *)*fn_extra;
+ if (typentry == NULL ||
+ typentry->type_id != arr_type)
+ {
+ typentry = lookup_type_cache(arr_type,
+ TYPECACHE_EQ_OPR_FINFO);
+ if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(arr_type))));
+ *fn_extra = (void *)typentry;
+ }
+ typlen = typentry->typlen;
+ typbyval = typentry->typbyval;
+ typalign = typentry->typalign;
+
+ /*
+ * Apply the comparison operator to each pair of array elements.
+ */
+ InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+ collation, NULL, NULL);
+
+ /* Loop over source data */
+ nelems = ArrayGetNItems(AARR_NDIM(array), AARR_DIMS(array));
+ array_iter_setup(&it1, array);
+
+ for (i = 0; i < nelems; i++)
+ {
+ Datum elt1;
+ bool isnull;
+ bool oprresult;
+
+ /* Get element, checking for NULL */
+ elt1 = array_iter_next(&it1, &isnull, i, typlen, typbyval, typalign);
+
+ /*
+ * We assume that the comparison operator is strict, so a NULL can't
+ * match anything. XXX this diverges from the "NULL=NULL" behavior of
+ * array_eq, should we act like that?
+ */
+ if (isnull)
+ continue;
+
+ /*
+ * Apply the operator to the element pair
+ */
+ locfcinfo.arg[0] = elt1;
+ locfcinfo.arg[1] = elem;
+ locfcinfo.argnull[0] = false;
+ locfcinfo.argnull[1] = false;
+ locfcinfo.isnull = false;
+ oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+ if (oprresult)
+ return true;
+ }
+
+ return false;
+}
+
+Datum
+arraycontainselem(PG_FUNCTION_ARGS)
+{
+ AnyArrayType *array = PG_GETARG_ANY_ARRAY(0);
+ Datum elem = PG_GETARG_DATUM(1);
+ Oid collation = PG_GET_COLLATION();
+ bool result;
+
+ /* we don't need to check if the elem is null or if
+ the elem datatype and array datatype match since this
+ is handled within internal calls already (a property
+ of polymorphic functions) */
+
+ result = array_contains_elem(array, elem, collation,
+ &fcinfo->flinfo->fn_extra);
+
+ /* Avoid leaking memory when handed toasted input. */
+ AARR_FREE_IF_COPY(array, 0);
+
+ PG_RETURN_BOOL(result);
+}
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
-@@ -2586,14 +2682,32 @@ ri_GenerateQual(StringInfo buf,
+@@ -2586,14 +2681,81 @@ ri_GenerateQual(StringInfo buf,
nspname = get_namespace_name(operform->oprnamespace);
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- {
- oprright = get_array_type(operform->oprright);
- if (!OidIsValid(oprright))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("could not find array type for data type %s",
- format_type_be(operform->oprright))));
- }
- else
- oprright = operform->oprright;
-
- appendStringInfo(buf, " %s %s", sep, leftop);
- if (leftoptype != operform->oprleft)
- ri_add_cast_to(buf, operform->oprleft);
+ appendStringInfo(buf, " %s %s", sep, leftop);
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
appendStringInfoString(buf, oprname);
appendStringInfo(buf, ") %s", rightop);
if (rightoptype != operform->oprright)
ri_add_cast_to(buf, operform->oprright);
-
- appendStringInfo(buf, " OPERATOR(%s.%s) ",
- quote_identifier(nspname), oprname);
-
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- appendStringInfoString(buf, "ANY (");
- appendStringInfoString(buf, rightop);
- if (rightoptype != oprright)
- ri_add_cast_to(buf, oprright);
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- appendStringInfoChar(buf, ')');
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ Oid oprright;
+ Oid oprleft;
+ Oid oprcommon;
+
+ /*
+ * since the @>> operator has the array as
+ * the first operand and the element as the second
+ * operand we switch their places in the query
+ * i.e opright is oprleft and vice versar
+ */
+
+ /*
+ * we first need to get the array types and decide
+ * the more appropriate one
+ */
+
+ /* get array type of refrenced element*/
+ oprright = get_array_type(operform->oprleft);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprleft))));
+
+ /* get array type of refrencing element*/
+ oprleft = get_array_type(operform->oprright);
+ if (!OidIsValid(oprleft))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprright))));
+
+ /* compare the two array types and try to find
+ a common supertype */
+ oprcommon = select_common_type_2args(oprleft, oprright);
+ if (!OidIsValid(oprcommon))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find common type between %s and %s",
+ format_type_be(oprleft), format_type_be(oprright))));
+
+ oprright = get_base_element_type(oprcommon);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find element type for data type %s",
+ format_type_be(oprcommon))));
+
+ appendStringInfo(buf, " %s %s", sep, rightop);
+ if (oprleft != oprcommon)
+ ri_add_cast_to(buf, oprcommon);
+
+ appendStringInfo(buf, " OPERATOR(pg_catalog.@>>) ");
+
+ appendStringInfoString(buf, leftop);
+ if (operform->oprleft != oprright)
+ ri_add_cast_to(buf, oprright);
+ }
+ else
+ {
+ appendStringInfo(buf, " %s %s", sep, leftop);
+
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+
+ appendStringInfo(buf, " OPERATOR(%s.%s) ",
+ quote_identifier(nspname), oprname);
+
++ appendStringInfoString(buf, rightop);
++
++ if (rightoptype != operform->oprright)
++ ri_add_cast_to(buf, operform->oprright);
++ }
ReleaseSysCache(opertup);
}
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index f850be490a..4e304a9fe4 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -689,6 +689,7 @@ DATA(insert ( 2745 2277 2277 1 s 2750 2742 0 ));
DATA(insert ( 2745 2277 2277 2 s 2751 2742 0 ));
DATA(insert ( 2745 2277 2277 3 s 2752 2742 0 ));
DATA(insert ( 2745 2277 2277 4 s 1070 2742 0 ));
+DATA(insert ( 2745 2277 2283 5 s 6108 2742 0 ));
/*
* btree enum_ops
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index 06f65293cb..f5bad57b70 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -68,6 +68,7 @@ extern int parser_coercion_errposition(ParseState *pstate,
extern Oid select_common_type(ParseState *pstate, List *exprs,
const char *context, Node **which_expr);
+extern Oid select_common_type_2args(Oid ptype, Oid ntype);
extern Node *coerce_to_common_type(ParseState *pstate, Node *node,
Oid targetTypeId,
const char *context);
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index c730563f03..0a9eff4d04 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -737,6 +737,17 @@ SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(6 rows)
+SELECT * FROM array_op_test WHERE i @>> 32 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+ 98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
+ 100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
+(6 rows)
+
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -761,6 +772,19 @@ SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
(8 rows)
+SELECT * FROM array_op_test WHERE i @>> 17 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
+ 53 | {38,17} | {AAAAAAAAAAA21658}
+ 65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+(8 rows)
+
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -942,6 +966,11 @@ SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
-------+---+---
(0 rows)
+SELECT * FROM array_op_test WHERE i @>> NULL ORDER BY seqno;
+ seqno | i | t
+-------+---+---
+(0 rows)
+
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
seqno | i | t
-------+---+---
@@ -953,6 +982,15 @@ SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
101 | {} | {}
(1 row)
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAA72908' ORDER BY seqno;
+ seqno | i | t
+-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
+ 22 | {11,6,56,62,53,30} | {AAAAAAAA72908}
+ 45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}
+ 72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+(4 rows)
+
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
seqno | i | t
-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
@@ -971,6 +1009,14 @@ SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
(4 rows)
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAAAA646' ORDER BY seqno;
+ seqno | i | t
+-------+------------------+--------------------------------------------------------------------
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+ 96 | {23,97,43} | {AAAAAAAAAA646,A87088}
+(3 rows)
+
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
seqno | i | t
-------+------------------+--------------------------------------------------------------------
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index fcf8bd7565..25ef369951 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1811,6 +1811,7 @@ ORDER BY 1, 2, 3;
2742 | 2 | @@@
2742 | 3 | <@
2742 | 4 | =
+ 2742 | 5 | @>>
2742 | 7 | @>
2742 | 9 | ?
2742 | 10 | ?|
@@ -1878,7 +1879,7 @@ ORDER BY 1, 2, 3;
4000 | 25 | <<=
4000 | 26 | >>
4000 | 27 | >>=
-(121 rows)
+(122 rows)
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 25dd4e2c6d..f6fb507bdd 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -313,8 +313,10 @@ SELECT ARRAY[0,0] || ARRAY[1,1] || ARRAY[2,2] AS "{0,0,1,1,2,2}";
SELECT 0 || ARRAY[1,2] || 3 AS "{0,1,2,3}";
SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> 32 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> 17 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{32,17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32,17}' ORDER BY seqno;
@@ -325,11 +327,14 @@ SELECT * FROM array_op_test WHERE i && '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> NULL ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAA72908' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAAAA646' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno;
diff --git a/src/test/regress/sql/element_foreign_key.sql b/src/test/regress/sql/element_foreign_key.sql
new file mode 100644
--- /dev/null
+++ b/src/test/regress/sql/element_foreign_key.sql
@@ -2242,6 +2927,10 @@ index 0000000000..8c1e4d9601
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
@@ -2251,10 +2940,6 @@ index 0000000000..8c1e4d9601
-CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
-DROP TABLE FKTABLEFORARRAY;
-CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
-DROP TABLE FKTABLEFORARRAY;
@@ -2361,6 +3046,31 @@ index 0000000000..8c1e4d9601
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
@@ -2604,3 +3302,87 @@ index 0000000000..8c1e4d9601
+
+-- Check GIN index
+-- Define PKTABLEFORARRAYGIN
+CREATE TABLE PKTABLEFORARRAYGIN ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAYGIN
+INSERT INTO PKTABLEFORARRAYGIN VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (5, 'Test5');
+
+-- Define FKTABLEFORARRAYGIN
+CREATE TABLE FKTABLEFORARRAYGIN ( ftest1 int[],
+ ftest2 int PRIMARY KEY,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYGIN
+ ON DELETE NO ACTION ON UPDATE NO ACTION);
+
+-- -- Create index on FKTABLEFORARRAYGIN
+CREATE INDEX ON FKTABLEFORARRAYGIN USING gin (ftest1 array_ops);
+
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,5,3}', 10);
+
+-- Try using the indexable operator
+SELECT * FROM FKTABLEFORARRAYGIN WHERE ftest1 @>> 5;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYGIN;
+DROP TABLE PKTABLEFORARRAYGIN;
+
+-- Check DELECTE CASCADE ACTION
+CREATE TABLE PKTABLEFORARRAYDELETE ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAYDELETE
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (5, 'Test5');
+
+-- Define FKTABLEFORARRAYDELETE
+CREATE TABLE FKTABLEFORARRAYDELETE ( ftest1 int[],
+ ftest2 int,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYDELETE
+ ON DELETE CASCADE ON UPDATE NO ACTION );
+
+-- Create index on FKTABLEFORARRAYDELETE
+CREATE INDEX ON FKTABLEFORARRAYDELETE USING gin (ftest1);
+
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,5,3}', 10);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+
+-- Check FKTABLE for target rows
+SELECT * FROM FKTABLEFORARRAYDELETE WHERE ftest1 @>> 5;
+
+-- Perfrorm delete
+DELETE FROM PKTABLEFORARRAYDELETE WHERE ptest1 = 5;
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYDELETE;
+DROP TABLE PKTABLEFORARRAYDELETE;
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
OriginalPatch_vs_NewPatch.diff (text/plain, 31.1K) download
diff --git a/home/mark/Dev/GSoC 2017/New Patches/Array-ELEMENT-foreign-key-v3-REBASED-42794d6.patch b/home/mark/Dev/GSoC 2017/Github repo/postgresql/Array-ELEMENT-foreign-key-v4.5.patch
index b125541d09..248a4c4623 100644
--- a/home/mark/Dev/GSoC 2017/New Patches/Array-ELEMENT-foreign-key-v3-REBASED-42794d6.patch
+++ b/home/mark/Dev/GSoC 2017/Github repo/postgresql/Array-ELEMENT-foreign-key-v4.5.patch
@@ -1,8 +1,8 @@
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
ref/create_table.sgml
@@ -199,13 +199,29 @@ index b15c19d3d0..c3fe34888e 100644
+
+ It is advisable to index the refrencing column using GIN index as it considerably enhances the performance.
+ Also concerning coercion while using the GIN index:
+
+ <programlisting>
+ CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+ CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ </programlisting>
+ This syntax is fine since this will cast ptest1 to int4 upon RI checks, however,
+
+ <programlisting>
+ CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+ CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ </programlisting>
+ This syntax will cast ftest1 to int4 upon RI checks, thus defeating the purpose of the index.
+
diff --git a/src/backend/access/gin/ginarrayproc.c b/src/backend/access/gin/ginarrayproc.c
index a5238c3af5..2587cf9ccf 100644
--- a/src/backend/access/gin/ginarrayproc.c
+++ b/src/backend/access/gin/ginarrayproc.c
@@ -24,6 +24,7 @@
#define GinContainsStrategy 2
#define GinContainedStrategy 3
#define GinEqualStrategy 4
+#define GinContainsElemStrategy 5
@@ -79,7 +80,7 @@ Datum
ginqueryarrayextract(PG_FUNCTION_ARGS)
{
/* Make copy of array input to ensure it doesn't disappear while in use */
- ArrayType *array = PG_GETARG_ARRAYTYPE_P_COPY(0);
+ ArrayType *array;
int32 *nkeys = (int32 *) PG_GETARG_POINTER(1);
StrategyNumber strategy = PG_GETARG_UINT16(2);
@@ -94,19 +95,35 @@ ginqueryarrayextract(PG_FUNCTION_ARGS)
bool *nulls;
int nelems;
- get_typlenbyvalalign(ARR_ELEMTYPE(array),
- &elmlen, &elmbyval, &elmalign);
+ if (strategy != GinContainsElemStrategy){
+ array = PG_GETARG_ARRAYTYPE_P_COPY(0);
- deconstruct_array(array,
- ARR_ELEMTYPE(array),
- elmlen, elmbyval, elmalign,
- &elems, &nulls, &nelems);
+ get_typlenbyvalalign(ARR_ELEMTYPE(array),
+ &elmlen, &elmbyval, &elmalign);
- *nkeys = nelems;
- *nullFlags = nulls;
+ deconstruct_array(array,
+ ARR_ELEMTYPE(array),
+ elmlen, elmbyval, elmalign,
+ &elems, &nulls, &nelems);
- switch (strategy)
- {
+ *nkeys = nelems;
+ *nullFlags = nulls;
+ }
+ else{
+ /*
+ * since this function return a pointer to a
+ * deconstructed array, there is nothing to do if
+ * operand is a single element except to return
+ * as is and configure the searchmode
+ */
+
+ elems = PG_GETARG_POINTER(0);
+ *nkeys = 1;
+ *nullFlags = PG_ARGISNULL(0);
+ }
+
+ switch (strategy)
+ {
case GinOverlapStrategy:
*searchMode = GIN_SEARCH_MODE_DEFAULT;
break;
@@ -126,6 +143,14 @@ ginqueryarrayextract(PG_FUNCTION_ARGS)
else
*searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
break;
+ case GinContainsElemStrategy:
+ /*
+ * only items that match the queried element
+ * are considered candidate
+ */
+
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+ break;
default:
elog(ERROR, "ginqueryarrayextract: unknown strategy number: %d",
strategy);
@@ -171,6 +196,7 @@ ginarrayconsistent(PG_FUNCTION_ARGS)
}
}
break;
+ case GinContainsElemStrategy:
case GinContainsStrategy:
/* result is not lossy */
*recheck = false;
@@ -258,7 +284,8 @@ ginarraytriconsistent(PG_FUNCTION_ARGS)
}
}
break;
- case GinContainsStrategy:
+ case GinContainsElemStrategy:
+ case GinContainsStrategy:
/* must have all elements in check[] true, and no nulls */
res = GIN_TRUE;
for (i = 0; i < nkeys; i++)
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 7ceea7a741..ca8390a147 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -178,7 +178,22 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
if (i < nUserQueryValues)
{
/* set up normal entry using extractQueryFn's outputs */
- queryKey = queryValues[i];
+
+ if (strategy == 5 && nQueryValues == 1)
+ {
+ /*
+ * strategy 5 is GinContainsElemStrategy.
+ * we are sure the strategy is GinContainsElemStrategy,
+ * so take the datum directly without accessing an array
+ * since it's an element not an array
+ */
+ queryKey = queryValues;
+ }
+ else
+ {
+ queryKey = queryValues[i];
+ }
+
queryCategory = queryCategories[i];
isPartialMatch =
(ginstate->canPartialMatch[attnum - 1] && partial_matches)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -458,89 +602,90 @@ index bb00858ad1..dc18fd1eae 100644
i++;
}
- /* Array foreign keys support only NO ACTION and RESTRICT actions */
+ /* Array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions */
if (has_array)
{
if ((fkconstraint->fk_upd_action != FKCONSTR_ACTION_NOACTION &&
fkconstraint->fk_upd_action != FKCONSTR_ACTION_RESTRICT) ||
(fkconstraint->fk_del_action != FKCONSTR_ACTION_NOACTION &&
- fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT))
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_CASCADE))
ereport(ERROR,
(errcode(ERRCODE_INVALID_FOREIGN_KEY),
- errmsg("array foreign keys support only NO ACTION and RESTRICT actions")));
+ errmsg("array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions")));
}
- /*
+ /*
* If the attribute list for the referenced table was omitted, lookup the
* definition of the primary key and use it. Otherwise, validate the
* supplied attribute list. In either case, discover the index OID and
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index e95cee1ebf..cf273e7c03 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1320,6 +1320,90 @@ select_common_type(ParseState *pstate, List *exprs, const char *context,
}
/*
+ * look at select_common_type(); in parse_coerce.c(src/backend/parser/parse_coerce.c)
+ *
+ * select_common_type_2args()
+ * Determine the common supertype of a list of input expressions.
+ * 'leftOid': left operand's type
+ * 'rightOid': left operand's type
+ */
+Oid select_common_type_2args(Oid leftOid, Oid rightOid)
+{
+ TYPCATEGORY leftcategory;
+ TYPCATEGORY rightcategory;
+ bool leftispreferred;
+ bool rightispreferred;
+
+ Assert(leftOid != InvalidOid);
+ Assert(rightOid != InvalidOid);
+
+ /*
+ * If input types are valid and exactly the same, just pick that type.
+ */
+ if (leftOid != UNKNOWNOID)
+ {
+ if (rightOid == leftOid)
+ return leftOid;
+ }
+
+ /*
+ * Nope, so set up for the full algorithm.
+ */
+ leftOid = getBaseType(leftOid);
+ rightOid = getBaseType(rightOid);
+ get_type_category_preferred(leftOid, &leftcategory, &leftispreferred);
+ get_type_category_preferred(rightOid, &rightcategory, &rightispreferred);
+
+ if (rightOid != UNKNOWNOID && rightOid != leftOid)
+ {
+ if (rightcategory != leftcategory)
+ {
+ /* both types in different categories? then not much hope... */
+ return InvalidOid;
+ }
+
+ if (can_coerce_type(1, &rightOid, &leftOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left */
+ if (!can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can not coerce to it implicitly right to left, thus coercion only works one way */
+ return leftOid;
+ }
+ else
+ {
+ /* coercion works both ways, then decide depending on preferred flag */
+ if (leftispreferred)
+ return leftOid;
+ else
+ rightOid;
+ }
+ }
+ else{
+ /* can not coerce to it implicitly right to left */
+ if (can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left, thus coercion only works one way */
+ return rightOid;
+ }
+ else{
+ /* can not coerce either way */
+ return InvalidOid;
+ }
+ }
+ }
+
+ /*
+ * If all the inputs were UNKNOWN type --- ie, unknown-type literals ---
+ * then resolve as type TEXT.
+ */
+ if (leftOid == UNKNOWNOID)
+ leftOid = TEXTOID;
+
+ return leftOid;
+}
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 34dadd6e19..b2db8baffd 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -4232,6 +4232,112 @@ arraycontained(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(result);
}
+/*
+ * array_contains_elem : checks an array for a spefific element
+ * adapted from array_contain_compare() for containment of a single element
+ */
+static bool
+array_contains_elem(AnyArrayType *array, Datum elem,
+ Oid collation, void **fn_extra)
+{
+ Oid arr_type = AARR_ELEMTYPE(array);
+ TypeCacheEntry *typentry;
+ int nelems;
+ int typlen;
+ bool typbyval;
+ char typalign;
+ int i;
+ array_iter it1;
+ FunctionCallInfoData locfcinfo;
+
+ /*
+ * We arrange to look up the equality function only once per series of
+ * calls, assuming the element type doesn't change underneath us. The
+ * typcache is used so that we have no memory leakage when being used as
+ * an index support function.
+ */
+ typentry = (TypeCacheEntry *)*fn_extra;
+ if (typentry == NULL ||
+ typentry->type_id != arr_type)
+ {
+ typentry = lookup_type_cache(arr_type,
+ TYPECACHE_EQ_OPR_FINFO);
+ if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(arr_type))));
+ *fn_extra = (void *)typentry;
+ }
+ typlen = typentry->typlen;
+ typbyval = typentry->typbyval;
+ typalign = typentry->typalign;
+
+ /*
+ * Apply the comparison operator to each pair of array elements.
+ */
+ InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+ collation, NULL, NULL);
+
+ /* Loop over source data */
+ nelems = ArrayGetNItems(AARR_NDIM(array), AARR_DIMS(array));
+ array_iter_setup(&it1, array);
+
+ for (i = 0; i < nelems; i++)
+ {
+ Datum elt1;
+ bool isnull;
+ bool oprresult;
+
+ /* Get element, checking for NULL */
+ elt1 = array_iter_next(&it1, &isnull, i, typlen, typbyval, typalign);
+
+ /*
+ * We assume that the comparison operator is strict, so a NULL can't
+ * match anything. XXX this diverges from the "NULL=NULL" behavior of
+ * array_eq, should we act like that?
+ */
+ if (isnull)
+ continue;
+
+ /*
+ * Apply the operator to the element pair
+ */
+ locfcinfo.arg[0] = elt1;
+ locfcinfo.arg[1] = elem;
+ locfcinfo.argnull[0] = false;
+ locfcinfo.argnull[1] = false;
+ locfcinfo.isnull = false;
+ oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+ if (oprresult)
+ return true;
+ }
+
+ return false;
+}
+
+Datum
+arraycontainselem(PG_FUNCTION_ARGS)
+{
+ AnyArrayType *array = PG_GETARG_ANY_ARRAY(0);
+ Datum elem = PG_GETARG_DATUM(1);
+ Oid collation = PG_GET_COLLATION();
+ bool result;
+
+ /* we don't need to check if the elem is null or if
+ the elem datatype and array datatype match since this
+ is handled within internal calls already (a property
+ of polymorphic functions) */
+
+ result = array_contains_elem(array, elem, collation,
+ &fcinfo->flinfo->fn_extra);
+
+ /* Avoid leaking memory when handed toasted input. */
+ AARR_FREE_IF_COPY(array, 0);
+
+ PG_RETURN_BOOL(result);
+}
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
-@@ -2586,14 +2682,32 @@ ri_GenerateQual(StringInfo buf,
+@@ -2586,14 +2681,81 @@ ri_GenerateQual(StringInfo buf,
nspname = get_namespace_name(operform->oprnamespace);
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- {
- oprright = get_array_type(operform->oprright);
- if (!OidIsValid(oprright))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("could not find array type for data type %s",
- format_type_be(operform->oprright))));
- }
- else
- oprright = operform->oprright;
-
- appendStringInfo(buf, " %s %s", sep, leftop);
- if (leftoptype != operform->oprleft)
- ri_add_cast_to(buf, operform->oprleft);
+ appendStringInfo(buf, " %s %s", sep, leftop);
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
appendStringInfoString(buf, oprname);
appendStringInfo(buf, ") %s", rightop);
if (rightoptype != operform->oprright)
ri_add_cast_to(buf, operform->oprright);
-
- appendStringInfo(buf, " OPERATOR(%s.%s) ",
- quote_identifier(nspname), oprname);
-
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- appendStringInfoString(buf, "ANY (");
- appendStringInfoString(buf, rightop);
- if (rightoptype != oprright)
- ri_add_cast_to(buf, oprright);
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- appendStringInfoChar(buf, ')');
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ Oid oprright;
+ Oid oprleft;
+ Oid oprcommon;
+
+ /*
+ * since the @>> operator has the array as
+ * the first operand and the element as the second
+ * operand we switch their places in the query
+ * i.e opright is oprleft and vice versar
+ */
+
+ /*
+ * we first need to get the array types and decide
+ * the more appropriate one
+ */
+
+ /* get array type of refrenced element*/
+ oprright = get_array_type(operform->oprleft);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprleft))));
+
+ /* get array type of refrencing element*/
+ oprleft = get_array_type(operform->oprright);
+ if (!OidIsValid(oprleft))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprright))));
+
+ /* compare the two array types and try to find
+ a common supertype */
+ oprcommon = select_common_type_2args(oprleft, oprright);
+ if (!OidIsValid(oprcommon))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find common type between %s and %s",
+ format_type_be(oprleft), format_type_be(oprright))));
+
+ oprright = get_base_element_type(oprcommon);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find element type for data type %s",
+ format_type_be(oprcommon))));
+
+ appendStringInfo(buf, " %s %s", sep, rightop);
+ if (oprleft != oprcommon)
+ ri_add_cast_to(buf, oprcommon);
+
+ appendStringInfo(buf, " OPERATOR(pg_catalog.@>>) ");
+
+ appendStringInfoString(buf, leftop);
+ if (operform->oprleft != oprright)
+ ri_add_cast_to(buf, oprright);
+ }
+ else
+ {
+ appendStringInfo(buf, " %s %s", sep, leftop);
+
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+
+ appendStringInfo(buf, " OPERATOR(%s.%s) ",
+ quote_identifier(nspname), oprname);
+
++ appendStringInfoString(buf, rightop);
++
++ if (rightoptype != operform->oprright)
++ ri_add_cast_to(buf, operform->oprright);
++ }
ReleaseSysCache(opertup);
}
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index f850be490a..4e304a9fe4 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -689,6 +689,7 @@ DATA(insert ( 2745 2277 2277 1 s 2750 2742 0 ));
DATA(insert ( 2745 2277 2277 2 s 2751 2742 0 ));
DATA(insert ( 2745 2277 2277 3 s 2752 2742 0 ));
DATA(insert ( 2745 2277 2277 4 s 1070 2742 0 ));
+DATA(insert ( 2745 2277 2283 5 s 6108 2742 0 ));
/*
* btree enum_ops
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index 06f65293cb..f5bad57b70 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -68,6 +68,7 @@ extern int parser_coercion_errposition(ParseState *pstate,
extern Oid select_common_type(ParseState *pstate, List *exprs,
const char *context, Node **which_expr);
+extern Oid select_common_type_2args(Oid ptype, Oid ntype);
extern Node *coerce_to_common_type(ParseState *pstate, Node *node,
Oid targetTypeId,
const char *context);
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index c730563f03..0a9eff4d04 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -737,6 +737,17 @@ SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(6 rows)
+SELECT * FROM array_op_test WHERE i @>> 32 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+ 98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
+ 100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
+(6 rows)
+
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -761,6 +772,19 @@ SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
(8 rows)
+SELECT * FROM array_op_test WHERE i @>> 17 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
+ 53 | {38,17} | {AAAAAAAAAAA21658}
+ 65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+(8 rows)
+
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -942,6 +966,11 @@ SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
-------+---+---
(0 rows)
+SELECT * FROM array_op_test WHERE i @>> NULL ORDER BY seqno;
+ seqno | i | t
+-------+---+---
+(0 rows)
+
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
seqno | i | t
-------+---+---
@@ -953,6 +982,15 @@ SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
101 | {} | {}
(1 row)
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAA72908' ORDER BY seqno;
+ seqno | i | t
+-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
+ 22 | {11,6,56,62,53,30} | {AAAAAAAA72908}
+ 45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}
+ 72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+(4 rows)
+
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
seqno | i | t
-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
@@ -971,6 +1009,14 @@ SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
(4 rows)
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAAAA646' ORDER BY seqno;
+ seqno | i | t
+-------+------------------+--------------------------------------------------------------------
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+ 96 | {23,97,43} | {AAAAAAAAAA646,A87088}
+(3 rows)
+
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
seqno | i | t
-------+------------------+--------------------------------------------------------------------
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index fcf8bd7565..25ef369951 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1811,6 +1811,7 @@ ORDER BY 1, 2, 3;
2742 | 2 | @@@
2742 | 3 | <@
2742 | 4 | =
+ 2742 | 5 | @>>
2742 | 7 | @>
2742 | 9 | ?
2742 | 10 | ?|
@@ -1878,7 +1879,7 @@ ORDER BY 1, 2, 3;
4000 | 25 | <<=
4000 | 26 | >>
4000 | 27 | >>=
-(121 rows)
+(122 rows)
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 25dd4e2c6d..f6fb507bdd 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -313,8 +313,10 @@ SELECT ARRAY[0,0] || ARRAY[1,1] || ARRAY[2,2] AS "{0,0,1,1,2,2}";
SELECT 0 || ARRAY[1,2] || 3 AS "{0,1,2,3}";
SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> 32 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> 17 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{32,17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32,17}' ORDER BY seqno;
@@ -325,11 +327,14 @@ SELECT * FROM array_op_test WHERE i && '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> NULL ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAA72908' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAAAA646' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno;
diff --git a/src/test/regress/sql/element_foreign_key.sql b/src/test/regress/sql/element_foreign_key.sql
new file mode 100644
--- /dev/null
+++ b/src/test/regress/sql/element_foreign_key.sql
@@ -2242,6 +2927,10 @@ index 0000000000..8c1e4d9601
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
@@ -2251,10 +2940,6 @@ index 0000000000..8c1e4d9601
-CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
-DROP TABLE FKTABLEFORARRAY;
-CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
-DROP TABLE FKTABLEFORARRAY;
@@ -2361,6 +3046,31 @@ index 0000000000..8c1e4d9601
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
@@ -2604,3 +3302,87 @@ index 0000000000..8c1e4d9601
+
+-- Check GIN index
+-- Define PKTABLEFORARRAYGIN
+CREATE TABLE PKTABLEFORARRAYGIN ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAYGIN
+INSERT INTO PKTABLEFORARRAYGIN VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (5, 'Test5');
+
+-- Define FKTABLEFORARRAYGIN
+CREATE TABLE FKTABLEFORARRAYGIN ( ftest1 int[],
+ ftest2 int PRIMARY KEY,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYGIN
+ ON DELETE NO ACTION ON UPDATE NO ACTION);
+
+-- -- Create index on FKTABLEFORARRAYGIN
+CREATE INDEX ON FKTABLEFORARRAYGIN USING gin (ftest1 array_ops);
+
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,5,3}', 10);
+
+-- Try using the indexable operator
+SELECT * FROM FKTABLEFORARRAYGIN WHERE ftest1 @>> 5;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYGIN;
+DROP TABLE PKTABLEFORARRAYGIN;
+
+-- Check DELECTE CASCADE ACTION
+CREATE TABLE PKTABLEFORARRAYDELETE ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAYDELETE
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (5, 'Test5');
+
+-- Define FKTABLEFORARRAYDELETE
+CREATE TABLE FKTABLEFORARRAYDELETE ( ftest1 int[],
+ ftest2 int,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYDELETE
+ ON DELETE CASCADE ON UPDATE NO ACTION );
+
+-- Create index on FKTABLEFORARRAYDELETE
+CREATE INDEX ON FKTABLEFORARRAYDELETE USING gin (ftest1);
+
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,5,3}', 10);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+
+-- Check FKTABLE for target rows
+SELECT * FROM FKTABLEFORARRAYDELETE WHERE ftest1 @>> 5;
+
+-- Perfrorm delete
+DELETE FROM PKTABLEFORARRAYDELETE WHERE ptest1 = 5;
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYDELETE;
+DROP TABLE PKTABLEFORARRAYDELETE;
Array-ELEMENT-foreign-key-v5.patch (text/x-patch, 143.3K) download
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ef7054cf26..f16638dcda 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2322,7 +2322,17 @@ SCRAM-SHA-256$<replaceable><iteration count></>:<replaceable><salt><
</row>
<row>
- <entry><structfield>conpfeqop</structfield></entry>
+ <entry><structfield>confreftype</structfield></entry>
+ <entry><type>char[]</type></entry>
+ <entry></entry>
+ <entry>If a foreign key, the reference semantics for each column:
+ <literal>p</> = plain (simple equality),
+ <literal>e</> = each element of referencing array must have a match
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>conpfeqop</structfield></entry>
<entry><type>oid[]</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</></entry>
<entry>If a foreign key, list of the equality operators for PK = FK comparisons</entry>
@@ -2367,6 +2377,12 @@ SCRAM-SHA-256$<replaceable><iteration count></>:<replaceable><salt><
</table>
<para>
+ When <structfield>confreftype</structfield> indicates array-vs-scalar
+ foreign key reference semantics, the equality operators listed in
+ <structfield>conpfeqop</structfield> etc are for the array's element type.
+ </para>
+
+ <para>
In the case of an exclusion constraint, <structfield>conkey</structfield>
is only useful for constraint elements that are simple column references.
For other cases, a zero appears in <structfield>conkey</structfield>
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index b05a9c2150..75da196334 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -882,6 +882,111 @@ CREATE TABLE order_items (
</para>
</sect2>
+ <sect2 id="ddl-constraints-element-fk">
+ <title>Array ELEMENT Foreign Keys</title>
+
+ <indexterm>
+ <primary>ELEMENT foreign key</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>Array ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>referential integrity</primary>
+ </indexterm>
+
+ <para>
+ Another option you have with foreign keys is to use a
+ referencing column which is an array of elements with
+ the same type (or a compatible one) as the referenced
+ column in the related table. This feature is called
+ <firstterm>array element foreign key</firstterm> and is implemented
+ in PostgreSQL with <firstterm>ELEMENT foreign key constraints</firstterm>,
+ as described in the following example:
+
+<programlisting>
+CREATE TABLE drivers (
+ driver_id integer PRIMARY KEY,
+ first_name text,
+ last_name text,
+ ...
+);
+
+CREATE TABLE races (
+ race_id integer PRIMARY KEY,
+ title text,
+ race_day DATE,
+ ...
+ final_positions integer[] <emphasis>ELEMENT REFERENCES drivers</emphasis>
+);
+</programlisting>
+
+ The above example uses an array (<literal>final_positions</literal>)
+ to store the results of a race: for each of its elements
+ a referential integrity check is enforced on the
+ <literal>drivers</literal> table.
+ Note that <literal>ELEMENT REFERENCES</literal> is an extension
+ of PostgreSQL and it is not included in the SQL standard.
+ </para>
+
+ <para>
+ Even though the most common use case for array <literal>ELEMENT</literal>
+ foreign keys is on a single column key, you can define an <quote>array
+ <literal>ELEMENT</literal> foreign key constraint</quote> on a group
+ of columns. As the following example shows, it must be written in table
+ constraint form:
+
+<programlisting>
+CREATE TABLE available_moves (
+ kind text,
+ move text,
+ description text,
+ PRIMARY KEY (kind, move)
+);
+
+CREATE TABLE paths (
+ description text,
+ kind text,
+ moves text[],
+ <emphasis>FOREIGN KEY (kind, ELEMENT moves) REFERENCES available_moves (kind, move)</emphasis>
+);
+
+INSERT INTO available_moves VALUES ('relative', 'LN', 'look north');
+INSERT INTO available_moves VALUES ('relative', 'RL', 'rotate left');
+INSERT INTO available_moves VALUES ('relative', 'RR', 'rotate right');
+INSERT INTO available_moves VALUES ('relative', 'MF', 'move forward');
+INSERT INTO available_moves VALUES ('absolute', 'N', 'move north');
+INSERT INTO available_moves VALUES ('absolute', 'S', 'move south');
+INSERT INTO available_moves VALUES ('absolute', 'E', 'move east');
+INSERT INTO available_moves VALUES ('absolute', 'W', 'move west');
+
+INSERT INTO paths VALUES ('L-shaped path', 'relative', '{LN, RL, MF, RR, MF, MF}');
+INSERT INTO paths VALUES ('L-shaped path', 'absolute', '{W, N, N}');
+</programlisting>
+
+ On top of standard foreign key requirements,
+ array <literal>ELEMENT</literal> foreign key constraints
+ require that the referencing column is an array of a compatible
+ type of the corresponding referenced column.
+ </para>
+
+ <para>
+ For more detailed information on array <literal>ELEMENT</literal>
+ foreign key options and special cases, please refer to the documentation
+ for <xref linkend="sql-createtable-foreign-key"> and
+ <xref linkend="sql-createtable-element-foreign-key-constraints">.
+ </para>
+
+ </sect2>
+
<sect2 id="ddl-constraints-exclusion">
<title>Exclusion Constraints</title>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index e9c2c49533..42eaa50c18 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -65,7 +65,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ] |
UNIQUE <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="PARAMETER">index_parameters</replaceable> |
- REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
+ [ELEMENT] REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -76,7 +76,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
UNIQUE ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ] |
- FOREIGN KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> [, ... ] ) ]
+ FOREIGN KEY ( [ELEMENT] <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> [, ... ] ) ]
[ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -812,10 +812,10 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</listitem>
</varlistentry>
- <varlistentry>
+ <varlistentry id="sql-createtable-foreign-key" xreflabel="FOREIGN KEY">
<term><literal>REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
- <term><literal>FOREIGN KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] )
+ <term><literal>FOREIGN KEY ( [ELEMENT] <replaceable class="parameter">column_name</replaceable> [, ... ] )
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] ) ]
[ MATCH <replaceable class="parameter">matchtype</replaceable> ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ]
@@ -839,6 +839,35 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</para>
<para>
+ In case the column name <replaceable class="parameter">column</replaceable>
+ is prepended with the <literal>ELEMENT</literal> keyword and <replaceable
+ class="parameter">column</replaceable> is an array of elements compatible
+ with the corresponding <replaceable class="parameter">refcolumn</replaceable>
+ in <replaceable class="parameter">reftable</replaceable>, an
+ array <literal>ELEMENT</literal> foreign key constraint is put in place
+ (see <xref linkend="sql-createtable-element-foreign-key-constraints">
+ for more information).
+ Multi-column keys with more than one <literal>ELEMENT</literal> column
+ are currently not allowed.
+
+ It is advisable to index the refrencing column using GIN index as it considerably enhances the performance.
+ Also concerning coercion while using the GIN index:
+
+ <programlisting>
+ CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+ CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ </programlisting>
+ This syntax is fine since this will cast ptest1 to int4 upon RI checks, however,
+
+ <programlisting>
+ CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+ CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ </programlisting>
+ This syntax will cast ftest1 to int4 upon RI checks, thus defeating the purpose of the index.
+
+ </para>
+
+ <para>
A value inserted into the referencing column(s) is matched against the
values of the referenced table and referenced columns using the
given match type. There are three match types: <literal>MATCH
@@ -901,7 +930,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
<para>
Delete any rows referencing the deleted row, or update the
values of the referencing column(s) to the new values of the
- referenced columns, respectively.
+ referenced columns, respectively. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -910,7 +940,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
<term><literal>SET NULL</literal></term>
<listitem>
<para>
- Set the referencing column(s) to null.
+ Set the referencing column(s) to null. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -922,6 +953,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
Set the referencing column(s) to their default values.
(There must be a row in the referenced table matching the default
values, if they are not null, or the operation will fail.)
+ Currently not supported with array <literal>ELEMENT</literal>
+ foreign keys.
</para>
</listitem>
</varlistentry>
@@ -937,6 +970,61 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</listitem>
</varlistentry>
+ <varlistentry id="sql-createtable-element-foreign-key-constraints" xreflabel="ELEMENT REFERENCES">
+ <term><literal>ELEMENT REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
+
+ <listitem>
+ <para>
+ The <literal>ELEMENT REFERENCES</literal> definition specifies
+ an <quote>array <literal>ELEMENT</literal> foreign key</quote>,
+ a special kind of foreign key
+ constraint requiring the referencing column to be an array of elements
+ of the same type (or a compatible one) as the referenced column
+ in the referenced table. The value of each element of the
+ <replaceable class="parameter">refcolumn</replaceable> array
+ will be matched against some row of <replaceable
+ class="parameter">reftable</replaceable>.
+ </para>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys are an extension
+ of PostgreSQL and are not included in the SQL standard.
+ </para>
+
+ <para>
+ Even with <literal>ELEMENT</literal> foreign keys, modifications
+ in the referenced column can trigger actions to be performed on
+ the referencing array.
+ Similarly to standard foreign keys, you can specify these
+ actions using the <literal>ON DELETE</literal> and
+ <literal>ON UPDATE</literal> clauses.
+ However, only the two following actions for each clause are
+ currently allowed:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>NO ACTION</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints. This is the default action.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RESTRICT</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>DEFERRABLE</literal></term>
<term><literal>NOT DEFERRABLE</literal></term>
@@ -1876,6 +1964,16 @@ CREATE TABLE cities_ab_10000_to_100000
</refsect2>
<refsect2>
+ <title id="sql-createtable-foreign-key-arrays">Array <literal>ELEMENT</literal> Foreign Keys</title>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys and the
+ <literal>ELEMENT REFERENCES</literal> clause
+ are a <productname>PostgreSQL</productname> extension.
+ </para>
+ </refsect2>
+
+ <refsect2>
<title><literal>PARTITION BY</> Clause</title>
<para>
diff --git a/src/backend/access/gin/ginarrayproc.c b/src/backend/access/gin/ginarrayproc.c
index a5238c3af5..2587cf9ccf 100644
--- a/src/backend/access/gin/ginarrayproc.c
+++ b/src/backend/access/gin/ginarrayproc.c
@@ -24,6 +24,7 @@
#define GinContainsStrategy 2
#define GinContainedStrategy 3
#define GinEqualStrategy 4
+#define GinContainsElemStrategy 5
/*
@@ -79,7 +80,7 @@ Datum
ginqueryarrayextract(PG_FUNCTION_ARGS)
{
/* Make copy of array input to ensure it doesn't disappear while in use */
- ArrayType *array = PG_GETARG_ARRAYTYPE_P_COPY(0);
+ ArrayType *array;
int32 *nkeys = (int32 *) PG_GETARG_POINTER(1);
StrategyNumber strategy = PG_GETARG_UINT16(2);
@@ -94,19 +95,35 @@ ginqueryarrayextract(PG_FUNCTION_ARGS)
bool *nulls;
int nelems;
- get_typlenbyvalalign(ARR_ELEMTYPE(array),
- &elmlen, &elmbyval, &elmalign);
+ if (strategy != GinContainsElemStrategy){
+ array = PG_GETARG_ARRAYTYPE_P_COPY(0);
- deconstruct_array(array,
- ARR_ELEMTYPE(array),
- elmlen, elmbyval, elmalign,
- &elems, &nulls, &nelems);
+ get_typlenbyvalalign(ARR_ELEMTYPE(array),
+ &elmlen, &elmbyval, &elmalign);
- *nkeys = nelems;
- *nullFlags = nulls;
+ deconstruct_array(array,
+ ARR_ELEMTYPE(array),
+ elmlen, elmbyval, elmalign,
+ &elems, &nulls, &nelems);
- switch (strategy)
- {
+ *nkeys = nelems;
+ *nullFlags = nulls;
+ }
+ else{
+ /*
+ * since this function return a pointer to a
+ * deconstructed array, there is nothing to do if
+ * operand is a single element except to return
+ * as is and configure the searchmode
+ */
+
+ elems = PG_GETARG_POINTER(0);
+ *nkeys = 1;
+ *nullFlags = PG_ARGISNULL(0);
+ }
+
+ switch (strategy)
+ {
case GinOverlapStrategy:
*searchMode = GIN_SEARCH_MODE_DEFAULT;
break;
@@ -126,6 +143,14 @@ ginqueryarrayextract(PG_FUNCTION_ARGS)
else
*searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
break;
+ case GinContainsElemStrategy:
+ /*
+ * only items that match the queried element
+ * are considered candidate
+ */
+
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+ break;
default:
elog(ERROR, "ginqueryarrayextract: unknown strategy number: %d",
strategy);
@@ -171,6 +196,7 @@ ginarrayconsistent(PG_FUNCTION_ARGS)
}
}
break;
+ case GinContainsElemStrategy:
case GinContainsStrategy:
/* result is not lossy */
*recheck = false;
@@ -258,7 +284,8 @@ ginarraytriconsistent(PG_FUNCTION_ARGS)
}
}
break;
- case GinContainsStrategy:
+ case GinContainsElemStrategy:
+ case GinContainsStrategy:
/* must have all elements in check[] true, and no nulls */
res = GIN_TRUE;
for (i = 0; i < nkeys; i++)
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 7ceea7a741..ca8390a147 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -178,7 +178,22 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
if (i < nUserQueryValues)
{
/* set up normal entry using extractQueryFn's outputs */
- queryKey = queryValues[i];
+
+ if (strategy == 5 && nQueryValues == 1)
+ {
+ /*
+ * strategy 5 is GinContainsElemStrategy.
+ * we are sure the strategy is GinContainsElemStrategy,
+ * so take the datum directly without accessing an array
+ * since it's an element not an array
+ */
+ queryKey = queryValues;
+ }
+ else
+ {
+ queryKey = queryValues[i];
+ }
+
queryCategory = queryCategories[i];
isPartialMatch =
(ginstate->canPartialMatch[attnum - 1] && partial_matches)
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 45ee9ac8b9..3fe6b1b247 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2100,6 +2100,7 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c7b2f031f0..db7978ec45 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1213,6 +1213,7 @@ index_constraint_create(Relation heapRelation,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 1336c46d3f..924affcdcd 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -59,6 +59,7 @@ CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
@@ -82,6 +83,7 @@ CreateConstraintEntry(const char *constraintName,
Datum values[Natts_pg_constraint];
ArrayType *conkeyArray;
ArrayType *confkeyArray;
+ ArrayType *confreftypeArray;
ArrayType *conpfeqopArray;
ArrayType *conppeqopArray;
ArrayType *conffeqopArray;
@@ -119,7 +121,11 @@ CreateConstraintEntry(const char *constraintName,
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = Int16GetDatum(foreignKey[i]);
confkeyArray = construct_array(fkdatums, foreignNKeys,
- INT2OID, 2, true, 's');
+ INT2OID, sizeof(int16), true, 's');
+ for (i = 0; i < foreignNKeys; i++)
+ fkdatums[i] = CharGetDatum(foreignRefType[i]);
+ confreftypeArray = construct_array(fkdatums, foreignNKeys,
+ CHAROID, sizeof(char), true, 'c');
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
conpfeqopArray = construct_array(fkdatums, foreignNKeys,
@@ -136,6 +142,7 @@ CreateConstraintEntry(const char *constraintName,
else
{
confkeyArray = NULL;
+ confreftypeArray = NULL;
conpfeqopArray = NULL;
conppeqopArray = NULL;
conffeqopArray = NULL;
@@ -188,6 +195,11 @@ CreateConstraintEntry(const char *constraintName,
else
nulls[Anum_pg_constraint_confkey - 1] = true;
+ if (confreftypeArray)
+ values[Anum_pg_constraint_confreftype - 1] = PointerGetDatum(confreftypeArray);
+ else
+ nulls[Anum_pg_constraint_confreftype - 1] = true;
+
if (conpfeqopArray)
values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
else
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 0f08245a67..fbd9756194 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7021,6 +7021,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Relation pkrel;
int16 pkattnum[INDEX_MAX_KEYS];
int16 fkattnum[INDEX_MAX_KEYS];
+ char fkreftypes[INDEX_MAX_KEYS];
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
Oid opclasses[INDEX_MAX_KEYS];
@@ -7028,10 +7029,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid ppeqoperators[INDEX_MAX_KEYS];
Oid ffeqoperators[INDEX_MAX_KEYS];
int i;
+ ListCell *lc;
int numfks,
numpks;
Oid indexOid;
Oid constrOid;
+ bool has_array;
bool old_check_ok;
ObjectAddress address;
ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
@@ -7108,6 +7111,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
*/
MemSet(pkattnum, 0, sizeof(pkattnum));
MemSet(fkattnum, 0, sizeof(fkattnum));
+ MemSet(fkreftypes, 0, sizeof(fkreftypes));
MemSet(pktypoid, 0, sizeof(pktypoid));
MemSet(fktypoid, 0, sizeof(fktypoid));
MemSet(opclasses, 0, sizeof(opclasses));
@@ -7120,6 +7124,51 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
fkattnum, fktypoid);
/*
+ * Validate the reference semantics codes, too, and convert list to array
+ * format to pass to CreateConstraintEntry.
+ */
+ Assert(list_length(fkconstraint->fk_reftypes) == numfks);
+ has_array = false;
+ i = 0;
+ foreach(lc, fkconstraint->fk_reftypes)
+ {
+ char reftype = lfirst_int(lc);
+
+ switch (reftype)
+ {
+ case FKCONSTR_REF_PLAIN:
+ /* OK, nothing to do */
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ /* At most one FK column can be an array reference */
+ if (has_array)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign keys support only one array column")));
+ has_array = true;
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d", (int) reftype);
+ break;
+ }
+ fkreftypes[i] = reftype;
+ i++;
+ }
+
+ /* Array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions */
+ if (has_array)
+ {
+ if ((fkconstraint->fk_upd_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_upd_action != FKCONSTR_ACTION_RESTRICT) ||
+ (fkconstraint->fk_del_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_CASCADE))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions")));
+ }
+
+ /*
* If the attribute list for the referenced table was omitted, lookup the
* definition of the primary key and use it. Otherwise, validate the
* supplied attribute list. In either case, discover the index OID and
@@ -7205,6 +7254,65 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
eqstrategy = BTEqualStrategyNumber;
/*
+ * If this is an array foreign key, we must look up the operators for
+ * the array element type, not the array type itself.
+ */
+ if (fkreftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ Oid elemopclass;
+
+ /* We look through any domain here */
+ fktype = get_base_element_type(fktype);
+ if (!OidIsValid(fktype))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has type %s which is not an array type.",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktypoid[i]))));
+
+ /*
+ * For the moment, we must also insist that the array's element
+ * type have a default btree opclass that is in the index's
+ * opfamily. This is necessary because ri_triggers.c relies on
+ * COUNT(DISTINCT x) on the element type, as well as on array_eq()
+ * on the array type, and we need those operations to have the
+ * same notion of equality that we're using otherwise.
+ *
+ * XXX this restriction is pretty annoying, considering the effort
+ * that's been put into the rest of the RI mechanisms to make them
+ * work with nondefault equality operators. In particular, it
+ * means that the cast-to-PK-datatype code path isn't useful for
+ * array-to-scalar references.
+ */
+ elemopclass = GetDefaultOpClass(fktype, BTREE_AM_OID);
+ if (!OidIsValid(elemopclass) ||
+ get_opclass_family(elemopclass) != opfamily)
+ {
+ /* Get the index opclass's name for the error message. */
+ char *opcname;
+
+ cla_ht = SearchSysCache1(CLAOID,
+ ObjectIdGetDatum(opclasses[i]));
+ if (!HeapTupleIsValid(cla_ht))
+ elog(ERROR, "cache lookup failed for opclass %u",
+ opclasses[i]);
+ cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
+ opcname = pstrdup(NameStr(cla_tup->opcname));
+ ReleaseSysCache(cla_ht);
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has element type %s which does not have a default btree operator class that's compatible with class \"%s\".",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktype),
+ opcname)));
+ }
+ }
+
+ /*
* There had better be a primary equality operator for the index.
* We'll use it for PK = PK comparisons.
*/
@@ -7265,14 +7373,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("foreign key constraint \"%s\" "
- "cannot be implemented",
- fkconstraint->conname),
- errdetail("Key columns \"%s\" and \"%s\" "
- "are of incompatible types: %s and %s.",
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key columns \"%s\" and \"%s\" are of incompatible types: %s and %s.",
strVal(list_nth(fkconstraint->fk_attrs, i)),
strVal(list_nth(fkconstraint->pk_attrs, i)),
- format_type_be(fktype),
+ format_type_be(fktypoid[i]),
format_type_be(pktype))));
if (old_check_ok)
@@ -7303,6 +7409,13 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
* We may assume that pg_constraint.conkey is not changing.
*/
old_fktype = attr->atttypid;
+ if (fkreftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ old_fktype = get_base_element_type(old_fktype);
+ /* this shouldn't happen ... */
+ if (!OidIsValid(old_fktype))
+ elog(ERROR, "old foreign key column is not an array");
+ }
new_fktype = fktype;
old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
&old_castfunc);
@@ -7345,7 +7458,6 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
new_castfunc == old_castfunc &&
(!IsPolymorphicType(pfeqop_right) ||
new_fktype == old_fktype));
-
}
pfeqoperators[i] = pfeqop;
@@ -7369,6 +7481,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
indexOid,
RelationGetRelid(pkrel),
pkattnum,
+ fkreftypes,
pfeqoperators,
ppeqoperators,
ffeqoperators,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index da0850bfd6..2151eea371 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -635,6 +635,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
@@ -1006,6 +1007,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
char fk_matchtype = FKCONSTR_MATCH_SIMPLE;
List *fk_attrs = NIL;
List *pk_attrs = NIL;
+ List *fk_reftypes = NIL;
StringInfoData buf;
int funcnum;
OldTriggerInfo *info = NULL;
@@ -1035,7 +1037,10 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
if (i % 2)
fk_attrs = lappend(fk_attrs, arg);
else
- pk_attrs = lappend(pk_attrs, arg);
+ {
+ pk_attrs = lappend(pk_attrs, arg);
+ fk_reftypes = lappend_int(fk_reftypes, FKCONSTR_REF_PLAIN);
+ }
}
/* Prepare description of constraint for use in messages */
@@ -1174,6 +1179,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
fkcon->conname = constr_name;
fkcon->fk_attrs = fk_attrs;
fkcon->pk_attrs = pk_attrs;
+ fkcon->fk_reftypes = fk_reftypes;
fkcon->fk_matchtype = fk_matchtype;
switch (info->funcoids[0])
{
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7ed16aeff4..8639854f4d 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -3092,6 +3092,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 72041693df..dc34cfe6bc 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2845,6 +2845,7 @@ _copyConstraint(const Constraint *from)
COPY_NODE_FIELD(pktable);
COPY_NODE_FIELD(fk_attrs);
COPY_NODE_FIELD(pk_attrs);
+ COPY_NODE_FIELD(fk_reftypes);
COPY_SCALAR_FIELD(fk_matchtype);
COPY_SCALAR_FIELD(fk_upd_action);
COPY_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 8d92c03633..8026f9f8c8 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2575,6 +2575,7 @@ _equalConstraint(const Constraint *a, const Constraint *b)
COMPARE_NODE_FIELD(pktable);
COMPARE_NODE_FIELD(fk_attrs);
COMPARE_NODE_FIELD(pk_attrs);
+ COMPARE_NODE_FIELD(fk_reftypes);
COMPARE_SCALAR_FIELD(fk_matchtype);
COMPARE_SCALAR_FIELD(fk_upd_action);
COMPARE_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 5ce3c7c599..916ccbf483 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3490,6 +3490,7 @@ _outConstraint(StringInfo str, const Constraint *node)
WRITE_NODE_FIELD(pktable);
WRITE_NODE_FIELD(fk_attrs);
WRITE_NODE_FIELD(pk_attrs);
+ WRITE_NODE_FIELD(fk_reftypes);
WRITE_CHAR_FIELD(fk_matchtype);
WRITE_CHAR_FIELD(fk_upd_action);
WRITE_CHAR_FIELD(fk_del_action);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7d0de99baf..a4c49bf78c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -126,6 +126,13 @@ typedef struct ImportQual
List *table_names;
} ImportQual;
+/* Private struct for the result of foreign_key_column_elem production */
+typedef struct FKColElem
+{
+ Node *name; /* name of the column (a String) */
+ char reftype; /* FKCONSTR_REF_xxx code */
+} FKColElem;
+
/* ConstraintAttributeSpec yields an integer bitmask of these flags: */
#define CAS_NOT_DEFERRABLE 0x01
#define CAS_DEFERRABLE 0x02
@@ -183,6 +190,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
static void SplitColQualList(List *qualList,
List **constraintList, CollateClause **collClause,
core_yyscan_t yyscanner);
+static void SplitFKColElems(List *fkcolelems, List **names, List **reftypes);
static void processCASbits(int cas_bits, int location, const char *constrType,
bool *deferrable, bool *initdeferred, bool *not_valid,
bool *no_inherit, core_yyscan_t yyscanner);
@@ -233,6 +241,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
A_Indices *aind;
ResTarget *target;
struct PrivTarget *privtarget;
+ struct FKColElem *fkcolelem;
AccessPriv *accesspriv;
struct ImportQual *importqual;
InsertStmt *istmt;
@@ -358,6 +367,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <accesspriv> privilege
%type <list> privileges privilege_list
%type <privtarget> privilege_target
+%type <fkcolelem> foreign_key_column_elem
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
%type <ival> defacl_privilege_target
@@ -392,7 +402,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
execute_param_clause using_clause returning_clause
opt_enum_val_list enum_val_list table_func_column_list
create_generic_options alter_generic_options
- relation_expr_list dostmt_opt_list
+ relation_expr_list dostmt_opt_list foreign_key_column_list
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
@@ -622,8 +632,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
DOUBLE_P DROP
- EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
- EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
+ EACH ELEMENT ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE
+ EVENT EXCEPT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
EXTENSION EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FILTER FIRST_P FLOAT_P FOLLOWING FOR
@@ -3409,8 +3419,10 @@ ColConstraintElem:
n->contype = CONSTR_FOREIGN;
n->location = @1;
n->pktable = $2;
+ /* fk_attrs will be filled in by parse analysis */
n->fk_attrs = NIL;
n->pk_attrs = $3;
+ n->fk_reftypes = list_make1_int(FKCONSTR_REF_PLAIN);
n->fk_matchtype = $4;
n->fk_upd_action = (char) ($5 >> 8);
n->fk_del_action = (char) ($5 & 0xFF);
@@ -3604,14 +3616,15 @@ ConstraintElem:
NULL, yyscanner);
$$ = (Node *)n;
}
- | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
- opt_column_list key_match key_actions ConstraintAttributeSpec
+ | FOREIGN KEY '(' foreign_key_column_list ')' REFERENCES
+ qualified_name opt_column_list key_match key_actions
+ ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_FOREIGN;
n->location = @1;
+ SplitFKColElems($4, &n->fk_attrs, &n->fk_reftypes);
n->pktable = $7;
- n->fk_attrs = $4;
n->pk_attrs = $8;
n->fk_matchtype = $9;
n->fk_upd_action = (char) ($10 >> 8);
@@ -3644,6 +3657,29 @@ columnElem: ColId
$$ = (Node *) makeString($1);
}
;
+ foreign_key_column_list:
+ foreign_key_column_elem
+ { $$ = list_make1($1); }
+ | foreign_key_column_list ',' foreign_key_column_elem
+ { $$ = lappend($1, $3); }
+ ;
+
+ foreign_key_column_elem:
+ ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($1);
+ n->reftype = FKCONSTR_REF_PLAIN;
+ $$ = n;
+ }
+ | EACH ELEMENT OF ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($4);
+ n->reftype = FKCONSTR_REF_EACH_ELEMENT;
+ $$ = n;
+ }
+ ;
key_match: MATCH FULL
{
@@ -14655,6 +14691,7 @@ unreserved_keyword:
| DOUBLE_P
| DROP
| EACH
+ | ELEMENT
| ENABLE_P
| ENCODING
| ENCRYPTED
@@ -15772,6 +15809,23 @@ SplitColQualList(List *qualList,
*constraintList = qualList;
}
+/* Split a list of FKColElem structs into separate name and reftype lists */
+static void
+SplitFKColElems(List *fkcolelems, List **names, List **reftypes)
+{
+ ListCell *lc;
+
+ *names = NIL;
+ *reftypes = NIL;
+ foreach(lc, fkcolelems)
+ {
+ FKColElem *fkcolelem = (FKColElem *) lfirst(lc);
+
+ *names = lappend(*names, fkcolelem->name);
+ *reftypes = lappend_int(*reftypes, fkcolelem->reftype);
+ }
+}
+
/*
* Process result of ConstraintAttributeSpec, and set appropriate bool flags
* in the output command node. Pass NULL for any flags the particular
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index e95cee1ebf..cf273e7c03 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1320,6 +1320,90 @@ select_common_type(ParseState *pstate, List *exprs, const char *context,
}
/*
+ * look at select_common_type(); in parse_coerce.c(src/backend/parser/parse_coerce.c)
+ *
+ * select_common_type_2args()
+ * Determine the common supertype of a list of input expressions.
+ * 'leftOid': left operand's type
+ * 'rightOid': left operand's type
+ */
+Oid select_common_type_2args(Oid leftOid, Oid rightOid)
+{
+ TYPCATEGORY leftcategory;
+ TYPCATEGORY rightcategory;
+ bool leftispreferred;
+ bool rightispreferred;
+
+ Assert(leftOid != InvalidOid);
+ Assert(rightOid != InvalidOid);
+
+ /*
+ * If input types are valid and exactly the same, just pick that type.
+ */
+ if (leftOid != UNKNOWNOID)
+ {
+ if (rightOid == leftOid)
+ return leftOid;
+ }
+
+ /*
+ * Nope, so set up for the full algorithm.
+ */
+ leftOid = getBaseType(leftOid);
+ rightOid = getBaseType(rightOid);
+ get_type_category_preferred(leftOid, &leftcategory, &leftispreferred);
+ get_type_category_preferred(rightOid, &rightcategory, &rightispreferred);
+
+ if (rightOid != UNKNOWNOID && rightOid != leftOid)
+ {
+ if (rightcategory != leftcategory)
+ {
+ /* both types in different categories? then not much hope... */
+ return InvalidOid;
+ }
+
+ if (can_coerce_type(1, &rightOid, &leftOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left */
+ if (!can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can not coerce to it implicitly right to left, thus coercion only works one way */
+ return leftOid;
+ }
+ else
+ {
+ /* coercion works both ways, then decide depending on preferred flag */
+ if (leftispreferred)
+ return leftOid;
+ else
+ rightOid;
+ }
+ }
+ else{
+ /* can not coerce to it implicitly right to left */
+ if (can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left, thus coercion only works one way */
+ return rightOid;
+ }
+ else{
+ /* can not coerce either way */
+ return InvalidOid;
+ }
+ }
+ }
+
+ /*
+ * If all the inputs were UNKNOWN type --- ie, unknown-type literals ---
+ * then resolve as type TEXT.
+ */
+ if (leftOid == UNKNOWNOID)
+ leftOid = TEXTOID;
+
+ return leftOid;
+}
+
+/*
* coerce_to_common_type()
* Coerce an expression to the given type.
*
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 20586797cc..7dc6bc3418 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -744,6 +744,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
* list of FK constraints to be processed later.
*/
constraint->fk_attrs = list_make1(makeString(column->colname));
+ /* grammar should have set fk_reftypes */
+ Assert(list_length(constraint->fk_reftypes) == 1);
cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
break;
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 34dadd6e19..b2db8baffd 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -4232,6 +4232,112 @@ arraycontained(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(result);
}
+/*
+ * array_contains_elem : checks an array for a spefific element
+ * adapted from array_contain_compare() for containment of a single element
+ */
+static bool
+array_contains_elem(AnyArrayType *array, Datum elem,
+ Oid collation, void **fn_extra)
+{
+ Oid arr_type = AARR_ELEMTYPE(array);
+ TypeCacheEntry *typentry;
+ int nelems;
+ int typlen;
+ bool typbyval;
+ char typalign;
+ int i;
+ array_iter it1;
+ FunctionCallInfoData locfcinfo;
+
+ /*
+ * We arrange to look up the equality function only once per series of
+ * calls, assuming the element type doesn't change underneath us. The
+ * typcache is used so that we have no memory leakage when being used as
+ * an index support function.
+ */
+ typentry = (TypeCacheEntry *)*fn_extra;
+ if (typentry == NULL ||
+ typentry->type_id != arr_type)
+ {
+ typentry = lookup_type_cache(arr_type,
+ TYPECACHE_EQ_OPR_FINFO);
+ if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(arr_type))));
+ *fn_extra = (void *)typentry;
+ }
+ typlen = typentry->typlen;
+ typbyval = typentry->typbyval;
+ typalign = typentry->typalign;
+
+ /*
+ * Apply the comparison operator to each pair of array elements.
+ */
+ InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+ collation, NULL, NULL);
+
+ /* Loop over source data */
+ nelems = ArrayGetNItems(AARR_NDIM(array), AARR_DIMS(array));
+ array_iter_setup(&it1, array);
+
+ for (i = 0; i < nelems; i++)
+ {
+ Datum elt1;
+ bool isnull;
+ bool oprresult;
+
+ /* Get element, checking for NULL */
+ elt1 = array_iter_next(&it1, &isnull, i, typlen, typbyval, typalign);
+
+ /*
+ * We assume that the comparison operator is strict, so a NULL can't
+ * match anything. XXX this diverges from the "NULL=NULL" behavior of
+ * array_eq, should we act like that?
+ */
+ if (isnull)
+ continue;
+
+ /*
+ * Apply the operator to the element pair
+ */
+ locfcinfo.arg[0] = elt1;
+ locfcinfo.arg[1] = elem;
+ locfcinfo.argnull[0] = false;
+ locfcinfo.argnull[1] = false;
+ locfcinfo.isnull = false;
+ oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+ if (oprresult)
+ return true;
+ }
+
+ return false;
+}
+
+Datum
+arraycontainselem(PG_FUNCTION_ARGS)
+{
+ AnyArrayType *array = PG_GETARG_ANY_ARRAY(0);
+ Datum elem = PG_GETARG_DATUM(1);
+ Oid collation = PG_GET_COLLATION();
+ bool result;
+
+ /* we don't need to check if the elem is null or if
+ the elem datatype and array datatype match since this
+ is handled within internal calls already (a property
+ of polymorphic functions) */
+
+ result = array_contains_elem(array, elem, collation,
+ &fcinfo->flinfo->fn_extra);
+
+ /* Avoid leaking memory when handed toasted input. */
+ AARR_FREE_IF_COPY(array, 0);
+
+ PG_RETURN_BOOL(result);
+}
+
/*-----------------------------------------------------------------------------
* Array iteration functions
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index c2891e6fa1..765da69d8e 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -118,9 +118,11 @@ typedef struct RI_ConstraintInfo
char confupdtype; /* foreign key's ON UPDATE action */
char confdeltype; /* foreign key's ON DELETE action */
char confmatchtype; /* foreign key's match type */
+ bool has_array; /* true if any reftype is EACH_ELEMENT */
int nkeys; /* number of key columns */
int16 pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */
int16 fk_attnums[RI_MAX_NUMKEYS]; /* attnums of referencing cols */
+ char fk_reftypes[RI_MAX_NUMKEYS]; /* reference semantics */
Oid pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = FK) */
Oid pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = PK) */
Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK = FK) */
@@ -204,7 +206,8 @@ static void ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype);
+ const char *rightop, Oid rightoptype,
+ char fkreftype);
static void ri_add_cast_to(StringInfo buf, Oid typid);
static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
static int ri_NullCheck(HeapTuple tup,
@@ -395,6 +398,7 @@ RI_FKey_check(TriggerData *trigdata)
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
StringInfoData querybuf;
+ StringInfoData countbuf;
char pkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
char paramname[16];
@@ -407,12 +411,22 @@ RI_FKey_check(TriggerData *trigdata)
* FOR KEY SHARE OF x
* The type id's for the $ parameters are those of the
* corresponding FK attributes.
+ *
+ * In case of an array ELEMENT foreign key, the previous query is used
+ * to count the number of matching rows and see if every combination
+ * is actually referenced.
+ * The wrapping query is
+ * SELECT 1 WHERE
+ * (SELECT count(DISTINCT y) FROM unnest($1) y)
+ * = (SELECT count(*) FROM (<QUERY>) z)
* ----------
*/
initStringInfo(&querybuf);
quoteRelationName(pkrelname, pk_rel);
appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
querysep = "WHERE";
+ initStringInfo(&countbuf);
+ appendStringInfo(&countbuf, "SELECT 1 WHERE ");
for (i = 0; i < riinfo->nkeys; i++)
{
Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
@@ -421,18 +435,41 @@ RI_FKey_check(TriggerData *trigdata)
quoteOneName(attname,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
sprintf(paramname, "$%d", i + 1);
+
+ /*
+ * In case of an array ELEMENT foreign key, we check that each
+ * distinct non-null value in the array is present in the PK
+ * table.
+ */
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&countbuf,
+ "(SELECT pg_catalog.count(DISTINCT y) FROM pg_catalog.unnest(%s) y)",
+ paramname);
+
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pf_eq_oprs[i],
- paramname, fk_type);
+ paramname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = fk_type;
}
appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
- /* Prepare and save the plan */
- qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
- &qkey, fk_rel, pk_rel, true);
+ if (riinfo->has_array)
+ {
+ appendStringInfo(&countbuf,
+ " OPERATOR(pg_catalog.=) (SELECT pg_catalog.count(*) FROM (%s) z)",
+ querybuf.data);
+
+ /* Prepare and save the plan for array ELEMENT foreign keys */
+ qplan = ri_PlanCheck(countbuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
+ }
+ else
+ /* Prepare and save the plan */
+ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
}
/*
@@ -559,7 +596,8 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pp_eq_oprs[i],
- paramname, pk_type);
+ paramname, pk_type,
+ FKCONSTR_REF_PLAIN);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -751,7 +789,8 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -974,7 +1013,8 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1130,7 +1170,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1309,7 +1350,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1475,7 +1517,8 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1651,7 +1694,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1817,7 +1861,8 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2008,7 +2053,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2327,6 +2373,14 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
* For MATCH FULL:
* (fk.keycol1 IS NOT NULL [OR ...])
*
+ * In case of an array ELEMENT column, relname is replaced with the
+ * following subquery:
+ *
+ * SELECT unnest("keycol1") k1, "keycol1" ak1 [, ...]
+ * FROM ONLY "public"."fk"
+ *
+ * where all the columns are renamed in order to prevent name collisions.
+ *
* We attach COLLATE clauses to the operators when comparing columns
* that have different collations.
*----------
@@ -2338,15 +2392,46 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
{
quoteOneName(fkattname,
RIAttName(fk_rel, riinfo->fk_attnums[i]));
- appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
+ if (riinfo->has_array)
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%sfk.ak%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.k%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
sep = ", ";
}
quoteRelationName(pkrelname, pk_rel);
quoteRelationName(fkrelname, fk_rel);
- appendStringInfo(&querybuf,
- " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
- fkrelname, pkrelname);
+
+ if (riinfo->has_array)
+ {
+ sep = "";
+ appendStringInfo(&querybuf,
+ " FROM (SELECT ");
+ for (i = 0; i < riinfo->nkeys; i++)
+ {
+ quoteOneName(fkattname,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%spg_catalog.unnest(%s) k%d, %s ak%d",
+ sep, fkattname, i + 1, fkattname, i + 1);
+ else
+ appendStringInfo(&querybuf, "%s%s k%d", sep, fkattname,
+ i + 1);
+ sep = ", ";
+ }
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s) fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
+ }
+ else
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
strcpy(pkattname, "pk.");
strcpy(fkattname, "fk.");
@@ -2360,12 +2445,16 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
quoteOneName(pkattname + 3,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
- quoteOneName(fkattname + 3,
- RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname + 3, "k%d", i + 1);
+ else
+ quoteOneName(fkattname + 3,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
ri_GenerateQual(&querybuf, sep,
pkattname, pk_type,
riinfo->pf_eq_oprs[i],
- fkattname, fk_type);
+ fkattname, fk_type,
+ FKCONSTR_REF_PLAIN);
if (pk_coll != fk_coll)
ri_GenerateQualCollation(&querybuf, pk_coll);
sep = "AND";
@@ -2381,7 +2470,10 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
sep = "";
for (i = 0; i < riinfo->nkeys; i++)
{
- quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname, "k%d", i + 1);
+ else
+ quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
appendStringInfo(&querybuf,
"%sfk.%s IS NOT NULL",
sep, fkattname);
@@ -2557,20 +2649,23 @@ quoteRelationName(char *buffer, Relation rel)
/*
* ri_GenerateQual --- generate a WHERE clause equating two variables
*
- * The idea is to append " sep leftop op rightop" to buf. The complexity
- * comes from needing to be sure that the parser will select the desired
- * operator. We always name the operator using OPERATOR(schema.op) syntax
- * (readability isn't a big priority here), so as to avoid search-path
- * uncertainties. We have to emit casts too, if either input isn't already
- * the input type of the operator; else we are at the mercy of the parser's
- * heuristics for ambiguous-operator resolution.
- */
+ * The idea is to append " sep leftop op rightop" to buf, or if fkreftype is
+ * FKCONSTR_REF_EACH_ELEMENT, append " sep rightop @>> leftop" to buf.
+ *
+ * The complexity comes from needing to be sure that the parser will select
+ * the desired operator. We always name the operator using
+ * OPERATOR(schema.op) syntax (readability isn't a big priority here), so as
+ * to avoid search-path uncertainties. We have to emit casts too, if either
+ * input isn't already the input type of the operator; else we are at the
+ * mercy of the parser's heuristics for ambiguous-operator resolution.
+ */
static void
ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype)
+ const char *rightop, Oid rightoptype,
+ char fkreftype)
{
HeapTuple opertup;
Form_pg_operator operform;
@@ -2586,14 +2681,81 @@ ri_GenerateQual(StringInfo buf,
nspname = get_namespace_name(operform->oprnamespace);
- appendStringInfo(buf, " %s %s", sep, leftop);
- if (leftoptype != operform->oprleft)
- ri_add_cast_to(buf, operform->oprleft);
- appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
- appendStringInfoString(buf, oprname);
- appendStringInfo(buf, ") %s", rightop);
- if (rightoptype != operform->oprright)
- ri_add_cast_to(buf, operform->oprright);
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ Oid oprright;
+ Oid oprleft;
+ Oid oprcommon;
+
+ /*
+ * since the @>> operator has the array as
+ * the first operand and the element as the second
+ * operand we switch their places in the query
+ * i.e opright is oprleft and vice versar
+ */
+
+ /*
+ * we first need to get the array types and decide
+ * the more appropriate one
+ */
+
+ /* get array type of refrenced element*/
+ oprright = get_array_type(operform->oprleft);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprleft))));
+
+ /* get array type of refrencing element*/
+ oprleft = get_array_type(operform->oprright);
+ if (!OidIsValid(oprleft))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprright))));
+
+ /* compare the two array types and try to find
+ a common supertype */
+ oprcommon = select_common_type_2args(oprleft, oprright);
+ if (!OidIsValid(oprcommon))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find common type between %s and %s",
+ format_type_be(oprleft), format_type_be(oprright))));
+
+ oprright = get_base_element_type(oprcommon);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find element type for data type %s",
+ format_type_be(oprcommon))));
+
+ appendStringInfo(buf, " %s %s", sep, rightop);
+ if (oprleft != oprcommon)
+ ri_add_cast_to(buf, oprcommon);
+
+ appendStringInfo(buf, " OPERATOR(pg_catalog.@>>) ");
+
+ appendStringInfoString(buf, leftop);
+ if (operform->oprleft != oprright)
+ ri_add_cast_to(buf, oprright);
+ }
+ else
+ {
+ appendStringInfo(buf, " %s %s", sep, leftop);
+
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+
+ appendStringInfo(buf, " OPERATOR(%s.%s) ",
+ quote_identifier(nspname), oprname);
+
+ appendStringInfoString(buf, rightop);
+
+ if (rightoptype != operform->oprright)
+ ri_add_cast_to(buf, operform->oprright);
+ }
ReleaseSysCache(opertup);
}
@@ -2801,6 +2963,7 @@ ri_LoadConstraintInfo(Oid constraintOid)
bool isNull;
ArrayType *arr;
int numkeys;
+ int i;
/*
* On the first call initialize the hashtable
@@ -2879,6 +3042,20 @@ ri_LoadConstraintInfo(Oid constraintOid)
pfree(arr); /* free de-toasted copy, if any */
adatum = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype, &isNull);
+ if (isNull)
+ elog(ERROR, "null confreftype for constraint %u", constraintOid);
+ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
+ if (ARR_NDIM(arr) != 1 ||
+ ARR_DIMS(arr)[0] != numkeys ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != CHAROID)
+ elog(ERROR, "confreftype is not a 1-D char array");
+ memcpy(riinfo->fk_reftypes, ARR_DATA_PTR(arr), numkeys * sizeof(char));
+ if ((Pointer) arr != DatumGetPointer(adatum))
+ pfree(arr); /* free de-toasted copy, if any */
+
+ adatum = SysCacheGetAttr(CONSTROID, tup,
Anum_pg_constraint_conpfeqop, &isNull);
if (isNull)
elog(ERROR, "null conpfeqop for constraint %u", constraintOid);
@@ -2921,6 +3098,24 @@ ri_LoadConstraintInfo(Oid constraintOid)
if ((Pointer) arr != DatumGetPointer(adatum))
pfree(arr); /* free de-toasted copy, if any */
+ /*
+ * Fix up some stuff for array foreign keys. We need a has_array flag
+ * indicating whether there's an array foreign key, and we want to set
+ * ff_eq_oprs[i] to array_eq() for array columns, because that's what
+ * makes sense for ri_KeysEqual, and we have no other use for ff_eq_oprs
+ * in this module. (If we did, substituting the array comparator at the
+ * call point in ri_KeysEqual might be more appropriate.)
+ */
+ riinfo->has_array = false;
+ for (i = 0; i < numkeys; i++)
+ {
+ if (riinfo->fk_reftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ riinfo->has_array = true;
+ riinfo->ff_eq_oprs[i] = ARRAY_EQ_OP;
+ }
+ }
+
ReleaseSysCache(tup);
/*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 43646d2c4f..1a5143300f 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -314,6 +314,9 @@ static char *pg_get_viewdef_worker(Oid viewoid,
static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
static void decompile_column_index_array(Datum column_index_array, Oid relId,
StringInfo buf);
+static void decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf);
static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
const Oid *excludeOps,
@@ -1882,7 +1885,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
{
case CONSTRAINT_FOREIGN:
{
- Datum val;
+ Datum colindexes;
+ Datum reftypes;
bool isnull;
const char *string;
@@ -1890,13 +1894,21 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
appendStringInfoString(&buf, "FOREIGN KEY (");
/* Fetch and build referencing-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_conkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_conkey,
+ &isnull);
if (isnull)
elog(ERROR, "null conkey for constraint %u",
constraintId);
+ reftypes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype,
+ &isnull);
+ if (isnull)
+ elog(ERROR, "null confreftype for constraint %u",
+ constraintId);
- decompile_column_index_array(val, conForm->conrelid, &buf);
+ decompile_fk_column_index_array(colindexes, reftypes,
+ conForm->conrelid, &buf);
/* add foreign relation name */
appendStringInfo(&buf, ") REFERENCES %s(",
@@ -1904,13 +1916,15 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
NIL));
/* Fetch and build referenced-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_confkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confkey,
+ &isnull);
if (isnull)
elog(ERROR, "null confkey for constraint %u",
constraintId);
- decompile_column_index_array(val, conForm->confrelid, &buf);
+ decompile_column_index_array(colindexes,
+ conForm->confrelid, &buf);
appendStringInfoChar(&buf, ')');
@@ -2185,6 +2199,66 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
}
}
+ /*
+ * Convert an int16[] Datum and a char[] Datum into a comma-separated
+ * list of column names for the indicated relation, prefixed by appropriate
+ * keywords depending on the foreign key reference semantics indicated by
+ * the char[] entries. Append the text to buf.
+ */
+ static void
+ decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf)
+ {
+ Datum *keys;
+ int nKeys;
+ Datum *reftypes;
+ int nReftypes;
+ int j;
+
+ /* Extract data from array of int16 */
+ deconstruct_array(DatumGetArrayTypeP(column_index_array),
+ INT2OID, sizeof(int16), true, 's',
+ &keys, NULL, &nKeys);
+
+ /* Extract data from array of char */
+ deconstruct_array(DatumGetArrayTypeP(fk_reftype_array),
+ CHAROID, sizeof(char), true, 'c',
+ &reftypes, NULL, &nReftypes);
+
+ if (nKeys != nReftypes)
+ elog(ERROR, "wrong confreftype cardinality");
+
+ for (j = 0; j < nKeys; j++)
+ {
+ char *colName;
+ const char *prefix;
+
+ colName = get_relid_attribute_name(relId, DatumGetInt16(keys[j]));
+
+ switch (DatumGetChar(reftypes[j]))
+ {
+ case FKCONSTR_REF_PLAIN:
+ prefix = "";
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ prefix = "EACH ELEMENT OF ";
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d",
+ (int) DatumGetChar(reftypes[j]));
+ prefix = NULL; /* keep compiler quiet */
+ break;
+ }
+
+ if (j == 0)
+ appendStringInfo(buf, "%s%s", prefix,
+ quote_identifier(colName));
+ else
+ appendStringInfo(buf, ", %s%s", prefix,
+ quote_identifier(colName));
+ }
+}
/* ----------
* get_expr - Decompile an expression tree
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index f850be490a..4e304a9fe4 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -689,6 +689,7 @@ DATA(insert ( 2745 2277 2277 1 s 2750 2742 0 ));
DATA(insert ( 2745 2277 2277 2 s 2751 2742 0 ));
DATA(insert ( 2745 2277 2277 3 s 2752 2742 0 ));
DATA(insert ( 2745 2277 2277 4 s 1070 2742 0 ));
+DATA(insert ( 2745 2277 2283 5 s 6108 2742 0 ));
/*
* btree enum_ops
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index ec035d8434..a6b296d4a0 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -104,8 +104,16 @@ CATALOG(pg_constraint,2606)
int16 confkey[1];
/*
+ * If a foreign key, the reference semantics for each column
+ */
+ char confreftype[1];
+
+ /*
* If a foreign key, the OIDs of the PK = FK equality operators for each
* column of the constraint
+ *
+ * Note: for array foreign keys, all these operators are for the array's
+ * element type.
*/
Oid conpfeqop[1];
@@ -150,7 +158,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* compiler constants for pg_constraint
* ----------------
*/
-#define Natts_pg_constraint 24
+#define Natts_pg_constraint 25
#define Anum_pg_constraint_conname 1
#define Anum_pg_constraint_connamespace 2
#define Anum_pg_constraint_contype 3
@@ -169,12 +177,13 @@ typedef FormData_pg_constraint *Form_pg_constraint;
#define Anum_pg_constraint_connoinherit 16
#define Anum_pg_constraint_conkey 17
#define Anum_pg_constraint_confkey 18
-#define Anum_pg_constraint_conpfeqop 19
-#define Anum_pg_constraint_conppeqop 20
-#define Anum_pg_constraint_conffeqop 21
-#define Anum_pg_constraint_conexclop 22
-#define Anum_pg_constraint_conbin 23
-#define Anum_pg_constraint_consrc 24
+#define Anum_pg_constraint_confreftype 19
+#define Anum_pg_constraint_conpfeqop 20
+#define Anum_pg_constraint_conppeqop 21
+#define Anum_pg_constraint_conffeqop 22
+#define Anum_pg_constraint_conexclop 23
+#define Anum_pg_constraint_conbin 24
+#define Anum_pg_constraint_consrc 25
/* ----------------
* initial contents of pg_constraint
@@ -195,7 +204,9 @@ typedef FormData_pg_constraint *Form_pg_constraint;
/*
* Valid values for confupdtype and confdeltype are the FKCONSTR_ACTION_xxx
* constants defined in parsenodes.h. Valid values for confmatchtype are
- * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h.
+ * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h. Valid values
+ * for elements of confreftype[] are the FKCONSTR_REF_xxx constants defined
+ * in parsenodes.h.
*/
#endif /* PG_CONSTRAINT_H */
diff --git a/src/include/catalog/pg_constraint_fn.h b/src/include/catalog/pg_constraint_fn.h
index a4c46897ed..9805ed220a 100644
--- a/src/include/catalog/pg_constraint_fn.h
+++ b/src/include/catalog/pg_constraint_fn.h
@@ -40,6 +40,7 @@ extern Oid CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index ffabc2003b..8a9d616d29 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1570,6 +1570,8 @@ DESCR("contains");
DATA(insert OID = 2752 ( "<@" PGNSP PGUID b f f 2277 2277 16 2751 0 arraycontained arraycontsel arraycontjoinsel ));
DESCR("is contained by");
#define OID_ARRAY_CONTAINED_OP 2752
+DATA(insert OID = 6108 ( "@>>" PGNSP PGUID b f f 2277 2283 16 0 0 arraycontainselem arraycontsel arraycontjoinsel ));
+DESCR("containselem");
/* capturing operators to preserve pre-8.3 behavior of text concatenation */
DATA(insert OID = 2779 ( "||" PGNSP PGUID b f f 25 2776 25 0 0 textanycat - - ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 8b33b4e0ea..01b207d363 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4308,6 +4308,7 @@ DESCR("GIN array support (obsolete)");
DATA(insert OID = 2747 ( arrayoverlap PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arrayoverlap _null_ _null_ _null_ ));
DATA(insert OID = 2748 ( arraycontains PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arraycontains _null_ _null_ _null_ ));
DATA(insert OID = 2749 ( arraycontained PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ _null_ arraycontained _null_ _null_ _null_ ));
+DATA(insert OID = 6109 ( arraycontainselem PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "2277 2283" _null_ _null_ _null_ _null_ _null_ arraycontainselem _null_ _null_ _null_ ));
/* BRIN minmax */
DATA(insert OID = 3383 ( brin_minmax_opcinfo PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2281 "2281" _null_ _null_ _null_ _null_ _null_ brin_minmax_opcinfo _null_ _null_ _null_ ));
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 5f2a4a75da..77c31c9b48 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2067,6 +2067,10 @@ typedef enum ConstrType /* types of constraints */
#define FKCONSTR_MATCH_PARTIAL 'p'
#define FKCONSTR_MATCH_SIMPLE 's'
+ /* Foreign key column reference semantics codes */
+ #define FKCONSTR_REF_PLAIN 'p'
+ #define FKCONSTR_REF_EACH_ELEMENT 'e'
+
typedef struct Constraint
{
NodeTag type;
@@ -2102,6 +2106,7 @@ typedef struct Constraint
RangeVar *pktable; /* Primary key table */
List *fk_attrs; /* Attributes of foreign key */
List *pk_attrs; /* Corresponding attrs in PK table */
+ List *fk_reftypes; /* Per-column reference semantics (int List) */
char fk_matchtype; /* FULL, PARTIAL, SIMPLE */
char fk_upd_action; /* ON UPDATE action */
char fk_del_action; /* ON DELETE action */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f50e45e886..d3f4803006 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -140,6 +140,7 @@ PG_KEYWORD("domain", DOMAIN_P, UNRESERVED_KEYWORD)
PG_KEYWORD("double", DOUBLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD)
PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD)
+PG_KEYWORD("element", ELEMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("else", ELSE, RESERVED_KEYWORD)
PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index 06f65293cb..f5bad57b70 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -68,6 +68,7 @@ extern int parser_coercion_errposition(ParseState *pstate,
extern Oid select_common_type(ParseState *pstate, List *exprs,
const char *context, Node **which_expr);
+extern Oid select_common_type_2args(Oid ptype, Oid ntype);
extern Node *coerce_to_common_type(ParseState *pstate, Node *node,
Oid targetTypeId,
const char *context);
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index c730563f03..0a9eff4d04 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -737,6 +737,17 @@ SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(6 rows)
+SELECT * FROM array_op_test WHERE i @>> 32 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+ 98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
+ 100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
+(6 rows)
+
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -761,6 +772,19 @@ SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
(8 rows)
+SELECT * FROM array_op_test WHERE i @>> 17 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
+ 53 | {38,17} | {AAAAAAAAAAA21658}
+ 65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+(8 rows)
+
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -942,6 +966,11 @@ SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
-------+---+---
(0 rows)
+SELECT * FROM array_op_test WHERE i @>> NULL ORDER BY seqno;
+ seqno | i | t
+-------+---+---
+(0 rows)
+
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
seqno | i | t
-------+---+---
@@ -953,6 +982,15 @@ SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
101 | {} | {}
(1 row)
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAA72908' ORDER BY seqno;
+ seqno | i | t
+-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
+ 22 | {11,6,56,62,53,30} | {AAAAAAAA72908}
+ 45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}
+ 72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+(4 rows)
+
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
seqno | i | t
-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
@@ -971,6 +1009,14 @@ SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
(4 rows)
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAAAA646' ORDER BY seqno;
+ seqno | i | t
+-------+------------------+--------------------------------------------------------------------
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+ 96 | {23,97,43} | {AAAAAAAAAA646,A87088}
+(3 rows)
+
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
seqno | i | t
-------+------------------+--------------------------------------------------------------------
diff --git a/src/test/regress/expected/element_foreign_key.out b/src/test/regress/expected/element_foreign_key.out
new file mode 100644
index 0000000000..2d19fa6abd
--- /dev/null
+++ b/src/test/regress/expected/element_foreign_key.out
@@ -0,0 +1,703 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fkarray"
+DETAIL: Key (ftest1)=({10,1}) is not present in table "pktableforarray".
+DROP TABLE FKTABLEFORARRAY;
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4,6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL,4,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+ERROR: insert or update on table "fktableforarraymdim" violates foreign key constraint "fktableforarraymdim_ftest1_fkey"
+DETAIL: Key (ftest1)=({{1,2},{6,NULL}}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+ERROR: null value in column "ftest1" violates not-null constraint
+DETAIL: Failing row contains (null, 21).
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+ {1} | 4
+(11 rows)
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({D}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({A,B,D}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(A) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(B) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ERROR: foreign key constraint "fktableforarray_ftest1_fkey" cannot be implemented
+DETAIL: Key column "ftest1" has element type integer which does not have a default btree operator class that's compatible with class "float8_ops".
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(A, {A,B,C}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(B, {A}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Test ELEMENT foreign keys with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,99)"}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,1)","(2010,1)"}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2010,99)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2011,1)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({0,1}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({2,1}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(2, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(20, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+ERROR: foreign keys support only one array column
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: foreign keys support only one array column
+DROP TABLE F1;
+-- Cleanup
+DROP TABLE DIM1;
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "x2_x1_fkey"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "fk_const"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+DROP TABLE x1;
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{100,100,100},{NULL,NULL,20},{7,8,10}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
+-- Check GIN index
+-- Define PKTABLEFORARRAYGIN
+CREATE TABLE PKTABLEFORARRAYGIN ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAYGIN
+INSERT INTO PKTABLEFORARRAYGIN VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (5, 'Test5');
+-- Define FKTABLEFORARRAYGIN
+CREATE TABLE FKTABLEFORARRAYGIN ( ftest1 int[],
+ ftest2 int PRIMARY KEY,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYGIN
+ ON DELETE NO ACTION ON UPDATE NO ACTION);
+-- -- Create index on FKTABLEFORARRAYGIN
+CREATE INDEX ON FKTABLEFORARRAYGIN USING gin (ftest1 array_ops);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,5,3}', 10);
+-- Try using the indexable operator
+SELECT * FROM FKTABLEFORARRAYGIN WHERE ftest1 @>> 5;
+ ftest1 | ftest2
+-------------+--------
+ {5} | 1
+ {3,5,2,5} | 3
+ {3,5,4,1,3} | 5
+ {5,1} | 7
+ {3,4,5,3} | 10
+(5 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYGIN;
+DROP TABLE PKTABLEFORARRAYGIN;
+-- Check DELECTE CASCADE ACTION
+CREATE TABLE PKTABLEFORARRAYDELETE ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAYDELETE
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (5, 'Test5');
+-- Define FKTABLEFORARRAYDELETE
+CREATE TABLE FKTABLEFORARRAYDELETE ( ftest1 int[],
+ ftest2 int,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYDELETE
+ ON DELETE CASCADE ON UPDATE NO ACTION );
+-- Create index on FKTABLEFORARRAYDELETE
+CREATE INDEX ON FKTABLEFORARRAYDELETE USING gin (ftest1);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,5,3}', 10);
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+ ftest1 | ftest2
+-------------+--------
+ {5} | 1
+ {3,2} | 2
+ {3,5,2,5} | 3
+ {3,4,4} | 4
+ {3,5,4,1,3} | 5
+ {1} | 6
+ {5,1} | 7
+ {2,1,2,4,1} | 8
+ {4,2} | 9
+ {3,4,5,3} | 10
+(10 rows)
+
+-- Check FKTABLE for target rows
+SELECT * FROM FKTABLEFORARRAYDELETE WHERE ftest1 @>> 5;
+ ftest1 | ftest2
+-------------+--------
+ {5} | 1
+ {3,5,2,5} | 3
+ {3,5,4,1,3} | 5
+ {5,1} | 7
+ {3,4,5,3} | 10
+(5 rows)
+
+-- Perfrorm delete
+DELETE FROM PKTABLEFORARRAYDELETE WHERE ptest1 = 5;
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+ ftest1 | ftest2
+-------------+--------
+ {3,2} | 2
+ {3,4,4} | 4
+ {1} | 6
+ {2,1,2,4,1} | 8
+ {4,2} | 9
+(5 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYDELETE;
+DROP TABLE PKTABLEFORARRAYDELETE;
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index fcf8bd7565..25ef369951 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1811,6 +1811,7 @@ ORDER BY 1, 2, 3;
2742 | 2 | @@@
2742 | 3 | <@
2742 | 4 | =
+ 2742 | 5 | @>>
2742 | 7 | @>
2742 | 9 | ?
2742 | 10 | ?|
@@ -1878,7 +1879,7 @@ ORDER BY 1, 2, 3;
4000 | 25 | <<=
4000 | 26 | >>
4000 | 27 | >>=
-(121 rows)
+(122 rows)
-- Check that all opclass search operators have selectivity estimators.
-- This is not absolutely required, but it seems a reasonable thing
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index eefdeeacae..d88ff3772c 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -103,7 +103,7 @@ test: publication subscription
# ----------
# Another group of parallel tests
# ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
+test: select_views portals_p2 foreign_key element_foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
# ----------
# Another group of parallel tests
# NB: temp.sql does a reconnect which transiently uses 2 connections,
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 76b0de30a7..9cb8638b4e 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -140,6 +140,7 @@ test: amutils
test: select_views
test: portals_p2
test: foreign_key
+test: element_foreign_key
test: cluster
test: dependency
test: guc
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 25dd4e2c6d..f6fb507bdd 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -313,8 +313,10 @@ SELECT ARRAY[0,0] || ARRAY[1,1] || ARRAY[2,2] AS "{0,0,1,1,2,2}";
SELECT 0 || ARRAY[1,2] || 3 AS "{0,1,2,3}";
SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> 32 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> 17 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{32,17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32,17}' ORDER BY seqno;
@@ -325,11 +327,14 @@ SELECT * FROM array_op_test WHERE i && '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> NULL ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAA72908' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAAAA646' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno;
diff --git a/src/test/regress/sql/element_foreign_key.sql b/src/test/regress/sql/element_foreign_key.sql
new file mode 100644
index 0000000000..7c0e84e2a5
--- /dev/null
+++ b/src/test/regress/sql/element_foreign_key.sql
@@ -0,0 +1,549 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Test ELEMENT foreign keys with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+
+
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+
+
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+-- Cleanup
+DROP TABLE DIM1;
+
+
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+DROP TABLE x2;
+DROP TABLE x1;
+
+
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+DROP TABLE F1;
+
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+DROP TABLE F1;
+
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
+
+-- Check GIN index
+-- Define PKTABLEFORARRAYGIN
+CREATE TABLE PKTABLEFORARRAYGIN ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAYGIN
+INSERT INTO PKTABLEFORARRAYGIN VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (5, 'Test5');
+
+-- Define FKTABLEFORARRAYGIN
+CREATE TABLE FKTABLEFORARRAYGIN ( ftest1 int[],
+ ftest2 int PRIMARY KEY,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYGIN
+ ON DELETE NO ACTION ON UPDATE NO ACTION);
+
+-- -- Create index on FKTABLEFORARRAYGIN
+CREATE INDEX ON FKTABLEFORARRAYGIN USING gin (ftest1 array_ops);
+
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,5,3}', 10);
+
+-- Try using the indexable operator
+SELECT * FROM FKTABLEFORARRAYGIN WHERE ftest1 @>> 5;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYGIN;
+DROP TABLE PKTABLEFORARRAYGIN;
+
+-- Check DELECTE CASCADE ACTION
+CREATE TABLE PKTABLEFORARRAYDELETE ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAYDELETE
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (5, 'Test5');
+
+-- Define FKTABLEFORARRAYDELETE
+CREATE TABLE FKTABLEFORARRAYDELETE ( ftest1 int[],
+ ftest2 int,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYDELETE
+ ON DELETE CASCADE ON UPDATE NO ACTION );
+
+-- Create index on FKTABLEFORARRAYDELETE
+CREATE INDEX ON FKTABLEFORARRAYDELETE USING gin (ftest1);
+
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,5,3}', 10);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+
+-- Check FKTABLE for target rows
+SELECT * FROM FKTABLEFORARRAYDELETE WHERE ftest1 @>> 5;
+
+-- Perfrorm delete
+DELETE FROM PKTABLEFORARRAYDELETE WHERE ptest1 = 5;
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYDELETE;
+DROP TABLE PKTABLEFORARRAYDELETE;
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-09-13 16:41 ` Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-09-13 16:41 UTC (permalink / raw)
To: Alexander Korotkov <aekorotkov@gmail.com>; Stephen Frost <sfrost@snowman.net>; David Steele <david@pgmasters.net>; Alvaro Herrera <alvherre@2ndquadrant.com>; andreas@proxel.se; +Cc: Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; pgsql-hackers-owner@postgresql.org, Tom Lane <tgl@sss.pgh.pa.us>; Hans-Jürgen Schönig <hs@cybertec.at>
As pointed out by the CI, created by Thomas Munro, the btree_gin regression
tests fail when running "make check-world"
https://travis-ci.org/postgresql-cfbot/postgresql/builds/274732512
The full backtrace can be found here: https://pastebin.com/5q3SG8kV.
The error originates from src/backend/access/gin/ginscan.c:182, where the
following if condition if (strategy == 5 && nQueryValues == 1) returns
false postivies .
The if condition is necessary since it decides whether "the queryKey" variable
is a single element or the first cell of an array. The logic behind the if
condition is as follows: since we defined GinContainsElemStrategy to be
strategy number 5. are sure the strategy is GinContainsElemStrategy.
The if condition needs to be more precise but can't find a common attribute
to check within the ginFillScanKey() function.
If someone can take a look. I can use some help with this.
I also worked in another direction. Since there was a lot of modifications
in the code to support taking an element instead of an array and this is
the source of many conflicts. I tried going back to the old RI query:
SELECT 1 FROM ONLY fktable x WHERE ARRAY[$1] <@ fkcol FOR SHARE OF x;
instead of
SELECT 1 FROM ONLY fktable x WHERE $1 @> fkcol FOR SHARE OF x;
We were worried about the decrease in performance caused by creating a new
array with every check So I put it to the test and here are the results:
https://pastebin.com/carj9Bur
note: v5 is with SELECT 1 FROM ONLY fktable x WHERE $1 @> fkcol FOR SHARE
OF x;
and v5.1 is with SELECT 1 FROM ONLY fktable x WHERE ARRAY[$1] <@ fkcol FOR
SHARE OF x;
As expected it led to a decrease in performance, but I think it is
forgivable.
Now all the contrib regression tests pass.
To summarise, we can fix the old if condition or neglect the decrease in
performance encountered when going back to the old RI query.
Best Regards,
Mark Rofail
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
Array-ELEMENT-foreign-key-v5.1.patch (text/x-patch, 124.5K) download
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 9af77c1f5a..67c1490bda 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2322,7 +2322,17 @@ SCRAM-SHA-256$<replaceable><iteration count></>:<replaceable><salt><
</row>
<row>
- <entry><structfield>conpfeqop</structfield></entry>
+ <entry><structfield>confreftype</structfield></entry>
+ <entry><type>char[]</type></entry>
+ <entry></entry>
+ <entry>If a foreign key, the reference semantics for each column:
+ <literal>p</> = plain (simple equality),
+ <literal>e</> = each element of referencing array must have a match
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>conpfeqop</structfield></entry>
<entry><type>oid[]</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</></entry>
<entry>If a foreign key, list of the equality operators for PK = FK comparisons</entry>
@@ -2367,6 +2377,12 @@ SCRAM-SHA-256$<replaceable><iteration count></>:<replaceable><salt><
</table>
<para>
+ When <structfield>confreftype</structfield> indicates array-vs-scalar
+ foreign key reference semantics, the equality operators listed in
+ <structfield>conpfeqop</structfield> etc are for the array's element type.
+ </para>
+
+ <para>
In the case of an exclusion constraint, <structfield>conkey</structfield>
is only useful for constraint elements that are simple column references.
For other cases, a zero appears in <structfield>conkey</structfield>
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index b05a9c2150..75da196334 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -882,6 +882,111 @@ CREATE TABLE order_items (
</para>
</sect2>
+ <sect2 id="ddl-constraints-element-fk">
+ <title>Array ELEMENT Foreign Keys</title>
+
+ <indexterm>
+ <primary>ELEMENT foreign key</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>Array ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>referential integrity</primary>
+ </indexterm>
+
+ <para>
+ Another option you have with foreign keys is to use a
+ referencing column which is an array of elements with
+ the same type (or a compatible one) as the referenced
+ column in the related table. This feature is called
+ <firstterm>array element foreign key</firstterm> and is implemented
+ in PostgreSQL with <firstterm>ELEMENT foreign key constraints</firstterm>,
+ as described in the following example:
+
+<programlisting>
+CREATE TABLE drivers (
+ driver_id integer PRIMARY KEY,
+ first_name text,
+ last_name text,
+ ...
+);
+
+CREATE TABLE races (
+ race_id integer PRIMARY KEY,
+ title text,
+ race_day DATE,
+ ...
+ final_positions integer[] <emphasis>ELEMENT REFERENCES drivers</emphasis>
+);
+</programlisting>
+
+ The above example uses an array (<literal>final_positions</literal>)
+ to store the results of a race: for each of its elements
+ a referential integrity check is enforced on the
+ <literal>drivers</literal> table.
+ Note that <literal>ELEMENT REFERENCES</literal> is an extension
+ of PostgreSQL and it is not included in the SQL standard.
+ </para>
+
+ <para>
+ Even though the most common use case for array <literal>ELEMENT</literal>
+ foreign keys is on a single column key, you can define an <quote>array
+ <literal>ELEMENT</literal> foreign key constraint</quote> on a group
+ of columns. As the following example shows, it must be written in table
+ constraint form:
+
+<programlisting>
+CREATE TABLE available_moves (
+ kind text,
+ move text,
+ description text,
+ PRIMARY KEY (kind, move)
+);
+
+CREATE TABLE paths (
+ description text,
+ kind text,
+ moves text[],
+ <emphasis>FOREIGN KEY (kind, ELEMENT moves) REFERENCES available_moves (kind, move)</emphasis>
+);
+
+INSERT INTO available_moves VALUES ('relative', 'LN', 'look north');
+INSERT INTO available_moves VALUES ('relative', 'RL', 'rotate left');
+INSERT INTO available_moves VALUES ('relative', 'RR', 'rotate right');
+INSERT INTO available_moves VALUES ('relative', 'MF', 'move forward');
+INSERT INTO available_moves VALUES ('absolute', 'N', 'move north');
+INSERT INTO available_moves VALUES ('absolute', 'S', 'move south');
+INSERT INTO available_moves VALUES ('absolute', 'E', 'move east');
+INSERT INTO available_moves VALUES ('absolute', 'W', 'move west');
+
+INSERT INTO paths VALUES ('L-shaped path', 'relative', '{LN, RL, MF, RR, MF, MF}');
+INSERT INTO paths VALUES ('L-shaped path', 'absolute', '{W, N, N}');
+</programlisting>
+
+ On top of standard foreign key requirements,
+ array <literal>ELEMENT</literal> foreign key constraints
+ require that the referencing column is an array of a compatible
+ type of the corresponding referenced column.
+ </para>
+
+ <para>
+ For more detailed information on array <literal>ELEMENT</literal>
+ foreign key options and special cases, please refer to the documentation
+ for <xref linkend="sql-createtable-foreign-key"> and
+ <xref linkend="sql-createtable-element-foreign-key-constraints">.
+ </para>
+
+ </sect2>
+
<sect2 id="ddl-constraints-exclusion">
<title>Exclusion Constraints</title>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 824253de40..738329da29 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -65,7 +65,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ] |
UNIQUE <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="PARAMETER">index_parameters</replaceable> |
- REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
+ [ELEMENT] REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -76,7 +76,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
UNIQUE ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ] |
- FOREIGN KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> [, ... ] ) ]
+ FOREIGN KEY ( [ELEMENT] <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> [, ... ] ) ]
[ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -834,10 +834,10 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</listitem>
</varlistentry>
- <varlistentry>
+ <varlistentry id="sql-createtable-foreign-key" xreflabel="FOREIGN KEY">
<term><literal>REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
- <term><literal>FOREIGN KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] )
+ <term><literal>FOREIGN KEY ( [ELEMENT] <replaceable class="parameter">column_name</replaceable> [, ... ] )
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] ) ]
[ MATCH <replaceable class="parameter">matchtype</replaceable> ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ]
@@ -861,6 +861,35 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</para>
<para>
+ In case the column name <replaceable class="parameter">column</replaceable>
+ is prepended with the <literal>ELEMENT</literal> keyword and <replaceable
+ class="parameter">column</replaceable> is an array of elements compatible
+ with the corresponding <replaceable class="parameter">refcolumn</replaceable>
+ in <replaceable class="parameter">reftable</replaceable>, an
+ array <literal>ELEMENT</literal> foreign key constraint is put in place
+ (see <xref linkend="sql-createtable-element-foreign-key-constraints">
+ for more information).
+ Multi-column keys with more than one <literal>ELEMENT</literal> column
+ are currently not allowed.
+
+ It is advisable to index the refrencing column using GIN index as it considerably enhances the performance.
+ Also concerning coercion while using the GIN index:
+
+ <programlisting>
+ CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+ CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ </programlisting>
+ This syntax is fine since this will cast ptest1 to int4 upon RI checks, however,
+
+ <programlisting>
+ CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+ CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ </programlisting>
+ This syntax will cast ftest1 to int4 upon RI checks, thus defeating the purpose of the index.
+
+ </para>
+
+ <para>
A value inserted into the referencing column(s) is matched against the
values of the referenced table and referenced columns using the
given match type. There are three match types: <literal>MATCH
@@ -923,7 +952,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
<para>
Delete any rows referencing the deleted row, or update the
values of the referencing column(s) to the new values of the
- referenced columns, respectively.
+ referenced columns, respectively. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -932,7 +962,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
<term><literal>SET NULL</literal></term>
<listitem>
<para>
- Set the referencing column(s) to null.
+ Set the referencing column(s) to null. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -944,6 +975,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
Set the referencing column(s) to their default values.
(There must be a row in the referenced table matching the default
values, if they are not null, or the operation will fail.)
+ Currently not supported with array <literal>ELEMENT</literal>
+ foreign keys.
</para>
</listitem>
</varlistentry>
@@ -959,6 +992,61 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</listitem>
</varlistentry>
+ <varlistentry id="sql-createtable-element-foreign-key-constraints" xreflabel="ELEMENT REFERENCES">
+ <term><literal>ELEMENT REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
+
+ <listitem>
+ <para>
+ The <literal>ELEMENT REFERENCES</literal> definition specifies
+ an <quote>array <literal>ELEMENT</literal> foreign key</quote>,
+ a special kind of foreign key
+ constraint requiring the referencing column to be an array of elements
+ of the same type (or a compatible one) as the referenced column
+ in the referenced table. The value of each element of the
+ <replaceable class="parameter">refcolumn</replaceable> array
+ will be matched against some row of <replaceable
+ class="parameter">reftable</replaceable>.
+ </para>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys are an extension
+ of PostgreSQL and are not included in the SQL standard.
+ </para>
+
+ <para>
+ Even with <literal>ELEMENT</literal> foreign keys, modifications
+ in the referenced column can trigger actions to be performed on
+ the referencing array.
+ Similarly to standard foreign keys, you can specify these
+ actions using the <literal>ON DELETE</literal> and
+ <literal>ON UPDATE</literal> clauses.
+ However, only the two following actions for each clause are
+ currently allowed:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>NO ACTION</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints. This is the default action.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>RESTRICT</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>DEFERRABLE</literal></term>
<term><literal>NOT DEFERRABLE</literal></term>
@@ -1905,6 +1993,16 @@ CREATE TABLE cities_partdef
</refsect2>
<refsect2>
+ <title id="sql-createtable-foreign-key-arrays">Array <literal>ELEMENT</literal> Foreign Keys</title>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys and the
+ <literal>ELEMENT REFERENCES</literal> clause
+ are a <productname>PostgreSQL</productname> extension.
+ </para>
+ </refsect2>
+
+ <refsect2>
<title><literal>PARTITION BY</> Clause</title>
<para>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 05e70818e7..4677c94d8e 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2124,6 +2124,7 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c7b2f031f0..db7978ec45 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1213,6 +1213,7 @@ index_constraint_create(Relation heapRelation,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 1336c46d3f..924affcdcd 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -59,6 +59,7 @@ CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
@@ -82,6 +83,7 @@ CreateConstraintEntry(const char *constraintName,
Datum values[Natts_pg_constraint];
ArrayType *conkeyArray;
ArrayType *confkeyArray;
+ ArrayType *confreftypeArray;
ArrayType *conpfeqopArray;
ArrayType *conppeqopArray;
ArrayType *conffeqopArray;
@@ -119,7 +121,11 @@ CreateConstraintEntry(const char *constraintName,
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = Int16GetDatum(foreignKey[i]);
confkeyArray = construct_array(fkdatums, foreignNKeys,
- INT2OID, 2, true, 's');
+ INT2OID, sizeof(int16), true, 's');
+ for (i = 0; i < foreignNKeys; i++)
+ fkdatums[i] = CharGetDatum(foreignRefType[i]);
+ confreftypeArray = construct_array(fkdatums, foreignNKeys,
+ CHAROID, sizeof(char), true, 'c');
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
conpfeqopArray = construct_array(fkdatums, foreignNKeys,
@@ -136,6 +142,7 @@ CreateConstraintEntry(const char *constraintName,
else
{
confkeyArray = NULL;
+ confreftypeArray = NULL;
conpfeqopArray = NULL;
conppeqopArray = NULL;
conffeqopArray = NULL;
@@ -188,6 +195,11 @@ CreateConstraintEntry(const char *constraintName,
else
nulls[Anum_pg_constraint_confkey - 1] = true;
+ if (confreftypeArray)
+ values[Anum_pg_constraint_confreftype - 1] = PointerGetDatum(confreftypeArray);
+ else
+ nulls[Anum_pg_constraint_confreftype - 1] = true;
+
if (conpfeqopArray)
values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
else
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 96354bdee5..6116999df9 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7103,6 +7103,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Relation pkrel;
int16 pkattnum[INDEX_MAX_KEYS];
int16 fkattnum[INDEX_MAX_KEYS];
+ char fkreftypes[INDEX_MAX_KEYS];
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
Oid opclasses[INDEX_MAX_KEYS];
@@ -7110,10 +7111,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid ppeqoperators[INDEX_MAX_KEYS];
Oid ffeqoperators[INDEX_MAX_KEYS];
int i;
+ ListCell *lc;
int numfks,
numpks;
Oid indexOid;
Oid constrOid;
+ bool has_array;
bool old_check_ok;
ObjectAddress address;
ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
@@ -7190,6 +7193,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
*/
MemSet(pkattnum, 0, sizeof(pkattnum));
MemSet(fkattnum, 0, sizeof(fkattnum));
+ MemSet(fkreftypes, 0, sizeof(fkreftypes));
MemSet(pktypoid, 0, sizeof(pktypoid));
MemSet(fktypoid, 0, sizeof(fktypoid));
MemSet(opclasses, 0, sizeof(opclasses));
@@ -7202,6 +7206,51 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
fkattnum, fktypoid);
/*
+ * Validate the reference semantics codes, too, and convert list to array
+ * format to pass to CreateConstraintEntry.
+ */
+ Assert(list_length(fkconstraint->fk_reftypes) == numfks);
+ has_array = false;
+ i = 0;
+ foreach(lc, fkconstraint->fk_reftypes)
+ {
+ char reftype = lfirst_int(lc);
+
+ switch (reftype)
+ {
+ case FKCONSTR_REF_PLAIN:
+ /* OK, nothing to do */
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ /* At most one FK column can be an array reference */
+ if (has_array)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign keys support only one array column")));
+ has_array = true;
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d", (int) reftype);
+ break;
+ }
+ fkreftypes[i] = reftype;
+ i++;
+ }
+
+ /* Array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions */
+ if (has_array)
+ {
+ if ((fkconstraint->fk_upd_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_upd_action != FKCONSTR_ACTION_RESTRICT) ||
+ (fkconstraint->fk_del_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_CASCADE))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions")));
+ }
+
+ /*
* If the attribute list for the referenced table was omitted, lookup the
* definition of the primary key and use it. Otherwise, validate the
* supplied attribute list. In either case, discover the index OID and
@@ -7287,6 +7336,65 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
eqstrategy = BTEqualStrategyNumber;
/*
+ * If this is an array foreign key, we must look up the operators for
+ * the array element type, not the array type itself.
+ */
+ if (fkreftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ Oid elemopclass;
+
+ /* We look through any domain here */
+ fktype = get_base_element_type(fktype);
+ if (!OidIsValid(fktype))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has type %s which is not an array type.",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktypoid[i]))));
+
+ /*
+ * For the moment, we must also insist that the array's element
+ * type have a default btree opclass that is in the index's
+ * opfamily. This is necessary because ri_triggers.c relies on
+ * COUNT(DISTINCT x) on the element type, as well as on array_eq()
+ * on the array type, and we need those operations to have the
+ * same notion of equality that we're using otherwise.
+ *
+ * XXX this restriction is pretty annoying, considering the effort
+ * that's been put into the rest of the RI mechanisms to make them
+ * work with nondefault equality operators. In particular, it
+ * means that the cast-to-PK-datatype code path isn't useful for
+ * array-to-scalar references.
+ */
+ elemopclass = GetDefaultOpClass(fktype, BTREE_AM_OID);
+ if (!OidIsValid(elemopclass) ||
+ get_opclass_family(elemopclass) != opfamily)
+ {
+ /* Get the index opclass's name for the error message. */
+ char *opcname;
+
+ cla_ht = SearchSysCache1(CLAOID,
+ ObjectIdGetDatum(opclasses[i]));
+ if (!HeapTupleIsValid(cla_ht))
+ elog(ERROR, "cache lookup failed for opclass %u",
+ opclasses[i]);
+ cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
+ opcname = pstrdup(NameStr(cla_tup->opcname));
+ ReleaseSysCache(cla_ht);
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has element type %s which does not have a default btree operator class that's compatible with class \"%s\".",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktype),
+ opcname)));
+ }
+ }
+
+ /*
* There had better be a primary equality operator for the index.
* We'll use it for PK = PK comparisons.
*/
@@ -7347,14 +7455,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("foreign key constraint \"%s\" "
- "cannot be implemented",
- fkconstraint->conname),
- errdetail("Key columns \"%s\" and \"%s\" "
- "are of incompatible types: %s and %s.",
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key columns \"%s\" and \"%s\" are of incompatible types: %s and %s.",
strVal(list_nth(fkconstraint->fk_attrs, i)),
strVal(list_nth(fkconstraint->pk_attrs, i)),
- format_type_be(fktype),
+ format_type_be(fktypoid[i]),
format_type_be(pktype))));
if (old_check_ok)
@@ -7385,6 +7491,13 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
* We may assume that pg_constraint.conkey is not changing.
*/
old_fktype = attr->atttypid;
+ if (fkreftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ old_fktype = get_base_element_type(old_fktype);
+ /* this shouldn't happen ... */
+ if (!OidIsValid(old_fktype))
+ elog(ERROR, "old foreign key column is not an array");
+ }
new_fktype = fktype;
old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
&old_castfunc);
@@ -7427,7 +7540,6 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
new_castfunc == old_castfunc &&
(!IsPolymorphicType(pfeqop_right) ||
new_fktype == old_fktype));
-
}
pfeqoperators[i] = pfeqop;
@@ -7451,6 +7563,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
indexOid,
RelationGetRelid(pkrel),
pkattnum,
+ fkreftypes,
pfeqoperators,
ppeqoperators,
ffeqoperators,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 269c9e17dd..e4d08e3889 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -635,6 +635,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
@@ -1006,6 +1007,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
char fk_matchtype = FKCONSTR_MATCH_SIMPLE;
List *fk_attrs = NIL;
List *pk_attrs = NIL;
+ List *fk_reftypes = NIL;
StringInfoData buf;
int funcnum;
OldTriggerInfo *info = NULL;
@@ -1035,7 +1037,10 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
if (i % 2)
fk_attrs = lappend(fk_attrs, arg);
else
- pk_attrs = lappend(pk_attrs, arg);
+ {
+ pk_attrs = lappend(pk_attrs, arg);
+ fk_reftypes = lappend_int(fk_reftypes, FKCONSTR_REF_PLAIN);
+ }
}
/* Prepare description of constraint for use in messages */
@@ -1174,6 +1179,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
fkcon->conname = constr_name;
fkcon->fk_attrs = fk_attrs;
fkcon->pk_attrs = pk_attrs;
+ fkcon->fk_reftypes = fk_reftypes;
fkcon->fk_matchtype = fk_matchtype;
switch (info->funcoids[0])
{
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7ed16aeff4..8639854f4d 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -3092,6 +3092,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f1bed14e2b..26e2d3da94 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2847,6 +2847,7 @@ _copyConstraint(const Constraint *from)
COPY_NODE_FIELD(pktable);
COPY_NODE_FIELD(fk_attrs);
COPY_NODE_FIELD(pk_attrs);
+ COPY_NODE_FIELD(fk_reftypes);
COPY_SCALAR_FIELD(fk_matchtype);
COPY_SCALAR_FIELD(fk_upd_action);
COPY_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 8b56b9146a..581bf601ce 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2576,6 +2576,7 @@ _equalConstraint(const Constraint *a, const Constraint *b)
COMPARE_NODE_FIELD(pktable);
COMPARE_NODE_FIELD(fk_attrs);
COMPARE_NODE_FIELD(pk_attrs);
+ COMPARE_NODE_FIELD(fk_reftypes);
COMPARE_SCALAR_FIELD(fk_matchtype);
COMPARE_SCALAR_FIELD(fk_upd_action);
COMPARE_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index b83d919e40..100f6fb570 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3492,6 +3492,7 @@ _outConstraint(StringInfo str, const Constraint *node)
WRITE_NODE_FIELD(pktable);
WRITE_NODE_FIELD(fk_attrs);
WRITE_NODE_FIELD(pk_attrs);
+ WRITE_NODE_FIELD(fk_reftypes);
WRITE_CHAR_FIELD(fk_matchtype);
WRITE_CHAR_FIELD(fk_upd_action);
WRITE_CHAR_FIELD(fk_del_action);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c303818c9b..5adeb61d99 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -126,6 +126,13 @@ typedef struct ImportQual
List *table_names;
} ImportQual;
+/* Private struct for the result of foreign_key_column_elem production */
+typedef struct FKColElem
+{
+ Node *name; /* name of the column (a String) */
+ char reftype; /* FKCONSTR_REF_xxx code */
+} FKColElem;
+
/* ConstraintAttributeSpec yields an integer bitmask of these flags: */
#define CAS_NOT_DEFERRABLE 0x01
#define CAS_DEFERRABLE 0x02
@@ -183,6 +190,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
static void SplitColQualList(List *qualList,
List **constraintList, CollateClause **collClause,
core_yyscan_t yyscanner);
+static void SplitFKColElems(List *fkcolelems, List **names, List **reftypes);
static void processCASbits(int cas_bits, int location, const char *constrType,
bool *deferrable, bool *initdeferred, bool *not_valid,
bool *no_inherit, core_yyscan_t yyscanner);
@@ -233,6 +241,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
A_Indices *aind;
ResTarget *target;
struct PrivTarget *privtarget;
+ struct FKColElem *fkcolelem;
AccessPriv *accesspriv;
struct ImportQual *importqual;
InsertStmt *istmt;
@@ -358,6 +367,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <accesspriv> privilege
%type <list> privileges privilege_list
%type <privtarget> privilege_target
+%type <fkcolelem> foreign_key_column_elem
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
%type <ival> defacl_privilege_target
@@ -392,7 +402,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
execute_param_clause using_clause returning_clause
opt_enum_val_list enum_val_list table_func_column_list
create_generic_options alter_generic_options
- relation_expr_list dostmt_opt_list
+ relation_expr_list dostmt_opt_list foreign_key_column_list
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
@@ -622,8 +632,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
DOUBLE_P DROP
- EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
- EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
+ EACH ELEMENT ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE
+ EVENT EXCEPT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
EXTENSION EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FILTER FIRST_P FLOAT_P FOLLOWING FOR
@@ -3438,8 +3448,10 @@ ColConstraintElem:
n->contype = CONSTR_FOREIGN;
n->location = @1;
n->pktable = $2;
+ /* fk_attrs will be filled in by parse analysis */
n->fk_attrs = NIL;
n->pk_attrs = $3;
+ n->fk_reftypes = list_make1_int(FKCONSTR_REF_PLAIN);
n->fk_matchtype = $4;
n->fk_upd_action = (char) ($5 >> 8);
n->fk_del_action = (char) ($5 & 0xFF);
@@ -3633,14 +3645,15 @@ ConstraintElem:
NULL, yyscanner);
$$ = (Node *)n;
}
- | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
- opt_column_list key_match key_actions ConstraintAttributeSpec
+ | FOREIGN KEY '(' foreign_key_column_list ')' REFERENCES
+ qualified_name opt_column_list key_match key_actions
+ ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_FOREIGN;
n->location = @1;
+ SplitFKColElems($4, &n->fk_attrs, &n->fk_reftypes);
n->pktable = $7;
- n->fk_attrs = $4;
n->pk_attrs = $8;
n->fk_matchtype = $9;
n->fk_upd_action = (char) ($10 >> 8);
@@ -3673,6 +3686,29 @@ columnElem: ColId
$$ = (Node *) makeString($1);
}
;
+ foreign_key_column_list:
+ foreign_key_column_elem
+ { $$ = list_make1($1); }
+ | foreign_key_column_list ',' foreign_key_column_elem
+ { $$ = lappend($1, $3); }
+ ;
+
+ foreign_key_column_elem:
+ ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($1);
+ n->reftype = FKCONSTR_REF_PLAIN;
+ $$ = n;
+ }
+ | EACH ELEMENT OF ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($4);
+ n->reftype = FKCONSTR_REF_EACH_ELEMENT;
+ $$ = n;
+ }
+ ;
key_match: MATCH FULL
{
@@ -14684,6 +14720,7 @@ unreserved_keyword:
| DOUBLE_P
| DROP
| EACH
+ | ELEMENT
| ENABLE_P
| ENCODING
| ENCRYPTED
@@ -15801,6 +15838,23 @@ SplitColQualList(List *qualList,
*constraintList = qualList;
}
+/* Split a list of FKColElem structs into separate name and reftype lists */
+static void
+SplitFKColElems(List *fkcolelems, List **names, List **reftypes)
+{
+ ListCell *lc;
+
+ *names = NIL;
+ *reftypes = NIL;
+ foreach(lc, fkcolelems)
+ {
+ FKColElem *fkcolelem = (FKColElem *) lfirst(lc);
+
+ *names = lappend(*names, fkcolelem->name);
+ *reftypes = lappend_int(*reftypes, fkcolelem->reftype);
+ }
+}
+
/*
* Process result of ConstraintAttributeSpec, and set appropriate bool flags
* in the output command node. Pass NULL for any flags the particular
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index e79ad26e71..108c997ed9 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1320,6 +1320,90 @@ select_common_type(ParseState *pstate, List *exprs, const char *context,
}
/*
+ * look at select_common_type(); in parse_coerce.c(src/backend/parser/parse_coerce.c)
+ *
+ * select_common_type_2args()
+ * Determine the common supertype of a list of input expressions.
+ * 'leftOid': left operand's type
+ * 'rightOid': left operand's type
+ */
+Oid select_common_type_2args(Oid leftOid, Oid rightOid)
+{
+ TYPCATEGORY leftcategory;
+ TYPCATEGORY rightcategory;
+ bool leftispreferred;
+ bool rightispreferred;
+
+ Assert(leftOid != InvalidOid);
+ Assert(rightOid != InvalidOid);
+
+ /*
+ * If input types are valid and exactly the same, just pick that type.
+ */
+ if (leftOid != UNKNOWNOID)
+ {
+ if (rightOid == leftOid)
+ return leftOid;
+ }
+
+ /*
+ * Nope, so set up for the full algorithm.
+ */
+ leftOid = getBaseType(leftOid);
+ rightOid = getBaseType(rightOid);
+ get_type_category_preferred(leftOid, &leftcategory, &leftispreferred);
+ get_type_category_preferred(rightOid, &rightcategory, &rightispreferred);
+
+ if (rightOid != UNKNOWNOID && rightOid != leftOid)
+ {
+ if (rightcategory != leftcategory)
+ {
+ /* both types in different categories? then not much hope... */
+ return InvalidOid;
+ }
+
+ if (can_coerce_type(1, &rightOid, &leftOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left */
+ if (!can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can not coerce to it implicitly right to left, thus coercion only works one way */
+ return leftOid;
+ }
+ else
+ {
+ /* coercion works both ways, then decide depending on preferred flag */
+ if (leftispreferred)
+ return leftOid;
+ else
+ rightOid;
+ }
+ }
+ else{
+ /* can not coerce to it implicitly right to left */
+ if (can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left, thus coercion only works one way */
+ return rightOid;
+ }
+ else{
+ /* can not coerce either way */
+ return InvalidOid;
+ }
+ }
+ }
+
+ /*
+ * If all the inputs were UNKNOWN type --- ie, unknown-type literals ---
+ * then resolve as type TEXT.
+ */
+ if (leftOid == UNKNOWNOID)
+ leftOid = TEXTOID;
+
+ return leftOid;
+}
+
+/*
* coerce_to_common_type()
* Coerce an expression to the given type.
*
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 655da02c10..456a2cb76e 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -744,6 +744,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
* list of FK constraints to be processed later.
*/
constraint->fk_attrs = list_make1(makeString(column->colname));
+ /* grammar should have set fk_reftypes */
+ Assert(list_length(constraint->fk_reftypes) == 1);
cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
break;
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index c2891e6fa1..8800fba4a5 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -118,9 +118,11 @@ typedef struct RI_ConstraintInfo
char confupdtype; /* foreign key's ON UPDATE action */
char confdeltype; /* foreign key's ON DELETE action */
char confmatchtype; /* foreign key's match type */
+ bool has_array; /* true if any reftype is EACH_ELEMENT */
int nkeys; /* number of key columns */
int16 pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */
int16 fk_attnums[RI_MAX_NUMKEYS]; /* attnums of referencing cols */
+ char fk_reftypes[RI_MAX_NUMKEYS]; /* reference semantics */
Oid pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = FK) */
Oid pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = PK) */
Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK = FK) */
@@ -204,7 +206,8 @@ static void ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype);
+ const char *rightop, Oid rightoptype,
+ char fkreftype);
static void ri_add_cast_to(StringInfo buf, Oid typid);
static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
static int ri_NullCheck(HeapTuple tup,
@@ -395,6 +398,7 @@ RI_FKey_check(TriggerData *trigdata)
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
StringInfoData querybuf;
+ StringInfoData countbuf;
char pkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
char paramname[16];
@@ -407,12 +411,22 @@ RI_FKey_check(TriggerData *trigdata)
* FOR KEY SHARE OF x
* The type id's for the $ parameters are those of the
* corresponding FK attributes.
+ *
+ * In case of an array ELEMENT foreign key, the previous query is used
+ * to count the number of matching rows and see if every combination
+ * is actually referenced.
+ * The wrapping query is
+ * SELECT 1 WHERE
+ * (SELECT count(DISTINCT y) FROM unnest($1) y)
+ * = (SELECT count(*) FROM (<QUERY>) z)
* ----------
*/
initStringInfo(&querybuf);
quoteRelationName(pkrelname, pk_rel);
appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
querysep = "WHERE";
+ initStringInfo(&countbuf);
+ appendStringInfo(&countbuf, "SELECT 1 WHERE ");
for (i = 0; i < riinfo->nkeys; i++)
{
Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
@@ -421,18 +435,41 @@ RI_FKey_check(TriggerData *trigdata)
quoteOneName(attname,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
sprintf(paramname, "$%d", i + 1);
+
+ /*
+ * In case of an array ELEMENT foreign key, we check that each
+ * distinct non-null value in the array is present in the PK
+ * table.
+ */
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&countbuf,
+ "(SELECT pg_catalog.count(DISTINCT y) FROM pg_catalog.unnest(%s) y)",
+ paramname);
+
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pf_eq_oprs[i],
- paramname, fk_type);
+ paramname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = fk_type;
}
appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
- /* Prepare and save the plan */
- qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
- &qkey, fk_rel, pk_rel, true);
+ if (riinfo->has_array)
+ {
+ appendStringInfo(&countbuf,
+ " OPERATOR(pg_catalog.=) (SELECT pg_catalog.count(*) FROM (%s) z)",
+ querybuf.data);
+
+ /* Prepare and save the plan for array ELEMENT foreign keys */
+ qplan = ri_PlanCheck(countbuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
+ }
+ else
+ /* Prepare and save the plan */
+ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
}
/*
@@ -559,7 +596,8 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pp_eq_oprs[i],
- paramname, pk_type);
+ paramname, pk_type,
+ FKCONSTR_REF_PLAIN);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -751,7 +789,8 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -974,7 +1013,8 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1130,7 +1170,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1309,7 +1350,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1475,7 +1517,8 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1651,7 +1694,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1817,7 +1861,8 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2008,7 +2053,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2327,6 +2373,14 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
* For MATCH FULL:
* (fk.keycol1 IS NOT NULL [OR ...])
*
+ * In case of an array ELEMENT column, relname is replaced with the
+ * following subquery:
+ *
+ * SELECT unnest("keycol1") k1, "keycol1" ak1 [, ...]
+ * FROM ONLY "public"."fk"
+ *
+ * where all the columns are renamed in order to prevent name collisions.
+ *
* We attach COLLATE clauses to the operators when comparing columns
* that have different collations.
*----------
@@ -2338,15 +2392,46 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
{
quoteOneName(fkattname,
RIAttName(fk_rel, riinfo->fk_attnums[i]));
- appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
+ if (riinfo->has_array)
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%sfk.ak%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.k%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
sep = ", ";
}
quoteRelationName(pkrelname, pk_rel);
quoteRelationName(fkrelname, fk_rel);
- appendStringInfo(&querybuf,
- " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
- fkrelname, pkrelname);
+
+ if (riinfo->has_array)
+ {
+ sep = "";
+ appendStringInfo(&querybuf,
+ " FROM (SELECT ");
+ for (i = 0; i < riinfo->nkeys; i++)
+ {
+ quoteOneName(fkattname,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%spg_catalog.unnest(%s) k%d, %s ak%d",
+ sep, fkattname, i + 1, fkattname, i + 1);
+ else
+ appendStringInfo(&querybuf, "%s%s k%d", sep, fkattname,
+ i + 1);
+ sep = ", ";
+ }
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s) fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
+ }
+ else
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
strcpy(pkattname, "pk.");
strcpy(fkattname, "fk.");
@@ -2360,12 +2445,16 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
quoteOneName(pkattname + 3,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
- quoteOneName(fkattname + 3,
- RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname + 3, "k%d", i + 1);
+ else
+ quoteOneName(fkattname + 3,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
ri_GenerateQual(&querybuf, sep,
pkattname, pk_type,
riinfo->pf_eq_oprs[i],
- fkattname, fk_type);
+ fkattname, fk_type,
+ FKCONSTR_REF_PLAIN);
if (pk_coll != fk_coll)
ri_GenerateQualCollation(&querybuf, pk_coll);
sep = "AND";
@@ -2381,7 +2470,10 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
sep = "";
for (i = 0; i < riinfo->nkeys; i++)
{
- quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname, "k%d", i + 1);
+ else
+ quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
appendStringInfo(&querybuf,
"%sfk.%s IS NOT NULL",
sep, fkattname);
@@ -2557,20 +2649,23 @@ quoteRelationName(char *buffer, Relation rel)
/*
* ri_GenerateQual --- generate a WHERE clause equating two variables
*
- * The idea is to append " sep leftop op rightop" to buf. The complexity
- * comes from needing to be sure that the parser will select the desired
- * operator. We always name the operator using OPERATOR(schema.op) syntax
- * (readability isn't a big priority here), so as to avoid search-path
- * uncertainties. We have to emit casts too, if either input isn't already
- * the input type of the operator; else we are at the mercy of the parser's
- * heuristics for ambiguous-operator resolution.
- */
+ * The idea is to append " sep leftop op rightop" to buf, or if fkreftype is
+ * FKCONSTR_REF_EACH_ELEMENT, append " sep rightop @> leftop" to buf.
+ *
+ * The complexity comes from needing to be sure that the parser will select
+ * the desired operator. We always name the operator using
+ * OPERATOR(schema.op) syntax (readability isn't a big priority here), so as
+ * to avoid search-path uncertainties. We have to emit casts too, if either
+ * input isn't already the input type of the operator; else we are at the
+ * mercy of the parser's heuristics for ambiguous-operator resolution.
+ */
static void
ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype)
+ const char *rightop, Oid rightoptype,
+ char fkreftype)
{
HeapTuple opertup;
Form_pg_operator operform;
@@ -2586,14 +2681,67 @@ ri_GenerateQual(StringInfo buf,
nspname = get_namespace_name(operform->oprnamespace);
- appendStringInfo(buf, " %s %s", sep, leftop);
- if (leftoptype != operform->oprleft)
- ri_add_cast_to(buf, operform->oprleft);
- appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
- appendStringInfoString(buf, oprname);
- appendStringInfo(buf, ") %s", rightop);
- if (rightoptype != operform->oprright)
- ri_add_cast_to(buf, operform->oprright);
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ Oid oprright;
+ Oid oprleft;
+ Oid oprcommon;
+
+ /*
+ * we first need to get the array types and decide
+ * the more appropriate one
+ */
+
+ /* get array type of refrenced element*/
+ oprright = get_array_type(operform->oprleft);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprleft))));
+
+ /* get array type of refrencing element*/
+ oprleft = get_array_type(operform->oprright);
+ if (!OidIsValid(oprleft))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprright))));
+
+ /* compare the two array types and try to find
+ a common supertype */
+ oprcommon = select_common_type_2args(oprleft, oprright);
+ if (!OidIsValid(oprcommon))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find common type between %s and %s",
+ format_type_be(oprleft), format_type_be(oprright))));
+
+ appendStringInfo(buf, " %s %s", sep, rightop);
+ if (oprleft != oprcommon)
+ ri_add_cast_to(buf, oprcommon);
+
+ appendStringInfo(buf, " OPERATOR(pg_catalog.@>) ");
+
+ appendStringInfo(buf, " ARRAY[%s]", leftop);
+ if (oprright != oprcommon)
+ ri_add_cast_to(buf, oprcommon);
+ }
+ else
+ {
+ appendStringInfo(buf, " %s %s", sep, leftop);
+
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+
+ appendStringInfo(buf, " OPERATOR(%s.%s) ",
+ quote_identifier(nspname), oprname);
+
+ appendStringInfoString(buf, rightop);
+
+ if (rightoptype != operform->oprright)
+ ri_add_cast_to(buf, operform->oprright);
+ }
ReleaseSysCache(opertup);
}
@@ -2801,6 +2949,7 @@ ri_LoadConstraintInfo(Oid constraintOid)
bool isNull;
ArrayType *arr;
int numkeys;
+ int i;
/*
* On the first call initialize the hashtable
@@ -2879,6 +3028,20 @@ ri_LoadConstraintInfo(Oid constraintOid)
pfree(arr); /* free de-toasted copy, if any */
adatum = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype, &isNull);
+ if (isNull)
+ elog(ERROR, "null confreftype for constraint %u", constraintOid);
+ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
+ if (ARR_NDIM(arr) != 1 ||
+ ARR_DIMS(arr)[0] != numkeys ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != CHAROID)
+ elog(ERROR, "confreftype is not a 1-D char array");
+ memcpy(riinfo->fk_reftypes, ARR_DATA_PTR(arr), numkeys * sizeof(char));
+ if ((Pointer) arr != DatumGetPointer(adatum))
+ pfree(arr); /* free de-toasted copy, if any */
+
+ adatum = SysCacheGetAttr(CONSTROID, tup,
Anum_pg_constraint_conpfeqop, &isNull);
if (isNull)
elog(ERROR, "null conpfeqop for constraint %u", constraintOid);
@@ -2921,6 +3084,24 @@ ri_LoadConstraintInfo(Oid constraintOid)
if ((Pointer) arr != DatumGetPointer(adatum))
pfree(arr); /* free de-toasted copy, if any */
+ /*
+ * Fix up some stuff for array foreign keys. We need a has_array flag
+ * indicating whether there's an array foreign key, and we want to set
+ * ff_eq_oprs[i] to array_eq() for array columns, because that's what
+ * makes sense for ri_KeysEqual, and we have no other use for ff_eq_oprs
+ * in this module. (If we did, substituting the array comparator at the
+ * call point in ri_KeysEqual might be more appropriate.)
+ */
+ riinfo->has_array = false;
+ for (i = 0; i < numkeys; i++)
+ {
+ if (riinfo->fk_reftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ riinfo->has_array = true;
+ riinfo->ff_eq_oprs[i] = ARRAY_EQ_OP;
+ }
+ }
+
ReleaseSysCache(tup);
/*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 0ea5078218..93140cf267 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -314,6 +314,9 @@ static char *pg_get_viewdef_worker(Oid viewoid,
static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
static void decompile_column_index_array(Datum column_index_array, Oid relId,
StringInfo buf);
+static void decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf);
static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
const Oid *excludeOps,
@@ -1882,7 +1885,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
{
case CONSTRAINT_FOREIGN:
{
- Datum val;
+ Datum colindexes;
+ Datum reftypes;
bool isnull;
const char *string;
@@ -1890,13 +1894,21 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
appendStringInfoString(&buf, "FOREIGN KEY (");
/* Fetch and build referencing-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_conkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_conkey,
+ &isnull);
if (isnull)
elog(ERROR, "null conkey for constraint %u",
constraintId);
+ reftypes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype,
+ &isnull);
+ if (isnull)
+ elog(ERROR, "null confreftype for constraint %u",
+ constraintId);
- decompile_column_index_array(val, conForm->conrelid, &buf);
+ decompile_fk_column_index_array(colindexes, reftypes,
+ conForm->conrelid, &buf);
/* add foreign relation name */
appendStringInfo(&buf, ") REFERENCES %s(",
@@ -1904,13 +1916,15 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
NIL));
/* Fetch and build referenced-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_confkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confkey,
+ &isnull);
if (isnull)
elog(ERROR, "null confkey for constraint %u",
constraintId);
- decompile_column_index_array(val, conForm->confrelid, &buf);
+ decompile_column_index_array(colindexes,
+ conForm->confrelid, &buf);
appendStringInfoChar(&buf, ')');
@@ -2185,6 +2199,66 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
}
}
+ /*
+ * Convert an int16[] Datum and a char[] Datum into a comma-separated
+ * list of column names for the indicated relation, prefixed by appropriate
+ * keywords depending on the foreign key reference semantics indicated by
+ * the char[] entries. Append the text to buf.
+ */
+ static void
+ decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf)
+ {
+ Datum *keys;
+ int nKeys;
+ Datum *reftypes;
+ int nReftypes;
+ int j;
+
+ /* Extract data from array of int16 */
+ deconstruct_array(DatumGetArrayTypeP(column_index_array),
+ INT2OID, sizeof(int16), true, 's',
+ &keys, NULL, &nKeys);
+
+ /* Extract data from array of char */
+ deconstruct_array(DatumGetArrayTypeP(fk_reftype_array),
+ CHAROID, sizeof(char), true, 'c',
+ &reftypes, NULL, &nReftypes);
+
+ if (nKeys != nReftypes)
+ elog(ERROR, "wrong confreftype cardinality");
+
+ for (j = 0; j < nKeys; j++)
+ {
+ char *colName;
+ const char *prefix;
+
+ colName = get_relid_attribute_name(relId, DatumGetInt16(keys[j]));
+
+ switch (DatumGetChar(reftypes[j]))
+ {
+ case FKCONSTR_REF_PLAIN:
+ prefix = "";
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ prefix = "EACH ELEMENT OF ";
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d",
+ (int) DatumGetChar(reftypes[j]));
+ prefix = NULL; /* keep compiler quiet */
+ break;
+ }
+
+ if (j == 0)
+ appendStringInfo(buf, "%s%s", prefix,
+ quote_identifier(colName));
+ else
+ appendStringInfo(buf, ", %s%s", prefix,
+ quote_identifier(colName));
+ }
+}
/* ----------
* get_expr - Decompile an expression tree
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index ec035d8434..a6b296d4a0 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -104,8 +104,16 @@ CATALOG(pg_constraint,2606)
int16 confkey[1];
/*
+ * If a foreign key, the reference semantics for each column
+ */
+ char confreftype[1];
+
+ /*
* If a foreign key, the OIDs of the PK = FK equality operators for each
* column of the constraint
+ *
+ * Note: for array foreign keys, all these operators are for the array's
+ * element type.
*/
Oid conpfeqop[1];
@@ -150,7 +158,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* compiler constants for pg_constraint
* ----------------
*/
-#define Natts_pg_constraint 24
+#define Natts_pg_constraint 25
#define Anum_pg_constraint_conname 1
#define Anum_pg_constraint_connamespace 2
#define Anum_pg_constraint_contype 3
@@ -169,12 +177,13 @@ typedef FormData_pg_constraint *Form_pg_constraint;
#define Anum_pg_constraint_connoinherit 16
#define Anum_pg_constraint_conkey 17
#define Anum_pg_constraint_confkey 18
-#define Anum_pg_constraint_conpfeqop 19
-#define Anum_pg_constraint_conppeqop 20
-#define Anum_pg_constraint_conffeqop 21
-#define Anum_pg_constraint_conexclop 22
-#define Anum_pg_constraint_conbin 23
-#define Anum_pg_constraint_consrc 24
+#define Anum_pg_constraint_confreftype 19
+#define Anum_pg_constraint_conpfeqop 20
+#define Anum_pg_constraint_conppeqop 21
+#define Anum_pg_constraint_conffeqop 22
+#define Anum_pg_constraint_conexclop 23
+#define Anum_pg_constraint_conbin 24
+#define Anum_pg_constraint_consrc 25
/* ----------------
* initial contents of pg_constraint
@@ -195,7 +204,9 @@ typedef FormData_pg_constraint *Form_pg_constraint;
/*
* Valid values for confupdtype and confdeltype are the FKCONSTR_ACTION_xxx
* constants defined in parsenodes.h. Valid values for confmatchtype are
- * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h.
+ * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h. Valid values
+ * for elements of confreftype[] are the FKCONSTR_REF_xxx constants defined
+ * in parsenodes.h.
*/
#endif /* PG_CONSTRAINT_H */
diff --git a/src/include/catalog/pg_constraint_fn.h b/src/include/catalog/pg_constraint_fn.h
index a4c46897ed..9805ed220a 100644
--- a/src/include/catalog/pg_constraint_fn.h
+++ b/src/include/catalog/pg_constraint_fn.h
@@ -40,6 +40,7 @@ extern Oid CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index f3e4c69753..dcce78f8cb 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2075,6 +2075,10 @@ typedef enum ConstrType /* types of constraints */
#define FKCONSTR_MATCH_PARTIAL 'p'
#define FKCONSTR_MATCH_SIMPLE 's'
+ /* Foreign key column reference semantics codes */
+ #define FKCONSTR_REF_PLAIN 'p'
+ #define FKCONSTR_REF_EACH_ELEMENT 'e'
+
typedef struct Constraint
{
NodeTag type;
@@ -2110,6 +2114,7 @@ typedef struct Constraint
RangeVar *pktable; /* Primary key table */
List *fk_attrs; /* Attributes of foreign key */
List *pk_attrs; /* Corresponding attrs in PK table */
+ List *fk_reftypes; /* Per-column reference semantics (int List) */
char fk_matchtype; /* FULL, PARTIAL, SIMPLE */
char fk_upd_action; /* ON UPDATE action */
char fk_del_action; /* ON DELETE action */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f50e45e886..d3f4803006 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -140,6 +140,7 @@ PG_KEYWORD("domain", DOMAIN_P, UNRESERVED_KEYWORD)
PG_KEYWORD("double", DOUBLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD)
PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD)
+PG_KEYWORD("element", ELEMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("else", ELSE, RESERVED_KEYWORD)
PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index 06f65293cb..f5bad57b70 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -68,6 +68,7 @@ extern int parser_coercion_errposition(ParseState *pstate,
extern Oid select_common_type(ParseState *pstate, List *exprs,
const char *context, Node **which_expr);
+extern Oid select_common_type_2args(Oid ptype, Oid ntype);
extern Node *coerce_to_common_type(ParseState *pstate, Node *node,
Oid targetTypeId,
const char *context);
diff --git a/src/test/regress/expected/element_foreign_key.out b/src/test/regress/expected/element_foreign_key.out
new file mode 100644
index 0000000000..0a2996bec3
--- /dev/null
+++ b/src/test/regress/expected/element_foreign_key.out
@@ -0,0 +1,703 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fkarray"
+DETAIL: Key (ftest1)=({10,1}) is not present in table "pktableforarray".
+DROP TABLE FKTABLEFORARRAY;
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4,6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL,4,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+ERROR: insert or update on table "fktableforarraymdim" violates foreign key constraint "fktableforarraymdim_ftest1_fkey"
+DETAIL: Key (ftest1)=({{1,2},{6,NULL}}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+ERROR: null value in column "ftest1" violates not-null constraint
+DETAIL: Failing row contains (null, 21).
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+ {1} | 4
+(11 rows)
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({D}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({A,B,D}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(A) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(B) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ERROR: foreign key constraint "fktableforarray_ftest1_fkey" cannot be implemented
+DETAIL: Key column "ftest1" has element type integer which does not have a default btree operator class that's compatible with class "float8_ops".
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(A, {A,B,C}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(B, {A}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Test ELEMENT foreign keys with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,99)"}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,1)","(2010,1)"}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2010,99)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2011,1)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({0,1}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({2,1}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(2, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(20, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+ERROR: foreign keys support only one array column
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: foreign keys support only one array column
+DROP TABLE F1;
+-- Cleanup
+DROP TABLE DIM1;
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "x2_x1_fkey"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "fk_const"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+DROP TABLE x1;
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{100,100,100},{NULL,NULL,20},{7,8,10}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
+-- Check GIN index
+-- Define PKTABLEFORARRAYGIN
+CREATE TABLE PKTABLEFORARRAYGIN ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAYGIN
+INSERT INTO PKTABLEFORARRAYGIN VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (5, 'Test5');
+-- Define FKTABLEFORARRAYGIN
+CREATE TABLE FKTABLEFORARRAYGIN ( ftest1 int[],
+ ftest2 int PRIMARY KEY,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYGIN
+ ON DELETE NO ACTION ON UPDATE NO ACTION);
+-- -- Create index on FKTABLEFORARRAYGIN
+CREATE INDEX ON FKTABLEFORARRAYGIN USING gin (ftest1 array_ops);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,5,3}', 10);
+-- Try using the indexable operator
+SELECT * FROM FKTABLEFORARRAYGIN WHERE ftest1 @> ARRAY[5];
+ ftest1 | ftest2
+-------------+--------
+ {5} | 1
+ {3,5,2,5} | 3
+ {3,5,4,1,3} | 5
+ {5,1} | 7
+ {3,4,5,3} | 10
+(5 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYGIN;
+DROP TABLE PKTABLEFORARRAYGIN;
+-- Check DELECTE CASCADE ACTION
+CREATE TABLE PKTABLEFORARRAYDELETE ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAYDELETE
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (5, 'Test5');
+-- Define FKTABLEFORARRAYDELETE
+CREATE TABLE FKTABLEFORARRAYDELETE ( ftest1 int[],
+ ftest2 int,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYDELETE
+ ON DELETE CASCADE ON UPDATE NO ACTION );
+-- Create index on FKTABLEFORARRAYDELETE
+CREATE INDEX ON FKTABLEFORARRAYDELETE USING gin (ftest1);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,5,3}', 10);
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+ ftest1 | ftest2
+-------------+--------
+ {5} | 1
+ {3,2} | 2
+ {3,5,2,5} | 3
+ {3,4,4} | 4
+ {3,5,4,1,3} | 5
+ {1} | 6
+ {5,1} | 7
+ {2,1,2,4,1} | 8
+ {4,2} | 9
+ {3,4,5,3} | 10
+(10 rows)
+
+-- Check FKTABLE for target rows
+SELECT * FROM FKTABLEFORARRAYDELETE WHERE ftest1 @> ARRAY[5];
+ ftest1 | ftest2
+-------------+--------
+ {5} | 1
+ {3,5,2,5} | 3
+ {3,5,4,1,3} | 5
+ {5,1} | 7
+ {3,4,5,3} | 10
+(5 rows)
+
+-- Perfrorm delete
+DELETE FROM PKTABLEFORARRAYDELETE WHERE ptest1 = 5;
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+ ftest1 | ftest2
+-------------+--------
+ {3,2} | 2
+ {3,4,4} | 4
+ {1} | 6
+ {2,1,2,4,1} | 8
+ {4,2} | 9
+(5 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYDELETE;
+DROP TABLE PKTABLEFORARRAYDELETE;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 2fd3f2b1b1..fdd6720fb8 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -103,7 +103,7 @@ test: publication subscription
# ----------
# Another group of parallel tests
# ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
+test: select_views portals_p2 foreign_key element_foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
# ----------
# Another group of parallel tests
# NB: temp.sql does a reconnect which transiently uses 2 connections,
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 76b0de30a7..9cb8638b4e 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -140,6 +140,7 @@ test: amutils
test: select_views
test: portals_p2
test: foreign_key
+test: element_foreign_key
test: cluster
test: dependency
test: guc
diff --git a/src/test/regress/sql/element_foreign_key.sql b/src/test/regress/sql/element_foreign_key.sql
new file mode 100644
index 0000000000..17ef03760f
--- /dev/null
+++ b/src/test/regress/sql/element_foreign_key.sql
@@ -0,0 +1,549 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Test ELEMENT foreign keys with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+
+
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+
+
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+-- Cleanup
+DROP TABLE DIM1;
+
+
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+DROP TABLE x2;
+DROP TABLE x1;
+
+
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+DROP TABLE F1;
+
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+DROP TABLE F1;
+
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
+
+-- Check GIN index
+-- Define PKTABLEFORARRAYGIN
+CREATE TABLE PKTABLEFORARRAYGIN ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAYGIN
+INSERT INTO PKTABLEFORARRAYGIN VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (5, 'Test5');
+
+-- Define FKTABLEFORARRAYGIN
+CREATE TABLE FKTABLEFORARRAYGIN ( ftest1 int[],
+ ftest2 int PRIMARY KEY,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYGIN
+ ON DELETE NO ACTION ON UPDATE NO ACTION);
+
+-- -- Create index on FKTABLEFORARRAYGIN
+CREATE INDEX ON FKTABLEFORARRAYGIN USING gin (ftest1 array_ops);
+
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,5,3}', 10);
+
+-- Try using the indexable operator
+SELECT * FROM FKTABLEFORARRAYGIN WHERE ftest1 @> ARRAY[5];
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYGIN;
+DROP TABLE PKTABLEFORARRAYGIN;
+
+-- Check DELECTE CASCADE ACTION
+CREATE TABLE PKTABLEFORARRAYDELETE ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAYDELETE
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (5, 'Test5');
+
+-- Define FKTABLEFORARRAYDELETE
+CREATE TABLE FKTABLEFORARRAYDELETE ( ftest1 int[],
+ ftest2 int,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYDELETE
+ ON DELETE CASCADE ON UPDATE NO ACTION );
+
+-- Create index on FKTABLEFORARRAYDELETE
+CREATE INDEX ON FKTABLEFORARRAYDELETE USING gin (ftest1);
+
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,5,3}', 10);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+
+-- Check FKTABLE for target rows
+SELECT * FROM FKTABLEFORARRAYDELETE WHERE ftest1 @> ARRAY[5];
+
+-- Perfrorm delete
+DELETE FROM PKTABLEFORARRAYDELETE WHERE ptest1 = 5;
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYDELETE;
+DROP TABLE PKTABLEFORARRAYDELETE;
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-09-18 01:55 ` Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Andreas Karlsson @ 2017-09-18 01:55 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; Alexander Korotkov <aekorotkov@gmail.com>; Stephen Frost <sfrost@snowman.net>; David Steele <david@pgmasters.net>; Alvaro Herrera <alvherre@2ndquadrant.com>; +Cc: Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; pgsql-hackers-owner@postgresql.org, Tom Lane <tgl@sss.pgh.pa.us>; Hans-Jürgen Schönig <hs@cybertec.at>
I have not looked at the issue with the btree_gin tests yet, but here is
the first part of my review.
= Review
This is my first quick review where I just read the documentation and
quickly tested the feature. I will review it more in-depth later.
This is a very useful feature, one which I have a long time wished for.
The patch applies, compiles and passes the test suite with just one warning.
parse_coerce.c: In function ‘select_common_type_2args’:
parse_coerce.c:1379:7: warning: statement with no effect [-Wunused-value]
rightOid;
^~~~~~~~
= Functional
The documentation does not agree with the code on the syntax. The
documentation claims it is "FOREIGN KEY (ELEMENT xs) REFERENCES t1 (x)"
when it actually is "FOREIGN KEY (EACH ELEMENT OF xs) REFERENCES t1 (x)".
Likewise I can't get the "final_positions integer[] ELEMENT REFERENCES
drivers" syntax to work, but here I cannot see any change in the syntax
to support it.
Related to the above: I am not sure if it is a good idea to make ELEMENT
a reserved word in column definitions. What if the SQL standard wants to
use it for something?
The documentation claims ON CASCADE DELETE is not supported by array
element foreign keys, but I do not think that is actually the case.
I think I prefer (EACH ELEMENT OF xs) over (ELEMENT xs) given how the
former is more in what I feel is the spirit of SQL. And if so we should
match it as "xs integer[] EACH ELEMENT REFERENCES t1 (x)", assuming we
want that syntax.
Once I have created an array element foreign key the basic features seem
to work as expected.
The error message below fails to mention that it is an array element
foreign key, but I do not think that is not a blocker for getting this
feature merged. Right now I cannot think of how to improve it either.
$ INSERT INTO t3 VALUES ('{1,3}');
ERROR: insert or update on table "t3" violates foreign key constraint
"t3_xs_fkey"
DETAIL: Key (xs)=({1,3}) is not present in table "t1".
= Nitpicking/style comments
In doc/src/sgml/catalogs.sgml the
"<entry><structfield>conpfeqop</structfield></entry>" line is
incorrectly indented.
I am not fan of calling it "array-vs-scalar". What about array to scalar?
In ddl.sgml date should be lower case like the other types in "race_day
DATE,".
In ddl.sgml I suggest removing the "..." from the examples to make it
possible to copy paste them easily.
Your text wrapping in ddl.sqml and create_table.sgqml is quite
arbitrary. I suggest wrapping all paragraphs at 80 characters (except
for code which should not be wrapped). Your text editor probably has
tools for wrapping paragraphs.
Please be consistent about how you write table names and SQL in general.
I think almost all places use lower case for table names, while your
examples in create_table.sgml are FKTABLEFORARRAY.
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
@ 2017-09-24 17:50 ` Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-09-24 17:50 UTC (permalink / raw)
To: Andreas Karlsson <andreas@proxel.se>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Stephen Frost <sfrost@snowman.net>; David Steele <david@pgmasters.net>; Alvaro Herrera <alvherre@2ndquadrant.com>; Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; pgsql-hackers-owner@postgresql.org, Tom Lane <tgl@sss.pgh.pa.us>; Hans-Jürgen Schönig <hs@cybertec.at>
Thank you for the review.
Please find the new patch here, with your comments taken into consideration.
However, the following comment "Related to the above: I am not sure if it
is a good idea to make ELEMENT a reserved word in column definitions. What
if the SQL standard wants to use it for something?
I think I prefer (EACH ELEMENT OF xs) over (ELEMENT xs) given how the
former is more in what I feel is the spirit of SQL. And if so we should
match it as "xs integer[] EACH ELEMENT REFERENCES t1 (x)", assuming we want
that syntax." is outside my area of expertise. The original authors should
take a look at it (Tom Lane and Marco). They had a whole discussion on the
matter, here.
https://www.postgresql.org/message-id/6256.1350613614%40sss.pgh.pa.us
Best Regards,
Mark Rofail
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
Array-ELEMENT-foreign-key-v5.2.patch (text/x-patch, 125.4K) download
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 9af77c1f5a..13a0314552 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2322,6 +2322,16 @@ SCRAM-SHA-256$<replaceable><iteration count></>:<replaceable><salt><
</row>
<row>
+ <entry><structfield>confreftype</structfield></entry>
+ <entry><type>char[]</type></entry>
+ <entry></entry>
+ <entry>If a foreign key, the reference semantics for each column:
+ <literal>p</> = plain (simple equality),
+ <literal>e</> = each element of referencing array must have a match
+ </entry>
+ </row>
+
+ <row>
<entry><structfield>conpfeqop</structfield></entry>
<entry><type>oid[]</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</></entry>
@@ -2367,13 +2377,18 @@ SCRAM-SHA-256$<replaceable><iteration count></>:<replaceable><salt><
</table>
<para>
- In the case of an exclusion constraint, <structfield>conkey</structfield>
- is only useful for constraint elements that are simple column references.
- For other cases, a zero appears in <structfield>conkey</structfield>
- and the associated index must be consulted to discover the expression
- that is constrained. (<structfield>conkey</structfield> thus has the
- same contents as <structname>pg_index</>.<structfield>indkey</> for the
- index.)
+ When <structfield>confreftype</structfield> indicates array to scalar
+ foreign key reference semantics, the equality operators listed in
+ <structfield>conpfeqop</structfield> etc are for the array's element type.
+ </para>
+
+ <para>
+ In the case of an exclusion constraint, <structfield>conkey</structfield> is
+ only useful for constraint elements that are simple column references. For
+ other cases, a zero appears in <structfield>conkey</structfield> and the
+ associated index must be consulted to discover the expression that is
+ constrained. (<structfield>conkey</structfield> thus has the same contents
+ as <structname>pg_index</>.<structfield>indkey</> for the index.)
</para>
<note>
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index b05a9c2150..19868a07d0 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -882,6 +882,108 @@ CREATE TABLE order_items (
</para>
</sect2>
+ <sect2 id="ddl-constraints-element-fk">
+ <title>Array ELEMENT Foreign Keys</title>
+
+ <indexterm>
+ <primary>ELEMENT foreign key</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>Array ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>referential integrity</primary>
+ </indexterm>
+
+ <para>
+ Another option you have with foreign keys is to use a referencing column
+ which is an array of elements with the same type (or a compatible one) as
+ the referenced column in the related table. This feature is called
+ <firstterm>array element foreign key</firstterm> and is implemented in
+ PostgreSQL with <firstterm>ELEMENT foreign key constraints</firstterm>, as
+ described in the following example:
+
+<programlisting>
+CREATE TABLE drivers (
+ driver_id integer PRIMARY KEY,
+ first_name text,
+ last_name text
+);
+
+CREATE TABLE races (
+ race_id integer PRIMARY KEY,
+ title text,
+ race_day date,
+ final_positions integer[],
+ FOREIGN KEY <emphasis> (EACH ELEMENT OF final_positions) REFERENCES drivers </emphasis>
+);
+</programlisting>
+
+ The above example uses an array (<literal>final_positions</literal>) to
+ store the results of a race: for each of its elements a referential
+ integrity check is enforced on the <literal>drivers</literal> table. Note
+ that <literal>(EACH ELEMENT OF ...) REFERENCES</literal> is an extension of
+ PostgreSQL and it is not included in the SQL standard.
+ </para>
+
+ <para>
+ Even though the most common use case for array <literal>ELEMENT</literal>
+ foreign keys is on a single column key, you can define an <quote>array
+ <literal>ELEMENT</literal> foreign key constraint</quote> on a group of
+ columns. As the following example shows, it must be written in table
+ constraint form:
+
+<programlisting>
+CREATE TABLE available_moves (
+ kind text,
+ move text,
+ description text,
+ PRIMARY KEY (kind, move)
+);
+
+CREATE TABLE paths (
+ description text,
+ kind text,
+ moves text[],
+ <emphasis>FOREIGN KEY (kind, EACH ELEMENT OF moves) REFERENCES available_moves (kind, move)</emphasis>
+);
+
+INSERT INTO available_moves VALUES ('relative', 'LN', 'look north');
+INSERT INTO available_moves VALUES ('relative', 'RL', 'rotate left');
+INSERT INTO available_moves VALUES ('relative', 'RR', 'rotate right');
+INSERT INTO available_moves VALUES ('relative', 'MF', 'move forward');
+INSERT INTO available_moves VALUES ('absolute', 'N', 'move north');
+INSERT INTO available_moves VALUES ('absolute', 'S', 'move south');
+INSERT INTO available_moves VALUES ('absolute', 'E', 'move east');
+INSERT INTO available_moves VALUES ('absolute', 'W', 'move west');
+
+INSERT INTO paths VALUES ('L-shaped path', 'relative', '{LN, RL, MF, RR, MF, MF}');
+INSERT INTO paths VALUES ('L-shaped path', 'absolute', '{W, N, N}');
+</programlisting>
+
+ On top of standard foreign key requirements, array
+ <literal>ELEMENT</literal> foreign key constraints require that the
+ referencing column is an array of a compatible type of the corresponding
+ referenced column.
+ </para>
+
+ <para>
+ For more detailed information on array <literal>ELEMENT</literal> foreign
+ key options and special cases, please refer to the documentation for <xref
+ linkend="sql-createtable-foreign-key"> and
+ <xref linkend="sql-createtable-element-foreign-key-constraints">.
+ </para>
+
+ </sect2>
+
<sect2 id="ddl-constraints-exclusion">
<title>Exclusion Constraints</title>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 1477288851..f40eefb019 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -65,7 +65,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ] |
UNIQUE <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="PARAMETER">index_parameters</replaceable> |
- REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
+ [ELEMENT] REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -76,7 +76,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
UNIQUE ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ] |
- FOREIGN KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> [, ... ] ) ]
+ FOREIGN KEY ( [ELEMENT] <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) REFERENCES <replaceable class="PARAMETER">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> [, ... ] ) ]
[ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -833,10 +833,10 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</listitem>
</varlistentry>
- <varlistentry>
+ <varlistentry id="sql-createtable-foreign-key" xreflabel="FOREIGN KEY">
<term><literal>REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
- <term><literal>FOREIGN KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] )
+ <term><literal>FOREIGN KEY ( [ELEMENT] <replaceable class="parameter">column_name</replaceable> [, ... ] )
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] ) ]
[ MATCH <replaceable class="parameter">matchtype</replaceable> ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ]
@@ -860,6 +860,37 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</para>
<para>
+ In case the column name <replaceable
+ class="parameter">column</replaceable> is prepended with the
+ <literal>ELEMENT</literal> keyword and <replaceable
+ class="parameter">column</replaceable> is an array of elements
+ compatible with the corresponding <replaceable
+ class="parameter">refcolumn</replaceable> in <replaceable
+ class="parameter">reftable</replaceable>, an array
+ <literal>ELEMENT</literal> foreign key constraint is put in place (see
+ <xref linkend="sql-createtable-element-foreign-key-constraints"> for
+ more information). Multi-column keys with more than one
+ <literal>ELEMENT</literal> column are currently not allowed.
+
+ It is advisable to index the refrencing column using GIN index as it
+ considerably enhances the performance. Also concerning coercion while
+ using the GIN index:
+
+ <programlisting>
+ CREATE TABLE pktableforarray ( ptest1 int2 PRIMARY KEY, ptest2 text );
+ CREATE TABLE fktableforarray ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ </programlisting>
+ This syntax is fine since this will cast ptest1 to int4 upon RI checks, however,
+
+ <programlisting>
+ CREATE TABLE pktableforarray ( ptest1 int4 PRIMARY KEY, ptest2 text );
+ CREATE TABLE fktableforarray ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ </programlisting>
+ This syntax will cast ftest1 to int4 upon RI checks, thus defeating the
+ purpose of the index.
+ </para>
+
+ <para>
A value inserted into the referencing column(s) is matched against the
values of the referenced table and referenced columns using the
given match type. There are three match types: <literal>MATCH
@@ -922,7 +953,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
<para>
Delete any rows referencing the deleted row, or update the
values of the referencing column(s) to the new values of the
- referenced columns, respectively.
+ referenced columns, respectively. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -931,7 +963,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
<term><literal>SET NULL</literal></term>
<listitem>
<para>
- Set the referencing column(s) to null.
+ Set the referencing column(s) to null. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -943,6 +976,8 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
Set the referencing column(s) to their default values.
(There must be a row in the referenced table matching the default
values, if they are not null, or the operation will fail.)
+ Currently not supported with array <literal>ELEMENT</literal>
+ foreign keys.
</para>
</listitem>
</varlistentry>
@@ -958,6 +993,67 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
</listitem>
</varlistentry>
+ <varlistentry id="sql-createtable-element-foreign-key-constraints" xreflabel="ELEMENT REFERENCES">
+ <term><literal>ELEMENT REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
+
+ <listitem>
+ <para>
+ The <literal>ELEMENT REFERENCES</literal> definition specifies an
+ <quote>array <literal>ELEMENT</literal> foreign key</quote>, a special
+ kind of foreign key constraint requiring the referencing column to be an
+ array of elements of the same type (or a compatible one) as the
+ referenced column in the referenced table. The value of each element of
+ the <replaceable class="parameter">refcolumn</replaceable> array will be
+ matched against some row of <replaceable
+ class="parameter">reftable</replaceable>.
+ </para>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys are an extension
+ of PostgreSQL and are not included in the SQL standard.
+ </para>
+
+ <para>
+ Even with <literal>ELEMENT</literal> foreign keys, modifications in the
+ referenced column can trigger actions to be performed on the referencing
+ array. Similarly to standard foreign keys, you can specify these actions
+ using the <literal>ON DELETE</literal> and <literal>ON UPDATE</literal>
+ clauses. However, only the following actions for each clause are
+ currently allowed:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>ON UPDATE/DELETE NO ACTION</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints. This is the default action.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ON UPDATE/DELETE RESTRICT</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ON DELETE CASCADE</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>DEFERRABLE</literal></term>
<term><literal>NOT DEFERRABLE</literal></term>
@@ -1904,6 +2000,16 @@ CREATE TABLE cities_partdef
</refsect2>
<refsect2>
+ <title id="sql-createtable-foreign-key-arrays">Array <literal>ELEMENT</literal> Foreign Keys</title>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys and the <literal>ELEMENT
+ REFERENCES</literal> clause are a <productname>PostgreSQL</productname>
+ extension.
+ </para>
+ </refsect2>
+
+ <refsect2>
<title><literal>PARTITION BY</> Clause</title>
<para>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 05e70818e7..4677c94d8e 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2124,6 +2124,7 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c7b2f031f0..db7978ec45 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1213,6 +1213,7 @@ index_constraint_create(Relation heapRelation,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 1336c46d3f..924affcdcd 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -59,6 +59,7 @@ CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
@@ -82,6 +83,7 @@ CreateConstraintEntry(const char *constraintName,
Datum values[Natts_pg_constraint];
ArrayType *conkeyArray;
ArrayType *confkeyArray;
+ ArrayType *confreftypeArray;
ArrayType *conpfeqopArray;
ArrayType *conppeqopArray;
ArrayType *conffeqopArray;
@@ -119,7 +121,11 @@ CreateConstraintEntry(const char *constraintName,
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = Int16GetDatum(foreignKey[i]);
confkeyArray = construct_array(fkdatums, foreignNKeys,
- INT2OID, 2, true, 's');
+ INT2OID, sizeof(int16), true, 's');
+ for (i = 0; i < foreignNKeys; i++)
+ fkdatums[i] = CharGetDatum(foreignRefType[i]);
+ confreftypeArray = construct_array(fkdatums, foreignNKeys,
+ CHAROID, sizeof(char), true, 'c');
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
conpfeqopArray = construct_array(fkdatums, foreignNKeys,
@@ -136,6 +142,7 @@ CreateConstraintEntry(const char *constraintName,
else
{
confkeyArray = NULL;
+ confreftypeArray = NULL;
conpfeqopArray = NULL;
conppeqopArray = NULL;
conffeqopArray = NULL;
@@ -188,6 +195,11 @@ CreateConstraintEntry(const char *constraintName,
else
nulls[Anum_pg_constraint_confkey - 1] = true;
+ if (confreftypeArray)
+ values[Anum_pg_constraint_confreftype - 1] = PointerGetDatum(confreftypeArray);
+ else
+ nulls[Anum_pg_constraint_confreftype - 1] = true;
+
if (conpfeqopArray)
values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
else
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 563bcda30c..aa43c552c5 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7103,6 +7103,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Relation pkrel;
int16 pkattnum[INDEX_MAX_KEYS];
int16 fkattnum[INDEX_MAX_KEYS];
+ char fkreftypes[INDEX_MAX_KEYS];
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
Oid opclasses[INDEX_MAX_KEYS];
@@ -7110,10 +7111,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid ppeqoperators[INDEX_MAX_KEYS];
Oid ffeqoperators[INDEX_MAX_KEYS];
int i;
+ ListCell *lc;
int numfks,
numpks;
Oid indexOid;
Oid constrOid;
+ bool has_array;
bool old_check_ok;
ObjectAddress address;
ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
@@ -7190,6 +7193,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
*/
MemSet(pkattnum, 0, sizeof(pkattnum));
MemSet(fkattnum, 0, sizeof(fkattnum));
+ MemSet(fkreftypes, 0, sizeof(fkreftypes));
MemSet(pktypoid, 0, sizeof(pktypoid));
MemSet(fktypoid, 0, sizeof(fktypoid));
MemSet(opclasses, 0, sizeof(opclasses));
@@ -7202,6 +7206,51 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
fkattnum, fktypoid);
/*
+ * Validate the reference semantics codes, too, and convert list to array
+ * format to pass to CreateConstraintEntry.
+ */
+ Assert(list_length(fkconstraint->fk_reftypes) == numfks);
+ has_array = false;
+ i = 0;
+ foreach(lc, fkconstraint->fk_reftypes)
+ {
+ char reftype = lfirst_int(lc);
+
+ switch (reftype)
+ {
+ case FKCONSTR_REF_PLAIN:
+ /* OK, nothing to do */
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ /* At most one FK column can be an array reference */
+ if (has_array)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign keys support only one array column")));
+ has_array = true;
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d", (int) reftype);
+ break;
+ }
+ fkreftypes[i] = reftype;
+ i++;
+ }
+
+ /* Array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions */
+ if (has_array)
+ {
+ if ((fkconstraint->fk_upd_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_upd_action != FKCONSTR_ACTION_RESTRICT) ||
+ (fkconstraint->fk_del_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_CASCADE))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions")));
+ }
+
+ /*
* If the attribute list for the referenced table was omitted, lookup the
* definition of the primary key and use it. Otherwise, validate the
* supplied attribute list. In either case, discover the index OID and
@@ -7287,6 +7336,65 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
eqstrategy = BTEqualStrategyNumber;
/*
+ * If this is an array foreign key, we must look up the operators for
+ * the array element type, not the array type itself.
+ */
+ if (fkreftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ Oid elemopclass;
+
+ /* We look through any domain here */
+ fktype = get_base_element_type(fktype);
+ if (!OidIsValid(fktype))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has type %s which is not an array type.",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktypoid[i]))));
+
+ /*
+ * For the moment, we must also insist that the array's element
+ * type have a default btree opclass that is in the index's
+ * opfamily. This is necessary because ri_triggers.c relies on
+ * COUNT(DISTINCT x) on the element type, as well as on array_eq()
+ * on the array type, and we need those operations to have the
+ * same notion of equality that we're using otherwise.
+ *
+ * XXX this restriction is pretty annoying, considering the effort
+ * that's been put into the rest of the RI mechanisms to make them
+ * work with nondefault equality operators. In particular, it
+ * means that the cast-to-PK-datatype code path isn't useful for
+ * array-to-scalar references.
+ */
+ elemopclass = GetDefaultOpClass(fktype, BTREE_AM_OID);
+ if (!OidIsValid(elemopclass) ||
+ get_opclass_family(elemopclass) != opfamily)
+ {
+ /* Get the index opclass's name for the error message. */
+ char *opcname;
+
+ cla_ht = SearchSysCache1(CLAOID,
+ ObjectIdGetDatum(opclasses[i]));
+ if (!HeapTupleIsValid(cla_ht))
+ elog(ERROR, "cache lookup failed for opclass %u",
+ opclasses[i]);
+ cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
+ opcname = pstrdup(NameStr(cla_tup->opcname));
+ ReleaseSysCache(cla_ht);
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has element type %s which does not have a default btree operator class that's compatible with class \"%s\".",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktype),
+ opcname)));
+ }
+ }
+
+ /*
* There had better be a primary equality operator for the index.
* We'll use it for PK = PK comparisons.
*/
@@ -7347,14 +7455,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("foreign key constraint \"%s\" "
- "cannot be implemented",
- fkconstraint->conname),
- errdetail("Key columns \"%s\" and \"%s\" "
- "are of incompatible types: %s and %s.",
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key columns \"%s\" and \"%s\" are of incompatible types: %s and %s.",
strVal(list_nth(fkconstraint->fk_attrs, i)),
strVal(list_nth(fkconstraint->pk_attrs, i)),
- format_type_be(fktype),
+ format_type_be(fktypoid[i]),
format_type_be(pktype))));
if (old_check_ok)
@@ -7385,6 +7491,13 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
* We may assume that pg_constraint.conkey is not changing.
*/
old_fktype = attr->atttypid;
+ if (fkreftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ old_fktype = get_base_element_type(old_fktype);
+ /* this shouldn't happen ... */
+ if (!OidIsValid(old_fktype))
+ elog(ERROR, "old foreign key column is not an array");
+ }
new_fktype = fktype;
old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
&old_castfunc);
@@ -7427,7 +7540,6 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
new_castfunc == old_castfunc &&
(!IsPolymorphicType(pfeqop_right) ||
new_fktype == old_fktype));
-
}
pfeqoperators[i] = pfeqop;
@@ -7451,6 +7563,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
indexOid,
RelationGetRelid(pkrel),
pkattnum,
+ fkreftypes,
pfeqoperators,
ppeqoperators,
ffeqoperators,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index e75a59d299..8e2a482dd3 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -661,6 +661,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
@@ -1032,6 +1033,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
char fk_matchtype = FKCONSTR_MATCH_SIMPLE;
List *fk_attrs = NIL;
List *pk_attrs = NIL;
+ List *fk_reftypes = NIL;
StringInfoData buf;
int funcnum;
OldTriggerInfo *info = NULL;
@@ -1061,7 +1063,10 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
if (i % 2)
fk_attrs = lappend(fk_attrs, arg);
else
- pk_attrs = lappend(pk_attrs, arg);
+ {
+ pk_attrs = lappend(pk_attrs, arg);
+ fk_reftypes = lappend_int(fk_reftypes, FKCONSTR_REF_PLAIN);
+ }
}
/* Prepare description of constraint for use in messages */
@@ -1200,6 +1205,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
fkcon->conname = constr_name;
fkcon->fk_attrs = fk_attrs;
fkcon->pk_attrs = pk_attrs;
+ fkcon->fk_reftypes = fk_reftypes;
fkcon->fk_matchtype = fk_matchtype;
switch (info->funcoids[0])
{
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 7ed16aeff4..8639854f4d 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -3092,6 +3092,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f1bed14e2b..26e2d3da94 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2847,6 +2847,7 @@ _copyConstraint(const Constraint *from)
COPY_NODE_FIELD(pktable);
COPY_NODE_FIELD(fk_attrs);
COPY_NODE_FIELD(pk_attrs);
+ COPY_NODE_FIELD(fk_reftypes);
COPY_SCALAR_FIELD(fk_matchtype);
COPY_SCALAR_FIELD(fk_upd_action);
COPY_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 8b56b9146a..581bf601ce 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2576,6 +2576,7 @@ _equalConstraint(const Constraint *a, const Constraint *b)
COMPARE_NODE_FIELD(pktable);
COMPARE_NODE_FIELD(fk_attrs);
COMPARE_NODE_FIELD(pk_attrs);
+ COMPARE_NODE_FIELD(fk_reftypes);
COMPARE_SCALAR_FIELD(fk_matchtype);
COMPARE_SCALAR_FIELD(fk_upd_action);
COMPARE_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index b83d919e40..100f6fb570 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3492,6 +3492,7 @@ _outConstraint(StringInfo str, const Constraint *node)
WRITE_NODE_FIELD(pktable);
WRITE_NODE_FIELD(fk_attrs);
WRITE_NODE_FIELD(pk_attrs);
+ WRITE_NODE_FIELD(fk_reftypes);
WRITE_CHAR_FIELD(fk_matchtype);
WRITE_CHAR_FIELD(fk_upd_action);
WRITE_CHAR_FIELD(fk_del_action);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c303818c9b..5adeb61d99 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -126,6 +126,13 @@ typedef struct ImportQual
List *table_names;
} ImportQual;
+/* Private struct for the result of foreign_key_column_elem production */
+typedef struct FKColElem
+{
+ Node *name; /* name of the column (a String) */
+ char reftype; /* FKCONSTR_REF_xxx code */
+} FKColElem;
+
/* ConstraintAttributeSpec yields an integer bitmask of these flags: */
#define CAS_NOT_DEFERRABLE 0x01
#define CAS_DEFERRABLE 0x02
@@ -183,6 +190,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
static void SplitColQualList(List *qualList,
List **constraintList, CollateClause **collClause,
core_yyscan_t yyscanner);
+static void SplitFKColElems(List *fkcolelems, List **names, List **reftypes);
static void processCASbits(int cas_bits, int location, const char *constrType,
bool *deferrable, bool *initdeferred, bool *not_valid,
bool *no_inherit, core_yyscan_t yyscanner);
@@ -233,6 +241,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
A_Indices *aind;
ResTarget *target;
struct PrivTarget *privtarget;
+ struct FKColElem *fkcolelem;
AccessPriv *accesspriv;
struct ImportQual *importqual;
InsertStmt *istmt;
@@ -358,6 +367,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <accesspriv> privilege
%type <list> privileges privilege_list
%type <privtarget> privilege_target
+%type <fkcolelem> foreign_key_column_elem
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
%type <ival> defacl_privilege_target
@@ -392,7 +402,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
execute_param_clause using_clause returning_clause
opt_enum_val_list enum_val_list table_func_column_list
create_generic_options alter_generic_options
- relation_expr_list dostmt_opt_list
+ relation_expr_list dostmt_opt_list foreign_key_column_list
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
@@ -622,8 +632,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
DOUBLE_P DROP
- EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
- EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
+ EACH ELEMENT ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE
+ EVENT EXCEPT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
EXTENSION EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FILTER FIRST_P FLOAT_P FOLLOWING FOR
@@ -3438,8 +3448,10 @@ ColConstraintElem:
n->contype = CONSTR_FOREIGN;
n->location = @1;
n->pktable = $2;
+ /* fk_attrs will be filled in by parse analysis */
n->fk_attrs = NIL;
n->pk_attrs = $3;
+ n->fk_reftypes = list_make1_int(FKCONSTR_REF_PLAIN);
n->fk_matchtype = $4;
n->fk_upd_action = (char) ($5 >> 8);
n->fk_del_action = (char) ($5 & 0xFF);
@@ -3633,14 +3645,15 @@ ConstraintElem:
NULL, yyscanner);
$$ = (Node *)n;
}
- | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
- opt_column_list key_match key_actions ConstraintAttributeSpec
+ | FOREIGN KEY '(' foreign_key_column_list ')' REFERENCES
+ qualified_name opt_column_list key_match key_actions
+ ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_FOREIGN;
n->location = @1;
+ SplitFKColElems($4, &n->fk_attrs, &n->fk_reftypes);
n->pktable = $7;
- n->fk_attrs = $4;
n->pk_attrs = $8;
n->fk_matchtype = $9;
n->fk_upd_action = (char) ($10 >> 8);
@@ -3673,6 +3686,29 @@ columnElem: ColId
$$ = (Node *) makeString($1);
}
;
+ foreign_key_column_list:
+ foreign_key_column_elem
+ { $$ = list_make1($1); }
+ | foreign_key_column_list ',' foreign_key_column_elem
+ { $$ = lappend($1, $3); }
+ ;
+
+ foreign_key_column_elem:
+ ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($1);
+ n->reftype = FKCONSTR_REF_PLAIN;
+ $$ = n;
+ }
+ | EACH ELEMENT OF ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($4);
+ n->reftype = FKCONSTR_REF_EACH_ELEMENT;
+ $$ = n;
+ }
+ ;
key_match: MATCH FULL
{
@@ -14684,6 +14720,7 @@ unreserved_keyword:
| DOUBLE_P
| DROP
| EACH
+ | ELEMENT
| ENABLE_P
| ENCODING
| ENCRYPTED
@@ -15801,6 +15838,23 @@ SplitColQualList(List *qualList,
*constraintList = qualList;
}
+/* Split a list of FKColElem structs into separate name and reftype lists */
+static void
+SplitFKColElems(List *fkcolelems, List **names, List **reftypes)
+{
+ ListCell *lc;
+
+ *names = NIL;
+ *reftypes = NIL;
+ foreach(lc, fkcolelems)
+ {
+ FKColElem *fkcolelem = (FKColElem *) lfirst(lc);
+
+ *names = lappend(*names, fkcolelem->name);
+ *reftypes = lappend_int(*reftypes, fkcolelem->reftype);
+ }
+}
+
/*
* Process result of ConstraintAttributeSpec, and set appropriate bool flags
* in the output command node. Pass NULL for any flags the particular
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index e79ad26e71..4722a1bf79 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1320,6 +1320,90 @@ select_common_type(ParseState *pstate, List *exprs, const char *context,
}
/*
+ * look at select_common_type(); in parse_coerce.c(src/backend/parser/parse_coerce.c)
+ *
+ * select_common_type_2args()
+ * Determine the common supertype of a list of input expressions.
+ * 'leftOid': left operand's type
+ * 'rightOid': left operand's type
+ */
+Oid select_common_type_2args(Oid leftOid, Oid rightOid)
+{
+ TYPCATEGORY leftcategory;
+ TYPCATEGORY rightcategory;
+ bool leftispreferred;
+ bool rightispreferred;
+
+ Assert(leftOid != InvalidOid);
+ Assert(rightOid != InvalidOid);
+
+ /*
+ * If input types are valid and exactly the same, just pick that type.
+ */
+ if (leftOid != UNKNOWNOID)
+ {
+ if (rightOid == leftOid)
+ return leftOid;
+ }
+
+ /*
+ * Nope, so set up for the full algorithm.
+ */
+ leftOid = getBaseType(leftOid);
+ rightOid = getBaseType(rightOid);
+ get_type_category_preferred(leftOid, &leftcategory, &leftispreferred);
+ get_type_category_preferred(rightOid, &rightcategory, &rightispreferred);
+
+ if (rightOid != UNKNOWNOID && rightOid != leftOid)
+ {
+ if (rightcategory != leftcategory)
+ {
+ /* both types in different categories? then not much hope... */
+ return InvalidOid;
+ }
+
+ if (can_coerce_type(1, &rightOid, &leftOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left */
+ if (!can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can not coerce to it implicitly right to left, thus coercion only works one way */
+ return leftOid;
+ }
+ else
+ {
+ /* coercion works both ways, then decide depending on preferred flag */
+ if (leftispreferred)
+ return leftOid;
+ else
+ return rightOid;
+ }
+ }
+ else{
+ /* can not coerce to it implicitly right to left */
+ if (can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left, thus coercion only works one way */
+ return rightOid;
+ }
+ else{
+ /* can not coerce either way */
+ return InvalidOid;
+ }
+ }
+ }
+
+ /*
+ * If all the inputs were UNKNOWN type --- ie, unknown-type literals ---
+ * then resolve as type TEXT.
+ */
+ if (leftOid == UNKNOWNOID)
+ leftOid = TEXTOID;
+
+ return leftOid;
+}
+
+/*
* coerce_to_common_type()
* Coerce an expression to the given type.
*
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 27e568fc62..a59b213ad2 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -745,6 +745,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
* list of FK constraints to be processed later.
*/
constraint->fk_attrs = list_make1(makeString(column->colname));
+ /* grammar should have set fk_reftypes */
+ Assert(list_length(constraint->fk_reftypes) == 1);
cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
break;
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index c2891e6fa1..8800fba4a5 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -118,9 +118,11 @@ typedef struct RI_ConstraintInfo
char confupdtype; /* foreign key's ON UPDATE action */
char confdeltype; /* foreign key's ON DELETE action */
char confmatchtype; /* foreign key's match type */
+ bool has_array; /* true if any reftype is EACH_ELEMENT */
int nkeys; /* number of key columns */
int16 pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */
int16 fk_attnums[RI_MAX_NUMKEYS]; /* attnums of referencing cols */
+ char fk_reftypes[RI_MAX_NUMKEYS]; /* reference semantics */
Oid pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = FK) */
Oid pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = PK) */
Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK = FK) */
@@ -204,7 +206,8 @@ static void ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype);
+ const char *rightop, Oid rightoptype,
+ char fkreftype);
static void ri_add_cast_to(StringInfo buf, Oid typid);
static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
static int ri_NullCheck(HeapTuple tup,
@@ -395,6 +398,7 @@ RI_FKey_check(TriggerData *trigdata)
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
StringInfoData querybuf;
+ StringInfoData countbuf;
char pkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
char paramname[16];
@@ -407,12 +411,22 @@ RI_FKey_check(TriggerData *trigdata)
* FOR KEY SHARE OF x
* The type id's for the $ parameters are those of the
* corresponding FK attributes.
+ *
+ * In case of an array ELEMENT foreign key, the previous query is used
+ * to count the number of matching rows and see if every combination
+ * is actually referenced.
+ * The wrapping query is
+ * SELECT 1 WHERE
+ * (SELECT count(DISTINCT y) FROM unnest($1) y)
+ * = (SELECT count(*) FROM (<QUERY>) z)
* ----------
*/
initStringInfo(&querybuf);
quoteRelationName(pkrelname, pk_rel);
appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
querysep = "WHERE";
+ initStringInfo(&countbuf);
+ appendStringInfo(&countbuf, "SELECT 1 WHERE ");
for (i = 0; i < riinfo->nkeys; i++)
{
Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
@@ -421,18 +435,41 @@ RI_FKey_check(TriggerData *trigdata)
quoteOneName(attname,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
sprintf(paramname, "$%d", i + 1);
+
+ /*
+ * In case of an array ELEMENT foreign key, we check that each
+ * distinct non-null value in the array is present in the PK
+ * table.
+ */
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&countbuf,
+ "(SELECT pg_catalog.count(DISTINCT y) FROM pg_catalog.unnest(%s) y)",
+ paramname);
+
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pf_eq_oprs[i],
- paramname, fk_type);
+ paramname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = fk_type;
}
appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
- /* Prepare and save the plan */
- qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
- &qkey, fk_rel, pk_rel, true);
+ if (riinfo->has_array)
+ {
+ appendStringInfo(&countbuf,
+ " OPERATOR(pg_catalog.=) (SELECT pg_catalog.count(*) FROM (%s) z)",
+ querybuf.data);
+
+ /* Prepare and save the plan for array ELEMENT foreign keys */
+ qplan = ri_PlanCheck(countbuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
+ }
+ else
+ /* Prepare and save the plan */
+ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
}
/*
@@ -559,7 +596,8 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pp_eq_oprs[i],
- paramname, pk_type);
+ paramname, pk_type,
+ FKCONSTR_REF_PLAIN);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -751,7 +789,8 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -974,7 +1013,8 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1130,7 +1170,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1309,7 +1350,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1475,7 +1517,8 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1651,7 +1694,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1817,7 +1861,8 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2008,7 +2053,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2327,6 +2373,14 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
* For MATCH FULL:
* (fk.keycol1 IS NOT NULL [OR ...])
*
+ * In case of an array ELEMENT column, relname is replaced with the
+ * following subquery:
+ *
+ * SELECT unnest("keycol1") k1, "keycol1" ak1 [, ...]
+ * FROM ONLY "public"."fk"
+ *
+ * where all the columns are renamed in order to prevent name collisions.
+ *
* We attach COLLATE clauses to the operators when comparing columns
* that have different collations.
*----------
@@ -2338,15 +2392,46 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
{
quoteOneName(fkattname,
RIAttName(fk_rel, riinfo->fk_attnums[i]));
- appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
+ if (riinfo->has_array)
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%sfk.ak%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.k%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
sep = ", ";
}
quoteRelationName(pkrelname, pk_rel);
quoteRelationName(fkrelname, fk_rel);
- appendStringInfo(&querybuf,
- " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
- fkrelname, pkrelname);
+
+ if (riinfo->has_array)
+ {
+ sep = "";
+ appendStringInfo(&querybuf,
+ " FROM (SELECT ");
+ for (i = 0; i < riinfo->nkeys; i++)
+ {
+ quoteOneName(fkattname,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%spg_catalog.unnest(%s) k%d, %s ak%d",
+ sep, fkattname, i + 1, fkattname, i + 1);
+ else
+ appendStringInfo(&querybuf, "%s%s k%d", sep, fkattname,
+ i + 1);
+ sep = ", ";
+ }
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s) fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
+ }
+ else
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
strcpy(pkattname, "pk.");
strcpy(fkattname, "fk.");
@@ -2360,12 +2445,16 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
quoteOneName(pkattname + 3,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
- quoteOneName(fkattname + 3,
- RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname + 3, "k%d", i + 1);
+ else
+ quoteOneName(fkattname + 3,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
ri_GenerateQual(&querybuf, sep,
pkattname, pk_type,
riinfo->pf_eq_oprs[i],
- fkattname, fk_type);
+ fkattname, fk_type,
+ FKCONSTR_REF_PLAIN);
if (pk_coll != fk_coll)
ri_GenerateQualCollation(&querybuf, pk_coll);
sep = "AND";
@@ -2381,7 +2470,10 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
sep = "";
for (i = 0; i < riinfo->nkeys; i++)
{
- quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname, "k%d", i + 1);
+ else
+ quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
appendStringInfo(&querybuf,
"%sfk.%s IS NOT NULL",
sep, fkattname);
@@ -2557,20 +2649,23 @@ quoteRelationName(char *buffer, Relation rel)
/*
* ri_GenerateQual --- generate a WHERE clause equating two variables
*
- * The idea is to append " sep leftop op rightop" to buf. The complexity
- * comes from needing to be sure that the parser will select the desired
- * operator. We always name the operator using OPERATOR(schema.op) syntax
- * (readability isn't a big priority here), so as to avoid search-path
- * uncertainties. We have to emit casts too, if either input isn't already
- * the input type of the operator; else we are at the mercy of the parser's
- * heuristics for ambiguous-operator resolution.
- */
+ * The idea is to append " sep leftop op rightop" to buf, or if fkreftype is
+ * FKCONSTR_REF_EACH_ELEMENT, append " sep rightop @> leftop" to buf.
+ *
+ * The complexity comes from needing to be sure that the parser will select
+ * the desired operator. We always name the operator using
+ * OPERATOR(schema.op) syntax (readability isn't a big priority here), so as
+ * to avoid search-path uncertainties. We have to emit casts too, if either
+ * input isn't already the input type of the operator; else we are at the
+ * mercy of the parser's heuristics for ambiguous-operator resolution.
+ */
static void
ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype)
+ const char *rightop, Oid rightoptype,
+ char fkreftype)
{
HeapTuple opertup;
Form_pg_operator operform;
@@ -2586,14 +2681,67 @@ ri_GenerateQual(StringInfo buf,
nspname = get_namespace_name(operform->oprnamespace);
- appendStringInfo(buf, " %s %s", sep, leftop);
- if (leftoptype != operform->oprleft)
- ri_add_cast_to(buf, operform->oprleft);
- appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
- appendStringInfoString(buf, oprname);
- appendStringInfo(buf, ") %s", rightop);
- if (rightoptype != operform->oprright)
- ri_add_cast_to(buf, operform->oprright);
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ Oid oprright;
+ Oid oprleft;
+ Oid oprcommon;
+
+ /*
+ * we first need to get the array types and decide
+ * the more appropriate one
+ */
+
+ /* get array type of refrenced element*/
+ oprright = get_array_type(operform->oprleft);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprleft))));
+
+ /* get array type of refrencing element*/
+ oprleft = get_array_type(operform->oprright);
+ if (!OidIsValid(oprleft))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprright))));
+
+ /* compare the two array types and try to find
+ a common supertype */
+ oprcommon = select_common_type_2args(oprleft, oprright);
+ if (!OidIsValid(oprcommon))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find common type between %s and %s",
+ format_type_be(oprleft), format_type_be(oprright))));
+
+ appendStringInfo(buf, " %s %s", sep, rightop);
+ if (oprleft != oprcommon)
+ ri_add_cast_to(buf, oprcommon);
+
+ appendStringInfo(buf, " OPERATOR(pg_catalog.@>) ");
+
+ appendStringInfo(buf, " ARRAY[%s]", leftop);
+ if (oprright != oprcommon)
+ ri_add_cast_to(buf, oprcommon);
+ }
+ else
+ {
+ appendStringInfo(buf, " %s %s", sep, leftop);
+
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+
+ appendStringInfo(buf, " OPERATOR(%s.%s) ",
+ quote_identifier(nspname), oprname);
+
+ appendStringInfoString(buf, rightop);
+
+ if (rightoptype != operform->oprright)
+ ri_add_cast_to(buf, operform->oprright);
+ }
ReleaseSysCache(opertup);
}
@@ -2801,6 +2949,7 @@ ri_LoadConstraintInfo(Oid constraintOid)
bool isNull;
ArrayType *arr;
int numkeys;
+ int i;
/*
* On the first call initialize the hashtable
@@ -2879,6 +3028,20 @@ ri_LoadConstraintInfo(Oid constraintOid)
pfree(arr); /* free de-toasted copy, if any */
adatum = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype, &isNull);
+ if (isNull)
+ elog(ERROR, "null confreftype for constraint %u", constraintOid);
+ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
+ if (ARR_NDIM(arr) != 1 ||
+ ARR_DIMS(arr)[0] != numkeys ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != CHAROID)
+ elog(ERROR, "confreftype is not a 1-D char array");
+ memcpy(riinfo->fk_reftypes, ARR_DATA_PTR(arr), numkeys * sizeof(char));
+ if ((Pointer) arr != DatumGetPointer(adatum))
+ pfree(arr); /* free de-toasted copy, if any */
+
+ adatum = SysCacheGetAttr(CONSTROID, tup,
Anum_pg_constraint_conpfeqop, &isNull);
if (isNull)
elog(ERROR, "null conpfeqop for constraint %u", constraintOid);
@@ -2921,6 +3084,24 @@ ri_LoadConstraintInfo(Oid constraintOid)
if ((Pointer) arr != DatumGetPointer(adatum))
pfree(arr); /* free de-toasted copy, if any */
+ /*
+ * Fix up some stuff for array foreign keys. We need a has_array flag
+ * indicating whether there's an array foreign key, and we want to set
+ * ff_eq_oprs[i] to array_eq() for array columns, because that's what
+ * makes sense for ri_KeysEqual, and we have no other use for ff_eq_oprs
+ * in this module. (If we did, substituting the array comparator at the
+ * call point in ri_KeysEqual might be more appropriate.)
+ */
+ riinfo->has_array = false;
+ for (i = 0; i < numkeys; i++)
+ {
+ if (riinfo->fk_reftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ riinfo->has_array = true;
+ riinfo->ff_eq_oprs[i] = ARRAY_EQ_OP;
+ }
+ }
+
ReleaseSysCache(tup);
/*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 84759b6149..3b5520ddf1 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -314,6 +314,9 @@ static char *pg_get_viewdef_worker(Oid viewoid,
static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
static void decompile_column_index_array(Datum column_index_array, Oid relId,
StringInfo buf);
+static void decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf);
static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
const Oid *excludeOps,
@@ -1882,7 +1885,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
{
case CONSTRAINT_FOREIGN:
{
- Datum val;
+ Datum colindexes;
+ Datum reftypes;
bool isnull;
const char *string;
@@ -1890,13 +1894,21 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
appendStringInfoString(&buf, "FOREIGN KEY (");
/* Fetch and build referencing-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_conkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_conkey,
+ &isnull);
if (isnull)
elog(ERROR, "null conkey for constraint %u",
constraintId);
+ reftypes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype,
+ &isnull);
+ if (isnull)
+ elog(ERROR, "null confreftype for constraint %u",
+ constraintId);
- decompile_column_index_array(val, conForm->conrelid, &buf);
+ decompile_fk_column_index_array(colindexes, reftypes,
+ conForm->conrelid, &buf);
/* add foreign relation name */
appendStringInfo(&buf, ") REFERENCES %s(",
@@ -1904,13 +1916,15 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
NIL));
/* Fetch and build referenced-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_confkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confkey,
+ &isnull);
if (isnull)
elog(ERROR, "null confkey for constraint %u",
constraintId);
- decompile_column_index_array(val, conForm->confrelid, &buf);
+ decompile_column_index_array(colindexes,
+ conForm->confrelid, &buf);
appendStringInfoChar(&buf, ')');
@@ -2185,6 +2199,66 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
}
}
+ /*
+ * Convert an int16[] Datum and a char[] Datum into a comma-separated
+ * list of column names for the indicated relation, prefixed by appropriate
+ * keywords depending on the foreign key reference semantics indicated by
+ * the char[] entries. Append the text to buf.
+ */
+ static void
+ decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf)
+ {
+ Datum *keys;
+ int nKeys;
+ Datum *reftypes;
+ int nReftypes;
+ int j;
+
+ /* Extract data from array of int16 */
+ deconstruct_array(DatumGetArrayTypeP(column_index_array),
+ INT2OID, sizeof(int16), true, 's',
+ &keys, NULL, &nKeys);
+
+ /* Extract data from array of char */
+ deconstruct_array(DatumGetArrayTypeP(fk_reftype_array),
+ CHAROID, sizeof(char), true, 'c',
+ &reftypes, NULL, &nReftypes);
+
+ if (nKeys != nReftypes)
+ elog(ERROR, "wrong confreftype cardinality");
+
+ for (j = 0; j < nKeys; j++)
+ {
+ char *colName;
+ const char *prefix;
+
+ colName = get_relid_attribute_name(relId, DatumGetInt16(keys[j]));
+
+ switch (DatumGetChar(reftypes[j]))
+ {
+ case FKCONSTR_REF_PLAIN:
+ prefix = "";
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ prefix = "EACH ELEMENT OF ";
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d",
+ (int) DatumGetChar(reftypes[j]));
+ prefix = NULL; /* keep compiler quiet */
+ break;
+ }
+
+ if (j == 0)
+ appendStringInfo(buf, "%s%s", prefix,
+ quote_identifier(colName));
+ else
+ appendStringInfo(buf, ", %s%s", prefix,
+ quote_identifier(colName));
+ }
+}
/* ----------
* get_expr - Decompile an expression tree
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index ec035d8434..a6b296d4a0 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -104,8 +104,16 @@ CATALOG(pg_constraint,2606)
int16 confkey[1];
/*
+ * If a foreign key, the reference semantics for each column
+ */
+ char confreftype[1];
+
+ /*
* If a foreign key, the OIDs of the PK = FK equality operators for each
* column of the constraint
+ *
+ * Note: for array foreign keys, all these operators are for the array's
+ * element type.
*/
Oid conpfeqop[1];
@@ -150,7 +158,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* compiler constants for pg_constraint
* ----------------
*/
-#define Natts_pg_constraint 24
+#define Natts_pg_constraint 25
#define Anum_pg_constraint_conname 1
#define Anum_pg_constraint_connamespace 2
#define Anum_pg_constraint_contype 3
@@ -169,12 +177,13 @@ typedef FormData_pg_constraint *Form_pg_constraint;
#define Anum_pg_constraint_connoinherit 16
#define Anum_pg_constraint_conkey 17
#define Anum_pg_constraint_confkey 18
-#define Anum_pg_constraint_conpfeqop 19
-#define Anum_pg_constraint_conppeqop 20
-#define Anum_pg_constraint_conffeqop 21
-#define Anum_pg_constraint_conexclop 22
-#define Anum_pg_constraint_conbin 23
-#define Anum_pg_constraint_consrc 24
+#define Anum_pg_constraint_confreftype 19
+#define Anum_pg_constraint_conpfeqop 20
+#define Anum_pg_constraint_conppeqop 21
+#define Anum_pg_constraint_conffeqop 22
+#define Anum_pg_constraint_conexclop 23
+#define Anum_pg_constraint_conbin 24
+#define Anum_pg_constraint_consrc 25
/* ----------------
* initial contents of pg_constraint
@@ -195,7 +204,9 @@ typedef FormData_pg_constraint *Form_pg_constraint;
/*
* Valid values for confupdtype and confdeltype are the FKCONSTR_ACTION_xxx
* constants defined in parsenodes.h. Valid values for confmatchtype are
- * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h.
+ * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h. Valid values
+ * for elements of confreftype[] are the FKCONSTR_REF_xxx constants defined
+ * in parsenodes.h.
*/
#endif /* PG_CONSTRAINT_H */
diff --git a/src/include/catalog/pg_constraint_fn.h b/src/include/catalog/pg_constraint_fn.h
index a4c46897ed..9805ed220a 100644
--- a/src/include/catalog/pg_constraint_fn.h
+++ b/src/include/catalog/pg_constraint_fn.h
@@ -40,6 +40,7 @@ extern Oid CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index f3e4c69753..dcce78f8cb 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2075,6 +2075,10 @@ typedef enum ConstrType /* types of constraints */
#define FKCONSTR_MATCH_PARTIAL 'p'
#define FKCONSTR_MATCH_SIMPLE 's'
+ /* Foreign key column reference semantics codes */
+ #define FKCONSTR_REF_PLAIN 'p'
+ #define FKCONSTR_REF_EACH_ELEMENT 'e'
+
typedef struct Constraint
{
NodeTag type;
@@ -2110,6 +2114,7 @@ typedef struct Constraint
RangeVar *pktable; /* Primary key table */
List *fk_attrs; /* Attributes of foreign key */
List *pk_attrs; /* Corresponding attrs in PK table */
+ List *fk_reftypes; /* Per-column reference semantics (int List) */
char fk_matchtype; /* FULL, PARTIAL, SIMPLE */
char fk_upd_action; /* ON UPDATE action */
char fk_del_action; /* ON DELETE action */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f50e45e886..d3f4803006 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -140,6 +140,7 @@ PG_KEYWORD("domain", DOMAIN_P, UNRESERVED_KEYWORD)
PG_KEYWORD("double", DOUBLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD)
PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD)
+PG_KEYWORD("element", ELEMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("else", ELSE, RESERVED_KEYWORD)
PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index 06f65293cb..f5bad57b70 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -68,6 +68,7 @@ extern int parser_coercion_errposition(ParseState *pstate,
extern Oid select_common_type(ParseState *pstate, List *exprs,
const char *context, Node **which_expr);
+extern Oid select_common_type_2args(Oid ptype, Oid ntype);
extern Node *coerce_to_common_type(ParseState *pstate, Node *node,
Oid targetTypeId,
const char *context);
diff --git a/src/test/regress/expected/element_foreign_key.out b/src/test/regress/expected/element_foreign_key.out
new file mode 100644
index 0000000000..0a2996bec3
--- /dev/null
+++ b/src/test/regress/expected/element_foreign_key.out
@@ -0,0 +1,703 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fkarray"
+DETAIL: Key (ftest1)=({10,1}) is not present in table "pktableforarray".
+DROP TABLE FKTABLEFORARRAY;
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4,6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL,4,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+ERROR: insert or update on table "fktableforarraymdim" violates foreign key constraint "fktableforarraymdim_ftest1_fkey"
+DETAIL: Key (ftest1)=({{1,2},{6,NULL}}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+ERROR: null value in column "ftest1" violates not-null constraint
+DETAIL: Failing row contains (null, 21).
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+ {1} | 4
+(11 rows)
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({D}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({A,B,D}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(A) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(B) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ERROR: foreign key constraint "fktableforarray_ftest1_fkey" cannot be implemented
+DETAIL: Key column "ftest1" has element type integer which does not have a default btree operator class that's compatible with class "float8_ops".
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(A, {A,B,C}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(B, {A}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Test ELEMENT foreign keys with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,99)"}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,1)","(2010,1)"}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2010,99)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2011,1)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({0,1}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({2,1}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(2, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(20, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+ERROR: foreign keys support only one array column
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: foreign keys support only one array column
+DROP TABLE F1;
+-- Cleanup
+DROP TABLE DIM1;
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "x2_x1_fkey"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "fk_const"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+DROP TABLE x1;
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{100,100,100},{NULL,NULL,20},{7,8,10}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
+-- Check GIN index
+-- Define PKTABLEFORARRAYGIN
+CREATE TABLE PKTABLEFORARRAYGIN ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAYGIN
+INSERT INTO PKTABLEFORARRAYGIN VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (5, 'Test5');
+-- Define FKTABLEFORARRAYGIN
+CREATE TABLE FKTABLEFORARRAYGIN ( ftest1 int[],
+ ftest2 int PRIMARY KEY,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYGIN
+ ON DELETE NO ACTION ON UPDATE NO ACTION);
+-- -- Create index on FKTABLEFORARRAYGIN
+CREATE INDEX ON FKTABLEFORARRAYGIN USING gin (ftest1 array_ops);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,5,3}', 10);
+-- Try using the indexable operator
+SELECT * FROM FKTABLEFORARRAYGIN WHERE ftest1 @> ARRAY[5];
+ ftest1 | ftest2
+-------------+--------
+ {5} | 1
+ {3,5,2,5} | 3
+ {3,5,4,1,3} | 5
+ {5,1} | 7
+ {3,4,5,3} | 10
+(5 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYGIN;
+DROP TABLE PKTABLEFORARRAYGIN;
+-- Check DELECTE CASCADE ACTION
+CREATE TABLE PKTABLEFORARRAYDELETE ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAYDELETE
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (5, 'Test5');
+-- Define FKTABLEFORARRAYDELETE
+CREATE TABLE FKTABLEFORARRAYDELETE ( ftest1 int[],
+ ftest2 int,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYDELETE
+ ON DELETE CASCADE ON UPDATE NO ACTION );
+-- Create index on FKTABLEFORARRAYDELETE
+CREATE INDEX ON FKTABLEFORARRAYDELETE USING gin (ftest1);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,5,3}', 10);
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+ ftest1 | ftest2
+-------------+--------
+ {5} | 1
+ {3,2} | 2
+ {3,5,2,5} | 3
+ {3,4,4} | 4
+ {3,5,4,1,3} | 5
+ {1} | 6
+ {5,1} | 7
+ {2,1,2,4,1} | 8
+ {4,2} | 9
+ {3,4,5,3} | 10
+(10 rows)
+
+-- Check FKTABLE for target rows
+SELECT * FROM FKTABLEFORARRAYDELETE WHERE ftest1 @> ARRAY[5];
+ ftest1 | ftest2
+-------------+--------
+ {5} | 1
+ {3,5,2,5} | 3
+ {3,5,4,1,3} | 5
+ {5,1} | 7
+ {3,4,5,3} | 10
+(5 rows)
+
+-- Perfrorm delete
+DELETE FROM PKTABLEFORARRAYDELETE WHERE ptest1 = 5;
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+ ftest1 | ftest2
+-------------+--------
+ {3,2} | 2
+ {3,4,4} | 4
+ {1} | 6
+ {2,1,2,4,1} | 8
+ {4,2} | 9
+(5 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYDELETE;
+DROP TABLE PKTABLEFORARRAYDELETE;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 2fd3f2b1b1..fdd6720fb8 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -103,7 +103,7 @@ test: publication subscription
# ----------
# Another group of parallel tests
# ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
+test: select_views portals_p2 foreign_key element_foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
# ----------
# Another group of parallel tests
# NB: temp.sql does a reconnect which transiently uses 2 connections,
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 76b0de30a7..9cb8638b4e 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -140,6 +140,7 @@ test: amutils
test: select_views
test: portals_p2
test: foreign_key
+test: element_foreign_key
test: cluster
test: dependency
test: guc
diff --git a/src/test/regress/sql/element_foreign_key.sql b/src/test/regress/sql/element_foreign_key.sql
new file mode 100644
index 0000000000..17ef03760f
--- /dev/null
+++ b/src/test/regress/sql/element_foreign_key.sql
@@ -0,0 +1,549 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Test ELEMENT foreign keys with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+
+
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+
+
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+-- Cleanup
+DROP TABLE DIM1;
+
+
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+DROP TABLE x2;
+DROP TABLE x1;
+
+
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+DROP TABLE F1;
+
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+DROP TABLE F1;
+
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
+
+-- Check GIN index
+-- Define PKTABLEFORARRAYGIN
+CREATE TABLE PKTABLEFORARRAYGIN ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAYGIN
+INSERT INTO PKTABLEFORARRAYGIN VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (5, 'Test5');
+
+-- Define FKTABLEFORARRAYGIN
+CREATE TABLE FKTABLEFORARRAYGIN ( ftest1 int[],
+ ftest2 int PRIMARY KEY,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYGIN
+ ON DELETE NO ACTION ON UPDATE NO ACTION);
+
+-- -- Create index on FKTABLEFORARRAYGIN
+CREATE INDEX ON FKTABLEFORARRAYGIN USING gin (ftest1 array_ops);
+
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,5,3}', 10);
+
+-- Try using the indexable operator
+SELECT * FROM FKTABLEFORARRAYGIN WHERE ftest1 @> ARRAY[5];
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYGIN;
+DROP TABLE PKTABLEFORARRAYGIN;
+
+-- Check DELECTE CASCADE ACTION
+CREATE TABLE PKTABLEFORARRAYDELETE ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAYDELETE
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (5, 'Test5');
+
+-- Define FKTABLEFORARRAYDELETE
+CREATE TABLE FKTABLEFORARRAYDELETE ( ftest1 int[],
+ ftest2 int,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYDELETE
+ ON DELETE CASCADE ON UPDATE NO ACTION );
+
+-- Create index on FKTABLEFORARRAYDELETE
+CREATE INDEX ON FKTABLEFORARRAYDELETE USING gin (ftest1);
+
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,5,3}', 10);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+
+-- Check FKTABLE for target rows
+SELECT * FROM FKTABLEFORARRAYDELETE WHERE ftest1 @> ARRAY[5];
+
+-- Perfrorm delete
+DELETE FROM PKTABLEFORARRAYDELETE WHERE ptest1 = 5;
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYDELETE;
+DROP TABLE PKTABLEFORARRAYDELETE;
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-10-30 01:24 ` Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Andreas Karlsson @ 2017-10-30 01:24 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Stephen Frost <sfrost@snowman.net>; David Steele <david@pgmasters.net>; Alvaro Herrera <alvherre@2ndquadrant.com>; Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; pgsql-hackers-owner@postgresql.org, Tom Lane <tgl@sss.pgh.pa.us>; Hans-Jürgen Schönig <hs@cybertec.at>
Sorry for the very late review.
I like this feature and have needed it myself in the past, and the
current syntax seems pretty good. One could argue for if the syntax
could be generalized to support other things like json and hstore, but I
do not think it would be fair to block this patch due to that.
== Limitations of the current design
1) Array element foreign keys can only be specified at the table level
(not at columns): I think this limitation is fine. Other PostgreSQL
specific features like exclusion contraints can also only be specified
at the table level.
2) Lack of support for SET NULL and SET DEFAULT: these do not seem very
useful for arrays.
3) Lack of support for specifiying multiple arrays in the foreign key:
seems like a good thing to me since it is not obvious what such a thing
even would do.
4) That you need to add a cast to the index if you have different types:
due to there not being a int4[] <@ int2[] operator you need to add an
index on (col::int4[]) to speed up deletes and updates. This one i
annoying since EXPLAIN wont give you the query plans for the foreign key
queries, but I do not think fixing this should be within the scope of
the patch and that having a smaller interger in the referring table is rare.
5) The use of count(DISTINCT) requiring types to support btree equality:
this has been discussed a lot up-thread and I think the current state is
good enough.
== Functional review
I have played around some with it and things seem to work and the test
suite passes, but I noticed a couple of strange behaviors.
1) MATCH FULL does not seem to care about NULLS in arrays. In the
example below I expected both inserts into the referring table to fail.
CREATE TABLE t (x int, y int, PRIMARY KEY (x, y));
CREATE TABLE fk (x int, ys int[], FOREIGN KEY (x, EACH ELEMENT OF ys)
REFERENCES t MATCH FULL);
INSERT INTO t VALUES (10, 1);
INSERT INTO fk VALUES (10, '{1,NULL}');
INSERT INTO fk VALUES (NULL, '{1}');
CREATE TABLE
CREATE TABLE
INSERT 0 1
INSERT 0 1
ERROR: insert or update on table "fk" violates foreign key constraint
"fk_x_fkey"
DETAIL: MATCH FULL does not allow mixing of null and nonnull key values.
2) To me it was not obvious that ON DELETE CASCADE would delete the
whole rows rather than delete the members from the array, and this kind
of misunderstanding can lead to pretty bad surprises in production. I am
leaning towards not supporting CASCADE.
== The @>> operator
A previous version of your patch added the "anyelement <<@ anyarray"
operator to avoid having to build arrays, but that part was reverted due
to a bug.
I am not expert on the gin code, but as far as I can tell it would be
relatively simple to fix that bug. Just allocate an array of Datums of
length one where you put the element you are searching for (or maybe a
copy of it).
Potential issues with adding the operators:
1) Do we really want to add an operator just for array element foreign
keys? I think this is not an issue since it seems like it should be
useful in general. I know I have wanted it myself.
2) I am not sure, but the committers might prefer if adding the
operators is done in a separate patch.
3) Bikeshedding about operator names. I personally think @>> is clear
enough and as far as I know it is not used for anything else.
== Code review
The patch no longer applies to HEAD, but the conflicts are small.
I think we should be more consistent in the naming, both in code and in
the documentation. Right now we have "array foreign keys", "element
foreign keys", "ELEMENT foreign keys", etc.
+ /*
+ * If this is an array foreign key, we must look up the
operators for
+ * the array element type, not the array type itself.
+ */
+ if (fkreftypes[i] != FKCONSTR_REF_PLAIN)
+ if (fkreftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ old_fktype = get_base_element_type(old_fktype);
+ /* this shouldn't happen ... */
+ if (!OidIsValid(old_fktype))
+ elog(ERROR, "old foreign key column is not an array");
+ }
+ if (riinfo->fk_reftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ riinfo->has_array = true;
+ riinfo->ff_eq_oprs[i] = ARRAY_EQ_OP;
+ }
In the three diffs above it would be much cleaner to check for "==
FKCONSTR_REF_EACH_ELEMENT" since that better conveys the intent and is
safer for adding new types in the future.
+ /* We look through any domain here */
+ fktype = get_base_element_type(fktype);
What does the comment above mean?
if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("foreign key constraint \"%s\" "
- "cannot be implemented",
- fkconstraint->conname),
- errdetail("Key columns \"%s\" and \"%s\" "
- "are of incompatible types: %s and %s.",
+ errmsg("foreign key constraint \"%s\" cannot be
implemented",
+ fkconstraint->conname),
+ errdetail("Key columns \"%s\" and \"%s\" are of
incompatible types: %s and %s.",
strVal(list_nth(fkconstraint->fk_attrs, i)),
strVal(list_nth(fkconstraint->pk_attrs, i)),
- format_type_be(fktype),
+ format_type_be(fktypoid[i]),
format_type_be(pktype))));
The above diff looks like pointless code churn which possibly introduces
a bug in how it changed fktype to fktypoid[i].
I think the code in RI_Initial_Check() would be cleaner if you used
"CROSS JOIN LATERAL unnest(col)" rather than having unnest() in the
target list. This way you would not need to rename all columns and the
code paths for the array case could look more like the code path for the
normal case.
== Minor things in the code
+ {
+ pk_attrs = lappend(pk_attrs, arg);
+ fk_reftypes = lappend_int(fk_reftypes, FKCONSTR_REF_PLAIN);
+ }
This is incorrectly indented.
I suggest that you should only allocate countbuf in RI_FKey_check() if
has_array is set.
I think the code would be more readable if both branches of
ri_GenerateQual() used the same pattern for whitespace so the
differences are easier to spot.
You can use DROP TABLE IF EXISTS to silence the "ERROR: table
"fktableforarray" does not exist" errors.
I am not sure that you need to test all combinations of ON UPDATE and ON
DELETE. I think it should be enough to test one of each ON UPDATE and
one of each ON DELETE should be enough.
+-- Create the foreign table
It is probably a bad idea to call the referencing table the foreign table.
+-- Repeat a similar test using INT4 keys coerced from INT2
This comment is repeated twice in the test suite.
== Documentation
Remove the ELEMENT REFERENCES form from
doc/src/sgml/ref/create_table.sgml since we no longer support it.
- FOREIGN KEY ( <replaceable
class="parameter">column_name</replaceable> [, ... ] ) REFERENCES
<replaceable class="parameter">reftable</replaceable> [ ( <replaceable
class="parameter">refcolumn</replaceable> [, ... ] ) ]
+ FOREIGN KEY ( [ELEMENT] <replaceable
class="parameter">column_name</replaceable> [, ... ] ) REFERENCES
<replaceable class="parameter">reftable</replaceable> [ ( <replaceable
class="parameter">refcolumn</replaceable> [, ... ] ) ]
Change this to "FOREIGN KEY ( [EACH ELEMENT OF] ...".
- <term><literal>FOREIGN KEY ( <replaceable
class="parameter">column_name</replaceable> [, ... ] )
+ <term><literal>FOREIGN KEY ( [ELEMENT] <replaceable
class="parameter">column_name</replaceable> [, ... ] )
Change this to "... FOREIGN KEY ( [EACH ELEMENT OF] ...".
+ <literal>ELEMENT</literal> keyword and <replaceable
Change this to "... <literal>EACH ELEMENT OF</literal> ...". Maybe you
should also fix other instances of ELEMENT in the same paragraph but
these are less obvious.
You should remove the "ELEMENT REFERENCES" section of
doc/src/sgml/ref/create_table.sgml, and move any still relevant bits
elsewhere since we do not support this syntax.
The sql-createtable-foreign-key-arrays section need to be updated to
remove references to "ELEMENT REFERENCES".
Your indentation in doc/src/sgml/catalogs.sgml is two spaces but the
rest of the file looks like it uses 1 space indentation. Additionally
you seem to have accidentally reindented something which was correctly
indented.
Same with the idnentation in doc/src/sgml/ddl.sgml and
doc/src/sgml/ref/create_table.sgml.
- <varlistentry>
+ <varlistentry id="sql-createtable-foreign-key" xreflabel="FOREIGN KEY">
You have accidentally reindented this in doc/src/sgml/ref/create_table.sgml.
The paragraph in doc/src/sgml/ref/create_table.sgml which starts with
"In case the column name" seems to actually be multiple paragraphs. Is
that intentional or a mistake?
The documentation in doc/src/sgml/ddl.sgml mentions that "it must be
written in table constraint form" for when you have multiple columns,
but I feel this is just redundant and confusing since this is always
true, both for array foreign keys and for when you have multiple columns.
The documentation in doc/src/sgml/ddl.sgml should mention that we only
support one array reference per foreign key.
Maybe the documentation in doc/src/sgml/ddl.sgml should mention that we
only support the table constraint form.
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
@ 2017-11-10 00:47 ` Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2017-11-10 00:47 UTC (permalink / raw)
To: Andreas Karlsson <andreas@proxel.se>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Stephen Frost <sfrost@snowman.net>; David Steele <david@pgmasters.net>; Alvaro Herrera <alvherre@2ndquadrant.com>; Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; pgsql-hackers-owner@postgresql.org, Tom Lane <tgl@sss.pgh.pa.us>; Hans-Jürgen Schönig <hs@cybertec.at>
I am sorry for the late reply
== Limitations of the current design
All the limitations are as you said, I don't think we can do much better at
the moment
== Functional review
> >1) MATCH FULL does not seem to care about NULLS in arrays. In the example
> below I expected both inserts into the referring table to fail.
It seems in your example the only failed case was: INSERT INTO fk VALUES
(NULL, '{1}');
which shouldn't work, can you clarify this?
>2) To me it was not obvious that ON DELETE CASCADE would delete the whole
> rows rather than delete the members from the array, and this kind of
> misunderstanding can lead to pretty bad surprises in production. I am
> leaning towards not supporting CASCADE.
I would say so too, maybe we should remove ON DELETE CASCADE until we have
supported all remaining actions.
== The @>> operator
I would argue that allocating an array of datums and building an array
would have the same complexity
== Code review
> >I think the code in RI_Initial_Check() would be cleaner if you used
> "CROSS JOIN LATERAL unnest(col)" rather than having unnest() in the target
> list. This way you would not need to rename all columns and the code paths
> for the array case could look more like the code path for the normal case.
Can you clarify what you mean a bit more?
== Minor things in the code
All minor things were corrected
== Documentation
All documentation was corrected
Best Regards,
Mark Rofail
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
Array-ELEMENT-foreign-key-v5.3.patch (text/x-patch, 124.4K) download
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ef60a58631..e911bebd16 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2342,6 +2342,16 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</row>
<row>
+ <entry><structfield>confreftype</structfield></entry>
+ <entry><type>char[]</type></entry>
+ <entry></entry>
+ <entry>If a foreign key, the reference semantics for each column:
+ <literal>p</> = plain (simple equality),
+ <literal>e</> = each element of referencing array must have a match
+ </entry>
+ </row>
+
+ <row>
<entry><structfield>conpfeqop</structfield></entry>
<entry><type>oid[]</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</literal></entry>
@@ -2387,6 +2397,12 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</table>
<para>
+ When <structfield>confreftype</structfield> indicates array to scalar
+ foreign key reference semantics, the equality operators listed in
+ <structfield>conpfeqop</structfield> etc are for the array's element type.
+ </para>
+
+ <para>
In the case of an exclusion constraint, <structfield>conkey</structfield>
is only useful for constraint elements that are simple column references.
For other cases, a zero appears in <structfield>conkey</structfield>
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index daba66c187..f22fd34a32 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -882,6 +882,116 @@ CREATE TABLE order_items (
</para>
</sect2>
+ <sect2 id="ddl-constraints-element-fk">
+ <title>Foreign Key Arrays</title>
+
+ <indexterm>
+ <primary>Foreign Key Arrays</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>ELEMENT foreign key</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>Array ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>referential integrity</primary>
+ </indexterm>
+
+ <para>
+ Another option you have with foreign keys is to use a referencing column
+ which is an array of elements with the same type (or a compatible one) as
+ the referenced column in the related table. This feature is called
+ <firstterm>Foreign Key Arrays</firstterm> and is implemented in PostgreSQL
+ with <firstterm>EACH ELEMENT OF foreign key constraints</firstterm>, as
+ described in the following example:
+
+ <programlisting>
+ CREATE TABLE drivers (
+ driver_id integer PRIMARY KEY,
+ first_name text,
+ last_name text
+ );
+
+ CREATE TABLE races (
+ race_id integer PRIMARY KEY,
+ title text,
+ race_day date,
+ final_positions integer[],
+ FOREIGN KEY <emphasis> (EACH ELEMENT OF final_positions) REFERENCES drivers </emphasis>
+ );
+ </programlisting>
+
+ The above example uses an array (<literal>final_positions</literal>) to
+ store the results of a race: for each of its elements a referential
+ integrity check is enforced on the <literal>drivers</literal> table. Note
+ that <literal>(EACH ELEMENT OF ...) REFERENCES</literal> is an extension of
+ PostgreSQL and it is not included in the SQL standard.
+ </para>
+
+ <para>
+ We currently only support the table constraint form.
+ </para>
+
+ <para>
+ Even though the most common use case for Foreign Key Array constraint is on
+ a single column key, you can define a Foreign Key Array constraint on a
+ group of columns.
+
+ <programlisting>
+ CREATE TABLE available_moves (
+ kind text,
+ move text,
+ description text,
+ PRIMARY KEY (kind, move)
+ );
+
+ CREATE TABLE paths (
+ description text,
+ kind text,
+ moves text[],
+ <emphasis>FOREIGN KEY (kind, EACH ELEMENT OF moves) REFERENCES available_moves (kind, move)</emphasis>
+ );
+
+ INSERT INTO available_moves VALUES ('relative', 'LN', 'look north');
+ INSERT INTO available_moves VALUES ('relative', 'RL', 'rotate left');
+ INSERT INTO available_moves VALUES ('relative', 'RR', 'rotate right');
+ INSERT INTO available_moves VALUES ('relative', 'MF', 'move forward');
+ INSERT INTO available_moves VALUES ('absolute', 'N', 'move north');
+ INSERT INTO available_moves VALUES ('absolute', 'S', 'move south');
+ INSERT INTO available_moves VALUES ('absolute', 'E', 'move east');
+ INSERT INTO available_moves VALUES ('absolute', 'W', 'move west');
+
+ INSERT INTO paths VALUES ('L-shaped path', 'relative', '{LN, RL, MF, RR, MF, MF}');
+ INSERT INTO paths VALUES ('L-shaped path', 'absolute', '{W, N, N}');
+ </programlisting>
+
+ On top of standard foreign key requirements, array
+ <literal>ELEMENT</literal> foreign key constraints require that the
+ referencing column is an array of a compatible type of the corresponding
+ referenced column.
+
+ However, it is very important to note that, currently, we only support
+ one array reference per foreign key.
+ </para>
+
+ <para>
+ For more detailed information on Foreign Key Array options and special
+ cases, please refer to the documentation for
+ <xref linkend="sql-createtable-foreign-key"> and
+ <xref linkend="sql-createtable-element-foreign-key-constraints">.
+ </para>
+ </sect2>
+
<sect2 id="ddl-constraints-exclusion">
<title>Exclusion Constraints</title>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index bbb3a51def..7a0e2c638d 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -65,7 +65,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ] |
UNIQUE <replaceable class="parameter">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="parameter">index_parameters</replaceable> |
- REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
+ [EACH ELEMENT OF] REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -867,10 +867,10 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
- <varlistentry>
+ <varlistentry id="sql-createtable-foreign-key" xreflabel="FOREIGN KEY">
<term><literal>REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
- <term><literal>FOREIGN KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] )
+ <term><literal>FOREIGN KEY ( [EACH ELEMENT OF] <replaceable class="parameter">column_name</replaceable> [, ... ] )
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] ) ]
[ MATCH <replaceable class="parameter">matchtype</replaceable> ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ]
@@ -894,6 +894,20 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</para>
<para>
+ In case the column name <replaceable
+ class="parameter">column</replaceable> is prepended with the
+ <literal>EACH ELEMENT OF</literal> keyword and <replaceable
+ class="parameter">column</replaceable> is an array of elements compatible
+ with the corresponding <replaceable
+ class="parameter">refcolumn</replaceable> in <replaceable
+ class="parameter">reftable</replaceable>, a Foreign Key Array constraint
+ is put in place (see <xref
+ linkend="sql-createtable-element-foreign-key-constraints"> for more
+ information). Multi-column keys with more than one <literal>EACH ELEMENT
+ OF</literal> column are currently not allowed.
+ </para>
+
+ <para>
A value inserted into the referencing column(s) is matched against the
values of the referenced table and referenced columns using the
given match type. There are three match types: <literal>MATCH
@@ -956,7 +970,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
<para>
Delete any rows referencing the deleted row, or update the
values of the referencing column(s) to the new values of the
- referenced columns, respectively.
+ referenced columns, respectively. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -965,7 +980,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
<term><literal>SET NULL</literal></term>
<listitem>
<para>
- Set the referencing column(s) to null.
+ Set the referencing column(s) to null. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -977,6 +993,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
Set the referencing column(s) to their default values.
(There must be a row in the referenced table matching the default
values, if they are not null, or the operation will fail.)
+ Currently not supported with array <literal>ELEMENT</literal>
+ foreign keys.
</para>
</listitem>
</varlistentry>
@@ -992,6 +1010,85 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
+ <varlistentry id="sql-createtable-element-foreign-key-constraints" xreflabel="FOREIGN KEY ARRAYS">
+ <term><literal>EACH ELEMENT OF <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
+
+ <listitem>
+ <para>
+ The <literal>EACH ELEMENT OF REFERENCES</literal> definition specifies a
+ <quote>Foreign Key Array</quote>, a special
+ kind of foreign key constraint requiring the referencing column to be an
+ array of elements of the same type (or a compatible one) as the
+ referenced column in the referenced table. The value of each element of
+ the <replaceable class="parameter">refcolumn</replaceable> array will be
+ matched against some row of <replaceable
+ class="parameter">reftable</replaceable>.
+ </para>
+
+ <para>
+ Foreign Key Arrays are an extension
+ of PostgreSQL and are not included in the SQL standard.
+ </para>
+
+ <para>
+ Even with Foreign Key Arrays, modifications in the referenced column can
+ trigger actions to be performed on the referencing array. Similarly to
+ standard foreign keys, you can specify these actions using the
+ <literal>ON DELETE</literal> and <literal>ON UPDATE</literal> clauses.
+ However, only the following actions for each clause are currently
+ allowed:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>ON UPDATE/DELETE NO ACTION</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints. This is the default action.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ON UPDATE/DELETE RESTRICT</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ON DELETE CASCADE</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints. Deletes the entire array.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ It is advisable to index the refrencing column using GIN index as it
+ considerably enhances the performance. Also concerning coercion while
+ using the GIN index:
+
+ <programlisting>
+ CREATE TABLE pktableforarray ( ptest1 int2 PRIMARY KEY, ptest2 text );
+ CREATE TABLE fktableforarray ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ </programlisting>
+ This syntax is fine since it will cast ptest1 to int4 upon RI checks,
+
+ <programlisting>
+ CREATE TABLE pktableforarray ( ptest1 int4 PRIMARY KEY, ptest2 text );
+ CREATE TABLE fktableforarray ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ </programlisting>
+ however, this syntax will cast ftest1 to int4 upon RI checks, thus defeating the
+ purpose of the index.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>DEFERRABLE</literal></term>
<term><literal>NOT DEFERRABLE</literal></term>
@@ -1961,6 +2058,16 @@ CREATE TABLE cities_partdef
</refsect2>
<refsect2>
+ <title id="sql-createtable-foreign-key-arrays">Array <literal>ELEMENT</literal> Foreign Keys</title>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys and the <literal>ELEMENT
+ REFERENCES</literal> clause are a <productname>PostgreSQL</productname>
+ extension.
+ </para>
+ </refsect2>
+
+ <refsect2>
<title><literal>PARTITION BY</literal> Clause</title>
<para>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 2bc9e90dcf..d9ca06f4e3 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2124,6 +2124,7 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c7b2f031f0..db7978ec45 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1213,6 +1213,7 @@ index_constraint_create(Relation heapRelation,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 7dee6db0eb..07d0fe980b 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -59,6 +59,7 @@ CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
@@ -82,6 +83,7 @@ CreateConstraintEntry(const char *constraintName,
Datum values[Natts_pg_constraint];
ArrayType *conkeyArray;
ArrayType *confkeyArray;
+ ArrayType *confreftypeArray;
ArrayType *conpfeqopArray;
ArrayType *conppeqopArray;
ArrayType *conffeqopArray;
@@ -119,7 +121,11 @@ CreateConstraintEntry(const char *constraintName,
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = Int16GetDatum(foreignKey[i]);
confkeyArray = construct_array(fkdatums, foreignNKeys,
- INT2OID, 2, true, 's');
+ INT2OID, sizeof(int16), true, 's');
+ for (i = 0; i < foreignNKeys; i++)
+ fkdatums[i] = CharGetDatum(foreignRefType[i]);
+ confreftypeArray = construct_array(fkdatums, foreignNKeys,
+ CHAROID, sizeof(char), true, 'c');
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
conpfeqopArray = construct_array(fkdatums, foreignNKeys,
@@ -136,6 +142,7 @@ CreateConstraintEntry(const char *constraintName,
else
{
confkeyArray = NULL;
+ confreftypeArray = NULL;
conpfeqopArray = NULL;
conppeqopArray = NULL;
conffeqopArray = NULL;
@@ -188,6 +195,11 @@ CreateConstraintEntry(const char *constraintName,
else
nulls[Anum_pg_constraint_confkey - 1] = true;
+ if (confreftypeArray)
+ values[Anum_pg_constraint_confreftype - 1] = PointerGetDatum(confreftypeArray);
+ else
+ nulls[Anum_pg_constraint_confreftype - 1] = true;
+
if (conpfeqopArray)
values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
else
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 165b165d55..83fdccb254 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7116,6 +7116,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Relation pkrel;
int16 pkattnum[INDEX_MAX_KEYS];
int16 fkattnum[INDEX_MAX_KEYS];
+ char fkreftypes[INDEX_MAX_KEYS];
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
Oid opclasses[INDEX_MAX_KEYS];
@@ -7123,10 +7124,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid ppeqoperators[INDEX_MAX_KEYS];
Oid ffeqoperators[INDEX_MAX_KEYS];
int i;
+ ListCell *lc;
int numfks,
numpks;
Oid indexOid;
Oid constrOid;
+ bool has_array;
bool old_check_ok;
ObjectAddress address;
ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
@@ -7203,6 +7206,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
*/
MemSet(pkattnum, 0, sizeof(pkattnum));
MemSet(fkattnum, 0, sizeof(fkattnum));
+ MemSet(fkreftypes, 0, sizeof(fkreftypes));
MemSet(pktypoid, 0, sizeof(pktypoid));
MemSet(fktypoid, 0, sizeof(fktypoid));
MemSet(opclasses, 0, sizeof(opclasses));
@@ -7215,6 +7219,51 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
fkattnum, fktypoid);
/*
+ * Validate the reference semantics codes, too, and convert list to array
+ * format to pass to CreateConstraintEntry.
+ */
+ Assert(list_length(fkconstraint->fk_reftypes) == numfks);
+ has_array = false;
+ i = 0;
+ foreach(lc, fkconstraint->fk_reftypes)
+ {
+ char reftype = lfirst_int(lc);
+
+ switch (reftype)
+ {
+ case FKCONSTR_REF_PLAIN:
+ /* OK, nothing to do */
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ /* At most one FK column can be an array reference */
+ if (has_array)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign keys support only one array column")));
+ has_array = true;
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d", (int) reftype);
+ break;
+ }
+ fkreftypes[i] = reftype;
+ i++;
+ }
+
+ /* Array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions */
+ if (has_array)
+ {
+ if ((fkconstraint->fk_upd_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_upd_action != FKCONSTR_ACTION_RESTRICT) ||
+ (fkconstraint->fk_del_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_CASCADE))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("Foreign Key Arrays support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions")));
+ }
+
+ /*
* If the attribute list for the referenced table was omitted, lookup the
* definition of the primary key and use it. Otherwise, validate the
* supplied attribute list. In either case, discover the index OID and
@@ -7300,6 +7349,65 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
eqstrategy = BTEqualStrategyNumber;
/*
+ * If this is an array foreign key, we must look up the operators for
+ * the array element type, not the array type itself.
+ */
+ if (fkreftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ Oid elemopclass;
+
+ /* We check if the array element type exists and is of valid Oid */
+ fktype = get_base_element_type(fktype);
+ if (!OidIsValid(fktype))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has type %s which is not an array type.",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktypoid[i]))));
+
+ /*
+ * For the moment, we must also insist that the array's element
+ * type have a default btree opclass that is in the index's
+ * opfamily. This is necessary because ri_triggers.c relies on
+ * COUNT(DISTINCT x) on the element type, as well as on array_eq()
+ * on the array type, and we need those operations to have the
+ * same notion of equality that we're using otherwise.
+ *
+ * XXX this restriction is pretty annoying, considering the effort
+ * that's been put into the rest of the RI mechanisms to make them
+ * work with nondefault equality operators. In particular, it
+ * means that the cast-to-PK-datatype code path isn't useful for
+ * array-to-scalar references.
+ */
+ elemopclass = GetDefaultOpClass(fktype, BTREE_AM_OID);
+ if (!OidIsValid(elemopclass) ||
+ get_opclass_family(elemopclass) != opfamily)
+ {
+ /* Get the index opclass's name for the error message. */
+ char *opcname;
+
+ cla_ht = SearchSysCache1(CLAOID,
+ ObjectIdGetDatum(opclasses[i]));
+ if (!HeapTupleIsValid(cla_ht))
+ elog(ERROR, "cache lookup failed for opclass %u",
+ opclasses[i]);
+ cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
+ opcname = pstrdup(NameStr(cla_tup->opcname));
+ ReleaseSysCache(cla_ht);
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has element type %s which does not have a default btree operator class that's compatible with class \"%s\".",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktype),
+ opcname)));
+ }
+ }
+
+ /*
* There had better be a primary equality operator for the index.
* We'll use it for PK = PK comparisons.
*/
@@ -7358,17 +7466,17 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
}
if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("foreign key constraint \"%s\" "
- "cannot be implemented",
- fkconstraint->conname),
- errdetail("Key columns \"%s\" and \"%s\" "
- "are of incompatible types: %s and %s.",
- strVal(list_nth(fkconstraint->fk_attrs, i)),
- strVal(list_nth(fkconstraint->pk_attrs, i)),
- format_type_be(fktype),
- format_type_be(pktype))));
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("foreign key constraint \"%s\" "
+ "cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key columns \"%s\" and \"%s\" "
+ "are of incompatible types: %s and %s.",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ strVal(list_nth(fkconstraint->pk_attrs, i)),
+ format_type_be(fktype),
+ format_type_be(pktype))));
if (old_check_ok)
{
@@ -7398,6 +7506,13 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
* We may assume that pg_constraint.conkey is not changing.
*/
old_fktype = attr->atttypid;
+ if (fkreftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ old_fktype = get_base_element_type(old_fktype);
+ /* this shouldn't happen ... */
+ if (!OidIsValid(old_fktype))
+ elog(ERROR, "old foreign key column is not an array");
+ }
new_fktype = fktype;
old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
&old_castfunc);
@@ -7440,7 +7555,6 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
new_castfunc == old_castfunc &&
(!IsPolymorphicType(pfeqop_right) ||
new_fktype == old_fktype));
-
}
pfeqoperators[i] = pfeqop;
@@ -7464,6 +7578,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
indexOid,
RelationGetRelid(pkrel),
pkattnum,
+ fkreftypes,
pfeqoperators,
ppeqoperators,
ffeqoperators,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 92ae3822d8..e876db9242 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -661,6 +661,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
@@ -1032,6 +1033,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
char fk_matchtype = FKCONSTR_MATCH_SIMPLE;
List *fk_attrs = NIL;
List *pk_attrs = NIL;
+ List *fk_reftypes = NIL;
StringInfoData buf;
int funcnum;
OldTriggerInfo *info = NULL;
@@ -1061,7 +1063,10 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
if (i % 2)
fk_attrs = lappend(fk_attrs, arg);
else
+ {
pk_attrs = lappend(pk_attrs, arg);
+ fk_reftypes = lappend_int(fk_reftypes, FKCONSTR_REF_PLAIN);
+ }
}
/* Prepare description of constraint for use in messages */
@@ -1200,6 +1205,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
fkcon->conname = constr_name;
fkcon->fk_attrs = fk_attrs;
fkcon->pk_attrs = pk_attrs;
+ fkcon->fk_reftypes = fk_reftypes;
fkcon->fk_matchtype = fk_matchtype;
switch (info->funcoids[0])
{
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 08f3a3d357..3724c083e3 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -3164,6 +3164,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index cadd253ef1..7470dcc020 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2846,6 +2846,7 @@ _copyConstraint(const Constraint *from)
COPY_NODE_FIELD(pktable);
COPY_NODE_FIELD(fk_attrs);
COPY_NODE_FIELD(pk_attrs);
+ COPY_NODE_FIELD(fk_reftypes);
COPY_SCALAR_FIELD(fk_matchtype);
COPY_SCALAR_FIELD(fk_upd_action);
COPY_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 2866fd7b4a..045ed984c6 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2584,6 +2584,7 @@ _equalConstraint(const Constraint *a, const Constraint *b)
COMPARE_NODE_FIELD(pktable);
COMPARE_NODE_FIELD(fk_attrs);
COMPARE_NODE_FIELD(pk_attrs);
+ COMPARE_NODE_FIELD(fk_reftypes);
COMPARE_SCALAR_FIELD(fk_matchtype);
COMPARE_SCALAR_FIELD(fk_upd_action);
COMPARE_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 291d1eeb46..cc6dde0243 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3496,6 +3496,7 @@ _outConstraint(StringInfo str, const Constraint *node)
WRITE_NODE_FIELD(pktable);
WRITE_NODE_FIELD(fk_attrs);
WRITE_NODE_FIELD(pk_attrs);
+ WRITE_NODE_FIELD(fk_reftypes);
WRITE_CHAR_FIELD(fk_matchtype);
WRITE_CHAR_FIELD(fk_upd_action);
WRITE_CHAR_FIELD(fk_del_action);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c301ca465d..e98dd2c0d3 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -126,6 +126,13 @@ typedef struct ImportQual
List *table_names;
} ImportQual;
+/* Private struct for the result of foreign_key_column_elem production */
+typedef struct FKColElem
+{
+ Node *name; /* name of the column (a String) */
+ char reftype; /* FKCONSTR_REF_xxx code */
+} FKColElem;
+
/* ConstraintAttributeSpec yields an integer bitmask of these flags: */
#define CAS_NOT_DEFERRABLE 0x01
#define CAS_DEFERRABLE 0x02
@@ -183,6 +190,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
static void SplitColQualList(List *qualList,
List **constraintList, CollateClause **collClause,
core_yyscan_t yyscanner);
+static void SplitFKColElems(List *fkcolelems, List **names, List **reftypes);
static void processCASbits(int cas_bits, int location, const char *constrType,
bool *deferrable, bool *initdeferred, bool *not_valid,
bool *no_inherit, core_yyscan_t yyscanner);
@@ -233,6 +241,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
A_Indices *aind;
ResTarget *target;
struct PrivTarget *privtarget;
+ struct FKColElem *fkcolelem;
AccessPriv *accesspriv;
struct ImportQual *importqual;
InsertStmt *istmt;
@@ -358,6 +367,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <accesspriv> privilege
%type <list> privileges privilege_list
%type <privtarget> privilege_target
+%type <fkcolelem> foreign_key_column_elem
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
%type <ival> defacl_privilege_target
@@ -393,7 +403,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
execute_param_clause using_clause returning_clause
opt_enum_val_list enum_val_list table_func_column_list
create_generic_options alter_generic_options
- relation_expr_list dostmt_opt_list
+ relation_expr_list dostmt_opt_list foreign_key_column_list
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
@@ -625,8 +635,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
DOUBLE_P DROP
- EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
- EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
+ EACH ELEMENT ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE
+ EVENT EXCEPT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
EXTENSION EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FILTER FIRST_P FLOAT_P FOLLOWING FOR
@@ -3512,8 +3522,10 @@ ColConstraintElem:
n->contype = CONSTR_FOREIGN;
n->location = @1;
n->pktable = $2;
+ /* fk_attrs will be filled in by parse analysis */
n->fk_attrs = NIL;
n->pk_attrs = $3;
+ n->fk_reftypes = list_make1_int(FKCONSTR_REF_PLAIN);
n->fk_matchtype = $4;
n->fk_upd_action = (char) ($5 >> 8);
n->fk_del_action = (char) ($5 & 0xFF);
@@ -3707,14 +3719,15 @@ ConstraintElem:
NULL, yyscanner);
$$ = (Node *)n;
}
- | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
- opt_column_list key_match key_actions ConstraintAttributeSpec
+ | FOREIGN KEY '(' foreign_key_column_list ')' REFERENCES
+ qualified_name opt_column_list key_match key_actions
+ ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_FOREIGN;
n->location = @1;
+ SplitFKColElems($4, &n->fk_attrs, &n->fk_reftypes);
n->pktable = $7;
- n->fk_attrs = $4;
n->pk_attrs = $8;
n->fk_matchtype = $9;
n->fk_upd_action = (char) ($10 >> 8);
@@ -3747,6 +3760,29 @@ columnElem: ColId
$$ = (Node *) makeString($1);
}
;
+ foreign_key_column_list:
+ foreign_key_column_elem
+ { $$ = list_make1($1); }
+ | foreign_key_column_list ',' foreign_key_column_elem
+ { $$ = lappend($1, $3); }
+ ;
+
+ foreign_key_column_elem:
+ ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($1);
+ n->reftype = FKCONSTR_REF_PLAIN;
+ $$ = n;
+ }
+ | EACH ELEMENT OF ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($4);
+ n->reftype = FKCONSTR_REF_EACH_ELEMENT;
+ $$ = n;
+ }
+ ;
key_match: MATCH FULL
{
@@ -14739,6 +14775,7 @@ unreserved_keyword:
| DOUBLE_P
| DROP
| EACH
+ | ELEMENT
| ENABLE_P
| ENCODING
| ENCRYPTED
@@ -15856,6 +15893,23 @@ SplitColQualList(List *qualList,
*constraintList = qualList;
}
+/* Split a list of FKColElem structs into separate name and reftype lists */
+static void
+SplitFKColElems(List *fkcolelems, List **names, List **reftypes)
+{
+ ListCell *lc;
+
+ *names = NIL;
+ *reftypes = NIL;
+ foreach(lc, fkcolelems)
+ {
+ FKColElem *fkcolelem = (FKColElem *) lfirst(lc);
+
+ *names = lappend(*names, fkcolelem->name);
+ *reftypes = lappend_int(*reftypes, fkcolelem->reftype);
+ }
+}
+
/*
* Process result of ConstraintAttributeSpec, and set appropriate bool flags
* in the output command node. Pass NULL for any flags the particular
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 7d10d74a3e..00f9dbf36d 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1386,6 +1386,90 @@ select_common_type(ParseState *pstate, List *exprs, const char *context,
}
/*
+ * look at select_common_type(); in parse_coerce.c(src/backend/parser/parse_coerce.c)
+ *
+ * select_common_type_2args()
+ * Determine the common supertype of a list of input expressions.
+ * 'leftOid': left operand's type
+ * 'rightOid': left operand's type
+ */
+Oid select_common_type_2args(Oid leftOid, Oid rightOid)
+{
+ TYPCATEGORY leftcategory;
+ TYPCATEGORY rightcategory;
+ bool leftispreferred;
+ bool rightispreferred;
+
+ Assert(leftOid != InvalidOid);
+ Assert(rightOid != InvalidOid);
+
+ /*
+ * If input types are valid and exactly the same, just pick that type.
+ */
+ if (leftOid != UNKNOWNOID)
+ {
+ if (rightOid == leftOid)
+ return leftOid;
+ }
+
+ /*
+ * Nope, so set up for the full algorithm.
+ */
+ leftOid = getBaseType(leftOid);
+ rightOid = getBaseType(rightOid);
+ get_type_category_preferred(leftOid, &leftcategory, &leftispreferred);
+ get_type_category_preferred(rightOid, &rightcategory, &rightispreferred);
+
+ if (rightOid != UNKNOWNOID && rightOid != leftOid)
+ {
+ if (rightcategory != leftcategory)
+ {
+ /* both types in different categories? then not much hope... */
+ return InvalidOid;
+ }
+
+ if (can_coerce_type(1, &rightOid, &leftOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left */
+ if (!can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can not coerce to it implicitly right to left, thus coercion only works one way */
+ return leftOid;
+ }
+ else
+ {
+ /* coercion works both ways, then decide depending on preferred flag */
+ if (leftispreferred)
+ return leftOid;
+ else
+ return rightOid;
+ }
+ }
+ else{
+ /* can not coerce to it implicitly right to left */
+ if (can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left, thus coercion only works one way */
+ return rightOid;
+ }
+ else{
+ /* can not coerce either way */
+ return InvalidOid;
+ }
+ }
+ }
+
+ /*
+ * If all the inputs were UNKNOWN type --- ie, unknown-type literals ---
+ * then resolve as type TEXT.
+ */
+ if (leftOid == UNKNOWNOID)
+ leftOid = TEXTOID;
+
+ return leftOid;
+}
+
+/*
* coerce_to_common_type()
* Coerce an expression to the given type.
*
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 8461da490a..984fdb6b14 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -745,6 +745,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
* list of FK constraints to be processed later.
*/
constraint->fk_attrs = list_make1(makeString(column->colname));
+ /* grammar should have set fk_reftypes */
+ Assert(list_length(constraint->fk_reftypes) == 1);
cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
break;
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 4badb5fd7c..987014880d 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -118,9 +118,11 @@ typedef struct RI_ConstraintInfo
char confupdtype; /* foreign key's ON UPDATE action */
char confdeltype; /* foreign key's ON DELETE action */
char confmatchtype; /* foreign key's match type */
+ bool has_array; /* true if any reftype is EACH_ELEMENT */
int nkeys; /* number of key columns */
int16 pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */
int16 fk_attnums[RI_MAX_NUMKEYS]; /* attnums of referencing cols */
+ char fk_reftypes[RI_MAX_NUMKEYS]; /* reference semantics */
Oid pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = FK) */
Oid pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = PK) */
Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK = FK) */
@@ -204,7 +206,8 @@ static void ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype);
+ const char *rightop, Oid rightoptype,
+ char fkreftype);
static void ri_add_cast_to(StringInfo buf, Oid typid);
static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
static int ri_NullCheck(HeapTuple tup,
@@ -395,6 +398,7 @@ RI_FKey_check(TriggerData *trigdata)
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
StringInfoData querybuf;
+ StringInfoData countbuf;
char pkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
char paramname[16];
@@ -407,12 +411,27 @@ RI_FKey_check(TriggerData *trigdata)
* FOR KEY SHARE OF x
* The type id's for the $ parameters are those of the
* corresponding FK attributes.
+ *
+ * In case of an array ELEMENT foreign key, the previous query is used
+ * to count the number of matching rows and see if every combination
+ * is actually referenced.
+ * The wrapping query is
+ * SELECT 1 WHERE
+ * (SELECT count(DISTINCT y) FROM unnest($1) y)
+ * = (SELECT count(*) FROM (<QUERY>) z)
* ----------
*/
initStringInfo(&querybuf);
quoteRelationName(pkrelname, pk_rel);
appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
querysep = "WHERE";
+
+ if (riinfo->has_array)
+ {
+ initStringInfo(&countbuf);
+ appendStringInfo(&countbuf, "SELECT 1 WHERE ");
+ }
+
for (i = 0; i < riinfo->nkeys; i++)
{
Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
@@ -421,18 +440,41 @@ RI_FKey_check(TriggerData *trigdata)
quoteOneName(attname,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
sprintf(paramname, "$%d", i + 1);
+
+ /*
+ * In case of an array ELEMENT foreign key, we check that each
+ * distinct non-null value in the array is present in the PK
+ * table.
+ */
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&countbuf,
+ "(SELECT pg_catalog.count(DISTINCT y) FROM pg_catalog.unnest(%s) y)",
+ paramname);
+
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pf_eq_oprs[i],
- paramname, fk_type);
+ paramname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = fk_type;
}
appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
- /* Prepare and save the plan */
- qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
- &qkey, fk_rel, pk_rel, true);
+ if (riinfo->has_array)
+ {
+ appendStringInfo(&countbuf,
+ " OPERATOR(pg_catalog.=) (SELECT pg_catalog.count(*) FROM (%s) z)",
+ querybuf.data);
+
+ /* Prepare and save the plan for Foreign Key Arrays */
+ qplan = ri_PlanCheck(countbuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
+ }
+ else
+ /* Prepare and save the plan */
+ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
}
/*
@@ -559,7 +601,8 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pp_eq_oprs[i],
- paramname, pk_type);
+ paramname, pk_type,
+ FKCONSTR_REF_PLAIN);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -751,7 +794,8 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -974,7 +1018,8 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1130,7 +1175,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1309,7 +1355,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1475,7 +1522,8 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1651,7 +1699,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1817,7 +1866,8 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2008,7 +2058,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2327,6 +2378,14 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
* For MATCH FULL:
* (fk.keycol1 IS NOT NULL [OR ...])
*
+ * In case of an array ELEMENT column, relname is replaced with the
+ * following subquery:
+ *
+ * SELECT unnest("keycol1") k1, "keycol1" ak1 [, ...]
+ * FROM ONLY "public"."fk"
+ *
+ * where all the columns are renamed in order to prevent name collisions.
+ *
* We attach COLLATE clauses to the operators when comparing columns
* that have different collations.
*----------
@@ -2338,15 +2397,46 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
{
quoteOneName(fkattname,
RIAttName(fk_rel, riinfo->fk_attnums[i]));
- appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
+ if (riinfo->has_array)
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%sfk.ak%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.k%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
sep = ", ";
}
quoteRelationName(pkrelname, pk_rel);
quoteRelationName(fkrelname, fk_rel);
- appendStringInfo(&querybuf,
- " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
- fkrelname, pkrelname);
+
+ if (riinfo->has_array)
+ {
+ sep = "";
+ appendStringInfo(&querybuf,
+ " FROM (SELECT ");
+ for (i = 0; i < riinfo->nkeys; i++)
+ {
+ quoteOneName(fkattname,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%spg_catalog.unnest(%s) k%d, %s ak%d",
+ sep, fkattname, i + 1, fkattname, i + 1);
+ else
+ appendStringInfo(&querybuf, "%s%s k%d", sep, fkattname,
+ i + 1);
+ sep = ", ";
+ }
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s) fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
+ }
+ else
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
strcpy(pkattname, "pk.");
strcpy(fkattname, "fk.");
@@ -2360,12 +2450,16 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
quoteOneName(pkattname + 3,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
- quoteOneName(fkattname + 3,
- RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname + 3, "k%d", i + 1);
+ else
+ quoteOneName(fkattname + 3,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
ri_GenerateQual(&querybuf, sep,
pkattname, pk_type,
riinfo->pf_eq_oprs[i],
- fkattname, fk_type);
+ fkattname, fk_type,
+ FKCONSTR_REF_PLAIN);
if (pk_coll != fk_coll)
ri_GenerateQualCollation(&querybuf, pk_coll);
sep = "AND";
@@ -2381,7 +2475,10 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
sep = "";
for (i = 0; i < riinfo->nkeys; i++)
{
- quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname, "k%d", i + 1);
+ else
+ quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
appendStringInfo(&querybuf,
"%sfk.%s IS NOT NULL",
sep, fkattname);
@@ -2557,20 +2654,23 @@ quoteRelationName(char *buffer, Relation rel)
/*
* ri_GenerateQual --- generate a WHERE clause equating two variables
*
- * The idea is to append " sep leftop op rightop" to buf. The complexity
- * comes from needing to be sure that the parser will select the desired
- * operator. We always name the operator using OPERATOR(schema.op) syntax
- * (readability isn't a big priority here), so as to avoid search-path
- * uncertainties. We have to emit casts too, if either input isn't already
- * the input type of the operator; else we are at the mercy of the parser's
- * heuristics for ambiguous-operator resolution.
- */
+ * The idea is to append " sep leftop op rightop" to buf, or if fkreftype is
+ * FKCONSTR_REF_EACH_ELEMENT, append " sep rightop @> leftop" to buf.
+ *
+ * The complexity comes from needing to be sure that the parser will select
+ * the desired operator. We always name the operator using
+ * OPERATOR(schema.op) syntax (readability isn't a big priority here), so as
+ * to avoid search-path uncertainties. We have to emit casts too, if either
+ * input isn't already the input type of the operator; else we are at the
+ * mercy of the parser's heuristics for ambiguous-operator resolution.
+ */
static void
ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype)
+ const char *rightop, Oid rightoptype,
+ char fkreftype)
{
HeapTuple opertup;
Form_pg_operator operform;
@@ -2586,14 +2686,60 @@ ri_GenerateQual(StringInfo buf,
nspname = get_namespace_name(operform->oprnamespace);
- appendStringInfo(buf, " %s %s", sep, leftop);
- if (leftoptype != operform->oprleft)
- ri_add_cast_to(buf, operform->oprleft);
- appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
- appendStringInfoString(buf, oprname);
- appendStringInfo(buf, ") %s", rightop);
- if (rightoptype != operform->oprright)
- ri_add_cast_to(buf, operform->oprright);
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ Oid oprright;
+ Oid oprleft;
+ Oid oprcommon;d
+ /*
+ * we first need to get the array types and decide
+ * the more appropriate one
+ */
+
+ /* get array type of refrenced element*/
+ oprright = get_array_type(operform->oprleft);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprleft))));
+ /* get array type of refrencing element*/
+ oprleft = get_array_type(operform->oprright);
+ if (!OidIsValid(oprleft))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprright))));
+ /* compare the two array types and try to find
+ a common supertype */
+ oprcommon = select_common_type_2args(oprleft, oprright);
+ if (!OidIsValid(oprcommon))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find common type between %s and %s",
+ format_type_be(oprleft), format_type_be(oprright))));
+
+
+ appendStringInfo(buf, " %s %s", sep, rightop);
+ if (oprleft != oprcommon)
+ ri_add_cast_to(buf, oprcommon);
+ appendStringInfo(buf, " OPERATOR(pg_catalog.");
+ appendStringInfo(buf, " @>");
+ appendStringInfo(buf, ") ARRAY[%s]", leftop);
+ if (oprright != oprcommon)
+ ri_add_cast_to(buf, oprcommon);
+ }
+ else
+ {
+ appendStringInfo(buf, " %s %s", sep, leftop);
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+ appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
+ appendStringInfoString(buf, oprname);
+ appendStringInfo(buf, ") %s", rightop);
+ if (rightoptype != operform->oprright)
+ ri_add_cast_to(buf, operform->oprright);
+ }
ReleaseSysCache(opertup);
}
@@ -2801,6 +2947,7 @@ ri_LoadConstraintInfo(Oid constraintOid)
bool isNull;
ArrayType *arr;
int numkeys;
+ int i;
/*
* On the first call initialize the hashtable
@@ -2879,6 +3026,20 @@ ri_LoadConstraintInfo(Oid constraintOid)
pfree(arr); /* free de-toasted copy, if any */
adatum = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype, &isNull);
+ if (isNull)
+ elog(ERROR, "null confreftype for constraint %u", constraintOid);
+ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
+ if (ARR_NDIM(arr) != 1 ||
+ ARR_DIMS(arr)[0] != numkeys ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != CHAROID)
+ elog(ERROR, "confreftype is not a 1-D char array");
+ memcpy(riinfo->fk_reftypes, ARR_DATA_PTR(arr), numkeys * sizeof(char));
+ if ((Pointer) arr != DatumGetPointer(adatum))
+ pfree(arr); /* free de-toasted copy, if any */
+
+ adatum = SysCacheGetAttr(CONSTROID, tup,
Anum_pg_constraint_conpfeqop, &isNull);
if (isNull)
elog(ERROR, "null conpfeqop for constraint %u", constraintOid);
@@ -2921,6 +3082,24 @@ ri_LoadConstraintInfo(Oid constraintOid)
if ((Pointer) arr != DatumGetPointer(adatum))
pfree(arr); /* free de-toasted copy, if any */
+ /*
+ * Fix up some stuff for Foreign Key Arrays. We need a has_array flag
+ * indicating whether there's an array foreign key, and we want to set
+ * ff_eq_oprs[i] to array_eq() for array columns, because that's what
+ * makes sense for ri_KeysEqual, and we have no other use for ff_eq_oprs
+ * in this module. (If we did, substituting the array comparator at the
+ * call point in ri_KeysEqual might be more appropriate.)
+ */
+ riinfo->has_array = false;
+ for (i = 0; i < numkeys; i++)
+ {
+ if (riinfo->fk_reftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ riinfo->has_array = true;
+ riinfo->ff_eq_oprs[i] = ARRAY_EQ_OP;
+ }
+ }
+
ReleaseSysCache(tup);
/*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index b543b7046c..919f1eded5 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -314,6 +314,9 @@ static char *pg_get_viewdef_worker(Oid viewoid,
static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
static void decompile_column_index_array(Datum column_index_array, Oid relId,
StringInfo buf);
+static void decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf);
static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
const Oid *excludeOps,
@@ -1899,7 +1902,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
{
case CONSTRAINT_FOREIGN:
{
- Datum val;
+ Datum colindexes;
+ Datum reftypes;
bool isnull;
const char *string;
@@ -1907,13 +1911,21 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
appendStringInfoString(&buf, "FOREIGN KEY (");
/* Fetch and build referencing-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_conkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_conkey,
+ &isnull);
if (isnull)
elog(ERROR, "null conkey for constraint %u",
constraintId);
+ reftypes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype,
+ &isnull);
+ if (isnull)
+ elog(ERROR, "null confreftype for constraint %u",
+ constraintId);
- decompile_column_index_array(val, conForm->conrelid, &buf);
+ decompile_fk_column_index_array(colindexes, reftypes,
+ conForm->conrelid, &buf);
/* add foreign relation name */
appendStringInfo(&buf, ") REFERENCES %s(",
@@ -1921,13 +1933,15 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
NIL));
/* Fetch and build referenced-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_confkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confkey,
+ &isnull);
if (isnull)
elog(ERROR, "null confkey for constraint %u",
constraintId);
- decompile_column_index_array(val, conForm->confrelid, &buf);
+ decompile_column_index_array(colindexes,
+ conForm->confrelid, &buf);
appendStringInfoChar(&buf, ')');
@@ -2202,6 +2216,66 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
}
}
+ /*
+ * Convert an int16[] Datum and a char[] Datum into a comma-separated
+ * list of column names for the indicated relation, prefixed by appropriate
+ * keywords depending on the foreign key reference semantics indicated by
+ * the char[] entries. Append the text to buf.
+ */
+ static void
+ decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf)
+ {
+ Datum *keys;
+ int nKeys;
+ Datum *reftypes;
+ int nReftypes;
+ int j;
+
+ /* Extract data from array of int16 */
+ deconstruct_array(DatumGetArrayTypeP(column_index_array),
+ INT2OID, sizeof(int16), true, 's',
+ &keys, NULL, &nKeys);
+
+ /* Extract data from array of char */
+ deconstruct_array(DatumGetArrayTypeP(fk_reftype_array),
+ CHAROID, sizeof(char), true, 'c',
+ &reftypes, NULL, &nReftypes);
+
+ if (nKeys != nReftypes)
+ elog(ERROR, "wrong confreftype cardinality");
+
+ for (j = 0; j < nKeys; j++)
+ {
+ char *colName;
+ const char *prefix;
+
+ colName = get_relid_attribute_name(relId, DatumGetInt16(keys[j]));
+
+ switch (DatumGetChar(reftypes[j]))
+ {
+ case FKCONSTR_REF_PLAIN:
+ prefix = "";
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ prefix = "EACH ELEMENT OF ";
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d",
+ (int) DatumGetChar(reftypes[j]));
+ prefix = NULL; /* keep compiler quiet */
+ break;
+ }
+
+ if (j == 0)
+ appendStringInfo(buf, "%s%s", prefix,
+ quote_identifier(colName));
+ else
+ appendStringInfo(buf, ", %s%s", prefix,
+ quote_identifier(colName));
+ }
+}
/* ----------
* get_expr - Decompile an expression tree
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index ec035d8434..903f9e19cb 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -104,8 +104,16 @@ CATALOG(pg_constraint,2606)
int16 confkey[1];
/*
+ * If a foreign key, the reference semantics for each column
+ */
+ char confreftype[1];
+
+ /*
* If a foreign key, the OIDs of the PK = FK equality operators for each
* column of the constraint
+ *
+ * Note: for Foreign Key Arrays, all these operators are for the array's
+ * element type.
*/
Oid conpfeqop[1];
@@ -150,7 +158,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* compiler constants for pg_constraint
* ----------------
*/
-#define Natts_pg_constraint 24
+#define Natts_pg_constraint 25
#define Anum_pg_constraint_conname 1
#define Anum_pg_constraint_connamespace 2
#define Anum_pg_constraint_contype 3
@@ -169,12 +177,13 @@ typedef FormData_pg_constraint *Form_pg_constraint;
#define Anum_pg_constraint_connoinherit 16
#define Anum_pg_constraint_conkey 17
#define Anum_pg_constraint_confkey 18
-#define Anum_pg_constraint_conpfeqop 19
-#define Anum_pg_constraint_conppeqop 20
-#define Anum_pg_constraint_conffeqop 21
-#define Anum_pg_constraint_conexclop 22
-#define Anum_pg_constraint_conbin 23
-#define Anum_pg_constraint_consrc 24
+#define Anum_pg_constraint_confreftype 19
+#define Anum_pg_constraint_conpfeqop 20
+#define Anum_pg_constraint_conppeqop 21
+#define Anum_pg_constraint_conffeqop 22
+#define Anum_pg_constraint_conexclop 23
+#define Anum_pg_constraint_conbin 24
+#define Anum_pg_constraint_consrc 25
/* ----------------
* initial contents of pg_constraint
@@ -195,7 +204,9 @@ typedef FormData_pg_constraint *Form_pg_constraint;
/*
* Valid values for confupdtype and confdeltype are the FKCONSTR_ACTION_xxx
* constants defined in parsenodes.h. Valid values for confmatchtype are
- * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h.
+ * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h. Valid values
+ * for elements of confreftype[] are the FKCONSTR_REF_xxx constants defined
+ * in parsenodes.h.
*/
#endif /* PG_CONSTRAINT_H */
diff --git a/src/include/catalog/pg_constraint_fn.h b/src/include/catalog/pg_constraint_fn.h
index 37b0b4ba82..cd1ce3c94e 100644
--- a/src/include/catalog/pg_constraint_fn.h
+++ b/src/include/catalog/pg_constraint_fn.h
@@ -40,6 +40,7 @@ extern Oid CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 34d6afc80f..09eea88b77 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2082,6 +2082,10 @@ typedef enum ConstrType /* types of constraints */
#define FKCONSTR_MATCH_PARTIAL 'p'
#define FKCONSTR_MATCH_SIMPLE 's'
+ /* Foreign key column reference semantics codes */
+ #define FKCONSTR_REF_PLAIN 'p'
+ #define FKCONSTR_REF_EACH_ELEMENT 'e'
+
typedef struct Constraint
{
NodeTag type;
@@ -2117,6 +2121,7 @@ typedef struct Constraint
RangeVar *pktable; /* Primary key table */
List *fk_attrs; /* Attributes of foreign key */
List *pk_attrs; /* Corresponding attrs in PK table */
+ List *fk_reftypes; /* Per-column reference semantics (int List) */
char fk_matchtype; /* FULL, PARTIAL, SIMPLE */
char fk_upd_action; /* ON UPDATE action */
char fk_del_action; /* ON DELETE action */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f50e45e886..d3f4803006 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -140,6 +140,7 @@ PG_KEYWORD("domain", DOMAIN_P, UNRESERVED_KEYWORD)
PG_KEYWORD("double", DOUBLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD)
PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD)
+PG_KEYWORD("element", ELEMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("else", ELSE, RESERVED_KEYWORD)
PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index e560f0c96e..c4a2e118ed 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -67,6 +67,7 @@ extern int parser_coercion_errposition(ParseState *pstate,
extern Oid select_common_type(ParseState *pstate, List *exprs,
const char *context, Node **which_expr);
+extern Oid select_common_type_2args(Oid ptype, Oid ntype);
extern Node *coerce_to_common_type(ParseState *pstate, Node *node,
Oid targetTypeId,
const char *context);
diff --git a/src/test/regress/expected/element_foreign_key.out b/src/test/regress/expected/element_foreign_key.out
new file mode 100644
index 0000000000..e9ad9bd54c
--- /dev/null
+++ b/src/test/regress/expected/element_foreign_key.out
@@ -0,0 +1,703 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fkarray"
+DETAIL: Key (ftest1)=({10,1}) is not present in table "pktableforarray".
+DROP TABLE FKTABLEFORARRAY;
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4,6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL,4,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+ERROR: insert or update on table "fktableforarraymdim" violates foreign key constraint "fktableforarraymdim_ftest1_fkey"
+DETAIL: Key (ftest1)=({{1,2},{6,NULL}}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+ERROR: null value in column "ftest1" violates not-null constraint
+DETAIL: Failing row contains (null, 21).
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+ {1} | 4
+(11 rows)
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+ERROR: array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({D}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({A,B,D}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(A) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(B) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- -- Repeat a similar test using INT2 keys coerced from INT4
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ERROR: foreign key constraint "fktableforarray_ftest1_fkey" cannot be implemented
+DETAIL: Key column "ftest1" has element type integer which does not have a default btree operator class that's compatible with class "float8_ops".
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(A, {A,B,C}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(B, {A}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Test Foreign Key Arrays with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,99)"}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,1)","(2010,1)"}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2010,99)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2011,1)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({0,1}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({2,1}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(2, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(20, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+ERROR: foreign keys support only one array column
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: foreign keys support only one array column
+DROP TABLE F1;
+-- Cleanup
+DROP TABLE DIM1;
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "x2_x1_fkey"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "fk_const"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+DROP TABLE x1;
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{100,100,100},{NULL,NULL,20},{7,8,10}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
+-- Check GIN index
+-- Define PKTABLEFORARRAYGIN
+CREATE TABLE PKTABLEFORARRAYGIN ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAYGIN
+INSERT INTO PKTABLEFORARRAYGIN VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (5, 'Test5');
+-- Define FKTABLEFORARRAYGIN
+CREATE TABLE FKTABLEFORARRAYGIN ( ftest1 int[],
+ ftest2 int PRIMARY KEY,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYGIN
+ ON DELETE NO ACTION ON UPDATE NO ACTION);
+-- -- Create index on FKTABLEFORARRAYGIN
+CREATE INDEX ON FKTABLEFORARRAYGIN USING gin (ftest1 array_ops);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,5,3}', 10);
+-- Try using the indexable operator
+SELECT * FROM FKTABLEFORARRAYGIN WHERE ftest1 @> ARRAY[5];
+ ftest1 | ftest2
+-------------+--------
+ {5} | 1
+ {3,5,2,5} | 3
+ {3,5,4,1,3} | 5
+ {5,1} | 7
+ {3,4,5,3} | 10
+(5 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYGIN;
+DROP TABLE PKTABLEFORARRAYGIN;
+-- Check DELECTE CASCADE ACTION
+CREATE TABLE PKTABLEFORARRAYDELETE ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAYDELETE
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (5, 'Test5');
+-- Define FKTABLEFORARRAYDELETE
+CREATE TABLE FKTABLEFORARRAYDELETE ( ftest1 int[],
+ ftest2 int,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYDELETE
+ ON DELETE CASCADE ON UPDATE NO ACTION );
+-- Create index on FKTABLEFORARRAYDELETE
+CREATE INDEX ON FKTABLEFORARRAYDELETE USING gin (ftest1);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,5,3}', 10);
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+ ftest1 | ftest2
+-------------+--------
+ {5} | 1
+ {3,2} | 2
+ {3,5,2,5} | 3
+ {3,4,4} | 4
+ {3,5,4,1,3} | 5
+ {1} | 6
+ {5,1} | 7
+ {2,1,2,4,1} | 8
+ {4,2} | 9
+ {3,4,5,3} | 10
+(10 rows)
+
+-- Check FKTABLE for target rows
+SELECT * FROM FKTABLEFORARRAYDELETE WHERE ftest1 @> ARRAY[5];
+ ftest1 | ftest2
+-------------+--------
+ {5} | 1
+ {3,5,2,5} | 3
+ {3,5,4,1,3} | 5
+ {5,1} | 7
+ {3,4,5,3} | 10
+(5 rows)
+
+-- Perfrorm delete
+DELETE FROM PKTABLEFORARRAYDELETE WHERE ptest1 = 5;
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+ ftest1 | ftest2
+-------------+--------
+ {3,2} | 2
+ {3,4,4} | 4
+ {1} | 6
+ {2,1,2,4,1} | 8
+ {4,2} | 9
+(5 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYDELETE;
+DROP TABLE PKTABLEFORARRAYDELETE;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index aa5e6af621..552f8d4e8a 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -104,7 +104,12 @@ test: publication subscription
# ----------
# Another group of parallel tests
# ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
+test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
+
+# ----------
+# Another group of parallel tests (no room in the previouse group)
+# ----------
+test: element_foreign_key
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 3866314a92..eb7710d518 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -142,6 +142,7 @@ test: amutils
test: select_views
test: portals_p2
test: foreign_key
+test: element_foreign_key
test: cluster
test: dependency
test: guc
diff --git a/src/test/regress/sql/element_foreign_key.sql b/src/test/regress/sql/element_foreign_key.sql
new file mode 100644
index 0000000000..9657ea1a12
--- /dev/null
+++ b/src/test/regress/sql/element_foreign_key.sql
@@ -0,0 +1,549 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- -- Repeat a similar test using INT2 keys coerced from INT4
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Test Foreign Key Arrays with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+
+
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+
+
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+-- Cleanup
+DROP TABLE DIM1;
+
+
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+DROP TABLE x2;
+DROP TABLE x1;
+
+
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+DROP TABLE F1;
+
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+DROP TABLE F1;
+
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
+
+-- Check GIN index
+-- Define PKTABLEFORARRAYGIN
+CREATE TABLE PKTABLEFORARRAYGIN ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAYGIN
+INSERT INTO PKTABLEFORARRAYGIN VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (5, 'Test5');
+
+-- Define FKTABLEFORARRAYGIN
+CREATE TABLE FKTABLEFORARRAYGIN ( ftest1 int[],
+ ftest2 int PRIMARY KEY,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYGIN
+ ON DELETE NO ACTION ON UPDATE NO ACTION);
+
+-- -- Create index on FKTABLEFORARRAYGIN
+CREATE INDEX ON FKTABLEFORARRAYGIN USING gin (ftest1 array_ops);
+
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,5,3}', 10);
+
+-- Try using the indexable operator
+SELECT * FROM FKTABLEFORARRAYGIN WHERE ftest1 @> ARRAY[5];
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYGIN;
+DROP TABLE PKTABLEFORARRAYGIN;
+
+-- Check DELECTE CASCADE ACTION
+CREATE TABLE PKTABLEFORARRAYDELETE ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAYDELETE
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (5, 'Test5');
+
+-- Define FKTABLEFORARRAYDELETE
+CREATE TABLE FKTABLEFORARRAYDELETE ( ftest1 int[],
+ ftest2 int,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYDELETE
+ ON DELETE CASCADE ON UPDATE NO ACTION );
+
+-- Create index on FKTABLEFORARRAYDELETE
+CREATE INDEX ON FKTABLEFORARRAYDELETE USING gin (ftest1);
+
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,5,3}', 10);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+
+-- Check FKTABLE for target rows
+SELECT * FROM FKTABLEFORARRAYDELETE WHERE ftest1 @> ARRAY[5];
+
+-- Perfrorm delete
+DELETE FROM PKTABLEFORARRAYDELETE WHERE ptest1 = 5;
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYDELETE;
+DROP TABLE PKTABLEFORARRAYDELETE;
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-11-13 00:41 ` Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Andreas Karlsson @ 2017-11-13 00:41 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Stephen Frost <sfrost@snowman.net>; David Steele <david@pgmasters.net>; Alvaro Herrera <alvherre@2ndquadrant.com>; Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; pgsql-hackers-owner@postgresql.org, Tom Lane <tgl@sss.pgh.pa.us>; Hans-Jürgen Schönig <hs@cybertec.at>
On 11/10/2017 01:47 AM, Mark Rofail wrote:
> I am sorry for the late reply
There is no reason for you to be. It did not take you 6 weeks to do a
review. :) Thanks for this new version.
> == Functional review
>
> >1) MATCH FULL does not seem to care about NULLS in arrays. In the
> example below I expected both inserts into the referring table to fail.
>
>
> It seems in your example the only failed case was: INSERT INTO fk VALUES
> (NULL, '{1}');
> which shouldn't work, can you clarify this?
I think that if you use MATH FULL the query should fail if you have a
NULL in the array.
> >2) To me it was not obvious that ON DELETE CASCADE would delete
> the whole rows rather than delete the members from the array, and
> this kind of misunderstanding can lead to pretty bad surprises in
> production. I am leaning towards not supporting CASCADE.
>
>
> I would say so too, maybe we should remove ON DELETE CASCADE until we
> have supported all remaining actions.
I am leaning towards this too. I would personally be fine with a first
version without support for CASCADE since it is not obvious to me what
CASCADE should do.
> == The @>> operator
> I would argue that allocating an array of datums and building an array
> would have the same complexity
I am not sure what you mean here. Just because something has the same
complexity does not mean there can't be major performance differences.
> == Code review
>
> >I think the code in RI_Initial_Check() would be cleaner if you
> used "CROSS JOIN LATERAL unnest(col)" rather than having unnest() in
> the target list. This way you would not need to rename all columns
> and the code paths for the array case could look more like the code
> path for the normal case.
>
> Can you clarify what you mean a bit more?
I think the code would look cleaner if you generate the following query:
SELECT fk.x, fk.ys FROM ONLY t2 fk CROSS JOIN LATERAL
pg_catalog.unnest(ys) a2 (v) LEFT OUTER JOIN ONLY t1 pk ON pk.x = fk.x
AND pk.y = a2.v WHERE [...]
rather than:
SELECT fk.k1, fk.ak2 FROM (SELECT x k1, pg_catalog.unnest(ys) k2, ys ak2
FROM ONLY t2) fk LEFT OUTER JOIN ONLY t1 pk ON pk.x = fk.k1 AND pk.y =
fk.k2 WHERE [...]
= New stuff
When applying the patch I got some white space warnings:
Array-ELEMENT-foreign-key-v5.3.patch:1343: space before tab in indent,
indent with spaces.
format_type_be(oprleft), format_type_be(oprright))));
Array-ELEMENT-foreign-key-v5.3.patch:1345: trailing whitespace.
When compiling I got an error:
ri_triggers.c: In function ‘ri_GenerateQual’:
ri_triggers.c:2693:19: error: unknown type name ‘d’
Oid oprcommon;d
^
ri_triggers.c:2700:3: error: conflicting types for ‘oprright’
oprright = get_array_type(operform->oprleft);
^~~~~~~~
ri_triggers.c:2691:9: note: previous declaration of ‘oprright’ was here
Oid oprright;
^~~~~~~~
<builtin>: recipe for target 'ri_triggers.o' failed
When building the documentation I got two warnings:
/usr/bin/osx:catalogs.sgml:2349:17:W: empty end-tag
/usr/bin/osx:catalogs.sgml:2350:17:W: empty end-tag
When running the tests I got a failure in element_foreign_key.
Andreas
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
@ 2017-11-13 11:32 ` Mark Rofail <markm.rofail@gmail.com>
2017-11-27 01:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
0 siblings, 2 replies; 200+ messages in thread
From: Mark Rofail @ 2017-11-13 11:32 UTC (permalink / raw)
To: Andreas Karlsson <andreas@proxel.se>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Stephen Frost <sfrost@snowman.net>; David Steele <david@pgmasters.net>; Alvaro Herrera <alvherre@2ndquadrant.com>; Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; pgsql-hackers-owner@postgresql.org, Tom Lane <tgl@sss.pgh.pa.us>; Hans-Jürgen Schönig <hs@cybertec.at>
>
> >2) To me it was not obvious that ON DELETE CASCADE would delete
>>
> the whole rows rather than delete the members from the array, and
>> this kind of misunderstanding can lead to pretty bad surprises in
>> production. I am leaning towards not supporting CASCADE.
>>
>> I would say so too, maybe we should remove ON DELETE CASCADE until we
>> have supported all remaining actions.
>
> Delete Cascade support is now removed
> == The @>> operator
>> I would argue that allocating an array of datums and building an array
>> would have the same complexity
>>
>
> I am not sure what you mean here. Just because something has the same
> complexity does not mean there can't be major performance differences.
I have spend a lot of time working on this operator and would like to
benefit from it. How should I go about this ? Start a new patch ?
> = New stuff
>
Everything is now working correctly
== Functional review
>
> >1) MATCH FULL does not seem to care about NULLS in arrays. In the
> example below I expected both inserts into the referring table to fail.
>
>
> It seems in your example the only failed case was: INSERT INTO fk VALUES
> (NULL, '{1}');
> which shouldn't work, can you clarify this?
I think that if you use MATH FULL the query should fail if you have a NULL
> in the array.
== Code review
>
> >I think the code in RI_Initial_Check() would be cleaner if you
> used "CROSS JOIN LATERAL unnest(col)" rather than having unnest() in
> the target list. This way you would not need to rename all columns
> and the code paths for the array case could look more like the code
> path for the normal case.
>
> Can you clarify what you mean a bit more?
I think the code would look cleaner if you generate the following query:
> SELECT fk.x, fk.ys FROM ONLY t2 fk CROSS JOIN LATERAL
> pg_catalog.unnest(ys) a2 (v) LEFT OUTER JOIN ONLY t1 pk ON pk.x = fk.x AND
> pk.y = a2.v WHERE [...]
> rather than:
> SELECT fk.k1, fk.ak2 FROM (SELECT x k1, pg_catalog.unnest(ys) k2, ys ak2
> FROM ONLY t2) fk LEFT OUTER JOIN ONLY t1 pk ON pk.x = fk.k1 AND pk.y =
> fk.k2 WHERE [...]
So the two main issues we remain to resolve are MATCH FULL and the
RI_Initial_Check() query refactoring. The problem is that I am not one of
the original authors and have not touched this part of the code.
I understand the problem but it will take some time for me to understand
how to resolve everything.
Best Regards,
Mark Rofail
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
Array-ELEMENT-foreign-key-v5.3(fixed).patch (text/x-patch, 119.7K) download
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ef60a58631..1861ca42e2 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2342,6 +2342,16 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</row>
<row>
+ <entry><structfield>confreftype</structfield></entry>
+ <entry><type>char[]</type></entry>
+ <entry></entry>
+ <entry>If a foreign key, the reference semantics for each column:
+ <literal>p</literal> = plain (simple equality),
+ <literal>e</literal> = each element of referencing array must have a match
+ </entry>
+ </row>
+
+ <row>
<entry><structfield>conpfeqop</structfield></entry>
<entry><type>oid[]</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</literal></entry>
@@ -2387,6 +2397,12 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</table>
<para>
+ When <structfield>confreftype</structfield> indicates array to scalar
+ foreign key reference semantics, the equality operators listed in
+ <structfield>conpfeqop</structfield> etc are for the array's element type.
+ </para>
+
+ <para>
In the case of an exclusion constraint, <structfield>conkey</structfield>
is only useful for constraint elements that are simple column references.
For other cases, a zero appears in <structfield>conkey</structfield>
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index daba66c187..f22fd34a32 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -882,6 +882,116 @@ CREATE TABLE order_items (
</para>
</sect2>
+ <sect2 id="ddl-constraints-element-fk">
+ <title>Foreign Key Arrays</title>
+
+ <indexterm>
+ <primary>Foreign Key Arrays</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>ELEMENT foreign key</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>Array ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>referential integrity</primary>
+ </indexterm>
+
+ <para>
+ Another option you have with foreign keys is to use a referencing column
+ which is an array of elements with the same type (or a compatible one) as
+ the referenced column in the related table. This feature is called
+ <firstterm>Foreign Key Arrays</firstterm> and is implemented in PostgreSQL
+ with <firstterm>EACH ELEMENT OF foreign key constraints</firstterm>, as
+ described in the following example:
+
+ <programlisting>
+ CREATE TABLE drivers (
+ driver_id integer PRIMARY KEY,
+ first_name text,
+ last_name text
+ );
+
+ CREATE TABLE races (
+ race_id integer PRIMARY KEY,
+ title text,
+ race_day date,
+ final_positions integer[],
+ FOREIGN KEY <emphasis> (EACH ELEMENT OF final_positions) REFERENCES drivers </emphasis>
+ );
+ </programlisting>
+
+ The above example uses an array (<literal>final_positions</literal>) to
+ store the results of a race: for each of its elements a referential
+ integrity check is enforced on the <literal>drivers</literal> table. Note
+ that <literal>(EACH ELEMENT OF ...) REFERENCES</literal> is an extension of
+ PostgreSQL and it is not included in the SQL standard.
+ </para>
+
+ <para>
+ We currently only support the table constraint form.
+ </para>
+
+ <para>
+ Even though the most common use case for Foreign Key Array constraint is on
+ a single column key, you can define a Foreign Key Array constraint on a
+ group of columns.
+
+ <programlisting>
+ CREATE TABLE available_moves (
+ kind text,
+ move text,
+ description text,
+ PRIMARY KEY (kind, move)
+ );
+
+ CREATE TABLE paths (
+ description text,
+ kind text,
+ moves text[],
+ <emphasis>FOREIGN KEY (kind, EACH ELEMENT OF moves) REFERENCES available_moves (kind, move)</emphasis>
+ );
+
+ INSERT INTO available_moves VALUES ('relative', 'LN', 'look north');
+ INSERT INTO available_moves VALUES ('relative', 'RL', 'rotate left');
+ INSERT INTO available_moves VALUES ('relative', 'RR', 'rotate right');
+ INSERT INTO available_moves VALUES ('relative', 'MF', 'move forward');
+ INSERT INTO available_moves VALUES ('absolute', 'N', 'move north');
+ INSERT INTO available_moves VALUES ('absolute', 'S', 'move south');
+ INSERT INTO available_moves VALUES ('absolute', 'E', 'move east');
+ INSERT INTO available_moves VALUES ('absolute', 'W', 'move west');
+
+ INSERT INTO paths VALUES ('L-shaped path', 'relative', '{LN, RL, MF, RR, MF, MF}');
+ INSERT INTO paths VALUES ('L-shaped path', 'absolute', '{W, N, N}');
+ </programlisting>
+
+ On top of standard foreign key requirements, array
+ <literal>ELEMENT</literal> foreign key constraints require that the
+ referencing column is an array of a compatible type of the corresponding
+ referenced column.
+
+ However, it is very important to note that, currently, we only support
+ one array reference per foreign key.
+ </para>
+
+ <para>
+ For more detailed information on Foreign Key Array options and special
+ cases, please refer to the documentation for
+ <xref linkend="sql-createtable-foreign-key"> and
+ <xref linkend="sql-createtable-element-foreign-key-constraints">.
+ </para>
+ </sect2>
+
<sect2 id="ddl-constraints-exclusion">
<title>Exclusion Constraints</title>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index bbb3a51def..7a0e2c638d 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -65,7 +65,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ] |
UNIQUE <replaceable class="parameter">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="parameter">index_parameters</replaceable> |
- REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
+ [EACH ELEMENT OF] REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="PARAMETER">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -867,10 +867,10 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
- <varlistentry>
+ <varlistentry id="sql-createtable-foreign-key" xreflabel="FOREIGN KEY">
<term><literal>REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
- <term><literal>FOREIGN KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] )
+ <term><literal>FOREIGN KEY ( [EACH ELEMENT OF] <replaceable class="parameter">column_name</replaceable> [, ... ] )
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] ) ]
[ MATCH <replaceable class="parameter">matchtype</replaceable> ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ]
@@ -894,6 +894,20 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</para>
<para>
+ In case the column name <replaceable
+ class="parameter">column</replaceable> is prepended with the
+ <literal>EACH ELEMENT OF</literal> keyword and <replaceable
+ class="parameter">column</replaceable> is an array of elements compatible
+ with the corresponding <replaceable
+ class="parameter">refcolumn</replaceable> in <replaceable
+ class="parameter">reftable</replaceable>, a Foreign Key Array constraint
+ is put in place (see <xref
+ linkend="sql-createtable-element-foreign-key-constraints"> for more
+ information). Multi-column keys with more than one <literal>EACH ELEMENT
+ OF</literal> column are currently not allowed.
+ </para>
+
+ <para>
A value inserted into the referencing column(s) is matched against the
values of the referenced table and referenced columns using the
given match type. There are three match types: <literal>MATCH
@@ -956,7 +970,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
<para>
Delete any rows referencing the deleted row, or update the
values of the referencing column(s) to the new values of the
- referenced columns, respectively.
+ referenced columns, respectively. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -965,7 +980,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
<term><literal>SET NULL</literal></term>
<listitem>
<para>
- Set the referencing column(s) to null.
+ Set the referencing column(s) to null. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -977,6 +993,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
Set the referencing column(s) to their default values.
(There must be a row in the referenced table matching the default
values, if they are not null, or the operation will fail.)
+ Currently not supported with array <literal>ELEMENT</literal>
+ foreign keys.
</para>
</listitem>
</varlistentry>
@@ -992,6 +1010,85 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
+ <varlistentry id="sql-createtable-element-foreign-key-constraints" xreflabel="FOREIGN KEY ARRAYS">
+ <term><literal>EACH ELEMENT OF <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
+
+ <listitem>
+ <para>
+ The <literal>EACH ELEMENT OF REFERENCES</literal> definition specifies a
+ <quote>Foreign Key Array</quote>, a special
+ kind of foreign key constraint requiring the referencing column to be an
+ array of elements of the same type (or a compatible one) as the
+ referenced column in the referenced table. The value of each element of
+ the <replaceable class="parameter">refcolumn</replaceable> array will be
+ matched against some row of <replaceable
+ class="parameter">reftable</replaceable>.
+ </para>
+
+ <para>
+ Foreign Key Arrays are an extension
+ of PostgreSQL and are not included in the SQL standard.
+ </para>
+
+ <para>
+ Even with Foreign Key Arrays, modifications in the referenced column can
+ trigger actions to be performed on the referencing array. Similarly to
+ standard foreign keys, you can specify these actions using the
+ <literal>ON DELETE</literal> and <literal>ON UPDATE</literal> clauses.
+ However, only the following actions for each clause are currently
+ allowed:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>ON UPDATE/DELETE NO ACTION</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints. This is the default action.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ON UPDATE/DELETE RESTRICT</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ON DELETE CASCADE</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints. Deletes the entire array.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ It is advisable to index the refrencing column using GIN index as it
+ considerably enhances the performance. Also concerning coercion while
+ using the GIN index:
+
+ <programlisting>
+ CREATE TABLE pktableforarray ( ptest1 int2 PRIMARY KEY, ptest2 text );
+ CREATE TABLE fktableforarray ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ </programlisting>
+ This syntax is fine since it will cast ptest1 to int4 upon RI checks,
+
+ <programlisting>
+ CREATE TABLE pktableforarray ( ptest1 int4 PRIMARY KEY, ptest2 text );
+ CREATE TABLE fktableforarray ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ </programlisting>
+ however, this syntax will cast ftest1 to int4 upon RI checks, thus defeating the
+ purpose of the index.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>DEFERRABLE</literal></term>
<term><literal>NOT DEFERRABLE</literal></term>
@@ -1961,6 +2058,16 @@ CREATE TABLE cities_partdef
</refsect2>
<refsect2>
+ <title id="sql-createtable-foreign-key-arrays">Array <literal>ELEMENT</literal> Foreign Keys</title>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys and the <literal>ELEMENT
+ REFERENCES</literal> clause are a <productname>PostgreSQL</productname>
+ extension.
+ </para>
+ </refsect2>
+
+ <refsect2>
<title><literal>PARTITION BY</literal> Clause</title>
<para>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 2bc9e90dcf..d9ca06f4e3 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2124,6 +2124,7 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c7b2f031f0..db7978ec45 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1213,6 +1213,7 @@ index_constraint_create(Relation heapRelation,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 7dee6db0eb..07d0fe980b 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -59,6 +59,7 @@ CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
@@ -82,6 +83,7 @@ CreateConstraintEntry(const char *constraintName,
Datum values[Natts_pg_constraint];
ArrayType *conkeyArray;
ArrayType *confkeyArray;
+ ArrayType *confreftypeArray;
ArrayType *conpfeqopArray;
ArrayType *conppeqopArray;
ArrayType *conffeqopArray;
@@ -119,7 +121,11 @@ CreateConstraintEntry(const char *constraintName,
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = Int16GetDatum(foreignKey[i]);
confkeyArray = construct_array(fkdatums, foreignNKeys,
- INT2OID, 2, true, 's');
+ INT2OID, sizeof(int16), true, 's');
+ for (i = 0; i < foreignNKeys; i++)
+ fkdatums[i] = CharGetDatum(foreignRefType[i]);
+ confreftypeArray = construct_array(fkdatums, foreignNKeys,
+ CHAROID, sizeof(char), true, 'c');
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
conpfeqopArray = construct_array(fkdatums, foreignNKeys,
@@ -136,6 +142,7 @@ CreateConstraintEntry(const char *constraintName,
else
{
confkeyArray = NULL;
+ confreftypeArray = NULL;
conpfeqopArray = NULL;
conppeqopArray = NULL;
conffeqopArray = NULL;
@@ -188,6 +195,11 @@ CreateConstraintEntry(const char *constraintName,
else
nulls[Anum_pg_constraint_confkey - 1] = true;
+ if (confreftypeArray)
+ values[Anum_pg_constraint_confreftype - 1] = PointerGetDatum(confreftypeArray);
+ else
+ nulls[Anum_pg_constraint_confreftype - 1] = true;
+
if (conpfeqopArray)
values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
else
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 165b165d55..91ee3f79b9 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7116,6 +7116,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Relation pkrel;
int16 pkattnum[INDEX_MAX_KEYS];
int16 fkattnum[INDEX_MAX_KEYS];
+ char fkreftypes[INDEX_MAX_KEYS];
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
Oid opclasses[INDEX_MAX_KEYS];
@@ -7123,10 +7124,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid ppeqoperators[INDEX_MAX_KEYS];
Oid ffeqoperators[INDEX_MAX_KEYS];
int i;
+ ListCell *lc;
int numfks,
numpks;
Oid indexOid;
Oid constrOid;
+ bool has_array;
bool old_check_ok;
ObjectAddress address;
ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
@@ -7203,6 +7206,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
*/
MemSet(pkattnum, 0, sizeof(pkattnum));
MemSet(fkattnum, 0, sizeof(fkattnum));
+ MemSet(fkreftypes, 0, sizeof(fkreftypes));
MemSet(pktypoid, 0, sizeof(pktypoid));
MemSet(fktypoid, 0, sizeof(fktypoid));
MemSet(opclasses, 0, sizeof(opclasses));
@@ -7215,6 +7219,50 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
fkattnum, fktypoid);
/*
+ * Validate the reference semantics codes, too, and convert list to array
+ * format to pass to CreateConstraintEntry.
+ */
+ Assert(list_length(fkconstraint->fk_reftypes) == numfks);
+ has_array = false;
+ i = 0;
+ foreach(lc, fkconstraint->fk_reftypes)
+ {
+ char reftype = lfirst_int(lc);
+
+ switch (reftype)
+ {
+ case FKCONSTR_REF_PLAIN:
+ /* OK, nothing to do */
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ /* At most one FK column can be an array reference */
+ if (has_array)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign keys support only one array column")));
+ has_array = true;
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d", (int) reftype);
+ break;
+ }
+ fkreftypes[i] = reftype;
+ i++;
+ }
+
+ /* Array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions */
+ if (has_array)
+ {
+ if ((fkconstraint->fk_upd_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_upd_action != FKCONSTR_ACTION_RESTRICT) ||
+ (fkconstraint->fk_del_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("Foreign Key Arrays support only NO ACTION and RESTRICT actions")));
+ }
+
+ /*
* If the attribute list for the referenced table was omitted, lookup the
* definition of the primary key and use it. Otherwise, validate the
* supplied attribute list. In either case, discover the index OID and
@@ -7300,6 +7348,65 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
eqstrategy = BTEqualStrategyNumber;
/*
+ * If this is an array foreign key, we must look up the operators for
+ * the array element type, not the array type itself.
+ */
+ if (fkreftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ Oid elemopclass;
+
+ /* We check if the array element type exists and is of valid Oid */
+ fktype = get_base_element_type(fktype);
+ if (!OidIsValid(fktype))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has type %s which is not an array type.",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktypoid[i]))));
+
+ /*
+ * For the moment, we must also insist that the array's element
+ * type have a default btree opclass that is in the index's
+ * opfamily. This is necessary because ri_triggers.c relies on
+ * COUNT(DISTINCT x) on the element type, as well as on array_eq()
+ * on the array type, and we need those operations to have the
+ * same notion of equality that we're using otherwise.
+ *
+ * XXX this restriction is pretty annoying, considering the effort
+ * that's been put into the rest of the RI mechanisms to make them
+ * work with nondefault equality operators. In particular, it
+ * means that the cast-to-PK-datatype code path isn't useful for
+ * array-to-scalar references.
+ */
+ elemopclass = GetDefaultOpClass(fktype, BTREE_AM_OID);
+ if (!OidIsValid(elemopclass) ||
+ get_opclass_family(elemopclass) != opfamily)
+ {
+ /* Get the index opclass's name for the error message. */
+ char *opcname;
+
+ cla_ht = SearchSysCache1(CLAOID,
+ ObjectIdGetDatum(opclasses[i]));
+ if (!HeapTupleIsValid(cla_ht))
+ elog(ERROR, "cache lookup failed for opclass %u",
+ opclasses[i]);
+ cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
+ opcname = pstrdup(NameStr(cla_tup->opcname));
+ ReleaseSysCache(cla_ht);
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has element type %s which does not have a default btree operator class that's compatible with class \"%s\".",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktype),
+ opcname)));
+ }
+ }
+
+ /*
* There had better be a primary equality operator for the index.
* We'll use it for PK = PK comparisons.
*/
@@ -7358,17 +7465,17 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
}
if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("foreign key constraint \"%s\" "
- "cannot be implemented",
- fkconstraint->conname),
- errdetail("Key columns \"%s\" and \"%s\" "
- "are of incompatible types: %s and %s.",
- strVal(list_nth(fkconstraint->fk_attrs, i)),
- strVal(list_nth(fkconstraint->pk_attrs, i)),
- format_type_be(fktype),
- format_type_be(pktype))));
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("foreign key constraint \"%s\" "
+ "cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key columns \"%s\" and \"%s\" "
+ "are of incompatible types: %s and %s.",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ strVal(list_nth(fkconstraint->pk_attrs, i)),
+ format_type_be(fktype),
+ format_type_be(pktype))));
if (old_check_ok)
{
@@ -7398,6 +7505,13 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
* We may assume that pg_constraint.conkey is not changing.
*/
old_fktype = attr->atttypid;
+ if (fkreftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ old_fktype = get_base_element_type(old_fktype);
+ /* this shouldn't happen ... */
+ if (!OidIsValid(old_fktype))
+ elog(ERROR, "old foreign key column is not an array");
+ }
new_fktype = fktype;
old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
&old_castfunc);
@@ -7440,7 +7554,6 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
new_castfunc == old_castfunc &&
(!IsPolymorphicType(pfeqop_right) ||
new_fktype == old_fktype));
-
}
pfeqoperators[i] = pfeqop;
@@ -7464,6 +7577,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
indexOid,
RelationGetRelid(pkrel),
pkattnum,
+ fkreftypes,
pfeqoperators,
ppeqoperators,
ffeqoperators,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 92ae3822d8..e876db9242 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -661,6 +661,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
@@ -1032,6 +1033,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
char fk_matchtype = FKCONSTR_MATCH_SIMPLE;
List *fk_attrs = NIL;
List *pk_attrs = NIL;
+ List *fk_reftypes = NIL;
StringInfoData buf;
int funcnum;
OldTriggerInfo *info = NULL;
@@ -1061,7 +1063,10 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
if (i % 2)
fk_attrs = lappend(fk_attrs, arg);
else
+ {
pk_attrs = lappend(pk_attrs, arg);
+ fk_reftypes = lappend_int(fk_reftypes, FKCONSTR_REF_PLAIN);
+ }
}
/* Prepare description of constraint for use in messages */
@@ -1200,6 +1205,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
fkcon->conname = constr_name;
fkcon->fk_attrs = fk_attrs;
fkcon->pk_attrs = pk_attrs;
+ fkcon->fk_reftypes = fk_reftypes;
fkcon->fk_matchtype = fk_matchtype;
switch (info->funcoids[0])
{
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 08f3a3d357..3724c083e3 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -3164,6 +3164,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index cadd253ef1..7470dcc020 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2846,6 +2846,7 @@ _copyConstraint(const Constraint *from)
COPY_NODE_FIELD(pktable);
COPY_NODE_FIELD(fk_attrs);
COPY_NODE_FIELD(pk_attrs);
+ COPY_NODE_FIELD(fk_reftypes);
COPY_SCALAR_FIELD(fk_matchtype);
COPY_SCALAR_FIELD(fk_upd_action);
COPY_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 2866fd7b4a..045ed984c6 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2584,6 +2584,7 @@ _equalConstraint(const Constraint *a, const Constraint *b)
COMPARE_NODE_FIELD(pktable);
COMPARE_NODE_FIELD(fk_attrs);
COMPARE_NODE_FIELD(pk_attrs);
+ COMPARE_NODE_FIELD(fk_reftypes);
COMPARE_SCALAR_FIELD(fk_matchtype);
COMPARE_SCALAR_FIELD(fk_upd_action);
COMPARE_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 291d1eeb46..cc6dde0243 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3496,6 +3496,7 @@ _outConstraint(StringInfo str, const Constraint *node)
WRITE_NODE_FIELD(pktable);
WRITE_NODE_FIELD(fk_attrs);
WRITE_NODE_FIELD(pk_attrs);
+ WRITE_NODE_FIELD(fk_reftypes);
WRITE_CHAR_FIELD(fk_matchtype);
WRITE_CHAR_FIELD(fk_upd_action);
WRITE_CHAR_FIELD(fk_del_action);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c301ca465d..e98dd2c0d3 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -126,6 +126,13 @@ typedef struct ImportQual
List *table_names;
} ImportQual;
+/* Private struct for the result of foreign_key_column_elem production */
+typedef struct FKColElem
+{
+ Node *name; /* name of the column (a String) */
+ char reftype; /* FKCONSTR_REF_xxx code */
+} FKColElem;
+
/* ConstraintAttributeSpec yields an integer bitmask of these flags: */
#define CAS_NOT_DEFERRABLE 0x01
#define CAS_DEFERRABLE 0x02
@@ -183,6 +190,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
static void SplitColQualList(List *qualList,
List **constraintList, CollateClause **collClause,
core_yyscan_t yyscanner);
+static void SplitFKColElems(List *fkcolelems, List **names, List **reftypes);
static void processCASbits(int cas_bits, int location, const char *constrType,
bool *deferrable, bool *initdeferred, bool *not_valid,
bool *no_inherit, core_yyscan_t yyscanner);
@@ -233,6 +241,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
A_Indices *aind;
ResTarget *target;
struct PrivTarget *privtarget;
+ struct FKColElem *fkcolelem;
AccessPriv *accesspriv;
struct ImportQual *importqual;
InsertStmt *istmt;
@@ -358,6 +367,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <accesspriv> privilege
%type <list> privileges privilege_list
%type <privtarget> privilege_target
+%type <fkcolelem> foreign_key_column_elem
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
%type <ival> defacl_privilege_target
@@ -393,7 +403,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
execute_param_clause using_clause returning_clause
opt_enum_val_list enum_val_list table_func_column_list
create_generic_options alter_generic_options
- relation_expr_list dostmt_opt_list
+ relation_expr_list dostmt_opt_list foreign_key_column_list
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
@@ -625,8 +635,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
DOUBLE_P DROP
- EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
- EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
+ EACH ELEMENT ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE
+ EVENT EXCEPT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
EXTENSION EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FILTER FIRST_P FLOAT_P FOLLOWING FOR
@@ -3512,8 +3522,10 @@ ColConstraintElem:
n->contype = CONSTR_FOREIGN;
n->location = @1;
n->pktable = $2;
+ /* fk_attrs will be filled in by parse analysis */
n->fk_attrs = NIL;
n->pk_attrs = $3;
+ n->fk_reftypes = list_make1_int(FKCONSTR_REF_PLAIN);
n->fk_matchtype = $4;
n->fk_upd_action = (char) ($5 >> 8);
n->fk_del_action = (char) ($5 & 0xFF);
@@ -3707,14 +3719,15 @@ ConstraintElem:
NULL, yyscanner);
$$ = (Node *)n;
}
- | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
- opt_column_list key_match key_actions ConstraintAttributeSpec
+ | FOREIGN KEY '(' foreign_key_column_list ')' REFERENCES
+ qualified_name opt_column_list key_match key_actions
+ ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_FOREIGN;
n->location = @1;
+ SplitFKColElems($4, &n->fk_attrs, &n->fk_reftypes);
n->pktable = $7;
- n->fk_attrs = $4;
n->pk_attrs = $8;
n->fk_matchtype = $9;
n->fk_upd_action = (char) ($10 >> 8);
@@ -3747,6 +3760,29 @@ columnElem: ColId
$$ = (Node *) makeString($1);
}
;
+ foreign_key_column_list:
+ foreign_key_column_elem
+ { $$ = list_make1($1); }
+ | foreign_key_column_list ',' foreign_key_column_elem
+ { $$ = lappend($1, $3); }
+ ;
+
+ foreign_key_column_elem:
+ ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($1);
+ n->reftype = FKCONSTR_REF_PLAIN;
+ $$ = n;
+ }
+ | EACH ELEMENT OF ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($4);
+ n->reftype = FKCONSTR_REF_EACH_ELEMENT;
+ $$ = n;
+ }
+ ;
key_match: MATCH FULL
{
@@ -14739,6 +14775,7 @@ unreserved_keyword:
| DOUBLE_P
| DROP
| EACH
+ | ELEMENT
| ENABLE_P
| ENCODING
| ENCRYPTED
@@ -15856,6 +15893,23 @@ SplitColQualList(List *qualList,
*constraintList = qualList;
}
+/* Split a list of FKColElem structs into separate name and reftype lists */
+static void
+SplitFKColElems(List *fkcolelems, List **names, List **reftypes)
+{
+ ListCell *lc;
+
+ *names = NIL;
+ *reftypes = NIL;
+ foreach(lc, fkcolelems)
+ {
+ FKColElem *fkcolelem = (FKColElem *) lfirst(lc);
+
+ *names = lappend(*names, fkcolelem->name);
+ *reftypes = lappend_int(*reftypes, fkcolelem->reftype);
+ }
+}
+
/*
* Process result of ConstraintAttributeSpec, and set appropriate bool flags
* in the output command node. Pass NULL for any flags the particular
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 7d10d74a3e..00f9dbf36d 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1386,6 +1386,90 @@ select_common_type(ParseState *pstate, List *exprs, const char *context,
}
/*
+ * look at select_common_type(); in parse_coerce.c(src/backend/parser/parse_coerce.c)
+ *
+ * select_common_type_2args()
+ * Determine the common supertype of a list of input expressions.
+ * 'leftOid': left operand's type
+ * 'rightOid': left operand's type
+ */
+Oid select_common_type_2args(Oid leftOid, Oid rightOid)
+{
+ TYPCATEGORY leftcategory;
+ TYPCATEGORY rightcategory;
+ bool leftispreferred;
+ bool rightispreferred;
+
+ Assert(leftOid != InvalidOid);
+ Assert(rightOid != InvalidOid);
+
+ /*
+ * If input types are valid and exactly the same, just pick that type.
+ */
+ if (leftOid != UNKNOWNOID)
+ {
+ if (rightOid == leftOid)
+ return leftOid;
+ }
+
+ /*
+ * Nope, so set up for the full algorithm.
+ */
+ leftOid = getBaseType(leftOid);
+ rightOid = getBaseType(rightOid);
+ get_type_category_preferred(leftOid, &leftcategory, &leftispreferred);
+ get_type_category_preferred(rightOid, &rightcategory, &rightispreferred);
+
+ if (rightOid != UNKNOWNOID && rightOid != leftOid)
+ {
+ if (rightcategory != leftcategory)
+ {
+ /* both types in different categories? then not much hope... */
+ return InvalidOid;
+ }
+
+ if (can_coerce_type(1, &rightOid, &leftOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left */
+ if (!can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can not coerce to it implicitly right to left, thus coercion only works one way */
+ return leftOid;
+ }
+ else
+ {
+ /* coercion works both ways, then decide depending on preferred flag */
+ if (leftispreferred)
+ return leftOid;
+ else
+ return rightOid;
+ }
+ }
+ else{
+ /* can not coerce to it implicitly right to left */
+ if (can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left, thus coercion only works one way */
+ return rightOid;
+ }
+ else{
+ /* can not coerce either way */
+ return InvalidOid;
+ }
+ }
+ }
+
+ /*
+ * If all the inputs were UNKNOWN type --- ie, unknown-type literals ---
+ * then resolve as type TEXT.
+ */
+ if (leftOid == UNKNOWNOID)
+ leftOid = TEXTOID;
+
+ return leftOid;
+}
+
+/*
* coerce_to_common_type()
* Coerce an expression to the given type.
*
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 8461da490a..984fdb6b14 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -745,6 +745,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
* list of FK constraints to be processed later.
*/
constraint->fk_attrs = list_make1(makeString(column->colname));
+ /* grammar should have set fk_reftypes */
+ Assert(list_length(constraint->fk_reftypes) == 1);
cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
break;
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 4badb5fd7c..e4c1fc6c1d 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -118,9 +118,11 @@ typedef struct RI_ConstraintInfo
char confupdtype; /* foreign key's ON UPDATE action */
char confdeltype; /* foreign key's ON DELETE action */
char confmatchtype; /* foreign key's match type */
+ bool has_array; /* true if any reftype is EACH_ELEMENT */
int nkeys; /* number of key columns */
int16 pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */
int16 fk_attnums[RI_MAX_NUMKEYS]; /* attnums of referencing cols */
+ char fk_reftypes[RI_MAX_NUMKEYS]; /* reference semantics */
Oid pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = FK) */
Oid pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = PK) */
Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK = FK) */
@@ -204,7 +206,8 @@ static void ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype);
+ const char *rightop, Oid rightoptype,
+ char fkreftype);
static void ri_add_cast_to(StringInfo buf, Oid typid);
static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
static int ri_NullCheck(HeapTuple tup,
@@ -395,6 +398,7 @@ RI_FKey_check(TriggerData *trigdata)
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
StringInfoData querybuf;
+ StringInfoData countbuf;
char pkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
char paramname[16];
@@ -407,12 +411,27 @@ RI_FKey_check(TriggerData *trigdata)
* FOR KEY SHARE OF x
* The type id's for the $ parameters are those of the
* corresponding FK attributes.
+ *
+ * In case of an array ELEMENT foreign key, the previous query is used
+ * to count the number of matching rows and see if every combination
+ * is actually referenced.
+ * The wrapping query is
+ * SELECT 1 WHERE
+ * (SELECT count(DISTINCT y) FROM unnest($1) y)
+ * = (SELECT count(*) FROM (<QUERY>) z)
* ----------
*/
initStringInfo(&querybuf);
quoteRelationName(pkrelname, pk_rel);
appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
querysep = "WHERE";
+
+ if (riinfo->has_array)
+ {
+ initStringInfo(&countbuf);
+ appendStringInfo(&countbuf, "SELECT 1 WHERE ");
+ }
+
for (i = 0; i < riinfo->nkeys; i++)
{
Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
@@ -421,18 +440,41 @@ RI_FKey_check(TriggerData *trigdata)
quoteOneName(attname,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
sprintf(paramname, "$%d", i + 1);
+
+ /*
+ * In case of an array ELEMENT foreign key, we check that each
+ * distinct non-null value in the array is present in the PK
+ * table.
+ */
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&countbuf,
+ "(SELECT pg_catalog.count(DISTINCT y) FROM pg_catalog.unnest(%s) y)",
+ paramname);
+
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pf_eq_oprs[i],
- paramname, fk_type);
+ paramname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = fk_type;
}
appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
- /* Prepare and save the plan */
- qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
- &qkey, fk_rel, pk_rel, true);
+ if (riinfo->has_array)
+ {
+ appendStringInfo(&countbuf,
+ " OPERATOR(pg_catalog.=) (SELECT pg_catalog.count(*) FROM (%s) z)",
+ querybuf.data);
+
+ /* Prepare and save the plan for Foreign Key Arrays */
+ qplan = ri_PlanCheck(countbuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
+ }
+ else
+ /* Prepare and save the plan */
+ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
}
/*
@@ -559,7 +601,8 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pp_eq_oprs[i],
- paramname, pk_type);
+ paramname, pk_type,
+ FKCONSTR_REF_PLAIN);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -751,7 +794,8 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -974,7 +1018,8 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1130,7 +1175,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1309,7 +1355,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1475,7 +1522,8 @@ RI_FKey_setnull_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1651,7 +1699,8 @@ RI_FKey_setnull_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1817,7 +1866,8 @@ RI_FKey_setdefault_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2008,7 +2058,8 @@ RI_FKey_setdefault_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -2327,6 +2378,14 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
* For MATCH FULL:
* (fk.keycol1 IS NOT NULL [OR ...])
*
+ * In case of an array ELEMENT column, relname is replaced with the
+ * following subquery:
+ *
+ * SELECT unnest("keycol1") k1, "keycol1" ak1 [, ...]
+ * FROM ONLY "public"."fk"
+ *
+ * where all the columns are renamed in order to prevent name collisions.
+ *
* We attach COLLATE clauses to the operators when comparing columns
* that have different collations.
*----------
@@ -2338,15 +2397,46 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
{
quoteOneName(fkattname,
RIAttName(fk_rel, riinfo->fk_attnums[i]));
- appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
+ if (riinfo->has_array)
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%sfk.ak%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.k%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
sep = ", ";
}
quoteRelationName(pkrelname, pk_rel);
quoteRelationName(fkrelname, fk_rel);
- appendStringInfo(&querybuf,
- " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
- fkrelname, pkrelname);
+
+ if (riinfo->has_array)
+ {
+ sep = "";
+ appendStringInfo(&querybuf,
+ " FROM (SELECT ");
+ for (i = 0; i < riinfo->nkeys; i++)
+ {
+ quoteOneName(fkattname,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%spg_catalog.unnest(%s) k%d, %s ak%d",
+ sep, fkattname, i + 1, fkattname, i + 1);
+ else
+ appendStringInfo(&querybuf, "%s%s k%d", sep, fkattname,
+ i + 1);
+ sep = ", ";
+ }
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s) fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
+ }
+ else
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
strcpy(pkattname, "pk.");
strcpy(fkattname, "fk.");
@@ -2360,12 +2450,16 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
quoteOneName(pkattname + 3,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
- quoteOneName(fkattname + 3,
- RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname + 3, "k%d", i + 1);
+ else
+ quoteOneName(fkattname + 3,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
ri_GenerateQual(&querybuf, sep,
pkattname, pk_type,
riinfo->pf_eq_oprs[i],
- fkattname, fk_type);
+ fkattname, fk_type,
+ FKCONSTR_REF_PLAIN);
if (pk_coll != fk_coll)
ri_GenerateQualCollation(&querybuf, pk_coll);
sep = "AND";
@@ -2381,7 +2475,10 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
sep = "";
for (i = 0; i < riinfo->nkeys; i++)
{
- quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname, "k%d", i + 1);
+ else
+ quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
appendStringInfo(&querybuf,
"%sfk.%s IS NOT NULL",
sep, fkattname);
@@ -2557,20 +2654,23 @@ quoteRelationName(char *buffer, Relation rel)
/*
* ri_GenerateQual --- generate a WHERE clause equating two variables
*
- * The idea is to append " sep leftop op rightop" to buf. The complexity
- * comes from needing to be sure that the parser will select the desired
- * operator. We always name the operator using OPERATOR(schema.op) syntax
- * (readability isn't a big priority here), so as to avoid search-path
- * uncertainties. We have to emit casts too, if either input isn't already
- * the input type of the operator; else we are at the mercy of the parser's
- * heuristics for ambiguous-operator resolution.
- */
+ * The idea is to append " sep leftop op rightop" to buf, or if fkreftype is
+ * FKCONSTR_REF_EACH_ELEMENT, append " sep rightop @> leftop" to buf.
+ *
+ * The complexity comes from needing to be sure that the parser will select
+ * the desired operator. We always name the operator using
+ * OPERATOR(schema.op) syntax (readability isn't a big priority here), so as
+ * to avoid search-path uncertainties. We have to emit casts too, if either
+ * input isn't already the input type of the operator; else we are at the
+ * mercy of the parser's heuristics for ambiguous-operator resolution.
+ */
static void
ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype)
+ const char *rightop, Oid rightoptype,
+ char fkreftype)
{
HeapTuple opertup;
Form_pg_operator operform;
@@ -2586,14 +2686,59 @@ ri_GenerateQual(StringInfo buf,
nspname = get_namespace_name(operform->oprnamespace);
- appendStringInfo(buf, " %s %s", sep, leftop);
- if (leftoptype != operform->oprleft)
- ri_add_cast_to(buf, operform->oprleft);
- appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
- appendStringInfoString(buf, oprname);
- appendStringInfo(buf, ") %s", rightop);
- if (rightoptype != operform->oprright)
- ri_add_cast_to(buf, operform->oprright);
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ Oid oprright;
+ Oid oprleft;
+ Oid oprcommon;
+ /*
+ * we first need to get the array types and decide
+ * the more appropriate one
+ */
+
+ /* get array type of refrenced element*/
+ oprright = get_array_type(operform->oprleft);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprleft))));
+ /* get array type of refrencing element*/
+ oprleft = get_array_type(operform->oprright);
+ if (!OidIsValid(oprleft))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprright))));
+ /* compare the two array types and try to find
+ a common supertype */
+ oprcommon = select_common_type_2args(oprleft, oprright);
+ if (!OidIsValid(oprcommon))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find common type between %s and %s",
+ format_type_be(oprleft), format_type_be(oprright))));
+
+ appendStringInfo(buf, " %s %s", sep, rightop);
+ if (oprleft != oprcommon)
+ ri_add_cast_to(buf, oprcommon);
+ appendStringInfo(buf, " OPERATOR(pg_catalog.");
+ appendStringInfo(buf, " @>");
+ appendStringInfo(buf, ") ARRAY[%s]", leftop);
+ if (oprright != oprcommon)
+ ri_add_cast_to(buf, oprcommon);
+ }
+ else
+ {
+ appendStringInfo(buf, " %s %s", sep, leftop);
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+ appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
+ appendStringInfoString(buf, oprname);
+ appendStringInfo(buf, ") %s", rightop);
+ if (rightoptype != operform->oprright)
+ ri_add_cast_to(buf, operform->oprright);
+ }
ReleaseSysCache(opertup);
}
@@ -2801,6 +2946,7 @@ ri_LoadConstraintInfo(Oid constraintOid)
bool isNull;
ArrayType *arr;
int numkeys;
+ int i;
/*
* On the first call initialize the hashtable
@@ -2879,6 +3025,20 @@ ri_LoadConstraintInfo(Oid constraintOid)
pfree(arr); /* free de-toasted copy, if any */
adatum = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype, &isNull);
+ if (isNull)
+ elog(ERROR, "null confreftype for constraint %u", constraintOid);
+ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
+ if (ARR_NDIM(arr) != 1 ||
+ ARR_DIMS(arr)[0] != numkeys ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != CHAROID)
+ elog(ERROR, "confreftype is not a 1-D char array");
+ memcpy(riinfo->fk_reftypes, ARR_DATA_PTR(arr), numkeys * sizeof(char));
+ if ((Pointer) arr != DatumGetPointer(adatum))
+ pfree(arr); /* free de-toasted copy, if any */
+
+ adatum = SysCacheGetAttr(CONSTROID, tup,
Anum_pg_constraint_conpfeqop, &isNull);
if (isNull)
elog(ERROR, "null conpfeqop for constraint %u", constraintOid);
@@ -2921,6 +3081,24 @@ ri_LoadConstraintInfo(Oid constraintOid)
if ((Pointer) arr != DatumGetPointer(adatum))
pfree(arr); /* free de-toasted copy, if any */
+ /*
+ * Fix up some stuff for Foreign Key Arrays. We need a has_array flag
+ * indicating whether there's an array foreign key, and we want to set
+ * ff_eq_oprs[i] to array_eq() for array columns, because that's what
+ * makes sense for ri_KeysEqual, and we have no other use for ff_eq_oprs
+ * in this module. (If we did, substituting the array comparator at the
+ * call point in ri_KeysEqual might be more appropriate.)
+ */
+ riinfo->has_array = false;
+ for (i = 0; i < numkeys; i++)
+ {
+ if (riinfo->fk_reftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ riinfo->has_array = true;
+ riinfo->ff_eq_oprs[i] = ARRAY_EQ_OP;
+ }
+ }
+
ReleaseSysCache(tup);
/*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index b543b7046c..919f1eded5 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -314,6 +314,9 @@ static char *pg_get_viewdef_worker(Oid viewoid,
static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
static void decompile_column_index_array(Datum column_index_array, Oid relId,
StringInfo buf);
+static void decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf);
static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
const Oid *excludeOps,
@@ -1899,7 +1902,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
{
case CONSTRAINT_FOREIGN:
{
- Datum val;
+ Datum colindexes;
+ Datum reftypes;
bool isnull;
const char *string;
@@ -1907,13 +1911,21 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
appendStringInfoString(&buf, "FOREIGN KEY (");
/* Fetch and build referencing-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_conkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_conkey,
+ &isnull);
if (isnull)
elog(ERROR, "null conkey for constraint %u",
constraintId);
+ reftypes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype,
+ &isnull);
+ if (isnull)
+ elog(ERROR, "null confreftype for constraint %u",
+ constraintId);
- decompile_column_index_array(val, conForm->conrelid, &buf);
+ decompile_fk_column_index_array(colindexes, reftypes,
+ conForm->conrelid, &buf);
/* add foreign relation name */
appendStringInfo(&buf, ") REFERENCES %s(",
@@ -1921,13 +1933,15 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
NIL));
/* Fetch and build referenced-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_confkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confkey,
+ &isnull);
if (isnull)
elog(ERROR, "null confkey for constraint %u",
constraintId);
- decompile_column_index_array(val, conForm->confrelid, &buf);
+ decompile_column_index_array(colindexes,
+ conForm->confrelid, &buf);
appendStringInfoChar(&buf, ')');
@@ -2202,6 +2216,66 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
}
}
+ /*
+ * Convert an int16[] Datum and a char[] Datum into a comma-separated
+ * list of column names for the indicated relation, prefixed by appropriate
+ * keywords depending on the foreign key reference semantics indicated by
+ * the char[] entries. Append the text to buf.
+ */
+ static void
+ decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf)
+ {
+ Datum *keys;
+ int nKeys;
+ Datum *reftypes;
+ int nReftypes;
+ int j;
+
+ /* Extract data from array of int16 */
+ deconstruct_array(DatumGetArrayTypeP(column_index_array),
+ INT2OID, sizeof(int16), true, 's',
+ &keys, NULL, &nKeys);
+
+ /* Extract data from array of char */
+ deconstruct_array(DatumGetArrayTypeP(fk_reftype_array),
+ CHAROID, sizeof(char), true, 'c',
+ &reftypes, NULL, &nReftypes);
+
+ if (nKeys != nReftypes)
+ elog(ERROR, "wrong confreftype cardinality");
+
+ for (j = 0; j < nKeys; j++)
+ {
+ char *colName;
+ const char *prefix;
+
+ colName = get_relid_attribute_name(relId, DatumGetInt16(keys[j]));
+
+ switch (DatumGetChar(reftypes[j]))
+ {
+ case FKCONSTR_REF_PLAIN:
+ prefix = "";
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ prefix = "EACH ELEMENT OF ";
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d",
+ (int) DatumGetChar(reftypes[j]));
+ prefix = NULL; /* keep compiler quiet */
+ break;
+ }
+
+ if (j == 0)
+ appendStringInfo(buf, "%s%s", prefix,
+ quote_identifier(colName));
+ else
+ appendStringInfo(buf, ", %s%s", prefix,
+ quote_identifier(colName));
+ }
+}
/* ----------
* get_expr - Decompile an expression tree
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index ec035d8434..903f9e19cb 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -104,8 +104,16 @@ CATALOG(pg_constraint,2606)
int16 confkey[1];
/*
+ * If a foreign key, the reference semantics for each column
+ */
+ char confreftype[1];
+
+ /*
* If a foreign key, the OIDs of the PK = FK equality operators for each
* column of the constraint
+ *
+ * Note: for Foreign Key Arrays, all these operators are for the array's
+ * element type.
*/
Oid conpfeqop[1];
@@ -150,7 +158,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* compiler constants for pg_constraint
* ----------------
*/
-#define Natts_pg_constraint 24
+#define Natts_pg_constraint 25
#define Anum_pg_constraint_conname 1
#define Anum_pg_constraint_connamespace 2
#define Anum_pg_constraint_contype 3
@@ -169,12 +177,13 @@ typedef FormData_pg_constraint *Form_pg_constraint;
#define Anum_pg_constraint_connoinherit 16
#define Anum_pg_constraint_conkey 17
#define Anum_pg_constraint_confkey 18
-#define Anum_pg_constraint_conpfeqop 19
-#define Anum_pg_constraint_conppeqop 20
-#define Anum_pg_constraint_conffeqop 21
-#define Anum_pg_constraint_conexclop 22
-#define Anum_pg_constraint_conbin 23
-#define Anum_pg_constraint_consrc 24
+#define Anum_pg_constraint_confreftype 19
+#define Anum_pg_constraint_conpfeqop 20
+#define Anum_pg_constraint_conppeqop 21
+#define Anum_pg_constraint_conffeqop 22
+#define Anum_pg_constraint_conexclop 23
+#define Anum_pg_constraint_conbin 24
+#define Anum_pg_constraint_consrc 25
/* ----------------
* initial contents of pg_constraint
@@ -195,7 +204,9 @@ typedef FormData_pg_constraint *Form_pg_constraint;
/*
* Valid values for confupdtype and confdeltype are the FKCONSTR_ACTION_xxx
* constants defined in parsenodes.h. Valid values for confmatchtype are
- * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h.
+ * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h. Valid values
+ * for elements of confreftype[] are the FKCONSTR_REF_xxx constants defined
+ * in parsenodes.h.
*/
#endif /* PG_CONSTRAINT_H */
diff --git a/src/include/catalog/pg_constraint_fn.h b/src/include/catalog/pg_constraint_fn.h
index 37b0b4ba82..cd1ce3c94e 100644
--- a/src/include/catalog/pg_constraint_fn.h
+++ b/src/include/catalog/pg_constraint_fn.h
@@ -40,6 +40,7 @@ extern Oid CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 34d6afc80f..09eea88b77 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2082,6 +2082,10 @@ typedef enum ConstrType /* types of constraints */
#define FKCONSTR_MATCH_PARTIAL 'p'
#define FKCONSTR_MATCH_SIMPLE 's'
+ /* Foreign key column reference semantics codes */
+ #define FKCONSTR_REF_PLAIN 'p'
+ #define FKCONSTR_REF_EACH_ELEMENT 'e'
+
typedef struct Constraint
{
NodeTag type;
@@ -2117,6 +2121,7 @@ typedef struct Constraint
RangeVar *pktable; /* Primary key table */
List *fk_attrs; /* Attributes of foreign key */
List *pk_attrs; /* Corresponding attrs in PK table */
+ List *fk_reftypes; /* Per-column reference semantics (int List) */
char fk_matchtype; /* FULL, PARTIAL, SIMPLE */
char fk_upd_action; /* ON UPDATE action */
char fk_del_action; /* ON DELETE action */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f50e45e886..d3f4803006 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -140,6 +140,7 @@ PG_KEYWORD("domain", DOMAIN_P, UNRESERVED_KEYWORD)
PG_KEYWORD("double", DOUBLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD)
PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD)
+PG_KEYWORD("element", ELEMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("else", ELSE, RESERVED_KEYWORD)
PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index e560f0c96e..c4a2e118ed 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -67,6 +67,7 @@ extern int parser_coercion_errposition(ParseState *pstate,
extern Oid select_common_type(ParseState *pstate, List *exprs,
const char *context, Node **which_expr);
+extern Oid select_common_type_2args(Oid ptype, Oid ntype);
extern Node *coerce_to_common_type(ParseState *pstate, Node *node,
Oid targetTypeId,
const char *context);
diff --git a/src/test/regress/expected/element_foreign_key.out b/src/test/regress/expected/element_foreign_key.out
new file mode 100644
index 0000000000..ed991e2fae
--- /dev/null
+++ b/src/test/regress/expected/element_foreign_key.out
@@ -0,0 +1,639 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fkarray"
+DETAIL: Key (ftest1)=({10,1}) is not present in table "pktableforarray".
+DROP TABLE FKTABLEFORARRAY;
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4,6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL,4,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+ERROR: insert or update on table "fktableforarraymdim" violates foreign key constraint "fktableforarraymdim_ftest1_fkey"
+DETAIL: Key (ftest1)=({{1,2},{6,NULL}}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+ERROR: null value in column "ftest1" violates not-null constraint
+DETAIL: Failing row contains (null, 21).
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+ {1} | 4
+(11 rows)
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({D}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({A,B,D}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(A) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(B) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- -- Repeat a similar test using INT2 keys coerced from INT4
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ERROR: foreign key constraint "fktableforarray_ftest1_fkey" cannot be implemented
+DETAIL: Key column "ftest1" has element type integer which does not have a default btree operator class that's compatible with class "float8_ops".
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(A, {A,B,C}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(B, {A}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Test Foreign Key Arrays with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,99)"}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,1)","(2010,1)"}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2010,99)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2011,1)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({0,1}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({2,1}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(2, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(20, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+ERROR: foreign keys support only one array column
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: foreign keys support only one array column
+DROP TABLE F1;
+-- Cleanup
+DROP TABLE DIM1;
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "x2_x1_fkey"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "fk_const"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+DROP TABLE x1;
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{100,100,100},{NULL,NULL,20},{7,8,10}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
+-- Check GIN index
+-- Define PKTABLEFORARRAYGIN
+CREATE TABLE PKTABLEFORARRAYGIN ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAYGIN
+INSERT INTO PKTABLEFORARRAYGIN VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (5, 'Test5');
+-- Define FKTABLEFORARRAYGIN
+CREATE TABLE FKTABLEFORARRAYGIN ( ftest1 int[],
+ ftest2 int PRIMARY KEY,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYGIN
+ ON DELETE NO ACTION ON UPDATE NO ACTION);
+-- -- Create index on FKTABLEFORARRAYGIN
+CREATE INDEX ON FKTABLEFORARRAYGIN USING gin (ftest1 array_ops);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,5,3}', 10);
+-- Try using the indexable operator
+SELECT * FROM FKTABLEFORARRAYGIN WHERE ftest1 @> ARRAY[5];
+ ftest1 | ftest2
+-------------+--------
+ {5} | 1
+ {3,5,2,5} | 3
+ {3,5,4,1,3} | 5
+ {5,1} | 7
+ {3,4,5,3} | 10
+(5 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYGIN;
+DROP TABLE PKTABLEFORARRAYGIN;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index aa5e6af621..552f8d4e8a 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -104,7 +104,12 @@ test: publication subscription
# ----------
# Another group of parallel tests
# ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
+test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
+
+# ----------
+# Another group of parallel tests (no room in the previouse group)
+# ----------
+test: element_foreign_key
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 3866314a92..eb7710d518 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -142,6 +142,7 @@ test: amutils
test: select_views
test: portals_p2
test: foreign_key
+test: element_foreign_key
test: cluster
test: dependency
test: guc
diff --git a/src/test/regress/sql/element_foreign_key.sql b/src/test/regress/sql/element_foreign_key.sql
new file mode 100644
index 0000000000..d012589430
--- /dev/null
+++ b/src/test/regress/sql/element_foreign_key.sql
@@ -0,0 +1,503 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- -- Repeat a similar test using INT2 keys coerced from INT4
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Test Foreign Key Arrays with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+
+
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+
+
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+-- Cleanup
+DROP TABLE DIM1;
+
+
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+DROP TABLE x2;
+DROP TABLE x1;
+
+
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+DROP TABLE F1;
+
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+DROP TABLE F1;
+
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
+
+-- Check GIN index
+-- Define PKTABLEFORARRAYGIN
+CREATE TABLE PKTABLEFORARRAYGIN ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAYGIN
+INSERT INTO PKTABLEFORARRAYGIN VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (5, 'Test5');
+
+-- Define FKTABLEFORARRAYGIN
+CREATE TABLE FKTABLEFORARRAYGIN ( ftest1 int[],
+ ftest2 int PRIMARY KEY,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYGIN
+ ON DELETE NO ACTION ON UPDATE NO ACTION);
+
+-- -- Create index on FKTABLEFORARRAYGIN
+CREATE INDEX ON FKTABLEFORARRAYGIN USING gin (ftest1 array_ops);
+
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,5,3}', 10);
+
+-- Try using the indexable operator
+SELECT * FROM FKTABLEFORARRAYGIN WHERE ftest1 @> ARRAY[5];
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYGIN;
+DROP TABLE PKTABLEFORARRAYGIN;
\ No newline at end of file
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2017-11-27 01:47 ` Andreas Karlsson <andreas@proxel.se>
2017-11-29 02:02 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Michael Paquier <michael.paquier@gmail.com>
1 sibling, 1 reply; 200+ messages in thread
From: Andreas Karlsson @ 2017-11-27 01:47 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Stephen Frost <sfrost@snowman.net>; David Steele <david@pgmasters.net>; Alvaro Herrera <alvherre@2ndquadrant.com>; Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; pgsql-hackers-owner@postgresql.org, Tom Lane <tgl@sss.pgh.pa.us>; Hans-Jürgen Schönig <hs@cybertec.at>
On 11/13/2017 12:32 PM, Mark Rofail wrote:
> == The @>> operator
> I would argue that allocating an array of datums and building an
> array would have the same complexity
>
>
> I am not sure what you mean here. Just because something has the
> same complexity does not mean there can't be major performance
> differences.
>
> I have spend a lot of time working on this operator and would like to
> benefit from it. How should I go about this ? Start a new patch ?
I am still not sure what you mean here. Feel free to add @>> to this
patch if you like. You may want to submit it as two patch files (but
please keep them as the same commitfest entry) but I leave that decision
all up to you.
> So the two main issues we remain to resolve are MATCH FULL and the
> RI_Initial_Check() query refactoring. The problem is that I am not one
> of the original authors and have not touched this part of the code.
> I understand the problem but it will take some time for me to understand
> how to resolve everything.
Cool, feel free to ask if you need some assistance. I want this patch.
Andreas
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-27 01:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
@ 2017-11-29 02:02 ` Michael Paquier <michael.paquier@gmail.com>
0 siblings, 0 replies; 200+ messages in thread
From: Michael Paquier @ 2017-11-29 02:02 UTC (permalink / raw)
To: Andreas Karlsson <andreas@proxel.se>; +Cc: Mark Rofail <markm.rofail@gmail.com>; Alexander Korotkov <aekorotkov@gmail.com>; Stephen Frost <sfrost@snowman.net>; David Steele <david@pgmasters.net>; Alvaro Herrera <alvherre@2ndquadrant.com>; Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; pgsql-hackers-owner@postgresql.org, Tom Lane <tgl@sss.pgh.pa.us>; Hans-Jürgen Schönig <hs@cybertec.at>
On Mon, Nov 27, 2017 at 10:47 AM, Andreas Karlsson <andreas@proxel.se> wrote:
> Cool, feel free to ask if you need some assistance. I want this patch.
The last patch submitted did not get a review, and it does not apply
as well. So I am moving it to next CF with waiting on author as
status. Please rebase the patch.
--
Michael
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2018-01-12 19:36 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
1 sibling, 1 reply; 200+ messages in thread
From: Alvaro Herrera @ 2018-01-12 19:36 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Andreas Karlsson <andreas@proxel.se>; Alexander Korotkov <aekorotkov@gmail.com>; Stephen Frost <sfrost@snowman.net>; David Steele <david@pgmasters.net>; Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; Tom Lane <tgl@sss.pgh.pa.us>; Hans-Jürgen Schönig <hs@cybertec.at>
Here's a rebased version of this patch. I have done nothing other than
fix the conflicts and verify that it passes existing regression tests.
I intend to go over the reviews sometime later and hopefully get it all
fixed for inclusion in pg11.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
From d513b449722b6e848b958584d0c81ca19b289d22 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Fri, 12 Jan 2018 16:28:51 -0300
Subject: [PATCH v6] Support foreign keys for array elements
---
doc/src/sgml/catalogs.sgml | 16 +
doc/src/sgml/ddl.sgml | 110 ++++
doc/src/sgml/ref/create_table.sgml | 116 +++-
src/backend/catalog/heap.c | 1 +
src/backend/catalog/index.c | 1 +
src/backend/catalog/pg_constraint.c | 14 +-
src/backend/commands/tablecmds.c | 138 ++++-
src/backend/commands/trigger.c | 6 +
src/backend/commands/typecmds.c | 1 +
src/backend/nodes/copyfuncs.c | 1 +
src/backend/nodes/equalfuncs.c | 1 +
src/backend/nodes/outfuncs.c | 1 +
src/backend/parser/gram.y | 66 ++-
src/backend/parser/parse_coerce.c | 84 +++
src/backend/parser/parse_utilcmd.c | 2 +
src/backend/utils/adt/ri_triggers.c | 245 +++++++--
src/backend/utils/adt/ruleutils.c | 88 ++-
src/include/catalog/pg_constraint.h | 27 +-
src/include/catalog/pg_constraint_fn.h | 1 +
src/include/nodes/parsenodes.h | 5 +
src/include/parser/kwlist.h | 1 +
src/include/parser/parse_coerce.h | 1 +
src/test/regress/expected/element_foreign_key.out | 639 ++++++++++++++++++++++
src/test/regress/parallel_schedule | 7 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/element_foreign_key.sql | 503 +++++++++++++++++
26 files changed, 2000 insertions(+), 76 deletions(-)
create mode 100644 src/test/regress/expected/element_foreign_key.out
create mode 100644 src/test/regress/sql/element_foreign_key.sql
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 3f02202caf..aa5f9fe21f 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2342,6 +2342,16 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</row>
<row>
+ <entry><structfield>confreftype</structfield></entry>
+ <entry><type>char[]</type></entry>
+ <entry></entry>
+ <entry>If a foreign key, the reference semantics for each column:
+ <literal>p</literal> = plain (simple equality),
+ <literal>e</literal> = each element of referencing array must have a match
+ </entry>
+ </row>
+
+ <row>
<entry><structfield>conpfeqop</structfield></entry>
<entry><type>oid[]</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</literal></entry>
@@ -2387,6 +2397,12 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</table>
<para>
+ When <structfield>confreftype</structfield> indicates array to scalar
+ foreign key reference semantics, the equality operators listed in
+ <structfield>conpfeqop</structfield> etc are for the array's element type.
+ </para>
+
+ <para>
In the case of an exclusion constraint, <structfield>conkey</structfield>
is only useful for constraint elements that are simple column references.
For other cases, a zero appears in <structfield>conkey</structfield>
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index b1167a40e6..5ca685b991 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -882,6 +882,116 @@ CREATE TABLE order_items (
</para>
</sect2>
+ <sect2 id="ddl-constraints-element-fk">
+ <title>Foreign Key Arrays</title>
+
+ <indexterm>
+ <primary>Foreign Key Arrays</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>ELEMENT foreign key</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>Array ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>referential integrity</primary>
+ </indexterm>
+
+ <para>
+ Another option you have with foreign keys is to use a referencing column
+ which is an array of elements with the same type (or a compatible one) as
+ the referenced column in the related table. This feature is called
+ <firstterm>Foreign Key Arrays</firstterm> and is implemented in PostgreSQL
+ with <firstterm>EACH ELEMENT OF foreign key constraints</firstterm>, as
+ described in the following example:
+
+ <programlisting>
+ CREATE TABLE drivers (
+ driver_id integer PRIMARY KEY,
+ first_name text,
+ last_name text
+ );
+
+ CREATE TABLE races (
+ race_id integer PRIMARY KEY,
+ title text,
+ race_day date,
+ final_positions integer[],
+ FOREIGN KEY <emphasis> (EACH ELEMENT OF final_positions) REFERENCES drivers </emphasis>
+ );
+ </programlisting>
+
+ The above example uses an array (<literal>final_positions</literal>) to
+ store the results of a race: for each of its elements a referential
+ integrity check is enforced on the <literal>drivers</literal> table. Note
+ that <literal>(EACH ELEMENT OF ...) REFERENCES</literal> is an extension of
+ PostgreSQL and it is not included in the SQL standard.
+ </para>
+
+ <para>
+ We currently only support the table constraint form.
+ </para>
+
+ <para>
+ Even though the most common use case for Foreign Key Array constraint is on
+ a single column key, you can define a Foreign Key Array constraint on a
+ group of columns.
+
+ <programlisting>
+ CREATE TABLE available_moves (
+ kind text,
+ move text,
+ description text,
+ PRIMARY KEY (kind, move)
+ );
+
+ CREATE TABLE paths (
+ description text,
+ kind text,
+ moves text[],
+ <emphasis>FOREIGN KEY (kind, EACH ELEMENT OF moves) REFERENCES available_moves (kind, move)</emphasis>
+ );
+
+ INSERT INTO available_moves VALUES ('relative', 'LN', 'look north');
+ INSERT INTO available_moves VALUES ('relative', 'RL', 'rotate left');
+ INSERT INTO available_moves VALUES ('relative', 'RR', 'rotate right');
+ INSERT INTO available_moves VALUES ('relative', 'MF', 'move forward');
+ INSERT INTO available_moves VALUES ('absolute', 'N', 'move north');
+ INSERT INTO available_moves VALUES ('absolute', 'S', 'move south');
+ INSERT INTO available_moves VALUES ('absolute', 'E', 'move east');
+ INSERT INTO available_moves VALUES ('absolute', 'W', 'move west');
+
+ INSERT INTO paths VALUES ('L-shaped path', 'relative', '{LN, RL, MF, RR, MF, MF}');
+ INSERT INTO paths VALUES ('L-shaped path', 'absolute', '{W, N, N}');
+ </programlisting>
+
+ On top of standard foreign key requirements, array
+ <literal>ELEMENT</literal> foreign key constraints require that the
+ referencing column is an array of a compatible type of the corresponding
+ referenced column.
+
+ However, it is very important to note that, currently, we only support
+ one array reference per foreign key.
+ </para>
+
+ <para>
+ For more detailed information on Foreign Key Array options and special
+ cases, please refer to the documentation for
+ <xref linkend="sql-createtable-foreign-key" /> and
+ <xref linkend="sql-createtable-element-foreign-key-constraints" />.
+ </para>
+ </sect2>
+
<sect2 id="ddl-constraints-exclusion">
<title>Exclusion Constraints</title>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index a0c9a6d257..39b0012b1a 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -65,7 +65,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ] |
UNIQUE <replaceable class="parameter">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="parameter">index_parameters</replaceable> |
- REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
+ [ EACH ELEMENT OF ] REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -867,10 +867,10 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
- <varlistentry>
+ <varlistentry id="sql-createtable-foreign-key" xreflabel="FOREIGN KEY">
<term><literal>REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
- <term><literal>FOREIGN KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] )
+ <term><literal>FOREIGN KEY ( [EACH ELEMENT OF] <replaceable class="parameter">column_name</replaceable> [, ... ] )
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] ) ]
[ MATCH <replaceable class="parameter">matchtype</replaceable> ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ]
@@ -894,6 +894,19 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</para>
<para>
+ In case the column name
+ <replaceable class="parameter">column</replaceable> is prepended with the
+ <literal>EACH ELEMENT OF</literal> keyword and
+ <replaceable class="parameter">column</replaceable> is an array of elements compatible
+ with the corresponding <replaceable class="parameter">refcolumn</replaceable>
+ in <replaceable class="parameter">reftable</replaceable>, a Foreign Key Array constraint
+ is put in place (see
+ <xref linkend="sql-createtable-element-foreign-key-constraints" /> for more
+ information). Multi-column keys with more than one
+ <literal>EACH ELEMENT OF</literal> column are currently not allowed.
+ </para>
+
+ <para>
A value inserted into the referencing column(s) is matched against the
values of the referenced table and referenced columns using the
given match type. There are three match types: <literal>MATCH
@@ -956,7 +969,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
<para>
Delete any rows referencing the deleted row, or update the
values of the referencing column(s) to the new values of the
- referenced columns, respectively.
+ referenced columns, respectively. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -965,7 +979,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
<term><literal>SET NULL</literal></term>
<listitem>
<para>
- Set the referencing column(s) to null.
+ Set the referencing column(s) to null. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -977,6 +992,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
Set the referencing column(s) to their default values.
(There must be a row in the referenced table matching the default
values, if they are not null, or the operation will fail.)
+ Currently not supported with array <literal>ELEMENT</literal>
+ foreign keys.
</para>
</listitem>
</varlistentry>
@@ -992,6 +1009,85 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
+ <varlistentry id="sql-createtable-element-foreign-key-constraints" xreflabel="FOREIGN KEY ARRAYS">
+ <term><literal>EACH ELEMENT OF <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
+
+ <listitem>
+ <para>
+ The <literal>EACH ELEMENT OF REFERENCES</literal> definition specifies a
+ <quote>Foreign Key Array</quote>, a special
+ kind of foreign key constraint requiring the referencing column to be an
+ array of elements of the same type (or a compatible one) as the
+ referenced column in the referenced table. The value of each element of
+ the <replaceable class="parameter">refcolumn</replaceable> array will be
+ matched against some row of <replaceable
+ class="parameter">reftable</replaceable>.
+ </para>
+
+ <para>
+ Foreign Key Arrays are an extension
+ of PostgreSQL and are not included in the SQL standard.
+ </para>
+
+ <para>
+ Even with Foreign Key Arrays, modifications in the referenced column can
+ trigger actions to be performed on the referencing array. Similarly to
+ standard foreign keys, you can specify these actions using the
+ <literal>ON DELETE</literal> and <literal>ON UPDATE</literal> clauses.
+ However, only the following actions for each clause are currently
+ allowed:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>ON UPDATE/DELETE NO ACTION</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints. This is the default action.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ON UPDATE/DELETE RESTRICT</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ON DELETE CASCADE</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints. Deletes the entire array.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ It is advisable to index the refrencing column using GIN index as it
+ considerably enhances the performance. Also concerning coercion while
+ using the GIN index:
+
+ <programlisting>
+ CREATE TABLE pktableforarray ( ptest1 int2 PRIMARY KEY, ptest2 text );
+ CREATE TABLE fktableforarray ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ </programlisting>
+ This syntax is fine since it will cast ptest1 to int4 upon RI checks,
+
+ <programlisting>
+ CREATE TABLE pktableforarray ( ptest1 int4 PRIMARY KEY, ptest2 text );
+ CREATE TABLE fktableforarray ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ </programlisting>
+ however, this syntax will cast ftest1 to int4 upon RI checks, thus defeating the
+ purpose of the index.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>DEFERRABLE</literal></term>
<term><literal>NOT DEFERRABLE</literal></term>
@@ -1982,6 +2078,16 @@ CREATE TABLE cities_partdef
</refsect2>
<refsect2>
+ <title id="sql-createtable-foreign-key-arrays">Array <literal>ELEMENT</literal> Foreign Keys</title>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys and the <literal>ELEMENT
+ REFERENCES</literal> clause are a <productname>PostgreSQL</productname>
+ extension.
+ </para>
+ </refsect2>
+
+ <refsect2>
<title><literal>PARTITION BY</literal> Clause</title>
<para>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 089b7965f2..21836ab680 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2126,6 +2126,7 @@ StoreRelCheck(Relation rel, const char *ccname, Node *expr,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 330488b96f..33b40ffcb1 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1215,6 +1215,7 @@ index_constraint_create(Relation heapRelation,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 442ae7e23d..37b7a3e275 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -59,6 +59,7 @@ CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
@@ -82,6 +83,7 @@ CreateConstraintEntry(const char *constraintName,
Datum values[Natts_pg_constraint];
ArrayType *conkeyArray;
ArrayType *confkeyArray;
+ ArrayType *confreftypeArray;
ArrayType *conpfeqopArray;
ArrayType *conppeqopArray;
ArrayType *conffeqopArray;
@@ -119,7 +121,11 @@ CreateConstraintEntry(const char *constraintName,
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = Int16GetDatum(foreignKey[i]);
confkeyArray = construct_array(fkdatums, foreignNKeys,
- INT2OID, 2, true, 's');
+ INT2OID, sizeof(int16), true, 's');
+ for (i = 0; i < foreignNKeys; i++)
+ fkdatums[i] = CharGetDatum(foreignRefType[i]);
+ confreftypeArray = construct_array(fkdatums, foreignNKeys,
+ CHAROID, sizeof(char), true, 'c');
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
conpfeqopArray = construct_array(fkdatums, foreignNKeys,
@@ -136,6 +142,7 @@ CreateConstraintEntry(const char *constraintName,
else
{
confkeyArray = NULL;
+ confreftypeArray = NULL;
conpfeqopArray = NULL;
conppeqopArray = NULL;
conffeqopArray = NULL;
@@ -188,6 +195,11 @@ CreateConstraintEntry(const char *constraintName,
else
nulls[Anum_pg_constraint_confkey - 1] = true;
+ if (confreftypeArray)
+ values[Anum_pg_constraint_confreftype - 1] = PointerGetDatum(confreftypeArray);
+ else
+ nulls[Anum_pg_constraint_confreftype - 1] = true;
+
if (conpfeqopArray)
values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
else
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index f2a928b823..58f53495e1 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7058,6 +7058,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Relation pkrel;
int16 pkattnum[INDEX_MAX_KEYS];
int16 fkattnum[INDEX_MAX_KEYS];
+ char fkreftypes[INDEX_MAX_KEYS];
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
Oid opclasses[INDEX_MAX_KEYS];
@@ -7065,10 +7066,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid ppeqoperators[INDEX_MAX_KEYS];
Oid ffeqoperators[INDEX_MAX_KEYS];
int i;
+ ListCell *lc;
int numfks,
numpks;
Oid indexOid;
Oid constrOid;
+ bool has_array;
bool old_check_ok;
ObjectAddress address;
ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
@@ -7145,6 +7148,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
*/
MemSet(pkattnum, 0, sizeof(pkattnum));
MemSet(fkattnum, 0, sizeof(fkattnum));
+ MemSet(fkreftypes, 0, sizeof(fkreftypes));
MemSet(pktypoid, 0, sizeof(pktypoid));
MemSet(fktypoid, 0, sizeof(fktypoid));
MemSet(opclasses, 0, sizeof(opclasses));
@@ -7157,6 +7161,50 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
fkattnum, fktypoid);
/*
+ * Validate the reference semantics codes, too, and convert list to array
+ * format to pass to CreateConstraintEntry.
+ */
+ Assert(list_length(fkconstraint->fk_reftypes) == numfks);
+ has_array = false;
+ i = 0;
+ foreach(lc, fkconstraint->fk_reftypes)
+ {
+ char reftype = lfirst_int(lc);
+
+ switch (reftype)
+ {
+ case FKCONSTR_REF_PLAIN:
+ /* OK, nothing to do */
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ /* At most one FK column can be an array reference */
+ if (has_array)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign keys support only one array column")));
+ has_array = true;
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d", (int) reftype);
+ break;
+ }
+ fkreftypes[i] = reftype;
+ i++;
+ }
+
+ /* Array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions */
+ if (has_array)
+ {
+ if ((fkconstraint->fk_upd_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_upd_action != FKCONSTR_ACTION_RESTRICT) ||
+ (fkconstraint->fk_del_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("Foreign Key Arrays support only NO ACTION and RESTRICT actions")));
+ }
+
+ /*
* If the attribute list for the referenced table was omitted, lookup the
* definition of the primary key and use it. Otherwise, validate the
* supplied attribute list. In either case, discover the index OID and
@@ -7242,6 +7290,65 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
eqstrategy = BTEqualStrategyNumber;
/*
+ * If this is an array foreign key, we must look up the operators for
+ * the array element type, not the array type itself.
+ */
+ if (fkreftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ Oid elemopclass;
+
+ /* We check if the array element type exists and is of valid Oid */
+ fktype = get_base_element_type(fktype);
+ if (!OidIsValid(fktype))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has type %s which is not an array type.",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktypoid[i]))));
+
+ /*
+ * For the moment, we must also insist that the array's element
+ * type have a default btree opclass that is in the index's
+ * opfamily. This is necessary because ri_triggers.c relies on
+ * COUNT(DISTINCT x) on the element type, as well as on array_eq()
+ * on the array type, and we need those operations to have the
+ * same notion of equality that we're using otherwise.
+ *
+ * XXX this restriction is pretty annoying, considering the effort
+ * that's been put into the rest of the RI mechanisms to make them
+ * work with nondefault equality operators. In particular, it
+ * means that the cast-to-PK-datatype code path isn't useful for
+ * array-to-scalar references.
+ */
+ elemopclass = GetDefaultOpClass(fktype, BTREE_AM_OID);
+ if (!OidIsValid(elemopclass) ||
+ get_opclass_family(elemopclass) != opfamily)
+ {
+ /* Get the index opclass's name for the error message. */
+ char *opcname;
+
+ cla_ht = SearchSysCache1(CLAOID,
+ ObjectIdGetDatum(opclasses[i]));
+ if (!HeapTupleIsValid(cla_ht))
+ elog(ERROR, "cache lookup failed for opclass %u",
+ opclasses[i]);
+ cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
+ opcname = pstrdup(NameStr(cla_tup->opcname));
+ ReleaseSysCache(cla_ht);
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has element type %s which does not have a default btree operator class that's compatible with class \"%s\".",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktype),
+ opcname)));
+ }
+ }
+
+ /*
* There had better be a primary equality operator for the index.
* We'll use it for PK = PK comparisons.
*/
@@ -7300,17 +7407,17 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
}
if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("foreign key constraint \"%s\" "
- "cannot be implemented",
- fkconstraint->conname),
- errdetail("Key columns \"%s\" and \"%s\" "
- "are of incompatible types: %s and %s.",
- strVal(list_nth(fkconstraint->fk_attrs, i)),
- strVal(list_nth(fkconstraint->pk_attrs, i)),
- format_type_be(fktype),
- format_type_be(pktype))));
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("foreign key constraint \"%s\" "
+ "cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key columns \"%s\" and \"%s\" "
+ "are of incompatible types: %s and %s.",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ strVal(list_nth(fkconstraint->pk_attrs, i)),
+ format_type_be(fktype),
+ format_type_be(pktype))));
if (old_check_ok)
{
@@ -7340,6 +7447,13 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
* We may assume that pg_constraint.conkey is not changing.
*/
old_fktype = attr->atttypid;
+ if (fkreftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ old_fktype = get_base_element_type(old_fktype);
+ /* this shouldn't happen ... */
+ if (!OidIsValid(old_fktype))
+ elog(ERROR, "old foreign key column is not an array");
+ }
new_fktype = fktype;
old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
&old_castfunc);
@@ -7382,7 +7496,6 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
new_castfunc == old_castfunc &&
(!IsPolymorphicType(pfeqop_right) ||
new_fktype == old_fktype));
-
}
pfeqoperators[i] = pfeqop;
@@ -7406,6 +7519,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
indexOid,
RelationGetRelid(pkrel),
pkattnum,
+ fkreftypes,
pfeqoperators,
ppeqoperators,
ffeqoperators,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 1c488c338a..510d6c91b0 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -661,6 +661,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
@@ -1032,6 +1033,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
char fk_matchtype = FKCONSTR_MATCH_SIMPLE;
List *fk_attrs = NIL;
List *pk_attrs = NIL;
+ List *fk_reftypes = NIL;
StringInfoData buf;
int funcnum;
OldTriggerInfo *info = NULL;
@@ -1061,7 +1063,10 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
if (i % 2)
fk_attrs = lappend(fk_attrs, arg);
else
+ {
pk_attrs = lappend(pk_attrs, arg);
+ fk_reftypes = lappend_int(fk_reftypes, FKCONSTR_REF_PLAIN);
+ }
}
/* Prepare description of constraint for use in messages */
@@ -1200,6 +1205,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
fkcon->conname = constr_name;
fkcon->fk_attrs = fk_attrs;
fkcon->pk_attrs = pk_attrs;
+ fkcon->fk_reftypes = fk_reftypes;
fkcon->fk_matchtype = fk_matchtype;
switch (info->funcoids[0])
{
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index a40b3cf752..f10757b122 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -3164,6 +3164,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index ddbbc79823..51e971d555 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2850,6 +2850,7 @@ _copyConstraint(const Constraint *from)
COPY_NODE_FIELD(pktable);
COPY_NODE_FIELD(fk_attrs);
COPY_NODE_FIELD(pk_attrs);
+ COPY_NODE_FIELD(fk_reftypes);
COPY_SCALAR_FIELD(fk_matchtype);
COPY_SCALAR_FIELD(fk_upd_action);
COPY_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 30ccc9c5ae..18d14b59f8 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2594,6 +2594,7 @@ _equalConstraint(const Constraint *a, const Constraint *b)
COMPARE_NODE_FIELD(pktable);
COMPARE_NODE_FIELD(fk_attrs);
COMPARE_NODE_FIELD(pk_attrs);
+ COMPARE_NODE_FIELD(fk_reftypes);
COMPARE_SCALAR_FIELD(fk_matchtype);
COMPARE_SCALAR_FIELD(fk_upd_action);
COMPARE_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 5e72df137e..3a4d9535d6 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3501,6 +3501,7 @@ _outConstraint(StringInfo str, const Constraint *node)
WRITE_NODE_FIELD(pktable);
WRITE_NODE_FIELD(fk_attrs);
WRITE_NODE_FIELD(pk_attrs);
+ WRITE_NODE_FIELD(fk_reftypes);
WRITE_CHAR_FIELD(fk_matchtype);
WRITE_CHAR_FIELD(fk_upd_action);
WRITE_CHAR_FIELD(fk_del_action);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index e42b7caff6..5ac121d91d 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -126,6 +126,13 @@ typedef struct ImportQual
List *table_names;
} ImportQual;
+/* Private struct for the result of foreign_key_column_elem production */
+typedef struct FKColElem
+{
+ Node *name; /* name of the column (a String) */
+ char reftype; /* FKCONSTR_REF_xxx code */
+} FKColElem;
+
/* ConstraintAttributeSpec yields an integer bitmask of these flags: */
#define CAS_NOT_DEFERRABLE 0x01
#define CAS_DEFERRABLE 0x02
@@ -183,6 +190,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
static void SplitColQualList(List *qualList,
List **constraintList, CollateClause **collClause,
core_yyscan_t yyscanner);
+static void SplitFKColElems(List *fkcolelems, List **names, List **reftypes);
static void processCASbits(int cas_bits, int location, const char *constrType,
bool *deferrable, bool *initdeferred, bool *not_valid,
bool *no_inherit, core_yyscan_t yyscanner);
@@ -233,6 +241,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
A_Indices *aind;
ResTarget *target;
struct PrivTarget *privtarget;
+ struct FKColElem *fkcolelem;
AccessPriv *accesspriv;
struct ImportQual *importqual;
InsertStmt *istmt;
@@ -358,6 +367,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <accesspriv> privilege
%type <list> privileges privilege_list
%type <privtarget> privilege_target
+%type <fkcolelem> foreign_key_column_elem
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
%type <ival> defacl_privilege_target
@@ -393,7 +403,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
execute_param_clause using_clause returning_clause
opt_enum_val_list enum_val_list table_func_column_list
create_generic_options alter_generic_options
- relation_expr_list dostmt_opt_list
+ relation_expr_list dostmt_opt_list foreign_key_column_list
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
@@ -625,8 +635,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
DOUBLE_P DROP
- EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
- EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
+ EACH ELEMENT ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE
+ EVENT EXCEPT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
EXTENSION EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FILTER FIRST_P FLOAT_P FOLLOWING FOR
@@ -3527,8 +3537,10 @@ ColConstraintElem:
n->contype = CONSTR_FOREIGN;
n->location = @1;
n->pktable = $2;
+ /* fk_attrs will be filled in by parse analysis */
n->fk_attrs = NIL;
n->pk_attrs = $3;
+ n->fk_reftypes = list_make1_int(FKCONSTR_REF_PLAIN);
n->fk_matchtype = $4;
n->fk_upd_action = (char) ($5 >> 8);
n->fk_del_action = (char) ($5 & 0xFF);
@@ -3722,14 +3734,15 @@ ConstraintElem:
NULL, yyscanner);
$$ = (Node *)n;
}
- | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
- opt_column_list key_match key_actions ConstraintAttributeSpec
+ | FOREIGN KEY '(' foreign_key_column_list ')' REFERENCES
+ qualified_name opt_column_list key_match key_actions
+ ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_FOREIGN;
n->location = @1;
+ SplitFKColElems($4, &n->fk_attrs, &n->fk_reftypes);
n->pktable = $7;
- n->fk_attrs = $4;
n->pk_attrs = $8;
n->fk_matchtype = $9;
n->fk_upd_action = (char) ($10 >> 8);
@@ -3762,6 +3775,29 @@ columnElem: ColId
$$ = (Node *) makeString($1);
}
;
+ foreign_key_column_list:
+ foreign_key_column_elem
+ { $$ = list_make1($1); }
+ | foreign_key_column_list ',' foreign_key_column_elem
+ { $$ = lappend($1, $3); }
+ ;
+
+ foreign_key_column_elem:
+ ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($1);
+ n->reftype = FKCONSTR_REF_PLAIN;
+ $$ = n;
+ }
+ | EACH ELEMENT OF ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($4);
+ n->reftype = FKCONSTR_REF_EACH_ELEMENT;
+ $$ = n;
+ }
+ ;
key_match: MATCH FULL
{
@@ -14976,6 +15012,7 @@ unreserved_keyword:
| DOUBLE_P
| DROP
| EACH
+ | ELEMENT
| ENABLE_P
| ENCODING
| ENCRYPTED
@@ -16096,6 +16133,23 @@ SplitColQualList(List *qualList,
*constraintList = qualList;
}
+/* Split a list of FKColElem structs into separate name and reftype lists */
+static void
+SplitFKColElems(List *fkcolelems, List **names, List **reftypes)
+{
+ ListCell *lc;
+
+ *names = NIL;
+ *reftypes = NIL;
+ foreach(lc, fkcolelems)
+ {
+ FKColElem *fkcolelem = (FKColElem *) lfirst(lc);
+
+ *names = lappend(*names, fkcolelem->name);
+ *reftypes = lappend_int(*reftypes, fkcolelem->reftype);
+ }
+}
+
/*
* Process result of ConstraintAttributeSpec, and set appropriate bool flags
* in the output command node. Pass NULL for any flags the particular
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 085aa8766c..7fa070a3c1 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1386,6 +1386,90 @@ select_common_type(ParseState *pstate, List *exprs, const char *context,
}
/*
+ * look at select_common_type(); in parse_coerce.c(src/backend/parser/parse_coerce.c)
+ *
+ * select_common_type_2args()
+ * Determine the common supertype of a list of input expressions.
+ * 'leftOid': left operand's type
+ * 'rightOid': left operand's type
+ */
+Oid select_common_type_2args(Oid leftOid, Oid rightOid)
+{
+ TYPCATEGORY leftcategory;
+ TYPCATEGORY rightcategory;
+ bool leftispreferred;
+ bool rightispreferred;
+
+ Assert(leftOid != InvalidOid);
+ Assert(rightOid != InvalidOid);
+
+ /*
+ * If input types are valid and exactly the same, just pick that type.
+ */
+ if (leftOid != UNKNOWNOID)
+ {
+ if (rightOid == leftOid)
+ return leftOid;
+ }
+
+ /*
+ * Nope, so set up for the full algorithm.
+ */
+ leftOid = getBaseType(leftOid);
+ rightOid = getBaseType(rightOid);
+ get_type_category_preferred(leftOid, &leftcategory, &leftispreferred);
+ get_type_category_preferred(rightOid, &rightcategory, &rightispreferred);
+
+ if (rightOid != UNKNOWNOID && rightOid != leftOid)
+ {
+ if (rightcategory != leftcategory)
+ {
+ /* both types in different categories? then not much hope... */
+ return InvalidOid;
+ }
+
+ if (can_coerce_type(1, &rightOid, &leftOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left */
+ if (!can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can not coerce to it implicitly right to left, thus coercion only works one way */
+ return leftOid;
+ }
+ else
+ {
+ /* coercion works both ways, then decide depending on preferred flag */
+ if (leftispreferred)
+ return leftOid;
+ else
+ return rightOid;
+ }
+ }
+ else{
+ /* can not coerce to it implicitly right to left */
+ if (can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left, thus coercion only works one way */
+ return rightOid;
+ }
+ else{
+ /* can not coerce either way */
+ return InvalidOid;
+ }
+ }
+ }
+
+ /*
+ * If all the inputs were UNKNOWN type --- ie, unknown-type literals ---
+ * then resolve as type TEXT.
+ */
+ if (leftOid == UNKNOWNOID)
+ leftOid = TEXTOID;
+
+ return leftOid;
+}
+
+/*
* coerce_to_common_type()
* Coerce an expression to the given type.
*
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 128f1679c6..4f41827d4d 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -757,6 +757,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
* list of FK constraints to be processed later.
*/
constraint->fk_attrs = list_make1(makeString(column->colname));
+ /* grammar should have set fk_reftypes */
+ Assert(list_length(constraint->fk_reftypes) == 1);
cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
break;
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 8faae1d069..d577df3512 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -115,9 +115,11 @@ typedef struct RI_ConstraintInfo
char confupdtype; /* foreign key's ON UPDATE action */
char confdeltype; /* foreign key's ON DELETE action */
char confmatchtype; /* foreign key's match type */
+ bool has_array; /* true if any reftype is EACH_ELEMENT */
int nkeys; /* number of key columns */
int16 pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */
int16 fk_attnums[RI_MAX_NUMKEYS]; /* attnums of referencing cols */
+ char fk_reftypes[RI_MAX_NUMKEYS]; /* reference semantics */
Oid pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = FK) */
Oid pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = PK) */
Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK = FK) */
@@ -202,7 +204,8 @@ static void ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype);
+ const char *rightop, Oid rightoptype,
+ char fkreftype);
static void ri_add_cast_to(StringInfo buf, Oid typid);
static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
static int ri_NullCheck(HeapTuple tup,
@@ -393,6 +396,7 @@ RI_FKey_check(TriggerData *trigdata)
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
StringInfoData querybuf;
+ StringInfoData countbuf;
char pkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
char paramname[16];
@@ -405,12 +409,27 @@ RI_FKey_check(TriggerData *trigdata)
* FOR KEY SHARE OF x
* The type id's for the $ parameters are those of the
* corresponding FK attributes.
+ *
+ * In case of an array ELEMENT foreign key, the previous query is used
+ * to count the number of matching rows and see if every combination
+ * is actually referenced.
+ * The wrapping query is
+ * SELECT 1 WHERE
+ * (SELECT count(DISTINCT y) FROM unnest($1) y)
+ * = (SELECT count(*) FROM (<QUERY>) z)
* ----------
*/
initStringInfo(&querybuf);
quoteRelationName(pkrelname, pk_rel);
appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
querysep = "WHERE";
+
+ if (riinfo->has_array)
+ {
+ initStringInfo(&countbuf);
+ appendStringInfo(&countbuf, "SELECT 1 WHERE ");
+ }
+
for (i = 0; i < riinfo->nkeys; i++)
{
Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
@@ -419,18 +438,41 @@ RI_FKey_check(TriggerData *trigdata)
quoteOneName(attname,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
sprintf(paramname, "$%d", i + 1);
+
+ /*
+ * In case of an array ELEMENT foreign key, we check that each
+ * distinct non-null value in the array is present in the PK
+ * table.
+ */
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&countbuf,
+ "(SELECT pg_catalog.count(DISTINCT y) FROM pg_catalog.unnest(%s) y)",
+ paramname);
+
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pf_eq_oprs[i],
- paramname, fk_type);
+ paramname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = fk_type;
}
appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
- /* Prepare and save the plan */
- qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
- &qkey, fk_rel, pk_rel, true);
+ if (riinfo->has_array)
+ {
+ appendStringInfo(&countbuf,
+ " OPERATOR(pg_catalog.=) (SELECT pg_catalog.count(*) FROM (%s) z)",
+ querybuf.data);
+
+ /* Prepare and save the plan for Foreign Key Arrays */
+ qplan = ri_PlanCheck(countbuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
+ }
+ else
+ /* Prepare and save the plan */
+ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
}
/*
@@ -557,7 +599,8 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pp_eq_oprs[i],
- paramname, pk_type);
+ paramname, pk_type,
+ FKCONSTR_REF_PLAIN);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -815,7 +858,8 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -971,7 +1015,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1150,7 +1195,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1368,7 +1414,8 @@ ri_setnull(TriggerData *trigdata)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1585,7 +1632,8 @@ ri_setdefault(TriggerData *trigdata)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1904,6 +1952,14 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
* For MATCH FULL:
* (fk.keycol1 IS NOT NULL [OR ...])
*
+ * In case of an array ELEMENT column, relname is replaced with the
+ * following subquery:
+ *
+ * SELECT unnest("keycol1") k1, "keycol1" ak1 [, ...]
+ * FROM ONLY "public"."fk"
+ *
+ * where all the columns are renamed in order to prevent name collisions.
+ *
* We attach COLLATE clauses to the operators when comparing columns
* that have different collations.
*----------
@@ -1915,15 +1971,46 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
{
quoteOneName(fkattname,
RIAttName(fk_rel, riinfo->fk_attnums[i]));
- appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
+ if (riinfo->has_array)
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%sfk.ak%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.k%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
sep = ", ";
}
quoteRelationName(pkrelname, pk_rel);
quoteRelationName(fkrelname, fk_rel);
- appendStringInfo(&querybuf,
- " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
- fkrelname, pkrelname);
+
+ if (riinfo->has_array)
+ {
+ sep = "";
+ appendStringInfo(&querybuf,
+ " FROM (SELECT ");
+ for (i = 0; i < riinfo->nkeys; i++)
+ {
+ quoteOneName(fkattname,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%spg_catalog.unnest(%s) k%d, %s ak%d",
+ sep, fkattname, i + 1, fkattname, i + 1);
+ else
+ appendStringInfo(&querybuf, "%s%s k%d", sep, fkattname,
+ i + 1);
+ sep = ", ";
+ }
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s) fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
+ }
+ else
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
strcpy(pkattname, "pk.");
strcpy(fkattname, "fk.");
@@ -1937,12 +2024,16 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
quoteOneName(pkattname + 3,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
- quoteOneName(fkattname + 3,
- RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname + 3, "k%d", i + 1);
+ else
+ quoteOneName(fkattname + 3,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
ri_GenerateQual(&querybuf, sep,
pkattname, pk_type,
riinfo->pf_eq_oprs[i],
- fkattname, fk_type);
+ fkattname, fk_type,
+ FKCONSTR_REF_PLAIN);
if (pk_coll != fk_coll)
ri_GenerateQualCollation(&querybuf, pk_coll);
sep = "AND";
@@ -1958,7 +2049,10 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
sep = "";
for (i = 0; i < riinfo->nkeys; i++)
{
- quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname, "k%d", i + 1);
+ else
+ quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
appendStringInfo(&querybuf,
"%sfk.%s IS NOT NULL",
sep, fkattname);
@@ -2134,20 +2228,23 @@ quoteRelationName(char *buffer, Relation rel)
/*
* ri_GenerateQual --- generate a WHERE clause equating two variables
*
- * The idea is to append " sep leftop op rightop" to buf. The complexity
- * comes from needing to be sure that the parser will select the desired
- * operator. We always name the operator using OPERATOR(schema.op) syntax
- * (readability isn't a big priority here), so as to avoid search-path
- * uncertainties. We have to emit casts too, if either input isn't already
- * the input type of the operator; else we are at the mercy of the parser's
- * heuristics for ambiguous-operator resolution.
- */
+ * The idea is to append " sep leftop op rightop" to buf, or if fkreftype is
+ * FKCONSTR_REF_EACH_ELEMENT, append " sep rightop @> leftop" to buf.
+ *
+ * The complexity comes from needing to be sure that the parser will select
+ * the desired operator. We always name the operator using
+ * OPERATOR(schema.op) syntax (readability isn't a big priority here), so as
+ * to avoid search-path uncertainties. We have to emit casts too, if either
+ * input isn't already the input type of the operator; else we are at the
+ * mercy of the parser's heuristics for ambiguous-operator resolution.
+ */
static void
ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype)
+ const char *rightop, Oid rightoptype,
+ char fkreftype)
{
HeapTuple opertup;
Form_pg_operator operform;
@@ -2163,14 +2260,57 @@ ri_GenerateQual(StringInfo buf,
nspname = get_namespace_name(operform->oprnamespace);
- appendStringInfo(buf, " %s %s", sep, leftop);
- if (leftoptype != operform->oprleft)
- ri_add_cast_to(buf, operform->oprleft);
- appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
- appendStringInfoString(buf, oprname);
- appendStringInfo(buf, ") %s", rightop);
- if (rightoptype != operform->oprright)
- ri_add_cast_to(buf, operform->oprright);
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ Oid oprright;
+ Oid oprleft;
+ Oid oprcommon;
+ /*
+ * we first need to get the array types and decide
+ * the more appropriate one
+ */
+
+ /* get array type of refrenced element*/
+ oprright = get_array_type(operform->oprleft);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprleft))));
+ /* get array type of refrencing element*/
+ oprleft = get_array_type(operform->oprright);
+ if (!OidIsValid(oprleft))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprright))));
+ /* compare the two array types and try to find
+ a common supertype */
+ oprcommon = select_common_type_2args(oprleft, oprright);
+ if (!OidIsValid(oprcommon))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find common type between %s and %s",
+ format_type_be(oprleft), format_type_be(oprright))));
+
+ appendStringInfo(buf, " %s %s", sep, rightop);
+ if (oprleft != oprcommon)
+ ri_add_cast_to(buf, oprcommon);
+ appendStringInfo(buf, " OPERATOR(pg_catalog.@>) ARRAY[%s]", leftop);
+ if (oprright != oprcommon)
+ ri_add_cast_to(buf, oprcommon);
+ }
+ else
+ {
+ appendStringInfo(buf, " %s %s", sep, leftop);
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+ appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
+ appendStringInfoString(buf, oprname);
+ appendStringInfo(buf, ") %s", rightop);
+ if (rightoptype != operform->oprright)
+ ri_add_cast_to(buf, operform->oprright);
+ }
ReleaseSysCache(opertup);
}
@@ -2378,6 +2518,7 @@ ri_LoadConstraintInfo(Oid constraintOid)
bool isNull;
ArrayType *arr;
int numkeys;
+ int i;
/*
* On the first call initialize the hashtable
@@ -2456,6 +2597,20 @@ ri_LoadConstraintInfo(Oid constraintOid)
pfree(arr); /* free de-toasted copy, if any */
adatum = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype, &isNull);
+ if (isNull)
+ elog(ERROR, "null confreftype for constraint %u", constraintOid);
+ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
+ if (ARR_NDIM(arr) != 1 ||
+ ARR_DIMS(arr)[0] != numkeys ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != CHAROID)
+ elog(ERROR, "confreftype is not a 1-D char array");
+ memcpy(riinfo->fk_reftypes, ARR_DATA_PTR(arr), numkeys * sizeof(char));
+ if ((Pointer) arr != DatumGetPointer(adatum))
+ pfree(arr); /* free de-toasted copy, if any */
+
+ adatum = SysCacheGetAttr(CONSTROID, tup,
Anum_pg_constraint_conpfeqop, &isNull);
if (isNull)
elog(ERROR, "null conpfeqop for constraint %u", constraintOid);
@@ -2498,6 +2653,24 @@ ri_LoadConstraintInfo(Oid constraintOid)
if ((Pointer) arr != DatumGetPointer(adatum))
pfree(arr); /* free de-toasted copy, if any */
+ /*
+ * Fix up some stuff for Foreign Key Arrays. We need a has_array flag
+ * indicating whether there's an array foreign key, and we want to set
+ * ff_eq_oprs[i] to array_eq() for array columns, because that's what
+ * makes sense for ri_KeysEqual, and we have no other use for ff_eq_oprs
+ * in this module. (If we did, substituting the array comparator at the
+ * call point in ri_KeysEqual might be more appropriate.)
+ */
+ riinfo->has_array = false;
+ for (i = 0; i < numkeys; i++)
+ {
+ if (riinfo->fk_reftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ riinfo->has_array = true;
+ riinfo->ff_eq_oprs[i] = ARRAY_EQ_OP;
+ }
+ }
+
ReleaseSysCache(tup);
/*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 9cdbb06add..229d5d8c93 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -314,6 +314,9 @@ static char *pg_get_viewdef_worker(Oid viewoid,
static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
static void decompile_column_index_array(Datum column_index_array, Oid relId,
StringInfo buf);
+static void decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf);
static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
const Oid *excludeOps,
@@ -1899,7 +1902,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
{
case CONSTRAINT_FOREIGN:
{
- Datum val;
+ Datum colindexes;
+ Datum reftypes;
bool isnull;
const char *string;
@@ -1907,13 +1911,21 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
appendStringInfoString(&buf, "FOREIGN KEY (");
/* Fetch and build referencing-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_conkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_conkey,
+ &isnull);
if (isnull)
elog(ERROR, "null conkey for constraint %u",
constraintId);
+ reftypes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype,
+ &isnull);
+ if (isnull)
+ elog(ERROR, "null confreftype for constraint %u",
+ constraintId);
- decompile_column_index_array(val, conForm->conrelid, &buf);
+ decompile_fk_column_index_array(colindexes, reftypes,
+ conForm->conrelid, &buf);
/* add foreign relation name */
appendStringInfo(&buf, ") REFERENCES %s(",
@@ -1921,13 +1933,15 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
NIL));
/* Fetch and build referenced-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_confkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confkey,
+ &isnull);
if (isnull)
elog(ERROR, "null confkey for constraint %u",
constraintId);
- decompile_column_index_array(val, conForm->confrelid, &buf);
+ decompile_column_index_array(colindexes,
+ conForm->confrelid, &buf);
appendStringInfoChar(&buf, ')');
@@ -2202,6 +2216,66 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
}
}
+ /*
+ * Convert an int16[] Datum and a char[] Datum into a comma-separated
+ * list of column names for the indicated relation, prefixed by appropriate
+ * keywords depending on the foreign key reference semantics indicated by
+ * the char[] entries. Append the text to buf.
+ */
+ static void
+ decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf)
+ {
+ Datum *keys;
+ int nKeys;
+ Datum *reftypes;
+ int nReftypes;
+ int j;
+
+ /* Extract data from array of int16 */
+ deconstruct_array(DatumGetArrayTypeP(column_index_array),
+ INT2OID, sizeof(int16), true, 's',
+ &keys, NULL, &nKeys);
+
+ /* Extract data from array of char */
+ deconstruct_array(DatumGetArrayTypeP(fk_reftype_array),
+ CHAROID, sizeof(char), true, 'c',
+ &reftypes, NULL, &nReftypes);
+
+ if (nKeys != nReftypes)
+ elog(ERROR, "wrong confreftype cardinality");
+
+ for (j = 0; j < nKeys; j++)
+ {
+ char *colName;
+ const char *prefix;
+
+ colName = get_relid_attribute_name(relId, DatumGetInt16(keys[j]));
+
+ switch (DatumGetChar(reftypes[j]))
+ {
+ case FKCONSTR_REF_PLAIN:
+ prefix = "";
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ prefix = "EACH ELEMENT OF ";
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d",
+ (int) DatumGetChar(reftypes[j]));
+ prefix = NULL; /* keep compiler quiet */
+ break;
+ }
+
+ if (j == 0)
+ appendStringInfo(buf, "%s%s", prefix,
+ quote_identifier(colName));
+ else
+ appendStringInfo(buf, ", %s%s", prefix,
+ quote_identifier(colName));
+ }
+}
/* ----------
* get_expr - Decompile an expression tree
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index 8fca86d71e..96eea9753d 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -104,8 +104,16 @@ CATALOG(pg_constraint,2606)
int16 confkey[1];
/*
+ * If a foreign key, the reference semantics for each column
+ */
+ char confreftype[1];
+
+ /*
* If a foreign key, the OIDs of the PK = FK equality operators for each
* column of the constraint
+ *
+ * Note: for Foreign Key Arrays, all these operators are for the array's
+ * element type.
*/
Oid conpfeqop[1];
@@ -150,7 +158,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* compiler constants for pg_constraint
* ----------------
*/
-#define Natts_pg_constraint 24
+#define Natts_pg_constraint 25
#define Anum_pg_constraint_conname 1
#define Anum_pg_constraint_connamespace 2
#define Anum_pg_constraint_contype 3
@@ -169,12 +177,13 @@ typedef FormData_pg_constraint *Form_pg_constraint;
#define Anum_pg_constraint_connoinherit 16
#define Anum_pg_constraint_conkey 17
#define Anum_pg_constraint_confkey 18
-#define Anum_pg_constraint_conpfeqop 19
-#define Anum_pg_constraint_conppeqop 20
-#define Anum_pg_constraint_conffeqop 21
-#define Anum_pg_constraint_conexclop 22
-#define Anum_pg_constraint_conbin 23
-#define Anum_pg_constraint_consrc 24
+#define Anum_pg_constraint_confreftype 19
+#define Anum_pg_constraint_conpfeqop 20
+#define Anum_pg_constraint_conppeqop 21
+#define Anum_pg_constraint_conffeqop 22
+#define Anum_pg_constraint_conexclop 23
+#define Anum_pg_constraint_conbin 24
+#define Anum_pg_constraint_consrc 25
/* ----------------
* initial contents of pg_constraint
@@ -195,7 +204,9 @@ typedef FormData_pg_constraint *Form_pg_constraint;
/*
* Valid values for confupdtype and confdeltype are the FKCONSTR_ACTION_xxx
* constants defined in parsenodes.h. Valid values for confmatchtype are
- * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h.
+ * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h. Valid values
+ * for elements of confreftype[] are the FKCONSTR_REF_xxx constants defined
+ * in parsenodes.h.
*/
#endif /* PG_CONSTRAINT_H */
diff --git a/src/include/catalog/pg_constraint_fn.h b/src/include/catalog/pg_constraint_fn.h
index 6bb1b09714..e720745546 100644
--- a/src/include/catalog/pg_constraint_fn.h
+++ b/src/include/catalog/pg_constraint_fn.h
@@ -40,6 +40,7 @@ extern Oid CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b72178efd1..2b84d8a960 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2086,6 +2086,10 @@ typedef enum ConstrType /* types of constraints */
#define FKCONSTR_MATCH_PARTIAL 'p'
#define FKCONSTR_MATCH_SIMPLE 's'
+ /* Foreign key column reference semantics codes */
+ #define FKCONSTR_REF_PLAIN 'p'
+ #define FKCONSTR_REF_EACH_ELEMENT 'e'
+
typedef struct Constraint
{
NodeTag type;
@@ -2121,6 +2125,7 @@ typedef struct Constraint
RangeVar *pktable; /* Primary key table */
List *fk_attrs; /* Attributes of foreign key */
List *pk_attrs; /* Corresponding attrs in PK table */
+ List *fk_reftypes; /* Per-column reference semantics (int List) */
char fk_matchtype; /* FULL, PARTIAL, SIMPLE */
char fk_upd_action; /* ON UPDATE action */
char fk_del_action; /* ON DELETE action */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 26af944e03..af00143580 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -141,6 +141,7 @@ PG_KEYWORD("domain", DOMAIN_P, UNRESERVED_KEYWORD)
PG_KEYWORD("double", DOUBLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD)
PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD)
+PG_KEYWORD("element", ELEMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("else", ELSE, RESERVED_KEYWORD)
PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index af12c136ef..2955b57ecc 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -67,6 +67,7 @@ extern int parser_coercion_errposition(ParseState *pstate,
extern Oid select_common_type(ParseState *pstate, List *exprs,
const char *context, Node **which_expr);
+extern Oid select_common_type_2args(Oid ptype, Oid ntype);
extern Node *coerce_to_common_type(ParseState *pstate, Node *node,
Oid targetTypeId,
const char *context);
diff --git a/src/test/regress/expected/element_foreign_key.out b/src/test/regress/expected/element_foreign_key.out
new file mode 100644
index 0000000000..ed991e2fae
--- /dev/null
+++ b/src/test/regress/expected/element_foreign_key.out
@@ -0,0 +1,639 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fkarray"
+DETAIL: Key (ftest1)=({10,1}) is not present in table "pktableforarray".
+DROP TABLE FKTABLEFORARRAY;
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4,6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL,4,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+ERROR: insert or update on table "fktableforarraymdim" violates foreign key constraint "fktableforarraymdim_ftest1_fkey"
+DETAIL: Key (ftest1)=({{1,2},{6,NULL}}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+ERROR: null value in column "ftest1" violates not-null constraint
+DETAIL: Failing row contains (null, 21).
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+ {1} | 4
+(11 rows)
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({D}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({A,B,D}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(A) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(B) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- -- Repeat a similar test using INT2 keys coerced from INT4
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ERROR: foreign key constraint "fktableforarray_ftest1_fkey" cannot be implemented
+DETAIL: Key column "ftest1" has element type integer which does not have a default btree operator class that's compatible with class "float8_ops".
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(A, {A,B,C}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(B, {A}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Test Foreign Key Arrays with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,99)"}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,1)","(2010,1)"}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2010,99)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2011,1)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({0,1}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({2,1}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(2, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(20, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+ERROR: foreign keys support only one array column
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: foreign keys support only one array column
+DROP TABLE F1;
+-- Cleanup
+DROP TABLE DIM1;
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "x2_x1_fkey"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "fk_const"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+DROP TABLE x1;
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{100,100,100},{NULL,NULL,20},{7,8,10}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
+-- Check GIN index
+-- Define PKTABLEFORARRAYGIN
+CREATE TABLE PKTABLEFORARRAYGIN ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAYGIN
+INSERT INTO PKTABLEFORARRAYGIN VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (5, 'Test5');
+-- Define FKTABLEFORARRAYGIN
+CREATE TABLE FKTABLEFORARRAYGIN ( ftest1 int[],
+ ftest2 int PRIMARY KEY,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYGIN
+ ON DELETE NO ACTION ON UPDATE NO ACTION);
+-- -- Create index on FKTABLEFORARRAYGIN
+CREATE INDEX ON FKTABLEFORARRAYGIN USING gin (ftest1 array_ops);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,5,3}', 10);
+-- Try using the indexable operator
+SELECT * FROM FKTABLEFORARRAYGIN WHERE ftest1 @> ARRAY[5];
+ ftest1 | ftest2
+-------------+--------
+ {5} | 1
+ {3,5,2,5} | 3
+ {3,5,4,1,3} | 5
+ {5,1} | 7
+ {3,4,5,3} | 10
+(5 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYGIN;
+DROP TABLE PKTABLEFORARRAYGIN;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index e224977791..52505e7539 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -104,7 +104,12 @@ test: publication subscription
# ----------
# Another group of parallel tests
# ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
+test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
+
+# ----------
+# Another group of parallel tests (no room in the previouse group)
+# ----------
+test: element_foreign_key
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 9fc5f1a268..eefd1df73b 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -143,6 +143,7 @@ test: amutils
test: select_views
test: portals_p2
test: foreign_key
+test: element_foreign_key
test: cluster
test: dependency
test: guc
diff --git a/src/test/regress/sql/element_foreign_key.sql b/src/test/regress/sql/element_foreign_key.sql
new file mode 100644
index 0000000000..d012589430
--- /dev/null
+++ b/src/test/regress/sql/element_foreign_key.sql
@@ -0,0 +1,503 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- -- Repeat a similar test using INT2 keys coerced from INT4
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Test Foreign Key Arrays with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+
+
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+
+
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+-- Cleanup
+DROP TABLE DIM1;
+
+
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+DROP TABLE x2;
+DROP TABLE x1;
+
+
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+DROP TABLE F1;
+
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+DROP TABLE F1;
+
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
+
+-- Check GIN index
+-- Define PKTABLEFORARRAYGIN
+CREATE TABLE PKTABLEFORARRAYGIN ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAYGIN
+INSERT INTO PKTABLEFORARRAYGIN VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (5, 'Test5');
+
+-- Define FKTABLEFORARRAYGIN
+CREATE TABLE FKTABLEFORARRAYGIN ( ftest1 int[],
+ ftest2 int PRIMARY KEY,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYGIN
+ ON DELETE NO ACTION ON UPDATE NO ACTION);
+
+-- -- Create index on FKTABLEFORARRAYGIN
+CREATE INDEX ON FKTABLEFORARRAYGIN USING gin (ftest1 array_ops);
+
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,5,3}', 10);
+
+-- Try using the indexable operator
+SELECT * FROM FKTABLEFORARRAYGIN WHERE ftest1 @> ARRAY[5];
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYGIN;
+DROP TABLE PKTABLEFORARRAYGIN;
\ No newline at end of file
--
2.11.0
Attachments:
v6-0001-Support-foreign-keys-for-array-elements.patch (text/plain, 120.7K) download
From d513b449722b6e848b958584d0c81ca19b289d22 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Fri, 12 Jan 2018 16:28:51 -0300
Subject: [PATCH v6] Support foreign keys for array elements
---
doc/src/sgml/catalogs.sgml | 16 +
doc/src/sgml/ddl.sgml | 110 ++++
doc/src/sgml/ref/create_table.sgml | 116 +++-
src/backend/catalog/heap.c | 1 +
src/backend/catalog/index.c | 1 +
src/backend/catalog/pg_constraint.c | 14 +-
src/backend/commands/tablecmds.c | 138 ++++-
src/backend/commands/trigger.c | 6 +
src/backend/commands/typecmds.c | 1 +
src/backend/nodes/copyfuncs.c | 1 +
src/backend/nodes/equalfuncs.c | 1 +
src/backend/nodes/outfuncs.c | 1 +
src/backend/parser/gram.y | 66 ++-
src/backend/parser/parse_coerce.c | 84 +++
src/backend/parser/parse_utilcmd.c | 2 +
src/backend/utils/adt/ri_triggers.c | 245 +++++++--
src/backend/utils/adt/ruleutils.c | 88 ++-
src/include/catalog/pg_constraint.h | 27 +-
src/include/catalog/pg_constraint_fn.h | 1 +
src/include/nodes/parsenodes.h | 5 +
src/include/parser/kwlist.h | 1 +
src/include/parser/parse_coerce.h | 1 +
src/test/regress/expected/element_foreign_key.out | 639 ++++++++++++++++++++++
src/test/regress/parallel_schedule | 7 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/element_foreign_key.sql | 503 +++++++++++++++++
26 files changed, 2000 insertions(+), 76 deletions(-)
create mode 100644 src/test/regress/expected/element_foreign_key.out
create mode 100644 src/test/regress/sql/element_foreign_key.sql
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 3f02202caf..aa5f9fe21f 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2342,6 +2342,16 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</row>
<row>
+ <entry><structfield>confreftype</structfield></entry>
+ <entry><type>char[]</type></entry>
+ <entry></entry>
+ <entry>If a foreign key, the reference semantics for each column:
+ <literal>p</literal> = plain (simple equality),
+ <literal>e</literal> = each element of referencing array must have a match
+ </entry>
+ </row>
+
+ <row>
<entry><structfield>conpfeqop</structfield></entry>
<entry><type>oid[]</type></entry>
<entry><literal><link linkend="catalog-pg-operator"><structname>pg_operator</structname></link>.oid</literal></entry>
@@ -2387,6 +2397,12 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</table>
<para>
+ When <structfield>confreftype</structfield> indicates array to scalar
+ foreign key reference semantics, the equality operators listed in
+ <structfield>conpfeqop</structfield> etc are for the array's element type.
+ </para>
+
+ <para>
In the case of an exclusion constraint, <structfield>conkey</structfield>
is only useful for constraint elements that are simple column references.
For other cases, a zero appears in <structfield>conkey</structfield>
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index b1167a40e6..5ca685b991 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -882,6 +882,116 @@ CREATE TABLE order_items (
</para>
</sect2>
+ <sect2 id="ddl-constraints-element-fk">
+ <title>Foreign Key Arrays</title>
+
+ <indexterm>
+ <primary>Foreign Key Arrays</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>ELEMENT foreign key</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>Array ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>constraint</primary>
+ <secondary>ELEMENT foreign key</secondary>
+ </indexterm>
+
+ <indexterm>
+ <primary>referential integrity</primary>
+ </indexterm>
+
+ <para>
+ Another option you have with foreign keys is to use a referencing column
+ which is an array of elements with the same type (or a compatible one) as
+ the referenced column in the related table. This feature is called
+ <firstterm>Foreign Key Arrays</firstterm> and is implemented in PostgreSQL
+ with <firstterm>EACH ELEMENT OF foreign key constraints</firstterm>, as
+ described in the following example:
+
+ <programlisting>
+ CREATE TABLE drivers (
+ driver_id integer PRIMARY KEY,
+ first_name text,
+ last_name text
+ );
+
+ CREATE TABLE races (
+ race_id integer PRIMARY KEY,
+ title text,
+ race_day date,
+ final_positions integer[],
+ FOREIGN KEY <emphasis> (EACH ELEMENT OF final_positions) REFERENCES drivers </emphasis>
+ );
+ </programlisting>
+
+ The above example uses an array (<literal>final_positions</literal>) to
+ store the results of a race: for each of its elements a referential
+ integrity check is enforced on the <literal>drivers</literal> table. Note
+ that <literal>(EACH ELEMENT OF ...) REFERENCES</literal> is an extension of
+ PostgreSQL and it is not included in the SQL standard.
+ </para>
+
+ <para>
+ We currently only support the table constraint form.
+ </para>
+
+ <para>
+ Even though the most common use case for Foreign Key Array constraint is on
+ a single column key, you can define a Foreign Key Array constraint on a
+ group of columns.
+
+ <programlisting>
+ CREATE TABLE available_moves (
+ kind text,
+ move text,
+ description text,
+ PRIMARY KEY (kind, move)
+ );
+
+ CREATE TABLE paths (
+ description text,
+ kind text,
+ moves text[],
+ <emphasis>FOREIGN KEY (kind, EACH ELEMENT OF moves) REFERENCES available_moves (kind, move)</emphasis>
+ );
+
+ INSERT INTO available_moves VALUES ('relative', 'LN', 'look north');
+ INSERT INTO available_moves VALUES ('relative', 'RL', 'rotate left');
+ INSERT INTO available_moves VALUES ('relative', 'RR', 'rotate right');
+ INSERT INTO available_moves VALUES ('relative', 'MF', 'move forward');
+ INSERT INTO available_moves VALUES ('absolute', 'N', 'move north');
+ INSERT INTO available_moves VALUES ('absolute', 'S', 'move south');
+ INSERT INTO available_moves VALUES ('absolute', 'E', 'move east');
+ INSERT INTO available_moves VALUES ('absolute', 'W', 'move west');
+
+ INSERT INTO paths VALUES ('L-shaped path', 'relative', '{LN, RL, MF, RR, MF, MF}');
+ INSERT INTO paths VALUES ('L-shaped path', 'absolute', '{W, N, N}');
+ </programlisting>
+
+ On top of standard foreign key requirements, array
+ <literal>ELEMENT</literal> foreign key constraints require that the
+ referencing column is an array of a compatible type of the corresponding
+ referenced column.
+
+ However, it is very important to note that, currently, we only support
+ one array reference per foreign key.
+ </para>
+
+ <para>
+ For more detailed information on Foreign Key Array options and special
+ cases, please refer to the documentation for
+ <xref linkend="sql-createtable-foreign-key" /> and
+ <xref linkend="sql-createtable-element-foreign-key-constraints" />.
+ </para>
+ </sect2>
+
<sect2 id="ddl-constraints-exclusion">
<title>Exclusion Constraints</title>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index a0c9a6d257..39b0012b1a 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -65,7 +65,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( <replaceable>sequence_options</replaceable> ) ] |
UNIQUE <replaceable class="parameter">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="parameter">index_parameters</replaceable> |
- REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
+ [ EACH ELEMENT OF ] REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@@ -867,10 +867,10 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
- <varlistentry>
+ <varlistentry id="sql-createtable-foreign-key" xreflabel="FOREIGN KEY">
<term><literal>REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
- <term><literal>FOREIGN KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] )
+ <term><literal>FOREIGN KEY ( [EACH ELEMENT OF] <replaceable class="parameter">column_name</replaceable> [, ... ] )
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] ) ]
[ MATCH <replaceable class="parameter">matchtype</replaceable> ]
[ ON DELETE <replaceable class="parameter">action</replaceable> ]
@@ -894,6 +894,19 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</para>
<para>
+ In case the column name
+ <replaceable class="parameter">column</replaceable> is prepended with the
+ <literal>EACH ELEMENT OF</literal> keyword and
+ <replaceable class="parameter">column</replaceable> is an array of elements compatible
+ with the corresponding <replaceable class="parameter">refcolumn</replaceable>
+ in <replaceable class="parameter">reftable</replaceable>, a Foreign Key Array constraint
+ is put in place (see
+ <xref linkend="sql-createtable-element-foreign-key-constraints" /> for more
+ information). Multi-column keys with more than one
+ <literal>EACH ELEMENT OF</literal> column are currently not allowed.
+ </para>
+
+ <para>
A value inserted into the referencing column(s) is matched against the
values of the referenced table and referenced columns using the
given match type. There are three match types: <literal>MATCH
@@ -956,7 +969,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
<para>
Delete any rows referencing the deleted row, or update the
values of the referencing column(s) to the new values of the
- referenced columns, respectively.
+ referenced columns, respectively. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -965,7 +979,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
<term><literal>SET NULL</literal></term>
<listitem>
<para>
- Set the referencing column(s) to null.
+ Set the referencing column(s) to null. Currently not supported
+ with array <literal>ELEMENT</literal> foreign keys.
</para>
</listitem>
</varlistentry>
@@ -977,6 +992,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
Set the referencing column(s) to their default values.
(There must be a row in the referenced table matching the default
values, if they are not null, or the operation will fail.)
+ Currently not supported with array <literal>ELEMENT</literal>
+ foreign keys.
</para>
</listitem>
</varlistentry>
@@ -992,6 +1009,85 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
+ <varlistentry id="sql-createtable-element-foreign-key-constraints" xreflabel="FOREIGN KEY ARRAYS">
+ <term><literal>EACH ELEMENT OF <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ]</literal> (column constraint)</term>
+
+ <listitem>
+ <para>
+ The <literal>EACH ELEMENT OF REFERENCES</literal> definition specifies a
+ <quote>Foreign Key Array</quote>, a special
+ kind of foreign key constraint requiring the referencing column to be an
+ array of elements of the same type (or a compatible one) as the
+ referenced column in the referenced table. The value of each element of
+ the <replaceable class="parameter">refcolumn</replaceable> array will be
+ matched against some row of <replaceable
+ class="parameter">reftable</replaceable>.
+ </para>
+
+ <para>
+ Foreign Key Arrays are an extension
+ of PostgreSQL and are not included in the SQL standard.
+ </para>
+
+ <para>
+ Even with Foreign Key Arrays, modifications in the referenced column can
+ trigger actions to be performed on the referencing array. Similarly to
+ standard foreign keys, you can specify these actions using the
+ <literal>ON DELETE</literal> and <literal>ON UPDATE</literal> clauses.
+ However, only the following actions for each clause are currently
+ allowed:
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>ON UPDATE/DELETE NO ACTION</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints. This is the default action.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ON UPDATE/DELETE RESTRICT</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>ON DELETE CASCADE</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints. Deletes the entire array.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ It is advisable to index the refrencing column using GIN index as it
+ considerably enhances the performance. Also concerning coercion while
+ using the GIN index:
+
+ <programlisting>
+ CREATE TABLE pktableforarray ( ptest1 int2 PRIMARY KEY, ptest2 text );
+ CREATE TABLE fktableforarray ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ </programlisting>
+ This syntax is fine since it will cast ptest1 to int4 upon RI checks,
+
+ <programlisting>
+ CREATE TABLE pktableforarray ( ptest1 int4 PRIMARY KEY, ptest2 text );
+ CREATE TABLE fktableforarray ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ </programlisting>
+ however, this syntax will cast ftest1 to int4 upon RI checks, thus defeating the
+ purpose of the index.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>DEFERRABLE</literal></term>
<term><literal>NOT DEFERRABLE</literal></term>
@@ -1982,6 +2078,16 @@ CREATE TABLE cities_partdef
</refsect2>
<refsect2>
+ <title id="sql-createtable-foreign-key-arrays">Array <literal>ELEMENT</literal> Foreign Keys</title>
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys and the <literal>ELEMENT
+ REFERENCES</literal> clause are a <productname>PostgreSQL</productname>
+ extension.
+ </para>
+ </refsect2>
+
+ <refsect2>
<title><literal>PARTITION BY</literal> Clause</title>
<para>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 089b7965f2..21836ab680 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2126,6 +2126,7 @@ StoreRelCheck(Relation rel, const char *ccname, Node *expr,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 330488b96f..33b40ffcb1 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1215,6 +1215,7 @@ index_constraint_create(Relation heapRelation,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 442ae7e23d..37b7a3e275 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -59,6 +59,7 @@ CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
@@ -82,6 +83,7 @@ CreateConstraintEntry(const char *constraintName,
Datum values[Natts_pg_constraint];
ArrayType *conkeyArray;
ArrayType *confkeyArray;
+ ArrayType *confreftypeArray;
ArrayType *conpfeqopArray;
ArrayType *conppeqopArray;
ArrayType *conffeqopArray;
@@ -119,7 +121,11 @@ CreateConstraintEntry(const char *constraintName,
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = Int16GetDatum(foreignKey[i]);
confkeyArray = construct_array(fkdatums, foreignNKeys,
- INT2OID, 2, true, 's');
+ INT2OID, sizeof(int16), true, 's');
+ for (i = 0; i < foreignNKeys; i++)
+ fkdatums[i] = CharGetDatum(foreignRefType[i]);
+ confreftypeArray = construct_array(fkdatums, foreignNKeys,
+ CHAROID, sizeof(char), true, 'c');
for (i = 0; i < foreignNKeys; i++)
fkdatums[i] = ObjectIdGetDatum(pfEqOp[i]);
conpfeqopArray = construct_array(fkdatums, foreignNKeys,
@@ -136,6 +142,7 @@ CreateConstraintEntry(const char *constraintName,
else
{
confkeyArray = NULL;
+ confreftypeArray = NULL;
conpfeqopArray = NULL;
conppeqopArray = NULL;
conffeqopArray = NULL;
@@ -188,6 +195,11 @@ CreateConstraintEntry(const char *constraintName,
else
nulls[Anum_pg_constraint_confkey - 1] = true;
+ if (confreftypeArray)
+ values[Anum_pg_constraint_confreftype - 1] = PointerGetDatum(confreftypeArray);
+ else
+ nulls[Anum_pg_constraint_confreftype - 1] = true;
+
if (conpfeqopArray)
values[Anum_pg_constraint_conpfeqop - 1] = PointerGetDatum(conpfeqopArray);
else
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index f2a928b823..58f53495e1 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7058,6 +7058,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Relation pkrel;
int16 pkattnum[INDEX_MAX_KEYS];
int16 fkattnum[INDEX_MAX_KEYS];
+ char fkreftypes[INDEX_MAX_KEYS];
Oid pktypoid[INDEX_MAX_KEYS];
Oid fktypoid[INDEX_MAX_KEYS];
Oid opclasses[INDEX_MAX_KEYS];
@@ -7065,10 +7066,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Oid ppeqoperators[INDEX_MAX_KEYS];
Oid ffeqoperators[INDEX_MAX_KEYS];
int i;
+ ListCell *lc;
int numfks,
numpks;
Oid indexOid;
Oid constrOid;
+ bool has_array;
bool old_check_ok;
ObjectAddress address;
ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
@@ -7145,6 +7148,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
*/
MemSet(pkattnum, 0, sizeof(pkattnum));
MemSet(fkattnum, 0, sizeof(fkattnum));
+ MemSet(fkreftypes, 0, sizeof(fkreftypes));
MemSet(pktypoid, 0, sizeof(pktypoid));
MemSet(fktypoid, 0, sizeof(fktypoid));
MemSet(opclasses, 0, sizeof(opclasses));
@@ -7157,6 +7161,50 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
fkattnum, fktypoid);
/*
+ * Validate the reference semantics codes, too, and convert list to array
+ * format to pass to CreateConstraintEntry.
+ */
+ Assert(list_length(fkconstraint->fk_reftypes) == numfks);
+ has_array = false;
+ i = 0;
+ foreach(lc, fkconstraint->fk_reftypes)
+ {
+ char reftype = lfirst_int(lc);
+
+ switch (reftype)
+ {
+ case FKCONSTR_REF_PLAIN:
+ /* OK, nothing to do */
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ /* At most one FK column can be an array reference */
+ if (has_array)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign keys support only one array column")));
+ has_array = true;
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d", (int) reftype);
+ break;
+ }
+ fkreftypes[i] = reftype;
+ i++;
+ }
+
+ /* Array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions */
+ if (has_array)
+ {
+ if ((fkconstraint->fk_upd_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_upd_action != FKCONSTR_ACTION_RESTRICT) ||
+ (fkconstraint->fk_del_action != FKCONSTR_ACTION_NOACTION &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("Foreign Key Arrays support only NO ACTION and RESTRICT actions")));
+ }
+
+ /*
* If the attribute list for the referenced table was omitted, lookup the
* definition of the primary key and use it. Otherwise, validate the
* supplied attribute list. In either case, discover the index OID and
@@ -7242,6 +7290,65 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
eqstrategy = BTEqualStrategyNumber;
/*
+ * If this is an array foreign key, we must look up the operators for
+ * the array element type, not the array type itself.
+ */
+ if (fkreftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ Oid elemopclass;
+
+ /* We check if the array element type exists and is of valid Oid */
+ fktype = get_base_element_type(fktype);
+ if (!OidIsValid(fktype))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has type %s which is not an array type.",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktypoid[i]))));
+
+ /*
+ * For the moment, we must also insist that the array's element
+ * type have a default btree opclass that is in the index's
+ * opfamily. This is necessary because ri_triggers.c relies on
+ * COUNT(DISTINCT x) on the element type, as well as on array_eq()
+ * on the array type, and we need those operations to have the
+ * same notion of equality that we're using otherwise.
+ *
+ * XXX this restriction is pretty annoying, considering the effort
+ * that's been put into the rest of the RI mechanisms to make them
+ * work with nondefault equality operators. In particular, it
+ * means that the cast-to-PK-datatype code path isn't useful for
+ * array-to-scalar references.
+ */
+ elemopclass = GetDefaultOpClass(fktype, BTREE_AM_OID);
+ if (!OidIsValid(elemopclass) ||
+ get_opclass_family(elemopclass) != opfamily)
+ {
+ /* Get the index opclass's name for the error message. */
+ char *opcname;
+
+ cla_ht = SearchSysCache1(CLAOID,
+ ObjectIdGetDatum(opclasses[i]));
+ if (!HeapTupleIsValid(cla_ht))
+ elog(ERROR, "cache lookup failed for opclass %u",
+ opclasses[i]);
+ cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
+ opcname = pstrdup(NameStr(cla_tup->opcname));
+ ReleaseSysCache(cla_ht);
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("foreign key constraint \"%s\" cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key column \"%s\" has element type %s which does not have a default btree operator class that's compatible with class \"%s\".",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ format_type_be(fktype),
+ opcname)));
+ }
+ }
+
+ /*
* There had better be a primary equality operator for the index.
* We'll use it for PK = PK comparisons.
*/
@@ -7300,17 +7407,17 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
}
if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("foreign key constraint \"%s\" "
- "cannot be implemented",
- fkconstraint->conname),
- errdetail("Key columns \"%s\" and \"%s\" "
- "are of incompatible types: %s and %s.",
- strVal(list_nth(fkconstraint->fk_attrs, i)),
- strVal(list_nth(fkconstraint->pk_attrs, i)),
- format_type_be(fktype),
- format_type_be(pktype))));
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("foreign key constraint \"%s\" "
+ "cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key columns \"%s\" and \"%s\" "
+ "are of incompatible types: %s and %s.",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ strVal(list_nth(fkconstraint->pk_attrs, i)),
+ format_type_be(fktype),
+ format_type_be(pktype))));
if (old_check_ok)
{
@@ -7340,6 +7447,13 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
* We may assume that pg_constraint.conkey is not changing.
*/
old_fktype = attr->atttypid;
+ if (fkreftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ old_fktype = get_base_element_type(old_fktype);
+ /* this shouldn't happen ... */
+ if (!OidIsValid(old_fktype))
+ elog(ERROR, "old foreign key column is not an array");
+ }
new_fktype = fktype;
old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
&old_castfunc);
@@ -7382,7 +7496,6 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
new_castfunc == old_castfunc &&
(!IsPolymorphicType(pfeqop_right) ||
new_fktype == old_fktype));
-
}
pfeqoperators[i] = pfeqop;
@@ -7406,6 +7519,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
indexOid,
RelationGetRelid(pkrel),
pkattnum,
+ fkreftypes,
pfeqoperators,
ppeqoperators,
ffeqoperators,
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 1c488c338a..510d6c91b0 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -661,6 +661,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
@@ -1032,6 +1033,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
char fk_matchtype = FKCONSTR_MATCH_SIMPLE;
List *fk_attrs = NIL;
List *pk_attrs = NIL;
+ List *fk_reftypes = NIL;
StringInfoData buf;
int funcnum;
OldTriggerInfo *info = NULL;
@@ -1061,7 +1063,10 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
if (i % 2)
fk_attrs = lappend(fk_attrs, arg);
else
+ {
pk_attrs = lappend(pk_attrs, arg);
+ fk_reftypes = lappend_int(fk_reftypes, FKCONSTR_REF_PLAIN);
+ }
}
/* Prepare description of constraint for use in messages */
@@ -1200,6 +1205,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
fkcon->conname = constr_name;
fkcon->fk_attrs = fk_attrs;
fkcon->pk_attrs = pk_attrs;
+ fkcon->fk_reftypes = fk_reftypes;
fkcon->fk_matchtype = fk_matchtype;
switch (info->funcoids[0])
{
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index a40b3cf752..f10757b122 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -3164,6 +3164,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
NULL,
NULL,
NULL,
+ NULL,
0,
' ',
' ',
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index ddbbc79823..51e971d555 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2850,6 +2850,7 @@ _copyConstraint(const Constraint *from)
COPY_NODE_FIELD(pktable);
COPY_NODE_FIELD(fk_attrs);
COPY_NODE_FIELD(pk_attrs);
+ COPY_NODE_FIELD(fk_reftypes);
COPY_SCALAR_FIELD(fk_matchtype);
COPY_SCALAR_FIELD(fk_upd_action);
COPY_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 30ccc9c5ae..18d14b59f8 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2594,6 +2594,7 @@ _equalConstraint(const Constraint *a, const Constraint *b)
COMPARE_NODE_FIELD(pktable);
COMPARE_NODE_FIELD(fk_attrs);
COMPARE_NODE_FIELD(pk_attrs);
+ COMPARE_NODE_FIELD(fk_reftypes);
COMPARE_SCALAR_FIELD(fk_matchtype);
COMPARE_SCALAR_FIELD(fk_upd_action);
COMPARE_SCALAR_FIELD(fk_del_action);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 5e72df137e..3a4d9535d6 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -3501,6 +3501,7 @@ _outConstraint(StringInfo str, const Constraint *node)
WRITE_NODE_FIELD(pktable);
WRITE_NODE_FIELD(fk_attrs);
WRITE_NODE_FIELD(pk_attrs);
+ WRITE_NODE_FIELD(fk_reftypes);
WRITE_CHAR_FIELD(fk_matchtype);
WRITE_CHAR_FIELD(fk_upd_action);
WRITE_CHAR_FIELD(fk_del_action);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index e42b7caff6..5ac121d91d 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -126,6 +126,13 @@ typedef struct ImportQual
List *table_names;
} ImportQual;
+/* Private struct for the result of foreign_key_column_elem production */
+typedef struct FKColElem
+{
+ Node *name; /* name of the column (a String) */
+ char reftype; /* FKCONSTR_REF_xxx code */
+} FKColElem;
+
/* ConstraintAttributeSpec yields an integer bitmask of these flags: */
#define CAS_NOT_DEFERRABLE 0x01
#define CAS_DEFERRABLE 0x02
@@ -183,6 +190,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
static void SplitColQualList(List *qualList,
List **constraintList, CollateClause **collClause,
core_yyscan_t yyscanner);
+static void SplitFKColElems(List *fkcolelems, List **names, List **reftypes);
static void processCASbits(int cas_bits, int location, const char *constrType,
bool *deferrable, bool *initdeferred, bool *not_valid,
bool *no_inherit, core_yyscan_t yyscanner);
@@ -233,6 +241,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
A_Indices *aind;
ResTarget *target;
struct PrivTarget *privtarget;
+ struct FKColElem *fkcolelem;
AccessPriv *accesspriv;
struct ImportQual *importqual;
InsertStmt *istmt;
@@ -358,6 +367,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <accesspriv> privilege
%type <list> privileges privilege_list
%type <privtarget> privilege_target
+%type <fkcolelem> foreign_key_column_elem
%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes
%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list
%type <ival> defacl_privilege_target
@@ -393,7 +403,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
execute_param_clause using_clause returning_clause
opt_enum_val_list enum_val_list table_func_column_list
create_generic_options alter_generic_options
- relation_expr_list dostmt_opt_list
+ relation_expr_list dostmt_opt_list foreign_key_column_list
transform_element_list transform_type_list
TriggerTransitions TriggerReferencing
publication_name_list
@@ -625,8 +635,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
DOUBLE_P DROP
- EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
- EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
+ EACH ELEMENT ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE
+ EVENT EXCEPT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
EXTENSION EXTERNAL EXTRACT
FALSE_P FAMILY FETCH FILTER FIRST_P FLOAT_P FOLLOWING FOR
@@ -3527,8 +3537,10 @@ ColConstraintElem:
n->contype = CONSTR_FOREIGN;
n->location = @1;
n->pktable = $2;
+ /* fk_attrs will be filled in by parse analysis */
n->fk_attrs = NIL;
n->pk_attrs = $3;
+ n->fk_reftypes = list_make1_int(FKCONSTR_REF_PLAIN);
n->fk_matchtype = $4;
n->fk_upd_action = (char) ($5 >> 8);
n->fk_del_action = (char) ($5 & 0xFF);
@@ -3722,14 +3734,15 @@ ConstraintElem:
NULL, yyscanner);
$$ = (Node *)n;
}
- | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
- opt_column_list key_match key_actions ConstraintAttributeSpec
+ | FOREIGN KEY '(' foreign_key_column_list ')' REFERENCES
+ qualified_name opt_column_list key_match key_actions
+ ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_FOREIGN;
n->location = @1;
+ SplitFKColElems($4, &n->fk_attrs, &n->fk_reftypes);
n->pktable = $7;
- n->fk_attrs = $4;
n->pk_attrs = $8;
n->fk_matchtype = $9;
n->fk_upd_action = (char) ($10 >> 8);
@@ -3762,6 +3775,29 @@ columnElem: ColId
$$ = (Node *) makeString($1);
}
;
+ foreign_key_column_list:
+ foreign_key_column_elem
+ { $$ = list_make1($1); }
+ | foreign_key_column_list ',' foreign_key_column_elem
+ { $$ = lappend($1, $3); }
+ ;
+
+ foreign_key_column_elem:
+ ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($1);
+ n->reftype = FKCONSTR_REF_PLAIN;
+ $$ = n;
+ }
+ | EACH ELEMENT OF ColId
+ {
+ FKColElem *n = (FKColElem *) palloc(sizeof(FKColElem));
+ n->name = (Node *) makeString($4);
+ n->reftype = FKCONSTR_REF_EACH_ELEMENT;
+ $$ = n;
+ }
+ ;
key_match: MATCH FULL
{
@@ -14976,6 +15012,7 @@ unreserved_keyword:
| DOUBLE_P
| DROP
| EACH
+ | ELEMENT
| ENABLE_P
| ENCODING
| ENCRYPTED
@@ -16096,6 +16133,23 @@ SplitColQualList(List *qualList,
*constraintList = qualList;
}
+/* Split a list of FKColElem structs into separate name and reftype lists */
+static void
+SplitFKColElems(List *fkcolelems, List **names, List **reftypes)
+{
+ ListCell *lc;
+
+ *names = NIL;
+ *reftypes = NIL;
+ foreach(lc, fkcolelems)
+ {
+ FKColElem *fkcolelem = (FKColElem *) lfirst(lc);
+
+ *names = lappend(*names, fkcolelem->name);
+ *reftypes = lappend_int(*reftypes, fkcolelem->reftype);
+ }
+}
+
/*
* Process result of ConstraintAttributeSpec, and set appropriate bool flags
* in the output command node. Pass NULL for any flags the particular
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 085aa8766c..7fa070a3c1 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1386,6 +1386,90 @@ select_common_type(ParseState *pstate, List *exprs, const char *context,
}
/*
+ * look at select_common_type(); in parse_coerce.c(src/backend/parser/parse_coerce.c)
+ *
+ * select_common_type_2args()
+ * Determine the common supertype of a list of input expressions.
+ * 'leftOid': left operand's type
+ * 'rightOid': left operand's type
+ */
+Oid select_common_type_2args(Oid leftOid, Oid rightOid)
+{
+ TYPCATEGORY leftcategory;
+ TYPCATEGORY rightcategory;
+ bool leftispreferred;
+ bool rightispreferred;
+
+ Assert(leftOid != InvalidOid);
+ Assert(rightOid != InvalidOid);
+
+ /*
+ * If input types are valid and exactly the same, just pick that type.
+ */
+ if (leftOid != UNKNOWNOID)
+ {
+ if (rightOid == leftOid)
+ return leftOid;
+ }
+
+ /*
+ * Nope, so set up for the full algorithm.
+ */
+ leftOid = getBaseType(leftOid);
+ rightOid = getBaseType(rightOid);
+ get_type_category_preferred(leftOid, &leftcategory, &leftispreferred);
+ get_type_category_preferred(rightOid, &rightcategory, &rightispreferred);
+
+ if (rightOid != UNKNOWNOID && rightOid != leftOid)
+ {
+ if (rightcategory != leftcategory)
+ {
+ /* both types in different categories? then not much hope... */
+ return InvalidOid;
+ }
+
+ if (can_coerce_type(1, &rightOid, &leftOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left */
+ if (!can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can not coerce to it implicitly right to left, thus coercion only works one way */
+ return leftOid;
+ }
+ else
+ {
+ /* coercion works both ways, then decide depending on preferred flag */
+ if (leftispreferred)
+ return leftOid;
+ else
+ return rightOid;
+ }
+ }
+ else{
+ /* can not coerce to it implicitly right to left */
+ if (can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left, thus coercion only works one way */
+ return rightOid;
+ }
+ else{
+ /* can not coerce either way */
+ return InvalidOid;
+ }
+ }
+ }
+
+ /*
+ * If all the inputs were UNKNOWN type --- ie, unknown-type literals ---
+ * then resolve as type TEXT.
+ */
+ if (leftOid == UNKNOWNOID)
+ leftOid = TEXTOID;
+
+ return leftOid;
+}
+
+/*
* coerce_to_common_type()
* Coerce an expression to the given type.
*
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 128f1679c6..4f41827d4d 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -757,6 +757,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
* list of FK constraints to be processed later.
*/
constraint->fk_attrs = list_make1(makeString(column->colname));
+ /* grammar should have set fk_reftypes */
+ Assert(list_length(constraint->fk_reftypes) == 1);
cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
break;
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 8faae1d069..d577df3512 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -115,9 +115,11 @@ typedef struct RI_ConstraintInfo
char confupdtype; /* foreign key's ON UPDATE action */
char confdeltype; /* foreign key's ON DELETE action */
char confmatchtype; /* foreign key's match type */
+ bool has_array; /* true if any reftype is EACH_ELEMENT */
int nkeys; /* number of key columns */
int16 pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */
int16 fk_attnums[RI_MAX_NUMKEYS]; /* attnums of referencing cols */
+ char fk_reftypes[RI_MAX_NUMKEYS]; /* reference semantics */
Oid pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = FK) */
Oid pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = PK) */
Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK = FK) */
@@ -202,7 +204,8 @@ static void ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype);
+ const char *rightop, Oid rightoptype,
+ char fkreftype);
static void ri_add_cast_to(StringInfo buf, Oid typid);
static void ri_GenerateQualCollation(StringInfo buf, Oid collation);
static int ri_NullCheck(HeapTuple tup,
@@ -393,6 +396,7 @@ RI_FKey_check(TriggerData *trigdata)
if ((qplan = ri_FetchPreparedPlan(&qkey)) == NULL)
{
StringInfoData querybuf;
+ StringInfoData countbuf;
char pkrelname[MAX_QUOTED_REL_NAME_LEN];
char attname[MAX_QUOTED_NAME_LEN];
char paramname[16];
@@ -405,12 +409,27 @@ RI_FKey_check(TriggerData *trigdata)
* FOR KEY SHARE OF x
* The type id's for the $ parameters are those of the
* corresponding FK attributes.
+ *
+ * In case of an array ELEMENT foreign key, the previous query is used
+ * to count the number of matching rows and see if every combination
+ * is actually referenced.
+ * The wrapping query is
+ * SELECT 1 WHERE
+ * (SELECT count(DISTINCT y) FROM unnest($1) y)
+ * = (SELECT count(*) FROM (<QUERY>) z)
* ----------
*/
initStringInfo(&querybuf);
quoteRelationName(pkrelname, pk_rel);
appendStringInfo(&querybuf, "SELECT 1 FROM ONLY %s x", pkrelname);
querysep = "WHERE";
+
+ if (riinfo->has_array)
+ {
+ initStringInfo(&countbuf);
+ appendStringInfo(&countbuf, "SELECT 1 WHERE ");
+ }
+
for (i = 0; i < riinfo->nkeys; i++)
{
Oid pk_type = RIAttType(pk_rel, riinfo->pk_attnums[i]);
@@ -419,18 +438,41 @@ RI_FKey_check(TriggerData *trigdata)
quoteOneName(attname,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
sprintf(paramname, "$%d", i + 1);
+
+ /*
+ * In case of an array ELEMENT foreign key, we check that each
+ * distinct non-null value in the array is present in the PK
+ * table.
+ */
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&countbuf,
+ "(SELECT pg_catalog.count(DISTINCT y) FROM pg_catalog.unnest(%s) y)",
+ paramname);
+
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pf_eq_oprs[i],
- paramname, fk_type);
+ paramname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = fk_type;
}
appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
- /* Prepare and save the plan */
- qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
- &qkey, fk_rel, pk_rel, true);
+ if (riinfo->has_array)
+ {
+ appendStringInfo(&countbuf,
+ " OPERATOR(pg_catalog.=) (SELECT pg_catalog.count(*) FROM (%s) z)",
+ querybuf.data);
+
+ /* Prepare and save the plan for Foreign Key Arrays */
+ qplan = ri_PlanCheck(countbuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
+ }
+ else
+ /* Prepare and save the plan */
+ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
+ &qkey, fk_rel, pk_rel, true);
}
/*
@@ -557,7 +599,8 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
ri_GenerateQual(&querybuf, querysep,
attname, pk_type,
riinfo->pp_eq_oprs[i],
- paramname, pk_type);
+ paramname, pk_type,
+ FKCONSTR_REF_PLAIN);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -815,7 +858,8 @@ ri_restrict(TriggerData *trigdata, bool is_no_action)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -971,7 +1015,8 @@ RI_FKey_cascade_del(PG_FUNCTION_ARGS)
ri_GenerateQual(&querybuf, querysep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = "AND";
queryoids[i] = pk_type;
}
@@ -1150,7 +1195,8 @@ RI_FKey_cascade_upd(PG_FUNCTION_ARGS)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1368,7 +1414,8 @@ ri_setnull(TriggerData *trigdata)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1585,7 +1632,8 @@ ri_setdefault(TriggerData *trigdata)
ri_GenerateQual(&qualbuf, qualsep,
paramname, pk_type,
riinfo->pf_eq_oprs[i],
- attname, fk_type);
+ attname, fk_type,
+ riinfo->fk_reftypes[i]);
querysep = ",";
qualsep = "AND";
queryoids[i] = pk_type;
@@ -1904,6 +1952,14 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
* For MATCH FULL:
* (fk.keycol1 IS NOT NULL [OR ...])
*
+ * In case of an array ELEMENT column, relname is replaced with the
+ * following subquery:
+ *
+ * SELECT unnest("keycol1") k1, "keycol1" ak1 [, ...]
+ * FROM ONLY "public"."fk"
+ *
+ * where all the columns are renamed in order to prevent name collisions.
+ *
* We attach COLLATE clauses to the operators when comparing columns
* that have different collations.
*----------
@@ -1915,15 +1971,46 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
{
quoteOneName(fkattname,
RIAttName(fk_rel, riinfo->fk_attnums[i]));
- appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
+ if (riinfo->has_array)
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%sfk.ak%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.k%d %s", sep, i + 1,
+ fkattname);
+ else
+ appendStringInfo(&querybuf, "%sfk.%s", sep, fkattname);
sep = ", ";
}
quoteRelationName(pkrelname, pk_rel);
quoteRelationName(fkrelname, fk_rel);
- appendStringInfo(&querybuf,
- " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
- fkrelname, pkrelname);
+
+ if (riinfo->has_array)
+ {
+ sep = "";
+ appendStringInfo(&querybuf,
+ " FROM (SELECT ");
+ for (i = 0; i < riinfo->nkeys; i++)
+ {
+ quoteOneName(fkattname,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->fk_reftypes[i] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfo(&querybuf, "%spg_catalog.unnest(%s) k%d, %s ak%d",
+ sep, fkattname, i + 1, fkattname, i + 1);
+ else
+ appendStringInfo(&querybuf, "%s%s k%d", sep, fkattname,
+ i + 1);
+ sep = ", ";
+ }
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s) fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
+ }
+ else
+ appendStringInfo(&querybuf,
+ " FROM ONLY %s fk LEFT OUTER JOIN ONLY %s pk ON",
+ fkrelname, pkrelname);
strcpy(pkattname, "pk.");
strcpy(fkattname, "fk.");
@@ -1937,12 +2024,16 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
quoteOneName(pkattname + 3,
RIAttName(pk_rel, riinfo->pk_attnums[i]));
- quoteOneName(fkattname + 3,
- RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname + 3, "k%d", i + 1);
+ else
+ quoteOneName(fkattname + 3,
+ RIAttName(fk_rel, riinfo->fk_attnums[i]));
ri_GenerateQual(&querybuf, sep,
pkattname, pk_type,
riinfo->pf_eq_oprs[i],
- fkattname, fk_type);
+ fkattname, fk_type,
+ FKCONSTR_REF_PLAIN);
if (pk_coll != fk_coll)
ri_GenerateQualCollation(&querybuf, pk_coll);
sep = "AND";
@@ -1958,7 +2049,10 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
sep = "";
for (i = 0; i < riinfo->nkeys; i++)
{
- quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
+ if (riinfo->has_array)
+ sprintf(fkattname, "k%d", i + 1);
+ else
+ quoteOneName(fkattname, RIAttName(fk_rel, riinfo->fk_attnums[i]));
appendStringInfo(&querybuf,
"%sfk.%s IS NOT NULL",
sep, fkattname);
@@ -2134,20 +2228,23 @@ quoteRelationName(char *buffer, Relation rel)
/*
* ri_GenerateQual --- generate a WHERE clause equating two variables
*
- * The idea is to append " sep leftop op rightop" to buf. The complexity
- * comes from needing to be sure that the parser will select the desired
- * operator. We always name the operator using OPERATOR(schema.op) syntax
- * (readability isn't a big priority here), so as to avoid search-path
- * uncertainties. We have to emit casts too, if either input isn't already
- * the input type of the operator; else we are at the mercy of the parser's
- * heuristics for ambiguous-operator resolution.
- */
+ * The idea is to append " sep leftop op rightop" to buf, or if fkreftype is
+ * FKCONSTR_REF_EACH_ELEMENT, append " sep rightop @> leftop" to buf.
+ *
+ * The complexity comes from needing to be sure that the parser will select
+ * the desired operator. We always name the operator using
+ * OPERATOR(schema.op) syntax (readability isn't a big priority here), so as
+ * to avoid search-path uncertainties. We have to emit casts too, if either
+ * input isn't already the input type of the operator; else we are at the
+ * mercy of the parser's heuristics for ambiguous-operator resolution.
+ */
static void
ri_GenerateQual(StringInfo buf,
const char *sep,
const char *leftop, Oid leftoptype,
Oid opoid,
- const char *rightop, Oid rightoptype)
+ const char *rightop, Oid rightoptype,
+ char fkreftype)
{
HeapTuple opertup;
Form_pg_operator operform;
@@ -2163,14 +2260,57 @@ ri_GenerateQual(StringInfo buf,
nspname = get_namespace_name(operform->oprnamespace);
- appendStringInfo(buf, " %s %s", sep, leftop);
- if (leftoptype != operform->oprleft)
- ri_add_cast_to(buf, operform->oprleft);
- appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
- appendStringInfoString(buf, oprname);
- appendStringInfo(buf, ") %s", rightop);
- if (rightoptype != operform->oprright)
- ri_add_cast_to(buf, operform->oprright);
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ Oid oprright;
+ Oid oprleft;
+ Oid oprcommon;
+ /*
+ * we first need to get the array types and decide
+ * the more appropriate one
+ */
+
+ /* get array type of refrenced element*/
+ oprright = get_array_type(operform->oprleft);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprleft))));
+ /* get array type of refrencing element*/
+ oprleft = get_array_type(operform->oprright);
+ if (!OidIsValid(oprleft))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprright))));
+ /* compare the two array types and try to find
+ a common supertype */
+ oprcommon = select_common_type_2args(oprleft, oprright);
+ if (!OidIsValid(oprcommon))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find common type between %s and %s",
+ format_type_be(oprleft), format_type_be(oprright))));
+
+ appendStringInfo(buf, " %s %s", sep, rightop);
+ if (oprleft != oprcommon)
+ ri_add_cast_to(buf, oprcommon);
+ appendStringInfo(buf, " OPERATOR(pg_catalog.@>) ARRAY[%s]", leftop);
+ if (oprright != oprcommon)
+ ri_add_cast_to(buf, oprcommon);
+ }
+ else
+ {
+ appendStringInfo(buf, " %s %s", sep, leftop);
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+ appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
+ appendStringInfoString(buf, oprname);
+ appendStringInfo(buf, ") %s", rightop);
+ if (rightoptype != operform->oprright)
+ ri_add_cast_to(buf, operform->oprright);
+ }
ReleaseSysCache(opertup);
}
@@ -2378,6 +2518,7 @@ ri_LoadConstraintInfo(Oid constraintOid)
bool isNull;
ArrayType *arr;
int numkeys;
+ int i;
/*
* On the first call initialize the hashtable
@@ -2456,6 +2597,20 @@ ri_LoadConstraintInfo(Oid constraintOid)
pfree(arr); /* free de-toasted copy, if any */
adatum = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype, &isNull);
+ if (isNull)
+ elog(ERROR, "null confreftype for constraint %u", constraintOid);
+ arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
+ if (ARR_NDIM(arr) != 1 ||
+ ARR_DIMS(arr)[0] != numkeys ||
+ ARR_HASNULL(arr) ||
+ ARR_ELEMTYPE(arr) != CHAROID)
+ elog(ERROR, "confreftype is not a 1-D char array");
+ memcpy(riinfo->fk_reftypes, ARR_DATA_PTR(arr), numkeys * sizeof(char));
+ if ((Pointer) arr != DatumGetPointer(adatum))
+ pfree(arr); /* free de-toasted copy, if any */
+
+ adatum = SysCacheGetAttr(CONSTROID, tup,
Anum_pg_constraint_conpfeqop, &isNull);
if (isNull)
elog(ERROR, "null conpfeqop for constraint %u", constraintOid);
@@ -2498,6 +2653,24 @@ ri_LoadConstraintInfo(Oid constraintOid)
if ((Pointer) arr != DatumGetPointer(adatum))
pfree(arr); /* free de-toasted copy, if any */
+ /*
+ * Fix up some stuff for Foreign Key Arrays. We need a has_array flag
+ * indicating whether there's an array foreign key, and we want to set
+ * ff_eq_oprs[i] to array_eq() for array columns, because that's what
+ * makes sense for ri_KeysEqual, and we have no other use for ff_eq_oprs
+ * in this module. (If we did, substituting the array comparator at the
+ * call point in ri_KeysEqual might be more appropriate.)
+ */
+ riinfo->has_array = false;
+ for (i = 0; i < numkeys; i++)
+ {
+ if (riinfo->fk_reftypes[i] != FKCONSTR_REF_PLAIN)
+ {
+ riinfo->has_array = true;
+ riinfo->ff_eq_oprs[i] = ARRAY_EQ_OP;
+ }
+ }
+
ReleaseSysCache(tup);
/*
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 9cdbb06add..229d5d8c93 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -314,6 +314,9 @@ static char *pg_get_viewdef_worker(Oid viewoid,
static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
static void decompile_column_index_array(Datum column_index_array, Oid relId,
StringInfo buf);
+static void decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf);
static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
const Oid *excludeOps,
@@ -1899,7 +1902,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
{
case CONSTRAINT_FOREIGN:
{
- Datum val;
+ Datum colindexes;
+ Datum reftypes;
bool isnull;
const char *string;
@@ -1907,13 +1911,21 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
appendStringInfoString(&buf, "FOREIGN KEY (");
/* Fetch and build referencing-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_conkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_conkey,
+ &isnull);
if (isnull)
elog(ERROR, "null conkey for constraint %u",
constraintId);
+ reftypes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confreftype,
+ &isnull);
+ if (isnull)
+ elog(ERROR, "null confreftype for constraint %u",
+ constraintId);
- decompile_column_index_array(val, conForm->conrelid, &buf);
+ decompile_fk_column_index_array(colindexes, reftypes,
+ conForm->conrelid, &buf);
/* add foreign relation name */
appendStringInfo(&buf, ") REFERENCES %s(",
@@ -1921,13 +1933,15 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
NIL));
/* Fetch and build referenced-column list */
- val = SysCacheGetAttr(CONSTROID, tup,
- Anum_pg_constraint_confkey, &isnull);
+ colindexes = SysCacheGetAttr(CONSTROID, tup,
+ Anum_pg_constraint_confkey,
+ &isnull);
if (isnull)
elog(ERROR, "null confkey for constraint %u",
constraintId);
- decompile_column_index_array(val, conForm->confrelid, &buf);
+ decompile_column_index_array(colindexes,
+ conForm->confrelid, &buf);
appendStringInfoChar(&buf, ')');
@@ -2202,6 +2216,66 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
}
}
+ /*
+ * Convert an int16[] Datum and a char[] Datum into a comma-separated
+ * list of column names for the indicated relation, prefixed by appropriate
+ * keywords depending on the foreign key reference semantics indicated by
+ * the char[] entries. Append the text to buf.
+ */
+ static void
+ decompile_fk_column_index_array(Datum column_index_array,
+ Datum fk_reftype_array,
+ Oid relId, StringInfo buf)
+ {
+ Datum *keys;
+ int nKeys;
+ Datum *reftypes;
+ int nReftypes;
+ int j;
+
+ /* Extract data from array of int16 */
+ deconstruct_array(DatumGetArrayTypeP(column_index_array),
+ INT2OID, sizeof(int16), true, 's',
+ &keys, NULL, &nKeys);
+
+ /* Extract data from array of char */
+ deconstruct_array(DatumGetArrayTypeP(fk_reftype_array),
+ CHAROID, sizeof(char), true, 'c',
+ &reftypes, NULL, &nReftypes);
+
+ if (nKeys != nReftypes)
+ elog(ERROR, "wrong confreftype cardinality");
+
+ for (j = 0; j < nKeys; j++)
+ {
+ char *colName;
+ const char *prefix;
+
+ colName = get_relid_attribute_name(relId, DatumGetInt16(keys[j]));
+
+ switch (DatumGetChar(reftypes[j]))
+ {
+ case FKCONSTR_REF_PLAIN:
+ prefix = "";
+ break;
+ case FKCONSTR_REF_EACH_ELEMENT:
+ prefix = "EACH ELEMENT OF ";
+ break;
+ default:
+ elog(ERROR, "invalid fk_reftype: %d",
+ (int) DatumGetChar(reftypes[j]));
+ prefix = NULL; /* keep compiler quiet */
+ break;
+ }
+
+ if (j == 0)
+ appendStringInfo(buf, "%s%s", prefix,
+ quote_identifier(colName));
+ else
+ appendStringInfo(buf, ", %s%s", prefix,
+ quote_identifier(colName));
+ }
+}
/* ----------
* get_expr - Decompile an expression tree
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index 8fca86d71e..96eea9753d 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -104,8 +104,16 @@ CATALOG(pg_constraint,2606)
int16 confkey[1];
/*
+ * If a foreign key, the reference semantics for each column
+ */
+ char confreftype[1];
+
+ /*
* If a foreign key, the OIDs of the PK = FK equality operators for each
* column of the constraint
+ *
+ * Note: for Foreign Key Arrays, all these operators are for the array's
+ * element type.
*/
Oid conpfeqop[1];
@@ -150,7 +158,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* compiler constants for pg_constraint
* ----------------
*/
-#define Natts_pg_constraint 24
+#define Natts_pg_constraint 25
#define Anum_pg_constraint_conname 1
#define Anum_pg_constraint_connamespace 2
#define Anum_pg_constraint_contype 3
@@ -169,12 +177,13 @@ typedef FormData_pg_constraint *Form_pg_constraint;
#define Anum_pg_constraint_connoinherit 16
#define Anum_pg_constraint_conkey 17
#define Anum_pg_constraint_confkey 18
-#define Anum_pg_constraint_conpfeqop 19
-#define Anum_pg_constraint_conppeqop 20
-#define Anum_pg_constraint_conffeqop 21
-#define Anum_pg_constraint_conexclop 22
-#define Anum_pg_constraint_conbin 23
-#define Anum_pg_constraint_consrc 24
+#define Anum_pg_constraint_confreftype 19
+#define Anum_pg_constraint_conpfeqop 20
+#define Anum_pg_constraint_conppeqop 21
+#define Anum_pg_constraint_conffeqop 22
+#define Anum_pg_constraint_conexclop 23
+#define Anum_pg_constraint_conbin 24
+#define Anum_pg_constraint_consrc 25
/* ----------------
* initial contents of pg_constraint
@@ -195,7 +204,9 @@ typedef FormData_pg_constraint *Form_pg_constraint;
/*
* Valid values for confupdtype and confdeltype are the FKCONSTR_ACTION_xxx
* constants defined in parsenodes.h. Valid values for confmatchtype are
- * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h.
+ * the FKCONSTR_MATCH_xxx constants defined in parsenodes.h. Valid values
+ * for elements of confreftype[] are the FKCONSTR_REF_xxx constants defined
+ * in parsenodes.h.
*/
#endif /* PG_CONSTRAINT_H */
diff --git a/src/include/catalog/pg_constraint_fn.h b/src/include/catalog/pg_constraint_fn.h
index 6bb1b09714..e720745546 100644
--- a/src/include/catalog/pg_constraint_fn.h
+++ b/src/include/catalog/pg_constraint_fn.h
@@ -40,6 +40,7 @@ extern Oid CreateConstraintEntry(const char *constraintName,
Oid indexRelId,
Oid foreignRelId,
const int16 *foreignKey,
+ const char *foreignRefType,
const Oid *pfEqOp,
const Oid *ppEqOp,
const Oid *ffEqOp,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b72178efd1..2b84d8a960 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2086,6 +2086,10 @@ typedef enum ConstrType /* types of constraints */
#define FKCONSTR_MATCH_PARTIAL 'p'
#define FKCONSTR_MATCH_SIMPLE 's'
+ /* Foreign key column reference semantics codes */
+ #define FKCONSTR_REF_PLAIN 'p'
+ #define FKCONSTR_REF_EACH_ELEMENT 'e'
+
typedef struct Constraint
{
NodeTag type;
@@ -2121,6 +2125,7 @@ typedef struct Constraint
RangeVar *pktable; /* Primary key table */
List *fk_attrs; /* Attributes of foreign key */
List *pk_attrs; /* Corresponding attrs in PK table */
+ List *fk_reftypes; /* Per-column reference semantics (int List) */
char fk_matchtype; /* FULL, PARTIAL, SIMPLE */
char fk_upd_action; /* ON UPDATE action */
char fk_del_action; /* ON DELETE action */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 26af944e03..af00143580 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -141,6 +141,7 @@ PG_KEYWORD("domain", DOMAIN_P, UNRESERVED_KEYWORD)
PG_KEYWORD("double", DOUBLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD)
PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD)
+PG_KEYWORD("element", ELEMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("else", ELSE, RESERVED_KEYWORD)
PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index af12c136ef..2955b57ecc 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -67,6 +67,7 @@ extern int parser_coercion_errposition(ParseState *pstate,
extern Oid select_common_type(ParseState *pstate, List *exprs,
const char *context, Node **which_expr);
+extern Oid select_common_type_2args(Oid ptype, Oid ntype);
extern Node *coerce_to_common_type(ParseState *pstate, Node *node,
Oid targetTypeId,
const char *context);
diff --git a/src/test/regress/expected/element_foreign_key.out b/src/test/regress/expected/element_foreign_key.out
new file mode 100644
index 0000000000..ed991e2fae
--- /dev/null
+++ b/src/test/regress/expected/element_foreign_key.out
@@ -0,0 +1,639 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fkarray"
+DETAIL: Key (ftest1)=({10,1}) is not present in table "pktableforarray".
+DROP TABLE FKTABLEFORARRAY;
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4,6}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({6,NULL,4,NULL}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+ERROR: insert or update on table "fktableforarraymdim" violates foreign key constraint "fktableforarraymdim_ftest1_fkey"
+DETAIL: Key (ftest1)=({{1,2},{6,NULL}}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+ERROR: null value in column "ftest1" violates not-null constraint
+DETAIL: Failing row contains (null, 21).
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(1) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {2} | 4
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+(11 rows)
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+----------+--------
+ {1} | 3
+ {1} | 5
+ {3} | 6
+ {1} | 7
+ {4,5} | 8
+ {4,4} | 9
+ | 10
+ {} | 11
+ {1,NULL} | 12
+ {NULL} | 13
+ {1} | 4
+(11 rows)
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE FKTABLEFORARRAY;
+ERROR: table "fktableforarray" does not exist
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+ERROR: Foreign Key Arrays support only NO ACTION and RESTRICT actions
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+NOTICE: table "fktableforarray" does not exist, skipping
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({D}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({A,B,D}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(A) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey" on table "fktableforarray"
+DETAIL: Key (ptest1)=(B) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ ftest1 | ftest2
+---------+--------
+ {A} | 1
+ {B} | 2
+ {C} | 3
+ {A,B,C} | 4
+(4 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- -- Repeat a similar test using INT2 keys coerced from INT4
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({4}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_ftest1_fkey"
+DETAIL: Key (ftest1)=({1,2,5}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+ERROR: foreign key constraint "fktableforarray_ftest1_fkey" cannot be implemented
+DETAIL: Key column "ftest1" has element type integer which does not have a default btree operator class that's compatible with class "float8_ops".
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(A, {A,B,C}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fid1_fkey"
+DETAIL: Key (fid1, fid2)=(B, {A}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- Test Foreign Key Arrays with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,99)"}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey"
+DETAIL: Key (invoice_ids)=({"(2011,1)","(2010,1)"}) is not present in table "pktableforarray".
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2010,99)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+ERROR: update or delete on table "pktableforarray" violates foreign key constraint "fktableforarray_invoice_ids_fkey" on table "fktableforarray"
+DETAIL: Key (id)=((2011,1)) is still referenced from table "fktableforarray".
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+ id | invoice_ids | ftest2
+----+-------------------------+-----------
+ 1 | {"(2010,99)"} | Product A
+ 2 | {"(2011,1)","(2011,2)"} | Product B
+ 3 | {"(2011,2)"} | Product C
+(3 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({0,1}) is not present in table "pktableforarray".
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+ERROR: insert or update on table "fktableforarray" violates foreign key constraint "fktableforarray_fids_fkey"
+DETAIL: Key (fids)=({2,1}) is not present in table "pktableforarray".
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(2, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(20, {0,2,3,4,6}) is not present in table "dim1".
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_x_fkey"
+DETAIL: Key (x, y)=(4, {0,2,4}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+ERROR: foreign keys support only one array column
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+ERROR: foreign keys support only one array column
+DROP TABLE F1;
+-- Cleanup
+DROP TABLE DIM1;
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "x2_x1_fkey"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+ERROR: insert or update on table "x2" violates foreign key constraint "fk_const"
+DETAIL: Key (x1, x2)=({1,3}, 6) is not present in table "x1".
+DROP TABLE x2;
+DROP TABLE x1;
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{100,100,100},{NULL,NULL,20},{7,8,10}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+ERROR: insert or update on table "f1" violates foreign key constraint "f1_slots_fkey"
+DETAIL: Key (slots)=({{NULL,1,NULL},{NULL,NULL,11},{NULL,NULL,6}}) is not present in table "dim1".
+DROP TABLE F1;
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
+-- Check GIN index
+-- Define PKTABLEFORARRAYGIN
+CREATE TABLE PKTABLEFORARRAYGIN ( ptest1 int PRIMARY KEY, ptest2 text );
+-- Insert test data into PKTABLEFORARRAYGIN
+INSERT INTO PKTABLEFORARRAYGIN VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (5, 'Test5');
+-- Define FKTABLEFORARRAYGIN
+CREATE TABLE FKTABLEFORARRAYGIN ( ftest1 int[],
+ ftest2 int PRIMARY KEY,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYGIN
+ ON DELETE NO ACTION ON UPDATE NO ACTION);
+-- -- Create index on FKTABLEFORARRAYGIN
+CREATE INDEX ON FKTABLEFORARRAYGIN USING gin (ftest1 array_ops);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,5,3}', 10);
+-- Try using the indexable operator
+SELECT * FROM FKTABLEFORARRAYGIN WHERE ftest1 @> ARRAY[5];
+ ftest1 | ftest2
+-------------+--------
+ {5} | 1
+ {3,5,2,5} | 3
+ {3,5,4,1,3} | 5
+ {5,1} | 7
+ {3,4,5,3} | 10
+(5 rows)
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYGIN;
+DROP TABLE PKTABLEFORARRAYGIN;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index e224977791..52505e7539 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -104,7 +104,12 @@ test: publication subscription
# ----------
# Another group of parallel tests
# ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
+test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass
+
+# ----------
+# Another group of parallel tests (no room in the previouse group)
+# ----------
+test: element_foreign_key
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 9fc5f1a268..eefd1df73b 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -143,6 +143,7 @@ test: amutils
test: select_views
test: portals_p2
test: foreign_key
+test: element_foreign_key
test: cluster
test: dependency
test: guc
diff --git a/src/test/regress/sql/element_foreign_key.sql b/src/test/regress/sql/element_foreign_key.sql
new file mode 100644
index 0000000000..d012589430
--- /dev/null
+++ b/src/test/regress/sql/element_foreign_key.sql
@@ -0,0 +1,503 @@
+-- EACH-ELEMENT FK CONSTRAINTS
+
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAY
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAY VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAY VALUES (5, 'Test5');
+
+-- Check alter table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 1);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check alter table with failing rows
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], ftest2 int );
+INSERT INTO FKTABLEFORARRAY VALUES ('{10,1}', 2);
+ALTER TABLE FKTABLEFORARRAY ADD CONSTRAINT FKARRAY FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAY;
+
+-- Check create table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYMDIM ( ftest1 int[][], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+CREATE TABLE FKTABLEFORARRAYNOTNULL ( ftest1 int[] NOT NULL, FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert successful rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{2}', 4);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{3}', 6);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1}', 7);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,5}', 8);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,4}', 9);
+INSERT INTO FKTABLEFORARRAY VALUES (NULL, 10);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}', 11);
+INSERT INTO FKTABLEFORARRAY VALUES ('{1,NULL}', 12);
+INSERT INTO FKTABLEFORARRAY VALUES ('{NULL}', 13);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{1,2},{1,3}}', 14);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{4,5},{NULL,2},{NULL,3}}', 15);
+
+-- Insert failed rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{6}', 16);
+INSERT INTO FKTABLEFORARRAY VALUES ('{4,6}', 17);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL}', 18);
+INSERT INTO FKTABLEFORARRAY VALUES ('{6,NULL,4,NULL}', 19);
+INSERT INTO FKTABLEFORARRAYMDIM VALUES ('{{1,2},{6,NULL}}', 20);
+INSERT INTO FKTABLEFORARRAYNOTNULL VALUES (NULL, 21);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE NO ACTION)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1=1;
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE NO ACTION)
+UPDATE PKTABLEFORARRAY SET ptest1=7 WHERE ptest1=1;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Check UPDATE on FKTABLE
+UPDATE FKTABLEFORARRAY SET ftest1=ARRAY[1] WHERE ftest2=4;
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE FKTABLEFORARRAYNOTNULL;
+DROP TABLE FKTABLEFORARRAYMDIM;
+
+-- Allowed references with actions (NO ACTION, RESTRICT)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+-- Not allowed references (SET NULL, SET DEFAULT, CASCADE)
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET DEFAULT, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE SET NULL, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE NO ACTION ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE RESTRICT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET DEFAULT ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE SET NULL ON UPDATE CASCADE, ftest2 int );
+DROP TABLE IF EXISTS FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Check reference on empty table
+CREATE TABLE PKTABLEFORARRAY (ptest1 int PRIMARY KEY);
+CREATE TABLE FKTABLEFORARRAY (ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY);
+INSERT INTO FKTABLEFORARRAY VALUES ('{}');
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using CHAR(1) keys rather than INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 CHAR(1) PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('C', 'Test C');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 char(1)[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON UPDATE RESTRICT ON DELETE RESTRICT, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A"}', 1);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"B"}', 2);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"C"}', 3);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","C"}', 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('{"D"}', 5);
+INSERT INTO FKTABLEFORARRAY VALUES ('{"A","B","D"}', 6);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE (must fail due to ON DELETE RESTRICT)
+DELETE FROM PKTABLEFORARRAY WHERE ptest1='A';
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE (must fail due to ON UPDATE RESTRICT)
+UPDATE PKTABLEFORARRAY SET ptest1='D' WHERE ptest1='B';
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- -- Repeat a similar test using INT2 keys coerced from INT4
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Repeat a similar test using FLOAT8 keys coerced from INTEGER
+CREATE TABLE PKTABLEFORARRAY ( ptest1 float8 PRIMARY KEY, ptest2 text );
+-- XXX this really ought to work, but currently we must disallow it
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Cleanup
+DROP TABLE PKTABLEFORARRAY;
+
+-- Composite primary keys
+CREATE TABLE PKTABLEFORARRAY ( id1 CHAR(1), id2 CHAR(1), ptest2 text, PRIMARY KEY (id1, id2) );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'A', 'Test A');
+INSERT INTO PKTABLEFORARRAY VALUES ('A', 'B', 'Test B');
+INSERT INTO PKTABLEFORARRAY VALUES ('B', 'C', 'Test B');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( fid1 CHAR(1), fid2 CHAR(1)[], ftest2 text, FOREIGN KEY (fid1, EACH ELEMENT OF fid2) REFERENCES PKTABLEFORARRAY);
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B'], '1');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['C'], '2');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES ('A', ARRAY['A','B', 'C'], '3');
+INSERT INTO FKTABLEFORARRAY VALUES ('B', ARRAY['A'], '4');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- Test Foreign Key Arrays with composite type
+CREATE TYPE INVOICEID AS (year_part INTEGER, progressive_part INTEGER);
+CREATE TABLE PKTABLEFORARRAY ( id INVOICEID PRIMARY KEY, ptest2 text);
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2010, 99), 'Last invoice for 2010');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 1), 'First invoice for 2011');
+INSERT INTO PKTABLEFORARRAY VALUES (ROW(2011, 2), 'Second invoice for 2011');
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, invoice_ids INVOICEID[], FOREIGN KEY (EACH ELEMENT OF invoice_ids) REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2010,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2011,2)']::INVOICEID[], 'Product B');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,2)']::INVOICEID[], 'Product C');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,99)']::INVOICEID[], 'Product A');
+INSERT INTO FKTABLEFORARRAY(invoice_ids, ftest2) VALUES (ARRAY['(2011,1)','(2010,1)']::INVOICEID[], 'Product B');
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Delete a row from PK TABLE
+DELETE FROM PKTABLEFORARRAY WHERE id=ROW(2010,99);
+
+-- Check FKTABLE for removal of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Update a row from PK TABLE
+UPDATE PKTABLEFORARRAY SET id=ROW(2011,99) WHERE id=ROW(2011,1);
+
+-- Check FKTABLE for update of matched row
+SELECT * FROM FKTABLEFORARRAY;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+DROP TYPE INVOICEID;
+
+-- Check for an array column referencing another array column (NOT ELEMENT FOREIGN KEY)
+-- Create primary table with an array primary key
+CREATE TABLE PKTABLEFORARRAY ( id INT[] PRIMARY KEY, ptest2 text);
+
+-- Create the refrencing table
+CREATE TABLE FKTABLEFORARRAY ( id SERIAL PRIMARY KEY, fids INT[] REFERENCES PKTABLEFORARRAY, ftest2 TEXT );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,1}', 'A');
+INSERT INTO PKTABLEFORARRAY VALUES ('{1,2}', 'B');
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,1}', 'Product A');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{1,2}', 'Product B');
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{0,1}', 'Product C');
+INSERT INTO FKTABLEFORARRAY (fids, ftest2) VALUES ('{2,1}', 'Product D');
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
+-- ---------------------------------------
+-- Multi-column "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL, Y INTEGER NOT NULL, PRIMARY KEY (X, Y));
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT x.t, x.t * y.t
+ FROM (SELECT generate_series(1, 10) AS t) x,
+ (SELECT generate_series(0, 10) AS t) y;
+
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- FAILS (2 is not present)
+INSERT INTO F1 VALUES (4, '{0,NULL,4}'); -- OK
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Try updates
+UPDATE F1 SET y = '{0,2,4,6}' WHERE x = 2; -- OK
+UPDATE F1 SET y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS
+UPDATE F1 SET x = 20, y = '{0,2,3,4,6}' WHERE x = 2; -- FAILS (20 does not exist)
+UPDATE F1 SET y = '{0,4,8}' WHERE x = 4; -- OK
+UPDATE F1 SET y = '{0,5,NULL,10}' WHERE x = 5; -- OK
+DROP TABLE F1;
+
+
+-- Test with FOREIGN KEY after TABLE population
+CREATE TABLE F1 (
+ x INTEGER PRIMARY KEY, y INTEGER[]
+);
+-- Insert facts
+INSERT INTO F1 VALUES (1, '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES (2, '{0,2,4,6}'); -- OK
+INSERT INTO F1 VALUES (3, '{0,3,6,9,0,3,6,9,0,0,0,0,9,9}'); -- OK (multiple occurrences)
+INSERT INTO F1 VALUES (4, '{0,2,4}'); -- OK (2 is not present)
+INSERT INTO F1 VALUES (5, '{0,NULL,5}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+
+-- Test with TABLE declaration of a two-dim ELEMENT foreign key constraint (FAILS)
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[],
+ FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y)
+);
+
+
+-- Test with two-dim ELEMENT foreign key after TABLE population
+CREATE TABLE F1 (
+ x INTEGER[] PRIMARY KEY, y INTEGER[]
+);
+INSERT INTO F1 VALUES ('{1}', '{0,1,2,3,4,5}'); -- OK
+INSERT INTO F1 VALUES ('{1,2}', '{0,2,4,6}'); -- OK
+-- Add foreign key (FAILS)
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF x, EACH ELEMENT OF y) REFERENCES DIM1(x, y);
+DROP TABLE F1;
+
+-- Cleanup
+DROP TABLE DIM1;
+
+
+-- Check for potential name conflicts (with internal integrity checks)
+CREATE TABLE x1(x1 int, x2 int, PRIMARY KEY(x1,x2));
+INSERT INTO x1 VALUES
+ (1,4),
+ (1,5),
+ (2,4),
+ (2,5),
+ (3,6),
+ (3,7)
+;
+CREATE TABLE x2(x1 int[], x2 int, FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6); -- FAILS
+DROP TABLE x2;
+CREATE TABLE x2(x1 int[], x2 int);
+INSERT INTO x2 VALUES ('{1,2}',4);
+INSERT INTO x2 VALUES ('{1,3}',6);
+ALTER TABLE x2 ADD CONSTRAINT fk_const FOREIGN KEY(EACH ELEMENT OF x1, x2) REFERENCES x1; -- FAILS
+DROP TABLE x2;
+DROP TABLE x1;
+
+
+-- ---------------------------------------
+-- Multi-dimensional "ELEMENT" foreign key tests
+-- ---------------------------------------
+
+-- Create DIM1 table with two-column primary key
+CREATE TABLE DIM1 (X INTEGER NOT NULL PRIMARY KEY,
+ CODE TEXT NOT NULL UNIQUE);
+-- Populate DIM1 table pairs
+INSERT INTO DIM1 SELECT t, 'DIM1-' || lpad(t::TEXT, 2, '0')
+ FROM (SELECT generate_series(1, 10)) x(t);
+
+-- Test with TABLE declaration of an element foreign key constraint (NO ACTION)
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3], FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+UPDATE F1 SET SLOTS = '{{NULL, 1, NULL}, {NULL, NULL, 3}, {7, 8, 10}}' WHERE ID = 1; -- OK
+UPDATE F1 SET SLOTS = '{{100, 100, 100}, {NULL, NULL, 20}, {7, 8, 10}}' WHERE ID = 1; -- FAILS
+DROP TABLE F1;
+
+-- Test with postponed foreign key
+CREATE TABLE F1 (
+ ID SERIAL PRIMARY KEY,
+ SLOTS INTEGER[3][3]
+);
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 3}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'); -- OK
+INSERT INTO F1(SLOTS) VALUES ('{1, 2, 3, 4, 5, 6, 7, 8, 9}'); -- OK
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- FAILS
+DELETE FROM F1 WHERE ID = 2; -- REMOVE ISSUE
+ALTER TABLE F1 ADD FOREIGN KEY (EACH ELEMENT OF SLOTS) REFERENCES DIM1; -- NOW OK
+INSERT INTO F1(SLOTS) VALUES ('{{NULL, 1, NULL}, {NULL, NULL, 11}, {NULL, NULL, 6}}'); -- FAILS
+DROP TABLE F1;
+
+-- Leave tables in the database
+CREATE TABLE PKTABLEFORELEMENTFK ( ptest1 int PRIMARY KEY, ptest2 text );
+CREATE TABLE FKTABLEFORELEMENTFK ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORELEMENTFK, ftest2 int );
+
+-- Check ALTER TABLE ALTER TYPE
+ALTER TABLE FKTABLEFORELEMENTFK ALTER FTEST1 TYPE INT[];
+
+-- Check GIN index
+-- Define PKTABLEFORARRAYGIN
+CREATE TABLE PKTABLEFORARRAYGIN ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAYGIN
+INSERT INTO PKTABLEFORARRAYGIN VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (5, 'Test5');
+
+-- Define FKTABLEFORARRAYGIN
+CREATE TABLE FKTABLEFORARRAYGIN ( ftest1 int[],
+ ftest2 int PRIMARY KEY,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYGIN
+ ON DELETE NO ACTION ON UPDATE NO ACTION);
+
+-- -- Create index on FKTABLEFORARRAYGIN
+CREATE INDEX ON FKTABLEFORARRAYGIN USING gin (ftest1 array_ops);
+
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,5,3}', 10);
+
+-- Try using the indexable operator
+SELECT * FROM FKTABLEFORARRAYGIN WHERE ftest1 @> ARRAY[5];
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYGIN;
+DROP TABLE PKTABLEFORARRAYGIN;
\ No newline at end of file
--
2.11.0
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
@ 2018-01-12 19:47 ` Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2018-01-12 19:47 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Andreas Karlsson <andreas@proxel.se>; Alexander Korotkov <aekorotkov@gmail.com>; Stephen Frost <sfrost@snowman.net>; David Steele <david@pgmasters.net>; Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; Tom Lane <tgl@sss.pgh.pa.us>; Hans-Jürgen Schönig <hs@cybertec.at>
According to the last review, there are still two main issues with the
patch.
On Mon, Oct 30, 2017 at 3:24 AM, Andreas Karlsson <andreas@proxel.se> wrote:
> 1) MATCH FULL does not seem to care about NULLS in arrays. In the example
> below I expected both inserts into the referring table to fail.
>
> CREATE TABLE t (x int, y int, PRIMARY KEY (x, y));
> CREATE TABLE fk (x int, ys int[], FOREIGN KEY (x, EACH ELEMENT OF ys)
> REFERENCES t MATCH FULL);
> INSERT INTO t VALUES (10, 1);
> INSERT INTO fk VALUES (10, '{1,NULL}');
> INSERT INTO fk VALUES (NULL, '{1}');
>
> CREATE TABLE
> CREATE TABLE
> INSERT 0 1
> INSERT 0 1
> ERROR: insert or update on table "fk" violates foreign key constraint
> "fk_x_fkey"
> DETAIL: MATCH FULL does not allow mixing of null and nonnull key values.
>
> I understand that Match full should contain nulls in the results. However,
I don't think that it's semantically correct, so I suggest we don't use
Match full. What would be the consequences of that ?
2) I think the code in RI_Initial_Check() would be cleaner if you used
> "CROSS JOIN LATERAL unnest(col)" rather than having unnest() in the target
> list. This way you would not need to rename all columns and the code paths
> for the array case could look more like the code path for the normal case.
>
I have repeatedly tried to generate the suggested query using C code and I
failed. I would like some help with it
Best Regards,
Mark Rofail
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2018-01-12 20:28 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Alvaro Herrera @ 2018-01-12 20:28 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Andreas Karlsson <andreas@proxel.se>; Alexander Korotkov <aekorotkov@gmail.com>; Stephen Frost <sfrost@snowman.net>; David Steele <david@pgmasters.net>; Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; Tom Lane <tgl@sss.pgh.pa.us>; Hans-Jürgen Schönig <hs@cybertec.at>
Mark Rofail wrote:
> > 1) MATCH FULL does not seem to care about NULLS in arrays. In the example
> > below I expected both inserts into the referring table to fail.
> >
> > CREATE TABLE t (x int, y int, PRIMARY KEY (x, y));
> > CREATE TABLE fk (x int, ys int[], FOREIGN KEY (x, EACH ELEMENT OF ys) REFERENCES t MATCH FULL);
> > INSERT INTO t VALUES (10, 1);
> > INSERT INTO fk VALUES (10, '{1,NULL}');
> > INSERT INTO fk VALUES (NULL, '{1}');
> >
> I understand that Match full should contain nulls in the results. However,
> I don't think that it's semantically correct, so I suggest we don't use
> Match full. What would be the consequences of that ?
Well, I think you could get away with not supporting MATCH FULL with
array FK references (meaning you ought to raise an error if you see it) ...
clearly EACH ELEMENT OF is an extension of the spec so we're not forced
to comply with all the clauses. On the other hand, it would be better if it
can be made to work.
If I understand correctly, you would need a new operator similar to @>
but which rejects NULLs in order to implement MATCH FULL, right?
> > 2) I think the code in RI_Initial_Check() would be cleaner if you used
> > "CROSS JOIN LATERAL unnest(col)" rather than having unnest() in the target
> > list. This way you would not need to rename all columns and the code paths
> > for the array case could look more like the code path for the normal case.
> >
> I have repeatedly tried to generate the suggested query using C code and I
> failed. I would like some help with it
Well, the way to go about it would be to first figure out what is the
correct SQL query, and only later try to implement it in C. Is SQL the
problem, or is it C? I'm sure we can get in touch with somebody that
knows a little bit of SQL. Can you do a write-up of the query requirements?
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
@ 2018-01-12 21:19 ` Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2018-01-12 21:19 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Andreas Karlsson <andreas@proxel.se>; Alexander Korotkov <aekorotkov@gmail.com>; Stephen Frost <sfrost@snowman.net>; David Steele <david@pgmasters.net>; Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; Tom Lane <tgl@sss.pgh.pa.us>; Hans-Jürgen Schönig <hs@cybertec.at>
On Mon, Nov 13, 2017 at 2:41 AM, Andreas Karlsson <andreas@proxel.se> wrote:
> I think the code would look cleaner if you generate the following query:
>
> SELECT fk.x, fk.ys FROM ONLY t2 fk CROSS JOIN LATERAL
> pg_catalog.unnest(ys) a2 (v) LEFT OUTER JOIN ONLY t1 pk ON pk.x = fk.x AND
> pk.y = a2.v WHERE [...]
>
> rather than:
>
> SELECT fk.k1, fk.ak2 FROM (SELECT x k1, pg_catalog.unnest(ys) k2, ys ak2
> FROM ONLY t2) fk LEFT OUTER JOIN ONLY t1 pk ON pk.x = fk.k1 AND pk.y =
> fk.k2 WHERE [...]
>
Andreas has kindly written the SQL query for us. My problem is generating
it with C code
Best Regards,
Mark Rofail
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2018-01-30 23:52 ` Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Andreas Karlsson @ 2018-01-30 23:52 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Stephen Frost <sfrost@snowman.net>; David Steele <david@pgmasters.net>; Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; Tom Lane <tgl@sss.pgh.pa.us>; Hans-Jürgen Schönig <hs@cybertec.at>
On 01/21/2018 10:36 PM, Mark Rofail wrote:
> == The @>> operator
>
> A previous version of your patch added the "anyelement <<@ anyarray"
> operator to avoid having to build arrays, but that part was reverted
> due to a bug.
>
> I am not expert on the gin code, but as far as I can tell it would
> be relatively simple to fix that bug. Just allocate an array of
> Datums of length one where you put the element you are searching for
> (or maybe a copy of it).
>
> The @>> is now restored and functioning correctly, all issues with
> contrib libraries has been resolved
Hi,
I looked some at your anyarray @>> anyelement code and sadly it does not
look like the index code could work. The issue I see is that
ginqueryarrayextract() needs to make a copy of the search key but to do
so it needs to know the type of anyelement (to know if it needs to
detoast, etc). But there is as far as I can tell no way to check the
type of anyelement in this context.
Am I missing something?
Andreas
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
@ 2018-02-01 15:17 ` Mark Rofail <markm.rofail@gmail.com>
2018-02-03 15:14 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2018-02-01 15:17 UTC (permalink / raw)
To: Andreas Karlsson <andreas@proxel.se>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Alexander Korotkov <aekorotkov@gmail.com>; Stephen Frost <sfrost@snowman.net>; David Steele <david@pgmasters.net>; Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; Tom Lane <tgl@sss.pgh.pa.us>; Hans-Jürgen Schönig <hs@cybertec.at>
On Wed, Jan 31, 2018 at 1:52 AM, Andreas Karlsson <andreas@proxel.se> wrote:
> I looked some at your anyarray @>> anyelement code and sadly it does not
> look like the index code could work. The issue I see is that
> ginqueryarrayextract() needs to make a copy of the search key but to do so
> it needs to know the type of anyelement (to know if it needs to detoast,
> etc). But there is as far as I can tell no way to check the type of
> anyelement in this context.
>
since its a polymorphic function it only passes if the `anyarray` is the
same type of the `anyelement` so we are sure they are the same type. Can't
we get the type from the anyarray ? the type is already stored in `
arr_type`.
Best Regards,
Mark Rofail
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2018-02-03 15:14 ` Andreas Karlsson <andreas@proxel.se>
2018-02-03 15:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Andreas Karlsson @ 2018-02-03 15:14 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Alexander Korotkov <aekorotkov@gmail.com>; Stephen Frost <sfrost@snowman.net>; David Steele <david@pgmasters.net>; Erik Rijkers <er@xs4all.nl>; Robert Haas <robertmhaas@gmail.com>; pgsql-hackers; Tom Lane <tgl@sss.pgh.pa.us>; Hans-Jürgen Schönig <hs@cybertec.at>
On 02/01/2018 04:17 PM, Mark Rofail wrote:
> since its a polymorphic function it only passes if the `anyarray` is the
> same type of the `anyelement` so we are sure they are the same type.
> Can't we get the type from the anyarray ? the type is already stored in
> ` arr_type`.
In theory, yes, but I saw no obvious way to get it without major changes
to the code. Unless I am missing something, of course.
If I am right my recommendation for getting this patch in is to
initially skip the new operators and go back to the version of the patch
which uses @>.
Andreas
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-03 15:14 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
@ 2018-02-03 15:19 ` Mark Rofail <markm.rofail@gmail.com>
2018-02-04 21:58 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2018-02-03 15:19 UTC (permalink / raw)
To: Andreas Karlsson <andreas@proxel.se>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; David Steele <david@pgmasters.net>; Erik Rijkers <er@xs4all.nl>; Hans-Jürgen Schönig <hs@cybertec.at>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers
On Sat, 3 Feb 2018 at 5:14 pm, Andreas Karlsson <andreas@proxel.se> wrote:
> If I am right my recommendation for getting this patch in is to
> initially skip the new operators and go back to the version of the patch
> which uses @>.
We really need an initial patch to get accepted and postpone anything that
won't affect the core mechanics of the patch.
So yes, I move towards delaying the introduction of @>> to the patch.
A new patch will be ready by tomorrow.
Besr Regards,
Mark Rofail
>
>
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-03 15:14 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-03 15:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-04 21:58 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2018-02-05 19:46 ` Andreas Karlsson <andreas@proxel.se>
2018-02-06 10:15 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Andreas Karlsson @ 2018-02-05 19:46 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; David Steele <david@pgmasters.net>; Erik Rijkers <er@xs4all.nl>; Hans-Jürgen Schönig <hs@cybertec.at>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers
The patch looks good to me now other than some smaller issues, mostly in
the documentation. If you fix these I will set it as ready for
committer, and let a committer chime in on the casting logic which we
both find a bit ugly.
== Comments
The only a bit bigger issue I see is the error messages. Can they be
improved?
For example:
CREATE TABLE t1 (a int, b int, PrIMARY KEY (a, b));
CREATE TABLE t2 (a int, bs int8[], FOREIGN KEY (a, EACH ELEMENT OF bs)
REFERENCES t1 (a, b));
INSERT INTO t2 VALUES (0, ARRAY[1, 2]);
Results in:
ERROR: insert or update on table "t2" violates foreign key constraint
"t2_a_fkey"
DETAIL: Key (a, bs)=(0, {1,2}) is not present in table "t1".
Would it be possible to make the DETAIL something like the below
instead? Do you think my suggested error message is clear?
I am imaging something like the below as a patch. Does it look sane? The
test cases need to be updated at least.
diff --git a/src/backend/utils/adt/ri_triggers.c
b/src/backend/utils/adt/ri_triggers.c
index 402bde19d4..9dc7c9812c 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -3041,6 +3041,10 @@ ri_ReportViolation(const RI_ConstraintInfo *riinfo,
appendStringInfoString(&key_names, ", ");
appendStringInfoString(&key_values, ", ");
}
+
+ if (riinfo->fk_reftypes[idx] == FKCONSTR_REF_EACH_ELEMENT)
+ appendStringInfoString(&key_names, "EACH ELEMENT OF ");
+
appendStringInfoString(&key_names, name);
appendStringInfoString(&key_values, val);
}
DETAIL: Key (a, EACH ELEMENT OF bs)=(0, {1,2}) is not present in table
"t1".
- REFERENCES <replaceable class="parameter">reftable</replaceable> [ (
<replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH FULL
| MATCH PARTIAL | MATCH SIMPLE ]
+ [EACH ELEMENT OF] REFERENCES <replaceable
class="parameter">reftable</replaceable> [ ( <replaceable
class="parameter">refcolumn</replaceable> ) ] [ MATCH FULL | MATCH
PARTIAL | MATCH SIMPLE ]
I think this documentation in doc/src/sgml/ref/create_table.sgml should
be removed since it is no longer correct.
+ <para>
+ Foreign Key Arrays are an extension
+ of PostgreSQL and are not included in the SQL standard.
+ </para>
This pargraph and some of the others you added to
doc/src/sgml/ref/create_table.sgml are strangely line wrapped.
+ <varlistentry>
+ <term><literal>ON DELETE CASCADE</literal></term>
+ <listitem>
+ <para>
+ Same as standard foreign key constraints. Deletes the entire
array.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
I thought this was no longer supported.
+ however, this syntax will cast ftest1 to int4 upon RI checks,
thus defeating the
+ purpose of the index.
There is a minor indentation error on these lines.
+
+ <para>
+ Array <literal>ELEMENT</literal> foreign keys and the <literal>ELEMENT
+ REFERENCES</literal> clause are a
<productname>PostgreSQL</productname>
+ extension.
+ </para>
We do not have any ELEMENT REFERENCES clause.
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("foreign key constraint \"%s\" "
- "cannot be implemented",
- fkconstraint->conname),
- errdetail("Key columns \"%s\" and \"%s\" "
- "are of incompatible types: %s and %s.",
- strVal(list_nth(fkconstraint->fk_attrs, i)),
- strVal(list_nth(fkconstraint->pk_attrs, i)),
- format_type_be(fktype),
- format_type_be(pktype))));
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("foreign key constraint \"%s\" "
+ "cannot be implemented",
+ fkconstraint->conname),
+ errdetail("Key columns \"%s\" and \"%s\" "
+ "are of incompatible types: %s and %s.",
+ strVal(list_nth(fkconstraint->fk_attrs, i)),
+ strVal(list_nth(fkconstraint->pk_attrs, i)),
+ format_type_be(fktype),
+ format_type_be(pktype))));
It seems like you accidentally change the indentation here,
Andreas
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-03 15:14 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-03 15:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-04 21:58 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-05 19:46 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-06 10:15 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2018-02-07 03:07 ` Andreas Karlsson <andreas@proxel.se>
2018-03-07 10:43 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
0 siblings, 1 reply; 200+ messages in thread
From: Andreas Karlsson @ 2018-02-07 03:07 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; David Steele <david@pgmasters.net>; Erik Rijkers <er@xs4all.nl>; Hans-Jürgen Schönig <hs@cybertec.at>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers
On 02/06/2018 11:15 AM, Mark Rofail wrote:
> A new patch including all the fixes is ready.
>
> Can you give the docs another look. I re-wrapped, re-indented and
> changed all `Foreign Key Arrays` to `Array Element Foreign Keys` for
> consistency.
Looks good to me so set it to ready for committer. I still feel the type
casting logic is a bit ugly but I am not sure if it can be improved much.
Andreas
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-03 15:14 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-03 15:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-04 21:58 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-05 19:46 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-06 10:15 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-07 03:07 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-03-07 10:43 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
@ 2018-03-26 14:13 ` David Steele <david@pgmasters.net>
2018-03-26 20:50 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: David Steele @ 2018-03-26 14:13 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@alvh.no-ip.org>; Mark Rofail <markm.rofail@gmail.com>; +Cc: Andreas Karlsson <andreas@proxel.se>; Alexander Korotkov <aekorotkov@gmail.com>; Erik Rijkers <er@xs4all.nl>; Hans-Jürgen Schönig <hs@cybertec.at>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers
On 3/7/18 5:43 AM, Alvaro Herrera wrote:
>
> At this point I'm not sure what to do with this patch. It needs a lot
> of further work, for which I don't have time now, and given the pressure
> we're under I think we should punt it to the next dev cycle.
>
> Here's a rebased pgindented version. I renamed the regression test. I
> didn't touch anything otherwise.
I have changed this entry to Waiting on Author in case Mark wants to
comment. At the end of the CF it will be Returned with Feedback.
Regards,
--
-David
david@pgmasters.net
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-03 15:14 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-03 15:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-04 21:58 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-05 19:46 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-06 10:15 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-07 03:07 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-03-07 10:43 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-03-26 14:13 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
@ 2018-03-26 20:50 ` Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2018-03-26 20:50 UTC (permalink / raw)
To: David Steele <david@pgmasters.net>; Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Andreas Karlsson <andreas@proxel.se>; Alexander Korotkov <aekorotkov@gmail.com>; Erik Rijkers <er@xs4all.nl>; Hans-Jürgen Schönig <hs@cybertec.at>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers
On 3/7/18 5:43 AM, Alvaro Herrera wrote:
I searched for the new GIN operator so that I
> could brush it up for commit ahead of the rest -- only to find out that
> it was eviscerated from the patch between v5 and v5.1.
>
> The latest version of the patch which contained the new GIN operator is
version `*Array-ELEMENT-foreign-key-v6.patch
<https://www.postgresql.org/message-id/attachment/58007/Array-ELEMENT-foreign-key-v6.patch>*`
which works fine and passed all the regression tests at the time
(2018-01-21). We abandoned the GIN operator since it couldn't follow the
same logic as the rest of GIN operators use since it operates on a Datum
not an array. Not because of any error.
just as Andreas said
On Wed, Jan 31, 2018 at 1:52 AM, Andreas Karlsson
<andreas(at)proxel(dot)se> wrote:
The issue I see is that
> ginqueryarrayextract() needs to make a copy of the search key but to do
> so it needs to know the type of anyelement (to know if it needs to
> detoast, etc). But there is as far as I can tell no way to check the
> type of anyelement in this context.
>
>
If there is any way to obtain a copy of the datum I would be more than
happy to integrate the GIN operator to the patch.
Best.
Mark Rofail
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-03 15:14 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-03 15:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-04 21:58 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-05 19:46 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-06 10:15 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-07 03:07 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-03-07 10:43 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-03-26 14:13 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-03-26 20:50 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2018-04-10 13:47 ` David Steele <david@pgmasters.net>
2018-04-10 13:57 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: David Steele @ 2018-04-10 13:47 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Andreas Karlsson <andreas@proxel.se>; Alexander Korotkov <aekorotkov@gmail.com>; Erik Rijkers <er@xs4all.nl>; Hans-Jürgen Schönig <hs@cybertec.at>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers
Hi Mark,
On 3/26/18 4:50 PM, Mark Rofail wrote:
> On 3/7/18 5:43 AM, Alvaro Herrera wrote:
>
> I searched for the new GIN operator so that I
> could brush it up for commit ahead of the rest -- only to find out that
> it was eviscerated from the patch between v5 and v5.1.
>
> The latest version of the patch which contained the new GIN operator is
> version `*Array-ELEMENT-foreign-key-v6.patch
> <https://www.postgresql.org/message-id/attachment/58007/Array-ELEMENT-foreign-key-v6.patch>*`
> which works fine and passed all the regression tests at the time
> (2018-01-21). We abandoned the GIN operator since it couldn't follow the
> same logic as the rest of GIN operators use since it operates on a Datum
> not an array. Not because of any error.
>
> just as Andreas said
>
> On Wed, Jan 31, 2018 at 1:52 AM, Andreas Karlsson <andreas(at)proxel(dot)se> wrote:
>
> The issue I see is that
> ginqueryarrayextract() needs to make a copy of the search key but to do
> so it needs to know the type of anyelement (to know if it needs to
> detoast, etc). But there is as far as I can tell no way to check the
> type of anyelement in this context.
>
>
> If there is any way to obtain a copy of the datum I would be more than
> happy to integrate the GIN operator to the patch.
Since you have expressed a willingness to continue work on this patch I
have moved it to the next CF in Waiting on Author state.
You should produce a new version by then that addresses Alvaro's
concerns and I imagine that will require quite a bit of discussion and
work. Everyone is a bit fatigued at the moment so it would best to hold
off on that for a while.
Regards,
--
-David
david@pgmasters.net
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-03 15:14 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-03 15:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-04 21:58 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-05 19:46 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-06 10:15 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-07 03:07 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-03-07 10:43 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-03-26 14:13 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-03-26 20:50 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
@ 2018-04-10 13:57 ` Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:59 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2018-04-10 13:57 UTC (permalink / raw)
To: David Steele <david@pgmasters.net>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Andreas Karlsson <andreas@proxel.se>; Alexander Korotkov <aekorotkov@gmail.com>; Erik Rijkers <er@xs4all.nl>; Hans-Jürgen Schönig <hs@cybertec.at>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers
Hello David,
On Tue, Apr 10, 2018 at 3:47 PM, David Steele <david@pgmasters.net> wrote:
> You should produce a new version by then that addresses Alvaro's
> concerns and I imagine that will require quite a bit of discussion and
> work.
I'll get working.
I'll produce a patch with two alternate versions, one with and one without
the GIN operators.
On 3/7/18 5:43 AM, Alvaro Herrera wrote:
> so the performance was measured to see if the GIN operator was
> worth it, and the numbers are pretty confusing (the test don't seem
> terribly exhaustive; some numbers go up, some go down, it doesn't appear
> that the tests were run more than once for each case therefore the
> numbers are pretty noisy
>
Any ideas how to perform more exhaustive tests ?
On 3/26/18 4:50 PM, Mark Rofail wrote:
> > On Wed, Jan 31, 2018 at 1:52 AM, Andreas Karlsson
> <andreas(at)proxel(dot)se> wrote:
> >
> > The issue I see is that
> > ginqueryarrayextract() needs to make a copy of the search key but to
> do
> > so it needs to know the type of anyelement (to know if it needs to
> > detoast, etc). But there is as far as I can tell no way to check the
> > type of anyelement in this context.
> >
> > If there is any way to obtain a copy of the datum I would be more than
> > happy to integrate the GIN operator to the patch.
as I said before we need a way to obtain a copy of a datum to comply with
the context of ginqueryarrayextract()
Best Regards
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-03 15:14 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-03 15:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-04 21:58 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-05 19:46 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-06 10:15 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-07 03:07 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-03-07 10:43 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-03-26 14:13 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-03-26 20:50 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-04-10 13:57 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2018-04-10 13:59 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-04-10 14:10 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Alvaro Herrera @ 2018-04-10 13:59 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: David Steele <david@pgmasters.net>; Andreas Karlsson <andreas@proxel.se>; Alexander Korotkov <aekorotkov@gmail.com>; Erik Rijkers <er@xs4all.nl>; Hans-Jürgen Schönig <hs@cybertec.at>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers
Mark Rofail wrote:
> Hello David,
>
> On Tue, Apr 10, 2018 at 3:47 PM, David Steele <david@pgmasters.net> wrote:
>
> > You should produce a new version by then that addresses Alvaro's
> > concerns and I imagine that will require quite a bit of discussion and
> > work.
>
> I'll get working.
> I'll produce a patch with two alternate versions, one with and one without
> the GIN operators.
I'd rather see one patch with just the GIN operator (you may think it's
a very small patch, but that's only because you forget to add
documentation to it and a few extensive tests to ensure it works well);
then the array-fk stuff as a follow-on patch.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-03 15:14 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-03 15:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-04 21:58 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-05 19:46 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-06 10:15 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-07 03:07 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-03-07 10:43 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-03-26 14:13 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-03-26 20:50 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-04-10 13:57 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:59 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
@ 2018-04-10 14:10 ` Mark Rofail <markm.rofail@gmail.com>
2018-04-10 14:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2018-04-10 14:10 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: David Steele <david@pgmasters.net>; Andreas Karlsson <andreas@proxel.se>; Alexander Korotkov <aekorotkov@gmail.com>; Erik Rijkers <er@xs4all.nl>; Hans-Jürgen Schönig <hs@cybertec.at>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers
On Tue, Apr 10, 2018 at 3:59 PM, Alvaro Herrera <alvherre@alvh.no-ip.org>
wrote:
>
> documentation to it and a few extensive tests to ensure it works well);
I think the existing regression tests verify that the patch works as
expectations, correct?
We need more *exhaustive* tests to test performance, not functionality.
Regards
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-03 15:14 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-03 15:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-04 21:58 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-05 19:46 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-06 10:15 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-07 03:07 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-03-07 10:43 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-03-26 14:13 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-03-26 20:50 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-04-10 13:57 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:59 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-04-10 14:10 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2018-04-10 14:17 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-04-11 09:16 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Alvaro Herrera @ 2018-04-10 14:17 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: David Steele <david@pgmasters.net>; Andreas Karlsson <andreas@proxel.se>; Alexander Korotkov <aekorotkov@gmail.com>; Erik Rijkers <er@xs4all.nl>; Hans-Jürgen Schönig <hs@cybertec.at>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers
Mark Rofail wrote:
> On Tue, Apr 10, 2018 at 3:59 PM, Alvaro Herrera <alvherre@alvh.no-ip.org>
> wrote:
> >
> > documentation to it and a few extensive tests to ensure it works well);
>
> I think the existing regression tests verify that the patch works as
> expectations, correct?
I meant for the GIN operator. (Remember, these are two patches, and each
of them needs its own tests.)
> We need more *exhaustive* tests to test performance, not functionality.
True. So my impression from the numbers you posted last time is that
you need to run each measurement case several times, and provide
averages/ stddevs/etc for the resulting numbers, and see about outliers
(maybe throw them away, or maybe they indicate some problem in the test
or in the code); then we can make an informed decision about whether the
variations between the several different scenarios are real improvements
(or pessimizations) or just measurement noise.
In particular: it seemed to me that you decided to throw away the idea
of the new GIN operator without sufficient evidence that it was
unnecessary.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-03 15:14 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-03 15:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-04 21:58 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-05 19:46 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-06 10:15 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-07 03:07 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-03-07 10:43 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-03-26 14:13 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-03-26 20:50 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-04-10 13:57 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:59 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-04-10 14:10 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 14:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
@ 2018-04-11 09:16 ` Mark Rofail <markm.rofail@gmail.com>
2018-04-11 13:29 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2018-04-11 09:16 UTC (permalink / raw)
To: pgsql-hackers; +Cc: David Steele <david@pgmasters.net>; Andreas Karlsson <andreas@proxel.se>; Alexander Korotkov <aekorotkov@gmail.com>; Erik Rijkers <er@xs4all.nl>; Hans-Jürgen Schönig <hs@cybertec.at>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; Tom Lane <tgl@sss.pgh.pa.us>
On Tue, 10 Apr 2018 at 4:17 pm, Alvaro Herrera <alvherre@alvh.no-ip.org>
wrote:
> Mark Rofail wrote:
> I meant for the GIN operator. (Remember, these are two patches, and each
> of them needs its own tests.)
Yes, you are right. I have been dealing with the code as a single patch
that I almost forgot.
True. So my impression from the numbers you posted last time is that
> you need to run each measurement case several times, and provide
> averages/ stddevs/etc for the resulting numbers, and see about outliers
> (maybe throw them away, or maybe they indicate some problem in the test
> or in the code); then we can make an informed decision about whether the
> variations between the several different scenarios are real improvements
> (or pessimizations) or just measurement noise.
I'd rather just throw away the previous results and start over with new
performance tests. However, like I showed you, it was my first time to
write performance tests. If there's something I could use as a reference
that would help me so much.
>
> In particular: it seemed to me that you decided to throw away the idea
> of the new GIN operator without sufficient evidence that it was
> unnecessary.
I have to admit to that. But in my defence @> is also GIN indexable so the
only difference in performance between 'anyarray @>> anyelement' and
'anyarray @> ARRAY [anyelement]' is the delay caused by the ARRAY[]
operation theoretically.
I apologise, however, I needed more evidence to support my claims.
Regards
On Tue, Apr 10, 2018 at 4:17 PM, Alvaro Herrera <alvherre@alvh.no-ip.org>
wrote:
> Mark Rofail wrote:
> > On Tue, Apr 10, 2018 at 3:59 PM, Alvaro Herrera <alvherre@alvh.no-ip.org
> >
> > wrote:
> > >
> > > documentation to it and a few extensive tests to ensure it works well);
> >
> > I think the existing regression tests verify that the patch works as
> > expectations, correct?
>
> I meant for the GIN operator. (Remember, these are two patches, and each
> of them needs its own tests.)
>
> > We need more *exhaustive* tests to test performance, not functionality.
>
> True. So my impression from the numbers you posted last time is that
> you need to run each measurement case several times, and provide
> averages/ stddevs/etc for the resulting numbers, and see about outliers
> (maybe throw them away, or maybe they indicate some problem in the test
> or in the code); then we can make an informed decision about whether the
> variations between the several different scenarios are real improvements
> (or pessimizations) or just measurement noise.
>
> In particular: it seemed to me that you decided to throw away the idea
> of the new GIN operator without sufficient evidence that it was
> unnecessary.
>
> --
> Álvaro Herrera https://www.2ndQuadrant.com/
> PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-03 15:14 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-03 15:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-04 21:58 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-05 19:46 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-06 10:15 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-07 03:07 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-03-07 10:43 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-03-26 14:13 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-03-26 20:50 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-04-10 13:57 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:59 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-04-10 14:10 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 14:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-04-11 09:16 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2018-04-11 13:29 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-05-16 08:31 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Alvaro Herrera @ 2018-04-11 13:29 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: pgsql-hackers; David Steele <david@pgmasters.net>; Andreas Karlsson <andreas@proxel.se>; Alexander Korotkov <aekorotkov@gmail.com>; Erik Rijkers <er@xs4all.nl>; Hans-Jürgen Schönig <hs@cybertec.at>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; Tom Lane <tgl@sss.pgh.pa.us>
Mark Rofail wrote:
> > In particular: it seemed to me that you decided to throw away the idea
> > of the new GIN operator without sufficient evidence that it was
> > unnecessary.
>
> I have to admit to that. But in my defence @> is also GIN indexable so the
> only difference in performance between 'anyarray @>> anyelement' and
> 'anyarray @> ARRAY [anyelement]' is the delay caused by the ARRAY[]
> operation theoretically.
I think I need to review Tom's bounce-for-rework email
https://postgr.es/m/28389.1351094795@sss.pgh.pa.us
to respond to this intelligently. Tom mentioned @> there but there was
a comment about the comparison semantics used by that operator, so I'm
unclear on whether or not that issue has been fixed.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-03 15:14 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-03 15:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-04 21:58 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-05 19:46 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-06 10:15 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-07 03:07 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-03-07 10:43 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-03-26 14:13 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-03-26 20:50 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-04-10 13:57 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:59 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-04-10 14:10 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 14:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-04-11 09:16 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-11 13:29 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
@ 2018-05-16 08:31 ` Mark Rofail <markm.rofail@gmail.com>
2018-05-27 15:31 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2018-05-16 08:31 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: pgsql-hackers; David Steele <david@pgmasters.net>; Andreas Karlsson <andreas@proxel.se>; Alexander Korotkov <aekorotkov@gmail.com>; Erik Rijkers <er@xs4all.nl>; Hans-Jürgen Schönig <hs@cybertec.at>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; Tom Lane <tgl@sss.pgh.pa.us>
I was wondering if anyone knows the proper way to write a benchmarking test
for the @>> operator. I used the below script in my previous attempt
https://gist.github.com/markrofail/174ed370a2f2ac24800fde2fc27e2d38
The script does the following steps:
1. Generate Table A with 5 rows
2. Generate Table B with n rows such as:
every row of Table B is an array of IDs referencing rows in Table A.
The tests we ran previously had Table B up to 1 million rows and the
results can be seen here :
https://www.postgresql.org/message-id/CAJvoCusMuLnYZUbwTBKt%2Bp6bB9GwiTqF95OsQFHXixJj3LkxVQ%40mail.g...
How would we change this so it would be more exhaustive and accurate?
Regards,
Mark Rofail
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-03 15:14 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-03 15:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-04 21:58 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-05 19:46 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-06 10:15 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-07 03:07 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-03-07 10:43 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-03-26 14:13 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-03-26 20:50 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-04-10 13:57 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:59 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-04-10 14:10 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 14:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-04-11 09:16 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-11 13:29 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-05-16 08:31 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-05-27 15:31 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2018-08-11 03:20 ` Mark Rofail <markm.rofail@gmail.com>
2018-10-02 05:13 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Michael Paquier <michael@paquier.xyz>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2018-08-11 03:20 UTC (permalink / raw)
To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Alexander Korotkov <aekorotkov@gmail.com>; Andreas Karlsson <andreas@proxel.se>; David Steele <david@pgmasters.net>; Erik Rijkers <er@xs4all.nl>; Hans-Jürgen Schönig <hs@cybertec.at>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers
I am still having problems rebasing this patch. I can not figure it out on
my own.
On Sun, 27 May 2018 at 5:31 pm, Mark Rofail <markm.rofail@gmail.com> wrote:
> issue 1: `pg_constraint.c:564`
> I need to check that `conppeqop` is not null and copy it but I don't know
> how to check its type since its a char*
>
> issue 2: `matview.c:768`
> I need to pass `fkreftype` but I don't have it in the rest of the functIon
>
> On Wed, May 16, 2018 at 10:31 AM, Mark Rofail <markm.rofail@gmail.com>
> wrote:
>
>> I was wondering if anyone knows the proper way to write a benchmarking
>> test for the @>> operator. I used the below script in my previous attempt
>> https://gist.github.com/markrofail/174ed370a2f2ac24800fde2fc27e2d38
>> The script does the following steps:
>>
>> 1. Generate Table A with 5 rows
>> 2. Generate Table B with n rows such as:
>> every row of Table B is an array of IDs referencing rows in Table A.
>>
>> The tests we ran previously had Table B up to 1 million rows and the
>> results can be seen here :
>>
>> https://www.postgresql.org/message-id/CAJvoCusMuLnYZUbwTBKt%2Bp6bB9GwiTqF95OsQFHXixJj3LkxVQ%40mail.g...
>>
>> How would we change this so it would be more exhaustive and accurate?
>>
>> Regards,
>> Mark Rofail
>>
>>
>
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-03 15:14 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-03 15:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-04 21:58 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-05 19:46 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-06 10:15 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-07 03:07 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-03-07 10:43 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-03-26 14:13 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-03-26 20:50 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-04-10 13:57 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:59 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-04-10 14:10 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 14:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-04-11 09:16 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-11 13:29 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-05-16 08:31 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-05-27 15:31 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-08-11 03:20 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
@ 2018-10-02 05:13 ` Michael Paquier <michael@paquier.xyz>
2021-01-23 12:34 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
0 siblings, 1 reply; 200+ messages in thread
From: Michael Paquier @ 2018-10-02 05:13 UTC (permalink / raw)
To: Mark Rofail <markm.rofail@gmail.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Alexander Korotkov <aekorotkov@gmail.com>; Andreas Karlsson <andreas@proxel.se>; David Steele <david@pgmasters.net>; Erik Rijkers <er@xs4all.nl>; Hans-Jürgen Schönig <hs@cybertec.at>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers
On Sat, Aug 11, 2018 at 05:20:57AM +0200, Mark Rofail wrote:
> I am still having problems rebasing this patch. I can not figure it out on
> my own.
Okay, it's been a couple of months since this last email, and nothing
has happened, so I am marking it as returned with feedback.
--
Michael
Attachments:
signature.asc (application/pgp-signature, 832B) download
^ permalink raw reply [nested|flat] 200+ messages in thread
* Re: [HACKERS] GSoC 2017: Foreign Key Arrays
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:43 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 21:14 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-19 20:08 ` Re: GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-21 13:56 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Re: GSoC 2017: Foreign Key Arrays Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Re: GSoC 2017: Foreign Key Arrays Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Re: GSoC 2017: Foreign Key Arrays Alexander Korotkov <aekorotkov@gmail.com>
2017-08-19 17:49 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Re: GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Re: GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 19:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-03 15:14 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-03 15:19 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-04 21:58 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-05 19:46 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-02-06 10:15 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-02-07 03:07 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Andreas Karlsson <andreas@proxel.se>
2018-03-07 10:43 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-03-26 14:13 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-03-26 20:50 ` Re: Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:47 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays David Steele <david@pgmasters.net>
2018-04-10 13:57 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:59 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-04-10 14:10 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-10 14:17 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-04-11 09:16 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-04-11 13:29 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-05-16 08:31 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-05-27 15:31 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-08-11 03:20 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2018-10-02 05:13 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Michael Paquier <michael@paquier.xyz>
2021-01-23 12:34 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2021-01-23 12:38 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2021-01-23 16:40 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Zhihong Yu <zyu@yugabyte.com>
2021-01-24 08:22 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2021-01-24 10:11 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Joel Jacobson <joel@compiler.org>
@ 2021-01-24 10:21 ` Mark Rofail <markm.rofail@gmail.com>
2021-01-24 11:00 ` Re: [HACKERS] GSoC 2017: Foreign Key Arrays Joel Jacobson <joel@compiler.org>
0 siblings, 1 reply; 200+ messages in thread
From: Mark Rofail @ 2021-01-24 10:21 UTC (permalink / raw)
To: Joel Jacobson <joel@compiler.org>; +Cc: Zhihong Yu <zyu@yugabyte.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; Alexander Korotkov <aekorotkov@gmail.com>; Andreas Karlsson <andreas@proxel.se>; David Steele <david@pgmasters.net>; Erik Rijkers <er@xs4all.nl>; Hans-Jürgen Schönig <hs@cybertec.at>; Robert Haas <robertmhaas@gmail.com>; Stephen Frost <sfrost@snowman.net>; Tom Lane <tgl@sss.pgh.pa.us>; pgsql-hackers; Michael Paquier <michael@paquier.xyz>
Hello Joel,
On Sun, Jan 24, 2021 at 12:11 PM Joel Jacobson <joel@compiler.org> wrote:
> HINT: No operator matches the given name and argument types. You might
> need to add explicit type casts.
> QUERY: SELECT 1 WHERE (SELECT pg_catalog.count(DISTINCT y) FROM
> pg_catalog.unnest($1) y) OPERATOR(pg_catalog.=) (SELECT pg_catalog.count(*)
> FROM (SELECT 1 FROM ONLY "catalog_clone"."pg_type" x WHERE $1
> OPERATOR(pg_catalog. @>) ARRAY["oid"] FOR KEY SHARE OF x) z)
>
> It seems to me there is some type conversion between oidvector and oid[]
> that isn't working properly?
>
This seems to be a type casting problem indeed. The coercion part of the
patch found in "ruleutils.c:11438-11491" is the culprit, is there a cleaner
way to achieve this?
I am aware of the problem and will try to fix it, but I assume this is
because of "Array-containselem-gin-v1.patch" can you give the v13 patch a
try alone?
I will work on a fix and send it soon.
/Mark
^ permalink raw reply [nested|flat] 200+ messages in thread
110 further messages in this thread omitted from this page (render size limit).
Use the per-message pages, or the t.mbox.gz byte stream, for the complete thread.
end of thread, other threads:[~2021-10-05 19:15 UTC | newest]
Thread overview: 200+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2017-05-22 23:51 GSoC 2017: Foreign Key Arrays Mark Rofail <markm.rofail@gmail.com>
2017-05-24 15:12 ` Robert Haas <robertmhaas@gmail.com>
2017-05-29 23:18 ` Mark Rofail <markm.rofail@gmail.com>
2017-05-30 13:02 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-06-02 22:28 ` Mark Rofail <markm.rofail@gmail.com>
2017-06-10 22:35 ` Mark Rofail <markm.rofail@gmail.com>
2017-06-17 21:41 ` Mark Rofail <markm.rofail@gmail.com>
2017-06-18 20:16 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-06-19 22:02 ` Mark Rofail <markm.rofail@gmail.com>
2017-06-19 22:19 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2017-06-25 23:26 ` Mark Rofail <markm.rofail@gmail.com>
2017-06-26 16:44 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-06-28 21:49 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-03 20:34 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-04 01:06 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-06 02:02 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-08 23:35 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-09 00:38 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-07-09 10:11 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-09 17:42 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-07-11 22:26 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:27 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-11 22:53 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-12 12:30 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:04 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-14 22:40 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-17 23:24 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-17 23:47 ` Enrique Meneses <emmeneses@gmail.com>
2017-07-18 17:43 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-07-18 17:48 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-18 17:51 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-18 21:13 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-18 21:14 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 11:56 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-19 12:08 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-19 17:28 ` Robert Haas <robertmhaas@gmail.com>
2017-07-19 18:29 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-19 19:02 ` Robert Haas <robertmhaas@gmail.com>
2017-07-19 20:08 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-19 20:46 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-21 13:56 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-07-22 21:50 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-23 02:41 ` Robert Haas <robertmhaas@gmail.com>
2017-07-24 20:50 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-24 20:51 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:08 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:25 ` Erik Rijkers <er@xs4all.nl>
2017-07-24 21:31 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-24 21:44 ` Erik Rijkers <er@xs4all.nl>
2017-07-24 22:16 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-27 00:31 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-27 10:54 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 12:07 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-27 17:30 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-07-27 19:15 ` Mark Rofail <markm.rofail@gmail.com>
2017-08-19 17:49 ` Mark Rofail <markm.rofail@gmail.com>
2017-08-21 13:43 ` Mark Rofail <markm.rofail@gmail.com>
2017-08-23 17:01 ` Mark Rofail <markm.rofail@gmail.com>
2017-09-13 16:41 ` Mark Rofail <markm.rofail@gmail.com>
2017-09-18 01:55 ` Andreas Karlsson <andreas@proxel.se>
2017-09-24 17:50 ` Mark Rofail <markm.rofail@gmail.com>
2017-10-30 01:24 ` Andreas Karlsson <andreas@proxel.se>
2017-11-10 00:47 ` Mark Rofail <markm.rofail@gmail.com>
2017-11-13 00:41 ` Andreas Karlsson <andreas@proxel.se>
2017-11-13 11:32 ` Mark Rofail <markm.rofail@gmail.com>
2017-11-27 01:47 ` Andreas Karlsson <andreas@proxel.se>
2017-11-29 02:02 ` Michael Paquier <michael.paquier@gmail.com>
2018-01-12 19:36 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 19:47 ` Mark Rofail <markm.rofail@gmail.com>
2018-01-12 20:28 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-01-12 21:19 ` Mark Rofail <markm.rofail@gmail.com>
2018-01-21 21:36 ` Mark Rofail <markm.rofail@gmail.com>
2018-01-30 23:52 ` Andreas Karlsson <andreas@proxel.se>
2018-02-01 15:17 ` Mark Rofail <markm.rofail@gmail.com>
2018-02-03 15:14 ` Andreas Karlsson <andreas@proxel.se>
2018-02-03 15:19 ` Mark Rofail <markm.rofail@gmail.com>
2018-02-04 21:58 ` Mark Rofail <markm.rofail@gmail.com>
2018-02-05 19:46 ` Andreas Karlsson <andreas@proxel.se>
2018-02-06 10:15 ` Mark Rofail <markm.rofail@gmail.com>
2018-02-07 03:07 ` Andreas Karlsson <andreas@proxel.se>
2018-03-07 10:43 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-03-26 14:13 ` David Steele <david@pgmasters.net>
2018-03-26 20:50 ` Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:47 ` David Steele <david@pgmasters.net>
2018-04-10 13:57 ` Mark Rofail <markm.rofail@gmail.com>
2018-04-10 13:59 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-04-10 14:10 ` Mark Rofail <markm.rofail@gmail.com>
2018-04-10 14:17 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-04-11 09:16 ` Mark Rofail <markm.rofail@gmail.com>
2018-04-11 13:29 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2018-05-16 08:31 ` Mark Rofail <markm.rofail@gmail.com>
2018-05-27 15:31 ` Mark Rofail <markm.rofail@gmail.com>
2018-08-11 03:20 ` Mark Rofail <markm.rofail@gmail.com>
2018-10-02 05:13 ` Michael Paquier <michael@paquier.xyz>
2021-01-23 12:34 ` Mark Rofail <markm.rofail@gmail.com>
2021-01-23 12:38 ` Mark Rofail <markm.rofail@gmail.com>
2021-01-23 16:40 ` Zhihong Yu <zyu@yugabyte.com>
2021-01-24 08:22 ` Mark Rofail <markm.rofail@gmail.com>
2021-01-24 10:11 ` Joel Jacobson <joel@compiler.org>
2021-01-24 10:21 ` Mark Rofail <markm.rofail@gmail.com>
2021-01-24 11:00 ` Joel Jacobson <joel@compiler.org>
2021-01-24 11:40 ` Joel Jacobson <joel@compiler.org>
2021-01-24 18:24 ` Mark Rofail <markm.rofail@gmail.com>
2021-01-24 20:46 ` Mark Rofail <markm.rofail@gmail.com>
2021-01-25 08:14 ` Joel Jacobson <joel@compiler.org>
2021-01-26 11:50 ` Joel Jacobson <joel@compiler.org>
2021-01-26 11:59 ` Mark Rofail <markm.rofail@gmail.com>
2021-01-27 03:11 ` Joel Jacobson <joel@compiler.org>
2021-01-27 09:58 ` Mark Rofail <markm.rofail@gmail.com>
2021-01-27 19:30 ` Joel Jacobson <joel@compiler.org>
2021-01-27 19:34 ` Mark Rofail <markm.rofail@gmail.com>
2021-01-27 20:37 ` Joel Jacobson <joel@compiler.org>
2021-01-27 21:36 ` Mark Rofail <markm.rofail@gmail.com>
2021-01-28 05:52 ` Joel Jacobson <joel@compiler.org>
2021-01-28 21:41 ` Mark Rofail <markm.rofail@gmail.com>
2021-01-29 06:14 ` Joel Jacobson <joel@compiler.org>
2021-02-02 12:51 ` Mark Rofail <markm.rofail@gmail.com>
2021-02-04 06:59 ` Joel Jacobson <joel@compiler.org>
2021-02-05 00:02 ` Mark Rofail <markm.rofail@gmail.com>
2021-02-05 00:25 ` Zhihong Yu <zyu@yugabyte.com>
2021-02-05 12:39 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2021-02-05 14:17 ` Mark Rofail <markm.rofail@gmail.com>
2021-02-05 14:46 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2021-02-05 15:05 ` Stephen Frost <sfrost@snowman.net>
2021-02-05 15:34 ` Mark Rofail <markm.rofail@gmail.com>
2021-02-07 14:10 ` Mark Rofail <markm.rofail@gmail.com>
2021-02-07 21:11 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2021-02-05 15:27 ` Mark Rofail <markm.rofail@gmail.com>
2021-02-07 21:17 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2021-02-08 08:40 ` Mark Rofail <markm.rofail@gmail.com>
2021-02-10 18:34 ` Joel Jacobson <joel@compiler.org>
2021-02-11 15:22 ` Joel Jacobson <joel@compiler.org>
2021-02-11 15:50 ` Mark Rofail <markm.rofail@gmail.com>
2021-02-11 21:24 ` Joel Jacobson <joel@compiler.org>
2021-02-12 19:56 ` Mark Rofail <markm.rofail@gmail.com>
2021-02-12 20:59 ` Joel Jacobson <joel@compiler.org>
2021-02-13 09:36 ` Joel Jacobson <joel@compiler.org>
2021-02-13 10:15 ` Mark Rofail <markm.rofail@gmail.com>
2021-02-13 10:12 ` Joel Jacobson <joel@compiler.org>
2021-02-13 10:49 ` Mark Rofail <markm.rofail@gmail.com>
2021-02-15 15:34 ` Joel Jacobson <joel@compiler.org>
2021-02-15 19:34 ` Mark Rofail <markm.rofail@gmail.com>
2021-02-16 07:24 ` Joel Jacobson <joel@compiler.org>
2021-02-16 10:07 ` Mark Rofail <markm.rofail@gmail.com>
2021-02-17 09:42 ` Joel Jacobson <joel@compiler.org>
2021-02-17 09:44 ` Joel Jacobson <joel@compiler.org>
2021-02-17 09:49 ` Mark Rofail <markm.rofail@gmail.com>
2021-02-21 22:23 ` Justin Pryzby <pryzby@telsasoft.com>
2021-02-23 21:52 ` Mark Rofail <markm.rofail@gmail.com>
2021-03-03 21:31 ` Mark Rofail <markm.rofail@gmail.com>
2021-03-03 22:10 ` Justin Pryzby <pryzby@telsasoft.com>
2021-03-03 22:22 ` Mark Rofail <markm.rofail@gmail.com>
2021-03-12 21:32 ` Mark Rofail <markm.rofail@gmail.com>
2021-03-12 23:11 ` Justin Pryzby <pryzby@telsasoft.com>
2021-03-13 10:08 ` Mark Rofail <markm.rofail@gmail.com>
2021-03-13 11:42 ` Mark Rofail <markm.rofail@gmail.com>
2021-03-14 20:06 ` Justin Pryzby <pryzby@telsasoft.com>
2021-03-15 15:29 ` Mark Rofail <markm.rofail@gmail.com>
2021-03-15 23:27 ` Andreas Karlsson <andreas@proxel.se>
2021-03-20 18:42 ` Mark Rofail <markm.rofail@gmail.com>
2021-03-21 02:52 ` Zhihong Yu <zyu@yugabyte.com>
2021-03-21 04:23 ` Mark Rofail <markm.rofail@gmail.com>
2021-03-27 20:01 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2021-03-27 20:21 ` Mark Rofail <markm.rofail@gmail.com>
2021-03-27 20:36 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2021-03-27 20:41 ` Mark Rofail <markm.rofail@gmail.com>
2021-03-29 20:43 ` Mark Rofail <markm.rofail@gmail.com>
2021-07-14 15:50 ` vignesh C <vignesh21@gmail.com>
2021-07-14 16:07 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2021-09-14 18:45 ` Daniel Gustafsson <daniel@yesql.se>
2021-09-14 18:54 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2021-09-14 18:59 ` Daniel Gustafsson <daniel@yesql.se>
2021-09-14 18:59 ` Mark Rofail <markm.rofail@gmail.com>
2021-10-03 17:37 ` Stefan Keller <sfkeller@gmail.com>
2021-10-05 19:15 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2021-02-13 11:35 ` Joel Jacobson <joel@compiler.org>
2021-02-13 16:10 ` Joel Jacobson <joel@compiler.org>
2021-01-28 00:32 ` Zhihong Yu <zyu@yugabyte.com>
2021-01-28 21:41 ` Mark Rofail <markm.rofail@gmail.com>
2021-01-25 00:46 ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2017-07-27 17:15 ` Erik Rijkers <er@xs4all.nl>
2017-07-27 19:08 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-28 11:19 ` Erik Rijkers <er@xs4all.nl>
2017-07-28 19:02 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-29 19:26 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-29 21:14 ` Mark Rofail <markm.rofail@gmail.com>
2017-07-29 23:59 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-30 05:26 ` Tom Lane <tgl@sss.pgh.pa.us>
2017-07-31 15:18 ` Alvaro Herrera <alvherre@2ndquadrant.com>
2017-07-31 17:20 ` Mark Rofail <markm.rofail@gmail.com>
2017-08-03 22:22 ` Mark Rofail <markm.rofail@gmail.com>
2017-08-05 20:36 ` Mark Rofail <markm.rofail@gmail.com>
2017-08-08 10:24 ` Mark Rofail <markm.rofail@gmail.com>
2017-08-08 12:25 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-08-08 13:12 ` Mark Rofail <markm.rofail@gmail.com>
2017-08-08 13:24 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-08-14 11:09 ` Mark Rofail <markm.rofail@gmail.com>
2017-08-14 20:35 ` Alexander Korotkov <aekorotkov@gmail.com>
2017-08-14 20:46 ` Tom Lane <tgl@sss.pgh.pa.us>
2017-08-19 17:47 ` Mark Rofail <markm.rofail@gmail.com>
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox