Updated patch attached.
Changes:
* Added syntax support:
CREATE INDEX foo_idx ON foo ... (a CONSTRAINT =, b CONSTRAINT &&);
* More aggressively clear the shared memory entries to avoid
unnecessary checks
* Code cleanup
TODO:
* When adding constraint to table with data already in it, verify that
existing data satisfies constraint.
* Clean up error messages a little
* Docs
The following are possible TODO items, but I'd like to get some feedback
first:
* It seems like an alternative language would be better:
ALTER TABLE foo ADD INDEX CONSTRAINT optional_name (a =, b &&)
USING foo_idx;
This language would be more like a table constraint that happens to
use an index. I think it's better because it allows multiple
constraints to be enforced by the same index.
* Right now it only supports index AMs that offer amgettuple, which
excludes GIN. Consider adding a crude implementation of gingettuple
that just calls gingetbitmap internally (obviously providing no
performance advantage over gingetbitmap).
Regards,
Jeff Davis
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 1515d9f..d88387b 100644
--- a/src/backend/access/index/indexam.c
+++ b/src/backend/access/index/indexam.c
@@ -26,6 +26,7 @@
* index_vacuum_cleanup - post-deletion cleanup of an index
* index_getprocid - get a support procedure OID
* index_getprocinfo - get a support procedure's lookup info
+ * index_check_constraint - check index constraints
*
* NOTES
* This file contains the index_ routines which used
@@ -64,9 +65,13 @@
#include "access/relscan.h"
#include "access/transam.h"
+#include "miscadmin.h"
#include "pgstat.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
+#include "storage/lwlock.h"
+#include "storage/procarray.h"
+#include "utils/lsyscache.h"
#include "utils/relcache.h"
#include "utils/snapmgr.h"
#include "utils/tqual.h"
@@ -116,6 +121,19 @@ do { \
static IndexScanDesc index_beginscan_internal(Relation indexRelation,
int nkeys, ScanKey key);
+typedef struct
+{
+ Oid relid;
+ TransactionId xid;
+ ItemPointerData tid;
+} CurrentIndexInsertEntry;
+
+static CurrentIndexInsertEntry *CurrentIndexInsertsTable = NULL;
+
+static bool index_check_constraint_conflict(TupleTableSlot *slot,
+ HeapTuple tup, int2 *heap_attnums,
+ int2 index_natts,
+ Oid *constraint_procs);
/* ----------------------------------------------------------------
* index_ interface functions
@@ -846,3 +864,303 @@ index_getprocinfo(Relation irel,
return locinfo;
}
+
+void
+index_check_constraint(Relation heap, Relation index,
+ ItemPointer tid, TupleTableSlot *slot)
+{
+ IndexScanDesc index_scan;
+ HeapTuple tup;
+ ScanKeyData *scankeys;
+ int2vector *constr_strats;
+ Oid *constr_procs;
+ int i;
+ int2 *heap_attnums = index->rd_index->indkey.values;
+ int2 index_natts = index->rd_index->indnatts;
+ SnapshotData DirtySnapshot;
+ int nkeys = 0;
+
+ CurrentIndexInsertEntry potential_conflicts[MaxBackends];
+ int n_potential_conflicts = 0;
+
+ /* Find constraint strategy numbers */
+ constr_strats = RelationGetIndexConstraintStrategies(index);
+
+ /* return if no constraint */
+ if (constr_strats == NULL)
+ return;
+
+ /*
+ * if any of the indexed columns are NULL, the constraint
+ * is satisfied
+ */
+ for (i = 0; i < index_natts; i++)
+ if (slot_attisnull(slot, heap_attnums[i]))
+ return;
+
+ /*
+ * Find the function that tests for a conflict based on the
+ * strategy number, operator family, and types.
+ */
+ constr_procs = palloc(sizeof(Oid) * index_natts);
+ for (i = 0; i < index_natts; i++)
+ {
+ /*
+ * Find the procedure implementing the strategy for the
+ * index for two arguments both with the type of the
+ * indexed attribute.
+ */
+ Oid oper;
+ Oid opfamily = index->rd_opfamily[i];
+ StrategyNumber strategy = constr_strats->values[i];
+ Oid typeOid = heap->rd_att->attrs[heap_attnums[i] - 1]->atttypid;
+
+ if (strategy == InvalidStrategy)
+ continue;
+
+ oper = get_opfamily_member(opfamily, typeOid, typeOid, strategy);
+
+ if(OidIsValid(oper))
+ constr_procs[i] = get_opcode(oper);
+ else
+ elog(ERROR, "cannot determine operator for type %d and "
+ "strategy %d", typeOid, strategy);
+ }
+
+ /*
+ * Check for conflicts with concurrent inserts. These are
+ * inserts that are actually in-progress now, and have not
+ * actually been put in the index yet.
+ */
+
+ Assert (CurrentIndexInsertsTable != NULL);
+
+ LWLockAcquire(IndexConstraintLock, LW_SHARED);
+
+ for (i = 0; i < MaxBackends; i++)
+ {
+ CurrentIndexInsertEntry entry = CurrentIndexInsertsTable[i];
+
+ if (RelationGetRelid(heap) == entry.relid &&
+ !TransactionIdIsCurrentTransactionId(entry.xid) &&
+ TransactionIdIsInProgress(entry.xid))
+ {
+ potential_conflicts[n_potential_conflicts++] = entry;
+ }
+ }
+
+ LWLockRelease(IndexConstraintLock);
+
+ InitDirtySnapshot(DirtySnapshot);
+
+ for (i = 0; i < n_potential_conflicts; i++)
+ {
+ bool does_conflict = true;
+ HeapTupleData tup;
+ Buffer buffer;
+
+ tup.t_self = potential_conflicts[i].tid;
+ if (!heap_fetch(heap, &DirtySnapshot, &tup, &buffer, false, NULL))
+ continue;
+
+ does_conflict = index_check_constraint_conflict(
+ slot, &tup, heap_attnums, index_natts, constr_procs);
+
+ ReleaseBuffer(buffer);
+
+ if (does_conflict)
+ {
+ CurrentIndexInsertEntry conflict = potential_conflicts[i];
+ if (TransactionIdIsCurrentTransactionId(conflict.xid))
+ elog(ERROR, "conflict detected 1");
+
+ XactLockTableWait(conflict.xid);
+ if (TransactionIdDidCommit(conflict.xid))
+ elog(ERROR, "conflict detected 2");
+ }
+ }
+
+ /*
+ * Now search the tuples that are actually in the index for
+ * any violations.
+ */
+
+ scankeys = palloc(index_natts * sizeof(ScanKeyData));
+ for (i = 0; i < index_natts; i++)
+ {
+ Datum key_datum;
+ bool isnull;
+
+ key_datum = slot_getattr(slot, heap_attnums[i], &isnull);
+ Assert(!isnull); /* already checked above */
+
+ if (constr_strats->values[i] == InvalidStrategy)
+ continue;
+
+ ScanKeyInit(&scankeys[nkeys], i + 1, constr_strats->values[i],
+ constr_procs[i], key_datum);
+ nkeys++;
+ }
+
+ /*
+ * We have to find all tuples, even those not visible
+ * yet. Other transactions may have inserted many tuples (or
+ * the transaction might be a prepared transaction), so there
+ * may be some tuples that are not in the shared memory
+ * structure and not visible.
+ */
+ index_scan = index_beginscan(heap, index, &DirtySnapshot, nkeys,
+ scankeys);
+ while((tup = index_getnext(index_scan, ForwardScanDirection)) != NULL)
+ {
+ if (index_scan->xs_recheck)
+ {
+ if (!index_check_constraint_conflict(
+ slot, tup, heap_attnums, index_natts, constr_procs))
+ continue;
+ }
+
+ /* If the in-progress inserting transaction aborts, proceed. */
+ if (TransactionIdIsValid(DirtySnapshot.xmin))
+ {
+ XactLockTableWait(DirtySnapshot.xmin);
+ if (TransactionIdDidAbort(DirtySnapshot.xmin))
+ continue;
+ }
+
+ /* If the in-progress deleting transaction commits, proceed. */
+ if (TransactionIdIsValid(DirtySnapshot.xmax))
+ {
+ XactLockTableWait(DirtySnapshot.xmax);
+ if (TransactionIdDidCommit(DirtySnapshot.xmax))
+ continue;
+ }
+
+ elog(ERROR, "conflict detected 3");
+ }
+
+ index_endscan(index_scan);
+ pfree(scankeys);
+
+ return;
+}
+
+/*
+ * For each attribute of the index, check for a conflict between the
+ * slot's tuple's value and the tuple's value. Only return true if all
+ * values conflict.
+ */
+static bool
+index_check_constraint_conflict(TupleTableSlot *slot, HeapTuple tup,
+ int2 *heap_attnums, int2 index_natts,
+ Oid* constraint_procs)
+{
+ int i;
+
+ for (i = 0; i < index_natts; i++)
+ {
+ Datum input_datum;
+ Datum existing_datum;
+ bool isnull;
+
+ if (!OidIsValid(constraint_procs[i]))
+ continue;
+
+ input_datum = slot_getattr(slot, heap_attnums[i], &isnull);
+ if (isnull)
+ return false;
+
+ existing_datum = heap_getattr(
+ tup, heap_attnums[i], slot->tts_tupleDescriptor, &isnull);
+ if (isnull)
+ return false;
+
+ if (!DatumGetBool(OidFunctionCall2(constraint_procs[i],
+ input_datum, existing_datum)))
+ return false;
+ }
+ return true;
+}
+
+void
+index_set_current_insert(Oid relid, ItemPointer tupleid)
+{
+ if (CurrentIndexInsertsTable == NULL)
+ {
+ bool found;
+
+ CurrentIndexInsertsTable = (CurrentIndexInsertEntry *)
+ ShmemInitStruct("Current Index Inserts Table",
+ CurrentIndexInsertsShmemSize(), &found);
+ Assert(found);
+ }
+
+ LWLockAcquire(IndexConstraintLock, LW_EXCLUSIVE);
+
+ CurrentIndexInsertsTable[MyBackendId - 1].relid = relid;
+ CurrentIndexInsertsTable[MyBackendId - 1].xid = GetCurrentTransactionId();
+ CurrentIndexInsertsTable[MyBackendId - 1].tid = *tupleid;
+
+ LWLockRelease(IndexConstraintLock);
+}
+
+void
+index_unset_current_insert(void)
+{
+ Assert(CurrentIndexInsertsTable != NULL);
+
+ LWLockAcquire(IndexConstraintLock, LW_EXCLUSIVE);
+
+ CurrentIndexInsertsTable[MyBackendId - 1].relid = InvalidOid;
+
+ LWLockRelease(IndexConstraintLock);
+}
+
+/*
+ * GistShmemSize --- report amount of shared memory space needed
+ */
+Size
+CurrentIndexInsertsShmemSize(void)
+{
+ return (sizeof(CurrentIndexInsertEntry) * MaxBackends);
+}
+
+/*
+ * GistShmemInit --- initialize this module's shared memory
+ */
+void
+CurrentIndexInsertsShmemInit(void)
+{
+ int i;
+ bool found;
+ CurrentIndexInsertEntry *current_inserts;
+
+ current_inserts = (CurrentIndexInsertEntry *)
+ ShmemInitStruct("Current Index Inserts Table",
+ CurrentIndexInsertsShmemSize(),
+ &found);
+
+ if (!IsUnderPostmaster)
+ {
+ /* Initialize shared memory area */
+ Assert(!found);
+
+ for (i = 0; i < MaxBackends; i++)
+ current_inserts[i].relid = InvalidOid;
+ }
+ else
+ Assert(found);
+}
+
+void
+AtEOXact_IndexConstraints(void)
+{
+ if (CurrentIndexInsertsTable != NULL)
+ {
+ LWLockAcquire(IndexConstraintLock, LW_EXCLUSIVE);
+
+ CurrentIndexInsertsTable[MyBackendId - 1].relid = InvalidOid;
+
+ LWLockRelease(IndexConstraintLock);
+ }
+}
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 117fdab..2716141 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1707,6 +1707,7 @@ CommitTransaction(void)
AtEOXact_HashTables(true);
AtEOXact_PgStat(true);
AtEOXact_Snapshot(true);
+ AtEOXact_IndexConstraints();
pgstat_report_xact_timestamp(0);
CurrentResourceOwner = NULL;
@@ -1942,6 +1943,7 @@ PrepareTransaction(void)
AtEOXact_HashTables(true);
/* don't call AtEOXact_PgStat here */
AtEOXact_Snapshot(true);
+ AtEOXact_IndexConstraints();
CurrentResourceOwner = NULL;
ResourceOwnerDelete(TopTransactionResourceOwner);
@@ -2085,6 +2087,7 @@ AbortTransaction(void)
AtEOXact_HashTables(false);
AtEOXact_PgStat(false);
AtEOXact_Snapshot(false);
+ AtEOXact_IndexConstraints();
pgstat_report_xact_timestamp(0);
/*
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c4e4cab..d285a78 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -86,6 +86,7 @@ static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
IndexInfo *indexInfo,
Oid *classOids,
int16 *coloptions,
+ StrategyNumber *constrats,
bool primary,
bool isvalid);
static void index_update_stats(Relation rel, bool hasindex, bool isprimary,
@@ -371,12 +372,14 @@ UpdateIndexRelation(Oid indexoid,
IndexInfo *indexInfo,
Oid *classOids,
int16 *coloptions,
+ StrategyNumber *constrats,
bool primary,
bool isvalid)
{
int2vector *indkey;
oidvector *indclass;
int2vector *indoption;
+ int2vector *indconstrats;
Datum exprsDatum;
Datum predDatum;
Datum values[Natts_pg_index];
@@ -394,6 +397,8 @@ UpdateIndexRelation(Oid indexoid,
indkey->values[i] = indexInfo->ii_KeyAttrNumbers[i];
indclass = buildoidvector(classOids, indexInfo->ii_NumIndexAttrs);
indoption = buildint2vector(coloptions, indexInfo->ii_NumIndexAttrs);
+ indconstrats = buildint2vector((int2 *) constrats,
+ indexInfo->ii_NumIndexAttrs);
/*
* Convert the index expressions (if any) to a text datum
@@ -447,6 +452,12 @@ UpdateIndexRelation(Oid indexoid,
values[Anum_pg_index_indkey - 1] = PointerGetDatum(indkey);
values[Anum_pg_index_indclass - 1] = PointerGetDatum(indclass);
values[Anum_pg_index_indoption - 1] = PointerGetDatum(indoption);
+
+ if (constrats != NULL)
+ values[Anum_pg_index_indconstrats - 1] = PointerGetDatum(indconstrats);
+ else
+ nulls[Anum_pg_index_indconstrats - 1] = true;
+
values[Anum_pg_index_indexprs - 1] = exprsDatum;
if (exprsDatum == (Datum) 0)
nulls[Anum_pg_index_indexprs - 1] = true;
@@ -506,6 +517,7 @@ index_create(Oid heapRelationId,
Oid tableSpaceId,
Oid *classObjectId,
int16 *coloptions,
+ StrategyNumber *constrats,
Datum reloptions,
bool isprimary,
bool isconstraint,
@@ -679,7 +691,8 @@ index_create(Oid heapRelationId,
* ----------------
*/
UpdateIndexRelation(indexRelationId, heapRelationId, indexInfo,
- classObjectId, coloptions, isprimary, !concurrent);
+ classObjectId, coloptions, constrats, isprimary,
+ !concurrent);
/*
* Register constraint and dependencies for the index.
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 6e7b5cf..1ab1a2b 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -252,7 +252,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
indexInfo,
BTREE_AM_OID,
rel->rd_rel->reltablespace,
- classObjectId, coloptions, (Datum) 0,
+ classObjectId, coloptions, NULL, (Datum) 0,
true, false, true, false, false);
/*
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 99ab0e5..7a9300f 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -36,6 +36,7 @@
#include "optimizer/clauses.h"
#include "parser/parse_coerce.h"
#include "parser/parse_func.h"
+#include "parser/parse_oper.h"
#include "parser/parsetree.h"
#include "storage/lmgr.h"
#include "storage/proc.h"
@@ -57,6 +58,8 @@ static void CheckPredicate(Expr *predicate);
static void ComputeIndexAttrs(IndexInfo *indexInfo,
Oid *classOidP,
int16 *colOptionP,
+ StrategyNumber *constrats,
+ bool *has_constraints,
List *attList,
Oid relId,
char *accessMethodName, Oid accessMethodId,
@@ -126,6 +129,8 @@ DefineIndex(RangeVar *heapRelation,
RegProcedure amoptions;
Datum reloptions;
int16 *coloptions;
+ StrategyNumber *constrats;
+ bool has_constraints;
IndexInfo *indexInfo;
int numberOfAttributes;
VirtualTransactionId *old_lockholders;
@@ -422,9 +427,15 @@ DefineIndex(RangeVar *heapRelation,
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
- ComputeIndexAttrs(indexInfo, classObjectId, coloptions, attributeList,
- relationId, accessMethodName, accessMethodId,
- amcanorder, isconstraint);
+ constrats = (StrategyNumber *) palloc(
+ numberOfAttributes * sizeof(StrategyNumber));
+ ComputeIndexAttrs(indexInfo, classObjectId, coloptions, constrats,
+ &has_constraints, attributeList, relationId,
+ accessMethodName, accessMethodId, amcanorder,
+ isconstraint);
+
+ if (!has_constraints)
+ constrats = NULL;
/*
* Report index creation if appropriate (delay this till after most of the
@@ -447,8 +458,9 @@ DefineIndex(RangeVar *heapRelation,
indexRelationId =
index_create(relationId, indexRelationName, indexRelationId,
indexInfo, accessMethodId, tablespaceId, classObjectId,
- coloptions, reloptions, primary, isconstraint,
- allowSystemTableMods, skip_build, concurrent);
+ coloptions, constrats, reloptions, primary,
+ isconstraint, allowSystemTableMods, skip_build,
+ concurrent);
return; /* We're done, in the standard case */
}
@@ -465,7 +477,7 @@ DefineIndex(RangeVar *heapRelation,
indexRelationId =
index_create(relationId, indexRelationName, indexRelationId,
indexInfo, accessMethodId, tablespaceId, classObjectId,
- coloptions, reloptions, primary, isconstraint,
+ coloptions, constrats, reloptions, primary, isconstraint,
allowSystemTableMods, true, concurrent);
/*
@@ -792,6 +804,8 @@ static void
ComputeIndexAttrs(IndexInfo *indexInfo,
Oid *classOidP,
int16 *colOptionP,
+ StrategyNumber *constrats, /* constraint strategies */
+ bool *has_constraints,
List *attList, /* list of IndexElem's */
Oid relId,
char *accessMethodName,
@@ -802,6 +816,8 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
ListCell *rest;
int attn = 0;
+ *has_constraints = false;
+
/*
* process attributeList
*/
@@ -891,6 +907,21 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
accessMethodName,
accessMethodId);
+ if (attribute->constraint_oper != NULL)
+ {
+ Oid opfamily = get_opclass_family(classOidP[attn]);
+ Oid opid = LookupOperName(NULL, attribute->constraint_oper,
+ atttype, atttype, false, -1);
+ constrats[attn] = get_op_opfamily_strategy(opid, opfamily);
+ if (constrats[attn] == InvalidStrategy)
+ elog(ERROR, "no strategy found for operator %d "
+ "in operator class %d", opid, classOidP[attn]);
+ *has_constraints = true;
+ }
+ else
+ constrats[attn] = InvalidStrategy;
+
+
/*
* Set up the per-column options (indoption field). For now, this is
* zero for any un-ordered index, while ordered indexes have DESC and
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 0ccd862..7e0cb18 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -1067,6 +1067,14 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
econtext->ecxt_scantuple = slot;
/*
+ * before inserting, check index constraints for each index
+ */
+ index_set_current_insert(RelationGetRelid(heapRelation), tupleid);
+ for (i = 0; i < numIndices; i++)
+ index_check_constraint(heapRelation, relationDescs[i],
+ tupleid, slot);
+
+ /*
* for each index, form and insert the index tuple
*/
for (i = 0; i < numIndices; i++)
@@ -1132,6 +1140,8 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
*/
IncrIndexInserted();
}
+
+ index_unset_current_insert();
}
/*
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 2c7d481..c575d18 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2058,6 +2058,7 @@ _copyIndexElem(IndexElem *from)
COPY_NODE_FIELD(opclass);
COPY_SCALAR_FIELD(ordering);
COPY_SCALAR_FIELD(nulls_ordering);
+ COPY_NODE_FIELD(constraint_oper);
return newnode;
}
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index c61de97..be8463f 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2039,6 +2039,7 @@ _equalIndexElem(IndexElem *a, IndexElem *b)
COMPARE_NODE_FIELD(opclass);
COMPARE_SCALAR_FIELD(ordering);
COMPARE_SCALAR_FIELD(nulls_ordering);
+ COMPARE_NODE_FIELD(constraint_oper);
return true;
}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 49f74d7..a5f7e38 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1876,6 +1876,7 @@ _outIndexElem(StringInfo str, IndexElem *node)
WRITE_NODE_FIELD(opclass);
WRITE_ENUM_FIELD(ordering, SortByDir);
WRITE_ENUM_FIELD(nulls_ordering, SortByNulls);
+ WRITE_NODE_FIELD(constraint_oper);
}
static void
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 20ab0ba..eb7d950 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -280,7 +280,7 @@ static TypeName *TableFuncTypeName(List *columns);
sort_clause opt_sort_clause sortby_list index_params
name_list from_clause from_list opt_array_bounds
qualified_name_list any_name any_name_list
- any_operator expr_list attrs
+ any_operator expr_list attrs opt_index_constraint
target_list insert_column_list set_target_list
set_clause_list set_clause multiple_set_clause
ctext_expr_list ctext_row def_list indirection opt_indirection
@@ -4560,7 +4560,7 @@ index_params: index_elem { $$ = list_make1($1); }
* expressions in parens. For backwards-compatibility reasons, we allow
* an expression that's just a function call to be written without parens.
*/
-index_elem: ColId opt_class opt_asc_desc opt_nulls_order
+index_elem: ColId opt_class opt_asc_desc opt_nulls_order opt_index_constraint
{
$$ = makeNode(IndexElem);
$$->name = $1;
@@ -4568,8 +4568,9 @@ index_elem: ColId opt_class opt_asc_desc opt_nulls_order
$$->opclass = $2;
$$->ordering = $3;
$$->nulls_ordering = $4;
+ $$->constraint_oper = $5;
}
- | func_expr opt_class opt_asc_desc opt_nulls_order
+ | func_expr opt_class opt_asc_desc opt_nulls_order opt_index_constraint
{
$$ = makeNode(IndexElem);
$$->name = NULL;
@@ -4577,8 +4578,9 @@ index_elem: ColId opt_class opt_asc_desc opt_nulls_order
$$->opclass = $2;
$$->ordering = $3;
$$->nulls_ordering = $4;
+ $$->constraint_oper = $5;
}
- | '(' a_expr ')' opt_class opt_asc_desc opt_nulls_order
+ | '(' a_expr ')' opt_class opt_asc_desc opt_nulls_order opt_index_constraint
{
$$ = makeNode(IndexElem);
$$->name = NULL;
@@ -4586,6 +4588,7 @@ index_elem: ColId opt_class opt_asc_desc opt_nulls_order
$$->opclass = $4;
$$->ordering = $5;
$$->nulls_ordering = $6;
+ $$->constraint_oper = $7;
}
;
@@ -4604,6 +4607,10 @@ opt_nulls_order: NULLS_FIRST { $$ = SORTBY_NULLS_FIRST; }
| /*EMPTY*/ { $$ = SORTBY_NULLS_DEFAULT; }
;
+opt_index_constraint: CONSTRAINT any_operator { $$ = $2; }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
/*****************************************************************************
*
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 3022867..8c04940 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -15,6 +15,7 @@
#include "postgres.h"
#include "access/clog.h"
+#include "access/genam.h"
#include "access/heapam.h"
#include "access/multixact.h"
#include "access/nbtree.h"
@@ -115,6 +116,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
size = add_size(size, BgWriterShmemSize());
size = add_size(size, AutoVacuumShmemSize());
size = add_size(size, BTreeShmemSize());
+ size = add_size(size, CurrentIndexInsertsShmemSize());
size = add_size(size, SyncScanShmemSize());
#ifdef EXEC_BACKEND
size = add_size(size, ShmemBackendArraySize());
@@ -215,6 +217,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
* Set up other modules that need some shared memory space
*/
BTreeShmemInit();
+ CurrentIndexInsertsShmemInit();
SyncScanShmemInit();
#ifdef EXEC_BACKEND
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 29976e7..24583d3 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -3284,6 +3284,22 @@ RelationGetIndexAttrBitmap(Relation relation)
return indexattrs;
}
+int2vector *
+RelationGetIndexConstraintStrategies(Relation relation)
+{
+ bool isnull;
+ Datum constraint_strategies;
+
+ constraint_strategies = heap_getattr(relation->rd_indextuple,
+ Anum_pg_index_indconstrats,
+ GetPgIndexDescriptor(),
+ &isnull);
+ if (isnull)
+ return NULL;
+
+ return (int2vector *) DatumGetPointer(constraint_strategies);
+
+}
/*
* load_relcache_init_file, write_relcache_init_file
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
index a6ac5db..af4346b 100644
--- a/src/include/access/genam.h
+++ b/src/include/access/genam.h
@@ -16,6 +16,8 @@
#include "access/sdir.h"
#include "access/skey.h"
+#include "access/xact.h"
+#include "executor/tuptable.h"
#include "nodes/tidbitmap.h"
#include "storage/buf.h"
#include "storage/lock.h"
@@ -129,6 +131,16 @@ extern RegProcedure index_getprocid(Relation irel, AttrNumber attnum,
uint16 procnum);
extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
uint16 procnum);
+extern void index_set_current_insert(Oid relid, ItemPointer tid);
+extern void index_unset_current_insert(void);
+extern void index_check_constraint(Relation heapRelation,
+ Relation indexRelation,
+ ItemPointer tid,
+ TupleTableSlot *slot);
+
+extern Size CurrentIndexInsertsShmemSize(void);
+extern void CurrentIndexInsertsShmemInit(void);
+extern void AtEOXact_IndexConstraints(void);
/*
* index access method support routines (in genam.c)
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index 7275641..bf14c3a 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -36,6 +36,7 @@ extern Oid index_create(Oid heapRelationId,
Oid tableSpaceId,
Oid *classObjectId,
int16 *coloptions,
+ StrategyNumber *constrats,
Datum reloptions,
bool isprimary,
bool isconstraint,
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index eaa405f..413cf0a 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -477,6 +477,7 @@ DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_));
{ 0, {"indclass"}, 30, -1, -1, 11, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \
{ 0, {"indoption"}, 22, -1, -1, 12, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \
{ 0, {"indexprs"}, 25, -1, -1, 13, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
-{ 0, {"indpred"}, 25, -1, -1, 14, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }
+{ 0, {"indpred"}, 25, -1, -1, 14, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \
+{ 0, {"indconstrats"}, 22, -1, -1, 15, 1, -1, -1, false, 'p', 'i', false, false, false, true, 0, { 0 } }
#endif /* PG_ATTRIBUTE_H */
diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h
index 19069db..9ab905c 100644
--- a/src/include/catalog/pg_index.h
+++ b/src/include/catalog/pg_index.h
@@ -49,6 +49,7 @@ CATALOG(pg_index,2610) BKI_WITHOUT_OIDS
* each zero entry in indkey[] */
text indpred; /* expression tree for predicate, if a partial
* index; else NULL */
+ int2vector indconstrats; /* index constraint strategies */
} FormData_pg_index;
/* ----------------
@@ -62,7 +63,7 @@ typedef FormData_pg_index *Form_pg_index;
* compiler constants for pg_index
* ----------------
*/
-#define Natts_pg_index 14
+#define Natts_pg_index 15
#define Anum_pg_index_indexrelid 1
#define Anum_pg_index_indrelid 2
#define Anum_pg_index_indnatts 3
@@ -77,6 +78,7 @@ typedef FormData_pg_index *Form_pg_index;
#define Anum_pg_index_indoption 12
#define Anum_pg_index_indexprs 13
#define Anum_pg_index_indpred 14
+#define Anum_pg_index_indconstrats 15
/*
* Index AMs that support ordered scans must support these two indoption
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index a108b80..acfcdc0 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -501,6 +501,7 @@ typedef struct IndexElem
List *opclass; /* name of desired opclass; NIL = default */
SortByDir ordering; /* ASC/DESC/default */
SortByNulls nulls_ordering; /* FIRST/LAST/default */
+ List *constraint_oper; /* name of constraint operator */
} IndexElem;
/*
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index e389c61..9e6f93e 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -63,6 +63,7 @@ typedef enum LWLockId
TwoPhaseStateLock,
TablespaceCreateLock,
BtreeVacuumLock,
+ IndexConstraintLock,
AddinShmemInitLock,
AutovacuumLock,
AutovacuumScheduleLock,
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 7d4d914..305d1d2 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -43,6 +43,7 @@ extern Oid RelationGetOidIndex(Relation relation);
extern List *RelationGetIndexExpressions(Relation relation);
extern List *RelationGetIndexPredicate(Relation relation);
extern Bitmapset *RelationGetIndexAttrBitmap(Relation relation);
+extern int2vector *RelationGetIndexConstraintStrategies(Relation relation);
extern void RelationSetIndexList(Relation relation,
List *indexIds, Oid oidIndex);
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers