Revert temporal primary keys and foreign keys

This feature set did not handle empty ranges correctly, and it's now
too late for PostgreSQL 17 to fix it.

The following commits are reverted:

    6db4598fcb8 Add stratnum GiST support function
    46a0cd4cefb Add temporal PRIMARY KEY and UNIQUE constraints
    86232a49a43 Fix comment on gist_stratnum_btree
    030e10ff1a3 Rename pg_constraint.conwithoutoverlaps to conperiod
    a88c800deb6 Use daterange and YMD in without_overlaps tests instead of tsrange.
    5577a71fb0c Use half-open interval notation in without_overlaps tests
    34768ee3616 Add temporal FOREIGN KEY contraints
    482e108cd38 Add test for REPLICA IDENTITY with a temporal key
    c3db1f30cba doc:  clarify PERIOD and WITHOUT OVERLAPS in CREATE TABLE
    144c2ce0cc7 Fix ON CONFLICT DO NOTHING/UPDATE for temporal indexes

Discussion: https://www.postgresql.org/message-id/d0b64a7a-dfe4-4b84-a906-c7dedfa40a3e@eisentraut.org
This commit is contained in:
Peter Eisentraut 2024-05-16 08:15:35 +02:00
parent f6ebb41831
commit 8aee330af5
47 changed files with 149 additions and 4475 deletions

View File

@ -33,14 +33,12 @@ EXTENSION = btree_gist
DATA = btree_gist--1.0--1.1.sql \ DATA = btree_gist--1.0--1.1.sql \
btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql \ btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql \
btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql \ btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql \
btree_gist--1.5--1.6.sql btree_gist--1.6--1.7.sql \ btree_gist--1.5--1.6.sql btree_gist--1.6--1.7.sql
btree_gist--1.7--1.8.sql
PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes" PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes"
REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \ REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \
time timetz date interval macaddr macaddr8 inet cidr text varchar char \ time timetz date interval macaddr macaddr8 inet cidr text varchar char \
bytea bit varbit numeric uuid not_equal enum bool partitions \ bytea bit varbit numeric uuid not_equal enum bool partitions
stratnum without_overlaps
SHLIB_LINK += $(filter -lm, $(LIBS)) SHLIB_LINK += $(filter -lm, $(LIBS))

View File

@ -1,87 +0,0 @@
/* contrib/btree_gist/btree_gist--1.7--1.8.sql */
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.8'" to load this file. \quit
CREATE FUNCTION gist_stratnum_btree(smallint)
RETURNS smallint
AS 'MODULE_PATHNAME'
LANGUAGE C IMMUTABLE PARALLEL SAFE STRICT;
ALTER OPERATOR FAMILY gist_oid_ops USING gist ADD
FUNCTION 12 (oid, oid) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_int2_ops USING gist ADD
FUNCTION 12 (int2, int2) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_int4_ops USING gist ADD
FUNCTION 12 (int4, int4) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_int8_ops USING gist ADD
FUNCTION 12 (int8, int8) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_float4_ops USING gist ADD
FUNCTION 12 (float4, float4) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_float8_ops USING gist ADD
FUNCTION 12 (float8, float8) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_timestamp_ops USING gist ADD
FUNCTION 12 (timestamp, timestamp) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_timestamptz_ops USING gist ADD
FUNCTION 12 (timestamptz, timestamptz) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_time_ops USING gist ADD
FUNCTION 12 (time, time) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_date_ops USING gist ADD
FUNCTION 12 (date, date) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_interval_ops USING gist ADD
FUNCTION 12 (interval, interval) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_cash_ops USING gist ADD
FUNCTION 12 (money, money) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_macaddr_ops USING gist ADD
FUNCTION 12 (macaddr, macaddr) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_text_ops USING gist ADD
FUNCTION 12 (text, text) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_bpchar_ops USING gist ADD
FUNCTION 12 (bpchar, bpchar) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_bytea_ops USING gist ADD
FUNCTION 12 (bytea, bytea) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_numeric_ops USING gist ADD
FUNCTION 12 (numeric, numeric) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_bit_ops USING gist ADD
FUNCTION 12 (bit, bit) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_vbit_ops USING gist ADD
FUNCTION 12 (varbit, varbit) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_inet_ops USING gist ADD
FUNCTION 12 (inet, inet) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_cidr_ops USING gist ADD
FUNCTION 12 (cidr, cidr) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_timetz_ops USING gist ADD
FUNCTION 12 (timetz, timetz) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_uuid_ops USING gist ADD
FUNCTION 12 (uuid, uuid) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_macaddr8_ops USING gist ADD
FUNCTION 12 (macaddr8, macaddr8) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_enum_ops USING gist ADD
FUNCTION 12 (anyenum, anyenum) gist_stratnum_btree (int2) ;
ALTER OPERATOR FAMILY gist_bool_ops USING gist ADD
FUNCTION 12 (bool, bool) gist_stratnum_btree (int2) ;

View File

@ -3,7 +3,6 @@
*/ */
#include "postgres.h" #include "postgres.h"
#include "access/stratnum.h"
#include "utils/builtins.h" #include "utils/builtins.h"
PG_MODULE_MAGIC; PG_MODULE_MAGIC;
@ -11,7 +10,6 @@ PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(gbt_decompress); PG_FUNCTION_INFO_V1(gbt_decompress);
PG_FUNCTION_INFO_V1(gbtreekey_in); PG_FUNCTION_INFO_V1(gbtreekey_in);
PG_FUNCTION_INFO_V1(gbtreekey_out); PG_FUNCTION_INFO_V1(gbtreekey_out);
PG_FUNCTION_INFO_V1(gist_stratnum_btree);
/************************************************** /**************************************************
* In/Out for keys * In/Out for keys
@ -53,28 +51,3 @@ gbt_decompress(PG_FUNCTION_ARGS)
{ {
PG_RETURN_POINTER(PG_GETARG_POINTER(0)); PG_RETURN_POINTER(PG_GETARG_POINTER(0));
} }
/*
* Returns the btree number for supported operators, otherwise invalid.
*/
Datum
gist_stratnum_btree(PG_FUNCTION_ARGS)
{
StrategyNumber strat = PG_GETARG_UINT16(0);
switch (strat)
{
case RTEqualStrategyNumber:
PG_RETURN_UINT16(BTEqualStrategyNumber);
case RTLessStrategyNumber:
PG_RETURN_UINT16(BTLessStrategyNumber);
case RTLessEqualStrategyNumber:
PG_RETURN_UINT16(BTLessEqualStrategyNumber);
case RTGreaterStrategyNumber:
PG_RETURN_UINT16(BTGreaterStrategyNumber);
case RTGreaterEqualStrategyNumber:
PG_RETURN_UINT16(BTGreaterEqualStrategyNumber);
default:
PG_RETURN_UINT16(InvalidStrategy);
}
}

View File

@ -1,6 +1,6 @@
# btree_gist extension # btree_gist extension
comment = 'support for indexing common datatypes in GiST' comment = 'support for indexing common datatypes in GiST'
default_version = '1.8' default_version = '1.7'
module_pathname = '$libdir/btree_gist' module_pathname = '$libdir/btree_gist'
relocatable = true relocatable = true
trusted = true trusted = true

View File

@ -1,13 +0,0 @@
-- test stratnum support func
SELECT gist_stratnum_btree(3::smallint);
gist_stratnum_btree
---------------------
0
(1 row)
SELECT gist_stratnum_btree(18::smallint);
gist_stratnum_btree
---------------------
3
(1 row)

View File

@ -1,92 +0,0 @@
-- Core must test WITHOUT OVERLAPS
-- with an int4range + daterange,
-- so here we do some simple tests
-- to make sure int + daterange works too,
-- since that is the expected use-case.
CREATE TABLE temporal_rng (
id integer,
valid_at daterange,
CONSTRAINT temporal_rng_pk PRIMARY KEY (id, valid_at WITHOUT OVERLAPS)
);
\d temporal_rng
Table "public.temporal_rng"
Column | Type | Collation | Nullable | Default
----------+-----------+-----------+----------+---------
id | integer | | not null |
valid_at | daterange | | not null |
Indexes:
"temporal_rng_pk" PRIMARY KEY (id, valid_at WITHOUT OVERLAPS)
SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conname = 'temporal_rng_pk';
pg_get_constraintdef
---------------------------------------------
PRIMARY KEY (id, valid_at WITHOUT OVERLAPS)
(1 row)
SELECT pg_get_indexdef(conindid, 0, true) FROM pg_constraint WHERE conname = 'temporal_rng_pk';
pg_get_indexdef
-------------------------------------------------------------------------------
CREATE UNIQUE INDEX temporal_rng_pk ON temporal_rng USING gist (id, valid_at)
(1 row)
INSERT INTO temporal_rng VALUES
(1, '[2000-01-01,2001-01-01)');
-- same key, doesn't overlap:
INSERT INTO temporal_rng VALUES
(1, '[2001-01-01,2002-01-01)');
-- overlaps but different key:
INSERT INTO temporal_rng VALUES
(2, '[2000-01-01,2001-01-01)');
-- should fail:
INSERT INTO temporal_rng VALUES
(1, '[2000-06-01,2001-01-01)');
ERROR: conflicting key value violates exclusion constraint "temporal_rng_pk"
DETAIL: Key (id, valid_at)=(1, [06-01-2000,01-01-2001)) conflicts with existing key (id, valid_at)=(1, [01-01-2000,01-01-2001)).
-- Foreign key
CREATE TABLE temporal_fk_rng2rng (
id integer,
valid_at daterange,
parent_id integer,
CONSTRAINT temporal_fk_rng2rng_pk PRIMARY KEY (id, valid_at WITHOUT OVERLAPS),
CONSTRAINT temporal_fk_rng2rng_fk FOREIGN KEY (parent_id, PERIOD valid_at)
REFERENCES temporal_rng (id, PERIOD valid_at)
);
\d temporal_fk_rng2rng
Table "public.temporal_fk_rng2rng"
Column | Type | Collation | Nullable | Default
-----------+-----------+-----------+----------+---------
id | integer | | not null |
valid_at | daterange | | not null |
parent_id | integer | | |
Indexes:
"temporal_fk_rng2rng_pk" PRIMARY KEY (id, valid_at WITHOUT OVERLAPS)
Foreign-key constraints:
"temporal_fk_rng2rng_fk" FOREIGN KEY (parent_id, PERIOD valid_at) REFERENCES temporal_rng(id, PERIOD valid_at)
SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conname = 'temporal_fk_rng2rng_fk';
pg_get_constraintdef
---------------------------------------------------------------------------------------
FOREIGN KEY (parent_id, PERIOD valid_at) REFERENCES temporal_rng(id, PERIOD valid_at)
(1 row)
-- okay
INSERT INTO temporal_fk_rng2rng VALUES
(1, '[2000-01-01,2001-01-01)', 1);
-- okay spanning two parent records:
INSERT INTO temporal_fk_rng2rng VALUES
(2, '[2000-01-01,2002-01-01)', 1);
-- key is missing
INSERT INTO temporal_fk_rng2rng VALUES
(3, '[2000-01-01,2001-01-01)', 3);
ERROR: insert or update on table "temporal_fk_rng2rng" violates foreign key constraint "temporal_fk_rng2rng_fk"
DETAIL: Key (parent_id, valid_at)=(3, [01-01-2000,01-01-2001)) is not present in table "temporal_rng".
-- key exist but is outside range
INSERT INTO temporal_fk_rng2rng VALUES
(4, '[2001-01-01,2002-01-01)', 2);
ERROR: insert or update on table "temporal_fk_rng2rng" violates foreign key constraint "temporal_fk_rng2rng_fk"
DETAIL: Key (parent_id, valid_at)=(2, [01-01-2001,01-01-2002)) is not present in table "temporal_rng".
-- key exist but is partly outside range
INSERT INTO temporal_fk_rng2rng VALUES
(5, '[2000-01-01,2002-01-01)', 2);
ERROR: insert or update on table "temporal_fk_rng2rng" violates foreign key constraint "temporal_fk_rng2rng_fk"
DETAIL: Key (parent_id, valid_at)=(2, [01-01-2000,01-01-2002)) is not present in table "temporal_rng".

View File

@ -50,7 +50,6 @@ install_data(
'btree_gist--1.4--1.5.sql', 'btree_gist--1.4--1.5.sql',
'btree_gist--1.5--1.6.sql', 'btree_gist--1.5--1.6.sql',
'btree_gist--1.6--1.7.sql', 'btree_gist--1.6--1.7.sql',
'btree_gist--1.7--1.8.sql',
kwargs: contrib_data_args, kwargs: contrib_data_args,
) )
@ -90,8 +89,6 @@ tests += {
'enum', 'enum',
'bool', 'bool',
'partitions', 'partitions',
'stratnum',
'without_overlaps',
], ],
}, },
} }

View File

@ -1,3 +0,0 @@
-- test stratnum support func
SELECT gist_stratnum_btree(3::smallint);
SELECT gist_stratnum_btree(18::smallint);

View File

@ -1,53 +0,0 @@
-- Core must test WITHOUT OVERLAPS
-- with an int4range + daterange,
-- so here we do some simple tests
-- to make sure int + daterange works too,
-- since that is the expected use-case.
CREATE TABLE temporal_rng (
id integer,
valid_at daterange,
CONSTRAINT temporal_rng_pk PRIMARY KEY (id, valid_at WITHOUT OVERLAPS)
);
\d temporal_rng
SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conname = 'temporal_rng_pk';
SELECT pg_get_indexdef(conindid, 0, true) FROM pg_constraint WHERE conname = 'temporal_rng_pk';
INSERT INTO temporal_rng VALUES
(1, '[2000-01-01,2001-01-01)');
-- same key, doesn't overlap:
INSERT INTO temporal_rng VALUES
(1, '[2001-01-01,2002-01-01)');
-- overlaps but different key:
INSERT INTO temporal_rng VALUES
(2, '[2000-01-01,2001-01-01)');
-- should fail:
INSERT INTO temporal_rng VALUES
(1, '[2000-06-01,2001-01-01)');
-- Foreign key
CREATE TABLE temporal_fk_rng2rng (
id integer,
valid_at daterange,
parent_id integer,
CONSTRAINT temporal_fk_rng2rng_pk PRIMARY KEY (id, valid_at WITHOUT OVERLAPS),
CONSTRAINT temporal_fk_rng2rng_fk FOREIGN KEY (parent_id, PERIOD valid_at)
REFERENCES temporal_rng (id, PERIOD valid_at)
);
\d temporal_fk_rng2rng
SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conname = 'temporal_fk_rng2rng_fk';
-- okay
INSERT INTO temporal_fk_rng2rng VALUES
(1, '[2000-01-01,2001-01-01)', 1);
-- okay spanning two parent records:
INSERT INTO temporal_fk_rng2rng VALUES
(2, '[2000-01-01,2002-01-01)', 1);
-- key is missing
INSERT INTO temporal_fk_rng2rng VALUES
(3, '[2000-01-01,2001-01-01)', 3);
-- key exist but is outside range
INSERT INTO temporal_fk_rng2rng VALUES
(4, '[2001-01-01,2002-01-01)', 2);
-- key exist but is partly outside range
INSERT INTO temporal_fk_rng2rng VALUES
(5, '[2000-01-01,2002-01-01)', 2);

View File

@ -2729,17 +2729,6 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
</para></entry> </para></entry>
</row> </row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>conperiod</structfield> <type>bool</type>
</para>
<para>
This constraint is defined with <literal>WITHOUT OVERLAPS</literal>
(for primary keys and unique constraints) or <literal>PERIOD</literal>
(for foreign keys).
</para></entry>
</row>
<row> <row>
<entry role="catalog_table_entry"><para role="column_definition"> <entry role="catalog_table_entry"><para role="column_definition">
<structfield>conkey</structfield> <type>int2[]</type> <structfield>conkey</structfield> <type>int2[]</type>

View File

