mirror of
https://github.com/postgres/postgres.git
synced 2026-04-09 11:06:21 -04:00
Fix WITHOUT OVERLAPS' interaction with domains.
UNIQUE/PRIMARY KEY ... WITHOUT OVERLAPS requires the no-overlap column to be a range or multirange, but it should allow a domain over such a type too. This requires minor adjustments in both the parser and executor. In passing, fix a nearby break-instead-of-continue thinko in transformIndexConstraint. This had the effect of disabling parse-time validation of the no-overlap column's type in the context of ALTER TABLE ADD CONSTRAINT, if it follows a dropped column. We'd still complain appropriately at runtime though. Author: Jian He <jian.universality@gmail.com> Reviewed-by: Paul A Jungwirth <pj@illuminatedcomputing.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Discussion: https://postgr.es/m/CACJufxGoAmN_0iJ=hjTG0vGpOSOyy-vYyfE+-q0AWxrq2_p5XQ@mail.gmail.com Backpatch-through: 18
This commit is contained in:
parent
294520c444
commit
4edd6036d6
4 changed files with 91 additions and 8 deletions
|
|
@ -115,6 +115,7 @@
|
|||
#include "nodes/nodeFuncs.h"
|
||||
#include "storage/lmgr.h"
|
||||
#include "utils/injection_point.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/multirangetypes.h"
|
||||
#include "utils/rangetypes.h"
|
||||
#include "utils/snapmgr.h"
|
||||
|
|
@ -753,11 +754,18 @@ check_exclusion_or_unique_constraint(Relation heap, Relation index,
|
|||
{
|
||||
TupleDesc tupdesc = RelationGetDescr(heap);
|
||||
Form_pg_attribute att = TupleDescAttr(tupdesc, attno - 1);
|
||||
TypeCacheEntry *typcache = lookup_type_cache(att->atttypid, 0);
|
||||
TypeCacheEntry *typcache = lookup_type_cache(att->atttypid,
|
||||
TYPECACHE_DOMAIN_BASE_INFO);
|
||||
char typtype;
|
||||
|
||||
if (OidIsValid(typcache->domainBaseType))
|
||||
typtype = get_typtype(typcache->domainBaseType);
|
||||
else
|
||||
typtype = typcache->typtype;
|
||||
|
||||
ExecWithoutOverlapsNotEmpty(heap, att->attname,
|
||||
values[indnkeyatts - 1],
|
||||
typcache->typtype, att->atttypid);
|
||||
typtype, att->atttypid);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2760,7 +2760,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
|
|||
|
||||
/*
|
||||
* The WITHOUT OVERLAPS part (if any) must be a range or
|
||||
* multirange type.
|
||||
* multirange type, or a domain over such a type.
|
||||
*/
|
||||
if (constraint->without_overlaps && lc == list_last_cell(constraint->keys))
|
||||
{
|
||||
|
|
@ -2778,8 +2778,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
|
|||
const char *attname;
|
||||
|
||||
if (attr->attisdropped)
|
||||
break;
|
||||
|
||||
continue;
|
||||
attname = NameStr(attr->attname);
|
||||
if (strcmp(attname, key) == 0)
|
||||
{
|
||||
|
|
@ -2791,10 +2790,16 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
|
|||
}
|
||||
if (found)
|
||||
{
|
||||
/* Look up column type if we didn't already */
|
||||
if (!OidIsValid(typid) && column)
|
||||
typid = typenameTypeId(NULL, column->typeName);
|
||||
|
||||
if (!OidIsValid(typid) || !(type_is_range(typid) || type_is_multirange(typid)))
|
||||
typid = typenameTypeId(cxt->pstate,
|
||||
column->typeName);
|
||||
/* Look through any domain */
|
||||
if (OidIsValid(typid))
|
||||
typid = getBaseType(typid);
|
||||
/* Complain if not range/multirange */
|
||||
if (!OidIsValid(typid) ||
|
||||
!(type_is_range(typid) || type_is_multirange(typid)))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("column \"%s\" in WITHOUT OVERLAPS is not a range or multirange type", key),
|
||||
|
|
|
|||
|
|
@ -314,6 +314,45 @@ ALTER TABLE temporal_rng3 DROP CONSTRAINT temporal_rng3_uq;
|
|||
DROP TABLE temporal_rng3;
|
||||
DROP TYPE textrange2;
|
||||
--
|
||||
-- test PRIMARY KEY and UNIQUE constraints' interaction with domains
|
||||
--
|
||||
-- range over domain:
|
||||
CREATE DOMAIN int4_d as integer check (value <> 10);
|
||||
CREATE TYPE int4_d_range as range (subtype = int4_d);
|
||||
CREATE TABLE temporal_rng4 (
|
||||
id int4range,
|
||||
valid_at int4_d_range,
|
||||
CONSTRAINT temporal_rng4_pk PRIMARY KEY(id, valid_at WITHOUT OVERLAPS)
|
||||
);
|
||||
INSERT INTO temporal_rng4 VALUES ('[1,11)', '[9,10)'); -- start bound violates domain
|
||||
ERROR: value for domain int4_d violates check constraint "int4_d_check"
|
||||
LINE 1: INSERT INTO temporal_rng4 VALUES ('[1,11)', '[9,10)');
|
||||
^
|
||||
INSERT INTO temporal_rng4 VALUES ('[1,2)', '[10,11)'); -- end bound violates domain
|
||||
ERROR: value for domain int4_d violates check constraint "int4_d_check"
|
||||
LINE 1: INSERT INTO temporal_rng4 VALUES ('[1,2)', '[10,11)');
|
||||
^
|
||||
INSERT INTO temporal_rng4 VALUES ('[1,2)', '[1,13)'), ('[1,2)', '[2,5)'); -- overlaps
|
||||
ERROR: conflicting key value violates exclusion constraint "temporal_rng4_pk"
|
||||
DETAIL: Key (id, valid_at)=([1,2), [2,5)) conflicts with existing key (id, valid_at)=([1,2), [1,13)).
|
||||
INSERT INTO temporal_rng4 VALUES ('[1,2)', '[1,13)'), ('[1,2)', '[20,23)'); -- okay
|
||||
INSERT INTO temporal_rng4 VALUES ('[1,2)', '[30,)'); -- null bound is okay
|
||||
DROP TABLE temporal_rng4;
|
||||
-- domain over range:
|
||||
CREATE DOMAIN int4range_d AS int4range CHECK (VALUE <> '[10,11)');
|
||||
CREATE TABLE temporal_rng4 (
|
||||
id int4range,
|
||||
valid_at int4range_d,
|
||||
CONSTRAINT temporal_rng4_pk UNIQUE (id, valid_at WITHOUT OVERLAPS)
|
||||
);
|
||||
INSERT INTO temporal_rng4 VALUES ('[1,2)', '[10,11)'); -- violates domain
|
||||
ERROR: value for domain int4range_d violates check constraint "int4range_d_check"
|
||||
INSERT INTO temporal_rng4 VALUES ('[1,2)', '[1,13)'), ('[1,2)', '[2,13)'); -- overlaps
|
||||
ERROR: conflicting key value violates exclusion constraint "temporal_rng4_pk"
|
||||
DETAIL: Key (id, valid_at)=([1,2), [2,13)) conflicts with existing key (id, valid_at)=([1,2), [1,13)).
|
||||
INSERT INTO temporal_rng4 VALUES ('[1,2)', '[1,13)'), ('[1,2)', '[20,23)'); -- okay
|
||||
DROP TABLE temporal_rng4;
|
||||
--
|
||||
-- test ALTER TABLE ADD CONSTRAINT
|
||||
--
|
||||
CREATE TABLE temporal_rng (
|
||||
|
|
|
|||
|
|
@ -180,6 +180,37 @@ ALTER TABLE temporal_rng3 DROP CONSTRAINT temporal_rng3_uq;
|
|||
DROP TABLE temporal_rng3;
|
||||
DROP TYPE textrange2;
|
||||
|
||||
--
|
||||
-- test PRIMARY KEY and UNIQUE constraints' interaction with domains
|
||||
--
|
||||
|
||||
-- range over domain:
|
||||
CREATE DOMAIN int4_d as integer check (value <> 10);
|
||||
CREATE TYPE int4_d_range as range (subtype = int4_d);
|
||||
CREATE TABLE temporal_rng4 (
|
||||
id int4range,
|
||||
valid_at int4_d_range,
|
||||
CONSTRAINT temporal_rng4_pk PRIMARY KEY(id, valid_at WITHOUT OVERLAPS)
|
||||
);
|
||||
INSERT INTO temporal_rng4 VALUES ('[1,11)', '[9,10)'); -- start bound violates domain
|
||||
INSERT INTO temporal_rng4 VALUES ('[1,2)', '[10,11)'); -- end bound violates domain
|
||||
INSERT INTO temporal_rng4 VALUES ('[1,2)', '[1,13)'), ('[1,2)', '[2,5)'); -- overlaps
|
||||
INSERT INTO temporal_rng4 VALUES ('[1,2)', '[1,13)'), ('[1,2)', '[20,23)'); -- okay
|
||||
INSERT INTO temporal_rng4 VALUES ('[1,2)', '[30,)'); -- null bound is okay
|
||||
DROP TABLE temporal_rng4;
|
||||
|
||||
-- domain over range:
|
||||
CREATE DOMAIN int4range_d AS int4range CHECK (VALUE <> '[10,11)');
|
||||
CREATE TABLE temporal_rng4 (
|
||||
id int4range,
|
||||
valid_at int4range_d,
|
||||
CONSTRAINT temporal_rng4_pk UNIQUE (id, valid_at WITHOUT OVERLAPS)
|
||||
);
|
||||
INSERT INTO temporal_rng4 VALUES ('[1,2)', '[10,11)'); -- violates domain
|
||||
INSERT INTO temporal_rng4 VALUES ('[1,2)', '[1,13)'), ('[1,2)', '[2,13)'); -- overlaps
|
||||
INSERT INTO temporal_rng4 VALUES ('[1,2)', '[1,13)'), ('[1,2)', '[20,23)'); -- okay
|
||||
DROP TABLE temporal_rng4;
|
||||
|
||||
--
|
||||
-- test ALTER TABLE ADD CONSTRAINT
|
||||
--
|
||||
|
|
|
|||
Loading…
Reference in a new issue