Multi-column GIN indexes. Teodor Sigaev
This commit is contained in:
parent
2d6599f471
commit
27cb66fdfe
|
@ -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 < ?</quote> which will never imply
|
<quote>x < ?</quote> which will never imply
|
||||||
<quote>x < 2</quote> for all possible values of the parameter.
|
<quote>x < 2</quote> for all possible values of the parameter.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
|
|
@ -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 — that sort ordering is already
|
single-column <literal>DESC</> index — 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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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++;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue