Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit dd16b7a

Browse files
committed
Get rid of cluster.c's apparatus for rebuilding a relation's indexes
in favor of using the REINDEX TABLE apparatus, which does the same thing simpler and faster. Also, make TRUNCATE not use cluster.c at all, but just assign a new relfilenode and REINDEX. This partially addresses Hartmut Raschick's complaint from last December that 7.4's TRUNCATE is an order of magnitude slower than prior releases. By getting rid of a lot of unnecessary catalog updates, these changes buy back about a factor of two (on my system). The remaining overhead seems associated with creating and deleting storage files, which we may not be able to do much about without abandoning transaction safety for TRUNCATE.
1 parent 7c6baad commit dd16b7a

File tree

6 files changed

+161
-262
lines changed

6 files changed

+161
-262
lines changed

src/backend/catalog/index.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.229 2004/05/05 04:48:45 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.230 2004/05/08 00:34:49 tgl Exp $
1212
*
1313
*
1414
* INTERFACE ROUTINES
@@ -1729,12 +1729,13 @@ reindex_index(Oid indexId)
17291729

17301730
/*
17311731
* reindex_relation - This routine is used to recreate all indexes
1732-
* of a relation (and its toast relation too, if any).
1732+
* of a relation (and optionally its toast relation too, if any).
17331733
*
1734-
* Returns true if any indexes were rebuilt.
1734+
* Returns true if any indexes were rebuilt. Note that a
1735+
* CommandCounterIncrement will occur after each index rebuild.
17351736
*/
17361737
bool
1737-
reindex_relation(Oid relid)
1738+
reindex_relation(Oid relid, bool toast_too)
17381739
{
17391740
Relation rel;
17401741
Oid toast_relid;
@@ -1810,8 +1811,8 @@ reindex_relation(Oid relid)
18101811
* If the relation has a secondary toast rel, reindex that too while we
18111812
* still hold the lock on the master table.
18121813
*/
1813-
if (toast_relid != InvalidOid)
1814-
result |= reindex_relation(toast_relid);
1814+
if (toast_too && OidIsValid(toast_relid))
1815+
result |= reindex_relation(toast_relid, false);
18151816

18161817
return result;
18171818
}

src/backend/commands/cluster.c

Lines changed: 87 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*
1212
*
1313
* IDENTIFICATION
14-
* $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.122 2004/05/06 16:10:57 tgl Exp $
14+
* $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.123 2004/05/08 00:34:49 tgl Exp $
1515
*
1616
*-------------------------------------------------------------------------
1717
*/
@@ -37,20 +37,6 @@
3737
#include "utils/relcache.h"
3838

3939

