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 (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers