1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-02 04:21:28 +03:00

Add temporal PRIMARY KEY and UNIQUE constraints

Add WITHOUT OVERLAPS clause to PRIMARY KEY and UNIQUE constraints.
These are backed by GiST indexes instead of B-tree indexes, since they
are essentially exclusion constraints with = for the scalar parts of
the key and && for the temporal part.

Author: Paul A. Jungwirth <pj@illuminatedcomputing.com>
Reviewed-by: Peter Eisentraut <peter@eisentraut.org>
Reviewed-by: jian he <jian.universality@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/CA+renyUApHgSZF9-nd-a0+OPGharLQLO=mDHcY4_qQ0+noCUVg@mail.gmail.com
This commit is contained in:
Peter Eisentraut
2024-01-24 15:43:41 +01:00
parent 74a7306310
commit 46a0cd4cef
34 changed files with 1135 additions and 52 deletions

View File

@@ -529,7 +529,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
columnref in_expr having_clause func_table xmltable array_expr
OptWhereClause operator_def_arg
%type <list> rowsfrom_item rowsfrom_list opt_col_def_list
%type <boolean> opt_ordinality
%type <boolean> opt_ordinality opt_without_overlaps
%type <list> ExclusionConstraintList ExclusionConstraintElem
%type <list> func_arg_list func_arg_list_opt
%type <node> func_arg_expr
@@ -4133,7 +4133,7 @@ ConstraintElem:
n->initially_valid = true;
$$ = (Node *) n;
}
| UNIQUE opt_unique_null_treatment '(' columnList ')' opt_c_include opt_definition OptConsTableSpace
| UNIQUE opt_unique_null_treatment '(' columnList opt_without_overlaps ')' opt_c_include opt_definition OptConsTableSpace
ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
@@ -4142,11 +4142,12 @@ ConstraintElem:
n->location = @1;
n->nulls_not_distinct = !$2;
n->keys = $4;
n->including = $6;
n->options = $7;
n->without_overlaps = $5;
n->including = $7;
n->options = $8;
n->indexname = NULL;
n->indexspace = $8;
processCASbits($9, @9, "UNIQUE",
n->indexspace = $9;
processCASbits($10, @10, "UNIQUE",
&n->deferrable, &n->initdeferred, NULL,
NULL, yyscanner);
$$ = (Node *) n;
@@ -4167,7 +4168,7 @@ ConstraintElem:
NULL, yyscanner);
$$ = (Node *) n;
}
| PRIMARY KEY '(' columnList ')' opt_c_include opt_definition OptConsTableSpace
| PRIMARY KEY '(' columnList opt_without_overlaps ')' opt_c_include opt_definition OptConsTableSpace
ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
@@ -4175,11 +4176,12 @@ ConstraintElem:
n->contype = CONSTR_PRIMARY;
n->location = @1;
n->keys = $4;
n->including = $6;
n->options = $7;
n->without_overlaps = $5;
n->including = $7;
n->options = $8;
n->indexname = NULL;
n->indexspace = $8;
processCASbits($9, @9, "PRIMARY KEY",
n->indexspace = $9;
processCASbits($10, @10, "PRIMARY KEY",
&n->deferrable, &n->initdeferred, NULL,
NULL, yyscanner);
$$ = (Node *) n;
@@ -4247,6 +4249,11 @@ opt_no_inherit: NO INHERIT { $$ = true; }
| /* EMPTY */ { $$ = false; }
;
opt_without_overlaps:
WITHOUT OVERLAPS { $$ = true; }
| /*EMPTY*/ { $$ = false; }
;
opt_column_list:
'(' columnList ')' { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; }

View File

@@ -1716,6 +1716,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
index->unique = idxrec->indisunique;
index->nulls_not_distinct = idxrec->indnullsnotdistinct;
index->primary = idxrec->indisprimary;
index->iswithoutoverlaps = (idxrec->indisprimary || idxrec->indisunique) && idxrec->indisexclusion;
index->transformed = true; /* don't need transformIndexStmt */
index->concurrent = false;
index->if_not_exists = false;
@@ -1765,7 +1766,9 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
int nElems;
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 */
datum = SysCacheGetAttrNotNull(CONSTROID, ht_constr,
Anum_pg_constraint_conexclop);
@@ -2305,6 +2308,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
}
index->nulls_not_distinct = constraint->nulls_not_distinct;
index->isconstraint = true;
index->iswithoutoverlaps = constraint->without_overlaps;
index->deferrable = constraint->deferrable;
index->initdeferred = constraint->initdeferred;
@@ -2397,6 +2401,11 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
errmsg("index \"%s\" is not valid", index_name),
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)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -2673,6 +2682,23 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
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";
}
}
/*