40-
/*
41-
* We need one of these structs for each index in the relation to be
42-
* clustered. It's basically the data needed by index_create() so
43-
* we can rebuild the indexes on the new heap.
44-
*/
45-
typedef struct
46-
{
47-
Oid indexOID;
48-
char *indexName;
49-
IndexInfo *indexInfo;
50-
Oid accessMethodOID;
51-
Oid *classOID;
52-
} IndexAttrs;
53-
5440
/*
5541
* This struct is used to pass around the information on tables to be
5642
* clustered. We need this so we can make a list of them when invoked without
@@ -64,6 +50,7 @@ typedef struct
6450

6551

6652
static void cluster_rel(RelToCluster *rv, bool recheck);
53+
static void rebuild_relation(Relation OldHeap, Oid indexOid);
6754
static void copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex);
6855
static List *get_tables_to_cluster(MemoryContext cluster_context);
6956

@@ -411,30 +398,99 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid)
411398
}
412399

413400
/*
414-
* rebuild_relation: rebuild an existing relation
401+
* mark_index_clustered: mark the specified index as the one clustered on
415402
*
416-
* This is shared code between CLUSTER and TRUNCATE. In the TRUNCATE
417-
* case, the new relation is built and left empty. In the CLUSTER case,
418-
* it is filled with data read from the old relation in the order specified
419-
* by the index.
403+
* With indexOid == InvalidOid, will mark all indexes of rel not-clustered.
404+
*/
405+
void
406+
mark_index_clustered(Relation rel, Oid indexOid)
407+
{
408+
HeapTuple indexTuple;
409+
Form_pg_index indexForm;
410+
Relation pg_index;
411+
List *index;
412+
413+
/*
414+
* If the index is already marked clustered, no need to do anything.
415+
*/
416+
if (OidIsValid(indexOid))
417+
{
418+
indexTuple = SearchSysCache(INDEXRELID,
419+
ObjectIdGetDatum(indexOid),
420+
0, 0, 0);
421+
if (!HeapTupleIsValid(indexTuple))
422+
elog(ERROR, "cache lookup failed for index %u", indexOid);
423+
indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
424+
425+
if (indexForm->indisclustered)
426+
{
427+
ReleaseSysCache(indexTuple);
428+
return;
429+
}
430+
431+
ReleaseSysCache(indexTuple);
432+
}
433+
434+
/*
435+
* Check each index of the relation and set/clear the bit as needed.
436+
*/
437+
pg_index = heap_openr(IndexRelationName, RowExclusiveLock);
438+
439+
foreach(index, RelationGetIndexList(rel))
440+
{
441+
Oid thisIndexOid = lfirsto(index);
442+
443+
indexTuple = SearchSysCacheCopy(INDEXRELID,
444+
ObjectIdGetDatum(thisIndexOid),
445+
0, 0, 0);
446+
if (!HeapTupleIsValid(indexTuple))
447+
elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
448+
indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
449+
450+
/*
451+
* Unset the bit if set. We know it's wrong because we checked
452+
* this earlier.
453+
*/
454+
if (indexForm->indisclustered)
455+
{
456+
indexForm->indisclustered = false;
457+
simple_heap_update(pg_index, &indexTuple->t_self, indexTuple);
458+
CatalogUpdateIndexes(pg_index, indexTuple);
459+
/* Ensure we see the update in the index's relcache entry */
460+
CacheInvalidateRelcacheByRelid(thisIndexOid);
461+
}
462+
else if (thisIndexOid == indexOid)
463+
{
464+
indexForm->indisclustered = true;
465+
simple_heap_update(pg_index, &indexTuple->t_self, indexTuple);
466+
CatalogUpdateIndexes(pg_index, indexTuple);
467+
/* Ensure we see the update in the index's relcache entry */
468+
CacheInvalidateRelcacheByRelid(thisIndexOid);
469+
}
470+
heap_freetuple(indexTuple);
471+
}
472+
473+
heap_close(pg_index, RowExclusiveLock);
474+
}
475+
476+
/*
477+
* rebuild_relation: rebuild an existing relation in index order
420478
*
421479
* OldHeap: table to rebuild --- must be opened and exclusive-locked!
422-
* indexOid: index to cluster by, or InvalidOid in TRUNCATE case
480+
* indexOid: index to cluster by
423481
*
424482
* NB: this routine closes OldHeap at the right time; caller should not.
425483
*/
426-
void
484+
static void
427485
rebuild_relation(Relation OldHeap, Oid indexOid)
428486
{
429487
Oid tableOid = RelationGetRelid(OldHeap);
430-
Oid oldClusterIndex;
431-
List *indexes;
432488
Oid OIDNewHeap;
433489
char NewHeapName[NAMEDATALEN];
434490
ObjectAddress object;
435491

436-
/* Save the information about all indexes on the relation. */
437-
indexes = get_indexattr_list(OldHeap, &oldClusterIndex);
492+
/* Mark the correct index as clustered */
493+
mark_index_clustered(OldHeap, indexOid);
438494

439495
/* Close relcache entry, but keep lock until transaction commit */
440496
heap_close(OldHeap, NoLock);
@@ -459,8 +515,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid)
459515
/*
460516
* Copy the heap data into the new table in the desired order.
461517
*/
462-
if (OidIsValid(indexOid))
463-
copy_heap_data(OIDNewHeap, tableOid, indexOid);
518+
copy_heap_data(OIDNewHeap, tableOid, indexOid);
464519

465520
/* To make the new heap's data visible (probably not needed?). */
466521
CommandCounterIncrement();
@@ -484,11 +539,11 @@ rebuild_relation(Relation OldHeap, Oid indexOid)
484539
/* performDeletion does CommandCounterIncrement at end */
485540

486541
/*
487-
* Recreate each index on the relation. We do not need
488-
* CommandCounterIncrement() because rebuild_indexes does it.
542+
* Rebuild each index on the relation (but not the toast table,
543+
* which is all-new at this point). We do not need
544+
* CommandCounterIncrement() because reindex_relation does it.
489545
*/
490-
rebuild_indexes(tableOid, indexes,
491-
(OidIsValid(indexOid) ? indexOid : oldClusterIndex));
546+
reindex_relation(tableOid, false);
492547
}
493548