@ -266,7 +266,7 @@ CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops);
<para> <para>
There are five methods that an index operator class for There are five methods that an index operator class for
<acronym>GiST</acronym> must provide, and seven that are optional. <acronym>GiST</acronym> must provide, and six that are optional.
Correctness of the index is ensured Correctness of the index is ensured
by proper implementation of the <function>same</function>, <function>consistent</function> by proper implementation of the <function>same</function>, <function>consistent</function>
and <function>union</function> methods, while efficiency (size and speed) of the and <function>union</function> methods, while efficiency (size and speed) of the
@ -289,11 +289,6 @@ CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops);
user-specified parameters. user-specified parameters.
The optional eleventh method <function>sortsupport</function> is used to The optional eleventh method <function>sortsupport</function> is used to
speed up building a <acronym>GiST</acronym> index. speed up building a <acronym>GiST</acronym> index.
The optional twelfth method <function>stratnum</function> is used to
translate well-known <literal>RT*StrategyNumber</literal>s (from
<filename>src/include/access/stratnum.h</filename>) into strategy numbers
used by the operator class. This lets the core code look up operators for
temporal constraint indexes.
</para> </para>
<variablelist> <variablelist>
@ -1168,76 +1163,6 @@ my_sortsupport(PG_FUNCTION_ARGS)
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><function>stratnum</function></term>
<listitem>
<para>
Given an <literal>RT*StrategyNumber</literal> value from
<filename>src/include/access/stratnum.h</filename>, returns a strategy
number used by this operator class for matching functionality. The
function should return <literal>InvalidStrategy</literal> if the
operator class has no matching strategy.
</para>
<para>
This is used for temporal index constraints (i.e., <literal>PRIMARY
KEY</literal> and <literal>UNIQUE</literal>). If the operator class
provides this function and it returns results for
<literal>RTEqualStrategyNumber</literal>, it can be used in the
non-<literal>WITHOUT OVERLAPS</literal> part(s) of an index constraint.
If it returns results for <literal>RTOverlapStrategyNumber</literal>,
the operator class can be used in the <literal>WITHOUT
OVERLAPS</literal> part of an index constraint.
</para>
<para>
The <acronym>SQL</acronym> declaration of the function must look like
this:
<programlisting>
CREATE OR REPLACE FUNCTION my_stratnum(integer)
RETURNS integer
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
</programlisting>
</para>
<para>
The matching code in the C module could then follow this skeleton:
<programlisting>
PG_FUNCTION_INFO_V1(my_stratnum);
Datum
my_stratnum(PG_FUNCTION_ARGS)
{
StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(1);
StrategyNumber ret = InvalidStrategy;
switch (strategy)
{
case RTEqualStrategyNumber:
ret = BTEqualStrategyNumber;
}
PG_RETURN_UINT16(ret);
}
</programlisting>
</para>
<para>
One translation function is provided by
<productname>PostgreSQL</productname>:
<literal>gist_stratnum_identity</literal> is for operator classes that
already use the <literal>RT*StrategyNumber</literal> constants. It
returns whatever is passed to it. The <literal>btree_gist</literal>
extension defines a second translation function,
<literal>gist_stratnum_btree</literal>, for operator classes that use
the <literal>BT*StrategyNumber</literal> constants.
</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
<para> <para>

View File

@ -77,10 +77,10 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
[ CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> ] [ CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> ]
{ CHECK ( <replaceable class="parameter">expression</replaceable> ) [ NO INHERIT ] | { CHECK ( <replaceable class="parameter">expression</replaceable> ) [ NO INHERIT ] |
UNIQUE [ NULLS [ NOT ] DISTINCT ] ( <replaceable class="parameter">column_name</replaceable> [, ... ] [, <replaceable class="parameter">column_name</replaceable> WITHOUT OVERLAPS ] ) <replaceable class="parameter">index_parameters</replaceable> | UNIQUE [ NULLS [ NOT ] DISTINCT ] ( <replaceable class="parameter">column_name</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> |
PRIMARY KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] [, <replaceable class="parameter">column_name</replaceable> WITHOUT OVERLAPS ] ) <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> ) ] | 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> [, ... ] [, PERIOD <replaceable class="parameter">column_name</replaceable> ] ) REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] [, PERIOD <replaceable class="parameter">column_name</replaceable> ] ) ] FOREIGN KEY ( <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 [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE <replaceable
class="parameter">referential_action</replaceable> ] [ ON UPDATE <replaceable class="parameter">referential_action</replaceable> ] } class="parameter">referential_action</replaceable> ] [ ON UPDATE <replaceable class="parameter">referential_action</replaceable> ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
@ -964,7 +964,7 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
<varlistentry id="sql-createtable-parms-unique"> <varlistentry id="sql-createtable-parms-unique">
<term><literal>UNIQUE [ NULLS [ NOT ] DISTINCT ]</literal> (column constraint)</term> <term><literal>UNIQUE [ NULLS [ NOT ] DISTINCT ]</literal> (column constraint)</term>
<term><literal>UNIQUE [ NULLS [ NOT ] DISTINCT ] ( <replaceable class="parameter">column_name</replaceable> [, ... ] [, <replaceable class="parameter">column_name</replaceable> WITHOUT OVERLAPS ] )</literal> <term><literal>UNIQUE [ NULLS [ NOT ] DISTINCT ] ( <replaceable class="parameter">column_name</replaceable> [, ... ] )</literal>
<optional> <literal>INCLUDE ( <replaceable class="parameter">column_name</replaceable> [, ...])</literal> </optional> (table constraint)</term> <optional> <literal>INCLUDE ( <replaceable class="parameter">column_name</replaceable> [, ...])</literal> </optional> (table constraint)</term>
<listitem> <listitem>
@ -978,30 +978,6 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
of these columns. of these columns.
</para> </para>
<para>
If the <literal>WITHOUT OVERLAPS</literal> option is specified for the
last column, then that column is checked for overlaps instead of
equality. In that case, the other columns of the constraint will allow
duplicates so long as the duplicates don't overlap in the
<literal>WITHOUT OVERLAPS</literal> column. (This is sometimes called a
temporal key, if the column is a range of dates or timestamps, but
PostgreSQL allows ranges over any base type.) In effect, such a
constraint is enforced with an <literal>EXCLUDE</literal> constraint
rather than a <literal>UNIQUE</literal> constraint. So for example
<literal>UNIQUE (id, valid_at WITHOUT OVERLAPS)</literal> behaves like
<literal>EXCLUDE USING GIST (id WITH =, valid_at WITH
&amp;&amp;)</literal>. The <literal>WITHOUT OVERLAPS</literal> column
must have a range or multirange type. (Technically, any type is allowed
whose default GiST opclass includes an overlaps operator. See the
<literal>stratnum</literal> support function under <xref
linkend="gist-extensibility"/> for details.) The non-<literal>WITHOUT
OVERLAPS</literal> columns of the constraint can be any type that can be
compared for equality in a GiST index. By default, only range types are
supported, but you can use other types by adding the <xref
linkend="btree-gist"/> extension (which is the expected way to use this
feature).
</para>
<para> <para>
For the purpose of a unique constraint, null values are not For the purpose of a unique constraint, null values are not
considered equal, unless <literal>NULLS NOT DISTINCT</literal> is considered equal, unless <literal>NULLS NOT DISTINCT</literal> is
@ -1023,11 +999,9 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</para> </para>
<para> <para>
Adding a unique constraint will automatically create a unique B-tree Adding a unique constraint will automatically create a unique btree
index on the column or group of columns used in the constraint. But if index on the column or group of columns used in the constraint. The
the constraint includes a <literal>WITHOUT OVERLAPS</literal> clause, it created index has the same name as the unique constraint.
will use a GiST index. The created index has the same name as the unique
constraint.
</para> </para>
<para> <para>
@ -1045,7 +1019,7 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
<varlistentry id="sql-createtable-parms-primary-key"> <varlistentry id="sql-createtable-parms-primary-key">
<term><literal>PRIMARY KEY</literal> (column constraint)</term> <term><literal>PRIMARY KEY</literal> (column constraint)</term>
<term><literal>PRIMARY KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] [, <replaceable class="parameter">column_name</replaceable> WITHOUT OVERLAPS ] )</literal> <term><literal>PRIMARY KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] )</literal>
<optional> <literal>INCLUDE ( <replaceable class="parameter">column_name</replaceable> [, ...])</literal> </optional> (table constraint)</term> <optional> <literal>INCLUDE ( <replaceable class="parameter">column_name</replaceable> [, ...])</literal> </optional> (table constraint)</term>
<listitem> <listitem>
<para> <para>
@ -1078,11 +1052,10 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</para> </para>
<para> <para>
As with a <literal>UNIQUE</literal> constraint, adding a Adding a <literal>PRIMARY KEY</literal> constraint will automatically
<literal>PRIMARY KEY</literal> constraint will automatically create a create a unique btree index on the column or group of columns used in
unique B-tree index, or GiST if <literal>WITHOUT OVERLAPS</literal> was the constraint. That index has the same name as the primary key
specified, on the column or group of columns used in the constraint. constraint.
That index has the same name as the primary key constraint.
</para> </para>
<para> <para>
@ -1151,8 +1124,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
<varlistentry id="sql-createtable-parms-references"> <varlistentry id="sql-createtable-parms-references">
<term><literal>REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">referential_action</replaceable> ] [ ON UPDATE <replaceable class="parameter">referential_action</replaceable> ]</literal> (column constraint)</term> <term><literal>REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> ) ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ ON DELETE <replaceable class="parameter">referential_action</replaceable> ] [ ON UPDATE <replaceable class="parameter">referential_action</replaceable> ]</literal> (column constraint)</term>
<term><literal>FOREIGN KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] [, PERIOD <replaceable class="parameter">column_name</replaceable> ] ) <term><literal>FOREIGN KEY ( <replaceable class="parameter">column_name</replaceable> [, ... ] )
REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] [, PERIOD <replaceable class="parameter">column_name</replaceable> ] ) ] REFERENCES <replaceable class="parameter">reftable</replaceable> [ ( <replaceable class="parameter">refcolumn</replaceable> [, ... ] ) ]
[ MATCH <replaceable class="parameter">matchtype</replaceable> ] [ MATCH <replaceable class="parameter">matchtype</replaceable> ]
[ ON DELETE <replaceable class="parameter">referential_action</replaceable> ] [ ON DELETE <replaceable class="parameter">referential_action</replaceable> ]
[ ON UPDATE <replaceable class="parameter">referential_action</replaceable> ]</literal> [ ON UPDATE <replaceable class="parameter">referential_action</replaceable> ]</literal>
@ -1168,32 +1141,7 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
primary key of the <replaceable class="parameter">reftable</replaceable> primary key of the <replaceable class="parameter">reftable</replaceable>
is used. Otherwise, the <replaceable class="parameter">refcolumn</replaceable> is used. Otherwise, the <replaceable class="parameter">refcolumn</replaceable>
list must refer to the columns of a non-deferrable unique or primary key list must refer to the columns of a non-deferrable unique or primary key
constraint or be the columns of a non-partial unique index. constraint or be the columns of a non-partial unique index. The user
</para>
<para>
If the last column is marked with <literal>PERIOD</literal>, it is
treated in a special way. While the non-<literal>PERIOD</literal>
columns are compared for equality (and there must be at least one of
them), the <literal>PERIOD</literal> column is not. Instead, the
constraint is considered satisfied if the referenced table has matching
records (based on the non-<literal>PERIOD</literal> parts of the key)
whose combined <literal>PERIOD</literal> values completely cover the
referencing record's. In other words, the reference must have a
referent for its entire duration. This column must be a range or
multirange type. In addition, the referenced table must have a primary
key or unique constraint declared with <literal>WITHOUT
OVERLAPS</literal>. Finally, if the foreign key has a PERIOD
<replaceable class="parameter">column_name</replaceable> specification
the corresponding <replaceable class="parameter">refcolumn</replaceable>,
if present, must also be marked <literal>PERIOD</literal>. If the
<replaceable class="parameter">refcolumn</replaceable> clause is omitted,
and thus the reftable's primary key constraint chosen, the primary key
must have its final column marked <literal>WITHOUT OVERLAPS</literal>.
</para>
<para>
The user
must have <literal>REFERENCES</literal> permission on the referenced must have <literal>REFERENCES</literal> permission on the referenced
table (either the whole table, or the specific referenced columns). The table (either the whole table, or the specific referenced columns). The
addition of a foreign key constraint requires a addition of a foreign key constraint requires a
@ -1267,10 +1215,6 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
values of the referencing column(s) to the new values of the values of the referencing column(s) to the new values of the
referenced columns, respectively. referenced columns, respectively.
</para> </para>
<para>
In a temporal foreign key, this option is not supported.
</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -1282,10 +1226,6 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
referencing columns, to null. A subset of columns can only be referencing columns, to null. A subset of columns can only be
specified for <literal>ON DELETE</literal> actions. specified for <literal>ON DELETE</literal> actions.
</para> </para>
<para>
In a temporal foreign key, this option is not supported.
</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -1299,10 +1239,6 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
(There must be a row in the referenced table matching the default (There must be a row in the referenced table matching the default
values, if they are not null, or the operation will fail.) values, if they are not null, or the operation will fail.)
</para> </para>
<para>
In a temporal foreign key, this option is not supported.
</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
</variablelist> </variablelist>

View File

@ -508,7 +508,7 @@
</table> </table>
<para> <para>
GiST indexes have twelve support functions, seven of which are optional, GiST indexes have eleven support functions, six of which are optional,
as shown in <xref linkend="xindex-gist-support-table"/>. as shown in <xref linkend="xindex-gist-support-table"/>.
(For more information see <xref linkend="gist"/>.) (For more information see <xref linkend="gist"/>.)
</para> </para>
@ -590,12 +590,6 @@
(optional)</entry> (optional)</entry>
<entry>11</entry> <entry>11</entry>
</row> </row>
<row>
<entry><function>stratnum</function></entry>
<entry>translate well-known strategy numbers to ones
used by the operator class (optional)</entry>
<entry>12</entry>
</row>
</tbody> </tbody>
</tgroup> </tgroup>
</table> </table>

View File

@ -21,7 +21,6 @@
#include "common/pg_prng.h" #include "common/pg_prng.h"
#include "storage/indexfsm.h" #include "storage/indexfsm.h"
#include "utils/float.h" #include "utils/float.h"
#include "utils/fmgrprotos.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/rel.h" #include "utils/rel.h"
#include "utils/snapmgr.h" #include "utils/snapmgr.h"
@ -1056,45 +1055,3 @@ gistGetFakeLSN(Relation rel)
return GetFakeLSNForUnloggedRel(); return GetFakeLSNForUnloggedRel();
} }
} }
/*
* Returns the same number that was received.
*
* This is for GiST opclasses that use the RT*StrategyNumber constants.
*/
Datum
gist_stratnum_identity(PG_FUNCTION_ARGS)
{
StrategyNumber strat = PG_GETARG_UINT16(0);
PG_RETURN_UINT16(strat);
}
/*
* Returns the opclass's private stratnum used for the given strategy.
*
* Calls the opclass's GIST_STRATNUM_PROC support function, if any,
* and returns the result.
* Returns InvalidStrategy if the function is not defined.
*/
StrategyNumber
GistTranslateStratnum(Oid opclass, StrategyNumber strat)
{
Oid opfamily;
Oid opcintype;
Oid funcid;
Datum result;
/* Look up the opclass family and input datatype. */
if (!get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
return InvalidStrategy;
/* Check whether the function is provided. */
funcid = get_opfamily_proc(opfamily, opcintype, opcintype, GIST_STRATNUM_PROC);
if (!OidIsValid(funcid))
return InvalidStrategy;
/* Ask the translation function */
result = OidFunctionCall1Coll(funcid, InvalidOid, UInt16GetDatum(strat));
return DatumGetUInt16(result);
}

View File

@ -146,10 +146,6 @@ gistvalidate(Oid opclassoid)
ok = check_amproc_signature(procform->amproc, VOIDOID, true, ok = check_amproc_signature(procform->amproc, VOIDOID, true,
1, 1, INTERNALOID); 1, 1, INTERNALOID);
break; break;
case GIST_STRATNUM_PROC:
ok = check_amproc_signature(procform->amproc, INT2OID, true,
1, 1, INT2OID);
break;
default: default:
ereport(INFO, ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@ -270,8 +266,7 @@ gistvalidate(Oid opclassoid)
continue; /* got it */ continue; /* got it */
if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC || if (i == GIST_DISTANCE_PROC || i == GIST_FETCH_PROC ||
i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC || i == GIST_COMPRESS_PROC || i == GIST_DECOMPRESS_PROC ||
i == GIST_OPTIONS_PROC || i == GIST_SORTSUPPORT_PROC || i == GIST_OPTIONS_PROC || i == GIST_SORTSUPPORT_PROC)
i == GIST_STRATNUM_PROC)
continue; /* optional methods */ continue; /* optional methods */
ereport(INFO, ereport(INFO,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@ -343,7 +338,6 @@ gistadjustmembers(Oid opfamilyoid,
case GIST_FETCH_PROC: case GIST_FETCH_PROC:
case GIST_OPTIONS_PROC: case GIST_OPTIONS_PROC:
case GIST_SORTSUPPORT_PROC: case GIST_SORTSUPPORT_PROC:
case GIST_STRATNUM_PROC:
/* Optional, so force it to be a soft family dependency */ /* Optional, so force it to be a soft family dependency */
op->ref_is_hard = false; op->ref_is_hard = false;
op->ref_is_family = true; op->ref_is_family = true;

View File

@ -2155,7 +2155,6 @@ StoreRelCheck(Relation rel, const char *ccname, Node *expr,
is_local, /* conislocal */ is_local, /* conislocal */
inhcount, /* coninhcount */ inhcount, /* coninhcount */
is_no_inherit, /* connoinherit */ is_no_inherit, /* connoinherit */
false, /* conperiod */
is_internal); /* internally constructed? */ is_internal); /* internally constructed? */
pfree(ccbin); pfree(ccbin);

View File

@ -1873,7 +1873,6 @@ index_concurrently_set_dead(Oid heapId, Oid indexId)
* INDEX_CONSTR_CREATE_UPDATE_INDEX: update the pg_index row * INDEX_CONSTR_CREATE_UPDATE_INDEX: update the pg_index row
* INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS: remove existing dependencies * INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS: remove existing dependencies
* of index on table's columns * of index on table's columns
* INDEX_CONSTR_CREATE_WITHOUT_OVERLAPS: constraint uses WITHOUT OVERLAPS
* allow_system_table_mods: allow table to be a system catalog * allow_system_table_mods: allow table to be a system catalog
* is_internal: index is constructed due to internal process * is_internal: index is constructed due to internal process
*/ */
@ -1897,13 +1896,11 @@ index_constraint_create(Relation heapRelation,
bool mark_as_primary; bool mark_as_primary;
bool islocal; bool islocal;
bool noinherit; bool noinherit;
bool is_without_overlaps;
int inhcount; int inhcount;
deferrable = (constr_flags & INDEX_CONSTR_CREATE_DEFERRABLE) != 0; deferrable = (constr_flags & INDEX_CONSTR_CREATE_DEFERRABLE) != 0;
initdeferred = (constr_flags & INDEX_CONSTR_CREATE_INIT_DEFERRED) != 0; initdeferred = (constr_flags & INDEX_CONSTR_CREATE_INIT_DEFERRED) != 0;
mark_as_primary = (constr_flags & INDEX_CONSTR_CREATE_MARK_AS_PRIMARY) != 0; mark_as_primary = (constr_flags & INDEX_CONSTR_CREATE_MARK_AS_PRIMARY) != 0;
is_without_overlaps = (constr_flags & INDEX_CONSTR_CREATE_WITHOUT_OVERLAPS) != 0;
/* constraint creation support doesn't work while bootstrapping */ /* constraint creation support doesn't work while bootstrapping */
Assert(!IsBootstrapProcessingMode()); Assert(!IsBootstrapProcessingMode());
@ -1980,7 +1977,6 @@ index_constraint_create(Relation heapRelation,
islocal, islocal,
inhcount, inhcount,
noinherit, noinherit,
is_without_overlaps,
is_internal); is_internal);
/* /*

View File

@ -15,7 +15,6 @@
#include "postgres.h" #include "postgres.h"
#include "access/genam.h" #include "access/genam.h"
#include "access/gist.h"
#include "access/htup_details.h" #include "access/htup_details.h"
#include "access/sysattr.h" #include "access/sysattr.h"
#include "access/table.h" #include "access/table.h"
@ -76,7 +75,6 @@ CreateConstraintEntry(const char *constraintName,
bool conIsLocal, bool conIsLocal,
int conInhCount, int conInhCount,
bool conNoInherit, bool conNoInherit,
bool conPeriod,
bool is_internal) bool is_internal)
{ {
Relation conDesc; Relation conDesc;
@ -192,7 +190,6 @@ CreateConstraintEntry(const char *constraintName,
values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal); values[Anum_pg_constraint_conislocal - 1] = BoolGetDatum(conIsLocal);
values[Anum_pg_constraint_coninhcount - 1] = Int16GetDatum(conInhCount); values[Anum_pg_constraint_coninhcount - 1] = Int16GetDatum(conInhCount);
values[Anum_pg_constraint_connoinherit - 1] = BoolGetDatum(conNoInherit); values[Anum_pg_constraint_connoinherit - 1] = BoolGetDatum(conNoInherit);
values[Anum_pg_constraint_conperiod - 1] = BoolGetDatum(conPeriod);
if (conkeyArray) if (conkeyArray)
values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray); values[Anum_pg_constraint_conkey - 1] = PointerGetDatum(conkeyArray);
@ -1350,63 +1347,6 @@ DeconstructFkConstraintRow(HeapTuple tuple, int *numfks,
*numfks = numkeys; *numfks = numkeys;
} }
/*
* FindFKPeriodOpers -
*
* Looks up the operator oids used for the PERIOD part of a temporal foreign key.
* The opclass should be the opclass of that PERIOD element.
* Everything else is an output: containedbyoperoid is the ContainedBy operator for
* types matching the PERIOD element.
* aggedcontainedbyoperoid is also a ContainedBy operator,
* but one whose rhs is a multirange.
* That way foreign keys can compare fkattr <@ range_agg(pkattr).
*/
void
FindFKPeriodOpers(Oid opclass,
Oid *containedbyoperoid,
Oid *aggedcontainedbyoperoid)
{
Oid opfamily = InvalidOid;
Oid opcintype = InvalidOid;
StrategyNumber strat;
/* Make sure we have a range or multirange. */
if (get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
{
if (opcintype != ANYRANGEOID && opcintype != ANYMULTIRANGEOID)
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("invalid type for PERIOD part of foreign key"),
errdetail("Only range and multirange are supported."));
}
else
elog(ERROR, "cache lookup failed for opclass %u", opclass);
/*
* Look up the ContainedBy operator whose lhs and rhs are the opclass's
* type. We use this to optimize RI checks: if the new value includes all
* of the old value, then we can treat the attribute as if it didn't
* change, and skip the RI check.
*/
strat = RTContainedByStrategyNumber;
GetOperatorFromWellKnownStrategy(opclass,
InvalidOid,
containedbyoperoid,
&strat);
/*
* Now look up the ContainedBy operator. Its left arg must be the type of
* the column (or rather of the opclass). Its right arg must match the
* return type of the support proc.
*/
strat = RTContainedByStrategyNumber;
GetOperatorFromWellKnownStrategy(opclass,
ANYMULTIRANGEOID,
aggedcontainedbyoperoid,
&strat);
}
/* /*
* Determine whether a relation can be proven functionally dependent on * Determine whether a relation can be proven functionally dependent on
* a set of grouping columns. If so, return true and add the pg_constraint * a set of grouping columns. If so, return true and add the pg_constraint

View File

@ -16,7 +16,6 @@
#include "postgres.h" #include "postgres.h"
#include "access/amapi.h" #include "access/amapi.h"
#include "access/gist.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "access/htup_details.h" #include "access/htup_details.h"
#include "access/reloptions.h" #include "access/reloptions.h"
@ -88,7 +87,6 @@ static void ComputeIndexAttrs(IndexInfo *indexInfo,
Oid accessMethodId, Oid accessMethodId,
bool amcanorder, bool amcanorder,
bool isconstraint, bool isconstraint,
bool iswithoutoverlaps,
Oid ddl_userid, Oid ddl_userid,
int ddl_sec_context, int ddl_sec_context,
int *ddl_save_nestlevel); int *ddl_save_nestlevel);
@ -147,7 +145,6 @@ typedef struct ReindexErrorInfo
* to index on. * to index on.
* 'exclusionOpNames': list of names of exclusion-constraint operators, * 'exclusionOpNames': list of names of exclusion-constraint operators,
* or NIL if not an exclusion constraint. * or NIL if not an exclusion constraint.
* 'isWithoutOverlaps': true iff this index has a WITHOUT OVERLAPS clause.
* *
* This is tailored to the needs of ALTER TABLE ALTER TYPE, which recreates * This is tailored to the needs of ALTER TABLE ALTER TYPE, which recreates
* any indexes that depended on a changing column from their pg_get_indexdef * any indexes that depended on a changing column from their pg_get_indexdef
@ -177,8 +174,7 @@ bool
CheckIndexCompatible(Oid oldId, CheckIndexCompatible(Oid oldId,
const char *accessMethodName, const char *accessMethodName,
const List *attributeList, const List *attributeList,
const List *exclusionOpNames, const List *exclusionOpNames)
bool isWithoutOverlaps)
{ {
bool isconstraint; bool isconstraint;
Oid *typeIds; Oid *typeIds;
@ -253,8 +249,8 @@ CheckIndexCompatible(Oid oldId,
coloptions, attributeList, coloptions, attributeList,
exclusionOpNames, relationId, exclusionOpNames, relationId,
accessMethodName, accessMethodId, accessMethodName, accessMethodId,
amcanorder, isconstraint, isWithoutOverlaps, InvalidOid, amcanorder, isconstraint, InvalidOid, 0, NULL);
0, NULL);
/* Get the soon-obsolete pg_index tuple. */ /* Get the soon-obsolete pg_index tuple. */
tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(oldId)); tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(oldId));
@ -564,7 +560,6 @@ DefineIndex(Oid tableId,
bool amcanorder; bool amcanorder;
bool amissummarizing; bool amissummarizing;
amoptions_function amoptions; amoptions_function amoptions;
bool exclusion;
bool partitioned; bool partitioned;
bool safe_index; bool safe_index;
Datum reloptions; Datum reloptions;
@ -685,12 +680,6 @@ DefineIndex(Oid tableId,
namespaceId = RelationGetNamespace(rel); namespaceId = RelationGetNamespace(rel);
/*
* It has exclusion constraint behavior if it's an EXCLUDE constraint or a
* temporal PRIMARY KEY/UNIQUE constraint
*/
exclusion = stmt->excludeOpNames || stmt->iswithoutoverlaps;
/* Ensure that it makes sense to index this kind of relation */ /* Ensure that it makes sense to index this kind of relation */
switch (rel->rd_rel->relkind) switch (rel->rd_rel->relkind)
{ {
@ -859,7 +848,7 @@ DefineIndex(Oid tableId,
pgstat_progress_update_param(PROGRESS_CREATEIDX_ACCESS_METHOD_OID, pgstat_progress_update_param(PROGRESS_CREATEIDX_ACCESS_METHOD_OID,
accessMethodId); accessMethodId);
if (stmt->unique && !stmt->iswithoutoverlaps && !amRoutine->amcanunique) if (stmt->unique && !amRoutine->amcanunique)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("access method \"%s\" does not support unique indexes", errmsg("access method \"%s\" does not support unique indexes",
@ -874,7 +863,7 @@ DefineIndex(Oid tableId,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("access method \"%s\" does not support multicolumn indexes", errmsg("access method \"%s\" does not support multicolumn indexes",
accessMethodName))); accessMethodName)));
if (exclusion && amRoutine->amgettuple == NULL) if (stmt->excludeOpNames && amRoutine->amgettuple == NULL)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("access method \"%s\" does not support exclusion constraints", errmsg("access method \"%s\" does not support exclusion constraints",
@ -927,9 +916,8 @@ DefineIndex(Oid tableId,
coloptions, allIndexParams, coloptions, allIndexParams,
stmt->excludeOpNames, tableId, stmt->excludeOpNames, tableId,
accessMethodName, accessMethodId, accessMethodName, accessMethodId,
amcanorder, stmt->isconstraint, stmt->iswithoutoverlaps, amcanorder, stmt->isconstraint, root_save_userid,
root_save_userid, root_save_sec_context, root_save_sec_context, &root_save_nestlevel);
&root_save_nestlevel);
/* /*
* Extra checks when creating a PRIMARY KEY index. * Extra checks when creating a PRIMARY KEY index.
@ -947,7 +935,7 @@ DefineIndex(Oid tableId,
* We could lift this limitation if we had global indexes, but those have * We could lift this limitation if we had global indexes, but those have
* their own problems, so this is a useful feature combination. * their own problems, so this is a useful feature combination.
*/ */
if (partitioned && (stmt->unique || exclusion)) if (partitioned && (stmt->unique || stmt->excludeOpNames))
{ {
PartitionKey key = RelationGetPartitionKey(rel); PartitionKey key = RelationGetPartitionKey(rel);
const char *constraint_type; const char *constraint_type;
@ -1001,10 +989,10 @@ DefineIndex(Oid tableId,
* associated with index columns, too. We know what to do with * associated with index columns, too. We know what to do with
* btree opclasses; if there are ever any other index types that * btree opclasses; if there are ever any other index types that
* support unique indexes, this logic will need extension. But if * support unique indexes, this logic will need extension. But if
* we have an exclusion constraint (or a temporal PK), it already * we have an exclusion constraint, it already knows the
* knows the operators, so we don't have to infer them. * operators, so we don't have to infer them.
*/ */
if (stmt->unique && !stmt->iswithoutoverlaps && accessMethodId != BTREE_AM_OID) if (stmt->unique && accessMethodId != BTREE_AM_OID)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot match partition key to an index using access method \"%s\"", errmsg("cannot match partition key to an index using access method \"%s\"",
@ -1043,12 +1031,12 @@ DefineIndex(Oid tableId,
{ {
Oid idx_eqop = InvalidOid; Oid idx_eqop = InvalidOid;
if (stmt->unique && !stmt->iswithoutoverlaps) if (stmt->unique)
idx_eqop = get_opfamily_member(idx_opfamily, idx_eqop = get_opfamily_member(idx_opfamily,
idx_opcintype, idx_opcintype,
idx_opcintype, idx_opcintype,
BTEqualStrategyNumber); BTEqualStrategyNumber);
else if (exclusion) else if (stmt->excludeOpNames)
idx_eqop = indexInfo->ii_ExclusionOps[j]; idx_eqop = indexInfo->ii_ExclusionOps[j];
Assert(idx_eqop); Assert(idx_eqop);
@ -1057,7 +1045,7 @@ DefineIndex(Oid tableId,
found = true; found = true;
break; break;
} }
else if (exclusion) else if (stmt->excludeOpNames)
{ {
/* /*
* We found a match, but it's not an equality * We found a match, but it's not an equality
@ -1201,8 +1189,6 @@ DefineIndex(Oid tableId,
constr_flags |= INDEX_CONSTR_CREATE_DEFERRABLE; constr_flags |= INDEX_CONSTR_CREATE_DEFERRABLE;
if (stmt->initdeferred) if (stmt->initdeferred)
constr_flags |= INDEX_CONSTR_CREATE_INIT_DEFERRED; constr_flags |= INDEX_CONSTR_CREATE_INIT_DEFERRED;
if (stmt->iswithoutoverlaps)
constr_flags |= INDEX_CONSTR_CREATE_WITHOUT_OVERLAPS;
indexRelationId = indexRelationId =
index_create(rel, indexRelationName, indexRelationId, parentIndexId, index_create(rel, indexRelationName, indexRelationId, parentIndexId,
@ -1868,7 +1854,6 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
Oid accessMethodId, Oid accessMethodId,
bool amcanorder, bool amcanorder,
bool isconstraint, bool isconstraint,
bool iswithoutoverlaps,
Oid ddl_userid, Oid ddl_userid,
int ddl_sec_context, int ddl_sec_context,
int *ddl_save_nestlevel) int *ddl_save_nestlevel)
@ -1892,14 +1877,6 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
else else
nextExclOp = NULL; nextExclOp = NULL;
/* exclusionOpNames can be non-NIL if we are creating a partition */
if (iswithoutoverlaps && exclusionOpNames == NIL)
{
indexInfo->ii_ExclusionOps = palloc_array(Oid, nkeycols);
indexInfo->ii_ExclusionProcs = palloc_array(Oid, nkeycols);
indexInfo->ii_ExclusionStrats = palloc_array(uint16, nkeycols);
}
if (OidIsValid(ddl_userid)) if (OidIsValid(ddl_userid))
GetUserIdAndSecContext(&save_userid, &save_sec_context); GetUserIdAndSecContext(&save_userid, &save_sec_context);
@ -2176,21 +2153,6 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
indexInfo->ii_ExclusionStrats[attn] = strat; indexInfo->ii_ExclusionStrats[attn] = strat;
nextExclOp = lnext(exclusionOpNames, nextExclOp); nextExclOp = lnext(exclusionOpNames, nextExclOp);
} }
else if (iswithoutoverlaps)
{
StrategyNumber strat;
Oid opid;
if (attn == nkeycols - 1)
strat = RTOverlapStrategyNumber;
else
strat = RTEqualStrategyNumber;
GetOperatorFromWellKnownStrategy(opclassOids[attn], InvalidOid,
&opid, &strat);
indexInfo->ii_ExclusionOps[attn] = opid;
indexInfo->ii_ExclusionProcs[attn] = get_opcode(opid);
indexInfo->ii_ExclusionStrats[attn] = strat;
}
/* /*
* Set up the per-column options (indoption field). For now, this is * Set up the per-column options (indoption field). For now, this is
@ -2421,88 +2383,6 @@ GetDefaultOpClass(Oid type_id, Oid am_id)
return InvalidOid; return InvalidOid;
} }
/*
* GetOperatorFromWellKnownStrategy
*
* opclass - the opclass to use
* rhstype - the type for the right-hand side, or InvalidOid to use the type of the given opclass.
* opid - holds the operator we found
* strat - holds the input and output strategy number
*
* Finds an operator from a "well-known" strategy number. This is used for
* temporal index constraints (and other temporal features) to look up
* equality and overlaps operators, since the strategy numbers for non-btree
* indexams need not follow any fixed scheme. We ask an opclass support
* function to translate from the well-known number to the internal value. If
* the function isn't defined or it gives no result, we return
* InvalidStrategy.
*/
void
GetOperatorFromWellKnownStrategy(Oid opclass, Oid rhstype,
Oid *opid, StrategyNumber *strat)
{
Oid opfamily;
Oid opcintype;
StrategyNumber instrat = *strat;
Assert(instrat == RTEqualStrategyNumber || instrat == RTOverlapStrategyNumber || instrat == RTContainedByStrategyNumber);
*opid = InvalidOid;
if (get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
{
/*
* Ask the opclass to translate to its internal stratnum
*
* For now we only need GiST support, but this could support other
* indexams if we wanted.
*/
*strat = GistTranslateStratnum(opclass, instrat);
if (*strat == InvalidStrategy)
{
HeapTuple tuple;
tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for operator class %u", opclass);
ereport(ERROR,
errcode(ERRCODE_UNDEFINED_OBJECT),
instrat == RTEqualStrategyNumber ? errmsg("could not identify an equality operator for type %s", format_type_be(opcintype)) :
instrat == RTOverlapStrategyNumber ? errmsg("could not identify an overlaps operator for type %s", format_type_be(opcintype)) :
instrat == RTContainedByStrategyNumber ? errmsg("could not identify a contained-by operator for type %s", format_type_be(opcintype)) : 0,
errdetail("Could not translate strategy number %d for operator class \"%s\" for access method \"%s\".",
instrat, NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname), "gist"));
}
/*
* We parameterize rhstype so foreign keys can ask for a <@ operator
* whose rhs matches the aggregate function. For example range_agg
* returns anymultirange.
*/
if (!OidIsValid(rhstype))
rhstype = opcintype;
*opid = get_opfamily_member(opfamily, opcintype, rhstype, *strat);
}
if (!OidIsValid(*opid))
{
HeapTuple tuple;
tuple = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamily));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for operator family %u", opfamily);
ereport(ERROR,
errcode(ERRCODE_UNDEFINED_OBJECT),
instrat == RTEqualStrategyNumber ? errmsg("could not identify an equality operator for type %s", format_type_be(opcintype)) :
instrat == RTOverlapStrategyNumber ? errmsg("could not identify an overlaps operator for type %s", format_type_be(opcintype)) :
instrat == RTContainedByStrategyNumber ? errmsg("could not identify a contained-by operator for type %s", format_type_be(opcintype)) : 0,
errdetail("There is no suitable operator in operator family \"%s\" for access method \"%s\".",
NameStr(((Form_pg_opfamily) GETSTRUCT(tuple))->opfname), "gist"));
}
}
/* /*
* makeObjectName() * makeObjectName()
* *

View File

@ -16,7 +16,6 @@
#include "access/attmap.h" #include "access/attmap.h"
#include "access/genam.h" #include "access/genam.h"
#include "access/gist.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "access/heapam_xlog.h" #include "access/heapam_xlog.h"
#include "access/multixact.h" #include "access/multixact.h"
@ -216,7 +215,6 @@ typedef struct NewConstraint
ConstrType contype; /* CHECK or FOREIGN */ ConstrType contype; /* CHECK or FOREIGN */
Oid refrelid; /* PK rel, if FOREIGN */ Oid refrelid; /* PK rel, if FOREIGN */
Oid refindid; /* OID of PK's index, if FOREIGN */ Oid refindid; /* OID of PK's index, if FOREIGN */
bool conwithperiod; /* Whether the new FOREIGN KEY uses PERIOD */
Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */
Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
ExprState *qualstate; /* Execution state for CHECK expr */ ExprState *qualstate; /* Execution state for CHECK expr */
@ -391,17 +389,16 @@ static int transformColumnNameList(Oid relId, List *colList,
static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid, static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
List **attnamelist, List **attnamelist,
int16 *attnums, Oid *atttypids, int16 *attnums, Oid *atttypids,
Oid *opclasses, bool *pk_has_without_overlaps); Oid *opclasses);
static Oid transformFkeyCheckAttrs(Relation pkrel, static Oid transformFkeyCheckAttrs(Relation pkrel,
int numattrs, int16 *attnums, int numattrs, int16 *attnums,
bool with_period, Oid *opclasses, Oid *opclasses);
bool *pk_has_without_overlaps);
static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts); static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId, static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
Oid *funcid); Oid *funcid);
static void validateForeignKeyConstraint(char *conname, static void validateForeignKeyConstraint(char *conname,
Relation rel, Relation pkrel, Relation rel, Relation pkrel,
Oid pkindOid, Oid constraintOid, bool hasperiod); Oid pkindOid, Oid constraintOid);
static void ATController(AlterTableStmt *parsetree, static void ATController(AlterTableStmt *parsetree,
Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
AlterTableUtilityContext *context); AlterTableUtilityContext *context);
@ -512,8 +509,7 @@ static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstra
Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
int numfkdelsetcols, int16 *fkdelsetcols, int numfkdelsetcols, int16 *fkdelsetcols,
bool old_check_ok, bool old_check_ok,
Oid parentDelTrigger, Oid parentUpdTrigger, Oid parentDelTrigger, Oid parentUpdTrigger);
bool with_period);
static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums, static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
int numfksetcols, const int16 *fksetcolsattnums, int numfksetcols, const int16 *fksetcolsattnums,
List *fksetcols); List *fksetcols);
@ -523,9 +519,7 @@ static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
int numfkdelsetcols, int16 *fkdelsetcols, int numfkdelsetcols, int16 *fkdelsetcols,
bool old_check_ok, LOCKMODE lockmode, bool old_check_ok, LOCKMODE lockmode,
Oid parentInsTrigger, Oid parentUpdTrigger, Oid parentInsTrigger, Oid parentUpdTrigger);
bool with_period);
static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel, static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
Relation partitionRel); Relation partitionRel);
static void CloneFkReferenced(Relation parentRel, Relation partitionRel); static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
@ -5924,8 +5918,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
validateForeignKeyConstraint(fkconstraint->conname, rel, refrel, validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
con->refindid, con->refindid,
con->conid, con->conid);
con->conwithperiod);
/* /*
* No need to mark the constraint row as validated, we did * No need to mark the constraint row as validated, we did
@ -9566,8 +9559,6 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
Oid ppeqoperators[INDEX_MAX_KEYS] = {0}; Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
Oid ffeqoperators[INDEX_MAX_KEYS] = {0}; Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
int16 fkdelsetcols[INDEX_MAX_KEYS] = {0}; int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
bool with_period;
bool pk_has_without_overlaps;
int i; int i;
int numfks, int numfks,
numpks, numpks,
@ -9662,11 +9653,6 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
numfks = transformColumnNameList(RelationGetRelid(rel), numfks = transformColumnNameList(RelationGetRelid(rel),
fkconstraint->fk_attrs, fkconstraint->fk_attrs,
fkattnum, fktypoid); fkattnum, fktypoid);
with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
if (with_period && !fkconstraint->fk_with_period)
ereport(ERROR,
errcode(ERRCODE_INVALID_FOREIGN_KEY),
errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel), numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
fkconstraint->fk_del_set_cols, fkconstraint->fk_del_set_cols,
@ -9686,40 +9672,18 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid, numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
&fkconstraint->pk_attrs, &fkconstraint->pk_attrs,
pkattnum, pktypoid, pkattnum, pktypoid,
opclasses, &pk_has_without_overlaps); opclasses);
/* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
ereport(ERROR,
errcode(ERRCODE_INVALID_FOREIGN_KEY),
errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
} }
else else
{ {
numpks = transformColumnNameList(RelationGetRelid(pkrel), numpks = transformColumnNameList(RelationGetRelid(pkrel),
fkconstraint->pk_attrs, fkconstraint->pk_attrs,
pkattnum, pktypoid); pkattnum, pktypoid);
/* Since we got pk_attrs, one should be a period. */
if (with_period && !fkconstraint->pk_with_period)
ereport(ERROR,
errcode(ERRCODE_INVALID_FOREIGN_KEY),
errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
/* Look for an index matching the column list */ /* Look for an index matching the column list */
indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum, indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
with_period, opclasses, &pk_has_without_overlaps); opclasses);
} }
/*
* If the referenced primary key has WITHOUT OVERLAPS, the foreign key
* must use PERIOD.
*/
if (pk_has_without_overlaps && !with_period)
ereport(ERROR,
errcode(ERRCODE_INVALID_FOREIGN_KEY),
errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
/* /*
* Now we can check permissions. * Now we can check permissions.
*/ */
@ -9753,28 +9717,6 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
} }
} }
/*
* Some actions are currently unsupported for foreign keys using PERIOD.
*/
if (fkconstraint->fk_with_period)
{
if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unsupported %s action for foreign key constraint using PERIOD",
"ON UPDATE"));
if (fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unsupported %s action for foreign key constraint using PERIOD",
"ON DELETE"));
}
/* /*
* Look up the equality operators to use in the constraint. * Look up the equality operators to use in the constraint.
* *
@ -9821,56 +9763,16 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
opcintype = cla_tup->opcintype; opcintype = cla_tup->opcintype;
ReleaseSysCache(cla_ht); ReleaseSysCache(cla_ht);
if (with_period) /*
{ * Check it's a btree; currently this can never fail since no other
StrategyNumber rtstrategy; * index AMs support unique indexes. If we ever did have other types
bool for_overlaps = with_period && i == numpks - 1; * of unique indexes, we'd need a way to determine which operator
* strategy number is equality. (Is it reasonable to insist that
/* * every such index AM use btree's number for equality?)
* GiST indexes are required to support temporal foreign keys */
* because they combine equals and overlaps. if (amid != BTREE_AM_OID)
*/ elog(ERROR, "only b-tree indexes are supported for foreign keys");
if (amid != GIST_AM_OID) eqstrategy = BTEqualStrategyNumber;
elog(ERROR, "only GiST indexes are supported for temporal foreign keys");
rtstrategy = for_overlaps ? RTOverlapStrategyNumber : RTEqualStrategyNumber;
/*
* An opclass can use whatever strategy numbers it wants, so we
* ask the opclass what number it actually uses instead of our RT*
* constants.
*/
eqstrategy = GistTranslateStratnum(opclasses[i], rtstrategy);
if (eqstrategy == InvalidStrategy)
{
HeapTuple tuple;
tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for operator class %u", opclasses[i]);
ereport(ERROR,
errcode(ERRCODE_UNDEFINED_OBJECT),
for_overlaps
? errmsg("could not identify an overlaps operator for foreign key")
: errmsg("could not identify an equality operator for foreign key"),
errdetail("Could not translate strategy number %d for operator class \"%s\" for access method \"%s\".",
rtstrategy, NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname), "gist"));
}
}
else
{
/*
* Check it's a btree; currently this can never fail since no
* other index AMs support unique indexes. If we ever did have
* other types of unique indexes, we'd need a way to determine
* which operator strategy number is equality. (We could use
* something like GistTranslateStratnum.)
*/
if (amid != BTREE_AM_OID)
elog(ERROR, "only b-tree indexes are supported for foreign keys");
eqstrategy = BTEqualStrategyNumber;
}
/* /*
* There had better be a primary equality operator for the index. * There had better be a primary equality operator for the index.
@ -10020,22 +9922,6 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
ffeqoperators[i] = ffeqop; ffeqoperators[i] = ffeqop;
} }
/*
* For FKs with PERIOD we need additional operators to check whether the
* referencing row's range is contained by the aggregated ranges of the
* referenced row(s). For rangetypes and multirangetypes this is
* fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
* support for now. FKs will look these up at "runtime", but we should
* make sure the lookup works here, even if we don't use the values.
*/
if (with_period)
{
Oid periodoperoid;
Oid aggedperiodoperoid;
FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid);
}
/* /*
* Create all the constraint and trigger objects, recursing to partitions * Create all the constraint and trigger objects, recursing to partitions
* as necessary. First handle the referenced side. * as necessary. First handle the referenced side.
@ -10052,8 +9938,7 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
numfkdelsetcols, numfkdelsetcols,
fkdelsetcols, fkdelsetcols,
old_check_ok, old_check_ok,
InvalidOid, InvalidOid, InvalidOid, InvalidOid);
with_period);
/* Now handle the referencing side. */ /* Now handle the referencing side. */
addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel, addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
@ -10069,8 +9954,7 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
fkdelsetcols, fkdelsetcols,
old_check_ok, old_check_ok,
lockmode, lockmode,
InvalidOid, InvalidOid, InvalidOid, InvalidOid);
with_period);
/* /*
* Done. Close pk table, but keep lock until we've committed. * Done. Close pk table, but keep lock until we've committed.
@ -10155,8 +10039,7 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
Oid *ppeqoperators, Oid *ffeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
int numfkdelsetcols, int16 *fkdelsetcols, int numfkdelsetcols, int16 *fkdelsetcols,
bool old_check_ok, bool old_check_ok,
Oid parentDelTrigger, Oid parentUpdTrigger, Oid parentDelTrigger, Oid parentUpdTrigger)
bool with_period)
{ {
ObjectAddress address; ObjectAddress address;
Oid constrOid; Oid constrOid;
@ -10242,7 +10125,6 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
conislocal, /* islocal */ conislocal, /* islocal */
coninhcount, /* inhcount */ coninhcount, /* inhcount */
connoinherit, /* conNoInherit */ connoinherit, /* conNoInherit */
with_period, /* conPeriod */
false); /* is_internal */ false); /* is_internal */
ObjectAddressSet(address, ConstraintRelationId, constrOid); ObjectAddressSet(address, ConstraintRelationId, constrOid);
@ -10318,8 +10200,7 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
pfeqoperators, ppeqoperators, ffeqoperators, pfeqoperators, ppeqoperators, ffeqoperators,
numfkdelsetcols, fkdelsetcols, numfkdelsetcols, fkdelsetcols,
old_check_ok, old_check_ok,
deleteTriggerOid, updateTriggerOid, deleteTriggerOid, updateTriggerOid);
with_period);
/* Done -- clean up (but keep the lock) */ /* Done -- clean up (but keep the lock) */
table_close(partRel, NoLock); table_close(partRel, NoLock);
@ -10377,8 +10258,7 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
int numfkdelsetcols, int16 *fkdelsetcols, int numfkdelsetcols, int16 *fkdelsetcols,
bool old_check_ok, LOCKMODE lockmode, bool old_check_ok, LOCKMODE lockmode,
Oid parentInsTrigger, Oid parentUpdTrigger, Oid parentInsTrigger, Oid parentUpdTrigger)
bool with_period)
{ {
Oid insertTriggerOid, Oid insertTriggerOid,
updateTriggerOid; updateTriggerOid;
@ -10426,7 +10306,6 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
newcon->refrelid = RelationGetRelid(pkrel); newcon->refrelid = RelationGetRelid(pkrel);
newcon->refindid = indexOid; newcon->refindid = indexOid;
newcon->conid = parentConstr; newcon->conid = parentConstr;
newcon->conwithperiod = fkconstraint->fk_with_period;
newcon->qual = (Node *) fkconstraint; newcon->qual = (Node *) fkconstraint;
tab->constraints = lappend(tab->constraints, newcon); tab->constraints = lappend(tab->constraints, newcon);
@ -10544,7 +10423,6 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
false, false,
1, 1,
false, false,
with_period, /* conPeriod */
false); false);
/* /*
@ -10575,8 +10453,7 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
old_check_ok, old_check_ok,
lockmode, lockmode,
insertTriggerOid, insertTriggerOid,
updateTriggerOid, updateTriggerOid);
with_period);
table_close(partition, NoLock); table_close(partition, NoLock);
} }
@ -10812,8 +10689,7 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel)
confdelsetcols, confdelsetcols,
true, true,
deleteTriggerOid, deleteTriggerOid,
updateTriggerOid, updateTriggerOid);
constrForm->conperiod);
table_close(fkRel, NoLock); table_close(fkRel, NoLock);
ReleaseSysCache(tuple); ReleaseSysCache(tuple);
@ -10906,7 +10782,6 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
ListCell *lc; ListCell *lc;
Oid insertTriggerOid, Oid insertTriggerOid,
updateTriggerOid; updateTriggerOid;
bool with_period;
tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid)); tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
if (!HeapTupleIsValid(tuple)) if (!HeapTupleIsValid(tuple))
@ -11022,7 +10897,6 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
fkconstraint->conname = pstrdup(NameStr(constrForm->conname)); fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
indexOid = constrForm->conindid; indexOid = constrForm->conindid;
with_period = constrForm->conperiod;
constrOid = constrOid =
CreateConstraintEntry(fkconstraint->conname, CreateConstraintEntry(fkconstraint->conname,
constrForm->connamespace, constrForm->connamespace,
@ -11054,7 +10928,6 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
false, /* islocal */ false, /* islocal */
1, /* inhcount */ 1, /* inhcount */
false, /* conNoInherit */ false, /* conNoInherit */
with_period, /* conPeriod */
true); true);
/* Set up partition dependencies for the new constraint */ /* Set up partition dependencies for the new constraint */
@ -11088,8 +10961,7 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
false, /* no old check exists */ false, /* no old check exists */
AccessExclusiveLock, AccessExclusiveLock,
insertTriggerOid, insertTriggerOid,
updateTriggerOid, updateTriggerOid);
with_period);
table_close(pkrel, NoLock); table_close(pkrel, NoLock);
} }
@ -11899,8 +11771,7 @@ transformColumnNameList(Oid relId, List *colList,
* *
* Look up the names, attnums, and types of the primary key attributes * Look up the names, attnums, and types of the primary key attributes
* for the pkrel. Also return the index OID and index opclasses of the * for the pkrel. Also return the index OID and index opclasses of the
* index supporting the primary key. Also return whether the index has * index supporting the primary key.
* WITHOUT OVERLAPS.
* *
* All parameters except pkrel are output parameters. Also, the function * All parameters except pkrel are output parameters. Also, the function
* return value is the number of attributes in the primary key. * return value is the number of attributes in the primary key.
@ -11911,7 +11782,7 @@ static int
transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid, transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
List **attnamelist, List **attnamelist,
int16 *attnums, Oid *atttypids, int16 *attnums, Oid *atttypids,
Oid *opclasses, bool *pk_has_without_overlaps) Oid *opclasses)
{ {
List *indexoidlist; List *indexoidlist;
ListCell *indexoidscan; ListCell *indexoidscan;
@ -11989,8 +11860,6 @@ transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno))))); makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
} }
*pk_has_without_overlaps = indexStruct->indisexclusion;
ReleaseSysCache(indexTuple); ReleaseSysCache(indexTuple);
return i; return i;
@ -12004,16 +11873,14 @@ transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
* *
* Returns the OID of the unique index supporting the constraint and * Returns the OID of the unique index supporting the constraint and
* populates the caller-provided 'opclasses' array with the opclasses * populates the caller-provided 'opclasses' array with the opclasses
* associated with the index columns. Also sets whether the index * associated with the index columns.
* uses WITHOUT OVERLAPS.
* *
* Raises an ERROR on validation failure. * Raises an ERROR on validation failure.
*/ */
static Oid static Oid
transformFkeyCheckAttrs(Relation pkrel, transformFkeyCheckAttrs(Relation pkrel,
int numattrs, int16 *attnums, int numattrs, int16 *attnums,
bool with_period, Oid *opclasses, Oid *opclasses)
bool *pk_has_without_overlaps)
{ {
Oid indexoid = InvalidOid; Oid indexoid = InvalidOid;
bool found = false; bool found = false;
@ -12060,12 +11927,12 @@ transformFkeyCheckAttrs(Relation pkrel,
indexStruct = (Form_pg_index) GETSTRUCT(indexTuple); indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
/* /*
* Must have the right number of columns; must be unique (or if * Must have the right number of columns; must be unique and not a
* temporal then exclusion instead) and not a partial index; forget it * partial index; forget it if there are any expressions, too. Invalid
* if there are any expressions, too. Invalid indexes are out as well. * indexes are out as well.
*/ */
if (indexStruct->indnkeyatts == numattrs && if (indexStruct->indnkeyatts == numattrs &&
(with_period ? indexStruct->indisexclusion : indexStruct->indisunique) && indexStruct->indisunique &&
indexStruct->indisvalid && indexStruct->indisvalid &&
heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) && heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL)) heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
@ -12103,13 +11970,6 @@ transformFkeyCheckAttrs(Relation pkrel,
if (!found) if (!found)
break; break;
} }
/* The last attribute in the index must be the PERIOD FK part */
if (found && with_period)
{
int16 periodattnum = attnums[numattrs - 1];
found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
}
/* /*
* Refuse to use a deferrable unique/primary key. This is per SQL * Refuse to use a deferrable unique/primary key. This is per SQL
@ -12125,10 +11985,6 @@ transformFkeyCheckAttrs(Relation pkrel,
found_deferrable = true; found_deferrable = true;
found = false; found = false;
} }
/* We need to know whether the index has WITHOUT OVERLAPS */
if (found)
*pk_has_without_overlaps = indexStruct->indisexclusion;
} }
ReleaseSysCache(indexTuple); ReleaseSysCache(indexTuple);
if (found) if (found)
@ -12223,8 +12079,7 @@ validateForeignKeyConstraint(char *conname,
Relation rel, Relation rel,
Relation pkrel, Relation pkrel,
Oid pkindOid, Oid pkindOid,
Oid constraintOid, Oid constraintOid)
bool hasperiod)
{ {
TupleTableSlot *slot; TupleTableSlot *slot;
TableScanDesc scan; TableScanDesc scan;
@ -12252,11 +12107,9 @@ validateForeignKeyConstraint(char *conname,
/* /*
* See if we can do it with a single LEFT JOIN query. A false result * See if we can do it with a single LEFT JOIN query. A false result
* indicates we must proceed with the fire-the-trigger method. We can't do * indicates we must proceed with the fire-the-trigger method.
* a LEFT JOIN for temporal FKs yet, but we can once we support temporal
* left joins.
*/ */
if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel)) if (RI_Initial_Check(&trig, rel, pkrel))
return; return;
/* /*
@ -12407,7 +12260,6 @@ createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstr
fk_trigger->whenClause = NULL; fk_trigger->whenClause = NULL;
fk_trigger->transitionRels = NIL; fk_trigger->transitionRels = NIL;
fk_trigger->constrrel = NULL; fk_trigger->constrrel = NULL;
switch (fkconstraint->fk_del_action) switch (fkconstraint->fk_del_action)
{ {
case FKCONSTR_ACTION_NOACTION: case FKCONSTR_ACTION_NOACTION:
@ -12468,7 +12320,6 @@ createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstr
fk_trigger->whenClause = NULL; fk_trigger->whenClause = NULL;
fk_trigger->transitionRels = NIL; fk_trigger->transitionRels = NIL;
fk_trigger->constrrel = NULL; fk_trigger->constrrel = NULL;
switch (fkconstraint->fk_upd_action) switch (fkconstraint->fk_upd_action)
{ {
case FKCONSTR_ACTION_NOACTION: case FKCONSTR_ACTION_NOACTION:
@ -14245,8 +14096,7 @@ TryReuseIndex(Oid oldId, IndexStmt *stmt)
if (CheckIndexCompatible(oldId, if (CheckIndexCompatible(oldId,
stmt->accessMethod, stmt->accessMethod,
stmt->indexParams, stmt->indexParams,
stmt->excludeOpNames, stmt->excludeOpNames))
stmt->iswithoutoverlaps))
{ {
Relation irel = index_open(oldId, NoLock); Relation irel = index_open(oldId, NoLock);

View File

@ -834,7 +834,6 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
true, /* islocal */ true, /* islocal */
0, /* inhcount */ 0, /* inhcount */
true, /* noinherit */ true, /* noinherit */
false, /* conperiod */
isInternal); /* is_internal */ isInternal); /* is_internal */
} }

View File

@ -3621,7 +3621,6 @@ domainAddCheckConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
true, /* is local */ true, /* is local */
0, /* inhcount */ 0, /* inhcount */
false, /* connoinherit */ false, /* connoinherit */
false, /* conperiod */
false); /* is_internal */ false); /* is_internal */
if (constrAddr) if (constrAddr)
ObjectAddressSet(*constrAddr, ConstraintRelationId, ccoid); ObjectAddressSet(*constrAddr, ConstraintRelationId, ccoid);
@ -3728,7 +3727,6 @@ domainAddNotNullConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
true, /* is local */ true, /* is local */
0, /* inhcount */ 0, /* inhcount */
false, /* connoinherit */ false, /* connoinherit */
false, /* conperiod */
false); /* is_internal */ false); /* is_internal */
if (constrAddr) if (constrAddr)

View File

@ -210,7 +210,7 @@ ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
* If the indexes are to be used for speculative insertion, add extra * If the indexes are to be used for speculative insertion, add extra
* information required by unique index entries. * information required by unique index entries.
*/ */
if (speculative && ii->ii_Unique && !indexDesc->rd_index->indisexclusion) if (speculative && ii->ii_Unique)
BuildSpeculativeIndexInfo(indexDesc, ii); BuildSpeculativeIndexInfo(indexDesc, ii);
relationDescs[i] = indexDesc; relationDescs[i] = indexDesc;

