1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-05 07:21:24 +03:00

Add exclusion constraints, which generalize the concept of uniqueness to

support any indexable commutative operator, not just equality.  Two rows
violate the exclusion constraint if "row1.col OP row2.col" is TRUE for
each of the columns in the constraint.

Jeff Davis, reviewed by Robert Haas
This commit is contained in:
Tom Lane
2009-12-07 05:22:23 +00:00
parent 8de7472b45
commit 0cb65564e5
43 changed files with 1613 additions and 236 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.292 2009/09/26 23:08:22 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.293 2009/12/07 05:22:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -60,9 +60,11 @@
#include "storage/fd.h"
#include "storage/lmgr.h"
#include "storage/smgr.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/relcache.h"
#include "utils/resowner.h"
@ -1079,10 +1081,13 @@ RelationInitIndexAccessInfo(Relation relation)
memcpy(relation->rd_indoption, indoption->values, natts * sizeof(int16));
/*
* expressions and predicate cache will be filled later
* expressions, predicate, exclusion caches will be filled later
*/
relation->rd_indexprs = NIL;
relation->rd_indpred = NIL;
relation->rd_exclops = NULL;
relation->rd_exclprocs = NULL;
relation->rd_exclstrats = NULL;
relation->rd_amcache = NULL;
}
@ -3453,6 +3458,130 @@ RelationGetIndexAttrBitmap(Relation relation)
return indexattrs;
}
/*
* RelationGetExclusionInfo -- get info about index's exclusion constraint
*
* This should be called only for an index that is known to have an
* associated exclusion constraint. It returns arrays (palloc'd in caller's
* context) of the exclusion operator 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
RelationGetExclusionInfo(Relation indexRelation,
Oid **operators,
Oid **procs,
uint16 **strategies)
{
int ncols = indexRelation->rd_rel->relnatts;
Oid *ops;
Oid *funcs;
uint16 *strats;
Relation conrel;
SysScanDesc conscan;
ScanKeyData skey[1];
HeapTuple htup;
bool found;
MemoryContext oldcxt;
int i;
/* Allocate result space in caller context */
*operators = ops = (Oid *) palloc(sizeof(Oid) * ncols);
*procs = funcs = (Oid *) palloc(sizeof(Oid) * ncols);
*strategies = strats = (uint16 *) palloc(sizeof(uint16) * ncols);
/* Quick exit if we have the data cached already */
if (indexRelation->rd_exclstrats != NULL)
{
memcpy(ops, indexRelation->rd_exclops, sizeof(Oid) * ncols);
memcpy(funcs, indexRelation->rd_exclprocs, sizeof(Oid) * ncols);
memcpy(strats, indexRelation->rd_exclstrats, sizeof(uint16) * ncols);
return;
}
/*
* Search pg_constraint for the constraint associated with the index.
* To make this not too painfully slow, we use the index on conrelid;
* that will hold the parent relation's OID not the index's own OID.
*/
ScanKeyInit(&skey[0],
Anum_pg_constraint_conrelid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(indexRelation->rd_index->indrelid));
conrel = heap_open(ConstraintRelationId, AccessShareLock);
conscan = systable_beginscan(conrel, ConstraintRelidIndexId, true,
SnapshotNow, 1, skey);
found = false;
while (HeapTupleIsValid(htup = systable_getnext(conscan)))
{
Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(htup);
Datum val;
bool isnull;
ArrayType *arr;
int nelem;
/* We want the exclusion constraint owning the index */
if (conform->contype != CONSTRAINT_EXCLUSION ||
conform->conindid != RelationGetRelid(indexRelation))
continue;
/* There should be only one */
if (found)
elog(ERROR, "unexpected exclusion constraint record found for rel %s",
RelationGetRelationName(indexRelation));
found = true;
/* Extract the operator OIDS from conexclop */
val = fastgetattr(htup,
Anum_pg_constraint_conexclop,
conrel->rd_att, &isnull);
if (isnull)
elog(ERROR, "null conexclop for rel %s",
RelationGetRelationName(indexRelation));
arr = DatumGetArrayTypeP(val); /* ensure not toasted */
nelem = ARR_DIMS(arr)[0];
if (ARR_NDIM(arr) != 1 ||
nelem != ncols ||
ARR_HASNULL(arr) ||
ARR_ELEMTYPE(arr) != OIDOID)
elog(ERROR, "conexclop is not a 1-D Oid array");
memcpy(ops, ARR_DATA_PTR(arr), sizeof(Oid) * ncols);
}
systable_endscan(conscan);
heap_close(conrel, AccessShareLock);
if (!found)
elog(ERROR, "exclusion constraint record missing for rel %s",
RelationGetRelationName(indexRelation));
/* We need the func OIDs and strategy numbers too */
for (i = 0; i < ncols; i++)
{
funcs[i] = get_opcode(ops[i]);
strats[i] = get_op_opfamily_strategy(ops[i],
indexRelation->rd_opfamily[i]);
/* shouldn't fail, since it was checked at index creation */
if (strats[i] == InvalidStrategy)
elog(ERROR, "could not find strategy for operator %u in family %u",
ops[i], indexRelation->rd_opfamily[i]);
}
/* Save a copy of the results in the relcache entry. */
oldcxt = MemoryContextSwitchTo(indexRelation->rd_indexcxt);
indexRelation->rd_exclops = (Oid *) palloc(sizeof(Oid) * ncols);
indexRelation->rd_exclprocs = (Oid *) palloc(sizeof(Oid) * ncols);
indexRelation->rd_exclstrats = (uint16 *) palloc(sizeof(uint16) * ncols);
memcpy(indexRelation->rd_exclops, ops, sizeof(Oid) * ncols);
memcpy(indexRelation->rd_exclprocs, funcs, sizeof(Oid) * ncols);
memcpy(indexRelation->rd_exclstrats, strats, sizeof(uint16) * ncols);
MemoryContextSwitchTo(oldcxt);
}
/*
* load_relcache_init_file, write_relcache_init_file
@ -3768,13 +3897,16 @@ load_relcache_init_file(bool shared)
* format is complex and subject to change). They must be rebuilt if
* needed by RelationCacheInitializePhase3. This is not expected to
* be a big performance hit since few system catalogs have such. Ditto
* for index expressions and predicates.
* for index expressions, predicates, and exclusion info.
*/
rel->rd_rules = NULL;
rel->rd_rulescxt = NULL;
rel->trigdesc = NULL;
rel->rd_indexprs = NIL;
rel->rd_indpred = NIL;
rel->rd_exclops = NULL;
rel->rd_exclprocs = NULL;
rel->rd_exclstrats = NULL;
/*
* Reset transient-state fields in the relcache entry