494549
/*
@@ -589,138 +644,6 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
589644
heap_close(NewHeap, NoLock);
590645
}
591646

592-
/*
593-
* Get the necessary info about the indexes of the relation and
594-
* return a list of IndexAttrs structures. Also, *OldClusterIndex
595-
* is set to the OID of the existing clustered index, or InvalidOid
596-
* if there is none.
597-
*/
598-
List *
599-
get_indexattr_list(Relation OldHeap, Oid *OldClusterIndex)
600-
{
601-
List *indexes = NIL;
602-
List *indlist;
603-
604-
*OldClusterIndex = InvalidOid;
605-
606-
/* Ask the relcache to produce a list of the indexes of the old rel */
607-
foreach(indlist, RelationGetIndexList(OldHeap))
608-
{
609-
Oid indexOID = lfirsto(indlist);
610-
Relation oldIndex;
611-
IndexAttrs *attrs;
612-
613-
oldIndex = index_open(indexOID);
614-
615-
attrs = (IndexAttrs *) palloc(sizeof(IndexAttrs));
616-
attrs->indexOID = indexOID;
617-
attrs->indexName = pstrdup(NameStr(oldIndex->rd_rel->relname));
618-
attrs->accessMethodOID = oldIndex->rd_rel->relam;
619-
attrs->indexInfo = BuildIndexInfo(oldIndex);
620-
attrs->classOID = (Oid *)
621-
palloc(sizeof(Oid) * attrs->indexInfo->ii_NumIndexAttrs);
622-
memcpy(attrs->classOID, oldIndex->rd_index->indclass,
623-
sizeof(Oid) * attrs->indexInfo->ii_NumIndexAttrs);
624-
if (oldIndex->rd_index->indisclustered)
625-
*OldClusterIndex = indexOID;
626-
627-
index_close(oldIndex);
628-
629-
indexes = lappend(indexes, attrs);
630-
}
631-
632-
return indexes;
633-
}
634-
635-
/*
636-
* Create new indexes and swap the filenodes with old indexes. Then drop
637-
* the new index (carrying the old index filenode along).
638-
*
639-
* OIDClusterIndex is the OID of the index to be marked as clustered, or
640-
* InvalidOid if none should be marked clustered.
641-
*/
642-
void
643-
rebuild_indexes(Oid OIDOldHeap, List *indexes, Oid OIDClusterIndex)
644-
{
645-
List *elem;
646-
647-
foreach(elem, indexes)
648-
{
649-
IndexAttrs *attrs = (IndexAttrs *) lfirst(elem);
650-
Oid oldIndexOID = attrs->indexOID;
651-
Oid newIndexOID;
652-
char newIndexName[NAMEDATALEN];
653-
bool isclustered;
654-
ObjectAddress object;
655-
Form_pg_index index;
656-
HeapTuple tuple;
657-
Relation pg_index;
658-
659-
/* Create the new index under a temporary name */
660-
snprintf(newIndexName, sizeof(newIndexName),
661-
"pg_temp_%u", oldIndexOID);
662-
663-
/*
664-
* The new index will have primary and constraint status set to
665-
* false, but since we will only use its filenode it doesn't
666-
* matter: after the filenode swap the index will keep the
667-
* constraint status of the old index.
668-
*/
669-
newIndexOID = index_create(OIDOldHeap,
670-
newIndexName,
671-
attrs->indexInfo,
672-
attrs->accessMethodOID,
673-
attrs->classOID,
674-
false,
675-
false,
676-
allowSystemTableMods,
677-
false);
678-
CommandCounterIncrement();
679-
680-
/* Swap the filenodes. */
681-
swap_relfilenodes(oldIndexOID, newIndexOID);
682-
683-
CommandCounterIncrement();
684-
685-
/*
686-
* Make sure that indisclustered is correct: it should be set only
687-
* for the index specified by the caller.
688-
*/
689-
isclustered = (oldIndexOID == OIDClusterIndex);
690-
691-
pg_index = heap_openr(IndexRelationName, RowExclusiveLock);
692-
tuple = SearchSysCacheCopy(INDEXRELID,
693-
ObjectIdGetDatum(oldIndexOID),
694-
0, 0, 0);
695-
if (!HeapTupleIsValid(tuple))
696-
elog(ERROR, "cache lookup failed for index %u", oldIndexOID);
697-
index = (Form_pg_index) GETSTRUCT(tuple);
698-
if (index->indisclustered != isclustered)
699-
{
700-
index->indisclustered = isclustered;
701-
simple_heap_update(pg_index, &tuple->t_self, tuple);
702-
CatalogUpdateIndexes(pg_index, tuple);
703-
/* Ensure we see the update in the index's relcache entry */
704-
CacheInvalidateRelcacheByRelid(oldIndexOID);
705-
}
706-
heap_freetuple(tuple);
707-
heap_close(pg_index, RowExclusiveLock);
708-
709-
/* Destroy new index with old filenode */
710-
object.classId = RelOid_pg_class;
711-
object.objectId = newIndexOID;
712-
object.objectSubId = 0;
713-
714-
/*
715-
* The relation is local to our transaction and we know nothing
716-
* depends on it, so DROP_RESTRICT should be OK.
717-
*/
718-
performDeletion(&object, DROP_RESTRICT);
719-
720-
/* performDeletion does CommandCounterIncrement() at its end */
721-
}
722-
}
723-
724647
/*
725648
* Swap the relfilenodes for two given relations.
726649
*

src/backend/commands/indexcmds.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.118 2004/05/05 04:48:45 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.119 2004/05/08 00:34:49 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -826,7 +826,7 @@ ReindexTable(RangeVar *relation, bool force /* currently unused */ )
826826

827827
ReleaseSysCache(tuple);
828828

829-
if (!reindex_relation(heapOid))
829+
if (!reindex_relation(heapOid, true))
830830
ereport(NOTICE,
831831
(errmsg("table \"%s\" has no indexes",
832832
relation->relname)));
@@ -936,7 +936,7 @@ ReindexDatabase(const char *dbname, bool force /* currently unused */,
936936
StartTransactionCommand();
937937
SetQuerySnapshot(); /* might be needed for functions in
938938
* indexes */
939-
if (reindex_relation(relid))
939+
if (reindex_relation(relid, true))
940940
ereport(NOTICE,
941941
(errmsg("table \"%s\" was reindexed",
942942
get_rel_name(relid))));

0 commit comments

Comments
 (0)