View File

@ -815,7 +815,7 @@ infer_arbiter_indexes(PlannerInfo *root)
*/ */
if (indexOidFromConstraint == idxForm->indexrelid) if (indexOidFromConstraint == idxForm->indexrelid)
{ {
if (idxForm->indisexclusion && onconflict->action == ONCONFLICT_UPDATE) if (!idxForm->indisunique && onconflict->action == ONCONFLICT_UPDATE)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE), (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("ON CONFLICT DO UPDATE not supported with exclusion constraints"))); errmsg("ON CONFLICT DO UPDATE not supported with exclusion constraints")));
@ -840,13 +840,6 @@ infer_arbiter_indexes(PlannerInfo *root)
if (!idxForm->indisunique) if (!idxForm->indisunique)
goto next; goto next;
/*
* So-called unique constraints with WITHOUT OVERLAPS are really
* exclusion constraints, so skip those too.
*/
if (idxForm->indisexclusion)
goto next;
/* Build BMS representation of plain (non expression) index attrs */ /* Build BMS representation of plain (non expression) index attrs */
indexedAttrs = NULL; indexedAttrs = NULL;
for (natt = 0; natt < idxForm->indnkeyatts; natt++) for (natt = 0; natt < idxForm->indnkeyatts; natt++)

View File

