Commit ff301d6e authored by Tom Lane's avatar Tom Lane

Implement "fastupdate" support for GIN indexes, in which we try to accumulate

multiple index entries in a holding area before adding them to the main index
structure.  This helps because bulk insert is (usually) significantly faster
than retail insert for GIN.

This patch also removes GIN support for amgettuple-style index scans.  The
API defined for amgettuple is difficult to support with fastupdate, and
the previously committed partial-match feature didn't really work with
it either.  We might eventually figure a way to put back amgettuple
support, but it won't happen for 8.4.

catversion bumped because of change in GIN's pg_am entry, and because
the format of GIN indexes changed on-disk (there's a metapage now,
and possibly a pending list).

Teodor Sigaev
parent 9987f660
<!-- $PostgreSQL: pgsql/doc/src/sgml/gin.sgml,v 2.16 2008/07/22 22:05:24 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/gin.sgml,v 2.17 2009/03/24 20:17:07 tgl Exp $ -->
<chapter id="GIN"> <chapter id="GIN">
<title>GIN Indexes</title> <title>GIN Indexes</title>
...@@ -188,6 +188,44 @@ ...@@ -188,6 +188,44 @@
list of heap pointers (PL, posting list) if the list is small enough. list of heap pointers (PL, posting list) if the list is small enough.
</para> </para>
<sect2 id="gin-fast-update">
<title>GIN fast update technique</title>
<para>
Updating a <acronym>GIN</acronym> index tends to be slow because of the
intrinsic nature of inverted indexes: inserting or updating one heap row
can cause many inserts into the index (one for each key extracted
from the indexed value). As of <productname>PostgreSQL</productname> 8.4,
<acronym>GIN</> is capable of postponing much of this work by inserting
new tuples into a temporary, unsorted list of pending entries.
When the table is vacuumed, or if the pending list becomes too large
(larger than <xref linkend="guc-work-mem">), the entries are moved to the
main <acronym>GIN</acronym> data structure using the same bulk insert
techniques used during initial index creation. This greatly improves
<acronym>GIN</acronym> index update speed, even counting the additional
vacuum overhead. Moreover the overhead can be done by a background
process instead of in foreground query processing.
</para>
<para>
The main disadvantage of this approach is that searches must scan the list
of pending entries in addition to searching the regular index, and so
a large list of pending entries will slow searches significantly.
Another disadvantage is that, while most updates are fast, an update
that causes the pending list to become <quote>too large</> will incur an
immediate cleanup cycle and thus be much slower than other updates.
Proper use of autovacuum can minimize both of these problems.
</para>
<para>
If consistent response time is more important than update speed,
use of pending entries can be disabled by turning off the
<literal>FASTUPDATE</literal> storage parameter for a
<acronym>GIN</acronym> index. See <xref linkend="sql-createindex"
endterm="sql-createindex-title"> for details.
</para>
</sect2>
<sect2 id="gin-partial-match"> <sect2 id="gin-partial-match">
<title>Partial match algorithm</title> <title>Partial match algorithm</title>
...@@ -205,14 +243,6 @@ ...@@ -205,14 +243,6 @@
to be searched, or greater than zero if the index key is past the range to be searched, or greater than zero if the index key is past the range
that could match. that could match.
</para> </para>
<para>
During a partial-match scan, all <literal>itemPointer</>s for matching keys
are OR'ed into a <literal>TIDBitmap</>.
The scan fails if the <literal>TIDBitmap</> becomes lossy.
In this case an error message will be reported with advice
to increase <literal>work_mem</>.
</para>
</sect2> </sect2>
</sect1> </sect1>
...@@ -225,11 +255,18 @@ ...@@ -225,11 +255,18 @@
<term>Create vs insert</term> <term>Create vs insert</term>
<listitem> <listitem>
<para> <para>
In most cases, insertion into a <acronym>GIN</acronym> index is slow Insertion into a <acronym>GIN</acronym> index can be slow
due to the likelihood of many keys being inserted for each value. due to the likelihood of many keys being inserted for each value.
So, for bulk insertions into a table it is advisable to drop the GIN So, for bulk insertions into a table it is advisable to drop the GIN
index and recreate it after finishing bulk insertion. index and recreate it after finishing bulk insertion.
</para> </para>
<para>
As of <productname>PostgreSQL</productname> 8.4, this advice is less
necessary since delayed indexing is used (see <xref
linkend="gin-fast-update"> for details). But for very large updates
it may still be best to drop and recreate the index.
</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -244,6 +281,23 @@ ...@@ -244,6 +281,23 @@
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><xref linkend="guc-work-mem"></term>
<listitem>
<para>
During a series of insertions into an existing <acronym>GIN</acronym>
index that has <literal>FASTUPDATE</> enabled, the system will clean up
the pending-entry list whenever it grows larger than
<varname>work_mem</>. To avoid fluctuations in observed response time,
it's desirable to have pending-list cleanup occur in the background
(i.e., via autovacuum). Foreground cleanup operations can be avoided by
increasing <varname>work_mem</> or making autovacuum more aggressive.
However, enlarging <varname>work_mem</> means that if a foreground
cleanup does occur, it will take even longer.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><xref linkend="guc-gin-fuzzy-search-limit"></term> <term><xref linkend="guc-gin-fuzzy-search-limit"></term>
<listitem> <listitem>
...@@ -311,8 +365,7 @@ ...@@ -311,8 +365,7 @@
<function>extractQuery</function> must convert an unrestricted search into <function>extractQuery</function> must convert an unrestricted search into
a partial-match query that will scan the whole index. This is inefficient a partial-match query that will scan the whole index. This is inefficient
but might be necessary to avoid corner-case failures with operators such but might be necessary to avoid corner-case failures with operators such
as LIKE. Note however that failure could still occur if the intermediate as <literal>LIKE</>.
<literal>TIDBitmap</> becomes lossy.
</para> </para>
</sect1> </sect1>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.29 2009/03/05 23:06:45 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.30 2009/03/24 20:17:08 tgl Exp $ -->
<chapter id="indexam"> <chapter id="indexam">
<title>Index Access Method Interface Definition</title> <title>Index Access Method Interface Definition</title>
...@@ -239,6 +239,16 @@ amvacuumcleanup (IndexVacuumInfo *info, ...@@ -239,6 +239,16 @@ amvacuumcleanup (IndexVacuumInfo *info,
be returned. be returned.
</para> </para>
<para>
As of <productname>PostgreSQL</productname> 8.4,
<function>amvacuumcleanup</> will also be called at completion of an
<command>ANALYZE</> operation. In this case <literal>stats</> is always
NULL and any return value will be ignored. This case can be distinguished
by checking <literal>info-&gt;analyze_only</literal>. It is recommended
that the access method do nothing except post-insert cleanup in such a
call, and that only in an autovacuum worker process.
</para>
<para> <para>
<programlisting> <programlisting>
void void
...@@ -344,7 +354,8 @@ amgetbitmap (IndexScanDesc scan, ...@@ -344,7 +354,8 @@ amgetbitmap (IndexScanDesc scan,
</programlisting> </programlisting>
Fetch all tuples in the given scan and add them to the caller-supplied Fetch all tuples in the given scan and add them to the caller-supplied
TIDBitmap (that is, OR the set of tuple IDs into whatever set is already TIDBitmap (that is, OR the set of tuple IDs into whatever set is already
in the bitmap). The number of tuples fetched is returned. in the bitmap). The number of tuples fetched is returned (this might be
just an approximate count, for instance some AMs do not detect duplicates).
While inserting tuple IDs into the bitmap, <function>amgetbitmap</> can While inserting tuple IDs into the bitmap, <function>amgetbitmap</> can
indicate that rechecking of the scan conditions is required for specific indicate that rechecking of the scan conditions is required for specific
tuple IDs. This is analogous to the <literal>xs_recheck</> output parameter tuple IDs. This is analogous to the <literal>xs_recheck</> output parameter
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/create_index.sgml,v 1.70 2009/02/02 19:31:38 alvherre Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/create_index.sgml,v 1.71 2009/03/24 20:17:08 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -294,6 +294,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] <replaceable class="parameter">name</re ...@@ -294,6 +294,37 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] <replaceable class="parameter">name</re
</variablelist> </variablelist>
<para>
<literal>GIN</literal> indexes accept a different parameter:
</para>
<variablelist>
<varlistentry>
<term><literal>FASTUPDATE</></term>
<listitem>
<para>
This setting controls usage of the fast update technique described in
<xref linkend="gin-fast-update">. It is a Boolean parameter:
<literal>ON</> enables fast update, <literal>OFF</> disables it.
(Alternative spellings of <literal>ON</> and <literal>OFF</> are
allowed as described in <xref linkend="config-setting">.) The
default is <literal>ON</>.
</para>
<note>
<para>
Turning <literal>FASTUPDATE</> off via <command>ALTER INDEX</> prevents
future insertions from going into the list of pending index entries,
but does not in itself flush previous entries. You might want to
<command>VACUUM</> the table afterward to ensure the pending list is
emptied.
</para>
</note>
</listitem>
</varlistentry>
</variablelist>
</refsect2> </refsect2>
<refsect2 id="SQL-CREATEINDEX-CONCURRENTLY"> <refsect2 id="SQL-CREATEINDEX-CONCURRENTLY">
...@@ -501,6 +532,13 @@ CREATE UNIQUE INDEX title_idx ON films (title) WITH (fillfactor = 70); ...@@ -501,6 +532,13 @@ CREATE UNIQUE INDEX title_idx ON films (title) WITH (fillfactor = 70);
</programlisting> </programlisting>
</para> </para>
<para>
To create a <acronym>GIN</> index with fast updates disabled:
<programlisting>
CREATE INDEX gin_idx ON documents_table (locations) WITH (fastupdate = off);
</programlisting>
</para>
<para> <para>
To create an index on the column <literal>code</> in the table To create an index on the column <literal>code</> in the table
<literal>films</> and have the index reside in the tablespace <literal>films</> and have the index reside in the tablespace
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/vacuum.sgml,v 1.54 2008/12/11 18:16:18 tgl Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/vacuum.sgml,v 1.55 2009/03/24 20:17:08 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -160,6 +160,13 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER"> ...@@ -160,6 +160,13 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">
<command>VACUUM</> cannot be executed inside a transaction block. <command>VACUUM</> cannot be executed inside a transaction block.
</para> </para>
<para>
For tables with <acronym>GIN</> indexes, <command>VACUUM</command> (in
any form) also completes any pending index insertions, by moving pending
index entries to the appropriate places in the main <acronym>GIN</> index
structure. See <xref linkend="gin-fast-update"> for details.
</para>
<para> <para>
We recommend that active production databases be We recommend that active production databases be
vacuumed frequently (at least nightly), in order to vacuumed frequently (at least nightly), in order to
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/textsearch.sgml,v 1.47 2009/01/07 22:40:49 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/textsearch.sgml,v 1.48 2009/03/24 20:17:08 tgl Exp $ -->
<chapter id="textsearch"> <chapter id="textsearch">
<title id="textsearch-title">Full Text Search</title> <title id="textsearch-title">Full Text Search</title>
...@@ -3224,12 +3224,14 @@ SELECT plainto_tsquery('supernovae stars'); ...@@ -3224,12 +3224,14 @@ SELECT plainto_tsquery('supernovae stars');
</listitem> </listitem>
<listitem> <listitem>
<para> <para>
GIN indexes are about ten times slower to update than GiST GIN indexes are moderately slower to update than GiST indexes, but
about 10 times slower if fast-update support was disabled
(see <xref linkend="gin-fast-update"> for details)
</para> </para>
</listitem> </listitem>
<listitem> <listitem>
<para> <para>
GIN indexes are two-to-three times larger than GiST GIN indexes are two-to-three times larger than GiST indexes
</para> </para>
</listitem> </listitem>
</itemizedlist> </itemizedlist>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.23 2009/03/23 16:36:27 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.24 2009/03/24 20:17:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -56,6 +56,14 @@ static relopt_bool boolRelOpts[] = ...@@ -56,6 +56,14 @@ static relopt_bool boolRelOpts[] =
}, },
true true
}, },
{
{
"fastupdate",
"Enables \"fast update\" feature for this GIN index",
RELOPT_KIND_GIN
},
true
},
/* list terminator */ /* list terminator */
{ { NULL } } { { NULL } }
}; };
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# Makefile for access/gin # Makefile for access/gin
# #
# IDENTIFICATION # IDENTIFICATION
# $PostgreSQL: pgsql/src/backend/access/gin/Makefile,v 1.3 2008/02/19 10:30:06 petere Exp $ # $PostgreSQL: pgsql/src/backend/access/gin/Makefile,v 1.4 2009/03/24 20:17:10 tgl Exp $
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
...@@ -14,6 +14,6 @@ include $(top_builddir)/src/Makefile.global ...@@ -14,6 +14,6 @@ include $(top_builddir)/src/Makefile.global
OBJS = ginutil.o gininsert.o ginxlog.o ginentrypage.o gindatapage.o \ OBJS = ginutil.o gininsert.o ginxlog.o ginentrypage.o gindatapage.o \
ginbtree.o ginscan.o ginget.o ginvacuum.o ginarrayproc.o \ ginbtree.o ginscan.o ginget.o ginvacuum.o ginarrayproc.o \
ginbulk.o ginbulk.o ginfast.o
include $(top_srcdir)/src/backend/common.mk include $(top_srcdir)/src/backend/common.mk
...@@ -8,7 +8,7 @@ ...@@ -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.14 2009/01/01 17:23:34 momjian Exp $ * $PostgreSQL: pgsql/src/backend/access/gin/ginbulk.c,v 1.15 2009/03/24 20:17:10 tgl Exp $
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -197,6 +197,8 @@ ginInsertRecordBA(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber att ...@@ -197,6 +197,8 @@ ginInsertRecordBA(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber att
if (nentry <= 0) if (nentry <= 0)
return; return;
Assert(ItemPointerIsValid(heapptr) && attnum >= FirstOffsetNumber);
i = nentry - 1; i = nentry - 1;
for (; i > 0; i >>= 1) for (; i > 0; i >>= 1)
nbit++; nbit++;
......
...@@ -8,7 +8,7 @@ ...@@ -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/gindatapage.c,v 1.13 2009/01/01 17:23:34 momjian Exp $ * $PostgreSQL: pgsql/src/backend/access/gin/gindatapage.c,v 1.14 2009/03/24 20:17:10 tgl Exp $
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -43,8 +43,16 @@ MergeItemPointers(ItemPointerData *dst, ItemPointerData *a, uint32 na, ItemPoint ...@@ -43,8 +43,16 @@ MergeItemPointers(ItemPointerData *dst, ItemPointerData *a, uint32 na, ItemPoint
while (aptr - a < na && bptr - b < nb) while (aptr - a < na && bptr - b < nb)
{ {
if (compareItemPointers(aptr, bptr) > 0) int cmp = compareItemPointers(aptr, bptr);
if (cmp > 0)
*dptr++ = *bptr++;
else if (cmp == 0)
{
/* we want only one copy of the identical items */
*dptr++ = *bptr++; *dptr++ = *bptr++;
aptr++;
}
else else
*dptr++ = *aptr++; *dptr++ = *aptr++;
} }
...@@ -630,10 +638,15 @@ insertItemPointer(GinPostingTreeScan *gdi, ItemPointerData *items, uint32 nitem) ...@@ -630,10 +638,15 @@ insertItemPointer(GinPostingTreeScan *gdi, ItemPointerData *items, uint32 nitem)
gdi->stack = ginFindLeafPage(&gdi->btree, gdi->stack); gdi->stack = ginFindLeafPage(&gdi->btree, gdi->stack);
if (gdi->btree.findItem(&(gdi->btree), gdi->stack)) if (gdi->btree.findItem(&(gdi->btree), gdi->stack))
elog(ERROR, "item pointer (%u,%d) already exists", {
ItemPointerGetBlockNumber(gdi->btree.items + gdi->btree.curitem), /*
ItemPointerGetOffsetNumber(gdi->btree.items + gdi->btree.curitem)); * gdi->btree.items[gdi->btree.curitem] already exists in index
*/
gdi->btree.curitem++;
LockBuffer(gdi->stack->buffer, GIN_UNLOCK);
freeGinBtreeStack(gdi->stack);
}
else
ginInsertValue(&(gdi->btree), gdi->stack); ginInsertValue(&(gdi->btree), gdi->stack);
gdi->stack = NULL; gdi->stack = NULL;
......
This diff is collapsed.
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -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.18 2009/01/01 17:23:34 momjian Exp $ * $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.19 2009/03/24 20:17:11 tgl Exp $
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -138,9 +138,11 @@ addItemPointersToTuple(Relation index, GinState *ginstate, GinBtreeStack *stack, ...@@ -138,9 +138,11 @@ 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 void
ginEntryInsert(Relation index, GinState *ginstate, OffsetNumber attnum, Datum value, ginEntryInsert(Relation index, GinState *ginstate,
ItemPointerData *items, uint32 nitem, bool isBuild) OffsetNumber attnum, Datum value,
ItemPointerData *items, uint32 nitem,
bool isBuild)
{ {
GinBtreeData btree; GinBtreeData btree;
GinBtreeStack *stack; GinBtreeStack *stack;
...@@ -273,7 +275,7 @@ ginbuild(PG_FUNCTION_ARGS) ...@@ -273,7 +275,7 @@ ginbuild(PG_FUNCTION_ARGS)
IndexBuildResult *result; IndexBuildResult *result;
double reltuples; double reltuples;
GinBuildState buildstate; GinBuildState buildstate;
Buffer buffer; Buffer RootBuffer, MetaBuffer;
ItemPointerData *list; ItemPointerData *list;
Datum entry; Datum entry;
uint32 nlist; uint32 nlist;
...@@ -286,11 +288,17 @@ ginbuild(PG_FUNCTION_ARGS) ...@@ -286,11 +288,17 @@ ginbuild(PG_FUNCTION_ARGS)
initGinState(&buildstate.ginstate, index); initGinState(&buildstate.ginstate, index);
/* initialize the meta page */
MetaBuffer = GinNewBuffer(index);
/* initialize the root page */ /* initialize the root page */
buffer = GinNewBuffer(index); RootBuffer = GinNewBuffer(index);
START_CRIT_SECTION(); START_CRIT_SECTION();
GinInitBuffer(buffer, GIN_LEAF); GinInitMetabuffer(MetaBuffer);
MarkBufferDirty(buffer); MarkBufferDirty(MetaBuffer);
GinInitBuffer(RootBuffer, GIN_LEAF);
MarkBufferDirty(RootBuffer);
if (!index->rd_istemp) if (!index->rd_istemp)
{ {
...@@ -303,16 +311,19 @@ ginbuild(PG_FUNCTION_ARGS) ...@@ -303,16 +311,19 @@ ginbuild(PG_FUNCTION_ARGS)
rdata.len = sizeof(RelFileNode); rdata.len = sizeof(RelFileNode);
rdata.next = NULL; rdata.next = NULL;
page = BufferGetPage(buffer);
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_CREATE_INDEX, &rdata); recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_CREATE_INDEX, &rdata);
page = BufferGetPage(RootBuffer);
PageSetLSN(page, recptr); PageSetLSN(page, recptr);
PageSetTLI(page, ThisTimeLineID); PageSetTLI(page, ThisTimeLineID);
page = BufferGetPage(MetaBuffer);
PageSetLSN(page, recptr);
PageSetTLI(page, ThisTimeLineID);
} }
UnlockReleaseBuffer(buffer); UnlockReleaseBuffer(MetaBuffer);
UnlockReleaseBuffer(RootBuffer);
END_CRIT_SECTION(); END_CRIT_SECTION();
/* build the index */ /* build the index */
...@@ -417,9 +428,26 @@ gininsert(PG_FUNCTION_ARGS) ...@@ -417,9 +428,26 @@ gininsert(PG_FUNCTION_ARGS)
initGinState(&ginstate, index); initGinState(&ginstate, index);
if ( GinGetUseFastUpdate(index) )
{
GinTupleCollector collector;
memset(&collector, 0, sizeof(GinTupleCollector));
for(i=0; i<ginstate.origTupdesc->natts;i++)
if ( !isnull[i] )
res += ginHeapTupleFastCollect(index, &ginstate, &collector,
(OffsetNumber)(i+1), values[i], ht_ctid);
ginHeapTupleFastInsert(index, &ginstate, &collector);
}
else
{
for(i=0; i<ginstate.origTupdesc->natts;i++) for(i=0; i<ginstate.origTupdesc->natts;i++)
if ( !isnull[i] ) if ( !isnull[i] )
res += ginHeapTupleInsert(index, &ginstate, (OffsetNumber)(i+1), values[i], ht_ctid); res += ginHeapTupleInsert(index, &ginstate,
(OffsetNumber)(i+1), values[i], ht_ctid);
}
MemoryContextSwitchTo(oldCtx); MemoryContextSwitchTo(oldCtx);
MemoryContextDelete(insertCtx); MemoryContextDelete(insertCtx);
......
...@@ -8,7 +8,7 @@ ...@@ -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.20 2009/01/05 17:14:28 alvherre Exp $ * $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.21 2009/03/24 20:17:11 tgl Exp $
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -213,6 +213,22 @@ GinInitBuffer(Buffer b, uint32 f) ...@@ -213,6 +213,22 @@ GinInitBuffer(Buffer b, uint32 f)
GinInitPage(BufferGetPage(b), f, BufferGetPageSize(b)); GinInitPage(BufferGetPage(b), f, BufferGetPageSize(b));
} }
void
GinInitMetabuffer(Buffer b)
{
GinMetaPageData *metadata;
Page page = BufferGetPage(b);
GinInitPage(page, GIN_META, BufferGetPageSize(b));
metadata = GinPageGetMeta(page);
metadata->head = metadata->tail = InvalidBlockNumber;
metadata->tailFreeSize = 0;
metadata->nPendingPages = 0;
metadata->nPendingHeapTuples = 0;
}
int int
compareEntries(GinState *ginstate, OffsetNumber attnum, Datum a, Datum b) compareEntries(GinState *ginstate, OffsetNumber attnum, Datum a, Datum b)
{ {
...@@ -315,10 +331,26 @@ ginoptions(PG_FUNCTION_ARGS) ...@@ -315,10 +331,26 @@ ginoptions(PG_FUNCTION_ARGS)
{ {
Datum reloptions = PG_GETARG_DATUM(0); Datum reloptions = PG_GETARG_DATUM(0);
bool validate = PG_GETARG_BOOL(1); bool validate = PG_GETARG_BOOL(1);
bytea *result; relopt_value *options;
GinOptions *rdopts;
result = default_reloptions(reloptions, validate, RELOPT_KIND_GIN); int numoptions;
if (result) static const relopt_parse_elt tab[] = {
PG_RETURN_BYTEA_P(result); {"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)}
};
options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIN,
&numoptions);
/* if none set, we're done */
if (numoptions == 0)
PG_RETURN_NULL(); PG_RETURN_NULL();
rdopts = allocateReloptStruct(sizeof(GinOptions), options, numoptions);
fillRelOptions((void *) rdopts, sizeof(GinOptions), options, numoptions,
validate, tab, lengthof(tab));
pfree(options);
PG_RETURN_BYTEA_P(rdopts);
} }
...@@ -8,7 +8,7 @@ ...@@ -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.27 2009/01/01 17:23:34 momjian Exp $ * $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.28 2009/03/24 20:17:11 tgl Exp $
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -19,8 +19,8 @@ ...@@ -19,8 +19,8 @@
#include "catalog/storage.h" #include "catalog/storage.h"
#include "commands/vacuum.h" #include "commands/vacuum.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "postmaster/autovacuum.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/indexfsm.h" #include "storage/indexfsm.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
...@@ -593,18 +593,24 @@ ginbulkdelete(PG_FUNCTION_ARGS) ...@@ -593,18 +593,24 @@ ginbulkdelete(PG_FUNCTION_ARGS)
BlockNumber rootOfPostingTree[BLCKSZ / (sizeof(IndexTupleData) + sizeof(ItemId))]; BlockNumber rootOfPostingTree[BLCKSZ / (sizeof(IndexTupleData) + sizeof(ItemId))];
uint32 nRoot; uint32 nRoot;
gvs.index = index;
gvs.callback = callback;
gvs.callback_state = callback_state;
gvs.strategy = info->strategy;
initGinState(&gvs.ginstate, index);
/* first time through? */ /* first time through? */
if (stats == NULL) if (stats == NULL)
{
/* Yes, so initialize stats to zeroes */
stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult)); stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
/* and cleanup any pending inserts */
ginInsertCleanup(index, &gvs.ginstate, true, stats);
}
/* we'll re-count the tuples each time */ /* we'll re-count the tuples each time */
stats->num_index_tuples = 0; stats->num_index_tuples = 0;
gvs.index = index;
gvs.result = stats; gvs.result = stats;
gvs.callback = callback;
gvs.callback_state = callback_state;
gvs.strategy = info->strategy;
initGinState(&gvs.ginstate, index);
buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno, buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
RBM_NORMAL, info->strategy); RBM_NORMAL, info->strategy);
...@@ -702,10 +708,32 @@ ginvacuumcleanup(PG_FUNCTION_ARGS) ...@@ -702,10 +708,32 @@ ginvacuumcleanup(PG_FUNCTION_ARGS)
BlockNumber totFreePages; BlockNumber totFreePages;
BlockNumber lastBlock = GIN_ROOT_BLKNO, BlockNumber lastBlock = GIN_ROOT_BLKNO,
lastFilledBlock = GIN_ROOT_BLKNO; lastFilledBlock = GIN_ROOT_BLKNO;
GinState ginstate;
/*
* In an autovacuum analyze, we want to clean up pending insertions.
* Otherwise, an ANALYZE-only call is a no-op.
*/
if (info->analyze_only)
{
if (IsAutoVacuumWorkerProcess())
{
initGinState(&ginstate, index);
ginInsertCleanup(index, &ginstate, true, stats);
}
PG_RETURN_POINTER(stats);
}
/* Set up all-zero stats if ginbulkdelete wasn't called */ /*
* Set up all-zero stats and cleanup pending inserts
* if ginbulkdelete wasn't called
*/
if (stats == NULL) if (stats == NULL)
{
stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult)); stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
initGinState(&ginstate, index);
ginInsertCleanup(index, &ginstate, true, stats);
}
/* /*
* XXX we always report the heap tuple count as the number of index * XXX we always report the heap tuple count as the number of index
......
...@@ -8,7 +8,7 @@ ...@@ -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.17 2009/01/20 18:59:36 heikki Exp $ * $PostgreSQL: pgsql/src/backend/access/gin/ginxlog.c,v 1.18 2009/03/24 20:17:11 tgl Exp $
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
...@@ -71,20 +71,30 @@ static void ...@@ -71,20 +71,30 @@ static void
ginRedoCreateIndex(XLogRecPtr lsn, XLogRecord *record) ginRedoCreateIndex(XLogRecPtr lsn, XLogRecord *record)
{ {
RelFileNode *node = (RelFileNode *) XLogRecGetData(record); RelFileNode *node = (RelFileNode *) XLogRecGetData(record);
Buffer buffer; Buffer RootBuffer, MetaBuffer;
Page page; Page page;
buffer = XLogReadBuffer(*node, GIN_ROOT_BLKNO, true); MetaBuffer = XLogReadBuffer(*node, GIN_METAPAGE_BLKNO, true);
Assert(BufferIsValid(buffer)); Assert(BufferIsValid(MetaBuffer));
page = (Page) BufferGetPage(buffer); GinInitMetabuffer(MetaBuffer);
page = (Page) BufferGetPage(MetaBuffer);
PageSetLSN(page, lsn);
PageSetTLI(page, ThisTimeLineID);
GinInitBuffer(buffer, GIN_LEAF); RootBuffer = XLogReadBuffer(*node, GIN_ROOT_BLKNO, true);
Assert(BufferIsValid(RootBuffer));
page = (Page) BufferGetPage(RootBuffer);
GinInitBuffer(RootBuffer, GIN_LEAF);
PageSetLSN(page, lsn); PageSetLSN(page, lsn);
PageSetTLI(page, ThisTimeLineID); PageSetTLI(page, ThisTimeLineID);
MarkBufferDirty(buffer); MarkBufferDirty(MetaBuffer);
UnlockReleaseBuffer(buffer); UnlockReleaseBuffer(MetaBuffer);
MarkBufferDirty(RootBuffer);
UnlockReleaseBuffer(RootBuffer);
} }
static void static void
...@@ -433,6 +443,174 @@ ginRedoDeletePage(XLogRecPtr lsn, XLogRecord *record) ...@@ -433,6 +443,174 @@ ginRedoDeletePage(XLogRecPtr lsn, XLogRecord *record)
} }
} }
static void
ginRedoUpdateMetapage(XLogRecPtr lsn, XLogRecord *record)
{
ginxlogUpdateMeta *data = (ginxlogUpdateMeta*) XLogRecGetData(record);
Buffer metabuffer;
Page metapage;
metabuffer = XLogReadBuffer(data->node, GIN_METAPAGE_BLKNO, false);
metapage = BufferGetPage(metabuffer);
if (!XLByteLE(lsn, PageGetLSN(metapage)))
{
memcpy( GinPageGetMeta(metapage), &data->metadata, sizeof(GinMetaPageData));
PageSetLSN(metapage, lsn);
PageSetTLI(metapage, ThisTimeLineID);
MarkBufferDirty(metabuffer);
}
if ( data->ntuples > 0 )
{
/*
* insert into tail page
*/
if (!(record->xl_info & XLR_BKP_BLOCK_1))
{
Buffer buffer = XLogReadBuffer(data->node, data->metadata.tail, false);
Page page = BufferGetPage(buffer);
if ( !XLByteLE(lsn, PageGetLSN(page)))
{
OffsetNumber l, off = (PageIsEmpty(page)) ? FirstOffsetNumber :
OffsetNumberNext(PageGetMaxOffsetNumber(page));
int i, tupsize;
IndexTuple tuples = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogUpdateMeta));
for(i=0; i<data->ntuples; i++)
{
tupsize = IndexTupleSize(tuples);
l = PageAddItem(page, (Item)tuples, tupsize, off, false, false);
if (l == InvalidOffsetNumber)
elog(ERROR, "failed to add item to index page");
tuples = (IndexTuple)( ((char*)tuples) + tupsize );
}
/*
* Increase counter of heap tuples
*/
GinPageGetOpaque(page)->maxoff++;
PageSetLSN(page, lsn);
PageSetTLI(page, ThisTimeLineID);
MarkBufferDirty(buffer);
}
UnlockReleaseBuffer(buffer);
}
}
else if ( data->prevTail != InvalidBlockNumber )
{
/*
* New tail
*/
Buffer buffer = XLogReadBuffer(data->node, data->prevTail, false);
Page page = BufferGetPage(buffer);
if ( !XLByteLE(lsn, PageGetLSN(page)))
{
GinPageGetOpaque(page)->rightlink = data->newRightlink;
PageSetLSN(page, lsn);
PageSetTLI(page, ThisTimeLineID);
MarkBufferDirty(buffer);
}
UnlockReleaseBuffer(buffer);
}
UnlockReleaseBuffer(metabuffer);
}
static void
ginRedoInsertListPage(XLogRecPtr lsn, XLogRecord *record)
{
ginxlogInsertListPage *data = (ginxlogInsertListPage*) XLogRecGetData(record);
Buffer buffer;
Page page;
OffsetNumber l, off = FirstOffsetNumber;
int i, tupsize;
IndexTuple tuples = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogInsertListPage));
if (record->xl_info & XLR_BKP_BLOCK_1)
return;
buffer = XLogReadBuffer(data->node, data->blkno, true);
page = BufferGetPage(buffer);
GinInitBuffer(buffer, GIN_LIST);
GinPageGetOpaque(page)->rightlink = data->rightlink;
if ( data->rightlink == InvalidBlockNumber )
{
/* tail of sublist */
GinPageSetFullRow(page);
GinPageGetOpaque(page)->maxoff = 1;
}
else
{
GinPageGetOpaque(page)->maxoff = 0;
}
for(i=0; i<data->ntuples; i++)
{
tupsize = IndexTupleSize(tuples);
l = PageAddItem(page, (Item)tuples, tupsize, off, false, false);
if (l == InvalidOffsetNumber)
elog(ERROR, "failed to add item to index page");
tuples = (IndexTuple)( ((char*)tuples) + tupsize );
}
PageSetLSN(page, lsn);
PageSetTLI(page, ThisTimeLineID);
MarkBufferDirty(buffer);
UnlockReleaseBuffer(buffer);
}
static void
ginRedoDeleteListPages(XLogRecPtr lsn, XLogRecord *record)
{
ginxlogDeleteListPages *data = (ginxlogDeleteListPages*) XLogRecGetData(record);
Buffer metabuffer;
Page metapage;
int i;
metabuffer = XLogReadBuffer(data->node, GIN_METAPAGE_BLKNO, false);
metapage = BufferGetPage(metabuffer);
if (!XLByteLE(lsn, PageGetLSN(metapage)))
{
memcpy( GinPageGetMeta(metapage), &data->metadata, sizeof(GinMetaPageData));
PageSetLSN(metapage, lsn);
PageSetTLI(metapage, ThisTimeLineID);
MarkBufferDirty(metabuffer);
}
for(i=0; i<data->ndeleted; i++)
{
Buffer buffer = XLogReadBuffer(data->node,data->toDelete[i],false);
Page page = BufferGetPage(buffer);
if ( !XLByteLE(lsn, PageGetLSN(page)))
{
GinPageGetOpaque(page)->flags = GIN_DELETED;
PageSetLSN(page, lsn);
PageSetTLI(page, ThisTimeLineID);
MarkBufferDirty(buffer);
}
UnlockReleaseBuffer(buffer);
}
UnlockReleaseBuffer(metabuffer);
}
void void
gin_redo(XLogRecPtr lsn, XLogRecord *record) gin_redo(XLogRecPtr lsn, XLogRecord *record)
{ {
...@@ -461,6 +639,15 @@ gin_redo(XLogRecPtr lsn, XLogRecord *record) ...@@ -461,6 +639,15 @@ gin_redo(XLogRecPtr lsn, XLogRecord *record)
case XLOG_GIN_DELETE_PAGE: case XLOG_GIN_DELETE_PAGE:
ginRedoDeletePage(lsn, record); ginRedoDeletePage(lsn, record);
break; break;
case XLOG_GIN_UPDATE_META_PAGE:
ginRedoUpdateMetapage(lsn, record);
break;
case XLOG_GIN_INSERT_LISTPAGE:
ginRedoInsertListPage(lsn, record);
break;
case XLOG_GIN_DELETE_LISTPAGE:
ginRedoDeleteListPages(lsn, record);
break;
default: default:
elog(PANIC, "gin_redo: unknown op code %u", info); elog(PANIC, "gin_redo: unknown op code %u", info);
} }
...@@ -516,6 +703,18 @@ gin_desc(StringInfo buf, uint8 xl_info, char *rec) ...@@ -516,6 +703,18 @@ gin_desc(StringInfo buf, uint8 xl_info, char *rec)
appendStringInfo(buf, "Delete page, "); appendStringInfo(buf, "Delete page, ");
desc_node(buf, ((ginxlogDeletePage *) rec)->node, ((ginxlogDeletePage *) rec)->blkno); desc_node(buf, ((ginxlogDeletePage *) rec)->node, ((ginxlogDeletePage *) rec)->blkno);
break; break;
case XLOG_GIN_UPDATE_META_PAGE:
appendStringInfo(buf, "Update metapage, ");
desc_node(buf, ((ginxlogUpdateMeta *) rec)->node, ((ginxlogUpdateMeta *) rec)->metadata.tail);
break;
case XLOG_GIN_INSERT_LISTPAGE:
appendStringInfo(buf, "Insert new list page, ");
desc_node(buf, ((ginxlogInsertListPage *) rec)->node, ((ginxlogInsertListPage *) rec)->blkno);
break;
case XLOG_GIN_DELETE_LISTPAGE:
appendStringInfo(buf, "Delete list pages (%d), ", ((ginxlogDeleteListPages *) rec)->ndeleted);
desc_node(buf, ((ginxlogDeleteListPages *) rec)->node, ((ginxlogDeleteListPages *) rec)->metadata.head);
break;
default: default:
elog(PANIC, "gin_desc: unknown op code %u", info); elog(PANIC, "gin_desc: unknown op code %u", info);
} }
......
...@@ -8,7 +8,7 @@ ...@@ -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/gist/gistvacuum.c,v 1.42 2009/01/01 17:23:35 momjian Exp $ * $PostgreSQL: pgsql/src/backend/access/gist/gistvacuum.c,v 1.43 2009/03/24 20:17:11 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -515,6 +515,10 @@ gistvacuumcleanup(PG_FUNCTION_ARGS) ...@@ -515,6 +515,10 @@ gistvacuumcleanup(PG_FUNCTION_ARGS)
lastFilledBlock = GIST_ROOT_BLKNO; lastFilledBlock = GIST_ROOT_BLKNO;
bool needLock; bool needLock;
/* No-op in ANALYZE ONLY mode */
if (info->analyze_only)
PG_RETURN_POINTER(stats);
/* Set up all-zero stats if gistbulkdelete wasn't called */ /* Set up all-zero stats if gistbulkdelete wasn't called */
if (stats == NULL) if (stats == NULL)
{ {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/hash/hash.c,v 1.108 2009/01/01 17:23:35 momjian Exp $ * $PostgreSQL: pgsql/src/backend/access/hash/hash.c,v 1.109 2009/03/24 20:17:11 tgl Exp $
* *
* NOTES * NOTES
* This file contains only the public interface routines. * This file contains only the public interface routines.
...@@ -647,6 +647,7 @@ hashvacuumcleanup(PG_FUNCTION_ARGS) ...@@ -647,6 +647,7 @@ hashvacuumcleanup(PG_FUNCTION_ARGS)
BlockNumber num_pages; BlockNumber num_pages;
/* If hashbulkdelete wasn't called, return NULL signifying no change */ /* If hashbulkdelete wasn't called, return NULL signifying no change */
/* Note: this covers the analyze_only case too */
if (stats == NULL) if (stats == NULL)
PG_RETURN_POINTER(NULL); PG_RETURN_POINTER(NULL);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.112 2009/01/01 17:23:35 momjian Exp $ * $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.113 2009/03/24 20:17:12 tgl Exp $
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
* index_open - open an index relation by relation OID * index_open - open an index relation by relation OID
...@@ -647,7 +647,8 @@ index_getnext(IndexScanDesc scan, ScanDirection direction) ...@@ -647,7 +647,8 @@ index_getnext(IndexScanDesc scan, ScanDirection direction)
* item slot could have been replaced by a newer tuple by the time we get * item slot could have been replaced by a newer tuple by the time we get
* to it. * to it.
* *
* Returns the number of matching tuples found. * Returns the number of matching tuples found. (Note: this might be only
* approximate, so it should only be used for statistical purposes.)
* ---------------- * ----------------
*/ */
int64 int64
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,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/nbtree/nbtree.c,v 1.167 2009/01/01 17:23:35 momjian Exp $ * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.168 2009/03/24 20:17:12 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -557,6 +557,10 @@ btvacuumcleanup(PG_FUNCTION_ARGS) ...@@ -557,6 +557,10 @@ btvacuumcleanup(PG_FUNCTION_ARGS)
IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0); IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1); IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
/* No-op in ANALYZE ONLY mode */
if (info->analyze_only)
PG_RETURN_POINTER(stats);
/* /*
* If btbulkdelete was called, we need not do anything, just return the * If btbulkdelete was called, we need not do anything, just return the
* stats from the latest btbulkdelete call. If it wasn't called, we must * stats from the latest btbulkdelete call. If it wasn't called, we must
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.312 2009/01/22 20:16:01 tgl Exp $ * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.313 2009/03/24 20:17:12 tgl Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -1938,6 +1938,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) ...@@ -1938,6 +1938,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot)
*/ */
ivinfo.index = indexRelation; ivinfo.index = indexRelation;
ivinfo.vacuum_full = false; ivinfo.vacuum_full = false;
ivinfo.analyze_only = false;
ivinfo.message_level = DEBUG2; ivinfo.message_level = DEBUG2;
ivinfo.num_heap_tuples = -1; ivinfo.num_heap_tuples = -1;
ivinfo.strategy = NULL; ivinfo.strategy = NULL;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.133 2009/01/22 20:16:01 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.134 2009/03/24 20:17:13 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -496,6 +496,28 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, ...@@ -496,6 +496,28 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
/* We skip to here if there were no analyzable columns */ /* We skip to here if there were no analyzable columns */
cleanup: cleanup:
/* If this isn't part of VACUUM ANALYZE, let index AMs do cleanup */
if (!vacstmt->vacuum)
{
for (ind = 0; ind < nindexes; ind++)
{
IndexBulkDeleteResult *stats;
IndexVacuumInfo ivinfo;
ivinfo.index = Irel[ind];
ivinfo.vacuum_full = false;
ivinfo.analyze_only = true;
ivinfo.message_level = elevel;
ivinfo.num_heap_tuples = -1; /* not known for sure */
ivinfo.strategy = vac_strategy;
stats = index_vacuum_cleanup(&ivinfo, NULL);
if (stats)
pfree(stats);
}
}
/* Done with indexes */ /* Done with indexes */
vac_close_indexes(nindexes, Irel, NoLock); vac_close_indexes(nindexes, Irel, NoLock);
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.385 2009/01/16 13:27:23 heikki Exp $ * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.386 2009/03/24 20:17:13 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -3388,6 +3388,7 @@ scan_index(Relation indrel, double num_tuples) ...@@ -3388,6 +3388,7 @@ scan_index(Relation indrel, double num_tuples)
ivinfo.index = indrel; ivinfo.index = indrel;
ivinfo.vacuum_full = true; ivinfo.vacuum_full = true;
ivinfo.analyze_only = false;
ivinfo.message_level = elevel; ivinfo.message_level = elevel;
ivinfo.num_heap_tuples = num_tuples; ivinfo.num_heap_tuples = num_tuples;
ivinfo.strategy = vac_strategy; ivinfo.strategy = vac_strategy;
...@@ -3454,6 +3455,7 @@ vacuum_index(VacPageList vacpagelist, Relation indrel, ...@@ -3454,6 +3455,7 @@ vacuum_index(VacPageList vacpagelist, Relation indrel,
ivinfo.index = indrel; ivinfo.index = indrel;
ivinfo.vacuum_full = true; ivinfo.vacuum_full = true;
ivinfo.analyze_only = false;
ivinfo.message_level = elevel; ivinfo.message_level = elevel;
ivinfo.num_heap_tuples = num_tuples + keep_tuples; ivinfo.num_heap_tuples = num_tuples + keep_tuples;
ivinfo.strategy = vac_strategy; ivinfo.strategy = vac_strategy;
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.118 2009/01/22 19:25:00 heikki Exp $ * $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.119 2009/03/24 20:17:14 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -875,6 +875,7 @@ lazy_vacuum_index(Relation indrel, ...@@ -875,6 +875,7 @@ lazy_vacuum_index(Relation indrel,
ivinfo.index = indrel; ivinfo.index = indrel;
ivinfo.vacuum_full = false; ivinfo.vacuum_full = false;
ivinfo.analyze_only = false;
ivinfo.message_level = elevel; ivinfo.message_level = elevel;
/* We don't yet know rel_tuples, so pass -1 */ /* We don't yet know rel_tuples, so pass -1 */
ivinfo.num_heap_tuples = -1; ivinfo.num_heap_tuples = -1;
...@@ -906,6 +907,7 @@ lazy_cleanup_index(Relation indrel, ...@@ -906,6 +907,7 @@ lazy_cleanup_index(Relation indrel,
ivinfo.index = indrel; ivinfo.index = indrel;
ivinfo.vacuum_full = false; ivinfo.vacuum_full = false;
ivinfo.analyze_only = false;
ivinfo.message_level = elevel; ivinfo.message_level = elevel;
ivinfo.num_heap_tuples = vacrelstats->rel_tuples; ivinfo.num_heap_tuples = vacrelstats->rel_tuples;
ivinfo.strategy = vac_strategy; ivinfo.strategy = vac_strategy;
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
* Copyright (c) 2003-2009, PostgreSQL Global Development Group * Copyright (c) 2003-2009, PostgreSQL Global Development Group
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/tidbitmap.c,v 1.17 2009/01/10 21:08:36 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/tidbitmap.c,v 1.18 2009/03/24 20:17:14 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -309,6 +309,22 @@ tbm_add_tuples(TIDBitmap *tbm, const ItemPointer tids, int ntids, ...@@ -309,6 +309,22 @@ tbm_add_tuples(TIDBitmap *tbm, const ItemPointer tids, int ntids,
} }
} }
/*
* tbm_add_page - add a whole page to a TIDBitmap
*
* This causes the whole page to be reported (with the recheck flag)
* when the TIDBitmap is scanned.
*/
void
tbm_add_page(TIDBitmap *tbm, BlockNumber pageno)
{
/* Enter the page in the bitmap, or mark it lossy if already present */
tbm_mark_page_lossy(tbm, pageno);
/* If we went over the memory limit, lossify some more pages */
if (tbm->nentries > tbm->maxentries)
tbm_lossify(tbm);
}
/* /*
* tbm_union - set union * tbm_union - set union
* *
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, 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/access/genam.h,v 1.75 2009/01/01 17:23:55 momjian Exp $ * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.76 2009/03/24 20:17:14 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -41,6 +41,7 @@ typedef struct IndexVacuumInfo ...@@ -41,6 +41,7 @@ typedef struct IndexVacuumInfo
{ {
Relation index; /* the index being vacuumed */ Relation index; /* the index being vacuumed */
bool vacuum_full; /* VACUUM FULL (we have exclusive lock) */ bool vacuum_full; /* VACUUM FULL (we have exclusive lock) */
bool analyze_only; /* ANALYZE (without any actual vacuum) */
int message_level; /* ereport level for progress messages */ int message_level; /* ereport level for progress messages */
double num_heap_tuples; /* tuples remaining in heap */ double num_heap_tuples; /* tuples remaining in heap */
BufferAccessStrategy strategy; /* access strategy for reads */ BufferAccessStrategy strategy; /* access strategy for reads */
......
...@@ -4,11 +4,9 @@ ...@@ -4,11 +4,9 @@
* *
* Copyright (c) 2006-2009, PostgreSQL Global Development Group * Copyright (c) 2006-2009, PostgreSQL Global Development Group
* *
* $PostgreSQL: pgsql/src/include/access/gin.h,v 1.28 2009/01/10 21:08:36 tgl Exp $ * $PostgreSQL: pgsql/src/include/access/gin.h,v 1.29 2009/03/24 20:17:14 tgl Exp $
*-------------------------------------------------------------------------- *--------------------------------------------------------------------------
*/ */
#ifndef GIN_H #ifndef GIN_H
#define GIN_H #define GIN_H
...@@ -16,11 +14,6 @@ ...@@ -16,11 +14,6 @@
#include "access/itup.h" #include "access/itup.h"
#include "access/xlog.h" #include "access/xlog.h"
#include "fmgr.h" #include "fmgr.h"
#include "nodes/tidbitmap.h"
#include "storage/block.h"
#include "storage/buf.h"
#include "storage/off.h"
#include "storage/relfilenode.h"
/* /*
...@@ -43,20 +36,52 @@ ...@@ -43,20 +36,52 @@
typedef struct GinPageOpaqueData typedef struct GinPageOpaqueData
{ {
BlockNumber rightlink; /* next page if any */ BlockNumber rightlink; /* next page if any */
OffsetNumber maxoff; /* number entries on GIN_DATA page: number of OffsetNumber maxoff; /* number entries on GIN_DATA page; number of
* heap ItemPointer on GIN_DATA|GIN_LEAF page * heap ItemPointer on GIN_DATA|GIN_LEAF page
* and number of records on GIN_DATA & * and number of records on GIN_DATA &
* ~GIN_LEAF page */ * ~GIN_LEAF page. On GIN_LIST page, number of
* heap tuples. */
uint16 flags; /* see bit definitions below */ uint16 flags; /* see bit definitions below */
} GinPageOpaqueData; } GinPageOpaqueData;
typedef GinPageOpaqueData *GinPageOpaque; typedef GinPageOpaqueData *GinPageOpaque;
#define GIN_ROOT_BLKNO (0)
#define GIN_DATA (1 << 0) #define GIN_DATA (1 << 0)
#define GIN_LEAF (1 << 1) #define GIN_LEAF (1 << 1)
#define GIN_DELETED (1 << 2) #define GIN_DELETED (1 << 2)
#define GIN_META (1 << 3)
#define GIN_LIST (1 << 4)
#define GIN_LIST_FULLROW (1 << 5) /* makes sense only on GIN_LIST page */
/* Page numbers of fixed-location pages */
#define GIN_METAPAGE_BLKNO (0)
#define GIN_ROOT_BLKNO (1)
typedef struct GinMetaPageData
{
/*
* Pointers to head and tail of pending list, which consists of GIN_LIST
* pages. These store fast-inserted entries that haven't yet been moved
* into the regular GIN structure.
*/
BlockNumber head;
BlockNumber tail;
/*
* Free space in bytes in the pending list's tail page.
*/
uint32 tailFreeSize;
/*
* We store both number of pages and number of heap tuples
* that are in the pending list.
*/
BlockNumber nPendingPages;
int64 nPendingHeapTuples;
} GinMetaPageData;
#define GinPageGetMeta(p) \
((GinMetaPageData *) PageGetContents(p))
/* /*
* Works on page * Works on page
...@@ -68,6 +93,8 @@ typedef GinPageOpaqueData *GinPageOpaque; ...@@ -68,6 +93,8 @@ typedef GinPageOpaqueData *GinPageOpaque;
#define GinPageSetNonLeaf(page) ( GinPageGetOpaque(page)->flags &= ~GIN_LEAF ) #define GinPageSetNonLeaf(page) ( GinPageGetOpaque(page)->flags &= ~GIN_LEAF )
#define GinPageIsData(page) ( GinPageGetOpaque(page)->flags & GIN_DATA ) #define GinPageIsData(page) ( GinPageGetOpaque(page)->flags & GIN_DATA )
#define GinPageSetData(page) ( GinPageGetOpaque(page)->flags |= GIN_DATA ) #define GinPageSetData(page) ( GinPageGetOpaque(page)->flags |= GIN_DATA )
#define GinPageHasFullRow(page) ( GinPageGetOpaque(page)->flags & GIN_LIST_FULLROW )
#define GinPageSetFullRow(page) ( GinPageGetOpaque(page)->flags |= GIN_LIST_FULLROW )
#define GinPageIsDeleted(page) ( GinPageGetOpaque(page)->flags & GIN_DELETED) #define GinPageIsDeleted(page) ( GinPageGetOpaque(page)->flags & GIN_DELETED)
#define GinPageSetDeleted(page) ( GinPageGetOpaque(page)->flags |= GIN_DELETED) #define GinPageSetDeleted(page) ( GinPageGetOpaque(page)->flags |= GIN_DELETED)
...@@ -76,8 +103,8 @@ typedef GinPageOpaqueData *GinPageOpaque; ...@@ -76,8 +103,8 @@ typedef GinPageOpaqueData *GinPageOpaque;
#define GinPageRightMost(page) ( GinPageGetOpaque(page)->rightlink == InvalidBlockNumber) #define GinPageRightMost(page) ( GinPageGetOpaque(page)->rightlink == InvalidBlockNumber)
/* /*
* Define our ItemPointerGet(BlockNumber|GetOffsetNumber) * We use our own ItemPointerGet(BlockNumber|GetOffsetNumber)
* to prevent asserts * to avoid Asserts, since sometimes the ip_posid isn't "valid"
*/ */
#define GinItemPointerGetBlockNumber(pointer) \ #define GinItemPointerGetBlockNumber(pointer) \
...@@ -86,6 +113,22 @@ typedef GinPageOpaqueData *GinPageOpaque; ...@@ -86,6 +113,22 @@ typedef GinPageOpaqueData *GinPageOpaque;
#define GinItemPointerGetOffsetNumber(pointer) \ #define GinItemPointerGetOffsetNumber(pointer) \
((pointer)->ip_posid) ((pointer)->ip_posid)
#define ItemPointerSetMin(p) \
ItemPointerSet((p), (BlockNumber)0, (OffsetNumber)0)
#define ItemPointerIsMin(p) \
(ItemPointerGetOffsetNumber(p) == (OffsetNumber)0 && \
ItemPointerGetBlockNumber(p) == (BlockNumber)0)
#define ItemPointerSetMax(p) \
ItemPointerSet((p), InvalidBlockNumber, (OffsetNumber)0xffff)
#define ItemPointerIsMax(p) \
(ItemPointerGetOffsetNumber(p) == (OffsetNumber)0xffff && \
ItemPointerGetBlockNumber(p) == InvalidBlockNumber)
#define ItemPointerSetLossyPage(p, b) \
ItemPointerSet((p), (b), (OffsetNumber)0xffff)
#define ItemPointerIsLossyPage(p) \
(ItemPointerGetOffsetNumber(p) == (OffsetNumber)0xffff && \
ItemPointerGetBlockNumber(p) != InvalidBlockNumber)
typedef struct typedef struct
{ {
BlockIdData child_blkno; /* use it instead of BlockNumber to save space BlockIdData child_blkno; /* use it instead of BlockNumber to save space
...@@ -135,6 +178,26 @@ typedef struct ...@@ -135,6 +178,26 @@ typedef struct
- GinPageGetOpaque(page)->maxoff * GinSizeOfItem(page) \ - GinPageGetOpaque(page)->maxoff * GinSizeOfItem(page) \
- MAXALIGN(sizeof(GinPageOpaqueData))) - MAXALIGN(sizeof(GinPageOpaqueData)))
/*
* List pages
*/
#define GinListPageSize \
( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GinPageOpaqueData)) )
/*
* Storage type for GIN's reloptions
*/
typedef struct GinOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
bool useFastUpdate; /* use fast updates? */
} GinOptions;
#define GIN_DEFAULT_USE_FASTUPDATE true
#define GinGetUseFastUpdate(relation) \
((relation)->rd_options ? \
((GinOptions *) (relation)->rd_options)->useFastUpdate : GIN_DEFAULT_USE_FASTUPDATE)
#define GIN_UNLOCK BUFFER_LOCK_UNLOCK #define GIN_UNLOCK BUFFER_LOCK_UNLOCK
#define GIN_SHARE BUFFER_LOCK_SHARE #define GIN_SHARE BUFFER_LOCK_SHARE
...@@ -234,12 +297,50 @@ typedef struct ginxlogDeletePage ...@@ -234,12 +297,50 @@ typedef struct ginxlogDeletePage
BlockNumber rightLink; BlockNumber rightLink;
} ginxlogDeletePage; } ginxlogDeletePage;
#define XLOG_GIN_UPDATE_META_PAGE 0x60
typedef struct ginxlogUpdateMeta
{
RelFileNode node;
GinMetaPageData metadata;
BlockNumber prevTail;
BlockNumber newRightlink;
int32 ntuples; /* if ntuples > 0 then metadata.tail was updated
* with that many tuples; else new sub list was
* inserted */
/* array of inserted tuples follows */
} ginxlogUpdateMeta;
#define XLOG_GIN_INSERT_LISTPAGE 0x70
typedef struct ginxlogInsertListPage
{
RelFileNode node;
BlockNumber blkno;
BlockNumber rightlink;
int32 ntuples;
/* array of inserted tuples follows */
} ginxlogInsertListPage;
#define XLOG_GIN_DELETE_LISTPAGE 0x80
#define GIN_NDELETE_AT_ONCE 16
typedef struct ginxlogDeleteListPages
{
RelFileNode node;
GinMetaPageData metadata;
int32 ndeleted;
BlockNumber toDelete[GIN_NDELETE_AT_ONCE];
} ginxlogDeleteListPages;
/* ginutil.c */ /* ginutil.c */
extern Datum ginoptions(PG_FUNCTION_ARGS); extern Datum ginoptions(PG_FUNCTION_ARGS);
extern void initGinState(GinState *state, Relation index); 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 void GinInitMetabuffer(Buffer b);
extern int compareEntries(GinState *ginstate, OffsetNumber attnum, Datum a, Datum b); extern int compareEntries(GinState *ginstate, OffsetNumber attnum, Datum a, Datum b);
extern int compareAttEntries(GinState *ginstate, OffsetNumber attnum_a, Datum a, extern int compareAttEntries(GinState *ginstate, OffsetNumber attnum_a, Datum a,
OffsetNumber attnum_b, Datum b); OffsetNumber attnum_b, Datum b);
...@@ -249,9 +350,14 @@ extern Datum *extractEntriesSU(GinState *ginstate, OffsetNumber attnum, Datum va ...@@ -249,9 +350,14 @@ extern Datum *extractEntriesSU(GinState *ginstate, OffsetNumber attnum, Datum va
extern Datum gin_index_getattr(GinState *ginstate, IndexTuple tuple); extern Datum gin_index_getattr(GinState *ginstate, IndexTuple tuple);
extern OffsetNumber gintuple_get_attrnum(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);
extern void ginEntryInsert(Relation index, GinState *ginstate,
OffsetNumber attnum, Datum value,
ItemPointerData *items, uint32 nitem,
bool isBuild);
/* ginxlog.c */ /* ginxlog.c */
extern void gin_redo(XLogRecPtr lsn, XLogRecord *record); extern void gin_redo(XLogRecPtr lsn, XLogRecord *record);
...@@ -440,13 +546,7 @@ extern void newScanKey(IndexScanDesc scan); ...@@ -440,13 +546,7 @@ extern void newScanKey(IndexScanDesc scan);
/* ginget.c */ /* ginget.c */
extern PGDLLIMPORT int GinFuzzySearchLimit; extern PGDLLIMPORT int GinFuzzySearchLimit;
#define ItemPointerSetMax(p) ItemPointerSet( (p), (BlockNumber)0xffffffff, (OffsetNumber)0xffff )
#define ItemPointerIsMax(p) ( ItemPointerGetBlockNumber(p) == (BlockNumber)0xffffffff && ItemPointerGetOffsetNumber(p) == (OffsetNumber)0xffff )
#define ItemPointerSetMin(p) ItemPointerSet( (p), (BlockNumber)0, (OffsetNumber)0)
#define ItemPointerIsMin(p) ( ItemPointerGetBlockNumber(p) == (BlockNumber)0 && ItemPointerGetOffsetNumber(p) == (OffsetNumber)0 )
extern Datum gingetbitmap(PG_FUNCTION_ARGS); extern Datum gingetbitmap(PG_FUNCTION_ARGS);
extern Datum gingettuple(PG_FUNCTION_ARGS);
/* ginvacuum.c */ /* ginvacuum.c */
extern Datum ginbulkdelete(PG_FUNCTION_ARGS); extern Datum ginbulkdelete(PG_FUNCTION_ARGS);
...@@ -489,4 +589,22 @@ extern void ginInsertRecordBA(BuildAccumulator *accum, ...@@ -489,4 +589,22 @@ extern void ginInsertRecordBA(BuildAccumulator *accum,
OffsetNumber attnum, Datum *entries, int32 nentry); OffsetNumber attnum, Datum *entries, int32 nentry);
extern ItemPointerData *ginGetEntry(BuildAccumulator *accum, OffsetNumber *attnum, Datum *entry, uint32 *n); extern ItemPointerData *ginGetEntry(BuildAccumulator *accum, OffsetNumber *attnum, Datum *entry, uint32 *n);
#endif /* ginfast.c */
typedef struct GinTupleCollector
{
IndexTuple *tuples;
uint32 ntuples;
uint32 lentuples;
uint32 sumsize;
} GinTupleCollector;
extern void ginHeapTupleFastInsert(Relation index, GinState *ginstate,
GinTupleCollector *collector);
extern uint32 ginHeapTupleFastCollect(Relation index, GinState *ginstate,
GinTupleCollector *collector,
OffsetNumber attnum, Datum value, ItemPointer item);
extern void ginInsertCleanup(Relation index, GinState *ginstate,
bool vac_delay, IndexBulkDeleteResult *stats);
#endif /* GIN_H */
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, 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.524 2009/02/24 10:06:34 petere Exp $ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.525 2009/03/24 20:17:15 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200902242 #define CATALOG_VERSION_NO 200903241
#endif #endif
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, 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.61 2009/03/05 23:06:45 tgl Exp $ * $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.62 2009/03/24 20:17:15 tgl Exp $
* *
* NOTES * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -118,7 +118,7 @@ DESCR("hash index access method"); ...@@ -118,7 +118,7 @@ DESCR("hash index access method");
DATA(insert OID = 783 ( gist 0 7 f f f t t t t t t 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions )); DATA(insert OID = 783 ( gist 0 7 f f f t t t t t t 0 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 t t f f t f 0 gininsert ginbeginscan gingettuple gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions )); DATA(insert OID = 2742 ( gin 0 5 f f f t t f f t f 0 gininsert ginbeginscan - 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
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, 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_proc.h,v 1.537 2009/02/24 10:06:34 petere Exp $ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.538 2009/03/24 20:17:15 tgl Exp $
* *
* NOTES * NOTES
* The script catalog/genbki.sh reads this file and generates .bki * The script catalog/genbki.sh reads this file and generates .bki
...@@ -4184,8 +4184,6 @@ DATA(insert OID = 2592 ( gist_circle_compress PGNSP PGUID 12 1 0 0 f f f t f i ...@@ -4184,8 +4184,6 @@ DATA(insert OID = 2592 ( gist_circle_compress PGNSP PGUID 12 1 0 0 f f f t f i
DESCR("GiST support"); DESCR("GiST support");
/* GIN */ /* GIN */
DATA(insert OID = 2730 ( gingettuple PGNSP PGUID 12 1 0 0 f f f t f v 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ gingettuple _null_ _null_ _null_ ));
DESCR("gin(internal)");
DATA(insert OID = 2731 ( gingetbitmap PGNSP PGUID 12 1 0 0 f f f t f v 2 0 20 "2281 2281" _null_ _null_ _null_ _null_ gingetbitmap _null_ _null_ _null_ )); DATA(insert OID = 2731 ( gingetbitmap PGNSP PGUID 12 1 0 0 f f f t f v 2 0 20 "2281 2281" _null_ _null_ _null_ _null_ gingetbitmap _null_ _null_ _null_ ));
DESCR("gin(internal)"); DESCR("gin(internal)");
DATA(insert OID = 2732 ( gininsert PGNSP PGUID 12 1 0 0 f f f t f v 6 0 16 "2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ gininsert _null_ _null_ _null_ )); DATA(insert OID = 2732 ( gininsert PGNSP PGUID 12 1 0 0 f f f t f v 6 0 16 "2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ gininsert _null_ _null_ _null_ ));
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* *
* Copyright (c) 2003-2009, PostgreSQL Global Development Group * Copyright (c) 2003-2009, PostgreSQL Global Development Group
* *
* $PostgreSQL: pgsql/src/include/nodes/tidbitmap.h,v 1.9 2009/01/10 21:08:36 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/tidbitmap.h,v 1.10 2009/03/24 20:17:18 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -52,6 +52,7 @@ extern void tbm_free(TIDBitmap *tbm); ...@@ -52,6 +52,7 @@ extern void tbm_free(TIDBitmap *tbm);
extern void tbm_add_tuples(TIDBitmap *tbm, extern void tbm_add_tuples(TIDBitmap *tbm,
const ItemPointer tids, int ntids, const ItemPointer tids, int ntids,
bool recheck); bool recheck);
extern void tbm_add_page(TIDBitmap *tbm, BlockNumber pageno);
extern void tbm_union(TIDBitmap *a, const TIDBitmap *b); extern void tbm_union(TIDBitmap *a, const TIDBitmap *b);
extern void tbm_intersect(TIDBitmap *a, const TIDBitmap *b); extern void tbm_intersect(TIDBitmap *a, const TIDBitmap *b);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment