Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions contrib/pageinspect/btreefuncs.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ PG_FUNCTION_INFO_V1(bt_page_stats);
#define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID)
#define DatumGetItemPointer(X) ((ItemPointer) DatumGetPointer(X))
#define ItemPointerGetDatum(X) PointerGetDatum(X)
#define IS_GLOBAL_INDEX(r) ((r)->rd_rel->relkind == RELKIND_GLOBAL_INDEX)

/* note: BlockNumber is unsigned, hence can't be negative */
#define CHECK_RELATION_BLOCK_RANGE(rel, blkno) { \
Expand Down Expand Up @@ -205,7 +206,7 @@ bt_page_stats_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
rel = relation_openrv(relrv, AccessShareLock);

if (!IS_INDEX(rel) || !IS_BTREE(rel))
if ((!IS_INDEX(rel) && !IS_GLOBAL_INDEX(rel)) || !IS_BTREE(rel))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a %s index",
Expand Down Expand Up @@ -477,7 +478,7 @@ bt_page_items_internal(PG_FUNCTION_ARGS, enum pageinspect_version ext_version)
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
rel = relation_openrv(relrv, AccessShareLock);

if (!IS_INDEX(rel) || !IS_BTREE(rel))
if ((!IS_INDEX(rel) && !IS_GLOBAL_INDEX(rel)) || !IS_BTREE(rel))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a %s index",
Expand Down Expand Up @@ -713,7 +714,7 @@ bt_metap(PG_FUNCTION_ARGS)
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
rel = relation_openrv(relrv, AccessShareLock);

if (!IS_INDEX(rel) || !IS_BTREE(rel))
if ((!IS_INDEX(rel) && !IS_GLOBAL_INDEX(rel)) || !IS_BTREE(rel))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a %s index",
Expand Down
13 changes: 13 additions & 0 deletions doc/src/sgml/ref/create_index.sgml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
[ WHERE <replaceable class="parameter">predicate</replaceable> ]
[ GLOBAL ]
</synopsis>
</refsynopsisdiv>

Expand Down Expand Up @@ -364,6 +365,18 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] <replaceable class=
</listitem>
</varlistentry>

<varlistentry>
<term><literal>GLOBAL</literal></term>
<listitem>
<para>
Used with <literal>UNIQUE</literal> to enable cross-partition
uniqueness check on a partitioned table. Attempts to insert or
update data which would result in duplicate entries in other
partitions as a whole will generate an error.
</para>
</listitem>
</varlistentry>

</variablelist>

<refsect2 id="sql-createindex-storage-parameters" xreflabel="Index Storage Parameters">
Expand Down
1 change: 1 addition & 0 deletions src/backend/access/common/reloptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -1412,6 +1412,7 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
options = view_reloptions(datum, false);
break;
case RELKIND_INDEX:
case RELKIND_GLOBAL_INDEX:
case RELKIND_PARTITIONED_INDEX:
options = index_reloptions(amoptions, datum, false);
break;
Expand Down
1 change: 1 addition & 0 deletions src/backend/access/index/indexam.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ static inline void
validate_relation_kind(Relation r)
{
if (r->rd_rel->relkind != RELKIND_INDEX &&
r->rd_rel->relkind != RELKIND_GLOBAL_INDEX &&
r->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
Expand Down
30 changes: 25 additions & 5 deletions src/backend/access/nbtree/nbtinsert.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ static BTStack _bt_search_insert(Relation rel, BTInsertState insertstate);
static TransactionId _bt_check_unique(Relation rel, BTInsertState insertstate,
Relation heapRel,
IndexUniqueCheck checkUnique, bool *is_unique,
uint32 *speculativeToken);
uint32 *speculativeToken, Relation origHeapRel);
static OffsetNumber _bt_findinsertloc(Relation rel,
BTInsertState insertstate,
bool checkingunique,
Expand Down Expand Up @@ -72,6 +72,11 @@ static BlockNumber *_bt_deadblocks(Page page, OffsetNumber *deletable,
int *nblocks);
static inline int _bt_blk_cmp(const void *arg1, const void *arg2);

TransactionId _bt_check_unique_gi(Relation rel, BTInsertState insertstate,
Relation heapRel,
IndexUniqueCheck checkUnique, bool *is_unique,
uint32 *speculativeToken, Relation origHeapRel);

/*
* _bt_doinsert() -- Handle insertion of a single index tuple in the tree.
*
Expand Down Expand Up @@ -205,7 +210,7 @@ _bt_doinsert(Relation rel, IndexTuple itup,
uint32 speculativeToken;

xwait = _bt_check_unique(rel, &insertstate, heapRel, checkUnique,
&is_unique, &speculativeToken);
&is_unique, &speculativeToken, NULL);

if (unlikely(TransactionIdIsValid(xwait)))
{
Expand Down Expand Up @@ -378,6 +383,15 @@ _bt_search_insert(Relation rel, BTInsertState insertstate)
NULL);
}

TransactionId
_bt_check_unique_gi(Relation rel, BTInsertState insertstate, Relation heapRel,
IndexUniqueCheck checkUnique, bool *is_unique,
uint32 *speculativeToken, Relation origHeapRel)
{
return _bt_check_unique(rel, insertstate, heapRel, checkUnique,
is_unique, speculativeToken, origHeapRel);
}

/*
* _bt_check_unique() -- Check for violation of unique index constraint
*
Expand All @@ -404,7 +418,7 @@ _bt_search_insert(Relation rel, BTInsertState insertstate)
static TransactionId
_bt_check_unique(Relation rel, BTInsertState insertstate, Relation heapRel,
IndexUniqueCheck checkUnique, bool *is_unique,
uint32 *speculativeToken)
uint32 *speculativeToken, Relation origHeapRel)
{
IndexTuple itup = insertstate->itup;
IndexTuple curitup = NULL;
Expand Down Expand Up @@ -559,6 +573,7 @@ _bt_check_unique(Relation rel, BTInsertState insertstate, Relation heapRel,
&all_dead))
{
TransactionId xwait;
bool idx_fetch_result;

/*
* It is a duplicate. If we are only doing a partial
Expand Down Expand Up @@ -612,8 +627,13 @@ _bt_check_unique(Relation rel, BTInsertState insertstate, Relation heapRel,
* entry.
*/
htid = itup->t_tid;
if (table_index_fetch_tuple_check(heapRel, &htid,
SnapshotSelf, NULL))
if (origHeapRel)
idx_fetch_result = table_index_fetch_tuple_check(origHeapRel, &htid,
SnapshotSelf, NULL);
else
idx_fetch_result = table_index_fetch_tuple_check(heapRel, &htid,
SnapshotSelf, NULL);
if (idx_fetch_result)
{
/* Normal case --- it's still live */
}
Expand Down
131 changes: 130 additions & 1 deletion src/backend/access/nbtree/nbtree.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
#include "access/nbtxlog.h"
#include "access/relscan.h"
#include "access/xlog.h"
#include "access/xloginsert.h"
#include "access/table.h"
#include "catalog/partition.h"
#include "commands/progress.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
Expand All @@ -33,9 +36,11 @@
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "storage/smgr.h"
#include "storage/predicate.h"
#include "utils/builtins.h"
#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
#include "partitioning/partdesc.h"


/*
Expand Down Expand Up @@ -85,7 +90,9 @@ static BTVacuumPosting btreevacuumposting(BTVacState *vstate,
IndexTuple posting,
OffsetNumber updatedoffset,
int *nremaining);

static void
btinsert_check_unique_gi(IndexTuple itup, Relation idxRel,
Relation heapRel, IndexUniqueCheck checkUnique);

/*
* Btree handler function: return IndexAmRoutine with access method parameters
Expand Down Expand Up @@ -176,6 +183,125 @@ btbuildempty(Relation index)
smgrimmedsync(RelationGetSmgr(index), INIT_FORKNUM);
}

/*
* btinsert_check_unique_gi() -- cross partitions uniqueness check.
*
* loop all partitions with global index for uniqueness check.
*/
static void
btinsert_check_unique_gi(IndexTuple itup, Relation idxRel,
Relation heapRel, IndexUniqueCheck checkUnique)
{
bool is_unique = false;
BTScanInsert itup_key = _bt_mkscankey(idxRel, itup);

if (!itup_key->anynullkeys &&
idxRel->rd_rel->relkind == RELKIND_GLOBAL_INDEX)
{
Oid parentId;
Relation parentTbl;
PartitionDesc partDesc;
int i;
int nparts;
Oid *partOids;

itup_key->scantid = NULL;
parentId = heapRel->rd_rel->relispartition ?
get_partition_parent(idxRel->rd_index->indrelid, false) : InvalidOid;
parentTbl = table_open(parentId, AccessShareLock);
partDesc = RelationGetPartitionDesc(parentTbl, true);
nparts = partDesc->nparts;
partOids = palloc(sizeof(Oid) * nparts);
memcpy(partOids, partDesc->oids, sizeof(Oid) * nparts);
for (i = 0; i < nparts; i++)
{
Oid childRelid = partOids[i];
List *childidxs;
ListCell *cell;

if (childRelid != heapRel->rd_rel->oid)
{
Relation hRel = table_open(childRelid, AccessShareLock);

childidxs = RelationGetIndexList(hRel);
foreach(cell, childidxs)
{
Oid cldidxid = lfirst_oid(cell);
Relation iRel = index_open(cldidxid, AccessShareLock);

if (iRel->rd_rel->relkind == RELKIND_GLOBAL_INDEX
&& iRel->rd_rel->oid != idxRel->rd_rel->oid)
{
BTStack stack;
uint32 speculativeToken;
BTInsertStateData insertstate;
TransactionId xwait = InvalidBuffer;

insertstate.itup = itup;
insertstate.itemsz = MAXALIGN(IndexTupleSize(itup));
insertstate.itup_key = itup_key;
insertstate.bounds_valid = false;
insertstate.buf = InvalidBuffer;
insertstate.postingoff = 0;

search_global:
stack = _bt_search(iRel, insertstate.itup_key,
&insertstate.buf, BT_READ, NULL);
/*
* If the index is empty, _bt_search returns
* InvalidBuffer. No duplicates are possible.
*/
if (BufferIsValid(insertstate.buf))
{
xwait = _bt_check_unique_gi(iRel, &insertstate,
hRel, checkUnique, &is_unique,
&speculativeToken, heapRel);
if (unlikely(TransactionIdIsValid(xwait)))
{
/* Have to wait for the other guy ... */
if (insertstate.buf)
{
_bt_relbuf(iRel, insertstate.buf);
insertstate.buf = InvalidBuffer;
}

/*
* If it's a speculative insertion, wait for it to
* finish (ie. to go ahead with the insertion, or
* kill the tuple). Otherwise wait for the
* transaction to finish as usual.
*/
if (speculativeToken)
SpeculativeInsertionWait(xwait, speculativeToken);
else
XactLockTableWait(xwait, iRel, &itup->t_tid, XLTW_InsertIndex);

/* start over... */
if (stack)
_bt_freestack(stack);
goto search_global;
}
if (insertstate.buf)
_bt_relbuf(iRel, insertstate.buf);
}
if (stack)
_bt_freestack(stack);
}
index_close(iRel, AccessShareLock);
}
if (childidxs)
list_free(childidxs);
table_close(hRel, AccessShareLock);
}
}
if (partOids)
pfree(partOids);
table_close(parentTbl, AccessShareLock);
}
if (itup_key)
pfree(itup_key);
}

/*
* btinsert() -- insert an index tuple into a btree.
*
Expand All @@ -198,6 +324,9 @@ btinsert(Relation rel, Datum *values, bool *isnull,

result = _bt_doinsert(rel, itup, checkUnique, indexUnchanged, heapRel);

if (checkUnique != UNIQUE_CHECK_NO)
btinsert_check_unique_gi(itup, rel, heapRel, checkUnique);

pfree(itup);

return result;
Expand Down
Loading