@ -525,15 +525,14 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
SetResetClause FunctionSetResetClause SetResetClause FunctionSetResetClause
%type <node> TableElement TypedTableElement ConstraintElem DomainConstraintElem TableFuncElement %type <node> TableElement TypedTableElement ConstraintElem DomainConstraintElem TableFuncElement
%type <node> columnDef columnOptions optionalPeriodName %type <node> columnDef columnOptions
%type <defelt> def_elem reloption_elem old_aggr_elem operator_def_elem %type <defelt> def_elem reloption_elem old_aggr_elem operator_def_elem
%type <node> def_arg columnElem where_clause where_or_current_clause %type <node> def_arg columnElem where_clause where_or_current_clause
a_expr b_expr c_expr AexprConst indirection_el opt_slice_bound a_expr b_expr c_expr AexprConst indirection_el opt_slice_bound
columnref in_expr having_clause func_table xmltable array_expr columnref in_expr having_clause func_table xmltable array_expr
OptWhereClause operator_def_arg OptWhereClause operator_def_arg
%type <list> opt_column_and_period_list
%type <list> rowsfrom_item rowsfrom_list opt_col_def_list %type <list> rowsfrom_item rowsfrom_list opt_col_def_list
%type <boolean> opt_ordinality opt_without_overlaps %type <boolean> opt_ordinality
%type <list> ExclusionConstraintList ExclusionConstraintElem %type <list> ExclusionConstraintList ExclusionConstraintElem
%type <list> func_arg_list func_arg_list_opt %type <list> func_arg_list func_arg_list_opt
%type <node> func_arg_expr %type <node> func_arg_expr
@ -765,7 +764,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER
PARALLEL PARAMETER PARSER PARTIAL PARTITION PARTITIONS PASSING PASSWORD PATH PARALLEL PARAMETER PARSER PARTIAL PARTITION PARTITIONS PASSING PASSWORD PATH
PERIOD PLACING PLAN PLANS POLICY PLACING PLAN PLANS POLICY
POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION
@ -4194,7 +4193,7 @@ ConstraintElem:
n->initially_valid = !n->skip_validation; n->initially_valid = !n->skip_validation;
$$ = (Node *) n; $$ = (Node *) n;
} }
| UNIQUE opt_unique_null_treatment '(' columnList opt_without_overlaps ')' opt_c_include opt_definition OptConsTableSpace | UNIQUE opt_unique_null_treatment '(' columnList ')' opt_c_include opt_definition OptConsTableSpace
ConstraintAttributeSpec ConstraintAttributeSpec
{ {
Constraint *n = makeNode(Constraint); Constraint *n = makeNode(Constraint);
@ -4203,12 +4202,11 @@ ConstraintElem:
n->location = @1; n->location = @1;
n->nulls_not_distinct = !$2; n->nulls_not_distinct = !$2;
n->keys = $4; n->keys = $4;
n->without_overlaps = $5; n->including = $6;
n->including = $7; n->options = $7;
n->options = $8;
n->indexname = NULL; n->indexname = NULL;
n->indexspace = $9; n->indexspace = $8;
processCASbits($10, @10, "UNIQUE", processCASbits($9, @9, "UNIQUE",
&n->deferrable, &n->initdeferred, NULL, &n->deferrable, &n->initdeferred, NULL,
NULL, yyscanner); NULL, yyscanner);
$$ = (Node *) n; $$ = (Node *) n;
@ -4229,7 +4227,7 @@ ConstraintElem:
NULL, yyscanner); NULL, yyscanner);
$$ = (Node *) n; $$ = (Node *) n;
} }
| PRIMARY KEY '(' columnList opt_without_overlaps ')' opt_c_include opt_definition OptConsTableSpace | PRIMARY KEY '(' columnList ')' opt_c_include opt_definition OptConsTableSpace
ConstraintAttributeSpec ConstraintAttributeSpec
{ {
Constraint *n = makeNode(Constraint); Constraint *n = makeNode(Constraint);
@ -4237,12 +4235,11 @@ ConstraintElem:
n->contype = CONSTR_PRIMARY; n->contype = CONSTR_PRIMARY;
n->location = @1; n->location = @1;
n->keys = $4; n->keys = $4;
n->without_overlaps = $5; n->including = $6;
n->including = $7; n->options = $7;
n->options = $8;
n->indexname = NULL; n->indexname = NULL;
n->indexspace = $9; n->indexspace = $8;
processCASbits($10, @10, "PRIMARY KEY", processCASbits($9, @9, "PRIMARY KEY",
&n->deferrable, &n->initdeferred, NULL, &n->deferrable, &n->initdeferred, NULL,
NULL, yyscanner); NULL, yyscanner);
$$ = (Node *) n; $$ = (Node *) n;
@ -4283,31 +4280,21 @@ ConstraintElem:
NULL, yyscanner); NULL, yyscanner);
$$ = (Node *) n; $$ = (Node *) n;
} }
| FOREIGN KEY '(' columnList optionalPeriodName ')' REFERENCES qualified_name | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
opt_column_and_period_list key_match key_actions ConstraintAttributeSpec opt_column_list key_match key_actions ConstraintAttributeSpec
{ {
Constraint *n = makeNode(Constraint); Constraint *n = makeNode(Constraint);
n->contype = CONSTR_FOREIGN; n->contype = CONSTR_FOREIGN;
n->location = @1; n->location = @1;
n->pktable = $8; n->pktable = $7;
n->fk_attrs = $4; n->fk_attrs = $4;
if ($5) n->pk_attrs = $8;
{ n->fk_matchtype = $9;
n->fk_attrs = lappend(n->fk_attrs, $5); n->fk_upd_action = ($10)->updateAction->action;
n->fk_with_period = true; n->fk_del_action = ($10)->deleteAction->action;
} n->fk_del_set_cols = ($10)->deleteAction->cols;
n->pk_attrs = linitial($9); processCASbits($11, @11, "FOREIGN KEY",
if (lsecond($9))
{
n->pk_attrs = lappend(n->pk_attrs, lsecond($9));
n->pk_with_period = true;
}
n->fk_matchtype = $10;
n->fk_upd_action = ($11)->updateAction->action;
n->fk_del_action = ($11)->deleteAction->action;
n->fk_del_set_cols = ($11)->deleteAction->cols;
processCASbits($12, @12, "FOREIGN KEY",
&n->deferrable, &n->initdeferred, &n->deferrable, &n->initdeferred,
&n->skip_validation, NULL, &n->skip_validation, NULL,
yyscanner); yyscanner);
@ -4374,11 +4361,6 @@ opt_no_inherit: NO INHERIT { $$ = true; }
| /* EMPTY */ { $$ = false; } | /* EMPTY */ { $$ = false; }
; ;
opt_without_overlaps:
WITHOUT OVERLAPS { $$ = true; }
| /*EMPTY*/ { $$ = false; }
;
opt_column_list: opt_column_list:
'(' columnList ')' { $$ = $2; } '(' columnList ')' { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; } | /*EMPTY*/ { $$ = NIL; }
@ -4389,16 +4371,6 @@ columnList:
| columnList ',' columnElem { $$ = lappend($1, $3); } | columnList ',' columnElem { $$ = lappend($1, $3); }
; ;
optionalPeriodName:
',' PERIOD columnElem { $$ = $3; }
| /*EMPTY*/ { $$ = NULL; }
;
opt_column_and_period_list:
'(' columnList optionalPeriodName ')' { $$ = list_make2($2, $3); }
| /*EMPTY*/ { $$ = list_make2(NIL, NULL); }
;
columnElem: ColId columnElem: ColId
{ {
$$ = (Node *) makeString($1); $$ = (Node *) makeString($1);
@ -17793,7 +17765,6 @@ unreserved_keyword:
| PASSING | PASSING
| PASSWORD | PASSWORD
| PATH | PATH
| PERIOD
| PLAN | PLAN
| PLANS | PLANS
| POLICY | POLICY
@ -18420,7 +18391,6 @@ bare_label_keyword:
| PASSING | PASSING
| PASSWORD | PASSWORD
| PATH | PATH
| PERIOD
| PLACING | PLACING
| PLAN | PLAN
| PLANS | PLANS

View File

@ -1562,7 +1562,6 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
index->unique = idxrec->indisunique; index->unique = idxrec->indisunique;
index->nulls_not_distinct = idxrec->indnullsnotdistinct; index->nulls_not_distinct = idxrec->indnullsnotdistinct;
index->primary = idxrec->indisprimary; index->primary = idxrec->indisprimary;
index->iswithoutoverlaps = (idxrec->indisprimary || idxrec->indisunique) && idxrec->indisexclusion;
index->transformed = true; /* don't need transformIndexStmt */ index->transformed = true; /* don't need transformIndexStmt */
index->concurrent = false; index->concurrent = false;
index->if_not_exists = false; index->if_not_exists = false;
@ -1612,9 +1611,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
int nElems; int nElems;
int i; int i;
Assert(conrec->contype == CONSTRAINT_EXCLUSION || Assert(conrec->contype == CONSTRAINT_EXCLUSION);
(index->iswithoutoverlaps &&
(conrec->contype == CONSTRAINT_PRIMARY || conrec->contype == CONSTRAINT_UNIQUE)));
/* Extract operator OIDs from the pg_constraint tuple */ /* Extract operator OIDs from the pg_constraint tuple */
datum = SysCacheGetAttrNotNull(CONSTROID, ht_constr, datum = SysCacheGetAttrNotNull(CONSTROID, ht_constr,
Anum_pg_constraint_conexclop); Anum_pg_constraint_conexclop);
@ -2156,7 +2153,6 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
} }
index->nulls_not_distinct = constraint->nulls_not_distinct; index->nulls_not_distinct = constraint->nulls_not_distinct;
index->isconstraint = true; index->isconstraint = true;
index->iswithoutoverlaps = constraint->without_overlaps;
index->deferrable = constraint->deferrable; index->deferrable = constraint->deferrable;
index->initdeferred = constraint->initdeferred; index->initdeferred = constraint->initdeferred;
@ -2249,11 +2245,6 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
errmsg("index \"%s\" is not valid", index_name), errmsg("index \"%s\" is not valid", index_name),
parser_errposition(cxt->pstate, constraint->location))); parser_errposition(cxt->pstate, constraint->location)));
/*
* Today we forbid non-unique indexes, but we could permit GiST
* indexes whose last entry is a range type and use that to create a
* WITHOUT OVERLAPS constraint (i.e. a temporal constraint).
*/
if (!index_form->indisunique) if (!index_form->indisunique)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE), (errcode(ERRCODE_WRONG_OBJECT_TYPE),
@ -2542,23 +2533,6 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
notnullcmds = lappend(notnullcmds, notnullcmd); notnullcmds = lappend(notnullcmds, notnullcmd);
} }
} }
if (constraint->without_overlaps)
{
/*
* This enforces that there is at least one equality column
* besides the WITHOUT OVERLAPS columns. This is per SQL
* standard. XXX Do we need this?
*/
if (list_length(constraint->keys) < 2)
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
errmsg("constraint using WITHOUT OVERLAPS needs at least two columns"));
/* WITHOUT OVERLAPS requires a GiST index */
index->accessMethod = "gist";
}
} }
/* /*

View File

@ -30,7 +30,6 @@
#include "access/xact.h" #include "access/xact.h"
#include "catalog/pg_collation.h" #include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h" #include "catalog/pg_constraint.h"
#include "catalog/pg_proc.h"
#include "commands/trigger.h" #include "commands/trigger.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "executor/spi.h" #include "executor/spi.h"
@ -46,7 +45,6 @@
#include "utils/inval.h" #include "utils/inval.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/rangetypes.h"
#include "utils/rel.h" #include "utils/rel.h"
#include "utils/rls.h" #include "utils/rls.h"
#include "utils/ruleutils.h" #include "utils/ruleutils.h"
@ -98,9 +96,6 @@
* *
* Information extracted from an FK pg_constraint entry. This is cached in * Information extracted from an FK pg_constraint entry. This is cached in
* ri_constraint_cache. * ri_constraint_cache.
*
* Note that pf/pp/ff_eq_oprs may hold the overlaps operator instead of equals
* for the PERIOD part of a temporal foreign key.
*/ */
typedef struct RI_ConstraintInfo typedef struct RI_ConstraintInfo
{ {
@ -120,15 +115,12 @@ typedef struct RI_ConstraintInfo
int16 confdelsetcols[RI_MAX_NUMKEYS]; /* attnums of cols to set on int16 confdelsetcols[RI_MAX_NUMKEYS]; /* attnums of cols to set on
* delete */ * delete */
char confmatchtype; /* foreign key's match type */ char confmatchtype; /* foreign key's match type */
bool hasperiod; /* if the foreign key uses PERIOD */
int nkeys; /* number of key columns */ int nkeys; /* number of key columns */
int16 pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */ int16 pk_attnums[RI_MAX_NUMKEYS]; /* attnums of referenced cols */
int16 fk_attnums[RI_MAX_NUMKEYS]; /* attnums of referencing cols */ int16 fk_attnums[RI_MAX_NUMKEYS]; /* attnums of referencing cols */
Oid pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = FK) */ Oid pf_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = FK) */
Oid pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = PK) */ Oid pp_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (PK = PK) */
Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK = FK) */ Oid ff_eq_oprs[RI_MAX_NUMKEYS]; /* equality operators (FK = FK) */
Oid period_contained_by_oper; /* anyrange <@ anyrange */
Oid agged_period_contained_by_oper; /* fkattr <@ range_agg(pkattr) */
dlist_node valid_link; /* Link in list of valid entries */ dlist_node valid_link; /* Link in list of valid entries */
} RI_ConstraintInfo; } RI_ConstraintInfo;
@ -207,8 +199,8 @@ static void ri_BuildQueryKey(RI_QueryKey *key,
int32 constr_queryno); int32 constr_queryno);
static bool ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot, static bool ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
const RI_ConstraintInfo *riinfo, bool rel_is_pk); const RI_ConstraintInfo *riinfo, bool rel_is_pk);
static bool ri_CompareWithCast(Oid eq_opr, Oid typeid, static bool ri_AttributesEqual(Oid eq_opr, Oid typeid,
Datum lhs, Datum rhs); Datum oldvalue, Datum newvalue);
static void ri_InitHashTables(void); static void ri_InitHashTables(void);
static void InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue); static void InvalidateConstraintCacheCallBack(Datum arg, int cacheid, uint32 hashvalue);
@ -370,41 +362,14 @@ RI_FKey_check(TriggerData *trigdata)
* FOR KEY SHARE OF x * FOR KEY SHARE OF x
* The type id's for the $ parameters are those of the * The type id's for the $ parameters are those of the
* corresponding FK attributes. * corresponding FK attributes.
*
* But for temporal FKs we need to make sure
* the FK's range is completely covered.
* So we use this query instead:
* SELECT 1
* FROM (
* SELECT pkperiodatt AS r
* FROM [ONLY] pktable x
* WHERE pkatt1 = $1 [AND ...]
* AND pkperiodatt && $n
* FOR KEY SHARE OF x
* ) x1
* HAVING $n <@ range_agg(x1.r)
* Note if FOR KEY SHARE ever allows GROUP BY and HAVING
* we can make this a bit simpler.
* ---------- * ----------
*/ */
initStringInfo(&querybuf); initStringInfo(&querybuf);
pk_only = pk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ? pk_only = pk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
"" : "ONLY "; "" : "ONLY ";
quoteRelationName(pkrelname, pk_rel); quoteRelationName(pkrelname, pk_rel);
if (riinfo->hasperiod) appendStringInfo(&querybuf, "SELECT 1 FROM %s%s x",
{ pk_only, pkrelname);
quoteOneName(attname,
RIAttName(pk_rel, riinfo->pk_attnums[riinfo->nkeys - 1]));
appendStringInfo(&querybuf,
"SELECT 1 FROM (SELECT %s AS r FROM %s%s x",
attname, pk_only, pkrelname);
}
else
{
appendStringInfo(&querybuf, "SELECT 1 FROM %s%s x",
pk_only, pkrelname);
}
querysep = "WHERE"; querysep = "WHERE";
for (int i = 0; i < riinfo->nkeys; i++) for (int i = 0; i < riinfo->nkeys; i++)
{ {
@ -422,18 +387,6 @@ RI_FKey_check(TriggerData *trigdata)
queryoids[i] = fk_type; queryoids[i] = fk_type;
} }
appendStringInfoString(&querybuf, " FOR KEY SHARE OF x"); appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
if (riinfo->hasperiod)
{
Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[riinfo->nkeys - 1]);
appendStringInfoString(&querybuf, ") x1 HAVING ");
sprintf(paramname, "$%d", riinfo->nkeys);
ri_GenerateQual(&querybuf, "",
paramname, fk_type,
riinfo->agged_period_contained_by_oper,
"pg_catalog.range_agg", ANYMULTIRANGEOID);
appendStringInfoString(&querybuf, "(x1.r)");
}
/* Prepare and save the plan */ /* Prepare and save the plan */
qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids, qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
@ -541,39 +494,14 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
* FOR KEY SHARE OF x * FOR KEY SHARE OF x
* The type id's for the $ parameters are those of the * The type id's for the $ parameters are those of the
* PK attributes themselves. * PK attributes themselves.
* But for temporal FKs we need to make sure
* the FK's range is completely covered.
* So we use this query instead:
* SELECT 1
* FROM (
* SELECT pkperiodatt AS r
* FROM [ONLY] pktable x
* WHERE pkatt1 = $1 [AND ...]
* AND pkperiodatt && $n
* FOR KEY SHARE OF x
* ) x1
* HAVING $n <@ range_agg(x1.r)
* Note if FOR KEY SHARE ever allows GROUP BY and HAVING
* we can make this a bit simpler.
* ---------- * ----------
*/ */
initStringInfo(&querybuf); initStringInfo(&querybuf);
pk_only = pk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ? pk_only = pk_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ?
"" : "ONLY "; "" : "ONLY ";
quoteRelationName(pkrelname, pk_rel); quoteRelationName(pkrelname, pk_rel);
if (riinfo->hasperiod) appendStringInfo(&querybuf, "SELECT 1 FROM %s%s x",
{ pk_only, pkrelname);
quoteOneName(attname, RIAttName(pk_rel, riinfo->pk_attnums[riinfo->nkeys - 1]));
appendStringInfo(&querybuf,
"SELECT 1 FROM (SELECT %s AS r FROM %s%s x",
attname, pk_only, pkrelname);
}
else
{
appendStringInfo(&querybuf, "SELECT 1 FROM %s%s x",
pk_only, pkrelname);
}
querysep = "WHERE"; querysep = "WHERE";
for (int i = 0; i < riinfo->nkeys; i++) for (int i = 0; i < riinfo->nkeys; i++)
{ {
@ -590,18 +518,6 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel,
queryoids[i] = pk_type; queryoids[i] = pk_type;
} }
appendStringInfoString(&querybuf, " FOR KEY SHARE OF x"); appendStringInfoString(&querybuf, " FOR KEY SHARE OF x");
if (riinfo->hasperiod)
{
Oid fk_type = RIAttType(fk_rel, riinfo->fk_attnums[riinfo->nkeys - 1]);
appendStringInfoString(&querybuf, ") x1 HAVING ");
sprintf(paramname, "$%d", riinfo->nkeys);
ri_GenerateQual(&querybuf, "",
paramname, fk_type,
riinfo->agged_period_contained_by_oper,
"pg_catalog.range_agg", ANYMULTIRANGEOID);
appendStringInfoString(&querybuf, "(x1.r)");
}
/* Prepare and save the plan */ /* Prepare and save the plan */
qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids, qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids,
@ -2246,7 +2162,6 @@ ri_LoadConstraintInfo(Oid constraintOid)
riinfo->confupdtype = conForm->confupdtype; riinfo->confupdtype = conForm->confupdtype;
riinfo->confdeltype = conForm->confdeltype; riinfo->confdeltype = conForm->confdeltype;
riinfo->confmatchtype = conForm->confmatchtype; riinfo->confmatchtype = conForm->confmatchtype;
riinfo->hasperiod = conForm->conperiod;
DeconstructFkConstraintRow(tup, DeconstructFkConstraintRow(tup,
&riinfo->nkeys, &riinfo->nkeys,
@ -2258,20 +2173,6 @@ ri_LoadConstraintInfo(Oid constraintOid)
&riinfo->ndelsetcols, &riinfo->ndelsetcols,
riinfo->confdelsetcols); riinfo->confdelsetcols);
/*
* For temporal FKs, get the operators and functions we need. We ask the
* opclass of the PK element for these. This all gets cached (as does the
* generated plan), so there's no performance issue.
*/
if (riinfo->hasperiod)
{
Oid opclass = get_index_column_opclass(conForm->conindid, riinfo->nkeys);
FindFKPeriodOpers(opclass,
&riinfo->period_contained_by_oper,
&riinfo->agged_period_contained_by_oper);
}
ReleaseSysCache(tup); ReleaseSysCache(tup);
/* /*
@ -2883,10 +2784,7 @@ ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
/* /*
* ri_KeysEqual - * ri_KeysEqual -
* *
* Check if all key values in OLD and NEW are "equivalent": * Check if all key values in OLD and NEW are equal.
* For normal FKs we check for equality.
* For temporal FKs we check that the PK side is a superset of its old value,
* or the FK side is a subset of its old value.
* *
* Note: at some point we might wish to redefine this as checking for * Note: at some point we might wish to redefine this as checking for
* "IS NOT DISTINCT" rather than "=", that is, allow two nulls to be * "IS NOT DISTINCT" rather than "=", that is, allow two nulls to be
@ -2942,25 +2840,13 @@ ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
} }
else else
{ {
Oid eq_opr;
/*
* When comparing the PERIOD columns we can skip the check
* whenever the referencing column stayed equal or shrank, so test
* with the contained-by operator instead.
*/
if (riinfo->hasperiod && i == riinfo->nkeys - 1)
eq_opr = riinfo->period_contained_by_oper;
else
eq_opr = riinfo->ff_eq_oprs[i];
/* /*
* For the FK table, compare with the appropriate equality * For the FK table, compare with the appropriate equality
* operator. Changes that compare equal will still satisfy the * operator. Changes that compare equal will still satisfy the
* constraint after the update. * constraint after the update.
*/ */
if (!ri_CompareWithCast(eq_opr, RIAttType(rel, attnums[i]), if (!ri_AttributesEqual(riinfo->ff_eq_oprs[i], RIAttType(rel, attnums[i]),
newvalue, oldvalue)) oldvalue, newvalue))
return false; return false;
} }
} }
@ -2970,31 +2856,29 @@ ri_KeysEqual(Relation rel, TupleTableSlot *oldslot, TupleTableSlot *newslot,
/* /*
* ri_CompareWithCast - * ri_AttributesEqual -
* *
* Call the appropriate comparison operator for two values. * Call the appropriate equality comparison operator for two values.
* Normally this is equality, but for the PERIOD part of foreign keys
* it is ContainedBy, so the order of lhs vs rhs is significant.
* *
* NB: we have already checked that neither value is null. * NB: we have already checked that neither value is null.
*/ */
static bool static bool
ri_CompareWithCast(Oid eq_opr, Oid typeid, ri_AttributesEqual(Oid eq_opr, Oid typeid,
Datum lhs, Datum rhs) Datum oldvalue, Datum newvalue)
{ {
RI_CompareHashEntry *entry = ri_HashCompareOp(eq_opr, typeid); RI_CompareHashEntry *entry = ri_HashCompareOp(eq_opr, typeid);
/* Do we need to cast the values? */ /* Do we need to cast the values? */
if (OidIsValid(entry->cast_func_finfo.fn_oid)) if (OidIsValid(entry->cast_func_finfo.fn_oid))
{ {
lhs = FunctionCall3(&entry->cast_func_finfo, oldvalue = FunctionCall3(&entry->cast_func_finfo,
lhs, oldvalue,
Int32GetDatum(-1), /* typmod */ Int32GetDatum(-1), /* typmod */
BoolGetDatum(false)); /* implicit coercion */ BoolGetDatum(false)); /* implicit coercion */
rhs = FunctionCall3(&entry->cast_func_finfo, newvalue = FunctionCall3(&entry->cast_func_finfo,
rhs, newvalue,
Int32GetDatum(-1), /* typmod */ Int32GetDatum(-1), /* typmod */
BoolGetDatum(false)); /* implicit coercion */ BoolGetDatum(false)); /* implicit coercion */
} }
/* /*
@ -3008,16 +2892,10 @@ ri_CompareWithCast(Oid eq_opr, Oid typeid,
* open), we'll just use the default collation here, which could lead to * open), we'll just use the default collation here, which could lead to
* some false negatives. All this would break if we ever allow * some false negatives. All this would break if we ever allow
* database-wide collations to be nondeterministic. * database-wide collations to be nondeterministic.
*
* With range/multirangetypes, the collation of the base type is stored as
* part of the rangetype (pg_range.rngcollation), and always used, so
* there is no danger of inconsistency even using a non-equals operator.
* But if we support arbitrary types with PERIOD, we should perhaps just
* always force a re-check.
*/ */
return DatumGetBool(FunctionCall2Coll(&entry->eq_opr_finfo, return DatumGetBool(FunctionCall2Coll(&entry->eq_opr_finfo,
DEFAULT_COLLATION_OID, DEFAULT_COLLATION_OID,
lhs, rhs)); oldvalue, newvalue));
} }
/* /*
@ -3072,7 +2950,7 @@ ri_HashCompareOp(Oid eq_opr, Oid typeid)
* the cast function to get to the operator's input type. * the cast function to get to the operator's input type.
* *
* XXX eventually it would be good to support array-coercion cases * XXX eventually it would be good to support array-coercion cases
* here and in ri_CompareWithCast(). At the moment there is no point * here and in ri_AttributesEqual(). At the moment there is no point
* because cases involving nonidentical array types will be rejected * because cases involving nonidentical array types will be rejected
* at constraint creation time. * at constraint creation time.
* *

View File

@ -338,7 +338,7 @@ static char *pg_get_viewdef_worker(Oid viewoid,
int prettyFlags, int wrapColumn); int prettyFlags, int wrapColumn);
static char *pg_get_triggerdef_worker(Oid trigid, bool pretty); static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
static int decompile_column_index_array(Datum column_index_array, Oid relId, static int decompile_column_index_array(Datum column_index_array, Oid relId,
bool withPeriod, StringInfo buf); StringInfo buf);
static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags); static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
static char *pg_get_indexdef_worker(Oid indexrelid, int colno, static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
const Oid *excludeOps, const Oid *excludeOps,
@ -2260,8 +2260,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
val = SysCacheGetAttrNotNull(CONSTROID, tup, val = SysCacheGetAttrNotNull(CONSTROID, tup,
Anum_pg_constraint_conkey); Anum_pg_constraint_conkey);
/* If it is a temporal foreign key then it uses PERIOD. */ decompile_column_index_array(val, conForm->conrelid, &buf);
decompile_column_index_array(val, conForm->conrelid, conForm->conperiod, &buf);
/* add foreign relation name */ /* add foreign relation name */
appendStringInfo(&buf, ") REFERENCES %s(", appendStringInfo(&buf, ") REFERENCES %s(",
@ -2272,7 +2271,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
val = SysCacheGetAttrNotNull(CONSTROID, tup, val = SysCacheGetAttrNotNull(CONSTROID, tup,
Anum_pg_constraint_confkey); Anum_pg_constraint_confkey);
decompile_column_index_array(val, conForm->confrelid, conForm->conperiod, &buf); decompile_column_index_array(val, conForm->confrelid, &buf);
appendStringInfoChar(&buf, ')'); appendStringInfoChar(&buf, ')');
@ -2358,7 +2357,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
if (!isnull) if (!isnull)
{ {
appendStringInfoString(&buf, " ("); appendStringInfoString(&buf, " (");
decompile_column_index_array(val, conForm->conrelid, false, &buf); decompile_column_index_array(val, conForm->conrelid, &buf);
appendStringInfoChar(&buf, ')'); appendStringInfoChar(&buf, ')');
} }
@ -2393,9 +2392,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
val = SysCacheGetAttrNotNull(CONSTROID, tup, val = SysCacheGetAttrNotNull(CONSTROID, tup,
Anum_pg_constraint_conkey); Anum_pg_constraint_conkey);
keyatts = decompile_column_index_array(val, conForm->conrelid, false, &buf); keyatts = decompile_column_index_array(val, conForm->conrelid, &buf);
if (conForm->conperiod)
appendStringInfoString(&buf, " WITHOUT OVERLAPS");
appendStringInfoChar(&buf, ')'); appendStringInfoChar(&buf, ')');
@ -2577,7 +2574,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
*/ */
static int static int
decompile_column_index_array(Datum column_index_array, Oid relId, decompile_column_index_array(Datum column_index_array, Oid relId,
bool withPeriod, StringInfo buf) StringInfo buf)
{ {
Datum *keys; Datum *keys;
int nKeys; int nKeys;
@ -2596,9 +2593,7 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
if (j == 0) if (j == 0)
appendStringInfoString(buf, quote_identifier(colName)); appendStringInfoString(buf, quote_identifier(colName));
else else
appendStringInfo(buf, ", %s%s", appendStringInfo(buf, ", %s", quote_identifier(colName));
(withPeriod && j == nKeys - 1) ? "PERIOD " : "",
quote_identifier(colName));
} }
return nKeys; return nKeys;

View File

@ -5540,14 +5540,11 @@ RelationGetIdentityKeyBitmap(Relation relation)
/* /*
* RelationGetExclusionInfo -- get info about index's exclusion constraint * RelationGetExclusionInfo -- get info about index's exclusion constraint
* *
* This should be called only for an index that is known to have an associated * This should be called only for an index that is known to have an
* exclusion constraint or primary key/unique constraint using WITHOUT * associated exclusion constraint. It returns arrays (palloc'd in caller's
* OVERLAPS. * context) of the exclusion operator OIDs, their underlying functions'
* OIDs, and their strategy numbers in the index's opclasses. We cache
* It returns arrays (palloc'd in caller's context) of the exclusion operator * all this information since it requires a fair amount of work to get.
* OIDs, their underlying functions' OIDs, and their strategy numbers in the
* index's opclasses. We cache all this information since it requires a fair
* amount of work to get.
*/ */
void void
RelationGetExclusionInfo(Relation indexRelation, RelationGetExclusionInfo(Relation indexRelation,
@ -5611,10 +5608,7 @@ RelationGetExclusionInfo(Relation indexRelation,
int nelem; int nelem;
/* We want the exclusion constraint owning the index */ /* We want the exclusion constraint owning the index */
if ((conform->contype != CONSTRAINT_EXCLUSION && if (conform->contype != CONSTRAINT_EXCLUSION ||
!(conform->conperiod && (
conform->contype == CONSTRAINT_PRIMARY
|| conform->contype == CONSTRAINT_UNIQUE))) ||
conform->conindid != RelationGetRelid(indexRelation)) conform->conindid != RelationGetRelid(indexRelation))
continue; continue;

View File

@ -7344,7 +7344,6 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
i_conname, i_conname,
i_condeferrable, i_condeferrable,
i_condeferred, i_condeferred,
i_conperiod,
i_contableoid, i_contableoid,
i_conoid, i_conoid,
i_condef, i_condef,
@ -7426,17 +7425,10 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
if (fout->remoteVersion >= 150000) if (fout->remoteVersion >= 150000)
appendPQExpBufferStr(query, appendPQExpBufferStr(query,
"i.indnullsnotdistinct, "); "i.indnullsnotdistinct ");
else else
appendPQExpBufferStr(query, appendPQExpBufferStr(query,
"false AS indnullsnotdistinct, "); "false AS indnullsnotdistinct ");
if (fout->remoteVersion >= 170000)
appendPQExpBufferStr(query,
"c.conperiod ");
else
appendPQExpBufferStr(query,
"NULL AS conperiod ");
/* /*
* The point of the messy-looking outer join is to find a constraint that * The point of the messy-looking outer join is to find a constraint that
@ -7504,7 +7496,6 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
i_conname = PQfnumber(res, "conname"); i_conname = PQfnumber(res, "conname");
i_condeferrable = PQfnumber(res, "condeferrable"); i_condeferrable = PQfnumber(res, "condeferrable");
i_condeferred = PQfnumber(res, "condeferred"); i_condeferred = PQfnumber(res, "condeferred");
i_conperiod = PQfnumber(res, "conperiod");
i_contableoid = PQfnumber(res, "contableoid"); i_contableoid = PQfnumber(res, "contableoid");
i_conoid = PQfnumber(res, "conoid"); i_conoid = PQfnumber(res, "conoid");
i_condef = PQfnumber(res, "condef"); i_condef = PQfnumber(res, "condef");
@ -7612,7 +7603,6 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
constrinfo->conindex = indxinfo[j].dobj.dumpId; constrinfo->conindex = indxinfo[j].dobj.dumpId;
constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't'; constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't'; constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
constrinfo->conislocal = true; constrinfo->conislocal = true;
constrinfo->separate = true; constrinfo->separate = true;
@ -17058,8 +17048,6 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
(k == 0) ? "" : ", ", (k == 0) ? "" : ", ",
fmtId(attname)); fmtId(attname));
} }
if (coninfo->conperiod)
appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
if (indxinfo->indnkeyattrs < indxinfo->indnattrs) if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
appendPQExpBufferStr(q, ") INCLUDE ("); appendPQExpBufferStr(q, ") INCLUDE (");

View File

@ -474,7 +474,6 @@ typedef struct _constraintInfo
DumpId conindex; /* identifies associated index if any */ DumpId conindex; /* identifies associated index if any */
bool condeferrable; /* true if constraint is DEFERRABLE */ bool condeferrable; /* true if constraint is DEFERRABLE */
bool condeferred; /* true if constraint is INITIALLY DEFERRED */ bool condeferred; /* true if constraint is INITIALLY DEFERRED */
bool conperiod; /* true if the constraint is WITHOUT OVERLAPS */
bool conislocal; /* true if constraint has local definition */ bool conislocal; /* true if constraint has local definition */
bool separate; /* true if must dump as separate item */ bool separate; /* true if must dump as separate item */
} ConstraintInfo; } ConstraintInfo;

View File

@ -1004,42 +1004,6 @@ my %tests = (
}, },
}, },
'CONSTRAINT PRIMARY KEY / WITHOUT OVERLAPS' => {
create_sql => 'CREATE TABLE dump_test.test_table_tpk (
col1 int4range,
col2 tstzrange,
CONSTRAINT test_table_tpk_pkey PRIMARY KEY (col1, col2 WITHOUT OVERLAPS));',
regexp => qr/^
\QALTER TABLE ONLY dump_test.test_table_tpk\E \n^\s+
\QADD CONSTRAINT test_table_tpk_pkey PRIMARY KEY (col1, col2 WITHOUT OVERLAPS);\E
/xm,
like => {
%full_runs, %dump_test_schema_runs, section_post_data => 1,
},
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'CONSTRAINT UNIQUE / WITHOUT OVERLAPS' => {
create_sql => 'CREATE TABLE dump_test.test_table_tuq (
col1 int4range,
col2 tstzrange,
CONSTRAINT test_table_tuq_uq UNIQUE (col1, col2 WITHOUT OVERLAPS));',
regexp => qr/^
\QALTER TABLE ONLY dump_test.test_table_tuq\E \n^\s+
\QADD CONSTRAINT test_table_tuq_uq UNIQUE (col1, col2 WITHOUT OVERLAPS);\E
/xm,
like => {
%full_runs, %dump_test_schema_runs, section_post_data => 1,
},
unlike => {
exclude_dump_test_schema => 1,
only_dump_measurement => 1,
},
},
'ALTER TABLE (partitioned) ADD CONSTRAINT ... FOREIGN KEY' => { 'ALTER TABLE (partitioned) ADD CONSTRAINT ... FOREIGN KEY' => {
create_order => 4, create_order => 4,
create_sql => 'CREATE TABLE dump_test.test_table_fk ( create_sql => 'CREATE TABLE dump_test.test_table_fk (

View File

@ -2383,10 +2383,6 @@ describeOneTableDetails(const char *schemaname,
else else
appendPQExpBufferStr(&buf, ", false AS indisreplident"); appendPQExpBufferStr(&buf, ", false AS indisreplident");
appendPQExpBufferStr(&buf, ", c2.reltablespace"); appendPQExpBufferStr(&buf, ", c2.reltablespace");
if (pset.sversion >= 170000)
appendPQExpBufferStr(&buf, ", con.conperiod");
else
appendPQExpBufferStr(&buf, ", false AS conperiod");
appendPQExpBuffer(&buf, appendPQExpBuffer(&buf,
"\nFROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n" "\nFROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\n"
" LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ('p','u','x'))\n" " LEFT JOIN pg_catalog.pg_constraint con ON (conrelid = i.indrelid AND conindid = i.indexrelid AND contype IN ('p','u','x'))\n"
@ -2408,12 +2404,8 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf, " \"%s\"", printfPQExpBuffer(&buf, " \"%s\"",
PQgetvalue(result, i, 0)); PQgetvalue(result, i, 0));
/* /* If exclusion constraint, print the constraintdef */
* If exclusion constraint or PK/UNIQUE constraint WITHOUT if (strcmp(PQgetvalue(result, i, 7), "x") == 0)
* OVERLAPS, print the constraintdef
*/
if (strcmp(PQgetvalue(result, i, 7), "x") == 0 ||
strcmp(PQgetvalue(result, i, 12), "t") == 0)
{ {
appendPQExpBuffer(&buf, " %s", appendPQExpBuffer(&buf, " %s",
PQgetvalue(result, i, 6)); PQgetvalue(result, i, 6));

View File

@ -17,7 +17,6 @@
#define GIST_H #define GIST_H
#include "access/itup.h" #include "access/itup.h"
#include "access/stratnum.h"
#include "access/transam.h" #include "access/transam.h"
#include "access/xlog.h" #include "access/xlog.h"
#include "access/xlogdefs.h" #include "access/xlogdefs.h"
@ -39,8 +38,7 @@
#define GIST_FETCH_PROC 9 #define GIST_FETCH_PROC 9
#define GIST_OPTIONS_PROC 10 #define GIST_OPTIONS_PROC 10
#define GIST_SORTSUPPORT_PROC 11 #define GIST_SORTSUPPORT_PROC 11
#define GIST_STRATNUM_PROC 12 #define GISTNProcs 11
#define GISTNProcs 12
/* /*
* Page opaque data in a GiST index page. * Page opaque data in a GiST index page.
@ -247,6 +245,4 @@ typedef struct
do { (e).key = (k); (e).rel = (r); (e).page = (pg); \ do { (e).key = (k); (e).rel = (r); (e).page = (pg); \
(e).offset = (o); (e).leafkey = (l); } while (0) (e).offset = (o); (e).leafkey = (l); } while (0)
extern StrategyNumber GistTranslateStratnum(Oid opclass, StrategyNumber strat);
#endif /* GIST_H */ #endif /* GIST_H */

View File

@ -57,6 +57,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 202405141 #define CATALOG_VERSION_NO 202405161
#endif #endif

View File

@ -93,7 +93,6 @@ extern Oid index_create(Relation heapRelation,
#define INDEX_CONSTR_CREATE_INIT_DEFERRED (1 << 2) #define INDEX_CONSTR_CREATE_INIT_DEFERRED (1 << 2)
#define INDEX_CONSTR_CREATE_UPDATE_INDEX (1 << 3) #define INDEX_CONSTR_CREATE_UPDATE_INDEX (1 << 3)
#define INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS (1 << 4) #define INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS (1 << 4)
#define INDEX_CONSTR_CREATE_WITHOUT_OVERLAPS (1 << 5)
extern Oid index_concurrently_create_copy(Relation heapRelation, extern Oid index_concurrently_create_copy(Relation heapRelation,
Oid oldIndexId, Oid oldIndexId,

View File

@ -507,9 +507,6 @@
amprocrighttype => 'box', amprocnum => '7', amproc => 'gist_box_same' }, amprocrighttype => 'box', amprocnum => '7', amproc => 'gist_box_same' },
{ amprocfamily => 'gist/box_ops', amproclefttype => 'box', { amprocfamily => 'gist/box_ops', amproclefttype => 'box',
amprocrighttype => 'box', amprocnum => '8', amproc => 'gist_box_distance' }, amprocrighttype => 'box', amprocnum => '8', amproc => 'gist_box_distance' },
{ amprocfamily => 'gist/box_ops', amproclefttype => 'box',
amprocrighttype => 'box', amprocnum => '12',
amproc => 'gist_stratnum_identity' },
{ amprocfamily => 'gist/poly_ops', amproclefttype => 'polygon', { amprocfamily => 'gist/poly_ops', amproclefttype => 'polygon',
amprocrighttype => 'polygon', amprocnum => '1', amprocrighttype => 'polygon', amprocnum => '1',
amproc => 'gist_poly_consistent' }, amproc => 'gist_poly_consistent' },
@ -529,9 +526,6 @@
{ amprocfamily => 'gist/poly_ops', amproclefttype => 'polygon', { amprocfamily => 'gist/poly_ops', amproclefttype => 'polygon',
amprocrighttype => 'polygon', amprocnum => '8', amprocrighttype => 'polygon', amprocnum => '8',
amproc => 'gist_poly_distance' }, amproc => 'gist_poly_distance' },
{ amprocfamily => 'gist/poly_ops', amproclefttype => 'polygon',
amprocrighttype => 'polygon', amprocnum => '12',
amproc => 'gist_stratnum_identity' },
{ amprocfamily => 'gist/circle_ops', amproclefttype => 'circle', { amprocfamily => 'gist/circle_ops', amproclefttype => 'circle',
amprocrighttype => 'circle', amprocnum => '1', amprocrighttype => 'circle', amprocnum => '1',
amproc => 'gist_circle_consistent' }, amproc => 'gist_circle_consistent' },
@ -550,9 +544,6 @@
{ amprocfamily => 'gist/circle_ops', amproclefttype => 'circle', { amprocfamily => 'gist/circle_ops', amproclefttype => 'circle',
amprocrighttype => 'circle', amprocnum => '8', amprocrighttype => 'circle', amprocnum => '8',
amproc => 'gist_circle_distance' }, amproc => 'gist_circle_distance' },
{ amprocfamily => 'gist/circle_ops', amproclefttype => 'circle',
amprocrighttype => 'circle', amprocnum => '12',
amproc => 'gist_stratnum_identity' },
{ amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector', { amprocfamily => 'gist/tsvector_ops', amproclefttype => 'tsvector',
amprocrighttype => 'tsvector', amprocnum => '1', amprocrighttype => 'tsvector', amprocnum => '1',
amproc => 'gtsvector_consistent(internal,tsvector,int2,oid,internal)' }, amproc => 'gtsvector_consistent(internal,tsvector,int2,oid,internal)' },
@ -607,9 +598,6 @@
{ amprocfamily => 'gist/range_ops', amproclefttype => 'anyrange', { amprocfamily => 'gist/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '7', amprocrighttype => 'anyrange', amprocnum => '7',
amproc => 'range_gist_same' }, amproc => 'range_gist_same' },
{ amprocfamily => 'gist/range_ops', amproclefttype => 'anyrange',
amprocrighttype => 'anyrange', amprocnum => '12',
amproc => 'gist_stratnum_identity' },
{ amprocfamily => 'gist/network_ops', amproclefttype => 'inet', { amprocfamily => 'gist/network_ops', amproclefttype => 'inet',
amprocrighttype => 'inet', amprocnum => '1', amprocrighttype => 'inet', amprocnum => '1',
amproc => 'inet_gist_consistent' }, amproc => 'inet_gist_consistent' },
@ -626,9 +614,6 @@
amprocrighttype => 'inet', amprocnum => '7', amproc => 'inet_gist_same' }, amprocrighttype => 'inet', amprocnum => '7', amproc => 'inet_gist_same' },
{ amprocfamily => 'gist/network_ops', amproclefttype => 'inet', { amprocfamily => 'gist/network_ops', amproclefttype => 'inet',
amprocrighttype => 'inet', amprocnum => '9', amproc => 'inet_gist_fetch' }, amprocrighttype => 'inet', amprocnum => '9', amproc => 'inet_gist_fetch' },
{ amprocfamily => 'gist/network_ops', amproclefttype => 'inet',
amprocrighttype => 'inet', amprocnum => '12',
amproc => 'gist_stratnum_identity' },
{ amprocfamily => 'gist/multirange_ops', amproclefttype => 'anymultirange', { amprocfamily => 'gist/multirange_ops', amproclefttype => 'anymultirange',
amprocrighttype => 'anymultirange', amprocnum => '1', amprocrighttype => 'anymultirange', amprocnum => '1',
amproc => 'multirange_gist_consistent' }, amproc => 'multirange_gist_consistent' },
@ -647,9 +632,6 @@
{ amprocfamily => 'gist/multirange_ops', amproclefttype => 'anymultirange', { amprocfamily => 'gist/multirange_ops', amproclefttype => 'anymultirange',
amprocrighttype => 'anymultirange', amprocnum => '7', amprocrighttype => 'anymultirange', amprocnum => '7',
amproc => 'range_gist_same' }, amproc => 'range_gist_same' },
{ amprocfamily => 'gist/multirange_ops', amproclefttype => 'anymultirange',
amprocrighttype => 'anymultirange', amprocnum => '12',
amproc => 'gist_stratnum_identity' },
# gin # gin
{ amprocfamily => 'gin/array_ops', amproclefttype => 'anyarray', { amprocfamily => 'gin/array_ops', amproclefttype => 'anyarray',

View File

@ -107,12 +107,6 @@ CATALOG(pg_constraint,2606,ConstraintRelationId)
/* Has a local definition and cannot be inherited */ /* Has a local definition and cannot be inherited */
bool connoinherit; bool connoinherit;
/*
* For primary keys, unique constraints, and foreign keys, signifies the
* last column uses overlaps instead of equals.
*/
bool conperiod;
#ifdef CATALOG_VARLEN /* variable-length fields start here */ #ifdef CATALOG_VARLEN /* variable-length fields start here */
/* /*
@ -127,22 +121,20 @@ CATALOG(pg_constraint,2606,ConstraintRelationId)
int16 confkey[1]; int16 confkey[1];
/* /*
* If a foreign key, the OIDs of the PK = FK equality/overlap operators * If a foreign key, the OIDs of the PK = FK equality operators for each
* for each column of the constraint * column of the constraint
*/ */
Oid conpfeqop[1] BKI_LOOKUP(pg_operator); Oid conpfeqop[1] BKI_LOOKUP(pg_operator);
/* /*
* If a foreign key, the OIDs of the PK = PK equality/overlap operators * If a foreign key, the OIDs of the PK = PK equality operators for each
* for each column of the constraint (i.e., equality for the referenced * column of the constraint (i.e., equality for the referenced columns)
* columns)
*/ */
Oid conppeqop[1] BKI_LOOKUP(pg_operator); Oid conppeqop[1] BKI_LOOKUP(pg_operator);
/* /*
* If a foreign key, the OIDs of the FK = FK equality/overlap operators * If a foreign key, the OIDs of the FK = FK equality operators for each
* for each column of the constraint (i.e., equality for the referencing * column of the constraint (i.e., equality for the referencing columns)
* columns)
*/ */
Oid conffeqop[1] BKI_LOOKUP(pg_operator); Oid conffeqop[1] BKI_LOOKUP(pg_operator);
@ -154,8 +146,7 @@ CATALOG(pg_constraint,2606,ConstraintRelationId)
/* /*
* If an exclusion constraint, the OIDs of the exclusion operators for * If an exclusion constraint, the OIDs of the exclusion operators for
* each column of the constraint. Also set for unique constraints/primary * each column of the constraint
* keys using WITHOUT OVERLAPS.
*/ */
Oid conexclop[1] BKI_LOOKUP(pg_operator); Oid conexclop[1] BKI_LOOKUP(pg_operator);
@ -247,7 +238,6 @@ extern Oid CreateConstraintEntry(const char *constraintName,
bool conIsLocal, bool conIsLocal,
int conInhCount, int conInhCount,
bool conNoInherit, bool conNoInherit,
bool conPeriod,
bool is_internal); bool is_internal);
extern bool ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId, extern bool ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId,
@ -279,9 +269,6 @@ extern void DeconstructFkConstraintRow(HeapTuple tuple, int *numfks,
AttrNumber *conkey, AttrNumber *confkey, AttrNumber *conkey, AttrNumber *confkey,
Oid *pf_eq_oprs, Oid *pp_eq_oprs, Oid *ff_eq_oprs, Oid *pf_eq_oprs, Oid *pp_eq_oprs, Oid *ff_eq_oprs,
int *num_fk_del_set_cols, AttrNumber *fk_del_set_cols); int *num_fk_del_set_cols, AttrNumber *fk_del_set_cols);
extern void FindFKPeriodOpers(Oid opclass,
Oid *containedbyoperoid,
Oid *aggedcontainedbyoperoid);
extern bool check_functional_grouping(Oid relid, extern bool check_functional_grouping(Oid relid,
Index varno, Index varlevelsup, Index varno, Index varlevelsup,

View File

@ -12185,9 +12185,4 @@
proargnames => '{summarized_tli,summarized_lsn,pending_lsn,summarizer_pid}', proargnames => '{summarized_tli,summarized_lsn,pending_lsn,summarizer_pid}',
prosrc => 'pg_get_wal_summarizer_state' }, prosrc => 'pg_get_wal_summarizer_state' },
# GiST stratnum implementations
{ oid => '6313', descr => 'GiST support',
proname => 'gist_stratnum_identity', prorettype => 'int2',
proargtypes => 'int2', prosrc => 'gist_stratnum_identity' },
] ]

View File

@ -14,7 +14,6 @@
#ifndef DEFREM_H #ifndef DEFREM_H
#define DEFREM_H #define DEFREM_H
#include "access/stratnum.h"
#include "catalog/objectaddress.h" #include "catalog/objectaddress.h"
#include "nodes/params.h" #include "nodes/params.h"
#include "parser/parse_node.h" #include "parser/parse_node.h"
@ -45,13 +44,10 @@ extern char *ChooseRelationName(const char *name1, const char *name2,
extern bool CheckIndexCompatible(Oid oldId, extern bool CheckIndexCompatible(Oid oldId,
const char *accessMethodName, const char *accessMethodName,
const List *attributeList, const List *attributeList,
const List *exclusionOpNames, const List *exclusionOpNames);
bool isWithoutOverlaps);
extern Oid GetDefaultOpClass(Oid type_id, Oid am_id); extern Oid GetDefaultOpClass(Oid type_id, Oid am_id);
extern Oid ResolveOpClass(const List *opclass, Oid attrType, extern Oid ResolveOpClass(const List *opclass, Oid attrType,
const char *accessMethodName, Oid accessMethodId); const char *accessMethodName, Oid accessMethodId);
extern void GetOperatorFromWellKnownStrategy(Oid opclass, Oid rhstype,
Oid *opid, StrategyNumber *strat);
/* commands/functioncmds.c */ /* commands/functioncmds.c */
extern ObjectAddress CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt); extern ObjectAddress CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt);

View File

@ -2750,7 +2750,6 @@ typedef struct Constraint
bool nulls_not_distinct; /* null treatment for UNIQUE constraints */ bool nulls_not_distinct; /* null treatment for UNIQUE constraints */
List *keys; /* String nodes naming referenced key List *keys; /* String nodes naming referenced key
* column(s); for UNIQUE/PK/NOT NULL */ * column(s); for UNIQUE/PK/NOT NULL */
bool without_overlaps; /* WITHOUT OVERLAPS specified */
List *including; /* String nodes naming referenced nonkey List *including; /* String nodes naming referenced nonkey
* column(s); for UNIQUE/PK */ * column(s); for UNIQUE/PK */
List *exclusions; /* list of (IndexElem, operator name) pairs; List *exclusions; /* list of (IndexElem, operator name) pairs;
@ -2767,8 +2766,6 @@ typedef struct Constraint
RangeVar *pktable; /* Primary key table */ RangeVar *pktable; /* Primary key table */
List *fk_attrs; /* Attributes of foreign key */ List *fk_attrs; /* Attributes of foreign key */
List *pk_attrs; /* Corresponding attrs in PK table */ List *pk_attrs; /* Corresponding attrs in PK table */
bool fk_with_period; /* Last attribute of FK uses PERIOD */
bool pk_with_period; /* Last attribute of PK uses PERIOD */
char fk_matchtype; /* FULL, PARTIAL, SIMPLE */ char fk_matchtype; /* FULL, PARTIAL, SIMPLE */
char fk_upd_action; /* ON UPDATE action */ char fk_upd_action; /* ON UPDATE action */
char fk_del_action; /* ON DELETE action */ char fk_del_action; /* ON DELETE action */
@ -3376,7 +3373,6 @@ typedef struct IndexStmt
bool nulls_not_distinct; /* null treatment for UNIQUE constraints */ bool nulls_not_distinct; /* null treatment for UNIQUE constraints */
bool primary; /* is index a primary key? */ bool primary; /* is index a primary key? */
bool isconstraint; /* is it for a pkey/unique constraint? */ bool isconstraint; /* is it for a pkey/unique constraint? */
bool iswithoutoverlaps; /* is the constraint WITHOUT OVERLAPS? */
bool deferrable; /* is the constraint DEFERRABLE? */ bool deferrable; /* is the constraint DEFERRABLE? */
bool initdeferred; /* is the constraint INITIALLY DEFERRED? */ bool initdeferred; /* is the constraint INITIALLY DEFERRED? */
bool transformed; /* true when transformIndexStmt is finished */ bool transformed; /* true when transformIndexStmt is finished */

View File

@ -339,7 +339,6 @@ PG_KEYWORD("partitions", PARTITIONS, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("passing", PASSING, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("passing", PASSING, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("password", PASSWORD, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("password", PASSWORD, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("path", PATH, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("path", PATH, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("period", PERIOD, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("placing", PLACING, RESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("placing", PLACING, RESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("plan", PLAN, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("plan", PLAN, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("plans", PLANS, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("plans", PLANS, UNRESERVED_KEYWORD, BARE_LABEL)

View File

@ -670,19 +670,6 @@ FROM pg_walfile_name_offset('0/0'::pg_lsn + :segment_size - 1),
0 | t 0 | t
(1 row) (1 row)
-- test stratnum support functions
SELECT gist_stratnum_identity(3::smallint);
gist_stratnum_identity
------------------------
3
(1 row)
SELECT gist_stratnum_identity(18::smallint);
gist_stratnum_identity
------------------------
18
(1 row)
-- pg_current_logfile -- pg_current_logfile
CREATE ROLE regress_current_logfile; CREATE ROLE regress_current_logfile;
-- not available by default -- not available by default

File diff suppressed because it is too large Load Diff

View File

@ -78,7 +78,7 @@ test: brin_bloom brin_multi
# psql depends on create_am # psql depends on create_am
# amutils depends on geometry, create_index_spgist, hash_index, brin # amutils depends on geometry, create_index_spgist, hash_index, brin
# ---------- # ----------
test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.utf8 collate.icu.utf8 incremental_sort create_role without_overlaps test: create_table_like alter_generic alter_operator misc async dbsize merge misc_functions sysviews tsrf tid tidscan tidrangescan collate.utf8 collate.icu.utf8 incremental_sort create_role
# collate.linux.utf8 and collate.icu.utf8 tests cannot be run in parallel with each other # collate.linux.utf8 and collate.icu.utf8 tests cannot be run in parallel with each other
test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252 test: rules psql psql_crosstab amutils stats_ext collate.linux.utf8 collate.windows.win1252

View File

@ -251,10 +251,6 @@ SELECT segment_number, file_offset = :segment_size - 1
FROM pg_walfile_name_offset('0/0'::pg_lsn + :segment_size - 1), FROM pg_walfile_name_offset('0/0'::pg_lsn + :segment_size - 1),
pg_split_walfile_name(file_name); pg_split_walfile_name(file_name);
-- test stratnum support functions
SELECT gist_stratnum_identity(3::smallint);
SELECT gist_stratnum_identity(18::smallint);
-- pg_current_logfile -- pg_current_logfile
CREATE ROLE regress_current_logfile; CREATE ROLE regress_current_logfile;
-- not available by default -- not available by default

File diff suppressed because it is too large Load Diff