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"> <chapter id="indexes">
<title id="indexes-title">Indexes</title> <title id="indexes-title">Indexes</title>
@ -198,7 +198,7 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable>
after a database crash. after a database crash.
For these reasons, hash index use is presently discouraged. For these reasons, hash index use is presently discouraged.
</para> </para>
</note> </note>
<para> <para>
<indexterm> <indexterm>
@ -250,9 +250,9 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable>
</indexterm> </indexterm>
GIN indexes are inverted indexes which can handle values that contain more GIN indexes are inverted indexes which can handle values that contain more
than one key, arrays for example. Like GiST, GIN can support than one key, arrays for example. Like GiST, GIN can support
many different user-defined indexing strategies and the particular many different user-defined indexing strategies and the particular
operators with which a GIN index can be used vary depending on the operators with which a GIN index can be used vary depending on the
indexing strategy. indexing strategy.
As an example, the standard distribution of As an example, the standard distribution of
<productname>PostgreSQL</productname> includes GIN operator classes <productname>PostgreSQL</productname> includes GIN operator classes
for one-dimensional arrays, which support indexed for one-dimensional arrays, which support indexed
@ -306,7 +306,7 @@ CREATE INDEX test2_mm_idx ON test2 (major, minor);
</para> </para>
<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 indexes. Up to 32 columns can be specified. (This limit can be
altered when building <productname>PostgreSQL</productname>; see the altered when building <productname>PostgreSQL</productname>; see the
file <filename>pg_config_manual.h</filename>.) file <filename>pg_config_manual.h</filename>.)
@ -336,14 +336,21 @@ CREATE INDEX test2_mm_idx ON test2 (major, minor);
<para> <para>
A multicolumn GiST index can be used with query conditions that A multicolumn GiST index can be used with query conditions that
involve any subset of the index's columns. Conditions on additional involve any subset of the index's columns. Conditions on additional
columns restrict the entries returned by the index, but the condition on 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 first column is the most important one for determining how much of
the index needs to be scanned. A GiST index will be relatively 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 ineffective if its first column has only a few distinct values, even if
there are many distinct values in additional columns. there are many distinct values in additional columns.
</para> </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> <para>
Of course, each column must be used with operators appropriate to the index Of course, each column must be used with operators appropriate to the index
type; clauses that involve other operators will not be considered. 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> <para>
<productname>PostgreSQL</productname> automatically creates a unique <productname>PostgreSQL</productname> automatically creates a unique
index when a unique constraint or a primary key is defined for a table. 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 columns (a multicolumn index, if appropriate), and is the mechanism
that enforces the constraint. that enforces the constraint.
</para> </para>
@ -798,9 +805,9 @@ SELECT * FROM orders WHERE order_nr = 3501;
or the index will not be recognized to be usable. Matching takes or the index will not be recognized to be usable. Matching takes
place at query planning time, not at run time. As a result, place at query planning time, not at run time. As a result,
parameterized query clauses will not work with a partial index. For parameterized query clauses will not work with a partial index. For
example a prepared query with a parameter might specify example a prepared query with a parameter might specify
<quote>x &lt; ?</quote> which will never imply <quote>x &lt; ?</quote> which will never imply
<quote>x &lt; 2</quote> for all possible values of the parameter. <quote>x &lt; 2</quote> for all possible values of the parameter.
</para> </para>
<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 PostgreSQL documentation
--> -->
@ -394,7 +394,7 @@ Indexes:
</para> </para>
<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. multicolumn indexes. Up to 32 fields can be specified by default.
(This limit can be altered when building (This limit can be altered when building
<productname>PostgreSQL</productname>.) Only B-tree currently <productname>PostgreSQL</productname>.) Only B-tree currently
@ -423,7 +423,7 @@ Indexes:
the optional clauses <literal>ASC</>, <literal>DESC</>, <literal>NULLS the optional clauses <literal>ASC</>, <literal>DESC</>, <literal>NULLS
FIRST</>, and/or <literal>NULLS LAST</> can be specified to reverse FIRST</>, and/or <literal>NULLS LAST</> can be specified to reverse
the normal sort direction of the index. Since an ordered index can be 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 single-column <literal>DESC</> index &mdash; that sort ordering is already
available with a regular index. The value of these options is that available with a regular index. The value of these options is that
multicolumn indexes can be created that match the sort ordering requested 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 * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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. * palloc'd space in accum.
*/ */
static Datum 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; Datum res;
if (att[0]->attbyval) if (att->attbyval)
res = value; res = value;
else else
{ {
res = datumCopy(value, false, att[0]->attlen); res = datumCopy(value, false, att->attlen);
accum->allocatedMemory += GetMemoryChunkSpace(DatumGetPointer(res)); accum->allocatedMemory += GetMemoryChunkSpace(DatumGetPointer(res));
} }
return res; return res;
@ -101,7 +101,7 @@ getDatumCopy(BuildAccumulator *accum, Datum value)
* Find/store one entry from indexed value. * Find/store one entry from indexed value.
*/ */
static void static void
ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, Datum entry) ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum, Datum entry)
{ {
EntryAccumulator *ea = accum->entries, EntryAccumulator *ea = accum->entries,
*pea = NULL; *pea = NULL;
@ -110,7 +110,7 @@ ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, Datum entry)
while (ea) while (ea)
{ {
res = compareEntries(accum->ginstate, entry, ea->value); res = compareAttEntries(accum->ginstate, attnum, entry, ea->attnum, ea->value);
if (res == 0) if (res == 0)
break; /* found */ break; /* found */
else else
@ -132,7 +132,8 @@ ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, Datum entry)
ea = EAAllocate(accum); ea = EAAllocate(accum);
ea->left = ea->right = NULL; ea->left = ea->right = NULL;
ea->value = getDatumCopy(accum, entry); ea->attnum = attnum;
ea->value = getDatumCopy(accum, attnum, entry);
ea->length = DEF_NPTR; ea->length = DEF_NPTR;
ea->number = 1; ea->number = 1;
ea->shouldSort = FALSE; ea->shouldSort = FALSE;
@ -160,7 +161,8 @@ ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, Datum entry)
* then calls itself for each parts * then calls itself for each parts
*/ */
static void 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 low, uint32 high, uint32 offset)
{ {
uint32 pos; uint32 pos;
@ -168,15 +170,15 @@ ginChooseElem(BuildAccumulator *accum, ItemPointer heapptr, Datum *entries, uint
pos = (low + middle) >> 1; pos = (low + middle) >> 1;
if (low != middle && pos >= offset && pos - offset < nentry) 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; pos = (high + middle + 1) >> 1;
if (middle + 1 != high && pos >= offset && pos - offset < nentry) 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) if (low != middle)
ginChooseElem(accum, heapptr, entries, nentry, low, middle, offset); ginChooseElem(accum, heapptr, attnum, entries, nentry, low, middle, offset);
if (high != middle + 1) 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. * next middle on left part and middle of right part.
*/ */
void void
ginInsertRecordBA(BuildAccumulator *accum, ItemPointer heapptr, Datum *entries, int32 nentry) ginInsertRecordBA(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum,
Datum *entries, int32 nentry)
{ {
uint32 i, uint32 i,
nbit = 0, nbit = 0,
@ -201,8 +204,8 @@ ginInsertRecordBA(BuildAccumulator *accum, ItemPointer heapptr, Datum *entries,
nbit = 1 << nbit; nbit = 1 << nbit;
offset = (nbit - nentry) / 2; offset = (nbit - nentry) / 2;
ginInsertEntry(accum, heapptr, entries[(nbit >> 1) - offset]); ginInsertEntry(accum, heapptr, attnum, entries[(nbit >> 1) - offset]);
ginChooseElem(accum, heapptr, entries, nentry, 0, nbit, offset); ginChooseElem(accum, heapptr, attnum, entries, nentry, 0, nbit, offset);
} }
static int static int
@ -259,7 +262,7 @@ walkTree(BuildAccumulator *accum)
} }
ItemPointerData * ItemPointerData *
ginGetEntry(BuildAccumulator *accum, Datum *value, uint32 *n) ginGetEntry(BuildAccumulator *accum, OffsetNumber *attnum, Datum *value, uint32 *n)
{ {
EntryAccumulator *entry; EntryAccumulator *entry;
ItemPointerData *list; ItemPointerData *list;
@ -299,6 +302,7 @@ ginGetEntry(BuildAccumulator *accum, Datum *value, uint32 *n)
return NULL; return NULL;
*n = entry->number; *n = entry->number;
*attnum = entry->attnum;
*value = entry->value; *value = entry->value;
list = entry->list; list = entry->list;

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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 * - ItemPointerGetBlockNumber(&itup->t_tid) contains block number of
* root of posting tree * root of posting tree
* - ItemPointerGetOffsetNumber(&itup->t_tid) contains magic number GIN_TREE_POSTING * - 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 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; 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)); GinSetOrigSizePosting(itup, IndexTupleSize(itup));
@ -88,28 +101,20 @@ getRightMostTuple(Page page)
return (IndexTuple) PageGetItem(page, PageGetItemId(page, maxoff)); 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 static bool
entryIsMoveRight(GinBtree btree, Page page) entryIsMoveRight(GinBtree btree, Page page)
{ {
Datum highkey; IndexTuple itup;
if (GinPageRightMost(page)) if (GinPageRightMost(page))
return FALSE; 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 TRUE;
return FALSE; return FALSE;
@ -154,11 +159,11 @@ entryLocateEntry(GinBtree btree, GinBtreeStack *stack)
result = -1; result = -1;
else else
{ {
bool isnull;
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid)); itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
result = compareEntries(btree->ginstate, btree->entryValue, result = compareAttEntries(btree->ginstate,
index_getattr(itup, FirstOffsetNumber, btree->ginstate->tupdesc, &isnull)); btree->entryAttnum, btree->entryValue,
gintuple_get_attrnum(btree->ginstate, itup),
gin_index_getattr(btree->ginstate, itup));
} }
if (result == 0) if (result == 0)
@ -217,13 +222,13 @@ entryLocateLeafEntry(GinBtree btree, GinBtreeStack *stack)
while (high > low) while (high > low)
{ {
OffsetNumber mid = low + ((high - low) / 2); OffsetNumber mid = low + ((high - low) / 2);
bool isnull;
int result; int result;
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid)); itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
result = compareEntries(btree->ginstate, btree->entryValue, result = compareAttEntries(btree->ginstate,
index_getattr(itup, FirstOffsetNumber, btree->ginstate->tupdesc, &isnull)); btree->entryAttnum, btree->entryValue,
gintuple_get_attrnum(btree->ginstate, itup),
gin_index_getattr(btree->ginstate, itup));
if (result == 0) if (result == 0)
{ {
stack->off = mid; stack->off = mid;
@ -587,7 +592,7 @@ entryFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf)
} }
void 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)); memset(btree, 0, sizeof(GinBtreeData));
@ -603,6 +608,7 @@ prepareEntryScan(GinBtree btree, Relation index, Datum value, GinState *ginstate
btree->index = index; btree->index = index;
btree->ginstate = ginstate; btree->ginstate = ginstate;
btree->entryAttnum = attnum;
btree->entryValue = value; btree->entryValue = value;
btree->isDelete = FALSE; btree->isDelete = FALSE;

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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; Page page;
IndexTuple itup; IndexTuple itup;
Datum idatum; Datum idatum;
bool isnull;
int32 cmp; int32 cmp;
scanEntry->partialMatch = tbm_create( work_mem * 1024L ); scanEntry->partialMatch = tbm_create( work_mem * 1024L );
@ -153,8 +152,15 @@ computePartialMatchList( GinBtreeData *btree, GinBtreeStack *stack, GinScanEntry
page = BufferGetPage(stack->buffer); page = BufferGetPage(stack->buffer);
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stack->off)); 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. * Check of partial match.
@ -163,7 +169,7 @@ computePartialMatchList( GinBtreeData *btree, GinBtreeStack *stack, GinScanEntry
* case cmp < 0 => not match and continue scan * 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, scanEntry->entry,
idatum, idatum,
UInt16GetDatum(scanEntry->strategy))); UInt16GetDatum(scanEntry->strategy)));
@ -182,8 +188,8 @@ computePartialMatchList( GinBtreeData *btree, GinBtreeStack *stack, GinScanEntry
Datum newDatum, Datum newDatum,
savedDatum = datumCopy ( savedDatum = datumCopy (
idatum, idatum,
btree->ginstate->tupdesc->attrs[0]->attbyval, btree->ginstate->origTupdesc->attrs[scanEntry->attnum-1]->attbyval,
btree->ginstate->tupdesc->attrs[0]->attlen btree->ginstate->origTupdesc->attrs[scanEntry->attnum-1]->attlen
); );
/* /*
* We should unlock current page (but not unpin) during * We should unlock current page (but not unpin) during
@ -220,12 +226,15 @@ computePartialMatchList( GinBtreeData *btree, GinBtreeStack *stack, GinScanEntry
page = BufferGetPage(stack->buffer); page = BufferGetPage(stack->buffer);
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stack->off)); 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! */ /* Found! */
if ( btree->ginstate->tupdesc->attrs[0]->attbyval == false ) if ( btree->ginstate->origTupdesc->attrs[scanEntry->attnum-1]->attbyval == false )
pfree( DatumGetPointer(savedDatum) ); pfree( DatumGetPointer(savedDatum) );
break; break;
} }
@ -270,7 +279,7 @@ startScanEntry(Relation index, GinState *ginstate, GinScanEntry entry)
* or just store posting list in memory * or just store posting list in memory
*/ */
prepareEntryScan(&btreeEntry, index, entry->entry, ginstate); prepareEntryScan(&btreeEntry, index, entry->attnum, entry->entry, ginstate);
btreeEntry.searchMode = TRUE; btreeEntry.searchMode = TRUE;
stackEntry = ginFindLeafPage(&btreeEntry, NULL); stackEntry = ginFindLeafPage(&btreeEntry, NULL);
page = BufferGetPage(stackEntry->buffer); page = BufferGetPage(stackEntry->buffer);
@ -705,7 +714,7 @@ keyGetItem(Relation index, GinState *ginstate, MemoryContext tempCtx,
*keyrecheck = true; *keyrecheck = true;
oldCtx = MemoryContextSwitchTo(tempCtx); oldCtx = MemoryContextSwitchTo(tempCtx);
res = DatumGetBool(FunctionCall4(&ginstate->consistentFn, res = DatumGetBool(FunctionCall4(&ginstate->consistentFn[key->attnum-1],
PointerGetDatum(key->entryRes), PointerGetDatum(key->entryRes),
UInt16GetDatum(key->strategy), UInt16GetDatum(key->strategy),
key->query, key->query,

View File

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

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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 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, Datum *entryValues, bool *partial_matches, uint32 nEntryValues,
StrategyNumber strategy) StrategyNumber strategy)
{ {
@ -47,6 +47,7 @@ fillScanKey(GinState *ginstate, GinScanKey key, Datum query,
key->entryRes = (bool *) palloc0(sizeof(bool) * nEntryValues); key->entryRes = (bool *) palloc0(sizeof(bool) * nEntryValues);
key->scanEntry = (GinScanEntry) palloc(sizeof(GinScanEntryData) * nEntryValues); key->scanEntry = (GinScanEntry) palloc(sizeof(GinScanEntryData) * nEntryValues);
key->strategy = strategy; key->strategy = strategy;
key->attnum = attnum;
key->query = query; key->query = query;
key->firstCall = TRUE; key->firstCall = TRUE;
ItemPointerSet(&(key->curItem), InvalidBlockNumber, InvalidOffsetNumber); 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].pval = key->entryRes + i;
key->scanEntry[i].entry = entryValues[i]; key->scanEntry[i].entry = entryValues[i];
key->scanEntry[i].attnum = attnum;
ItemPointerSet(&(key->scanEntry[i].curItem), InvalidBlockNumber, InvalidOffsetNumber); ItemPointerSet(&(key->scanEntry[i].curItem), InvalidBlockNumber, InvalidOffsetNumber);
key->scanEntry[i].offset = InvalidOffsetNumber; key->scanEntry[i].offset = InvalidOffsetNumber;
key->scanEntry[i].buffer = InvalidBuffer; key->scanEntry[i].buffer = InvalidBuffer;
key->scanEntry[i].partialMatch = NULL; key->scanEntry[i].partialMatch = NULL;
key->scanEntry[i].list = NULL; key->scanEntry[i].list = NULL;
key->scanEntry[i].nlist = 0; 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; ? partial_matches[i] : false;
/* link to the equals entry in current scan key */ /* link to the equals entry in current scan key */
key->scanEntry[i].master = NULL; key->scanEntry[i].master = NULL;
for (j = 0; j < i; j++) 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; key->scanEntry[i].master = key->scanEntry + j;
break; break;
@ -164,19 +166,17 @@ newScanKey(IndexScanDesc scan)
int32 nEntryValues; int32 nEntryValues;
bool *partial_matches = NULL; bool *partial_matches = NULL;
Assert(scankey[i].sk_attno == 1);
/* XXX can't we treat nulls by just setting isVoidRes? */ /* XXX can't we treat nulls by just setting isVoidRes? */
/* This would amount to assuming that all GIN operators are strict */ /* This would amount to assuming that all GIN operators are strict */
if (scankey[i].sk_flags & SK_ISNULL) if (scankey[i].sk_flags & SK_ISNULL)
elog(ERROR, "GIN doesn't support NULL as scan key"); elog(ERROR, "GIN doesn't support NULL as scan key");
entryValues = (Datum *) DatumGetPointer(FunctionCall4( entryValues = (Datum *) DatumGetPointer(FunctionCall4(
&so->ginstate.extractQueryFn, &so->ginstate.extractQueryFn[scankey[i].sk_attno - 1],
scankey[i].sk_argument, scankey[i].sk_argument,
PointerGetDatum(&nEntryValues), PointerGetDatum(&nEntryValues),
UInt16GetDatum(scankey[i].sk_strategy), UInt16GetDatum(scankey[i].sk_strategy),
PointerGetDatum(&partial_matches))); PointerGetDatum(&partial_matches)));
if (nEntryValues < 0) if (nEntryValues < 0)
{ {
/* /*
@ -194,7 +194,7 @@ newScanKey(IndexScanDesc scan)
/* full scan... */ /* full scan... */
continue; 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); entryValues, partial_matches, nEntryValues, scankey[i].sk_strategy);
nkeys++; nkeys++;
} }

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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/genam.h"
#include "access/gin.h" #include "access/gin.h"
#include "access/reloptions.h" #include "access/reloptions.h"
#include "catalog/pg_type.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/freespace.h" #include "storage/freespace.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
@ -23,40 +24,116 @@
void void
initGinState(GinState *state, Relation index) initGinState(GinState *state, Relation index)
{ {
if (index->rd_att->natts != 1) int i;
elog(ERROR, "numberOfAttributes %d != 1",
index->rd_att->natts);
state->tupdesc = index->rd_att; state->origTupdesc = index->rd_att;
fmgr_info_copy(&(state->compareFn), state->oneCol = (index->rd_att->natts == 1) ? true : false;
index_getprocinfo(index, 1, GIN_COMPARE_PROC),
CurrentMemoryContext); for(i=0;i<index->rd_att->natts;i++)
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 )
{ {
fmgr_info_copy(&(state->comparePartialFn), state->tupdesc[i] = CreateTemplateTupleDesc(2,false);
index_getprocinfo(index, 1, GIN_COMPARE_PARTIAL_PROC),
CurrentMemoryContext);
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 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 int
compareEntries(GinState *ginstate, Datum a, Datum b) compareEntries(GinState *ginstate, OffsetNumber attnum, Datum a, Datum b)
{ {
return DatumGetInt32( return DatumGetInt32(
FunctionCall2( FunctionCall2(
&ginstate->compareFn, &ginstate->compareFn[attnum-1],
a, b 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 typedef struct
{ {
FmgrInfo *cmpDatumFunc; FmgrInfo *cmpDatumFunc;
@ -165,13 +252,13 @@ cmpEntries(const Datum *a, const Datum *b, cmpEntriesData *arg)
} }
Datum * Datum *
extractEntriesS(GinState *ginstate, Datum value, int32 *nentries, extractEntriesS(GinState *ginstate, OffsetNumber attnum, Datum value, int32 *nentries,
bool *needUnique) bool *needUnique)
{ {
Datum *entries; Datum *entries;
entries = (Datum *) DatumGetPointer(FunctionCall2( entries = (Datum *) DatumGetPointer(FunctionCall2(
&ginstate->extractValueFn, &ginstate->extractValueFn[attnum-1],
value, value,
PointerGetDatum(nentries) PointerGetDatum(nentries)
)); ));
@ -184,7 +271,7 @@ extractEntriesS(GinState *ginstate, Datum value, int32 *nentries,
{ {
cmpEntriesData arg; cmpEntriesData arg;
arg.cmpDatumFunc = &ginstate->compareFn; arg.cmpDatumFunc = &ginstate->compareFn[attnum-1];
arg.needUnique = needUnique; arg.needUnique = needUnique;
qsort_arg(entries, *nentries, sizeof(Datum), qsort_arg(entries, *nentries, sizeof(Datum),
(qsort_arg_comparator) cmpEntries, (void *) &arg); (qsort_arg_comparator) cmpEntries, (void *) &arg);
@ -195,10 +282,10 @@ extractEntriesS(GinState *ginstate, Datum value, int32 *nentries,
Datum * Datum *
extractEntriesSU(GinState *ginstate, Datum value, int32 *nentries) extractEntriesSU(GinState *ginstate, OffsetNumber attnum, Datum value, int32 *nentries)
{ {
bool needUnique; bool needUnique;
Datum *entries = extractEntriesS(ginstate, value, nentries, Datum *entries = extractEntriesS(ginstate, attnum, value, nentries,
&needUnique); &needUnique);
if (needUnique) if (needUnique)
@ -210,7 +297,7 @@ extractEntriesSU(GinState *ginstate, Datum value, int32 *nentries)
while (ptr - entries < *nentries) while (ptr - entries < *nentries)
{ {
if (compareEntries(ginstate, *ptr, *res) != 0) if (compareEntries(ginstate, attnum, *ptr, *res) != 0)
*(++res) = *ptr++; *(++res) = *ptr++;
else else
ptr++; ptr++;

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) if (GinGetNPosting(itup) != newN)
{ {
bool isnull; Datum value;
Datum value; OffsetNumber attnum;
/* /*
* Some ItemPointers was deleted, so we should remake our * 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)); itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
} }
value = index_getattr(itup, FirstOffsetNumber, gvs->ginstate.tupdesc, &isnull); value = gin_index_getattr(&gvs->ginstate, itup);
itup = GinFormTuple(&gvs->ginstate, value, GinGetPosting(itup), newN); attnum = gintuple_get_attrnum(&gvs->ginstate, itup);
itup = GinFormTuple(&gvs->ginstate, attnum, value, GinGetPosting(itup), newN);
PageIndexTupleDelete(tmppage, i); PageIndexTupleDelete(tmppage, i);
if (PageAddItem(tmppage, (Item) itup, IndexTupleSize(itup), i, false, false) != 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 * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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" #include "postgres.h"
@ -549,7 +549,7 @@ ginContinueSplit(ginIncompleteSplit *split)
if (split->rootBlkno == GIN_ROOT_BLKNO) if (split->rootBlkno == GIN_ROOT_BLKNO)
{ {
prepareEntryScan(&btree, reln, (Datum) 0, NULL); prepareEntryScan(&btree, reln, InvalidOffsetNumber, (Datum) 0, NULL);
btree.entry = ginPageGetLinkItup(buffer); btree.entry = ginPageGetLinkItup(buffer);
} }
else else

View File

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

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200807031 #define CATALOG_VERSION_NO 200807111
#endif #endif

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 * NOTES
* the genbki.sh script reads this file and generates .bki * 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 )); 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"); DESCR("GiST index access method");
#define GIST_AM_OID 783 #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"); DESCR("GIN index access method");
#define GIN_AM_OID 2742 #define GIN_AM_OID 2742

View File

@ -170,7 +170,7 @@ RESET enable_seqscan;
RESET enable_indexscan; RESET enable_indexscan;
RESET enable_bitmapscan; RESET enable_bitmapscan;
-- --
-- GIN over int[] -- GIN over int[] and text[]
-- --
SET enable_seqscan = OFF; SET enable_seqscan = OFF;
SET enable_indexscan = ON; 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} 95 | {47,77} | {AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA74076,AAAAAAAAAA18107,AAAAA40681,AAAAAAAAAAAAAAA35875,AAAAA60038,AAAAAAA56483}
(1 row) (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_seqscan;
RESET enable_indexscan; RESET enable_indexscan;
RESET enable_bitmapscan; RESET enable_bitmapscan;

View File

@ -138,7 +138,7 @@ RESET enable_indexscan;
RESET enable_bitmapscan; RESET enable_bitmapscan;
-- --
-- GIN over int[] -- GIN over int[] and text[]
-- --
SET enable_seqscan = OFF; 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 <@ '{38,34,32,89}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i = '{47,77}' 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_seqscan;
RESET enable_indexscan; RESET enable_indexscan;
RESET enable_bitmapscan; RESET enable_bitmapscan;