Re: GSoC 2017: Foreign Key Arrays

From: Mark Rofail <markm(dot)rofail(at)gmail(dot)com>
To: Alexander Korotkov <aekorotkov(at)gmail(dot)com>
Cc: Robert Haas <robertmhaas(at)gmail(dot)com>, "pgsql-hackers(at)postgresql(dot)org" <pgsql-hackers(at)postgresql(dot)org>, Alvaro Herrera <alvaro(dot)herrera(at)2ndquadrant(dot)com>, David Steele <david(at)pgmasters(dot)net>, Stephen Frost <sfrost(at)snowman(dot)net>
Subject: Re: GSoC 2017: Foreign Key Arrays
Date: 2017-06-02 22:28:07
Message-ID: CAJvoCus-eACvA4-eHqrG4ft38z6wd6QLpu0Vfzu3J2xFfEotCg@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

>
> 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.

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Petr Jelinek 2017-06-02 22:55:22 Re: walsender termination error messages worse in v10
Previous Message Andres Freund 2017-06-02 21:45:59 Re: walsender termination error messages worse in v10