Commit 282d2a03 authored by Tom Lane's avatar Tom Lane

HOT updates. When we update a tuple without changing any of its indexed

columns, and the new version can be stored on the same heap page, we no longer
generate extra index entries for the new version.  Instead, index searches
follow the HOT-chain links to ensure they find the correct tuple version.

In addition, this patch introduces the ability to "prune" dead tuples on a
per-page basis, without having to do a complete VACUUM pass to recover space.
VACUUM is still needed to clean up dead index entries, however.

Pavan Deolasee, with help from a bunch of other people.
parent bbf4fdc2
/* /*
* $PostgreSQL: pgsql/contrib/pgstattuple/pgstattuple.c,v 1.29 2007/09/12 22:10:25 tgl Exp $ * $PostgreSQL: pgsql/contrib/pgstattuple/pgstattuple.c,v 1.30 2007/09/20 17:56:30 tgl Exp $
* *
* Copyright (c) 2001,2002 Tatsuo Ishii * Copyright (c) 2001,2002 Tatsuo Ishii
* *
...@@ -290,7 +290,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo) ...@@ -290,7 +290,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
{ {
buffer = ReadBuffer(rel, block); buffer = ReadBuffer(rel, block);
LockBuffer(buffer, BUFFER_LOCK_SHARE); LockBuffer(buffer, BUFFER_LOCK_SHARE);
stat.free_space += PageGetFreeSpace((Page) BufferGetPage(buffer)); stat.free_space += PageGetHeapFreeSpace((Page) BufferGetPage(buffer));
LockBuffer(buffer, BUFFER_LOCK_UNLOCK); LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
ReleaseBuffer(buffer); ReleaseBuffer(buffer);
block++; block++;
...@@ -301,7 +301,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo) ...@@ -301,7 +301,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo)
while (block < nblocks) while (block < nblocks)
{ {
buffer = ReadBuffer(rel, block); buffer = ReadBuffer(rel, block);
stat.free_space += PageGetFreeSpace((Page) BufferGetPage(buffer)); stat.free_space += PageGetHeapFreeSpace((Page) BufferGetPage(buffer));
ReleaseBuffer(buffer); ReleaseBuffer(buffer);
block++; block++;
} }
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.157 2007/09/05 18:10:47 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.158 2007/09/20 17:56:30 tgl Exp $ -->
<!-- <!--
Documentation of the system catalogs, directed toward PostgreSQL developers Documentation of the system catalogs, directed toward PostgreSQL developers
--> -->
...@@ -2565,6 +2565,29 @@ ...@@ -2565,6 +2565,29 @@
</entry> </entry>
</row> </row>
<row>
<entry><structfield>indcheckxmin</structfield></entry>
<entry><type>bool</type></entry>
<entry></entry>
<entry>
If true, queries must not use the index until the <structfield>xmin</>
of this <structname>pg_index</> row is below their TransactionXmin
event horizon, because the table may contain broken HOT chains with
incompatible rows that they can see
</entry>
</row>
<row>
<entry><structfield>indisready</structfield></entry>
<entry><type>bool</type></entry>
<entry></entry>
<entry>
If true, the index is currently ready for inserts. False means the
index must be ignored by <command>INSERT</>/<command>UPDATE</>
operations
</entry>
</row>
<row> <row>
<entry><structfield>indkey</structfield></entry> <entry><structfield>indkey</structfield></entry>
<entry><type>int2vector</type></entry> <entry><type>int2vector</type></entry>
......
<!-- $PostgreSQL: pgsql/doc/src/sgml/monitoring.sgml,v 1.51 2007/06/28 00:02:37 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/monitoring.sgml,v 1.52 2007/09/20 17:56:30 tgl Exp $ -->
<chapter id="monitoring"> <chapter id="monitoring">
<title>Monitoring Database Activity</title> <title>Monitoring Database Activity</title>
...@@ -276,6 +276,8 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re ...@@ -276,6 +276,8 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
scans, number of index scans initiated (over all indexes scans, number of index scans initiated (over all indexes
belonging to the table), number of live rows fetched by index belonging to the table), number of live rows fetched by index
scans, numbers of row insertions, updates, and deletions, scans, numbers of row insertions, updates, and deletions,
number of row updates that were HOT (i.e., no separate index update),
numbers of live and dead rows,
the last time the table was vacuumed manually, the last time the table was vacuumed manually,
the last time it was vacuumed by the autovacuum daemon, the last time it was vacuumed by the autovacuum daemon,
the last time it was analyzed manually, the last time it was analyzed manually,
...@@ -580,7 +582,7 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re ...@@ -580,7 +582,7 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
<entry><literal><function>pg_stat_get_tuples_updated</function>(<type>oid</type>)</literal></entry> <entry><literal><function>pg_stat_get_tuples_updated</function>(<type>oid</type>)</literal></entry>
<entry><type>bigint</type></entry> <entry><type>bigint</type></entry>
<entry> <entry>
Number of rows updated in table Number of rows updated in table (includes HOT updates)
</entry> </entry>
</row> </row>
...@@ -592,6 +594,30 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re ...@@ -592,6 +594,30 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry> </entry>
</row> </row>
<row>
<entry><literal><function>pg_stat_get_tuples_hot_updated</function>(<type>oid</type>)</literal></entry>
<entry><type>bigint</type></entry>
<entry>
Number of rows HOT-updated in table
</entry>
</row>
<row>
<entry><literal><function>pg_stat_get_live_tuples</function>(<type>oid</type>)</literal></entry>
<entry><type>bigint</type></entry>
<entry>
Number of live rows in table
</entry>
</row>
<row>
<entry><literal><function>pg_stat_get_dead_tuples</function>(<type>oid</type>)</literal></entry>
<entry><type>bigint</type></entry>
<entry>
Number of dead rows in table
</entry>
</row>
<row> <row>
<entry><literal><function>pg_stat_get_blocks_fetched</function>(<type>oid</type>)</literal></entry> <entry><literal><function>pg_stat_get_blocks_fetched</function>(<type>oid</type>)</literal></entry>
<entry><type>bigint</type></entry> <entry><type>bigint</type></entry>
...@@ -716,6 +742,18 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re ...@@ -716,6 +742,18 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
</entry> </entry>
</row> </row>
<row>
<entry><literal><function>pg_stat_get_backend_xact_start</function>(<type>integer</type>)</literal></entry>
<entry><type>timestamp with time zone</type></entry>
<entry>
The time at which the given server process' currently
executing transaction was started, but only if the
current user is a superuser or the same user as that of
the session being queried (and
<varname>stats_command_string</varname> is on)
</entry>
</row>
<row> <row>
<entry><literal><function>pg_stat_get_backend_start</function>(<type>integer</type>)</literal></entry> <entry><literal><function>pg_stat_get_backend_start</function>(<type>integer</type>)</literal></entry>
<entry><type>timestamp with time zone</type></entry> <entry><type>timestamp with time zone</type></entry>
......
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/create_index.sgml,v 1.64 2007/09/07 00:58:56 tgl Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/create_index.sgml,v 1.65 2007/09/20 17:56:30 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -329,7 +329,10 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] <replaceable class="parameter">name</re ...@@ -329,7 +329,10 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] <replaceable class="parameter">name</re
</para> </para>
<para> <para>
If a problem arises during the second scan of the table, such as a In a concurrent index build, the index is actually entered into the
system catalogs in one transaction, then the two table scans occur in a
second and third transaction.
If a problem arises while scanning the table, such as a
uniqueness violation in a unique index, the <command>CREATE INDEX</> uniqueness violation in a unique index, the <command>CREATE INDEX</>
command will fail but leave behind an <quote>invalid</> index. This index command will fail but leave behind an <quote>invalid</> index. This index
will be ignored for querying purposes because it might be incomplete; will be ignored for querying purposes because it might be incomplete;
......
...@@ -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/ginentrypage.c,v 1.8 2007/09/12 22:10:25 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/gin/ginentrypage.c,v 1.9 2007/09/20 17:56:30 tgl Exp $
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -359,7 +359,7 @@ entryPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prd ...@@ -359,7 +359,7 @@ entryPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prd
*prdata = rdata; *prdata = rdata;
data.updateBlkno = entryPreparePage(btree, page, off); data.updateBlkno = entryPreparePage(btree, page, off);
placed = PageAddItem(page, (Item) btree->entry, IndexTupleSize(btree->entry), off, false); placed = PageAddItem(page, (Item) btree->entry, IndexTupleSize(btree->entry), off, false, false);
if (placed != off) if (placed != off)
elog(ERROR, "failed to add item to index page in \"%s\"", elog(ERROR, "failed to add item to index page in \"%s\"",
RelationGetRelationName(btree->index)); RelationGetRelationName(btree->index));
...@@ -488,7 +488,7 @@ entrySplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogR ...@@ -488,7 +488,7 @@ entrySplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogR
lsize += MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData); lsize += MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
} }
if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false) == InvalidOffsetNumber) if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
elog(ERROR, "failed to add item to index page in \"%s\"", elog(ERROR, "failed to add item to index page in \"%s\"",
RelationGetRelationName(btree->index)); RelationGetRelationName(btree->index));
ptr += MAXALIGN(IndexTupleSize(itup)); ptr += MAXALIGN(IndexTupleSize(itup));
...@@ -563,11 +563,11 @@ entryFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf) ...@@ -563,11 +563,11 @@ entryFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf)
page = BufferGetPage(root); page = BufferGetPage(root);
itup = ginPageGetLinkItup(lbuf); itup = ginPageGetLinkItup(lbuf);
if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false) == InvalidOffsetNumber) if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
elog(ERROR, "failed to add item to index root page"); elog(ERROR, "failed to add item to index root page");
itup = ginPageGetLinkItup(rbuf); itup = ginPageGetLinkItup(rbuf);
if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false) == InvalidOffsetNumber) if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
elog(ERROR, "failed to add item to index root page"); elog(ERROR, "failed to add item to index root page");
} }
......
...@@ -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.16 2007/09/12 22:10:25 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.17 2007/09/20 17:56:30 tgl Exp $
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -544,7 +544,7 @@ ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3 ...@@ -544,7 +544,7 @@ ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3
itup = GinFormTuple(&gvs->ginstate, value, GinGetPosting(itup), newN); itup = GinFormTuple(&gvs->ginstate, value, GinGetPosting(itup), newN);
PageIndexTupleDelete(tmppage, i); PageIndexTupleDelete(tmppage, i);
if (PageAddItem(tmppage, (Item) itup, IndexTupleSize(itup), i, false) != i) if (PageAddItem(tmppage, (Item) itup, IndexTupleSize(itup), i, false, false) != i)
elog(ERROR, "failed to add item to index page in \"%s\"", elog(ERROR, "failed to add item to index page in \"%s\"",
RelationGetRelationName(gvs->index)); RelationGetRelationName(gvs->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.8 2007/09/12 22:10:25 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/gin/ginxlog.c,v 1.9 2007/09/20 17:56:30 tgl Exp $
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
...@@ -199,7 +199,7 @@ ginRedoInsert(XLogRecPtr lsn, XLogRecord *record) ...@@ -199,7 +199,7 @@ ginRedoInsert(XLogRecPtr lsn, XLogRecord *record)
itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogInsert)); itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogInsert));
if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), data->offset, false) == InvalidOffsetNumber) if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), data->offset, false, false) == InvalidOffsetNumber)
elog(ERROR, "failed to add item to index page in %u/%u/%u", elog(ERROR, "failed to add item to index page in %u/%u/%u",
data->node.spcNode, data->node.dbNode, data->node.relNode); data->node.spcNode, data->node.dbNode, data->node.relNode);
...@@ -281,7 +281,7 @@ ginRedoSplit(XLogRecPtr lsn, XLogRecord *record) ...@@ -281,7 +281,7 @@ ginRedoSplit(XLogRecPtr lsn, XLogRecord *record)
for (i = 0; i < data->separator; i++) for (i = 0; i < data->separator; i++)
{ {
if (PageAddItem(lpage, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false) == InvalidOffsetNumber) if (PageAddItem(lpage, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
elog(ERROR, "failed to add item to index page in %u/%u/%u", elog(ERROR, "failed to add item to index page in %u/%u/%u",
data->node.spcNode, data->node.dbNode, data->node.relNode); data->node.spcNode, data->node.dbNode, data->node.relNode);
itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup))); itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
...@@ -289,7 +289,7 @@ ginRedoSplit(XLogRecPtr lsn, XLogRecord *record) ...@@ -289,7 +289,7 @@ ginRedoSplit(XLogRecPtr lsn, XLogRecord *record)
for (i = data->separator; i < data->nitem; i++) for (i = data->separator; i < data->nitem; i++)
{ {
if (PageAddItem(rpage, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false) == InvalidOffsetNumber) if (PageAddItem(rpage, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
elog(ERROR, "failed to add item to index page in %u/%u/%u", elog(ERROR, "failed to add item to index page in %u/%u/%u",
data->node.spcNode, data->node.dbNode, data->node.relNode); data->node.spcNode, data->node.dbNode, data->node.relNode);
itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup))); itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
...@@ -375,7 +375,7 @@ ginRedoVacuumPage(XLogRecPtr lsn, XLogRecord *record) ...@@ -375,7 +375,7 @@ ginRedoVacuumPage(XLogRecPtr lsn, XLogRecord *record)
for (i = 0; i < data->nitem; i++) for (i = 0; i < data->nitem; i++)
{ {
if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false) == InvalidOffsetNumber) if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
elog(ERROR, "failed to add item to index page in %u/%u/%u", elog(ERROR, "failed to add item to index page in %u/%u/%u",
data->node.spcNode, data->node.dbNode, data->node.relNode); data->node.spcNode, data->node.dbNode, data->node.relNode);
itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup))); itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
......
...@@ -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/gist.c,v 1.146 2007/09/12 22:10:25 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/gist/gist.c,v 1.147 2007/09/20 17:56:30 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -366,7 +366,7 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate) ...@@ -366,7 +366,7 @@ gistplacetopage(GISTInsertState *state, GISTSTATE *giststate)
data = (char *) (ptr->list); data = (char *) (ptr->list);
for (i = 0; i < ptr->block.num; i++) for (i = 0; i < ptr->block.num; i++)
{ {
if (PageAddItem(ptr->page, (Item) data, IndexTupleSize((IndexTuple) data), i + FirstOffsetNumber, false) == InvalidOffsetNumber) if (PageAddItem(ptr->page, (Item) data, IndexTupleSize((IndexTuple) data), i + FirstOffsetNumber, false, false) == InvalidOffsetNumber)
elog(ERROR, "failed to add item to index page in \"%s\"", RelationGetRelationName(state->r)); elog(ERROR, "failed to add item to index page in \"%s\"", RelationGetRelationName(state->r));
data += IndexTupleSize((IndexTuple) data); data += IndexTupleSize((IndexTuple) data);
} }
......
...@@ -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/gistutil.c,v 1.23 2007/09/12 22:10:25 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/gist/gistutil.c,v 1.24 2007/09/20 17:56:30 tgl Exp $
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
...@@ -42,7 +42,7 @@ gistfillbuffer(Relation r, Page page, IndexTuple *itup, ...@@ -42,7 +42,7 @@ gistfillbuffer(Relation r, Page page, IndexTuple *itup,
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
{ {
l = PageAddItem(page, (Item) itup[i], IndexTupleSize(itup[i]), l = PageAddItem(page, (Item) itup[i], IndexTupleSize(itup[i]),
off, false); off, false, false);
if (l == InvalidOffsetNumber) if (l == InvalidOffsetNumber)
elog(ERROR, "failed to add item to index page in \"%s\"", elog(ERROR, "failed to add item to index page in \"%s\"",
RelationGetRelationName(r)); RelationGetRelationName(r));
......
...@@ -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.31 2007/09/12 22:10:25 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/gist/gistvacuum.c,v 1.32 2007/09/20 17:56:30 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -201,7 +201,7 @@ vacuumSplitPage(GistVacuum *gv, Page tempPage, Buffer buffer, IndexTuple *addon, ...@@ -201,7 +201,7 @@ vacuumSplitPage(GistVacuum *gv, Page tempPage, Buffer buffer, IndexTuple *addon,
data = (char *) (ptr->list); data = (char *) (ptr->list);
for (i = 0; i < ptr->block.num; i++) for (i = 0; i < ptr->block.num; i++)
{ {
if (PageAddItem(ptr->page, (Item) data, IndexTupleSize((IndexTuple) data), i + FirstOffsetNumber, false) == InvalidOffsetNumber) if (PageAddItem(ptr->page, (Item) data, IndexTupleSize((IndexTuple) data), i + FirstOffsetNumber, false, false) == InvalidOffsetNumber)
elog(ERROR, "failed to add item to index page in \"%s\"", RelationGetRelationName(gv->index)); elog(ERROR, "failed to add item to index page in \"%s\"", RelationGetRelationName(gv->index));
data += IndexTupleSize((IndexTuple) data); data += IndexTupleSize((IndexTuple) data);
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/hash/hashinsert.c,v 1.46 2007/09/12 22:10:25 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/hash/hashinsert.c,v 1.47 2007/09/20 17:56:30 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -200,7 +200,7 @@ _hash_pgaddtup(Relation rel, ...@@ -200,7 +200,7 @@ _hash_pgaddtup(Relation rel,
page = BufferGetPage(buf); page = BufferGetPage(buf);
itup_off = OffsetNumberNext(PageGetMaxOffsetNumber(page)); itup_off = OffsetNumberNext(PageGetMaxOffsetNumber(page));
if (PageAddItem(page, (Item) itup, itemsize, itup_off, false) if (PageAddItem(page, (Item) itup, itemsize, itup_off, false, false)
== InvalidOffsetNumber) == InvalidOffsetNumber)
elog(ERROR, "failed to add index item to \"%s\"", elog(ERROR, "failed to add index item to \"%s\"",
RelationGetRelationName(rel)); RelationGetRelationName(rel));
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/hash/hashovfl.c,v 1.59 2007/09/12 22:10:25 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/hash/hashovfl.c,v 1.60 2007/09/20 17:56:30 tgl Exp $
* *
* NOTES * NOTES
* Overflow pages look like ordinary relation pages. * Overflow pages look like ordinary relation pages.
...@@ -684,7 +684,7 @@ _hash_squeezebucket(Relation rel, ...@@ -684,7 +684,7 @@ _hash_squeezebucket(Relation rel,
* we have found room so insert on the "write" page. * we have found room so insert on the "write" page.
*/ */
woffnum = OffsetNumberNext(PageGetMaxOffsetNumber(wpage)); woffnum = OffsetNumberNext(PageGetMaxOffsetNumber(wpage));
if (PageAddItem(wpage, (Item) itup, itemsz, woffnum, false) if (PageAddItem(wpage, (Item) itup, itemsz, woffnum, false, false)
== InvalidOffsetNumber) == InvalidOffsetNumber)
elog(ERROR, "failed to add index item to \"%s\"", elog(ERROR, "failed to add index item to \"%s\"",
RelationGetRelationName(rel)); RelationGetRelationName(rel));
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/hash/hashpage.c,v 1.69 2007/09/12 22:10:25 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/hash/hashpage.c,v 1.70 2007/09/20 17:56:30 tgl Exp $
* *
* NOTES * NOTES
* Postgres hash pages look like ordinary relation pages. The opaque * Postgres hash pages look like ordinary relation pages. The opaque
...@@ -830,7 +830,7 @@ _hash_splitbucket(Relation rel, ...@@ -830,7 +830,7 @@ _hash_splitbucket(Relation rel,
} }
noffnum = OffsetNumberNext(PageGetMaxOffsetNumber(npage)); noffnum = OffsetNumberNext(PageGetMaxOffsetNumber(npage));
if (PageAddItem(npage, (Item) itup, itemsz, noffnum, false) if (PageAddItem(npage, (Item) itup, itemsz, noffnum, false, false)
== InvalidOffsetNumber) == InvalidOffsetNumber)
elog(ERROR, "failed to add index item to \"%s\"", elog(ERROR, "failed to add index item to \"%s\"",
RelationGetRelationName(rel)); RelationGetRelationName(rel));
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# Makefile for access/heap # Makefile for access/heap
# #
# IDENTIFICATION # IDENTIFICATION
# $PostgreSQL: pgsql/src/backend/access/heap/Makefile,v 1.16 2007/06/08 18:23:52 tgl Exp $ # $PostgreSQL: pgsql/src/backend/access/heap/Makefile,v 1.17 2007/09/20 17:56:30 tgl Exp $
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
...@@ -12,7 +12,7 @@ subdir = src/backend/access/heap ...@@ -12,7 +12,7 @@ subdir = src/backend/access/heap
top_builddir = ../../../.. top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global include $(top_builddir)/src/Makefile.global
OBJS = heapam.o hio.o rewriteheap.o syncscan.o tuptoaster.o OBJS = heapam.o hio.o pruneheap.o rewriteheap.o syncscan.o tuptoaster.o
all: SUBSYS.o all: SUBSYS.o
......
This diff is collapsed.
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/heap/hio.c,v 1.66 2007/09/12 22:10:26 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/heap/hio.c,v 1.67 2007/09/20 17:56:30 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -41,7 +41,7 @@ RelationPutHeapTuple(Relation relation, ...@@ -41,7 +41,7 @@ RelationPutHeapTuple(Relation relation,
pageHeader = BufferGetPage(buffer); pageHeader = BufferGetPage(buffer);
offnum = PageAddItem(pageHeader, (Item) tuple->t_data, offnum = PageAddItem(pageHeader, (Item) tuple->t_data,
tuple->t_len, InvalidOffsetNumber, false); tuple->t_len, InvalidOffsetNumber, false, true);
if (offnum == InvalidOffsetNumber) if (offnum == InvalidOffsetNumber)
elog(PANIC, "failed to add tuple to page"); elog(PANIC, "failed to add tuple to page");
...@@ -218,7 +218,7 @@ RelationGetBufferForTuple(Relation relation, Size len, ...@@ -218,7 +218,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
* we're done. * we're done.
*/ */
pageHeader = (Page) BufferGetPage(buffer); pageHeader = (Page) BufferGetPage(buffer);
pageFreeSpace = PageGetFreeSpace(pageHeader); pageFreeSpace = PageGetHeapFreeSpace(pageHeader);
if (len + saveFreeSpace <= pageFreeSpace) if (len + saveFreeSpace <= pageFreeSpace)
{ {
/* use this page as future insert target, too */ /* use this page as future insert target, too */
...@@ -311,7 +311,7 @@ RelationGetBufferForTuple(Relation relation, Size len, ...@@ -311,7 +311,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
PageInit(pageHeader, BufferGetPageSize(buffer), 0); PageInit(pageHeader, BufferGetPageSize(buffer), 0);
if (len > PageGetFreeSpace(pageHeader)) if (len > PageGetHeapFreeSpace(pageHeader))
{ {
/* We should not get here given the test at the top */ /* We should not get here given the test at the top */
elog(PANIC, "tuple is too big: size %lu", (unsigned long) len); elog(PANIC, "tuple is too big: size %lu", (unsigned long) len);
......
This diff is collapsed.
...@@ -96,7 +96,7 @@ ...@@ -96,7 +96,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California * Portions Copyright (c) 1994-5, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/heap/rewriteheap.c,v 1.6 2007/09/12 22:10:26 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/heap/rewriteheap.c,v 1.7 2007/09/20 17:56:30 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -320,12 +320,14 @@ rewrite_heap_tuple(RewriteState state, ...@@ -320,12 +320,14 @@ rewrite_heap_tuple(RewriteState state,
* Copy the original tuple's visibility information into new_tuple. * Copy the original tuple's visibility information into new_tuple.
* *
* XXX we might later need to copy some t_infomask2 bits, too? * XXX we might later need to copy some t_infomask2 bits, too?
* Right now, we intentionally clear the HOT status bits.
*/ */
memcpy(&new_tuple->t_data->t_choice.t_heap, memcpy(&new_tuple->t_data->t_choice.t_heap,
&old_tuple->t_data->t_choice.t_heap, &old_tuple->t_data->t_choice.t_heap,
sizeof(HeapTupleFields)); sizeof(HeapTupleFields));
new_tuple->t_data->t_infomask &= ~HEAP_XACT_MASK; new_tuple->t_data->t_infomask &= ~HEAP_XACT_MASK;
new_tuple->t_data->t_infomask2 &= ~HEAP2_XACT_MASK;
new_tuple->t_data->t_infomask |= new_tuple->t_data->t_infomask |=
old_tuple->t_data->t_infomask & HEAP_XACT_MASK; old_tuple->t_data->t_infomask & HEAP_XACT_MASK;
...@@ -593,7 +595,7 @@ raw_heap_insert(RewriteState state, HeapTuple tup) ...@@ -593,7 +595,7 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
/* Now we can check to see if there's enough free space already. */ /* Now we can check to see if there's enough free space already. */
if (state->rs_buffer_valid) if (state->rs_buffer_valid)
{ {
pageFreeSpace = PageGetFreeSpace(page); pageFreeSpace = PageGetHeapFreeSpace(page);
if (len + saveFreeSpace > pageFreeSpace) if (len + saveFreeSpace > pageFreeSpace)
{ {
...@@ -628,7 +630,7 @@ raw_heap_insert(RewriteState state, HeapTuple tup) ...@@ -628,7 +630,7 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
/* And now we can insert the tuple into the page */ /* And now we can insert the tuple into the page */
newoff = PageAddItem(page, (Item) heaptup->t_data, len, newoff = PageAddItem(page, (Item) heaptup->t_data, len,
InvalidOffsetNumber, false); InvalidOffsetNumber, false, true);
if (newoff == InvalidOffsetNumber) if (newoff == InvalidOffsetNumber)
elog(ERROR, "failed to add tuple"); elog(ERROR, "failed to add tuple");
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.62 2007/05/27 03:50:38 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/index/genam.c,v 1.63 2007/09/20 17:56:30 tgl Exp $
* *
* NOTES * NOTES
* many of the old access method routines have been turned into * many of the old access method routines have been turned into
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "access/genam.h" #include "access/genam.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "access/transam.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "pgstat.h" #include "pgstat.h"
...@@ -95,6 +96,9 @@ RelationGetIndexScan(Relation indexRelation, ...@@ -95,6 +96,9 @@ RelationGetIndexScan(Relation indexRelation,
ItemPointerSetInvalid(&scan->xs_ctup.t_self); ItemPointerSetInvalid(&scan->xs_ctup.t_self);
scan->xs_ctup.t_data = NULL; scan->xs_ctup.t_data = NULL;
scan->xs_cbuf = InvalidBuffer; scan->xs_cbuf = InvalidBuffer;
scan->xs_prev_xmax = InvalidTransactionId;
scan->xs_next_hot = InvalidOffsetNumber;
scan->xs_hot_dead = false;
/* /*
* Let the AM fill in the key and any opaque data it wants. * Let the AM fill in the key and any opaque data it wants.
......
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.159 2007/09/12 22:10:26 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.160 2007/09/20 17:56:30 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -193,8 +193,6 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel, ...@@ -193,8 +193,6 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
*/ */
for (;;) for (;;)
{ {
HeapTupleData htup;
Buffer hbuffer;
ItemId curitemid; ItemId curitemid;
IndexTuple curitup; IndexTuple curitup;
BlockNumber nblkno; BlockNumber nblkno;
...@@ -223,6 +221,9 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel, ...@@ -223,6 +221,9 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
*/ */
if (!ItemIdIsDead(curitemid)) if (!ItemIdIsDead(curitemid))
{ {
ItemPointerData htid;
bool all_dead;
/* /*
* _bt_compare returns 0 for (1,NULL) and (1,NULL) - this's * _bt_compare returns 0 for (1,NULL) and (1,NULL) - this's
* how we handling NULLs - and so we must not use _bt_compare * how we handling NULLs - and so we must not use _bt_compare
...@@ -234,17 +235,20 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel, ...@@ -234,17 +235,20 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
/* okay, we gotta fetch the heap tuple ... */ /* okay, we gotta fetch the heap tuple ... */
curitup = (IndexTuple) PageGetItem(page, curitemid); curitup = (IndexTuple) PageGetItem(page, curitemid);
htup.t_self = curitup->t_tid; htid = curitup->t_tid;
if (heap_fetch(heapRel, &SnapshotDirty, &htup, &hbuffer,
true, NULL)) /*
* We check the whole HOT-chain to see if there is any tuple
* that satisfies SnapshotDirty. This is necessary because
* we have just a single index entry for the entire chain.
*/
if (heap_hot_search(&htid, heapRel, &SnapshotDirty, &all_dead))
{ {
/* it is a duplicate */ /* it is a duplicate */
TransactionId xwait = TransactionId xwait =
(TransactionIdIsValid(SnapshotDirty.xmin)) ? (TransactionIdIsValid(SnapshotDirty.xmin)) ?
SnapshotDirty.xmin : SnapshotDirty.xmax; SnapshotDirty.xmin : SnapshotDirty.xmax;
ReleaseBuffer(hbuffer);
/* /*
* If this tuple is being updated by other transaction * If this tuple is being updated by other transaction
* then we have to wait for its commit/abort. * then we have to wait for its commit/abort.
...@@ -263,15 +267,22 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel, ...@@ -263,15 +267,22 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
* is itself now committed dead --- if so, don't complain. * is itself now committed dead --- if so, don't complain.
* This is a waste of time in normal scenarios but we must * This is a waste of time in normal scenarios but we must
* do it to support CREATE INDEX CONCURRENTLY. * do it to support CREATE INDEX CONCURRENTLY.
*
* We must follow HOT-chains here because during
* concurrent index build, we insert the root TID though
* the actual tuple may be somewhere in the HOT-chain.
* While following the chain we might not stop at the exact
* tuple which triggered the insert, but that's OK because
* if we find a live tuple anywhere in this chain, we have
* a unique key conflict. The other live tuple is not part
* of this chain because it had a different index entry.
*/ */
htup.t_self = itup->t_tid; htid = itup->t_tid;
if (heap_fetch(heapRel, SnapshotSelf, &htup, &hbuffer, if (heap_hot_search(&htid, heapRel, SnapshotSelf, NULL))
false, NULL))
{ {
/* Normal case --- it's still live */ /* Normal case --- it's still live */
ReleaseBuffer(hbuffer);
} }
else if (htup.t_data != NULL) else
{ {
/* /*
* It's been deleted, so no error, and no need to * It's been deleted, so no error, and no need to
...@@ -279,39 +290,27 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel, ...@@ -279,39 +290,27 @@ _bt_check_unique(Relation rel, IndexTuple itup, Relation heapRel,
*/ */
break; break;
} }
else
{
/* couldn't find the tuple?? */
elog(ERROR, "failed to fetch tuple being inserted");
}
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNIQUE_VIOLATION), (errcode(ERRCODE_UNIQUE_VIOLATION),
errmsg("duplicate key value violates unique constraint \"%s\"", errmsg("duplicate key value violates unique constraint \"%s\"",
RelationGetRelationName(rel)))); RelationGetRelationName(rel))));
} }
else if (htup.t_data != NULL) else if (all_dead)
{ {
/* /*
* Hmm, if we can't see the tuple, maybe it can be marked * The conflicting tuple (or whole HOT chain) is dead to
* killed. This logic should match index_getnext and * everyone, so we may as well mark the index entry
* btgettuple. * killed.
*/ */
LockBuffer(hbuffer, BUFFER_LOCK_SHARE); ItemIdMarkDead(curitemid);
if (HeapTupleSatisfiesVacuum(htup.t_data, RecentGlobalXmin, opaque->btpo_flags |= BTP_HAS_GARBAGE;
hbuffer) == HEAPTUPLE_DEAD) /* be sure to mark the proper buffer dirty... */
{ if (nbuf != InvalidBuffer)
ItemIdMarkDead(curitemid); SetBufferCommitInfoNeedsSave(nbuf);
opaque->btpo_flags |= BTP_HAS_GARBAGE; else
/* be sure to mark the proper buffer dirty... */ SetBufferCommitInfoNeedsSave(buf);
if (nbuf != InvalidBuffer)
SetBufferCommitInfoNeedsSave(nbuf);
else
SetBufferCommitInfoNeedsSave(buf);
}
LockBuffer(hbuffer, BUFFER_LOCK_UNLOCK);
} }
ReleaseBuffer(hbuffer);
} }
} }
...@@ -840,7 +839,7 @@ _bt_split(Relation rel, Buffer buf, OffsetNumber firstright, ...@@ -840,7 +839,7 @@ _bt_split(Relation rel, Buffer buf, OffsetNumber firstright,
itemsz = ItemIdGetLength(itemid); itemsz = ItemIdGetLength(itemid);
item = (IndexTuple) PageGetItem(origpage, itemid); item = (IndexTuple) PageGetItem(origpage, itemid);
if (PageAddItem(rightpage, (Item) item, itemsz, rightoff, if (PageAddItem(rightpage, (Item) item, itemsz, rightoff,
false) == InvalidOffsetNumber) false, false) == InvalidOffsetNumber)
elog(PANIC, "failed to add hikey to the right sibling"); elog(PANIC, "failed to add hikey to the right sibling");
rightoff = OffsetNumberNext(rightoff); rightoff = OffsetNumberNext(rightoff);
} }
...@@ -865,7 +864,7 @@ _bt_split(Relation rel, Buffer buf, OffsetNumber firstright, ...@@ -865,7 +864,7 @@ _bt_split(Relation rel, Buffer buf, OffsetNumber firstright,
item = (IndexTuple) PageGetItem(origpage, itemid); item = (IndexTuple) PageGetItem(origpage, itemid);
} }
if (PageAddItem(leftpage, (Item) item, itemsz, leftoff, if (PageAddItem(leftpage, (Item) item, itemsz, leftoff,
false) == InvalidOffsetNumber) false, false) == InvalidOffsetNumber)
elog(PANIC, "failed to add hikey to the left sibling"); elog(PANIC, "failed to add hikey to the left sibling");
leftoff = OffsetNumberNext(leftoff); leftoff = OffsetNumberNext(leftoff);
...@@ -1700,7 +1699,7 @@ _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf) ...@@ -1700,7 +1699,7 @@ _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf)
* benefit of _bt_restore_page(). * benefit of _bt_restore_page().
*/ */
if (PageAddItem(rootpage, (Item) new_item, itemsz, P_HIKEY, if (PageAddItem(rootpage, (Item) new_item, itemsz, P_HIKEY,
false) == InvalidOffsetNumber) false, false) == InvalidOffsetNumber)
elog(PANIC, "failed to add leftkey to new root page"); elog(PANIC, "failed to add leftkey to new root page");
pfree(new_item); pfree(new_item);
...@@ -1718,7 +1717,7 @@ _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf) ...@@ -1718,7 +1717,7 @@ _bt_newroot(Relation rel, Buffer lbuf, Buffer rbuf)
* insert the right page pointer into the new root page. * insert the right page pointer into the new root page.
*/ */
if (PageAddItem(rootpage, (Item) new_item, itemsz, P_FIRSTKEY, if (PageAddItem(rootpage, (Item) new_item, itemsz, P_FIRSTKEY,
false) == InvalidOffsetNumber) false, false) == InvalidOffsetNumber)
elog(PANIC, "failed to add rightkey to new root page"); elog(PANIC, "failed to add rightkey to new root page");
pfree(new_item); pfree(new_item);
...@@ -1805,7 +1804,7 @@ _bt_pgaddtup(Relation rel, ...@@ -1805,7 +1804,7 @@ _bt_pgaddtup(Relation rel,
} }
if (PageAddItem(page, (Item) itup, itemsize, itup_off, if (PageAddItem(page, (Item) itup, itemsize, itup_off,
false) == InvalidOffsetNumber) false, false) == InvalidOffsetNumber)
elog(PANIC, "failed to add item to the %s for \"%s\"", elog(PANIC, "failed to add item to the %s for \"%s\"",
where, RelationGetRelationName(rel)); where, RelationGetRelationName(rel));
} }
......
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,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/nbtsort.c,v 1.112 2007/09/12 22:10:26 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsort.c,v 1.113 2007/09/20 17:56:30 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -400,7 +400,7 @@ _bt_sortaddtup(Page page, ...@@ -400,7 +400,7 @@ _bt_sortaddtup(Page page,
} }
if (PageAddItem(page, (Item) itup, itemsize, itup_off, if (PageAddItem(page, (Item) itup, itemsize, itup_off,
false) == InvalidOffsetNumber) false, false) == InvalidOffsetNumber)
elog(ERROR, "failed to add item to the index page"); elog(ERROR, "failed to add item to the index page");
} }
......
...@@ -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/nbtree/nbtxlog.c,v 1.45 2007/09/12 22:10:26 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtxlog.c,v 1.46 2007/09/20 17:56:30 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -141,8 +141,8 @@ _bt_restore_page(Page page, char *from, int len) ...@@ -141,8 +141,8 @@ _bt_restore_page(Page page, char *from, int len)
memcpy(&itupdata, from, sizeof(IndexTupleData)); memcpy(&itupdata, from, sizeof(IndexTupleData));
itemsz = IndexTupleDSize(itupdata); itemsz = IndexTupleDSize(itupdata);
itemsz = MAXALIGN(itemsz); itemsz = MAXALIGN(itemsz);
if (PageAddItem(page, (Item) from, itemsz, if (PageAddItem(page, (Item) from, itemsz, FirstOffsetNumber,
FirstOffsetNumber, false) == InvalidOffsetNumber) false, false) == InvalidOffsetNumber)
elog(PANIC, "_bt_restore_page: cannot add item to page"); elog(PANIC, "_bt_restore_page: cannot add item to page");
from += itemsz; from += itemsz;
} }
...@@ -238,7 +238,7 @@ btree_xlog_insert(bool isleaf, bool ismeta, ...@@ -238,7 +238,7 @@ btree_xlog_insert(bool isleaf, bool ismeta,
{ {
if (PageAddItem(page, (Item) datapos, datalen, if (PageAddItem(page, (Item) datapos, datalen,
ItemPointerGetOffsetNumber(&(xlrec->target.tid)), ItemPointerGetOffsetNumber(&(xlrec->target.tid)),
false) == InvalidOffsetNumber) false, false) == InvalidOffsetNumber)
elog(PANIC, "btree_insert_redo: failed to add item"); elog(PANIC, "btree_insert_redo: failed to add item");
PageSetLSN(page, lsn); PageSetLSN(page, lsn);
...@@ -389,7 +389,7 @@ btree_xlog_split(bool onleft, bool isroot, ...@@ -389,7 +389,7 @@ btree_xlog_split(bool onleft, bool isroot,
if (onleft) if (onleft)
{ {
if (PageAddItem(lpage, newitem, newitemsz, newitemoff, if (PageAddItem(lpage, newitem, newitemsz, newitemoff,
false) == InvalidOffsetNumber) false, false) == InvalidOffsetNumber)
elog(PANIC, "failed to add new item to left page after split"); elog(PANIC, "failed to add new item to left page after split");
} }
...@@ -398,7 +398,7 @@ btree_xlog_split(bool onleft, bool isroot, ...@@ -398,7 +398,7 @@ btree_xlog_split(bool onleft, bool isroot,
hiItem = PageGetItem(rpage, hiItemId); hiItem = PageGetItem(rpage, hiItemId);
if (PageAddItem(lpage, hiItem, ItemIdGetLength(hiItemId), if (PageAddItem(lpage, hiItem, ItemIdGetLength(hiItemId),
P_HIKEY, false) == InvalidOffsetNumber) P_HIKEY, false, false) == InvalidOffsetNumber)
elog(PANIC, "failed to add high key to left page after split"); elog(PANIC, "failed to add high key to left page after split");
/* Fix opaque fields */ /* Fix opaque fields */
......
This diff is collapsed.
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/indexing.c,v 1.114 2007/01/05 22:19:24 momjian Exp $ * $PostgreSQL: pgsql/src/backend/catalog/indexing.c,v 1.115 2007/09/20 17:56:30 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -78,6 +78,10 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple) ...@@ -78,6 +78,10 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
Datum values[INDEX_MAX_KEYS]; Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS];
/* HOT update does not require index inserts */
if (HeapTupleIsHeapOnly(heapTuple))
return;
/* /*
* Get information from the state structure. Fall out if nothing to do. * Get information from the state structure. Fall out if nothing to do.
*/ */
...@@ -101,6 +105,10 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple) ...@@ -101,6 +105,10 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
indexInfo = indexInfoArray[i]; indexInfo = indexInfoArray[i];
/* If the index is marked as read-only, ignore it */
if (!indexInfo->ii_ReadyForInserts)
continue;
/* /*
* Expressional and partial indexes on system catalogs are not * Expressional and partial indexes on system catalogs are not
* supported * supported
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Copyright (c) 1996-2007, PostgreSQL Global Development Group * Copyright (c) 1996-2007, PostgreSQL Global Development Group
* *
* $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.44 2007/09/11 08:51:22 teodor Exp $ * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.45 2007/09/20 17:56:30 tgl Exp $
*/ */
CREATE VIEW pg_roles AS CREATE VIEW pg_roles AS
...@@ -207,6 +207,7 @@ CREATE VIEW pg_stat_all_tables AS ...@@ -207,6 +207,7 @@ CREATE VIEW pg_stat_all_tables AS
pg_stat_get_tuples_inserted(C.oid) AS n_tup_ins, pg_stat_get_tuples_inserted(C.oid) AS n_tup_ins,
pg_stat_get_tuples_updated(C.oid) AS n_tup_upd, pg_stat_get_tuples_updated(C.oid) AS n_tup_upd,
pg_stat_get_tuples_deleted(C.oid) AS n_tup_del, pg_stat_get_tuples_deleted(C.oid) AS n_tup_del,
pg_stat_get_tuples_hot_updated(C.oid) AS n_tup_hot_upd,
pg_stat_get_live_tuples(C.oid) AS n_live_tup, pg_stat_get_live_tuples(C.oid) AS n_live_tup,
pg_stat_get_dead_tuples(C.oid) AS n_dead_tup, pg_stat_get_dead_tuples(C.oid) AS n_dead_tup,
pg_stat_get_last_vacuum_time(C.oid) as last_vacuum, pg_stat_get_last_vacuum_time(C.oid) as last_vacuum,
......
...@@ -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/catalog/toasting.c,v 1.7 2007/07/25 22:16:18 tgl Exp $ * $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.8 2007/09/20 17:56:30 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -225,7 +225,9 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid) ...@@ -225,7 +225,9 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid)
indexInfo->ii_Predicate = NIL; indexInfo->ii_Predicate = NIL;
indexInfo->ii_PredicateState = NIL; indexInfo->ii_PredicateState = NIL;
indexInfo->ii_Unique = true; indexInfo->ii_Unique = true;
indexInfo->ii_ReadyForInserts = true;
indexInfo->ii_Concurrent = false; indexInfo->ii_Concurrent = false;
indexInfo->ii_BrokenHotChain = false;
classObjectId[0] = OID_BTREE_OPS_OID; classObjectId[0] = OID_BTREE_OPS_OID;
classObjectId[1] = INT4_BTREE_OPS_OID; classObjectId[1] = INT4_BTREE_OPS_OID;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.165 2007/09/10 21:59:37 alvherre Exp $ * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.166 2007/09/20 17:56:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -119,6 +119,7 @@ DefineIndex(RangeVar *heapRelation, ...@@ -119,6 +119,7 @@ DefineIndex(RangeVar *heapRelation,
Oid namespaceId; Oid namespaceId;
Oid tablespaceId; Oid tablespaceId;
Relation rel; Relation rel;
Relation indexRelation;
HeapTuple tuple; HeapTuple tuple;
Form_pg_am accessMethodForm; Form_pg_am accessMethodForm;
bool amcanorder; bool amcanorder;
...@@ -420,7 +421,10 @@ DefineIndex(RangeVar *heapRelation, ...@@ -420,7 +421,10 @@ DefineIndex(RangeVar *heapRelation,
indexInfo->ii_Predicate = make_ands_implicit(predicate); indexInfo->ii_Predicate = make_ands_implicit(predicate);
indexInfo->ii_PredicateState = NIL; indexInfo->ii_PredicateState = NIL;
indexInfo->ii_Unique = unique; indexInfo->ii_Unique = unique;
/* In a concurrent build, mark it not-ready-for-inserts */
indexInfo->ii_ReadyForInserts = !concurrent;
indexInfo->ii_Concurrent = concurrent; indexInfo->ii_Concurrent = concurrent;
indexInfo->ii_BrokenHotChain = false;
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid)); classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16)); coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
...@@ -439,23 +443,38 @@ DefineIndex(RangeVar *heapRelation, ...@@ -439,23 +443,38 @@ DefineIndex(RangeVar *heapRelation,
primary ? "PRIMARY KEY" : "UNIQUE", primary ? "PRIMARY KEY" : "UNIQUE",
indexRelationName, RelationGetRelationName(rel)))); indexRelationName, RelationGetRelationName(rel))));
/* save lockrelid for below, then close rel */ /* save lockrelid and locktag for below, then close rel */
heaprelid = rel->rd_lockInfo.lockRelId; heaprelid = rel->rd_lockInfo.lockRelId;
SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId);
heap_close(rel, NoLock); heap_close(rel, NoLock);
if (!concurrent)
{
indexRelationId =
index_create(relationId, indexRelationName, indexRelationId,
indexInfo, accessMethodId, tablespaceId, classObjectId,
coloptions, reloptions, primary, isconstraint,
allowSystemTableMods, skip_build, concurrent);
return; /* We're done, in the standard case */
}
/*
* For a concurrent build, we next insert the catalog entry and add
* constraints. We don't build the index just yet; we must first make
* the catalog entry so that the new index is visible to updating
* transactions. That will prevent them from making incompatible HOT
* updates. The new index will be marked not indisready and not
* indisvalid, so that no one else tries to either insert into it or use
* it for queries. We pass skip_build = true to prevent the build.
*/
indexRelationId = indexRelationId =
index_create(relationId, indexRelationName, indexRelationId, index_create(relationId, indexRelationName, indexRelationId,
indexInfo, accessMethodId, tablespaceId, classObjectId, indexInfo, accessMethodId, tablespaceId, classObjectId,
coloptions, reloptions, primary, isconstraint, coloptions, reloptions, primary, isconstraint,
allowSystemTableMods, skip_build, concurrent); allowSystemTableMods, true, concurrent);
if (!concurrent)
return; /* We're done, in the standard case */
/* /*
* Phase 2 of concurrent index build (see comments for validate_index()
* for an overview of how this works)
*
* We must commit our current transaction so that the index becomes * We must commit our current transaction so that the index becomes
* visible; then start another. Note that all the data structures we just * visible; then start another. Note that all the data structures we just
* built are lost in the commit. The only data we keep past here are the * built are lost in the commit. The only data we keep past here are the
...@@ -476,6 +495,9 @@ DefineIndex(RangeVar *heapRelation, ...@@ -476,6 +495,9 @@ DefineIndex(RangeVar *heapRelation,
StartTransactionCommand(); StartTransactionCommand();
/* /*
* Phase 2 of concurrent index build (see comments for validate_index()
* for an overview of how this works)
*
* Now we must wait until no running transaction could have the table open * Now we must wait until no running transaction could have the table open
* with the old list of indexes. To do this, inquire which xacts * with the old list of indexes. To do this, inquire which xacts
* currently would conflict with ShareLock on the table -- ie, which ones * currently would conflict with ShareLock on the table -- ie, which ones
...@@ -494,7 +516,91 @@ DefineIndex(RangeVar *heapRelation, ...@@ -494,7 +516,91 @@ DefineIndex(RangeVar *heapRelation,
* check for that. Also, prepared xacts are not reported, which is * check for that. Also, prepared xacts are not reported, which is
* fine since they certainly aren't going to do anything more. * fine since they certainly aren't going to do anything more.
*/ */
SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId); old_lockholders = GetLockConflicts(&heaplocktag, ShareLock);
while (VirtualTransactionIdIsValid(*old_lockholders))
{
VirtualXactLockTableWait(*old_lockholders);
old_lockholders++;
}
/*
* At this moment we are sure that there are no transactions with the
* table open for write that don't have this new index in their list of
* indexes. We have waited out all the existing transactions and any new
* transaction will have the new index in its list, but the index is still
* marked as "not-ready-for-inserts". The index is consulted while
* deciding HOT-safety though. This arrangement ensures that no new HOT
* chains can be created where the new tuple and the old tuple in the
* chain have different index keys.
*
* We now take a new snapshot, and build the index using all tuples that
* are visible in this snapshot. We can be sure that any HOT updates
* to these tuples will be compatible with the index, since any updates
* made by transactions that didn't know about the index are now committed
* or rolled back. Thus, each visible tuple is either the end of its
* HOT-chain or the extension of the chain is HOT-safe for this index.
*/
/* Open and lock the parent heap relation */
rel = heap_openrv(heapRelation, ShareUpdateExclusiveLock);
/* And the target index relation */
indexRelation = index_open(indexRelationId, RowExclusiveLock);
/* Set ActiveSnapshot since functions in the indexes may need it */
ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
/* We have to re-build the IndexInfo struct, since it was lost in commit */
indexInfo = BuildIndexInfo(indexRelation);
Assert(!indexInfo->ii_ReadyForInserts);
indexInfo->ii_Concurrent = true;
indexInfo->ii_BrokenHotChain = false;
/* Now build the index */
index_build(rel, indexRelation, indexInfo, primary);
/* Close both the relations, but keep the locks */
heap_close(rel, NoLock);
index_close(indexRelation, NoLock);
/*
* Update the pg_index row to mark the index as ready for inserts.
* Once we commit this transaction, any new transactions that
* open the table must insert new entries into the index for insertions
* and non-HOT updates.
*/
pg_index = heap_open(IndexRelationId, RowExclusiveLock);
indexTuple = SearchSysCacheCopy(INDEXRELID,
ObjectIdGetDatum(indexRelationId),
0, 0, 0);
if (!HeapTupleIsValid(indexTuple))
elog(ERROR, "cache lookup failed for index %u", indexRelationId);
indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
Assert(!indexForm->indisready);
Assert(!indexForm->indisvalid);
indexForm->indisready = true;
simple_heap_update(pg_index, &indexTuple->t_self, indexTuple);
CatalogUpdateIndexes(pg_index, indexTuple);
heap_close(pg_index, RowExclusiveLock);
/*
* Commit this transaction to make the indisready update visible.
*/
CommitTransactionCommand();
StartTransactionCommand();
/*
* Phase 3 of concurrent index build
*
* We once again wait until no transaction can have the table open with
* the index marked as read-only for updates.
*/
old_lockholders = GetLockConflicts(&heaplocktag, ShareLock); old_lockholders = GetLockConflicts(&heaplocktag, ShareLock);
while (VirtualTransactionIdIsValid(*old_lockholders)) while (VirtualTransactionIdIsValid(*old_lockholders))
...@@ -505,7 +611,7 @@ DefineIndex(RangeVar *heapRelation, ...@@ -505,7 +611,7 @@ DefineIndex(RangeVar *heapRelation,
/* /*
* Now take the "reference snapshot" that will be used by validate_index() * Now take the "reference snapshot" that will be used by validate_index()
* to filter candidate tuples. Beware! There might be still snapshots * to filter candidate tuples. Beware! There might still be snapshots
* in use that treat some transaction as in-progress that our reference * in use that treat some transaction as in-progress that our reference
* snapshot treats as committed. If such a recently-committed transaction * snapshot treats as committed. If such a recently-committed transaction
* deleted tuples in the table, we will not include them in the index; yet * deleted tuples in the table, we will not include them in the index; yet
...@@ -560,7 +666,7 @@ DefineIndex(RangeVar *heapRelation, ...@@ -560,7 +666,7 @@ DefineIndex(RangeVar *heapRelation,
elog(ERROR, "cache lookup failed for index %u", indexRelationId); elog(ERROR, "cache lookup failed for index %u", indexRelationId);
indexForm = (Form_pg_index) GETSTRUCT(indexTuple); indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
Assert(indexForm->indexrelid = indexRelationId); Assert(indexForm->indisready);
Assert(!indexForm->indisvalid); Assert(!indexForm->indisvalid);
indexForm->indisvalid = true; indexForm->indisvalid = true;
...@@ -575,7 +681,8 @@ DefineIndex(RangeVar *heapRelation, ...@@ -575,7 +681,8 @@ DefineIndex(RangeVar *heapRelation,
* relcache entries for the index itself, but we should also send a * relcache entries for the index itself, but we should also send a
* relcache inval on the parent table to force replanning of cached plans. * relcache inval on the parent table to force replanning of cached plans.
* Otherwise existing sessions might fail to use the new index where it * Otherwise existing sessions might fail to use the new index where it
* would be useful. * would be useful. (Note that our earlier commits did not create
* reasons to replan; relcache flush on the index itself was sufficient.)
*/ */
CacheInvalidateRelcacheByRelid(heaprelid.relId); CacheInvalidateRelcacheByRelid(heaprelid.relId);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.145 2007/09/12 22:10:26 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.146 2007/09/20 17:56:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1281,7 +1281,7 @@ seq_redo(XLogRecPtr lsn, XLogRecord *record) ...@@ -1281,7 +1281,7 @@ seq_redo(XLogRecPtr lsn, XLogRecord *record)
itemsz = record->xl_len - sizeof(xl_seq_rec); itemsz = record->xl_len - sizeof(xl_seq_rec);
itemsz = MAXALIGN(itemsz); itemsz = MAXALIGN(itemsz);
if (PageAddItem(page, (Item) item, itemsz, if (PageAddItem(page, (Item) item, itemsz,
FirstOffsetNumber, false) == InvalidOffsetNumber) FirstOffsetNumber, false, false) == InvalidOffsetNumber)
elog(PANIC, "seq_redo: failed to add item to page"); elog(PANIC, "seq_redo: failed to add item to page");
PageSetLSN(page, lsn); PageSetLSN(page, lsn);
......
This diff is collapsed.
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.96 2007/09/16 02:37:46 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.97 2007/09/20 17:56:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -326,8 +326,8 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -326,8 +326,8 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
buf = ReadBufferWithStrategy(onerel, blkno, vac_strategy); buf = ReadBufferWithStrategy(onerel, blkno, vac_strategy);
/* Initially, we only need shared access to the buffer */ /* We need buffer cleanup lock so that we can prune HOT chains. */
LockBuffer(buf, BUFFER_LOCK_SHARE); LockBufferForCleanup(buf);
page = BufferGetPage(buf); page = BufferGetPage(buf);
...@@ -341,11 +341,10 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -341,11 +341,10 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
* We have to be careful here because we could be looking at a * We have to be careful here because we could be looking at a
* page that someone has just added to the relation and not yet * page that someone has just added to the relation and not yet
* been able to initialize (see RelationGetBufferForTuple). To * been able to initialize (see RelationGetBufferForTuple). To
* interlock against that, release the buffer read lock (which we * protect against that, release the buffer lock, grab the
* must do anyway) and grab the relation extension lock before * relation extension lock momentarily, and re-lock the buffer.
* re-locking in exclusive mode. If the page is still * If the page is still uninitialized by then, it must be left
* uninitialized by then, it must be left over from a crashed * over from a crashed backend, and we can initialize it.
* backend, and we can initialize it.
* *
* We don't really need the relation lock when this is a new or * We don't really need the relation lock when this is a new or
* temp relation, but it's probably not worth the code space to * temp relation, but it's probably not worth the code space to
...@@ -357,7 +356,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -357,7 +356,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
LockBuffer(buf, BUFFER_LOCK_UNLOCK); LockBuffer(buf, BUFFER_LOCK_UNLOCK);
LockRelationForExtension(onerel, ExclusiveLock); LockRelationForExtension(onerel, ExclusiveLock);
UnlockRelationForExtension(onerel, ExclusiveLock); UnlockRelationForExtension(onerel, ExclusiveLock);
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); LockBufferForCleanup(buf);
if (PageIsNew(page)) if (PageIsNew(page))
{ {
ereport(WARNING, ereport(WARNING,
...@@ -366,7 +365,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -366,7 +365,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
PageInit(page, BufferGetPageSize(buf), 0); PageInit(page, BufferGetPageSize(buf), 0);
empty_pages++; empty_pages++;
lazy_record_free_space(vacrelstats, blkno, lazy_record_free_space(vacrelstats, blkno,
PageGetFreeSpace(page)); PageGetHeapFreeSpace(page));
} }
MarkBufferDirty(buf); MarkBufferDirty(buf);
UnlockReleaseBuffer(buf); UnlockReleaseBuffer(buf);
...@@ -377,11 +376,23 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -377,11 +376,23 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
{ {
empty_pages++; empty_pages++;
lazy_record_free_space(vacrelstats, blkno, lazy_record_free_space(vacrelstats, blkno,
PageGetFreeSpace(page)); PageGetHeapFreeSpace(page));
UnlockReleaseBuffer(buf); UnlockReleaseBuffer(buf);
continue; continue;
} }
/*
* Prune all HOT-update chains in this page.
*
* We count tuples removed by the pruning step as removed by VACUUM.
*/
tups_vacuumed += heap_page_prune(onerel, buf, OldestXmin,
false, false);
/*
* Now scan the page to collect vacuumable items and check for
* tuples requiring freezing.
*/
nfrozen = 0; nfrozen = 0;
hastup = false; hastup = false;
prev_dead_count = vacrelstats->num_dead_tuples; prev_dead_count = vacrelstats->num_dead_tuples;
...@@ -394,22 +405,64 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -394,22 +405,64 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
itemid = PageGetItemId(page, offnum); itemid = PageGetItemId(page, offnum);
/* Unused items require no processing, but we count 'em */
if (!ItemIdIsUsed(itemid)) if (!ItemIdIsUsed(itemid))
{ {
nunused += 1; nunused += 1;
continue; continue;
} }
/* Redirect items mustn't be touched */
if (ItemIdIsRedirected(itemid))
{
hastup = true; /* this page won't be truncatable */
continue;
}
ItemPointerSet(&(tuple.t_self), blkno, offnum);
/*
* DEAD item pointers are to be vacuumed normally; but we don't
* count them in tups_vacuumed, else we'd be double-counting
* (at least in the common case where heap_page_prune() just
* freed up a non-HOT tuple).
*/
if (ItemIdIsDead(itemid))
{
lazy_record_dead_tuple(vacrelstats, &(tuple.t_self));
continue;
}
Assert(ItemIdIsNormal(itemid));
tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid); tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
tuple.t_len = ItemIdGetLength(itemid); tuple.t_len = ItemIdGetLength(itemid);
ItemPointerSet(&(tuple.t_self), blkno, offnum);
tupgone = false; tupgone = false;
switch (HeapTupleSatisfiesVacuum(tuple.t_data, OldestXmin, buf)) switch (HeapTupleSatisfiesVacuum(tuple.t_data, OldestXmin, buf))
{ {
case HEAPTUPLE_DEAD: case HEAPTUPLE_DEAD:
tupgone = true; /* we can delete the tuple */ /*
* Ordinarily, DEAD tuples would have been removed by
* heap_page_prune(), but it's possible that the tuple
* state changed since heap_page_prune() looked. In
* particular an INSERT_IN_PROGRESS tuple could have
* changed to DEAD if the inserter aborted. So this
* cannot be considered an error condition.
*
* If the tuple is HOT-updated then it must only be
* removed by a prune operation; so we keep it just as
* if it were RECENTLY_DEAD. Also, if it's a heap-only
* tuple, we choose to keep it, because it'll be a
* lot cheaper to get rid of it in the next pruning pass
* than to treat it like an indexed tuple.
*/
if (HeapTupleIsHotUpdated(&tuple) ||
HeapTupleIsHeapOnly(&tuple))
nkeep += 1;
else
tupgone = true; /* we can delete the tuple */
break; break;
case HEAPTUPLE_LIVE: case HEAPTUPLE_LIVE:
/* Tuple is good --- but let's do some validity checks */ /* Tuple is good --- but let's do some validity checks */
...@@ -449,11 +502,10 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -449,11 +502,10 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
/* /*
* Each non-removable tuple must be checked to see if it * Each non-removable tuple must be checked to see if it
* needs freezing. If we already froze anything, then * needs freezing. Note we already have exclusive buffer lock.
* we've already switched the buffer lock to exclusive.
*/ */
if (heap_freeze_tuple(tuple.t_data, FreezeLimit, if (heap_freeze_tuple(tuple.t_data, FreezeLimit,
(nfrozen > 0) ? InvalidBuffer : buf)) InvalidBuffer))
frozen[nfrozen++] = offnum; frozen[nfrozen++] = offnum;
} }
} /* scan along page */ } /* scan along page */
...@@ -485,9 +537,6 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -485,9 +537,6 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
if (nindexes == 0 && if (nindexes == 0 &&
vacrelstats->num_dead_tuples > 0) vacrelstats->num_dead_tuples > 0)
{ {
/* Trade in buffer share lock for super-exclusive lock */
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
LockBufferForCleanup(buf);
/* Remove tuples from heap */ /* Remove tuples from heap */
lazy_vacuum_page(onerel, blkno, buf, 0, vacrelstats); lazy_vacuum_page(onerel, blkno, buf, 0, vacrelstats);
/* Forget the now-vacuumed tuples, and press on */ /* Forget the now-vacuumed tuples, and press on */
...@@ -505,7 +554,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, ...@@ -505,7 +554,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
if (vacrelstats->num_dead_tuples == prev_dead_count) if (vacrelstats->num_dead_tuples == prev_dead_count)
{ {
lazy_record_free_space(vacrelstats, blkno, lazy_record_free_space(vacrelstats, blkno,
PageGetFreeSpace(page)); PageGetHeapFreeSpace(page));
} }
/* Remember the location of the last page with nonremovable tuples */ /* Remember the location of the last page with nonremovable tuples */
...@@ -598,7 +647,7 @@ lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats) ...@@ -598,7 +647,7 @@ lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats)
/* Now that we've compacted the page, record its available space */ /* Now that we've compacted the page, record its available space */
page = BufferGetPage(buf); page = BufferGetPage(buf);
lazy_record_free_space(vacrelstats, tblk, lazy_record_free_space(vacrelstats, tblk,
PageGetFreeSpace(page)); PageGetHeapFreeSpace(page));
UnlockReleaseBuffer(buf); UnlockReleaseBuffer(buf);
npages++; npages++;
} }
...@@ -615,7 +664,7 @@ lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats) ...@@ -615,7 +664,7 @@ lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats)
* lazy_vacuum_page() -- free dead tuples on a page * lazy_vacuum_page() -- free dead tuples on a page
* and repair its fragmentation. * and repair its fragmentation.
* *
* Caller must hold pin and lock on the buffer. * Caller must hold pin and buffer cleanup lock on the buffer.
* *
* tupindex is the index in vacrelstats->dead_tuples of the first dead * tupindex is the index in vacrelstats->dead_tuples of the first dead
* tuple for this page. We assume the rest follow sequentially. * tuple for this page. We assume the rest follow sequentially.
...@@ -625,10 +674,9 @@ static int ...@@ -625,10 +674,9 @@ static int
lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer, lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
int tupindex, LVRelStats *vacrelstats) int tupindex, LVRelStats *vacrelstats)
{ {
OffsetNumber unused[MaxOffsetNumber];
int uncnt;
Page page = BufferGetPage(buffer); Page page = BufferGetPage(buffer);
ItemId itemid; OffsetNumber unused[MaxOffsetNumber];
int uncnt = 0;
START_CRIT_SECTION(); START_CRIT_SECTION();
...@@ -636,6 +684,7 @@ lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer, ...@@ -636,6 +684,7 @@ lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
{ {
BlockNumber tblk; BlockNumber tblk;
OffsetNumber toff; OffsetNumber toff;
ItemId itemid;
tblk = ItemPointerGetBlockNumber(&vacrelstats->dead_tuples[tupindex]); tblk = ItemPointerGetBlockNumber(&vacrelstats->dead_tuples[tupindex]);
if (tblk != blkno) if (tblk != blkno)
...@@ -643,9 +692,10 @@ lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer, ...@@ -643,9 +692,10 @@ lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
toff = ItemPointerGetOffsetNumber(&vacrelstats->dead_tuples[tupindex]); toff = ItemPointerGetOffsetNumber(&vacrelstats->dead_tuples[tupindex]);
itemid = PageGetItemId(page, toff); itemid = PageGetItemId(page, toff);
ItemIdSetUnused(itemid); ItemIdSetUnused(itemid);
unused[uncnt++] = toff;
} }
uncnt = PageRepairFragmentation(page, unused); PageRepairFragmentation(page);
MarkBufferDirty(buffer); MarkBufferDirty(buffer);
...@@ -654,7 +704,10 @@ lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer, ...@@ -654,7 +704,10 @@ lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
{ {
XLogRecPtr recptr; XLogRecPtr recptr;
recptr = log_heap_clean(onerel, buffer, unused, uncnt); recptr = log_heap_clean(onerel, buffer,
NULL, 0, NULL, 0,
unused, uncnt,
false);
PageSetLSN(page, recptr); PageSetLSN(page, recptr);
PageSetTLI(page, ThisTimeLineID); PageSetTLI(page, ThisTimeLineID);
} }
...@@ -980,7 +1033,7 @@ lazy_record_dead_tuple(LVRelStats *vacrelstats, ...@@ -980,7 +1033,7 @@ lazy_record_dead_tuple(LVRelStats *vacrelstats,
/* /*
* The array shouldn't overflow under normal behavior, but perhaps it * The array shouldn't overflow under normal behavior, but perhaps it
* could if we are given a really small maintenance_work_mem. In that * could if we are given a really small maintenance_work_mem. In that
* case, just forget the last few tuples. * case, just forget the last few tuples (we'll get 'em next time).
*/ */
if (vacrelstats->num_dead_tuples < vacrelstats->max_dead_tuples) if (vacrelstats->num_dead_tuples < vacrelstats->max_dead_tuples)
{ {
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.297 2007/09/07 20:59:26 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.298 2007/09/20 17:56:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1813,8 +1813,10 @@ lreplace:; ...@@ -1813,8 +1813,10 @@ lreplace:;
* *
* Note: heap_update returns the tid (location) of the new tuple in the * Note: heap_update returns the tid (location) of the new tuple in the
* t_self field. * t_self field.
*
* If it's a HOT update, we mustn't insert new index entries.
*/ */
if (resultRelInfo->ri_NumIndices > 0) if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
/* AFTER ROW UPDATE Triggers */ /* AFTER ROW UPDATE Triggers */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.150 2007/08/15 21:39:50 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.151 2007/09/20 17:56:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -981,6 +981,10 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo) ...@@ -981,6 +981,10 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo)
* stuff as it only exists here because the genam stuff * stuff as it only exists here because the genam stuff
* doesn't provide the functionality needed by the * doesn't provide the functionality needed by the
* executor.. -cim 9/27/89 * executor.. -cim 9/27/89
*
* CAUTION: this must not be called for a HOT update.
* We can't defend against that here for lack of info.
* Should we change the API to make it safer?
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
void void
...@@ -1029,6 +1033,10 @@ ExecInsertIndexTuples(TupleTableSlot *slot, ...@@ -1029,6 +1033,10 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
indexInfo = indexInfoArray[i]; indexInfo = indexInfoArray[i];
/* If the index is marked as read-only, ignore it */
if (!indexInfo->ii_ReadyForInserts)
continue;
/* Check for partial index */ /* Check for partial index */
if (indexInfo->ii_Predicate != NIL) if (indexInfo->ii_Predicate != NIL)
{ {
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeBitmapHeapscan.c,v 1.19 2007/09/12 22:10:26 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/nodeBitmapHeapscan.c,v 1.20 2007/09/20 17:56:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -240,12 +240,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres) ...@@ -240,12 +240,7 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
BlockNumber page = tbmres->blockno; BlockNumber page = tbmres->blockno;
Buffer buffer; Buffer buffer;
Snapshot snapshot; Snapshot snapshot;
Page dp;
int ntup; int ntup;
int curslot;
int minslot;
int maxslot;
int maxoff;
/* /*
* Acquire pin on the target heap page, trading in any pin we held before. * Acquire pin on the target heap page, trading in any pin we held before.
...@@ -258,6 +253,13 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres) ...@@ -258,6 +253,13 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
buffer = scan->rs_cbuf; buffer = scan->rs_cbuf;
snapshot = scan->rs_snapshot; snapshot = scan->rs_snapshot;
ntup = 0;
/*
* Prune and repair fragmentation for the whole page, if possible.
*/
heap_page_prune_opt(scan->rs_rd, buffer, RecentGlobalXmin);
/* /*
* We must hold share lock on the buffer content while examining tuple * We must hold share lock on the buffer content while examining tuple
* visibility. Afterwards, however, the tuples we have found to be * visibility. Afterwards, however, the tuples we have found to be
...@@ -265,71 +267,51 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres) ...@@ -265,71 +267,51 @@ bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
*/ */
LockBuffer(buffer, BUFFER_LOCK_SHARE); LockBuffer(buffer, BUFFER_LOCK_SHARE);
dp = (Page) BufferGetPage(buffer);
maxoff = PageGetMaxOffsetNumber(dp);
/* /*
* Determine how many entries we need to look at on this page. If the * We need two separate strategies for lossy and non-lossy cases.
* bitmap is lossy then we need to look at each physical item pointer;
* otherwise we just look through the offsets listed in tbmres.
*/ */
if (tbmres->ntuples >= 0) if (tbmres->ntuples >= 0)
{ {
/* non-lossy case */
minslot = 0;
maxslot = tbmres->ntuples - 1;
}
else
{
/* lossy case */
minslot = FirstOffsetNumber;
maxslot = maxoff;
}
ntup = 0;
for (curslot = minslot; curslot <= maxslot; curslot++)
{
OffsetNumber targoffset;
ItemId lp;
HeapTupleData loctup;
bool valid;
if (tbmres->ntuples >= 0)
{
/* non-lossy case */
targoffset = tbmres->offsets[curslot];
}
else
{
/* lossy case */
targoffset = (OffsetNumber) curslot;
}
/* /*
* We'd better check for out-of-range offnum in case of VACUUM since * Bitmap is non-lossy, so we just look through the offsets listed in
* the TID was obtained. * tbmres; but we have to follow any HOT chain starting at each such
* offset.
*/ */
if (targoffset < FirstOffsetNumber || targoffset > maxoff) int curslot;
continue;
lp = PageGetItemId(dp, targoffset); for (curslot = 0; curslot < tbmres->ntuples; curslot++)
{
OffsetNumber offnum = tbmres->offsets[curslot];
ItemPointerData tid;
ItemPointerSet(&tid, page, offnum);
if (heap_hot_search_buffer(&tid, buffer, snapshot, NULL))
scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
}
}
else
{
/* /*
* Must check for deleted tuple. * Bitmap is lossy, so we must examine each item pointer on the page.
* But we can ignore HOT chains, since we'll check each tuple anyway.
*/ */
if (!ItemIdIsNormal(lp)) Page dp = (Page) BufferGetPage(buffer);
continue; OffsetNumber maxoff = PageGetMaxOffsetNumber(dp);
OffsetNumber offnum;
/* for (offnum = FirstOffsetNumber; offnum <= maxoff; offnum++)
* check time qualification of tuple, remember it if valid {
*/ ItemId lp;
loctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp); HeapTupleData loctup;
loctup.t_len = ItemIdGetLength(lp);
ItemPointerSet(&(loctup.t_self), page, targoffset);
valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer); lp = PageGetItemId(dp, offnum);
if (valid) if (!ItemIdIsNormal(lp))
scan->rs_vistuples[ntup++] = targoffset; continue;
loctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
loctup.t_len = ItemIdGetLength(lp);
if (HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer))
scan->rs_vistuples[ntup++] = offnum;
}
} }
LockBuffer(buffer, BUFFER_LOCK_UNLOCK); LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.180 2007/08/15 19:15:46 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.181 2007/09/20 17:56:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1407,6 +1407,7 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan) ...@@ -1407,6 +1407,7 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
plansource->num_params = nargs; plansource->num_params = nargs;
plansource->fully_planned = true; plansource->fully_planned = true;
plansource->fixed_result = false; plansource->fixed_result = false;
/* no need to set search_path, generation or saved_xmin */
plansource->resultDesc = PlanCacheComputeResultDesc(stmt_list); plansource->resultDesc = PlanCacheComputeResultDesc(stmt_list);
plansource->plan = cplan; plansource->plan = cplan;
...@@ -1973,6 +1974,7 @@ _SPI_copy_plan(SPIPlanPtr plan, MemoryContext parentcxt) ...@@ -1973,6 +1974,7 @@ _SPI_copy_plan(SPIPlanPtr plan, MemoryContext parentcxt)
newsource->num_params = newplan->nargs; newsource->num_params = newplan->nargs;
newsource->fully_planned = plansource->fully_planned; newsource->fully_planned = plansource->fully_planned;
newsource->fixed_result = plansource->fixed_result; newsource->fixed_result = plansource->fixed_result;
/* no need to worry about seach_path, generation or saved_xmin */
if (plansource->resultDesc) if (plansource->resultDesc)
newsource->resultDesc = CreateTupleDescCopy(plansource->resultDesc); newsource->resultDesc = CreateTupleDescCopy(plansource->resultDesc);
newsource->plan = newcplan; newsource->plan = newcplan;
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
* Copyright (c) 2003-2007, PostgreSQL Global Development Group * Copyright (c) 2003-2007, PostgreSQL Global Development Group
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/tidbitmap.c,v 1.12 2007/04/26 23:24:44 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/tidbitmap.c,v 1.13 2007/09/20 17:56:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <limits.h> #include <limits.h>
#include "access/htup.h" #include "access/htup.h"
#include "nodes/bitmapset.h"
#include "nodes/tidbitmap.h" #include "nodes/tidbitmap.h"
#include "storage/bufpage.h" #include "storage/bufpage.h"
#include "utils/hsearch.h" #include "utils/hsearch.h"
...@@ -61,9 +62,7 @@ ...@@ -61,9 +62,7 @@
*/ */
#define PAGES_PER_CHUNK (BLCKSZ / 32) #define PAGES_PER_CHUNK (BLCKSZ / 32)
/* The bitmap unit size can be adjusted by changing these declarations: */ /* We use BITS_PER_BITMAPWORD and typedef bitmapword from nodes/bitmapset.h */
#define BITS_PER_BITMAPWORD 32
typedef uint32 bitmapword; /* must be an unsigned type */
#define WORDNUM(x) ((x) / BITS_PER_BITMAPWORD) #define WORDNUM(x) ((x) / BITS_PER_BITMAPWORD)
#define BITNUM(x) ((x) % BITS_PER_BITMAPWORD) #define BITNUM(x) ((x) % BITS_PER_BITMAPWORD)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.221 2007/05/26 18:23:01 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.222 2007/09/20 17:56:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -134,6 +134,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) ...@@ -134,6 +134,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
glob->subrtables = NIL; glob->subrtables = NIL;
glob->rewindPlanIDs = NULL; glob->rewindPlanIDs = NULL;
glob->finalrtable = NIL; glob->finalrtable = NIL;
glob->transientPlan = false;
/* Determine what fraction of the plan is likely to be scanned */ /* Determine what fraction of the plan is likely to be scanned */
if (cursorOptions & CURSOR_OPT_FAST_PLAN) if (cursorOptions & CURSOR_OPT_FAST_PLAN)
...@@ -183,6 +184,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) ...@@ -183,6 +184,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->commandType = parse->commandType; result->commandType = parse->commandType;
result->canSetTag = parse->canSetTag; result->canSetTag = parse->canSetTag;
result->transientPlan = glob->transientPlan;
result->planTree = top_plan; result->planTree = top_plan;
result->rtable = glob->finalrtable; result->rtable = glob->finalrtable;
result->resultRelations = root->resultRelations; result->resultRelations = root->resultRelations;
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.136 2007/05/31 16:57:34 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.137 2007/09/20 17:56:31 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "access/genam.h" #include "access/genam.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "access/transam.h"
#include "catalog/pg_inherits.h" #include "catalog/pg_inherits.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
...@@ -164,6 +165,20 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, ...@@ -164,6 +165,20 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
continue; continue;
} }
/*
* If the index is valid, but cannot yet be used, ignore it;
* but mark the plan we are generating as transient.
* See src/backend/access/heap/README.HOT for discussion.
*/
if (index->indcheckxmin &&
!TransactionIdPrecedes(HeapTupleHeaderGetXmin(indexRelation->rd_indextuple->t_data),
TransactionXmin))
{
root->glob->transientPlan = true;
index_close(indexRelation, NoLock);
continue;
}
info = makeNode(IndexOptInfo); info = makeNode(IndexOptInfo);
info->indexoid = index->indexrelid; info->indexoid = index->indexrelid;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -415,6 +415,7 @@ Table "public.concur_heap" ...@@ -415,6 +415,7 @@ Table "public.concur_heap"
f2 | text | f2 | text |
Indexes: Indexes:
"concur_index2" UNIQUE, btree (f1) "concur_index2" UNIQUE, btree (f1)
"concur_index3" UNIQUE, btree (f2) INVALID
"concur_index1" btree (f2, f1) "concur_index1" btree (f2, f1)
"concur_index4" btree (f2) WHERE f1 = 'a'::text "concur_index4" btree (f2) WHERE f1 = 'a'::text
"concur_index5" btree (f2) WHERE f1 = 'x'::text "concur_index5" btree (f2) WHERE f1 = 'x'::text
......
This diff is collapsed.
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