diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml index 01381ff7058049d15c59d8ef0788323de7833dcc..61956cdfcf5d7386cef83c7b17c834d39ebf2be7 100644 --- a/doc/src/sgml/indexam.sgml +++ b/doc/src/sgml/indexam.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.10 2006/05/02 22:25:09 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.11 2006/05/10 23:18:38 tgl Exp $ --> <chapter id="indexam"> <title>Index Access Method Interface Definition</title> @@ -144,7 +144,7 @@ <para> <programlisting> -void +IndexBuildResult * ambuild (Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo); @@ -155,6 +155,8 @@ ambuild (Relation heapRelation, in the table. Ordinarily the <function>ambuild</> function will call <function>IndexBuildHeapScan()</> to scan the table for existing tuples and compute the keys that need to be inserted into the index. + The function must return a palloc'd struct containing statistics about + the new index. </para> <para> diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c index fd7fc9f823bfe067c10d58c88ae4d30c8ef3d98c..a4416a94cb3348d970ef10ed90f4c1a28b86d481 100644 --- a/src/backend/access/gin/gininsert.c +++ b/src/backend/access/gin/gininsert.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.1 2006/05/02 11:28:54 teodor Exp $ + * $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.2 2006/05/10 23:18:38 tgl Exp $ *------------------------------------------------------------------------- */ @@ -242,6 +242,7 @@ ginbuild(PG_FUNCTION_ARGS) { Relation heap = (Relation) PG_GETARG_POINTER(0); Relation index = (Relation) PG_GETARG_POINTER(1); IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2); + IndexBuildResult *result; double reltuples; GinBuildState buildstate; Buffer buffer; @@ -310,10 +311,15 @@ ginbuild(PG_FUNCTION_ARGS) { MemoryContextDelete(buildstate.tmpCtx); - /* since we just counted the # of tuples, may as well update stats */ - IndexCloseAndUpdateStats(heap, reltuples, index, buildstate.indtuples); + /* + * Return statistics + */ + result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult)); + + result->heap_tuples = reltuples; + result->index_tuples = buildstate.indtuples; - PG_RETURN_VOID(); + PG_RETURN_POINTER(result); } /* diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c index 2272e3339d14d24e50742dab7cf3d8423b0e3d12..4ce461d4463ba739439d6b2eaafd3a02d8f4c1a8 100644 --- a/src/backend/access/gist/gist.c +++ b/src/backend/access/gist/gist.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/gist/gist.c,v 1.133 2006/05/10 09:19:54 teodor Exp $ + * $PostgreSQL: pgsql/src/backend/access/gist/gist.c,v 1.134 2006/05/10 23:18:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -89,6 +89,7 @@ gistbuild(PG_FUNCTION_ARGS) Relation heap = (Relation) PG_GETARG_POINTER(0); Relation index = (Relation) PG_GETARG_POINTER(1); IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2); + IndexBuildResult *result; double reltuples; GISTBuildState buildstate; Buffer buffer; @@ -154,12 +155,17 @@ gistbuild(PG_FUNCTION_ARGS) /* okay, all heap tuples are indexed */ MemoryContextDelete(buildstate.tmpCtx); - /* since we just counted the # of tuples, may as well update stats */ - IndexCloseAndUpdateStats(heap, reltuples, index, buildstate.indtuples); - freeGISTstate(&buildstate.giststate); - PG_RETURN_VOID(); + /* + * Return statistics + */ + result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult)); + + result->heap_tuples = reltuples; + result->index_tuples = buildstate.indtuples; + + PG_RETURN_POINTER(result); } /* diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c index ff54052f6de28e2f586103d6567c7a1126020adf..d94104854e97cc8ed80f9a7dded8f0a22479ff09 100644 --- a/src/backend/access/hash/hash.c +++ b/src/backend/access/hash/hash.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/hash/hash.c,v 1.89 2006/05/02 22:25:10 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/hash/hash.c,v 1.90 2006/05/10 23:18:38 tgl Exp $ * * NOTES * This file contains only the public interface routines. @@ -51,6 +51,7 @@ hashbuild(PG_FUNCTION_ARGS) Relation heap = (Relation) PG_GETARG_POINTER(0); Relation index = (Relation) PG_GETARG_POINTER(1); IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2); + IndexBuildResult *result; double reltuples; HashBuildState buildstate; @@ -72,10 +73,15 @@ hashbuild(PG_FUNCTION_ARGS) reltuples = IndexBuildHeapScan(heap, index, indexInfo, hashbuildCallback, (void *) &buildstate); - /* since we just counted the # of tuples, may as well update stats */ - IndexCloseAndUpdateStats(heap, reltuples, index, buildstate.indtuples); + /* + * Return statistics + */ + result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult)); - PG_RETURN_VOID(); + result->heap_tuples = reltuples; + result->index_tuples = buildstate.indtuples; + + PG_RETURN_POINTER(result); } /* diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 779e86a38940ba4619d856a1f034e88c2708f889..dcb9fc8fb64dca094b7345fde5f1fbfeacb7d40e 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.211 2006/03/31 23:32:05 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.212 2006/05/10 23:18:39 tgl Exp $ * * * INTERFACE ROUTINES @@ -2673,6 +2673,97 @@ l3: return HeapTupleMayBeUpdated; } + +/* + * heap_inplace_update - update a tuple "in place" (ie, overwrite it) + * + * Overwriting violates both MVCC and transactional safety, so the uses + * of this function in Postgres are extremely limited. Nonetheless we + * find some places to use it. + * + * The tuple cannot change size, and therefore it's reasonable to assume + * that its null bitmap (if any) doesn't change either. So we just + * overwrite the data portion of the tuple without touching the null + * bitmap or any of the header fields. + * + * tuple is an in-memory tuple structure containing the data to be written + * over the target tuple. Also, tuple->t_self identifies the target tuple. + */ +void +heap_inplace_update(Relation relation, HeapTuple tuple) +{ + Buffer buffer; + Page page; + OffsetNumber offnum; + ItemId lp = NULL; + HeapTupleHeader htup; + uint32 oldlen; + uint32 newlen; + + buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self))); + LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); + page = (Page) BufferGetPage(buffer); + + offnum = ItemPointerGetOffsetNumber(&(tuple->t_self)); + if (PageGetMaxOffsetNumber(page) >= offnum) + lp = PageGetItemId(page, offnum); + + if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsUsed(lp)) + elog(ERROR, "heap_inplace_update: invalid lp"); + + htup = (HeapTupleHeader) PageGetItem(page, lp); + + oldlen = ItemIdGetLength(lp) - htup->t_hoff; + newlen = tuple->t_len - tuple->t_data->t_hoff; + if (oldlen != newlen || htup->t_hoff != tuple->t_data->t_hoff) + elog(ERROR, "heap_inplace_update: wrong tuple length"); + + /* NO EREPORT(ERROR) from here till changes are logged */ + START_CRIT_SECTION(); + + memcpy((char *) htup + htup->t_hoff, + (char *) tuple->t_data + tuple->t_data->t_hoff, + newlen); + + MarkBufferDirty(buffer); + + /* XLOG stuff */ + if (!relation->rd_istemp) + { + xl_heap_inplace xlrec; + XLogRecPtr recptr; + XLogRecData rdata[2]; + + xlrec.target.node = relation->rd_node; + xlrec.target.tid = tuple->t_self; + + rdata[0].data = (char *) &xlrec; + rdata[0].len = SizeOfHeapInplace; + rdata[0].buffer = InvalidBuffer; + rdata[0].next = &(rdata[1]); + + rdata[1].data = (char *) htup + htup->t_hoff; + rdata[1].len = newlen; + rdata[1].buffer = buffer; + rdata[1].buffer_std = true; + rdata[1].next = NULL; + + recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_INPLACE, rdata); + + PageSetLSN(page, recptr); + PageSetTLI(page, ThisTimeLineID); + } + + END_CRIT_SECTION(); + + UnlockReleaseBuffer(buffer); + + /* Send out shared cache inval if necessary */ + if (!IsBootstrapProcessingMode()) + CacheInvalidateHeapTuple(relation, tuple); +} + + /* ---------------- * heap_markpos - mark scan position * ---------------- @@ -3329,6 +3420,59 @@ heap_xlog_lock(XLogRecPtr lsn, XLogRecord *record) UnlockReleaseBuffer(buffer); } +static void +heap_xlog_inplace(XLogRecPtr lsn, XLogRecord *record) +{ + xl_heap_inplace *xlrec = (xl_heap_inplace *) XLogRecGetData(record); + Relation reln = XLogOpenRelation(xlrec->target.node); + Buffer buffer; + Page page; + OffsetNumber offnum; + ItemId lp = NULL; + HeapTupleHeader htup; + uint32 oldlen; + uint32 newlen; + + if (record->xl_info & XLR_BKP_BLOCK_1) + return; + + buffer = XLogReadBuffer(reln, + ItemPointerGetBlockNumber(&(xlrec->target.tid)), + false); + if (!BufferIsValid(buffer)) + return; + page = (Page) BufferGetPage(buffer); + + if (XLByteLE(lsn, PageGetLSN(page))) /* changes are applied */ + { + UnlockReleaseBuffer(buffer); + return; + } + + offnum = ItemPointerGetOffsetNumber(&(xlrec->target.tid)); + if (PageGetMaxOffsetNumber(page) >= offnum) + lp = PageGetItemId(page, offnum); + + if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsUsed(lp)) + elog(PANIC, "heap_inplace_redo: invalid lp"); + + htup = (HeapTupleHeader) PageGetItem(page, lp); + + oldlen = ItemIdGetLength(lp) - htup->t_hoff; + newlen = record->xl_len - SizeOfHeapInplace; + if (oldlen != newlen) + elog(PANIC, "heap_inplace_redo: wrong tuple length"); + + memcpy((char *) htup + htup->t_hoff, + (char *) xlrec + SizeOfHeapInplace, + newlen); + + PageSetLSN(page, lsn); + PageSetTLI(page, ThisTimeLineID); + MarkBufferDirty(buffer); + UnlockReleaseBuffer(buffer); +} + void heap_redo(XLogRecPtr lsn, XLogRecord *record) { @@ -3349,6 +3493,8 @@ heap_redo(XLogRecPtr lsn, XLogRecord *record) heap_xlog_newpage(lsn, record); else if (info == XLOG_HEAP_LOCK) heap_xlog_lock(lsn, record); + else if (info == XLOG_HEAP_INPLACE) + heap_xlog_inplace(lsn, record); else elog(PANIC, "heap_redo: unknown op code %u", info); } @@ -3442,6 +3588,13 @@ heap_desc(StringInfo buf, uint8 xl_info, char *rec) appendStringInfo(buf, "%u ", xlrec->locking_xid); out_target(buf, &(xlrec->target)); } + else if (info == XLOG_HEAP_INPLACE) + { + xl_heap_inplace *xlrec = (xl_heap_inplace *) rec; + + appendStringInfo(buf, "inplace: "); + out_target(buf, &(xlrec->target)); + } else appendStringInfo(buf, "UNKNOWN"); } diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c index d501249a7a602fc1aabf2f86cbfa61221513b00d..3329321c0ffd14727301296d441ace0dd1ad4bce 100644 --- a/src/backend/access/nbtree/nbtree.c +++ b/src/backend/access/nbtree/nbtree.c @@ -12,21 +12,18 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.148 2006/05/08 00:00:10 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtree.c,v 1.149 2006/05/10 23:18:39 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/genam.h" -#include "access/heapam.h" #include "access/nbtree.h" #include "catalog/index.h" #include "commands/vacuum.h" -#include "miscadmin.h" #include "storage/freespace.h" -#include "storage/smgr.h" -#include "utils/inval.h" +#include "storage/lmgr.h" #include "utils/memutils.h" @@ -84,6 +81,7 @@ btbuild(PG_FUNCTION_ARGS) Relation heap = (Relation) PG_GETARG_POINTER(0); Relation index = (Relation) PG_GETARG_POINTER(1); IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2); + IndexBuildResult *result; double reltuples; BTBuildState buildstate; @@ -149,18 +147,20 @@ btbuild(PG_FUNCTION_ARGS) /* * If we are reindexing a pre-existing index, it is critical to send out * a relcache invalidation SI message to ensure all backends re-read the - * index metapage. In most circumstances the update-stats operation will - * cause that to happen, but at the moment there are corner cases where - * no pg_class update will occur, so force an inval here. XXX FIXME: - * the upper levels of CREATE INDEX should handle the stats update as - * well as guaranteeing relcache inval. + * index metapage. We expect that the caller will ensure that happens + * (typically as a side effect of updating index stats, but it must + * happen even if the stats don't change!) */ - CacheInvalidateRelcache(index); - /* since we just counted the # of tuples, may as well update stats */ - IndexCloseAndUpdateStats(heap, reltuples, index, buildstate.indtuples); + /* + * Return statistics + */ + result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult)); - PG_RETURN_VOID(); + result->heap_tuples = reltuples; + result->index_tuples = buildstate.indtuples; + + PG_RETURN_POINTER(result); } /* diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index c1d16b0e8fccd81bf4d8b93921b8cc6aba734774..5dfda5a427d48de7835286ecb5337299b578543c 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.214 2006/04/04 19:35:33 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.215 2006/05/10 23:18:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1173,11 +1173,12 @@ AddStr(char *str, int strlength, int mderef) * index_register() -- record an index that has been set up for building * later. * - * At bootstrap time, we define a bunch of indices on system catalogs. - * We postpone actually building the indices until just before we're - * finished with initialization, however. This is because more classes - * and indices may be defined, and we want to be sure that all of them - * are present in the index. + * At bootstrap time, we define a bunch of indexes on system catalogs. + * We postpone actually building the indexes until just before we're + * finished with initialization, however. This is because the indexes + * themselves have catalog entries, and those have to be included in the + * indexes on those catalogs. Doing it in two phases is the simplest + * way of making sure the indexes have the right contents at the end. */ void index_register(Oid heap, @@ -1189,7 +1190,7 @@ index_register(Oid heap, /* * XXX mao 10/31/92 -- don't gc index reldescs, associated info at - * bootstrap time. we'll declare the indices now, but want to create them + * bootstrap time. we'll declare the indexes now, but want to create them * later. */ @@ -1223,6 +1224,10 @@ index_register(Oid heap, MemoryContextSwitchTo(oldcxt); } + +/* + * build_indices -- fill in all the indexes registered earlier + */ void build_indices(void) { @@ -1233,13 +1238,10 @@ build_indices(void) heap = heap_open(ILHead->il_heap, NoLock); ind = index_open(ILHead->il_ind); - index_build(heap, ind, ILHead->il_info); - /* - * In normal processing mode, index_build would close the heap and - * index, but in bootstrap mode it will not. - */ + index_build(heap, ind, ILHead->il_info, false, false); - /* XXX Probably we ought to close the heap and index here? */ + index_close(ind); + heap_close(heap, NoLock); } } diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index e4514c37ea24b0bbc42a4891c83e92e80339ea59..a3e629307452ab6fff7b49f5436e8c86d69b5a72 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.298 2006/04/30 01:08:06 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.299 2006/05/10 23:18:39 tgl Exp $ * * * INTERFACE ROUTINES @@ -1921,17 +1921,14 @@ RelationTruncateIndexes(Oid heapId) RelationTruncate(currentIndex, 0); /* Initialize the index and rebuild */ - index_build(heapRelation, currentIndex, indexInfo); + /* Note: we do not need to re-establish pkey or toast settings */ + index_build(heapRelation, currentIndex, indexInfo, false, false); - /* - * index_build will close both the heap and index relations (but not - * give up the locks we hold on them). We're done with this index, - * but we must re-open the heap rel. - */ - heapRelation = heap_open(heapId, NoLock); + /* We're done with this index */ + index_close(currentIndex); } - /* Finish by closing the heap rel again */ + /* And now done with the heap; but keep lock until commit */ heap_close(heapRelation, NoLock); } diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 75302edfafddda4cd06beb4ddef73a2c2996575b..d0f216475ea5cbed9f127284274782741c49a1c3 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.265 2006/03/31 23:32:06 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.266 2006/05/10 23:18:39 tgl Exp $ * * * INTERFACE ROUTINES @@ -61,8 +61,9 @@ static void UpdateIndexRelation(Oid indexoid, Oid heapoid, IndexInfo *indexInfo, Oid *classOids, bool primary); +static void index_update_stats(Relation rel, bool hasindex, bool isprimary, + Oid reltoastidxid, double reltuples); static Oid IndexGetRelation(Oid indexId); -static void UpdateStats(Oid relid, double reltuples); /* @@ -437,15 +438,26 @@ UpdateIndexRelation(Oid indexoid, } -/* ---------------------------------------------------------------- - * index_create +/* + * index_create * - * indexRelationId is normally InvalidOid to let this routine - * generate an OID for the index. During bootstrap it may be + * heapRelationId: OID of table to build index on + * indexRelationName: what it say + * indexRelationId: normally, pass InvalidOid to let this routine + * generate an OID for the index. During bootstrap this may be * nonzero to specify a preselected OID. + * indexInfo: same info executor uses to insert into the index + * accessMethodObjectId: OID of index AM to use + * tableSpaceId: OID of tablespace to use + * classObjectId: array of index opclass OIDs, one per index column + * isprimary: index is a PRIMARY KEY + * istoast: index is a toast table's index + * isconstraint: index is owned by a PRIMARY KEY or UNIQUE constraint + * allow_system_table_mods: allow table to be a system catalog + * skip_build: true to skip the index_build() step for the moment; caller + * must do it later (typically via reindex_index()) * * Returns OID of the created index. - * ---------------------------------------------------------------- */ Oid index_create(Oid heapRelationId, @@ -455,7 +467,8 @@ index_create(Oid heapRelationId, Oid accessMethodObjectId, Oid tableSpaceId, Oid *classObjectId, - bool primary, + bool isprimary, + bool istoast, bool isconstraint, bool allow_system_table_mods, bool skip_build) @@ -595,7 +608,7 @@ index_create(Oid heapRelationId, * ---------------- */ UpdateIndexRelation(indexRelationId, heapRelationId, indexInfo, - classObjectId, primary); + classObjectId, isprimary); /* * Register constraint and dependencies for the index. @@ -625,7 +638,7 @@ index_create(Oid heapRelationId, char constraintType; Oid conOid; - if (primary) + if (isprimary) constraintType = CONSTRAINT_PRIMARY; else if (indexInfo->ii_Unique) constraintType = CONSTRAINT_UNIQUE; @@ -736,28 +749,40 @@ index_create(Oid heapRelationId, * Similarly, if the caller specified skip_build then filling the index is * delayed till later (ALTER TABLE can save work in some cases with this). * Otherwise, we call the AM routine that constructs the index. - * - * In normal processing mode, the heap and index relations are closed, but - * we continue to hold the ShareLock on the heap and the exclusive lock on - * the index that we acquired above, until end of transaction. */ if (IsBootstrapProcessingMode()) { index_register(heapRelationId, indexRelationId, indexInfo); - /* XXX shouldn't we close the heap and index rels here? */ } else if (skip_build) { - /* caller is responsible for filling the index later on */ - relation_close(indexRelation, NoLock); - heap_close(heapRelation, NoLock); + /* + * Caller is responsible for filling the index later on. However, + * we'd better make sure that the heap relation is correctly marked + * as having an index. + */ + index_update_stats(heapRelation, + true, + isprimary, + InvalidOid, + heapRelation->rd_rel->reltuples); + /* Make the above update visible */ + CommandCounterIncrement(); } else { - index_build(heapRelation, indexRelation, indexInfo); - /* index_build closes the passed rels */ + index_build(heapRelation, indexRelation, indexInfo, + isprimary, istoast); } + /* + * Close the heap and index; but we keep the ShareLock on the heap and + * the exclusive lock on the index that we acquired above, until end of + * transaction. + */ + index_close(indexRelation); + heap_close(heapRelation, NoLock); + return indexRelationId; } @@ -983,38 +1008,59 @@ FormIndexDatum(IndexInfo *indexInfo, } -/* ---------------- - * set relhasindex of relation's pg_class entry +/* + * index_update_stats --- update pg_class entry after CREATE INDEX * - * If isprimary is TRUE, we are defining a primary index, so also set - * relhaspkey to TRUE. Otherwise, leave relhaspkey alone. + * This routine updates the pg_class row of either an index or its parent + * relation after CREATE INDEX. Its rather bizarre API is designed to + * ensure we can do all the necessary work in just one update. * - * If reltoastidxid is not InvalidOid, also set reltoastidxid to that value. - * This is only used for TOAST relations. + * hasindex: set relhasindex to this value + * isprimary: if true, set relhaspkey true; else no change + * reltoastidxid: if not InvalidOid, set reltoastidxid to this value; + * else no change + * reltuples: set reltuples to this value + * + * relpages is also updated (using RelationGetNumberOfBlocks()). * * NOTE: an important side-effect of this operation is that an SI invalidation * message is sent out to all backends --- including me --- causing relcache - * entries to be flushed or updated with the new hasindex data. This must - * happen even if we find that no change is needed in the pg_class row. - * ---------------- + * entries to be flushed or updated with the new data. This must happen even + * if we find that no change is needed in the pg_class row. When updating + * a heap entry, this ensures that other backends find out about the new + * index. When updating an index, it's important because some index AMs + * expect a relcache flush to occur after REINDEX. */ -void -setRelhasindex(Oid relid, bool hasindex, bool isprimary, Oid reltoastidxid) +static void +index_update_stats(Relation rel, bool hasindex, bool isprimary, + Oid reltoastidxid, double reltuples) { + BlockNumber relpages = RelationGetNumberOfBlocks(rel); + Oid relid = RelationGetRelid(rel); Relation pg_class; HeapTuple tuple; - Form_pg_class classtuple; - bool dirty = false; - HeapScanDesc pg_class_scan = NULL; + Form_pg_class rd_rel; + bool in_place_upd; + bool dirty; /* - * Find the tuple to update in pg_class. In bootstrap mode we can't use - * heap_update, so cheat and overwrite the tuple in-place. In normal - * processing, make a copy to scribble on. + * Find the tuple to update in pg_class. Normally we make a copy of the + * tuple using the syscache, modify it, and apply heap_update. But in + * bootstrap mode we can't use heap_update, so we use a nontransactional + * update, ie, overwrite the tuple in-place. + * + * We also must use an in-place update if reindexing pg_class itself, + * because the target index may presently not be part of the set of + * indexes that CatalogUpdateIndexes would update (see reindex_relation). */ pg_class = heap_open(RelationRelationId, RowExclusiveLock); - if (!IsBootstrapProcessingMode()) + in_place_upd = IsBootstrapProcessingMode() || + ReindexIsProcessingHeap(RelationRelationId); + +restart: + + if (!in_place_upd) { tuple = SearchSysCacheCopy(RELOID, ObjectIdGetDatum(relid), @@ -1022,6 +1068,8 @@ setRelhasindex(Oid relid, bool hasindex, bool isprimary, Oid reltoastidxid) } else { + /* don't assume syscache will work */ + HeapScanDesc pg_class_scan; ScanKeyData key[1]; ScanKeyInit(&key[0], @@ -1031,65 +1079,110 @@ setRelhasindex(Oid relid, bool hasindex, bool isprimary, Oid reltoastidxid) pg_class_scan = heap_beginscan(pg_class, SnapshotNow, 1, key); tuple = heap_getnext(pg_class_scan, ForwardScanDirection); + tuple = heap_copytuple(tuple); + heap_endscan(pg_class_scan); } if (!HeapTupleIsValid(tuple)) elog(ERROR, "could not find tuple for relation %u", relid); - classtuple = (Form_pg_class) GETSTRUCT(tuple); - - /* Apply required updates */ + rd_rel = (Form_pg_class) GETSTRUCT(tuple); - if (pg_class_scan) - LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE); + /* Apply required updates, if any, to copied tuple */ - if (classtuple->relhasindex != hasindex) + dirty = false; + if (rd_rel->relhasindex != hasindex) { - classtuple->relhasindex = hasindex; + rd_rel->relhasindex = hasindex; dirty = true; } if (isprimary) { - if (!classtuple->relhaspkey) + if (!rd_rel->relhaspkey) { - classtuple->relhaspkey = true; + rd_rel->relhaspkey = true; dirty = true; } } if (OidIsValid(reltoastidxid)) { - Assert(classtuple->relkind == RELKIND_TOASTVALUE); - if (classtuple->reltoastidxid != reltoastidxid) + Assert(rd_rel->relkind == RELKIND_TOASTVALUE); + if (rd_rel->reltoastidxid != reltoastidxid) { - classtuple->reltoastidxid = reltoastidxid; + rd_rel->reltoastidxid = reltoastidxid; dirty = true; } } - - if (pg_class_scan) + if (rd_rel->reltuples != (float4) reltuples) { - MarkBufferDirty(pg_class_scan->rs_cbuf); - LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_UNLOCK); - /* Send out shared cache inval if necessary */ - if (!IsBootstrapProcessingMode()) - CacheInvalidateHeapTuple(pg_class, tuple); + rd_rel->reltuples = (float4) reltuples; + dirty = true; } - else if (dirty) + if (rd_rel->relpages != (int32) relpages) { - simple_heap_update(pg_class, &tuple->t_self, tuple); + rd_rel->relpages = (int32) relpages; + dirty = true; + } - /* Keep the catalog indexes up to date */ - CatalogUpdateIndexes(pg_class, tuple); + /* + * If anything changed, write out the tuple + */ + if (dirty) + { + if (in_place_upd) + { + heap_inplace_update(pg_class, tuple); + } + else + { + /* + * Because PG allows concurrent CREATE INDEX commands, it's + * possible that someone else tries to update the pg_class + * row at about the same time we do. Hence, instead of using + * simple_heap_update(), we must use full heap_update() and + * cope with HeapTupleUpdated result. If we see that, just + * go back and try the whole update again. + */ + HTSU_Result result; + ItemPointerData update_ctid; + TransactionId update_xmax; + + result = heap_update(pg_class, &tuple->t_self, tuple, + &update_ctid, &update_xmax, + GetCurrentCommandId(), InvalidSnapshot, + true /* wait for commit */ ); + switch (result) + { + case HeapTupleSelfUpdated: + /* Tuple was already updated in current command? */ + elog(ERROR, "tuple already updated by self"); + break; + + case HeapTupleMayBeUpdated: + /* done successfully */ + break; + + case HeapTupleUpdated: + heap_freetuple(tuple); + /* Must do CCI so we can see the updated tuple */ + CommandCounterIncrement(); + goto restart; + + default: + elog(ERROR, "unrecognized heap_update status: %u", result); + break; + } + + /* Keep the catalog indexes up to date */ + CatalogUpdateIndexes(pg_class, tuple); + } } else { - /* no need to change tuple, but force relcache rebuild anyway */ + /* no need to change tuple, but force relcache inval anyway */ CacheInvalidateRelcacheByTuple(tuple); } - if (!pg_class_scan) - heap_freetuple(tuple); - else - heap_endscan(pg_class_scan); + heap_freetuple(tuple); heap_close(pg_class, RowExclusiveLock); } @@ -1163,177 +1256,31 @@ setNewRelfilenode(Relation relation) } -/* - * This is invoked by the various index AMs once they have finished - * constructing an index. Constructing an index involves counting the - * number of tuples in both the relation and the index, so we take - * advantage of the opportunity to update pg_class to ensure that the - * planner takes advantage of the index we just created. But, only - * update statistics during normal index definitions, not for indices - * on system catalogs created during bootstrap processing. We must - * close the relations before updating statistics to guarantee that - * the relcache entries are flushed when we increment the command - * counter in UpdateStats(). But we do not release any locks on the - * relations; those will be held until end of transaction. - */ -void -IndexCloseAndUpdateStats(Relation heap, double heapTuples, - Relation index, double indexTuples) -{ - Oid hrelid = RelationGetRelid(heap); - Oid irelid = RelationGetRelid(index); - - if (!IsNormalProcessingMode()) - return; - - heap_close(heap, NoLock); - index_close(index); - UpdateStats(hrelid, heapTuples); - UpdateStats(irelid, indexTuples); -} - - -/* ---------------- - * UpdateStats - * - * Update pg_class' relpages and reltuples statistics for the given relation - * (which can be either a table or an index). Note that this is not used - * in the context of VACUUM, only CREATE INDEX. - * ---------------- - */ -static void -UpdateStats(Oid relid, double reltuples) -{ - Relation whichRel; - Relation pg_class; - HeapTuple tuple; - BlockNumber relpages; - Form_pg_class rd_rel; - HeapScanDesc pg_class_scan = NULL; - bool in_place_upd; - - /* - * This routine handles updates for both the heap and index relation - * statistics. In order to guarantee that we're able to *see* the index - * relation tuple, we bump the command counter id here. The index - * relation tuple was created in the current transaction. - */ - CommandCounterIncrement(); - - /* - * CommandCounterIncrement() flushes invalid cache entries, including - * those for the heap and index relations for which we're updating - * statistics. Now that the cache is flushed, it's safe to open the - * relation again. We need the relation open in order to figure out how - * many blocks it contains. - */ - - /* - * Grabbing lock here is probably redundant ... - */ - whichRel = relation_open(relid, ShareLock); - - /* - * Find the tuple to update in pg_class. Normally we make a copy of the - * tuple using the syscache, modify it, and apply heap_update. But in - * bootstrap mode we can't use heap_update, so we cheat and overwrite the - * tuple in-place. (Note: as of PG 8.0 this isn't called during - * bootstrap, but leave the code here for possible future use.) - * - * We also must cheat if reindexing pg_class itself, because the target - * index may presently not be part of the set of indexes that - * CatalogUpdateIndexes would update (see reindex_relation). In this case - * the stats updates will not be WAL-logged and so could be lost in a - * crash. This seems OK considering VACUUM does the same thing. - */ - pg_class = heap_open(RelationRelationId, RowExclusiveLock); - - in_place_upd = IsBootstrapProcessingMode() || - ReindexIsProcessingHeap(RelationRelationId); - - if (!in_place_upd) - { - tuple = SearchSysCacheCopy(RELOID, - ObjectIdGetDatum(relid), - 0, 0, 0); - } - else - { - ScanKeyData key[1]; - - ScanKeyInit(&key[0], - ObjectIdAttributeNumber, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(relid)); - - pg_class_scan = heap_beginscan(pg_class, SnapshotNow, 1, key); - tuple = heap_getnext(pg_class_scan, ForwardScanDirection); - } - - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "could not find tuple for relation %u", relid); - rd_rel = (Form_pg_class) GETSTRUCT(tuple); - - /* - * Update statistics in pg_class, if they changed. (Avoiding an - * unnecessary update is not just a tiny performance improvement; it also - * reduces the window wherein concurrent CREATE INDEX commands may - * conflict.) - */ - relpages = RelationGetNumberOfBlocks(whichRel); - - if (rd_rel->relpages != (int32) relpages || - rd_rel->reltuples != (float4) reltuples) - { - if (in_place_upd) - { - /* Bootstrap or reindex case: overwrite fields in place. */ - LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_EXCLUSIVE); - rd_rel->relpages = (int32) relpages; - rd_rel->reltuples = (float4) reltuples; - MarkBufferDirty(pg_class_scan->rs_cbuf); - LockBuffer(pg_class_scan->rs_cbuf, BUFFER_LOCK_UNLOCK); - if (!IsBootstrapProcessingMode()) - CacheInvalidateHeapTuple(pg_class, tuple); - } - else - { - /* During normal processing, must work harder. */ - rd_rel->relpages = (int32) relpages; - rd_rel->reltuples = (float4) reltuples; - simple_heap_update(pg_class, &tuple->t_self, tuple); - CatalogUpdateIndexes(pg_class, tuple); - } - } - - if (in_place_upd) - heap_endscan(pg_class_scan); - else - heap_freetuple(tuple); - - /* - * We shouldn't have to do this, but we do... Modify the reldesc in place - * with the new values so that the cache contains the latest copy. (XXX - * is this really still necessary? The relcache will get fixed at next - * CommandCounterIncrement, so why bother here?) - */ - whichRel->rd_rel->relpages = (int32) relpages; - whichRel->rd_rel->reltuples = (float4) reltuples; - - heap_close(pg_class, RowExclusiveLock); - relation_close(whichRel, NoLock); -} - - /* * index_build - invoke access-method-specific index build procedure + * + * On entry, the index's catalog entries are valid, and its physical disk + * file has been created but is empty. We call the AM-specific build + * procedure to fill in the index contents. We then update the pg_class + * entries of the index and heap relation as needed, using statistics + * returned by ambuild as well as data passed by the caller. + * + * Note: when reindexing an existing index, isprimary and istoast can be + * false; the index is already properly marked and need not be re-marked. + * + * Note: before Postgres 8.2, the passed-in heap and index Relations + * were automatically closed by this routine. This is no longer the case. + * The caller opened 'em, and the caller should close 'em. */ void index_build(Relation heapRelation, Relation indexRelation, - IndexInfo *indexInfo) + IndexInfo *indexInfo, + bool isprimary, + bool istoast) { RegProcedure procedure; + IndexBuildResult *stats; /* * sanity checks @@ -1347,10 +1294,30 @@ index_build(Relation heapRelation, /* * Call the access method's build procedure */ - OidFunctionCall3(procedure, - PointerGetDatum(heapRelation), - PointerGetDatum(indexRelation), - PointerGetDatum(indexInfo)); + stats = (IndexBuildResult *) + DatumGetPointer(OidFunctionCall3(procedure, + PointerGetDatum(heapRelation), + PointerGetDatum(indexRelation), + PointerGetDatum(indexInfo))); + Assert(PointerIsValid(stats)); + + /* + * Update heap and index pg_class rows + */ + index_update_stats(heapRelation, + true, + isprimary, + istoast ? RelationGetRelid(indexRelation) : InvalidOid, + stats->heap_tuples); + + index_update_stats(indexRelation, + false, + false, + InvalidOid, + stats->index_tuples); + + /* Make the updated versions visible */ + CommandCounterIncrement(); } @@ -1674,12 +1641,8 @@ reindex_index(Oid indexId) } /* Initialize the index and rebuild */ - index_build(heapRelation, iRel, indexInfo); - - /* - * index_build will close both the heap and index relations (but not - * give up the locks we hold on them). So we're done. - */ + /* Note: we do not need to re-establish pkey or toast settings */ + index_build(heapRelation, iRel, indexInfo, false, false); } PG_CATCH(); { @@ -1689,6 +1652,10 @@ reindex_index(Oid indexId) } PG_END_TRY(); ResetReindexProcessing(); + + /* Close rels, but keep locks */ + index_close(iRel); + heap_close(heapRelation, NoLock); } /* diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 431d8dcd08bfe971d3913f464fa93ca665dc8eba..ef57db4a0c02495d2811eb99a7c071999a2070ff 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.138 2006/03/05 15:58:24 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.139 2006/05/10 23:18:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -397,17 +397,8 @@ DefineIndex(RangeVar *heapRelation, index_create(relationId, indexRelationName, indexRelationId, indexInfo, accessMethodId, tablespaceId, classObjectId, - primary, isconstraint, + primary, false, isconstraint, allowSystemTableMods, skip_build); - - /* - * We update the relation's pg_class tuple even if it already has - * relhasindex = true. This is needed to cause a shared-cache-inval - * message to be sent for the pg_class tuple, which will cause other - * backends to flush their relcache entries and in particular their cached - * lists of the indexes for this relation. - */ - setRelhasindex(relationId, true, primary, InvalidOid); } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index f670ccf14eb02fe165ddd982b7c98b193b16c287..9da1d86c42c8d8b2f8d86cb2f7e7dd5a9479e0ab 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.183 2006/04/30 01:08:07 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.184 2006/05/10 23:18:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -6046,14 +6046,7 @@ AlterTableCreateToastTable(Oid relOid, bool silent) BTREE_AM_OID, rel->rd_rel->reltablespace, classObjectId, - true, false, true, false); - - /* - * Update toast rel's pg_class entry to show that it has an index. The - * index OID is stored into the reltoastidxid field for easy access by the - * tuple toaster. - */ - setRelhasindex(toast_relid, true, true, toast_idxid); + true, true, false, true, false); /* * Store the toast table's OID in the parent relation's pg_class row diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 7f13bae8099053fcf2b8a1567dec5ce24c33b5b5..4381369bc034b168b3cd6f1a51f8871c28665a1e 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -13,7 +13,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.329 2006/05/03 22:45:26 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.330 2006/05/10 23:18:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -673,9 +673,10 @@ vacuum_set_xid_limits(VacuumStmt *vacstmt, bool sharedRel, * doing ANALYZE, but we always update these stats. This routine works * for both index and heap relation entries in pg_class. * - * We violate no-overwrite semantics here by storing new values for the - * statistics columns directly into the pg_class tuple that's already on - * the page. The reason for this is that if we updated these tuples in + * We violate transaction semantics here by overwriting the rel's + * existing pg_class tuple with the new values. This is reasonably + * safe since the new values are correct whether or not this transaction + * commits. The reason for this is that if we updated these tuples in * the usual way, vacuuming pg_class itself wouldn't work very well --- * by the time we got done with a vacuum cycle, most of the tuples in * pg_class would've been obsoleted. Of course, this only works for @@ -689,59 +690,57 @@ vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples, bool hasindex) { Relation rd; - HeapTupleData rtup; HeapTuple ctup; Form_pg_class pgcform; - Buffer buffer; + bool dirty; - /* - * update number of tuples and number of pages in pg_class - */ rd = heap_open(RelationRelationId, RowExclusiveLock); - ctup = SearchSysCache(RELOID, - ObjectIdGetDatum(relid), - 0, 0, 0); + /* Fetch a copy of the tuple to scribble on */ + ctup = SearchSysCacheCopy(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); if (!HeapTupleIsValid(ctup)) elog(ERROR, "pg_class entry for relid %u vanished during vacuuming", relid); + pgcform = (Form_pg_class) GETSTRUCT(ctup); - /* get the buffer cache tuple */ - rtup.t_self = ctup->t_self; - ReleaseSysCache(ctup); - if (!heap_fetch(rd, SnapshotNow, &rtup, &buffer, false, NULL)) - elog(ERROR, "pg_class entry for relid %u vanished during vacuuming", - relid); - - /* ensure no one else does this at the same time */ - LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); - - /* overwrite the existing statistics in the tuple */ - pgcform = (Form_pg_class) GETSTRUCT(&rtup); - pgcform->relpages = (int32) num_pages; - pgcform->reltuples = (float4) num_tuples; - pgcform->relhasindex = hasindex; + /* Apply required updates, if any, to copied tuple */ + dirty = false; + if (pgcform->relpages != (int32) num_pages) + { + pgcform->relpages = (int32) num_pages; + dirty = true; + } + if (pgcform->reltuples != (float4) num_tuples) + { + pgcform->reltuples = (float4) num_tuples; + dirty = true; + } + if (pgcform->relhasindex != hasindex) + { + pgcform->relhasindex = hasindex; + dirty = true; + } /* * If we have discovered that there are no indexes, then there's no * primary key either. This could be done more thoroughly... */ if (!hasindex) - pgcform->relhaspkey = false; - - MarkBufferDirty(buffer); - - LockBuffer(buffer, BUFFER_LOCK_UNLOCK); + { + if (pgcform->relhaspkey) + { + pgcform->relhaspkey = false; + dirty = true; + } + } /* - * Invalidate the tuple in the catcaches; this also arranges to flush the - * relation's relcache entry. (If we fail to commit for some reason, no - * flush will occur, but no great harm is done since there are no - * noncritical state updates here.) + * If anything changed, write out the tuple */ - CacheInvalidateHeapTuple(rd, &rtup); - - ReleaseBuffer(buffer); + if (dirty) + heap_inplace_update(rd, ctup); heap_close(rd, RowExclusiveLock); } @@ -753,10 +752,11 @@ vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples, * Update the whole-database statistics that are kept in its pg_database * row, and the flat-file copy of pg_database. * - * We violate no-overwrite semantics here by storing new values for the - * statistics columns directly into the tuple that's already on the page. - * As with vac_update_relstats, this avoids leaving dead tuples behind - * after a VACUUM. + * We violate transaction semantics here by overwriting the database's + * existing pg_database tuple with the new values. This is reasonably + * safe since the new values are correct whether or not this transaction + * commits. As with vac_update_relstats, this avoids leaving dead tuples + * behind after a VACUUM. * * This routine is shared by full and lazy VACUUM. Note that it is only * applied after a database-wide VACUUM operation. @@ -767,49 +767,24 @@ vac_update_dbstats(Oid dbid, TransactionId frozenXID) { Relation relation; - ScanKeyData entry[1]; - SysScanDesc scan; HeapTuple tuple; - Buffer buf; Form_pg_database dbform; relation = heap_open(DatabaseRelationId, RowExclusiveLock); - ScanKeyInit(&entry[0], - ObjectIdAttributeNumber, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(dbid)); - - scan = systable_beginscan(relation, DatabaseOidIndexId, true, - SnapshotNow, 1, entry); - - tuple = systable_getnext(scan); - + /* Fetch a copy of the tuple to scribble on */ + tuple = SearchSysCacheCopy(DATABASEOID, + ObjectIdGetDatum(dbid), + 0, 0, 0); if (!HeapTupleIsValid(tuple)) elog(ERROR, "could not find tuple for database %u", dbid); - - if (scan->irel) - buf = scan->iscan->xs_cbuf; - else - buf = scan->scan->rs_cbuf; - - /* ensure no one else does this at the same time */ - LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE); - dbform = (Form_pg_database) GETSTRUCT(tuple); /* overwrite the existing statistics in the tuple */ dbform->datvacuumxid = vacuumXID; dbform->datfrozenxid = frozenXID; - MarkBufferDirty(buf); - - LockBuffer(buf, BUFFER_LOCK_UNLOCK); - - /* invalidate the tuple in the cache so we'll see the change in cache */ - CacheInvalidateHeapTuple(relation, tuple); - - systable_endscan(scan); + heap_inplace_update(relation, tuple); heap_close(relation, RowExclusiveLock); diff --git a/src/include/access/genam.h b/src/include/access/genam.h index 67a9ef60ee9c8ef85fc511c9102e6d39b90f49c6..c1859a0c6d1ccc0fd6e245c848c3af36af116386 100644 --- a/src/include/access/genam.h +++ b/src/include/access/genam.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.59 2006/05/02 22:25:10 tgl Exp $ + * $PostgreSQL: pgsql/src/include/access/genam.h,v 1.60 2006/05/10 23:18:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,15 @@ #include "nodes/primnodes.h" +/* + * Struct for statistics returned by ambuild + */ +typedef struct IndexBuildResult +{ + double heap_tuples; /* # of tuples seen in parent table */ + double index_tuples; /* # of tuples inserted into index */ +} IndexBuildResult; + /* * Struct for input arguments passed to ambulkdelete and amvacuumcleanup * diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h index fdf2e8d86f5d9caeb13cdc36b6fb178f6e4d5aba..e016a527403ff5ed2018a6f7964d37cd0d966b7a 100644 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.110 2006/04/24 22:24:58 momjian Exp $ + * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.111 2006/05/10 23:18:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -168,6 +168,7 @@ extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple, Buffer *buffer, ItemPointer ctid, TransactionId *update_xmax, CommandId cid, LockTupleMode mode, bool nowait); +extern void heap_inplace_update(Relation relation, HeapTuple tuple); extern Oid simple_heap_insert(Relation relation, HeapTuple tup); extern void simple_heap_delete(Relation relation, ItemPointer tid); diff --git a/src/include/access/htup.h b/src/include/access/htup.h index 9dacfcb8521c607a7b040c8d732a2c31edca595c..57db9f8493469bb14aaa3750c92bcb4d5e2d38e0 100644 --- a/src/include/access/htup.h +++ b/src/include/access/htup.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/access/htup.h,v 1.81 2006/03/05 15:58:53 momjian Exp $ + * $PostgreSQL: pgsql/src/include/access/htup.h,v 1.82 2006/05/10 23:18:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -444,7 +444,7 @@ typedef HeapTupleData *HeapTuple; #define XLOG_HEAP_CLEAN 0x40 #define XLOG_HEAP_NEWPAGE 0x50 #define XLOG_HEAP_LOCK 0x60 -/* opcode 0x70 still free */ +#define XLOG_HEAP_INPLACE 0x70 #define XLOG_HEAP_OPMASK 0x70 /* * When we insert 1st item on new page in INSERT/UPDATE @@ -545,4 +545,13 @@ typedef struct xl_heap_lock #define SizeOfHeapLock (offsetof(xl_heap_lock, shared_lock) + sizeof(bool)) +/* This is what we need to know about in-place update */ +typedef struct xl_heap_inplace +{ + xl_heaptid target; /* updated tuple id */ + /* TUPLE DATA FOLLOWS AT END OF STRUCT */ +} xl_heap_inplace; + +#define SizeOfHeapInplace (offsetof(xl_heap_inplace, target) + SizeOfHeapTid) + #endif /* HTUP_H */ diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index d7ea6f5846db526af6e41433b99cade3c2168960..6819426d49eff200e7e38f164a1c7e07b2b0f3d3 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * index.h - * prototypes for index.c. + * prototypes for catalog/index.c. * * * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/index.h,v 1.65 2006/03/05 15:58:54 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/index.h,v 1.66 2006/05/10 23:18:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -37,7 +37,8 @@ extern Oid index_create(Oid heapRelationId, Oid accessMethodObjectId, Oid tableSpaceId, Oid *classObjectId, - bool primary, + bool isprimary, + bool istoast, bool isconstraint, bool allow_system_table_mods, bool skip_build); @@ -52,16 +53,13 @@ extern void FormIndexDatum(IndexInfo *indexInfo, Datum *values, bool *isnull); -extern void IndexCloseAndUpdateStats(Relation heap, double heapTuples, - Relation index, double indexTuples); - -extern void setRelhasindex(Oid relid, bool hasindex, - bool isprimary, Oid reltoastidxid); - extern void setNewRelfilenode(Relation relation); -extern void index_build(Relation heapRelation, Relation indexRelation, - IndexInfo *indexInfo); +extern void index_build(Relation heapRelation, + Relation indexRelation, + IndexInfo *indexInfo, + bool isprimary, + bool istoast); extern double IndexBuildHeapScan(Relation heapRelation, Relation indexRelation, diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index ade008fa4e6af1cbad48b2c66a3ca167c3c4d2cc..09d57d940da589a67640a055f9eb98669e5635a6 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.410 2006/05/02 22:25:10 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.411 2006/05/10 23:18:39 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -672,7 +672,7 @@ DATA(insert OID = 336 ( btmarkpos PGNSP PGUID 12 f f t f v 1 2278 "2281" _n DESCR("btree(internal)"); DATA(insert OID = 337 ( btrestrpos PGNSP PGUID 12 f f t f v 1 2278 "2281" _null_ _null_ _null_ btrestrpos - _null_ )); DESCR("btree(internal)"); -DATA(insert OID = 338 ( btbuild PGNSP PGUID 12 f f t f v 3 2278 "2281 2281 2281" _null_ _null_ _null_ btbuild - _null_ )); +DATA(insert OID = 338 ( btbuild PGNSP PGUID 12 f f t f v 3 2281 "2281 2281 2281" _null_ _null_ _null_ btbuild - _null_ )); DESCR("btree(internal)"); DATA(insert OID = 332 ( btbulkdelete PGNSP PGUID 12 f f t f v 4 2281 "2281 2281 2281 2281" _null_ _null_ _null_ btbulkdelete - _null_ )); DESCR("btree(internal)"); @@ -789,7 +789,7 @@ DATA(insert OID = 446 ( hashmarkpos PGNSP PGUID 12 f f t f v 1 2278 "2281" _ DESCR("hash(internal)"); DATA(insert OID = 447 ( hashrestrpos PGNSP PGUID 12 f f t f v 1 2278 "2281" _null_ _null_ _null_ hashrestrpos - _null_ )); DESCR("hash(internal)"); -DATA(insert OID = 448 ( hashbuild PGNSP PGUID 12 f f t f v 3 2278 "2281 2281 2281" _null_ _null_ _null_ hashbuild - _null_ )); +DATA(insert OID = 448 ( hashbuild PGNSP PGUID 12 f f t f v 3 2281 "2281 2281 2281" _null_ _null_ _null_ hashbuild - _null_ )); DESCR("hash(internal)"); DATA(insert OID = 442 ( hashbulkdelete PGNSP PGUID 12 f f t f v 4 2281 "2281 2281 2281 2281" _null_ _null_ _null_ hashbulkdelete - _null_ )); DESCR("hash(internal)"); @@ -1055,7 +1055,7 @@ DATA(insert OID = 780 ( gistmarkpos PGNSP PGUID 12 f f t f v 1 2278 "2281" _ DESCR("gist(internal)"); DATA(insert OID = 781 ( gistrestrpos PGNSP PGUID 12 f f t f v 1 2278 "2281" _null_ _null_ _null_ gistrestrpos - _null_ )); DESCR("gist(internal)"); -DATA(insert OID = 782 ( gistbuild PGNSP PGUID 12 f f t f v 3 2278 "2281 2281 2281" _null_ _null_ _null_ gistbuild - _null_ )); +DATA(insert OID = 782 ( gistbuild PGNSP PGUID 12 f f t f v 3 2281 "2281 2281 2281" _null_ _null_ _null_ gistbuild - _null_ )); DESCR("gist(internal)"); DATA(insert OID = 776 ( gistbulkdelete PGNSP PGUID 12 f f t f v 4 2281 "2281 2281 2281 2281" _null_ _null_ _null_ gistbulkdelete - _null_ )); DESCR("gist(internal)"); @@ -3833,7 +3833,7 @@ DATA(insert OID = 2736 ( ginmarkpos PGNSP PGUID 12 f f t f v 1 2278 "2281" _ DESCR("gin(internal)"); DATA(insert OID = 2737 ( ginrestrpos PGNSP PGUID 12 f f t f v 1 2278 "2281" _null_ _null_ _null_ ginrestrpos - _null_ )); DESCR("gin(internal)"); -DATA(insert OID = 2738 ( ginbuild PGNSP PGUID 12 f f t f v 3 2278 "2281 2281 2281" _null_ _null_ _null_ ginbuild - _null_ )); +DATA(insert OID = 2738 ( ginbuild PGNSP PGUID 12 f f t f v 3 2281 "2281 2281 2281" _null_ _null_ _null_ ginbuild - _null_ )); DESCR("gin(internal)"); DATA(insert OID = 2739 ( ginbulkdelete PGNSP PGUID 12 f f t f v 4 2281 "2281 2281 2281 2281" _null_ _null_ _null_ ginbulkdelete - _null_ )); DESCR("gin(internal)");