Multi-column GIN indexes. Teodor Sigaev

This commit is contained in:
Tom Lane 2008-07-11 21:06:29 +00:00
parent 2d6599f471
commit 27cb66fdfe
15 changed files with 465 additions and 182 deletions

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/indices.sgml,v 1.73 2008/05/27 00:13:08 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/indices.sgml,v 1.74 2008/07/11 21:06:28 tgl Exp $ -->
<chapter id="indexes">
<title id="indexes-title">Indexes</title>
@ -198,7 +198,7 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable>
after a database crash.
For these reasons, hash index use is presently discouraged.
</para>
</note>
</note>
<para>
<indexterm>
@ -250,9 +250,9 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable>
</indexterm>
GIN indexes are inverted indexes which can handle values that contain more
than one key, arrays for example. Like GiST, GIN can support
many different user-defined indexing strategies and the particular
operators with which a GIN index can be used vary depending on the
indexing strategy.
many different user-defined indexing strategies and the particular
operators with which a GIN index can be used vary depending on the
indexing strategy.
As an example, the standard distribution of
<productname>PostgreSQL</productname> includes GIN operator classes
for one-dimensional arrays, which support indexed
@ -306,7 +306,7 @@ CREATE INDEX test2_mm_idx ON test2 (major, minor);
</para>
<para>
Currently, only the B-tree and GiST index types support multicolumn
Currently, only the B-tree, GiST and GIN index types support multicolumn
indexes. Up to 32 columns can be specified. (This limit can be
altered when building <productname>PostgreSQL</productname>; see the
file <filename>pg_config_manual.h</filename>.)
@ -336,14 +336,21 @@ CREATE INDEX test2_mm_idx ON test2 (major, minor);
<para>
A multicolumn GiST index can be used with query conditions that
involve any subset of the index's columns. Conditions on additional
columns restrict the entries returned by the index, but the condition on
the first column is the most important one for determining how much of
the index needs to be scanned. A GiST index will be relatively
ineffective if its first column has only a few distinct values, even if
involve any subset of the index's columns. Conditions on additional
columns restrict the entries returned by the index, but the condition on
the first column is the most important one for determining how much of
the index needs to be scanned. A GiST index will be relatively
ineffective if its first column has only a few distinct values, even if
there are many distinct values in additional columns.
</para>
<para>
A multicolumn GIN index can be used with query conditions that
involve any subset of the index's columns. Unlike B-tree or GiST,
index search effectiveness is the same regardless of which index column(s)
the query conditions use.
</para>
<para>
Of course, each column must be used with operators appropriate to the index
type; clauses that involve other operators will not be considered.
@ -551,7 +558,7 @@ CREATE UNIQUE INDEX <replaceable>name</replaceable> ON <replaceable>table</repla
<para>
<productname>PostgreSQL</productname> automatically creates a unique
index when a unique constraint or a primary key is defined for a table.
The index covers the columns that make up the primary key or unique
The index covers the columns that make up the primary key or unique
columns (a multicolumn index, if appropriate), and is the mechanism
that enforces the constraint.
</para>
@ -798,9 +805,9 @@ SELECT * FROM orders WHERE order_nr = 3501;
or the index will not be recognized to be usable. Matching takes
place at query planning time, not at run time. As a result,
parameterized query clauses will not work with a partial index. For
example a prepared query with a parameter might specify
<quote>x &lt; ?</quote> which will never imply
<quote>x &lt; 2</quote> for all possible values of the parameter.
example a prepared query with a parameter might specify
<quote>x &lt; ?</quote> which will never imply
<quote>x &lt; 2</quote> for all possible values of the parameter.
</para>
<para>

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/create_index.sgml,v 1.67 2008/03/16 23:57:51 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/create_index.sgml,v 1.68 2008/07/11 21:06:29 tgl Exp $
PostgreSQL documentation
-->
@ -394,7 +394,7 @@ Indexes:
</para>
<para>
Currently, only the B-tree and GiST index methods support
Currently, only the B-tree, GiST and GIN index methods support
multicolumn indexes. Up to 32 fields can be specified by default.
(This limit can be altered when building
<productname>PostgreSQL</productname>.) Only B-tree currently
@ -423,7 +423,7 @@ Indexes:
the optional clauses <literal>ASC</>, <literal>DESC</>, <literal>NULLS
FIRST</>, and/or <literal>NULLS LAST</> can be specified to reverse
the normal sort direction of the index. Since an ordered index can be
scanned either forward or backward, it is not normally useful to create a
scanned either forward or backward, it is not normally useful to create a
single-column <literal>DESC</> index &mdash; that sort ordering is already
available with a regular index. The value of these options is that
multicolumn indexes can be created that match the sort ordering requested

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginbulk.c,v 1.12 2008/06/29 21:04:01 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/ginbulk.c,v 1.13 2008/07/11 21:06:29 tgl Exp $
*-------------------------------------------------------------------------
*/
@ -82,16 +82,16 @@ ginInsertData(BuildAccumulator *accum, EntryAccumulator *entry, ItemPointer heap
* palloc'd space in accum.
*/
static Datum
getDatumCopy(BuildAccumulator *accum, Datum value)
getDatumCopy(BuildAccumulator *accum, OffsetNumber attnum, Datum value)
{
Form_pg_attribute *att = accum->ginstate->tupdesc->attrs;
Form_pg_attribute att = accum->ginstate->origTupdesc->attrs[ attnum - 1 ];
Datum res;
if (att[0]->attbyval)
if (att->attbyval)
res = value;
else
{
res = datumCopy(value, false, att[0]->attlen);
res = datumCopy(value, false, att->attlen);
accum->allocatedMemory += GetMemoryChunkSpace(DatumGetPointer(res));
}
return res;
@ -101,7 +101,7 @@ getDatumCopy(BuildAccumulator *accum, Datum value)
* Find/store one entry from indexed value.
*/
static void
ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, Datum entry)
ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum, Datum entry)
{
EntryAccumulator *ea = accum->entries,
*pea = NULL;
@ -110,7 +110,7 @@ ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, Datum entry)
while (ea)
{
res = compareEntries(accum->ginstate, entry, ea->value);
res = compareAttEntries(accum->ginstate, attnum, entry, ea->attnum, ea->value);
if (res == 0)
break; /* found */
else
@ -132,7 +132,8 @@ ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, Datum entry)
ea = EAAllocate(accum);
ea->left = ea->right = NULL;
ea->value = getDatumCopy(accum, entry);
ea->attnum = attnum;
ea->value = getDatumCopy(accum, attnum, entry);
ea->length = DEF_NPTR;
ea->number = 1;
ea->shouldSort = FALSE;
@ -160,7 +161,8 @@ ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, Datum entry)
* then calls itself for each parts
*/
static void
ginChooseElem(BuildAccumulator *accum, ItemPointer heapptr, Datum *entries, uint32 nentry,
ginChooseElem(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum,
Datum *entries, uint32 nentry,
uint32 low, uint32 high, uint32 offset)
{
uint32 pos;
@ -168,15 +170,15 @@ ginChooseElem(BuildAccumulator *accum, ItemPointer heapptr, Datum *entries, uint
pos = (low + middle) >> 1;
if (low != middle && pos >= offset && pos - offset < nentry)
ginInsertEntry(accum, heapptr, entries[pos - offset]);
ginInsertEntry(accum, heapptr, attnum, entries[pos - offset]);
pos = (high + middle + 1) >> 1;
if (middle + 1 != high && pos >= offset && pos - offset < nentry)
ginInsertEntry(accum, heapptr, entries[pos - offset]);
ginInsertEntry(accum, heapptr, attnum, entries[pos - offset]);
if (low != middle)
ginChooseElem(accum, heapptr, entries, nentry, low, middle, offset);
ginChooseElem(accum, heapptr, attnum, entries, nentry, low, middle, offset);
if (high != middle + 1)
ginChooseElem(accum, heapptr, entries, nentry, middle + 1, high, offset);
ginChooseElem(accum, heapptr, attnum, entries, nentry, middle + 1, high, offset);
}
/*
@ -185,7 +187,8 @@ ginChooseElem(BuildAccumulator *accum, ItemPointer heapptr, Datum *entries, uint
* next middle on left part and middle of right part.
*/
void
ginInsertRecordBA(BuildAccumulator *accum, ItemPointer heapptr, Datum *entries, int32 nentry)
ginInsertRecordBA(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum,
Datum *entries, int32 nentry)
{
uint32 i,
nbit = 0,
@ -201,8 +204,8 @@ ginInsertRecordBA(BuildAccumulator *accum, ItemPointer heapptr, Datum *entries,
nbit = 1 << nbit;
offset = (nbit - nentry) / 2;
ginInsertEntry(accum, heapptr, entries[(nbit >> 1) - offset]);
ginChooseElem(accum, heapptr, entries, nentry, 0, nbit, offset);
ginInsertEntry(accum, heapptr, attnum, entries[(nbit >> 1) - offset]);
ginChooseElem(accum, heapptr, attnum, entries, nentry, 0, nbit, offset);
}
static int
@ -259,7 +262,7 @@ walkTree(BuildAccumulator *accum)
}
ItemPointerData *
ginGetEntry(BuildAccumulator *accum, Datum *value, uint32 *n)
ginGetEntry(BuildAccumulator *accum, OffsetNumber *attnum, Datum *value, uint32 *n)
{
EntryAccumulator *entry;
ItemPointerData *list;
@ -299,6 +302,7 @@ ginGetEntry(BuildAccumulator *accum, Datum *value, uint32 *n)
return NULL;
*n = entry->number;
*attnum = entry->attnum;
*value = entry->value;
list = entry->list;

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginentrypage.c,v 1.16 2008/06/19 00:46:03 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/ginentrypage.c,v 1.17 2008/07/11 21:06:29 tgl Exp $
*-------------------------------------------------------------------------
*/
@ -38,14 +38,27 @@
* - ItemPointerGetBlockNumber(&itup->t_tid) contains block number of
* root of posting tree
* - ItemPointerGetOffsetNumber(&itup->t_tid) contains magic number GIN_TREE_POSTING
*
* Storage of attributes of tuple are different for single and multicolumn index.
* For single-column index tuple stores only value to be indexed and for
* multicolumn variant it stores two attributes: column number of value and value.
*/
IndexTuple
GinFormTuple(GinState *ginstate, Datum key, ItemPointerData *ipd, uint32 nipd)
GinFormTuple(GinState *ginstate, OffsetNumber attnum, Datum key, ItemPointerData *ipd, uint32 nipd)
{
bool isnull = FALSE;
bool isnull[2] = {FALSE,FALSE};
IndexTuple itup;
itup = index_form_tuple(ginstate->tupdesc, &key, &isnull);
if ( ginstate->oneCol )
itup = index_form_tuple(ginstate->origTupdesc, &key, isnull);
else
{
Datum datums[2];
datums[0] = UInt16GetDatum(attnum);
datums[1] = key;
itup = index_form_tuple(ginstate->tupdesc[attnum-1], datums, isnull);
}
GinSetOrigSizePosting(itup, IndexTupleSize(itup));
@ -88,28 +101,20 @@ getRightMostTuple(Page page)
return (IndexTuple) PageGetItem(page, PageGetItemId(page, maxoff));
}
Datum
ginGetHighKey(GinState *ginstate, Page page)
{
IndexTuple itup;
bool isnull;
itup = getRightMostTuple(page);
return index_getattr(itup, FirstOffsetNumber, ginstate->tupdesc, &isnull);
}
static bool
entryIsMoveRight(GinBtree btree, Page page)
{
Datum highkey;
IndexTuple itup;
if (GinPageRightMost(page))
return FALSE;
highkey = ginGetHighKey(btree->ginstate, page);
itup = getRightMostTuple(page);
if (compareEntries(btree->ginstate, btree->entryValue, highkey) > 0)
if (compareAttEntries(btree->ginstate,
btree->entryAttnum, btree->entryValue,
gintuple_get_attrnum(btree->ginstate, itup),
gin_index_getattr(btree->ginstate, itup)) > 0)
return TRUE;
return FALSE;
@ -154,11 +159,11 @@ entryLocateEntry(GinBtree btree, GinBtreeStack *stack)
result = -1;
else
{
bool isnull;
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
result = compareEntries(btree->ginstate, btree->entryValue,
index_getattr(itup, FirstOffsetNumber, btree->ginstate->tupdesc, &isnull));
result = compareAttEntries(btree->ginstate,
btree->entryAttnum, btree->entryValue,
gintuple_get_attrnum(btree->ginstate, itup),
gin_index_getattr(btree->ginstate, itup));
}
if (result == 0)
@ -217,13 +222,13 @@ entryLocateLeafEntry(GinBtree btree, GinBtreeStack *stack)
while (high > low)
{
OffsetNumber mid = low + ((high - low) / 2);
bool isnull;
int result;
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
result = compareEntries(btree->ginstate, btree->entryValue,
index_getattr(itup, FirstOffsetNumber, btree->ginstate->tupdesc, &isnull));
result = compareAttEntries(btree->ginstate,
btree->entryAttnum, btree->entryValue,
gintuple_get_attrnum(btree->ginstate, itup),
gin_index_getattr(btree->ginstate, itup));
if (result == 0)
{
stack->off = mid;
@ -587,7 +592,7 @@ entryFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf)
}
void
prepareEntryScan(GinBtree btree, Relation index, Datum value, GinState *ginstate)
prepareEntryScan(GinBtree btree, Relation index, OffsetNumber attnum, Datum value, GinState *ginstate)
{
memset(btree, 0, sizeof(GinBtreeData));
@ -603,6 +608,7 @@ prepareEntryScan(GinBtree btree, Relation index, Datum value, GinState *ginstate
btree->index = index;
btree->ginstate = ginstate;
btree->entryAttnum = attnum;
btree->entryValue = value;
btree->isDelete = FALSE;

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginget.c,v 1.17 2008/06/19 00:46:03 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/ginget.c,v 1.18 2008/07/11 21:06:29 tgl Exp $
*-------------------------------------------------------------------------
*/
@ -138,7 +138,6 @@ computePartialMatchList( GinBtreeData *btree, GinBtreeStack *stack, GinScanEntry
Page page;
IndexTuple itup;
Datum idatum;
bool isnull;
int32 cmp;
scanEntry->partialMatch = tbm_create( work_mem * 1024L );
@ -153,8 +152,15 @@ computePartialMatchList( GinBtreeData *btree, GinBtreeStack *stack, GinScanEntry
page = BufferGetPage(stack->buffer);
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stack->off));
idatum = index_getattr(itup, 1, btree->ginstate->tupdesc, &isnull);
Assert(!isnull);
/*
* If tuple stores another attribute then stop scan
*/
if ( gintuple_get_attrnum( btree->ginstate, itup ) != scanEntry->attnum )
return true;
idatum = gin_index_getattr( btree->ginstate, itup );
/*----------
* Check of partial match.
@ -163,7 +169,7 @@ computePartialMatchList( GinBtreeData *btree, GinBtreeStack *stack, GinScanEntry
* case cmp < 0 => not match and continue scan
*----------
*/
cmp = DatumGetInt32(FunctionCall3(&btree->ginstate->comparePartialFn,
cmp = DatumGetInt32(FunctionCall3(&btree->ginstate->comparePartialFn[scanEntry->attnum-1],
scanEntry->entry,
idatum,
UInt16GetDatum(scanEntry->strategy)));
@ -182,8 +188,8 @@ computePartialMatchList( GinBtreeData *btree, GinBtreeStack *stack, GinScanEntry
Datum newDatum,
savedDatum = datumCopy (
idatum,
btree->ginstate->tupdesc->attrs[0]->attbyval,
btree->ginstate->tupdesc->attrs[0]->attlen
btree->ginstate->origTupdesc->attrs[scanEntry->attnum-1]->attbyval,
btree->ginstate->origTupdesc->attrs[scanEntry->attnum-1]->attlen
);
/*
* We should unlock current page (but not unpin) during
@ -220,12 +226,15 @@ computePartialMatchList( GinBtreeData *btree, GinBtreeStack *stack, GinScanEntry
page = BufferGetPage(stack->buffer);
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stack->off));
newDatum = index_getattr(itup, FirstOffsetNumber, btree->ginstate->tupdesc, &isnull);
newDatum = gin_index_getattr( btree->ginstate, itup );
if ( compareEntries(btree->ginstate, newDatum, savedDatum) == 0 )
if ( gintuple_get_attrnum( btree->ginstate, itup ) != scanEntry->attnum )
elog(ERROR, "lost saved point in index"); /* must not happen !!! */
if ( compareEntries(btree->ginstate, scanEntry->attnum, newDatum, savedDatum) == 0 )
{
/* Found! */
if ( btree->ginstate->tupdesc->attrs[0]->attbyval == false )
if ( btree->ginstate->origTupdesc->attrs[scanEntry->attnum-1]->attbyval == false )
pfree( DatumGetPointer(savedDatum) );
break;
}
@ -270,7 +279,7 @@ startScanEntry(Relation index, GinState *ginstate, GinScanEntry entry)
* or just store posting list in memory
*/
prepareEntryScan(&btreeEntry, index, entry->entry, ginstate);
prepareEntryScan(&btreeEntry, index, entry->attnum, entry->entry, ginstate);
btreeEntry.searchMode = TRUE;
stackEntry = ginFindLeafPage(&btreeEntry, NULL);
page = BufferGetPage(stackEntry->buffer);
@ -705,7 +714,7 @@ keyGetItem(Relation index, GinState *ginstate, MemoryContext tempCtx,
*keyrecheck = true;
oldCtx = MemoryContextSwitchTo(tempCtx);
res = DatumGetBool(FunctionCall4(&ginstate->consistentFn,
res = DatumGetBool(FunctionCall4(&ginstate->consistentFn[key->attnum-1],
PointerGetDatum(key->entryRes),
UInt16GetDatum(key->strategy),
key->query,

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.13 2008/05/16 01:27:06 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.14 2008/07/11 21:06:29 tgl Exp $
*-------------------------------------------------------------------------
*/
@ -99,9 +99,9 @@ static IndexTuple
addItemPointersToTuple(Relation index, GinState *ginstate, GinBtreeStack *stack,
IndexTuple old, ItemPointerData *items, uint32 nitem, bool isBuild)
{
bool isnull;
Datum key = index_getattr(old, FirstOffsetNumber, ginstate->tupdesc, &isnull);
IndexTuple res = GinFormTuple(ginstate, key, NULL, nitem + GinGetNPosting(old));
Datum key = gin_index_getattr(ginstate, old);
OffsetNumber attnum = gintuple_get_attrnum(ginstate, old);
IndexTuple res = GinFormTuple(ginstate, attnum, key, NULL, nitem + GinGetNPosting(old));
if (res)
{
@ -119,7 +119,7 @@ addItemPointersToTuple(Relation index, GinState *ginstate, GinBtreeStack *stack,
GinPostingTreeScan *gdi;
/* posting list becomes big, so we need to make posting's tree */
res = GinFormTuple(ginstate, key, NULL, 0);
res = GinFormTuple(ginstate, attnum, key, NULL, 0);
postingRoot = createPostingTree(index, GinGetPosting(old), GinGetNPosting(old));
GinSetPostingTree(res, postingRoot);
@ -138,14 +138,15 @@ addItemPointersToTuple(Relation index, GinState *ginstate, GinBtreeStack *stack,
* Inserts only one entry to the index, but it can add more than 1 ItemPointer.
*/
static void
ginEntryInsert(Relation index, GinState *ginstate, Datum value, ItemPointerData *items, uint32 nitem, bool isBuild)
ginEntryInsert(Relation index, GinState *ginstate, OffsetNumber attnum, Datum value,
ItemPointerData *items, uint32 nitem, bool isBuild)
{
GinBtreeData btree;
GinBtreeStack *stack;
IndexTuple itup;
Page page;
prepareEntryScan(&btree, index, value, ginstate);
prepareEntryScan(&btree, index, attnum, value, ginstate);
stack = ginFindLeafPage(&btree, NULL);
page = BufferGetPage(stack->buffer);
@ -180,7 +181,7 @@ ginEntryInsert(Relation index, GinState *ginstate, Datum value, ItemPointerData
else
{
/* We suppose, that tuple can store at list one itempointer */
itup = GinFormTuple(ginstate, value, items, 1);
itup = GinFormTuple(ginstate, attnum, value, items, 1);
if (itup == NULL || IndexTupleSize(itup) >= GinMaxItemSize)
elog(ERROR, "huge tuple");
@ -203,21 +204,21 @@ ginEntryInsert(Relation index, GinState *ginstate, Datum value, ItemPointerData
* Function isn't used during normal insert
*/
static uint32
ginHeapTupleBulkInsert(GinBuildState *buildstate, Datum value, ItemPointer heapptr)
ginHeapTupleBulkInsert(GinBuildState *buildstate, OffsetNumber attnum, Datum value, ItemPointer heapptr)
{
Datum *entries;
int32 nentries;
MemoryContext oldCtx;
oldCtx = MemoryContextSwitchTo(buildstate->funcCtx);
entries = extractEntriesSU(buildstate->accum.ginstate, value, &nentries);
entries = extractEntriesSU(buildstate->accum.ginstate, attnum, value, &nentries);
MemoryContextSwitchTo(oldCtx);
if (nentries == 0)
/* nothing to insert */
return 0;
ginInsertRecordBA(&buildstate->accum, heapptr, entries, nentries);
ginInsertRecordBA(&buildstate->accum, heapptr, attnum, entries, nentries);
MemoryContextReset(buildstate->funcCtx);
@ -230,13 +231,15 @@ ginBuildCallback(Relation index, HeapTuple htup, Datum *values,
{
GinBuildState *buildstate = (GinBuildState *) state;
MemoryContext oldCtx;
if (*isnull)
return;
int i;
oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
buildstate->indtuples += ginHeapTupleBulkInsert(buildstate, *values, &htup->t_self);
for(i=0; i<buildstate->ginstate.origTupdesc->natts;i++)
if ( !isnull[i] )
buildstate->indtuples += ginHeapTupleBulkInsert(buildstate,
(OffsetNumber)(i+1), values[i],
&htup->t_self);
/* If we've maxed out our available memory, dump everything to the index */
if (buildstate->accum.allocatedMemory >= maintenance_work_mem * 1024L)
@ -244,12 +247,13 @@ ginBuildCallback(Relation index, HeapTuple htup, Datum *values,
ItemPointerData *list;
Datum entry;
uint32 nlist;
OffsetNumber attnum;
while ((list = ginGetEntry(&buildstate->accum, &entry, &nlist)) != NULL)
while ((list = ginGetEntry(&buildstate->accum, &attnum, &entry, &nlist)) != NULL)
{
/* there could be many entries, so be willing to abort here */
CHECK_FOR_INTERRUPTS();
ginEntryInsert(index, &buildstate->ginstate, entry, list, nlist, TRUE);
ginEntryInsert(index, &buildstate->ginstate, attnum, entry, list, nlist, TRUE);
}
MemoryContextReset(buildstate->tmpCtx);
@ -273,6 +277,7 @@ ginbuild(PG_FUNCTION_ARGS)
Datum entry;
uint32 nlist;
MemoryContext oldCtx;
OffsetNumber attnum;
if (RelationGetNumberOfBlocks(index) != 0)
elog(ERROR, "index \"%s\" already contains data",
@ -337,11 +342,11 @@ ginbuild(PG_FUNCTION_ARGS)
/* dump remaining entries to the index */
oldCtx = MemoryContextSwitchTo(buildstate.tmpCtx);
while ((list = ginGetEntry(&buildstate.accum, &entry, &nlist)) != NULL)
while ((list = ginGetEntry(&buildstate.accum, &attnum, &entry, &nlist)) != NULL)
{
/* there could be many entries, so be willing to abort here */
CHECK_FOR_INTERRUPTS();
ginEntryInsert(index, &buildstate.ginstate, entry, list, nlist, TRUE);
ginEntryInsert(index, &buildstate.ginstate, attnum, entry, list, nlist, TRUE);
}
MemoryContextSwitchTo(oldCtx);
@ -362,20 +367,20 @@ ginbuild(PG_FUNCTION_ARGS)
* Inserts value during normal insertion
*/
static uint32
ginHeapTupleInsert(Relation index, GinState *ginstate, Datum value, ItemPointer item)
ginHeapTupleInsert(Relation index, GinState *ginstate, OffsetNumber attnum, Datum value, ItemPointer item)
{
Datum *entries;
int32 i,
nentries;
entries = extractEntriesSU(ginstate, value, &nentries);
entries = extractEntriesSU(ginstate, attnum, value, &nentries);
if (nentries == 0)
/* nothing to insert */
return 0;
for (i = 0; i < nentries; i++)
ginEntryInsert(index, ginstate, entries[i], item, 1, FALSE);
ginEntryInsert(index, ginstate, attnum, entries[i], item, 1, FALSE);
return nentries;
}
@ -395,10 +400,8 @@ gininsert(PG_FUNCTION_ARGS)
GinState ginstate;
MemoryContext oldCtx;
MemoryContext insertCtx;
uint32 res;
if (*isnull)
PG_RETURN_BOOL(false);
uint32 res = 0;
int i;
insertCtx = AllocSetContextCreate(CurrentMemoryContext,
"Gin insert temporary context",
@ -410,7 +413,9 @@ gininsert(PG_FUNCTION_ARGS)
initGinState(&ginstate, index);
res = ginHeapTupleInsert(index, &ginstate, *values, ht_ctid);
for(i=0; i<ginstate.origTupdesc->natts;i++)
if ( !isnull[i] )
res += ginHeapTupleInsert(index, &ginstate, (OffsetNumber)(i+1), values[i], ht_ctid);
MemoryContextSwitchTo(oldCtx);
MemoryContextDelete(insertCtx);

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginscan.c,v 1.16 2008/07/04 13:21:18 teodor Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/ginscan.c,v 1.17 2008/07/11 21:06:29 tgl Exp $
*-------------------------------------------------------------------------
*/
@ -36,7 +36,7 @@ ginbeginscan(PG_FUNCTION_ARGS)
}
static void
fillScanKey(GinState *ginstate, GinScanKey key, Datum query,
fillScanKey(GinState *ginstate, GinScanKey key, OffsetNumber attnum, Datum query,
Datum *entryValues, bool *partial_matches, uint32 nEntryValues,
StrategyNumber strategy)
{
@ -47,6 +47,7 @@ fillScanKey(GinState *ginstate, GinScanKey key, Datum query,
key->entryRes = (bool *) palloc0(sizeof(bool) * nEntryValues);
key->scanEntry = (GinScanEntry) palloc(sizeof(GinScanEntryData) * nEntryValues);
key->strategy = strategy;
key->attnum = attnum;
key->query = query;
key->firstCall = TRUE;
ItemPointerSet(&(key->curItem), InvalidBlockNumber, InvalidOffsetNumber);
@ -55,19 +56,20 @@ fillScanKey(GinState *ginstate, GinScanKey key, Datum query,
{
key->scanEntry[i].pval = key->entryRes + i;
key->scanEntry[i].entry = entryValues[i];
key->scanEntry[i].attnum = attnum;
ItemPointerSet(&(key->scanEntry[i].curItem), InvalidBlockNumber, InvalidOffsetNumber);
key->scanEntry[i].offset = InvalidOffsetNumber;
key->scanEntry[i].buffer = InvalidBuffer;
key->scanEntry[i].partialMatch = NULL;
key->scanEntry[i].list = NULL;
key->scanEntry[i].nlist = 0;
key->scanEntry[i].isPartialMatch = ( ginstate->canPartialMatch && partial_matches )
key->scanEntry[i].isPartialMatch = ( ginstate->canPartialMatch[attnum - 1] && partial_matches )
? partial_matches[i] : false;
/* link to the equals entry in current scan key */
key->scanEntry[i].master = NULL;
for (j = 0; j < i; j++)
if (compareEntries(ginstate, entryValues[i], entryValues[j]) == 0)
if (compareEntries(ginstate, attnum, entryValues[i], entryValues[j]) == 0)
{
key->scanEntry[i].master = key->scanEntry + j;
break;
@ -164,19 +166,17 @@ newScanKey(IndexScanDesc scan)
int32 nEntryValues;
bool *partial_matches = NULL;
Assert(scankey[i].sk_attno == 1);
/* XXX can't we treat nulls by just setting isVoidRes? */
/* This would amount to assuming that all GIN operators are strict */
if (scankey[i].sk_flags & SK_ISNULL)
elog(ERROR, "GIN doesn't support NULL as scan key");
entryValues = (Datum *) DatumGetPointer(FunctionCall4(
&so->ginstate.extractQueryFn,
scankey[i].sk_argument,
PointerGetDatum(&nEntryValues),
UInt16GetDatum(scankey[i].sk_strategy),
PointerGetDatum(&partial_matches)));
&so->ginstate.extractQueryFn[scankey[i].sk_attno - 1],
scankey[i].sk_argument,
PointerGetDatum(&nEntryValues),
UInt16GetDatum(scankey[i].sk_strategy),
PointerGetDatum(&partial_matches)));
if (nEntryValues < 0)
{
/*
@ -194,7 +194,7 @@ newScanKey(IndexScanDesc scan)
/* full scan... */
continue;
fillScanKey(&so->ginstate, &(so->keys[nkeys]), scankey[i].sk_argument,
fillScanKey(&so->ginstate, &(so->keys[nkeys]), scankey[i].sk_attno, scankey[i].sk_argument,
entryValues, partial_matches, nEntryValues, scankey[i].sk_strategy);
nkeys++;
}

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.15 2008/05/16 16:31:01 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.16 2008/07/11 21:06:29 tgl Exp $
*-------------------------------------------------------------------------
*/
@ -16,6 +16,7 @@
#include "access/genam.h"
#include "access/gin.h"
#include "access/reloptions.h"
#include "catalog/pg_type.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/lmgr.h"
@ -23,40 +24,116 @@
void
initGinState(GinState *state, Relation index)
{
if (index->rd_att->natts != 1)
elog(ERROR, "numberOfAttributes %d != 1",
index->rd_att->natts);
int i;
state->tupdesc = index->rd_att;
state->origTupdesc = index->rd_att;
fmgr_info_copy(&(state->compareFn),
index_getprocinfo(index, 1, GIN_COMPARE_PROC),
CurrentMemoryContext);
fmgr_info_copy(&(state->extractValueFn),
index_getprocinfo(index, 1, GIN_EXTRACTVALUE_PROC),
CurrentMemoryContext);
fmgr_info_copy(&(state->extractQueryFn),
index_getprocinfo(index, 1, GIN_EXTRACTQUERY_PROC),
CurrentMemoryContext);
fmgr_info_copy(&(state->consistentFn),
index_getprocinfo(index, 1, GIN_CONSISTENT_PROC),
CurrentMemoryContext);
/*
* Check opclass capability to do partial match.
*/
if ( index_getprocid(index, 1, GIN_COMPARE_PARTIAL_PROC) != InvalidOid )
state->oneCol = (index->rd_att->natts == 1) ? true : false;
for(i=0;i<index->rd_att->natts;i++)
{
fmgr_info_copy(&(state->comparePartialFn),
index_getprocinfo(index, 1, GIN_COMPARE_PARTIAL_PROC),
CurrentMemoryContext);
state->tupdesc[i] = CreateTemplateTupleDesc(2,false);
state->canPartialMatch = true;
TupleDescInitEntry( state->tupdesc[i], (AttrNumber) 1, NULL,
INT2OID, -1, 0);
TupleDescInitEntry( state->tupdesc[i], (AttrNumber) 2, NULL,
index->rd_att->attrs[i]->atttypid,
index->rd_att->attrs[i]->atttypmod,
index->rd_att->attrs[i]->attndims
);
fmgr_info_copy(&(state->compareFn[i]),
index_getprocinfo(index, i+1, GIN_COMPARE_PROC),
CurrentMemoryContext);
fmgr_info_copy(&(state->extractValueFn[i]),
index_getprocinfo(index, i+1, GIN_EXTRACTVALUE_PROC),
CurrentMemoryContext);
fmgr_info_copy(&(state->extractQueryFn[i]),
index_getprocinfo(index, i+1, GIN_EXTRACTQUERY_PROC),
CurrentMemoryContext);
fmgr_info_copy(&(state->consistentFn[i]),
index_getprocinfo(index, i+1, GIN_CONSISTENT_PROC),
CurrentMemoryContext);
/*
* Check opclass capability to do partial match.
*/
if ( index_getprocid(index, i+1, GIN_COMPARE_PARTIAL_PROC) != InvalidOid )
{
fmgr_info_copy(&(state->comparePartialFn[i]),
index_getprocinfo(index, i+1, GIN_COMPARE_PARTIAL_PROC),
CurrentMemoryContext);
state->canPartialMatch[i] = true;
}
else
{
state->canPartialMatch[i] = false;
}
}
}
/*
* Extract attribute (column) number of stored entry from GIN tuple
*/
OffsetNumber
gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple)
{
OffsetNumber colN = FirstOffsetNumber;
if ( !ginstate->oneCol )
{
Datum res;
bool isnull;
/*
* First attribute is always int16, so we can safely use any
* tuple descriptor to obtain first attribute of tuple
*/
res = index_getattr(tuple, FirstOffsetNumber, ginstate->tupdesc[0],
&isnull);
Assert(!isnull);
colN = DatumGetUInt16(res);
Assert( colN >= FirstOffsetNumber && colN <= ginstate->origTupdesc->natts );
}
return colN;
}
/*
* Extract stored datum from GIN tuple
*/
Datum
gin_index_getattr(GinState *ginstate, IndexTuple tuple)
{
bool isnull;
Datum res;
if ( ginstate->oneCol )
{
/*
* Single column index doesn't store attribute numbers in tuples
*/
res = index_getattr(tuple, FirstOffsetNumber, ginstate->origTupdesc,
&isnull);
}
else
{
state->canPartialMatch = false;
/*
* Since the datum type depends on which index column it's from,
* we must be careful to use the right tuple descriptor here.
*/
OffsetNumber colN = gintuple_get_attrnum(ginstate, tuple);
res = index_getattr(tuple, OffsetNumberNext(FirstOffsetNumber),
ginstate->tupdesc[colN - 1],
&isnull);
}
Assert(!isnull);
return res;
}
/*
@ -136,16 +213,26 @@ GinInitBuffer(Buffer b, uint32 f)
}
int
compareEntries(GinState *ginstate, Datum a, Datum b)
compareEntries(GinState *ginstate, OffsetNumber attnum, Datum a, Datum b)
{
return DatumGetInt32(
FunctionCall2(
&ginstate->compareFn,
&ginstate->compareFn[attnum-1],
a, b
)
);
}
int
compareAttEntries(GinState *ginstate, OffsetNumber attnum_a, Datum a,
OffsetNumber attnum_b, Datum b)
{
if ( attnum_a == attnum_b )
return compareEntries( ginstate, attnum_a, a, b);
return ( attnum_a < attnum_b ) ? -1 : 1;
}
typedef struct
{
FmgrInfo *cmpDatumFunc;
@ -165,13 +252,13 @@ cmpEntries(const Datum *a, const Datum *b, cmpEntriesData *arg)
}
Datum *
extractEntriesS(GinState *ginstate, Datum value, int32 *nentries,
extractEntriesS(GinState *ginstate, OffsetNumber attnum, Datum value, int32 *nentries,
bool *needUnique)
{
Datum *entries;
entries = (Datum *) DatumGetPointer(FunctionCall2(
&ginstate->extractValueFn,
&ginstate->extractValueFn[attnum-1],
value,
PointerGetDatum(nentries)
));
@ -184,7 +271,7 @@ extractEntriesS(GinState *ginstate, Datum value, int32 *nentries,
{
cmpEntriesData arg;
arg.cmpDatumFunc = &ginstate->compareFn;
arg.cmpDatumFunc = &ginstate->compareFn[attnum-1];
arg.needUnique = needUnique;
qsort_arg(entries, *nentries, sizeof(Datum),
(qsort_arg_comparator) cmpEntries, (void *) &arg);
@ -195,10 +282,10 @@ extractEntriesS(GinState *ginstate, Datum value, int32 *nentries,
Datum *
extractEntriesSU(GinState *ginstate, Datum value, int32 *nentries)
extractEntriesSU(GinState *ginstate, OffsetNumber attnum, Datum value, int32 *nentries)
{
bool needUnique;
Datum *entries = extractEntriesS(ginstate, value, nentries,
Datum *entries = extractEntriesS(ginstate, attnum, value, nentries,
&needUnique);
if (needUnique)
@ -210,7 +297,7 @@ extractEntriesSU(GinState *ginstate, Datum value, int32 *nentries)
while (ptr - entries < *nentries)
{
if (compareEntries(ginstate, *ptr, *res) != 0)
if (compareEntries(ginstate, attnum, *ptr, *res) != 0)
*(++res) = *ptr++;
else
ptr++;

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.20 2008/05/12 00:00:44 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.21 2008/07/11 21:06:29 tgl Exp $
*-------------------------------------------------------------------------
*/
@ -515,8 +515,8 @@ ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3
if (GinGetNPosting(itup) != newN)
{
bool isnull;
Datum value;
Datum value;
OffsetNumber attnum;
/*
* Some ItemPointers was deleted, so we should remake our
@ -544,8 +544,9 @@ ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3
itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
}
value = index_getattr(itup, FirstOffsetNumber, gvs->ginstate.tupdesc, &isnull);
itup = GinFormTuple(&gvs->ginstate, value, GinGetPosting(itup), newN);
value = gin_index_getattr(&gvs->ginstate, itup);
attnum = gintuple_get_attrnum(&gvs->ginstate, itup);
itup = GinFormTuple(&gvs->ginstate, attnum, value, GinGetPosting(itup), newN);
PageIndexTupleDelete(tmppage, i);
if (PageAddItem(tmppage, (Item) itup, IndexTupleSize(itup), i, false, false) != i)

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginxlog.c,v 1.14 2008/06/12 09:12:29 heikki Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/ginxlog.c,v 1.15 2008/07/11 21:06:29 tgl Exp $
*-------------------------------------------------------------------------
*/
#include "postgres.h"
@ -549,7 +549,7 @@ ginContinueSplit(ginIncompleteSplit *split)
if (split->rootBlkno == GIN_ROOT_BLKNO)
{
prepareEntryScan(&btree, reln, (Datum) 0, NULL);
prepareEntryScan(&btree, reln, InvalidOffsetNumber, (Datum) 0, NULL);
btree.entry = ginPageGetLinkItup(buffer);
}
else

View File

@ -4,7 +4,7 @@
*
* Copyright (c) 2006-2008, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/include/access/gin.h,v 1.22 2008/06/19 00:46:05 alvherre Exp $
* $PostgreSQL: pgsql/src/include/access/gin.h,v 1.23 2008/07/11 21:06:29 tgl Exp $
*--------------------------------------------------------------------------
*/
@ -140,15 +140,18 @@ typedef struct
typedef struct GinState
{
FmgrInfo compareFn;
FmgrInfo extractValueFn;
FmgrInfo extractQueryFn;
FmgrInfo consistentFn;
FmgrInfo comparePartialFn; /* optional method */
FmgrInfo compareFn[INDEX_MAX_KEYS];
FmgrInfo extractValueFn[INDEX_MAX_KEYS];
FmgrInfo extractQueryFn[INDEX_MAX_KEYS];
FmgrInfo consistentFn[INDEX_MAX_KEYS];
FmgrInfo comparePartialFn[INDEX_MAX_KEYS]; /* optional method */
bool canPartialMatch; /* can opclass perform partial
* match (prefix search)? */
TupleDesc tupdesc;
bool canPartialMatch[INDEX_MAX_KEYS]; /* can opclass perform partial
* match (prefix search)? */
TupleDesc tupdesc[INDEX_MAX_KEYS];
TupleDesc origTupdesc;
bool oneCol;
} GinState;
/* XLog stuff */
@ -235,12 +238,16 @@ extern void initGinState(GinState *state, Relation index);
extern Buffer GinNewBuffer(Relation index);
extern void GinInitBuffer(Buffer b, uint32 f);
extern void GinInitPage(Page page, uint32 f, Size pageSize);
extern int compareEntries(GinState *ginstate, Datum a, Datum b);
extern Datum *extractEntriesS(GinState *ginstate, Datum value,
extern int compareEntries(GinState *ginstate, OffsetNumber attnum, Datum a, Datum b);
extern int compareAttEntries(GinState *ginstate, OffsetNumber attnum_a, Datum a,
OffsetNumber attnum_b, Datum b);
extern Datum *extractEntriesS(GinState *ginstate, OffsetNumber attnum, Datum value,
int32 *nentries, bool *needUnique);
extern Datum *extractEntriesSU(GinState *ginstate, Datum value, int32 *nentries);
extern Datum *extractEntriesSU(GinState *ginstate, OffsetNumber attnum, Datum value, int32 *nentries);
extern Page GinPageGetCopyPage(Page page);
extern Datum gin_index_getattr(GinState *ginstate, IndexTuple tuple);
extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
/* gininsert.c */
extern Datum ginbuild(PG_FUNCTION_ARGS);
extern Datum gininsert(PG_FUNCTION_ARGS);
@ -291,6 +298,7 @@ typedef struct GinBtreeData
BlockNumber rightblkno;
/* Entry options */
OffsetNumber entryAttnum;
Datum entryValue;
IndexTuple entry;
bool isDelete;
@ -310,9 +318,10 @@ extern void ginInsertValue(GinBtree btree, GinBtreeStack *stack);
extern void findParents(GinBtree btree, GinBtreeStack *stack, BlockNumber rootBlkno);
/* ginentrypage.c */
extern IndexTuple GinFormTuple(GinState *ginstate, Datum key, ItemPointerData *ipd, uint32 nipd);
extern Datum ginGetHighKey(GinState *ginstate, Page page);
extern void prepareEntryScan(GinBtree btree, Relation index, Datum value, GinState *ginstate);
extern IndexTuple GinFormTuple(GinState *ginstate, OffsetNumber attnum, Datum key,
ItemPointerData *ipd, uint32 nipd);
extern void prepareEntryScan(GinBtree btree, Relation index, OffsetNumber attnum,
Datum value, GinState *ginstate);
extern void entryFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf);
extern IndexTuple ginPageGetLinkItup(Buffer buf);
@ -359,6 +368,7 @@ typedef struct GinScanEntryData
/* entry, got from extractQueryFn */
Datum entry;
OffsetNumber attnum;
/* Current page in posting tree */
Buffer buffer;
@ -396,6 +406,7 @@ typedef struct GinScanKeyData
/* for calling consistentFn(GinScanKey->entryRes, strategy, query) */
StrategyNumber strategy;
Datum query;
OffsetNumber attnum;
ItemPointerData curItem;
bool firstCall;
@ -450,11 +461,12 @@ extern Datum ginarrayconsistent(PG_FUNCTION_ARGS);
/* ginbulk.c */
typedef struct EntryAccumulator
{
Datum value;
uint32 length;
uint32 number;
OffsetNumber attnum;
Datum value;
uint32 length;
uint32 number;
ItemPointerData *list;
bool shouldSort;
bool shouldSort;
struct EntryAccumulator *left;
struct EntryAccumulator *right;
} EntryAccumulator;
@ -474,7 +486,8 @@ typedef struct
extern void ginInitBA(BuildAccumulator *accum);
extern void ginInsertRecordBA(BuildAccumulator *accum,
ItemPointer heapptr, Datum *entries, int32 nentry);
extern ItemPointerData *ginGetEntry(BuildAccumulator *accum, Datum *entry, uint32 *n);
ItemPointer heapptr,
OffsetNumber attnum, Datum *entries, int32 nentry);
extern ItemPointerData *ginGetEntry(BuildAccumulator *accum, OffsetNumber *attnum, Datum *entry, uint32 *n);
#endif

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.465 2008/07/03 20:58:46 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.466 2008/07/11 21:06:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200807031
#define CATALOG_VERSION_NO 200807111
#endif

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.56 2008/05/16 16:31:01 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.57 2008/07/11 21:06:29 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@ -114,7 +114,7 @@ DESCR("hash index access method");
DATA(insert OID = 783 ( gist 0 7 f f t t t t t t gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions ));
DESCR("GiST index access method");
#define GIST_AM_OID 783
DATA(insert OID = 2742 ( gin 0 5 f f f f f f t f gininsert ginbeginscan gingettuple gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions ));
DATA(insert OID = 2742 ( gin 0 5 f f t t f f t f gininsert ginbeginscan gingettuple gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions ));
DESCR("GIN index access method");
#define GIN_AM_OID 2742

View File

@ -170,7 +170,7 @@ RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
--
-- GIN over int[]
-- GIN over int[] and text[]
--
SET enable_seqscan = OFF;
SET enable_indexscan = ON;
@ -416,6 +416,130 @@ SELECT * FROM array_index_op_test WHERE i = '{47,77}' ORDER BY seqno;
95 | {47,77} | {AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA74076,AAAAAAAAAA18107,AAAAA40681,AAAAAAAAAAAAAAA35875,AAAAA60038,AAAAAAA56483}
(1 row)
-- And try it with a multicolumn GIN index
DROP INDEX intarrayidx, textarrayidx;
CREATE INDEX botharrayidx ON array_index_op_test USING gin (i, t);
SET enable_seqscan = OFF;
SET enable_indexscan = ON;
SET enable_bitmapscan = OFF;
SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(6 rows)
SELECT * FROM array_index_op_test WHERE i && '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(6 rows)
SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAA80240}' ORDER BY seqno;
seqno | i | t
-------+--------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------
19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
30 | {26,81,47,91,34} | {AAAAAAAAAAAAAAAAAAA70104,AAAAAAA80240}
64 | {26,19,34,24,81,78} | {A96617,AAAAAAAAAAAAAAAAAAA70104,A68938,AAAAAAAAAAA53908,AAAAAAAAAAAAAAA453,AA17009,AAAAAAA80240}
82 | {34,60,4,79,78,16,86,89,42,50} | {AAAAA40681,AAAAAAAAAAAAAAAAAA12591,AAAAAAA80240,AAAAAAAAAAAAAAAA55798,AAAAAAAAAAAAAAAAAAA70104}
88 | {41,90,77,24,6,24} | {AAAA35194,AAAA35194,AAAAAAA80240,AAAAAAAAAAA46154,AAAAAA58494,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAAA59334,AAAAAAAAAAAAAAAAAAA91804,AA74433}
97 | {54,2,86,65} | {47735,AAAAAAA99836,AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAA29150,AAAAAAA80240,AAAAAAAAAAAAAAAA98414,AAAAAAA56483,AAAAAAAAAAAAAAAA29150,AAAAAAA39692,AA21643}
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(7 rows)
SELECT * FROM array_index_op_test WHERE t && '{AAAAAAA80240}' ORDER BY seqno;
seqno | i | t
-------+--------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------
19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
30 | {26,81,47,91,34} | {AAAAAAAAAAAAAAAAAAA70104,AAAAAAA80240}
64 | {26,19,34,24,81,78} | {A96617,AAAAAAAAAAAAAAAAAAA70104,A68938,AAAAAAAAAAA53908,AAAAAAAAAAAAAAA453,AA17009,AAAAAAA80240}
82 | {34,60,4,79,78,16,86,89,42,50} | {AAAAA40681,AAAAAAAAAAAAAAAAAA12591,AAAAAAA80240,AAAAAAAAAAAAAAAA55798,AAAAAAAAAAAAAAAAAAA70104}
88 | {41,90,77,24,6,24} | {AAAA35194,AAAA35194,AAAAAAA80240,AAAAAAAAAAA46154,AAAAAA58494,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAAA59334,AAAAAAAAAAAAAAAAAAA91804,AA74433}
97 | {54,2,86,65} | {47735,AAAAAAA99836,AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAA29150,AAAAAAA80240,AAAAAAAAAAAAAAAA98414,AAAAAAA56483,AAAAAAAAAAAAAAAA29150,AAAAAAA39692,AA21643}
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(7 rows)
SELECT * FROM array_index_op_test WHERE i @> '{32}' AND t && '{AAAAAAA80240}' ORDER BY seqno;
seqno | i | t
-------+-----------------------------+------------------------------------------------------------------------------
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(1 row)
SELECT * FROM array_index_op_test WHERE i && '{32}' AND t @> '{AAAAAAA80240}' ORDER BY seqno;
seqno | i | t
-------+-----------------------------+------------------------------------------------------------------------------
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(1 row)
SET enable_indexscan = OFF;
SET enable_bitmapscan = ON;
SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(6 rows)
SELECT * FROM array_index_op_test WHERE i && '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(6 rows)
SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAA80240}' ORDER BY seqno;
seqno | i | t
-------+--------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------
19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
30 | {26,81,47,91,34} | {AAAAAAAAAAAAAAAAAAA70104,AAAAAAA80240}
64 | {26,19,34,24,81,78} | {A96617,AAAAAAAAAAAAAAAAAAA70104,A68938,AAAAAAAAAAA53908,AAAAAAAAAAAAAAA453,AA17009,AAAAAAA80240}
82 | {34,60,4,79,78,16,86,89,42,50} | {AAAAA40681,AAAAAAAAAAAAAAAAAA12591,AAAAAAA80240,AAAAAAAAAAAAAAAA55798,AAAAAAAAAAAAAAAAAAA70104}
88 | {41,90,77,24,6,24} | {AAAA35194,AAAA35194,AAAAAAA80240,AAAAAAAAAAA46154,AAAAAA58494,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAAA59334,AAAAAAAAAAAAAAAAAAA91804,AA74433}
97 | {54,2,86,65} | {47735,AAAAAAA99836,AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAA29150,AAAAAAA80240,AAAAAAAAAAAAAAAA98414,AAAAAAA56483,AAAAAAAAAAAAAAAA29150,AAAAAAA39692,AA21643}
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(7 rows)
SELECT * FROM array_index_op_test WHERE t && '{AAAAAAA80240}' ORDER BY seqno;
seqno | i | t
-------+--------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------
19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
30 | {26,81,47,91,34} | {AAAAAAAAAAAAAAAAAAA70104,AAAAAAA80240}
64 | {26,19,34,24,81,78} | {A96617,AAAAAAAAAAAAAAAAAAA70104,A68938,AAAAAAAAAAA53908,AAAAAAAAAAAAAAA453,AA17009,AAAAAAA80240}
82 | {34,60,4,79,78,16,86,89,42,50} | {AAAAA40681,AAAAAAAAAAAAAAAAAA12591,AAAAAAA80240,AAAAAAAAAAAAAAAA55798,AAAAAAAAAAAAAAAAAAA70104}
88 | {41,90,77,24,6,24} | {AAAA35194,AAAA35194,AAAAAAA80240,AAAAAAAAAAA46154,AAAAAA58494,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAAA59334,AAAAAAAAAAAAAAAAAAA91804,AA74433}
97 | {54,2,86,65} | {47735,AAAAAAA99836,AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAA29150,AAAAAAA80240,AAAAAAAAAAAAAAAA98414,AAAAAAA56483,AAAAAAAAAAAAAAAA29150,AAAAAAA39692,AA21643}
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(7 rows)
SELECT * FROM array_index_op_test WHERE i @> '{32}' AND t && '{AAAAAAA80240}' ORDER BY seqno;
seqno | i | t
-------+-----------------------------+------------------------------------------------------------------------------
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(1 row)
SELECT * FROM array_index_op_test WHERE i && '{32}' AND t @> '{AAAAAAA80240}' ORDER BY seqno;
seqno | i | t
-------+-----------------------------+------------------------------------------------------------------------------
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(1 row)
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;

View File

@ -138,7 +138,7 @@ RESET enable_indexscan;
RESET enable_bitmapscan;
--
-- GIN over int[]
-- GIN over int[] and text[]
--
SET enable_seqscan = OFF;
@ -180,6 +180,33 @@ SELECT * FROM array_index_op_test WHERE i && '{32,17}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i <@ '{38,34,32,89}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i = '{47,77}' ORDER BY seqno;
-- And try it with a multicolumn GIN index
DROP INDEX intarrayidx, textarrayidx;
CREATE INDEX botharrayidx ON array_index_op_test USING gin (i, t);
SET enable_seqscan = OFF;
SET enable_indexscan = ON;
SET enable_bitmapscan = OFF;
SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i && '{32}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAA80240}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE t && '{AAAAAAA80240}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i @> '{32}' AND t && '{AAAAAAA80240}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i && '{32}' AND t @> '{AAAAAAA80240}' ORDER BY seqno;
SET enable_indexscan = OFF;
SET enable_bitmapscan = ON;
SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i && '{32}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAA80240}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE t && '{AAAAAAA80240}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i @> '{32}' AND t && '{AAAAAAA80240}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i && '{32}' AND t @> '{AAAAAAA80240}' ORDER BY seqno;
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;