index-constraints-20090705.patch
text/x-patch
Filename: index-constraints-20090705.patch
Type: text/x-patch
Part: 0
Message:
WIP: generalized index constraints
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/access/index/indexam.c | 293 | 0 |
| src/backend/catalog/index.c | 1 | 0 |
| src/backend/executor/execUtils.c | 9 | 0 |
| src/backend/storage/ipc/ipci.c | 3 | 0 |
| src/backend/utils/cache/relcache.c | 16 | 0 |
| src/include/access/genam.h | 9 | 0 |
| src/include/catalog/pg_attribute.h | 2 | 1 |
| src/include/catalog/pg_index.h | 3 | 1 |
| src/include/storage/lwlock.h | 1 | 0 |
| src/include/utils/relcache.h | 1 | 0 |
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
index 1515d9f..eedb456 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,278 @@ 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 *MyIndexInsertEntry;
+ 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);
+ }
+
+
+ if (CurrentIndexInsertsTable == NULL)
+ {
+ bool found;
+
+ CurrentIndexInsertsTable = (CurrentIndexInsertEntry *)
+ ShmemInitStruct("Current Index Inserts Table",
+ CurrentIndexInsertsShmemSize(), &found);
+ Assert(found);
+ }
+
+ MyIndexInsertEntry = &CurrentIndexInsertsTable[MyBackendId - 1];
+
+ /*
+ * 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.
+ */
+
+ /* TODO: this lock should be partitioned by heap relid hash. */
+ LWLockAcquire(IndexConstraintLock, LW_EXCLUSIVE);
+
+ for (i = 0; i < MaxBackends; i++)
+ {
+ CurrentIndexInsertEntry entry = CurrentIndexInsertsTable[i];
+
+ if (RelationGetRelid(heap) == entry.relid &&
+ !TransactionIdIsCurrentTransactionId(entry.xid))
+ {
+ if (TransactionIdIsInProgress(entry.xid))
+ {
+ /* build a list of potential conflicts */
+ potential_conflicts[n_potential_conflicts++] = entry;
+ }
+ else
+ entry.relid = InvalidOid;
+ }
+ }
+
+ MyIndexInsertEntry->relid = heap->rd_id;
+ MyIndexInsertEntry->xid = GetCurrentTransactionId();
+ MyIndexInsertEntry->tid = *tid;
+
+ 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;
+}
+
+/*
+ * 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);
+}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c4e4cab..8a136bb 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -444,6 +444,7 @@ UpdateIndexRelation(Oid indexoid,
values[Anum_pg_index_indcheckxmin - 1] = BoolGetDatum(false);
/* we set isvalid and isready the same way */
values[Anum_pg_index_indisready - 1] = BoolGetDatum(isvalid);
+ nulls[Anum_pg_index_indconstrats - 1] = true;
values[Anum_pg_index_indkey - 1] = PointerGetDatum(indkey);
values[Anum_pg_index_indclass - 1] = PointerGetDatum(indclass);
values[Anum_pg_index_indoption - 1] = PointerGetDatum(indoption);
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 0ccd862..e8d704c 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -1067,6 +1067,15 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
econtext->ecxt_scantuple = slot;
/*
+ * before actually inserting, check index constraints for each index
+ */
+ 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++)
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..2aad8b4 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,13 @@ extern RegProcedure index_getprocid(Relation irel, AttrNumber attnum,
uint16 procnum);
extern FmgrInfo *index_getprocinfo(Relation irel, AttrNumber attnum,
uint16 procnum);
+extern void index_check_constraint(Relation heapRelation,
+ Relation indexRelation,
+ ItemPointer tid,
+ TupleTableSlot *slot);
+
+extern Size CurrentIndexInsertsShmemSize(void);
+extern void CurrentIndexInsertsShmemInit(void);
/*
* index access method support routines (in genam.c)
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/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);