From 2ef085d0e6960f5087c97266a7211d37ddaa9f68 Mon Sep 17 00:00:00 2001 From: Fujii Masao Date: Thu, 4 Jul 2013 03:24:09 +0900 Subject: [PATCH] Get rid of pg_class.reltoastidxid. Treat TOAST index just the same as normal one and get the OID of TOAST index from pg_index but not pg_class.reltoastidxid. This change allows us to handle multiple TOAST indexes, and which is required infrastructure for upcoming REINDEX CONCURRENTLY feature. Patch by Michael Paquier, reviewed by Andres Freund and me. --- contrib/pg_upgrade/info.c | 15 +- doc/src/sgml/catalogs.sgml | 9 - doc/src/sgml/diskusage.sgml | 26 +-- doc/src/sgml/monitoring.sgml | 4 +- src/backend/access/heap/tuptoaster.c | 225 +++++++++++++++++++++---- src/backend/catalog/heap.c | 1 - src/backend/catalog/index.c | 23 +-- src/backend/catalog/system_views.sql | 10 +- src/backend/commands/cluster.c | 34 ++-- src/backend/commands/tablecmds.c | 20 ++- src/backend/rewrite/rewriteDefine.c | 5 +- src/backend/utils/adt/dbsize.c | 24 ++- src/bin/pg_dump/pg_dump.c | 14 +- src/include/access/tuptoaster.h | 9 + src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_class.h | 44 +++-- src/test/regress/expected/oidjoins.out | 8 - src/test/regress/expected/rules.out | 16 +- src/test/regress/sql/oidjoins.sql | 4 - src/test/regress/sql/rules.sql | 2 +- src/tools/findoidjoins/README | 1 - 21 files changed, 333 insertions(+), 163 deletions(-) diff --git a/contrib/pg_upgrade/info.c b/contrib/pg_upgrade/info.c index c381f112f1..18daf1c32f 100644 --- a/contrib/pg_upgrade/info.c +++ b/contrib/pg_upgrade/info.c @@ -321,12 +321,19 @@ get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo) "INSERT INTO info_rels " "SELECT reltoastrelid " "FROM info_rels i JOIN pg_catalog.pg_class c " - " ON i.reloid = c.oid")); + " ON i.reloid = c.oid " + " AND c.reltoastrelid != %u", InvalidOid)); PQclear(executeQueryOrDie(conn, "INSERT INTO info_rels " - "SELECT reltoastidxid " - "FROM info_rels i JOIN pg_catalog.pg_class c " - " ON i.reloid = c.oid")); + "SELECT indexrelid " + "FROM pg_index " + "WHERE indisvalid " + " AND indrelid IN (SELECT reltoastrelid " + " FROM info_rels i " + " JOIN pg_catalog.pg_class c " + " ON i.reloid = c.oid " + " AND c.reltoastrelid != %u)", + InvalidOid)); snprintf(query, sizeof(query), "SELECT c.oid, n.nspname, c.relname, " diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 09f7e40b29..67157829fd 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1744,15 +1744,6 @@ - - reltoastidxid - oid - pg_class.oid - - For a TOAST table, the OID of its index. 0 if not a TOAST table. - - - relhasindex bool diff --git a/doc/src/sgml/diskusage.sgml b/doc/src/sgml/diskusage.sgml index de1d0b4b00..461deb9dba 100644 --- a/doc/src/sgml/diskusage.sgml +++ b/doc/src/sgml/diskusage.sgml @@ -20,12 +20,12 @@ stored. If the table has any columns with potentially-wide values, there also might be a TOAST file associated with the table, which is used to store values too wide to fit comfortably in the main - table (see ). There will be one index on the - TOAST table, if present. There also might be indexes associated - with the base table. Each table and index is stored in a separate disk - file — possibly more than one file, if the file would exceed one - gigabyte. Naming conventions for these files are described in . + table (see ). There will be one valid index + on the TOAST table, if present. There also might be indexes + associated with the base table. Each table and index is stored in a + separate disk file — possibly more than one file, if the file would + exceed one gigabyte. Naming conventions for these files are described + in . @@ -44,7 +44,7 @@ SELECT pg_relation_filepath(oid), relpages FROM pg_class WHERE relname = 'customer'; - pg_relation_filepath | relpages + pg_relation_filepath | relpages ----------------------+---------- base/16384/16806 | 60 (1 row) @@ -65,12 +65,12 @@ FROM pg_class, FROM pg_class WHERE relname = 'customer') AS ss WHERE oid = ss.reltoastrelid OR - oid = (SELECT reltoastidxid - FROM pg_class - WHERE oid = ss.reltoastrelid) + oid = (SELECT indexrelid + FROM pg_index + WHERE indrelid = ss.reltoastrelid) ORDER BY relname; - relname | relpages + relname | relpages ----------------------+---------- pg_toast_16806 | 0 pg_toast_16806_index | 1 @@ -87,7 +87,7 @@ WHERE c.relname = 'customer' AND c2.oid = i.indexrelid ORDER BY c2.relname; - relname | relpages + relname | relpages ----------------------+---------- customer_id_indexdex | 26 @@ -101,7 +101,7 @@ SELECT relname, relpages FROM pg_class ORDER BY relpages DESC; - relname | relpages + relname | relpages ----------------------+---------- bigtable | 3290 customer | 3144 diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index b37b6c301c..d38c009db3 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -1163,12 +1163,12 @@ postgres: user database host tidx_blks_read bigint - Number of disk blocks read from this table's TOAST table index (if any) + Number of disk blocks read from this table's TOAST table indexes (if any) tidx_blks_hit bigint - Number of buffer hits in this table's TOAST table index (if any) + Number of buffer hits in this table's TOAST table indexes (if any) diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c index 445a7ed9fb..675bfcc81f 100644 --- a/src/backend/access/heap/tuptoaster.c +++ b/src/backend/access/heap/tuptoaster.c @@ -78,6 +78,12 @@ static bool toastid_valueid_exists(Oid toastrelid, Oid valueid); static struct varlena *toast_fetch_datum(struct varlena * attr); static struct varlena *toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length); +static int toast_open_indexes(Relation toastrel, + LOCKMODE lock, + Relation **toastidxs, + int *num_indexes); +static void toast_close_indexes(Relation *toastidxs, int num_indexes, + LOCKMODE lock); /* ---------- @@ -1286,6 +1292,39 @@ toast_compress_datum(Datum value) } +/* ---------- + * toast_get_valid_index + * + * Get OID of valid index associated to given toast relation. A toast + * relation can have only one valid index at the same time. + */ +Oid +toast_get_valid_index(Oid toastoid, LOCKMODE lock) +{ + int num_indexes; + int validIndex; + Oid validIndexOid; + Relation *toastidxs; + Relation toastrel; + + /* Open the toast relation */ + toastrel = heap_open(toastoid, lock); + + /* Look for the valid index of the toast relation */ + validIndex = toast_open_indexes(toastrel, + lock, + &toastidxs, + &num_indexes); + validIndexOid = RelationGetRelid(toastidxs[validIndex]); + + /* Close the toast relation and all its indexes */ + toast_close_indexes(toastidxs, num_indexes, lock); + heap_close(toastrel, lock); + + return validIndexOid; +} + + /* ---------- * toast_save_datum - * @@ -1303,7 +1342,7 @@ toast_save_datum(Relation rel, Datum value, struct varlena * oldexternal, int options) { Relation toastrel; - Relation toastidx; + Relation *toastidxs; HeapTuple toasttup; TupleDesc toasttupDesc; Datum t_values[3]; @@ -1322,17 +1361,24 @@ toast_save_datum(Relation rel, Datum value, char *data_p; int32 data_todo; Pointer dval = DatumGetPointer(value); + int num_indexes; + int validIndex; Assert(!VARATT_IS_EXTERNAL(value)); /* - * Open the toast relation and its index. We can use the index to check + * Open the toast relation and its indexes. We can use the index to check * uniqueness of the OID we assign to the toasted item, even though it has * additional columns besides OID. */ toastrel = heap_open(rel->rd_rel->reltoastrelid, RowExclusiveLock); toasttupDesc = toastrel->rd_att; - toastidx = index_open(toastrel->rd_rel->reltoastidxid, RowExclusiveLock); + + /* Open all the toast indexes and look for the valid */ + validIndex = toast_open_indexes(toastrel, + RowExclusiveLock, + &toastidxs, + &num_indexes); /* * Get the data pointer and length, and compute va_rawsize and va_extsize. @@ -1397,7 +1443,7 @@ toast_save_datum(Relation rel, Datum value, /* normal case: just choose an unused OID */ toast_pointer.va_valueid = GetNewOidWithIndex(toastrel, - RelationGetRelid(toastidx), + RelationGetRelid(toastidxs[validIndex]), (AttrNumber) 1); } else @@ -1451,7 +1497,7 @@ toast_save_datum(Relation rel, Datum value, { toast_pointer.va_valueid = GetNewOidWithIndex(toastrel, - RelationGetRelid(toastidx), + RelationGetRelid(toastidxs[validIndex]), (AttrNumber) 1); } while (toastid_valueid_exists(rel->rd_toastoid, toast_pointer.va_valueid)); @@ -1472,6 +1518,8 @@ toast_save_datum(Relation rel, Datum value, */ while (data_todo > 0) { + int i; + /* * Calculate the size of this chunk */ @@ -1490,16 +1538,22 @@ toast_save_datum(Relation rel, Datum value, /* * Create the index entry. We cheat a little here by not using * FormIndexDatum: this relies on the knowledge that the index columns - * are the same as the initial columns of the table. + * are the same as the initial columns of the table for all the + * indexes. * * Note also that there had better not be any user-created index on * the TOAST table, since we don't bother to update anything else. */ - index_insert(toastidx, t_values, t_isnull, - &(toasttup->t_self), - toastrel, - toastidx->rd_index->indisunique ? - UNIQUE_CHECK_YES : UNIQUE_CHECK_NO); + for (i = 0; i < num_indexes; i++) + { + /* Only index relations marked as ready can updated */ + if (IndexIsReady(toastidxs[i]->rd_index)) + index_insert(toastidxs[i], t_values, t_isnull, + &(toasttup->t_self), + toastrel, + toastidxs[i]->rd_index->indisunique ? + UNIQUE_CHECK_YES : UNIQUE_CHECK_NO); + } /* * Free memory @@ -1514,9 +1568,9 @@ toast_save_datum(Relation rel, Datum value, } /* - * Done - close toast relation + * Done - close toast relation and its indexes */ - index_close(toastidx, RowExclusiveLock); + toast_close_indexes(toastidxs, num_indexes, RowExclusiveLock); heap_close(toastrel, RowExclusiveLock); /* @@ -1542,10 +1596,12 @@ toast_delete_datum(Relation rel, Datum value) struct varlena *attr = (struct varlena *) DatumGetPointer(value); struct varatt_external toast_pointer; Relation toastrel; - Relation toastidx; + Relation *toastidxs; ScanKeyData toastkey; SysScanDesc toastscan; HeapTuple toasttup; + int num_indexes; + int validIndex; if (!VARATT_IS_EXTERNAL_ONDISK(attr)) return; @@ -1554,10 +1610,15 @@ toast_delete_datum(Relation rel, Datum value) VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr); /* - * Open the toast relation and its index + * Open the toast relation and its indexes */ toastrel = heap_open(toast_pointer.va_toastrelid, RowExclusiveLock); - toastidx = index_open(toastrel->rd_rel->reltoastidxid, RowExclusiveLock); + + /* Fetch valid relation used for process */ + validIndex = toast_open_indexes(toastrel, + RowExclusiveLock, + &toastidxs, + &num_indexes); /* * Setup a scan key to find chunks with matching va_valueid @@ -1572,7 +1633,7 @@ toast_delete_datum(Relation rel, Datum value) * sequence or not, but since we've already locked the index we might as * well use systable_beginscan_ordered.) */ - toastscan = systable_beginscan_ordered(toastrel, toastidx, + toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex], SnapshotToast, 1, &toastkey); while ((toasttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL) { @@ -1586,7 +1647,7 @@ toast_delete_datum(Relation rel, Datum value) * End scan and close relations */ systable_endscan_ordered(toastscan); - index_close(toastidx, RowExclusiveLock); + toast_close_indexes(toastidxs, num_indexes, RowExclusiveLock); heap_close(toastrel, RowExclusiveLock); } @@ -1603,6 +1664,15 @@ toastrel_valueid_exists(Relation toastrel, Oid valueid) bool result = false; ScanKeyData toastkey; SysScanDesc toastscan; + int num_indexes; + int validIndex; + Relation *toastidxs; + + /* Fetch a valid index relation */ + validIndex = toast_open_indexes(toastrel, + RowExclusiveLock, + &toastidxs, + &num_indexes); /* * Setup a scan key to find chunks with matching va_valueid @@ -1615,14 +1685,18 @@ toastrel_valueid_exists(Relation toastrel, Oid valueid) /* * Is there any such chunk? */ - toastscan = systable_beginscan(toastrel, toastrel->rd_rel->reltoastidxid, - true, SnapshotToast, 1, &toastkey); + toastscan = systable_beginscan(toastrel, + RelationGetRelid(toastidxs[validIndex]), + true, SnapshotToast, 1, &toastkey); if (systable_getnext(toastscan) != NULL) result = true; systable_endscan(toastscan); + /* Clean up */ + toast_close_indexes(toastidxs, num_indexes, RowExclusiveLock); + return result; } @@ -1659,7 +1733,7 @@ static struct varlena * toast_fetch_datum(struct varlena * attr) { Relation toastrel; - Relation toastidx; + Relation *toastidxs; ScanKeyData toastkey; SysScanDesc toastscan; HeapTuple ttup; @@ -1674,6 +1748,8 @@ toast_fetch_datum(struct varlena * attr) bool isnull; char *chunkdata; int32 chunksize; + int num_indexes; + int validIndex; if (VARATT_IS_EXTERNAL_INDIRECT(attr)) elog(ERROR, "shouldn't be called for indirect tuples"); @@ -1692,11 +1768,16 @@ toast_fetch_datum(struct varlena * attr) SET_VARSIZE(result, ressize + VARHDRSZ); /* - * Open the toast relation and its index + * Open the toast relation and its indexes */ toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock); toasttupDesc = toastrel->rd_att; - toastidx = index_open(toastrel->rd_rel->reltoastidxid, AccessShareLock); + + /* Look for the valid index of the toast relation */ + validIndex = toast_open_indexes(toastrel, + AccessShareLock, + &toastidxs, + &num_indexes); /* * Setup a scan key to fetch from the index by va_valueid @@ -1715,7 +1796,7 @@ toast_fetch_datum(struct varlena * attr) */ nextidx = 0; - toastscan = systable_beginscan_ordered(toastrel, toastidx, + toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex], SnapshotToast, 1, &toastkey); while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL) { @@ -1804,7 +1885,7 @@ toast_fetch_datum(struct varlena * attr) * End scan and close relations */ systable_endscan_ordered(toastscan); - index_close(toastidx, AccessShareLock); + toast_close_indexes(toastidxs, num_indexes, AccessShareLock); heap_close(toastrel, AccessShareLock); return result; @@ -1821,7 +1902,7 @@ static struct varlena * toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length) { Relation toastrel; - Relation toastidx; + Relation *toastidxs; ScanKeyData toastkey[3]; int nscankeys; SysScanDesc toastscan; @@ -1844,6 +1925,8 @@ toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length) int32 chunksize; int32 chcpystrt; int32 chcpyend; + int num_indexes; + int validIndex; Assert(VARATT_IS_EXTERNAL_ONDISK(attr)); @@ -1886,11 +1969,16 @@ toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length) endoffset = (sliceoffset + length - 1) % TOAST_MAX_CHUNK_SIZE; /* - * Open the toast relation and its index + * Open the toast relation and its indexes */ toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock); toasttupDesc = toastrel->rd_att; - toastidx = index_open(toastrel->rd_rel->reltoastidxid, AccessShareLock); + + /* Look for the valid index of toast relation */ + validIndex = toast_open_indexes(toastrel, + AccessShareLock, + &toastidxs, + &num_indexes); /* * Setup a scan key to fetch from the index. This is either two keys or @@ -1931,7 +2019,7 @@ toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length) * The index is on (valueid, chunkidx) so they will come in order */ nextidx = startchunk; - toastscan = systable_beginscan_ordered(toastrel, toastidx, + toastscan = systable_beginscan_ordered(toastrel, toastidxs[validIndex], SnapshotToast, nscankeys, toastkey); while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL) { @@ -2028,8 +2116,85 @@ toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length) * End scan and close relations */ systable_endscan_ordered(toastscan); - index_close(toastidx, AccessShareLock); + toast_close_indexes(toastidxs, num_indexes, AccessShareLock); heap_close(toastrel, AccessShareLock); return result; } + +/* ---------- + * toast_open_indexes + * + * Get an array of the indexes associated to the given toast relation + * and return as well the position of the valid index used by the toast + * relation in this array. It is the responsibility of the caller of this + * function to close the indexes as well as free them. + */ +static int +toast_open_indexes(Relation toastrel, + LOCKMODE lock, + Relation **toastidxs, + int *num_indexes) +{ + int i = 0; + int res = 0; + bool found = false; + List *indexlist; + ListCell *lc; + + /* Get index list of the toast relation */ + indexlist = RelationGetIndexList(toastrel); + Assert(indexlist != NIL); + + *num_indexes = list_length(indexlist); + + /* Open all the index relations */ + *toastidxs = (Relation *) palloc(*num_indexes * sizeof(Relation)); + foreach(lc, indexlist) + (*toastidxs)[i++] = index_open(lfirst_oid(lc), lock); + + /* Fetch the first valid index in list */ + for (i = 0; i < *num_indexes; i++) + { + Relation toastidx = *toastidxs[i]; + if (toastidx->rd_index->indisvalid) + { + res = i; + found = true; + break; + } + } + + /* + * Free index list, not necessary anymore as relations are opened + * and a valid index has been found. + */ + list_free(indexlist); + + /* + * The toast relation should have one valid index, so something is + * going wrong if there is nothing. + */ + if (!found) + elog(ERROR, "no valid index found for toast relation with Oid %d", + RelationGetRelid(toastrel)); + + return res; +} + +/* ---------- + * toast_close_indexes + * + * Close an array of indexes for a toast relation and free it. This should + * be called for a set of indexes opened previously with toast_open_indexes. + */ +static void +toast_close_indexes(Relation *toastidxs, int num_indexes, LOCKMODE lock) +{ + int i; + + /* Close relations and clean up things */ + for (i = 0; i < num_indexes; i++) + index_close(toastidxs[i], lock); + pfree(toastidxs); +} diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 4fd42ed1af..f1cdef9e13 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -781,7 +781,6 @@ InsertPgClassTuple(Relation pg_class_desc, values[Anum_pg_class_reltuples - 1] = Float4GetDatum(rd_rel->reltuples); values[Anum_pg_class_relallvisible - 1] = Int32GetDatum(rd_rel->relallvisible); values[Anum_pg_class_reltoastrelid - 1] = ObjectIdGetDatum(rd_rel->reltoastrelid); - values[Anum_pg_class_reltoastidxid - 1] = ObjectIdGetDatum(rd_rel->reltoastidxid); values[Anum_pg_class_relhasindex - 1] = BoolGetDatum(rd_rel->relhasindex); values[Anum_pg_class_relisshared - 1] = BoolGetDatum(rd_rel->relisshared); values[Anum_pg_class_relpersistence - 1] = CharGetDatum(rd_rel->relpersistence); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index ca0c672c38..8525cb9ec8 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -103,7 +103,7 @@ static void UpdateIndexRelation(Oid indexoid, Oid heapoid, bool isvalid); static void index_update_stats(Relation rel, bool hasindex, bool isprimary, - Oid reltoastidxid, double reltuples); + double reltuples); static void IndexCheckExclusion(Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo); @@ -1072,7 +1072,6 @@ index_create(Relation heapRelation, index_update_stats(heapRelation, true, isprimary, - InvalidOid, -1.0); /* Make the above update visible */ CommandCounterIncrement(); @@ -1254,7 +1253,6 @@ index_constraint_create(Relation heapRelation, index_update_stats(heapRelation, true, true, - InvalidOid, -1.0); /* @@ -1764,8 +1762,6 @@ FormIndexDatum(IndexInfo *indexInfo, * * 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: if >= 0, set reltuples to this value; else no change * * If reltuples >= 0, relpages and relallvisible are also updated (using @@ -1781,8 +1777,9 @@ FormIndexDatum(IndexInfo *indexInfo, */ static void index_update_stats(Relation rel, - bool hasindex, bool isprimary, - Oid reltoastidxid, double reltuples) + bool hasindex, + bool isprimary, + double reltuples) { Oid relid = RelationGetRelid(rel); Relation pg_class; @@ -1876,15 +1873,6 @@ index_update_stats(Relation rel, dirty = true; } } - if (OidIsValid(reltoastidxid)) - { - Assert(rd_rel->relkind == RELKIND_TOASTVALUE); - if (rd_rel->reltoastidxid != reltoastidxid) - { - rd_rel->reltoastidxid = reltoastidxid; - dirty = true; - } - } if (reltuples >= 0) { @@ -2072,14 +2060,11 @@ index_build(Relation heapRelation, index_update_stats(heapRelation, true, isprimary, - (heapRelation->rd_rel->relkind == RELKIND_TOASTVALUE) ? - RelationGetRelid(indexRelation) : InvalidOid, stats->heap_tuples); index_update_stats(indexRelation, false, false, - InvalidOid, stats->index_tuples); /* Make the updated catalog row versions visible */ diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 81d7c4fec8..d3086f43dd 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -473,16 +473,16 @@ CREATE VIEW pg_statio_all_tables AS pg_stat_get_blocks_fetched(T.oid) - pg_stat_get_blocks_hit(T.oid) AS toast_blks_read, pg_stat_get_blocks_hit(T.oid) AS toast_blks_hit, - pg_stat_get_blocks_fetched(X.oid) - - pg_stat_get_blocks_hit(X.oid) AS tidx_blks_read, - pg_stat_get_blocks_hit(X.oid) AS tidx_blks_hit + sum(pg_stat_get_blocks_fetched(X.indexrelid) - + pg_stat_get_blocks_hit(X.indexrelid))::bigint AS tidx_blks_read, + sum(pg_stat_get_blocks_hit(X.indexrelid))::bigint AS tidx_blks_hit FROM pg_class C LEFT JOIN pg_index I ON C.oid = I.indrelid LEFT JOIN pg_class T ON C.reltoastrelid = T.oid LEFT JOIN - pg_class X ON T.reltoastidxid = X.oid + pg_index X ON T.oid = X.indrelid LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) WHERE C.relkind IN ('r', 't', 'm') - GROUP BY C.oid, N.nspname, C.relname, T.oid, X.oid; + GROUP BY C.oid, N.nspname, C.relname, T.oid, X.indrelid; CREATE VIEW pg_statio_sys_tables AS SELECT * FROM pg_statio_all_tables diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index f23730c26f..686770f881 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -21,6 +21,7 @@ #include "access/relscan.h" #include "access/rewriteheap.h" #include "access/transam.h" +#include "access/tuptoaster.h" #include "access/xact.h" #include "catalog/catalog.h" #include "catalog/dependency.h" @@ -1177,8 +1178,6 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class, swaptemp = relform1->reltoastrelid; relform1->reltoastrelid = relform2->reltoastrelid; relform2->reltoastrelid = swaptemp; - - /* we should NOT swap reltoastidxid */ } } else @@ -1398,18 +1397,30 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class, /* * If we're swapping two toast tables by content, do the same for their - * indexes. + * valid index. The swap can actually be safely done only if the relations + * have indexes. */ if (swap_toast_by_content && - relform1->reltoastidxid && relform2->reltoastidxid) - swap_relation_files(relform1->reltoastidxid, - relform2->reltoastidxid, + relform1->relkind == RELKIND_TOASTVALUE && + relform2->relkind == RELKIND_TOASTVALUE) + { + Oid toastIndex1, toastIndex2; + + /* Get valid index for each relation */ + toastIndex1 = toast_get_valid_index(r1, + AccessExclusiveLock); + toastIndex2 = toast_get_valid_index(r2, + AccessExclusiveLock); + + swap_relation_files(toastIndex1, + toastIndex2, target_is_pg_class, swap_toast_by_content, is_internal, InvalidTransactionId, InvalidMultiXactId, mapped_tables); + } /* Clean up. */ heap_freetuple(reltup1); @@ -1533,14 +1544,12 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, newrel = heap_open(OIDOldHeap, NoLock); if (OidIsValid(newrel->rd_rel->reltoastrelid)) { - Relation toastrel; Oid toastidx; char NewToastName[NAMEDATALEN]; - toastrel = relation_open(newrel->rd_rel->reltoastrelid, - AccessShareLock); - toastidx = toastrel->rd_rel->reltoastidxid; - relation_close(toastrel, AccessShareLock); + /* Get the associated valid index to be renamed */ + toastidx = toast_get_valid_index(newrel->rd_rel->reltoastrelid, + AccessShareLock); /* rename the toast table ... */ snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u", @@ -1548,9 +1557,10 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, RenameRelationInternal(newrel->rd_rel->reltoastrelid, NewToastName, true); - /* ... and its index too */ + /* ... and its valid index too. */ snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index", OIDOldHeap); + RenameRelationInternal(toastidx, NewToastName, true); } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 6a7aa44ccc..6708725d69 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8878,7 +8878,6 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode) Relation rel; Oid oldTableSpace; Oid reltoastrelid; - Oid reltoastidxid; Oid newrelfilenode; RelFileNode newrnode; SMgrRelation dstrel; @@ -8886,6 +8885,8 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode) HeapTuple tuple; Form_pg_class rd_rel; ForkNumber forkNum; + List *reltoastidxids = NIL; + ListCell *lc; /* * Need lock here in case we are recursing to toast table or index @@ -8932,7 +8933,13 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode) errmsg("cannot move temporary tables of other sessions"))); reltoastrelid = rel->rd_rel->reltoastrelid; - reltoastidxid = rel->rd_rel->reltoastidxid; + /* Fetch the list of indexes on toast relation if necessary */ + if (OidIsValid(reltoastrelid)) + { + Relation toastRel = relation_open(reltoastrelid, lockmode); + reltoastidxids = RelationGetIndexList(toastRel); + relation_close(toastRel, lockmode); + } /* Get a modifiable copy of the relation's pg_class row */ pg_class = heap_open(RelationRelationId, RowExclusiveLock); @@ -9010,11 +9017,14 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode) /* Make sure the reltablespace change is visible */ CommandCounterIncrement(); - /* Move associated toast relation and/or index, too */ + /* Move associated toast relation and/or indexes, too */ if (OidIsValid(reltoastrelid)) ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode); - if (OidIsValid(reltoastidxid)) - ATExecSetTableSpace(reltoastidxid, newTableSpace, lockmode); + foreach(lc, reltoastidxids) + ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode); + + /* Clean up */ + list_free(reltoastidxids); } /* diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 3157aba330..92396b39bd 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -579,8 +579,8 @@ DefineQueryRewrite(char *rulename, /* * Fix pg_class entry to look like a normal view's, including setting - * the correct relkind and removal of reltoastrelid/reltoastidxid of - * the toast table we potentially removed above. + * the correct relkind and removal of reltoastrelid of the toast table + * we potentially removed above. */ classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(event_relid)); if (!HeapTupleIsValid(classTup)) @@ -592,7 +592,6 @@ DefineQueryRewrite(char *rulename, classForm->reltuples = 0; classForm->relallvisible = 0; classForm->reltoastrelid = InvalidOid; - classForm->reltoastidxid = InvalidOid; classForm->relhasindex = false; classForm->relkind = RELKIND_VIEW; classForm->relhasoids = false; diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c index 5ddeffe482..34482abee3 100644 --- a/src/backend/utils/adt/dbsize.c +++ b/src/backend/utils/adt/dbsize.c @@ -332,7 +332,7 @@ pg_relation_size(PG_FUNCTION_ARGS) } /* - * Calculate total on-disk size of a TOAST relation, including its index. + * Calculate total on-disk size of a TOAST relation, including its indexes. * Must not be applied to non-TOAST relations. */ static int64 @@ -340,8 +340,9 @@ calculate_toast_table_size(Oid toastrelid) { int64 size = 0; Relation toastRel; - Relation toastIdxRel; ForkNumber forkNum; + ListCell *lc; + List *indexlist; toastRel = relation_open(toastrelid, AccessShareLock); @@ -351,12 +352,21 @@ calculate_toast_table_size(Oid toastrelid) toastRel->rd_backend, forkNum); /* toast index size, including FSM and VM size */ - toastIdxRel = relation_open(toastRel->rd_rel->reltoastidxid, AccessShareLock); - for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++) - size += calculate_relation_size(&(toastIdxRel->rd_node), - toastIdxRel->rd_backend, forkNum); + indexlist = RelationGetIndexList(toastRel); - relation_close(toastIdxRel, AccessShareLock); + /* Size is calculated using all the indexes available */ + foreach(lc, indexlist) + { + Relation toastIdxRel; + toastIdxRel = relation_open(lfirst_oid(lc), + AccessShareLock); + for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++) + size += calculate_relation_size(&(toastIdxRel->rd_node), + toastIdxRel->rd_backend, forkNum); + + relation_close(toastIdxRel, AccessShareLock); + } + list_free(indexlist); relation_close(toastRel, AccessShareLock); return size; diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 9ee9ea2baf..f40961ffce 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -2778,19 +2778,19 @@ binary_upgrade_set_pg_class_oids(Archive *fout, PQExpBuffer upgrade_query = createPQExpBuffer(); PGresult *upgrade_res; Oid pg_class_reltoastrelid; - Oid pg_class_reltoastidxid; + Oid pg_index_indexrelid; appendPQExpBuffer(upgrade_query, - "SELECT c.reltoastrelid, t.reltoastidxid " + "SELECT c.reltoastrelid, i.indexrelid " "FROM pg_catalog.pg_class c LEFT JOIN " - "pg_catalog.pg_class t ON (c.reltoastrelid = t.oid) " + "pg_catalog.pg_index i ON (c.reltoastrelid = i.indrelid AND i.indisvalid) " "WHERE c.oid = '%u'::pg_catalog.oid;", pg_class_oid); upgrade_res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data); pg_class_reltoastrelid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "reltoastrelid"))); - pg_class_reltoastidxid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "reltoastidxid"))); + pg_index_indexrelid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "indexrelid"))); appendPQExpBuffer(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_class oids\n"); @@ -2819,7 +2819,7 @@ binary_upgrade_set_pg_class_oids(Archive *fout, /* every toast table has an index */ appendPQExpBuffer(upgrade_buffer, "SELECT binary_upgrade.set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n", - pg_class_reltoastidxid); + pg_index_indexrelid); } } else @@ -13126,7 +13126,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) * attislocal correctly, plus fix up any inherited CHECK constraints. * Analogously, we set up typed tables using ALTER TABLE / OF here. */ - if (binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || + if (binary_upgrade && (tbinfo->relkind == RELKIND_RELATION || tbinfo->relkind == RELKIND_FOREIGN_TABLE) ) { for (j = 0; j < tbinfo->numatts; j++) @@ -13151,7 +13151,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) else appendPQExpBuffer(q, "ALTER FOREIGN TABLE %s ", fmtId(tbinfo->dobj.name)); - + appendPQExpBuffer(q, "DROP COLUMN %s;\n", fmtId(tbinfo->attnames[j])); } diff --git a/src/include/access/tuptoaster.h b/src/include/access/tuptoaster.h index d0c17fde03..b4e0242c14 100644 --- a/src/include/access/tuptoaster.h +++ b/src/include/access/tuptoaster.h @@ -15,6 +15,7 @@ #include "access/htup_details.h" #include "utils/relcache.h" +#include "storage/lock.h" /* * This enables de-toasting of index entries. Needed until VACUUM is @@ -193,4 +194,12 @@ extern Size toast_raw_datum_size(Datum value); */ extern Size toast_datum_size(Datum value); +/* ---------- + * toast_get_valid_index - + * + * Return OID of valid index associated to a toast relation + * ---------- + */ +extern Oid toast_get_valid_index(Oid toastoid, LOCKMODE lock); + #endif /* TUPTOASTER_H */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index d46fe9ede3..9358e95547 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201306121 +#define CATALOG_VERSION_NO 201307031 #endif diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index 2225787e40..49c4f6f136 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -48,7 +48,6 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO int32 relallvisible; /* # of all-visible blocks (not always * up-to-date) */ Oid reltoastrelid; /* OID of toast table; 0 if none */ - Oid reltoastidxid; /* if toast table, OID of chunk_id index */ bool relhasindex; /* T if has (or has had) any indexes */ bool relisshared; /* T if shared across databases */ char relpersistence; /* see RELPERSISTENCE_xxx constants below */ @@ -94,7 +93,7 @@ typedef FormData_pg_class *Form_pg_class; * ---------------- */ -#define Natts_pg_class 29 +#define Natts_pg_class 28 #define Anum_pg_class_relname 1 #define Anum_pg_class_relnamespace 2 #define Anum_pg_class_reltype 3 @@ -107,23 +106,22 @@ typedef FormData_pg_class *Form_pg_class; #define Anum_pg_class_reltuples 10 #define Anum_pg_class_relallvisible 11 #define Anum_pg_class_reltoastrelid 12 -#define Anum_pg_class_reltoastidxid 13 -#define Anum_pg_class_relhasindex 14 -#define Anum_pg_class_relisshared 15 -#define Anum_pg_class_relpersistence 16 -#define Anum_pg_class_relkind 17 -#define Anum_pg_class_relnatts 18 -#define Anum_pg_class_relchecks 19 -#define Anum_pg_class_relhasoids 20 -#define Anum_pg_class_relhaspkey 21 -#define Anum_pg_class_relhasrules 22 -#define Anum_pg_class_relhastriggers 23 -#define Anum_pg_class_relhassubclass 24 -#define Anum_pg_class_relispopulated 25 -#define Anum_pg_class_relfrozenxid 26 -#define Anum_pg_class_relminmxid 27 -#define Anum_pg_class_relacl 28 -#define Anum_pg_class_reloptions 29 +#define Anum_pg_class_relhasindex 13 +#define Anum_pg_class_relisshared 14 +#define Anum_pg_class_relpersistence 15 +#define Anum_pg_class_relkind 16 +#define Anum_pg_class_relnatts 17 +#define Anum_pg_class_relchecks 18 +#define Anum_pg_class_relhasoids 19 +#define Anum_pg_class_relhaspkey 20 +#define Anum_pg_class_relhasrules 21 +#define Anum_pg_class_relhastriggers 22 +#define Anum_pg_class_relhassubclass 23 +#define Anum_pg_class_relispopulated 24 +#define Anum_pg_class_relfrozenxid 25 +#define Anum_pg_class_relminmxid 26 +#define Anum_pg_class_relacl 27 +#define Anum_pg_class_reloptions 28 /* ---------------- * initial contents of pg_class @@ -138,13 +136,13 @@ typedef FormData_pg_class *Form_pg_class; * Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId; * similarly, "1" in relminmxid stands for FirstMultiXactId */ -DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 0 f f p r 30 0 t f f f f t 3 1 _null_ _null_ )); +DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f t 3 1 _null_ _null_ )); DESCR(""); -DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 0 f f p r 21 0 f f f f f t 3 1 _null_ _null_ )); +DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 21 0 f f f f f t 3 1 _null_ _null_ )); DESCR(""); -DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 0 f f p r 27 0 t f f f f t 3 1 _null_ _null_ )); +DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 27 0 t f f f f t 3 1 _null_ _null_ )); DESCR(""); -DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 0 f f p r 29 0 t f f f f t 3 1 _null_ _null_ )); +DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f p r 28 0 t f f f f t 3 1 _null_ _null_ )); DESCR(""); diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out index 06ed85677a..6c5cb5a873 100644 --- a/src/test/regress/expected/oidjoins.out +++ b/src/test/regress/expected/oidjoins.out @@ -353,14 +353,6 @@ WHERE reltoastrelid != 0 AND ------+--------------- (0 rows) -SELECT ctid, reltoastidxid -FROM pg_catalog.pg_class fk -WHERE reltoastidxid != 0 AND - NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.reltoastidxid); - ctid | reltoastidxid -------+--------------- -(0 rows) - SELECT ctid, collnamespace FROM pg_catalog.pg_collation fk WHERE collnamespace != 0 AND diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 57ae8427ec..4b182e7f54 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1852,15 +1852,15 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem | (sum(pg_stat_get_blocks_hit(i.indexrelid)))::bigint AS idx_blks_hit, + | (pg_stat_get_blocks_fetched(t.oid) - pg_stat_get_blocks_hit(t.oid)) AS toast_blks_read, + | pg_stat_get_blocks_hit(t.oid) AS toast_blks_hit, + - | (pg_stat_get_blocks_fetched(x.oid) - pg_stat_get_blocks_hit(x.oid)) AS tidx_blks_read, + - | pg_stat_get_blocks_hit(x.oid) AS tidx_blks_hit + + | (sum((pg_stat_get_blocks_fetched(x.indexrelid) - pg_stat_get_blocks_hit(x.indexrelid))))::bigint AS tidx_blks_read, + + | (sum(pg_stat_get_blocks_hit(x.indexrelid)))::bigint AS tidx_blks_hit + | FROM ((((pg_class c + | LEFT JOIN pg_index i ON ((c.oid = i.indrelid))) + | LEFT JOIN pg_class t ON ((c.reltoastrelid = t.oid))) + - | LEFT JOIN pg_class x ON ((t.reltoastidxid = x.oid))) + + | LEFT JOIN pg_index x ON ((t.oid = x.indrelid))) + | LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) + | WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char", 'm'::"char"])) + - | GROUP BY c.oid, n.nspname, c.relname, t.oid, x.oid; + | GROUP BY c.oid, n.nspname, c.relname, t.oid, x.indrelid; pg_statio_sys_indexes | SELECT pg_statio_all_indexes.relid, + | pg_statio_all_indexes.indexrelid, + | pg_statio_all_indexes.schemaname, + @@ -2347,11 +2347,11 @@ select xmin, * from fooview; -- fail, views don't have such a column ERROR: column "xmin" does not exist LINE 1: select xmin, * from fooview; ^ -select reltoastrelid, reltoastidxid, relkind, relfrozenxid +select reltoastrelid, relkind, relfrozenxid from pg_class where oid = 'fooview'::regclass; - reltoastrelid | reltoastidxid | relkind | relfrozenxid ----------------+---------------+---------+-------------- - 0 | 0 | v | 0 + reltoastrelid | relkind | relfrozenxid +---------------+---------+-------------- + 0 | v | 0 (1 row) drop view fooview; diff --git a/src/test/regress/sql/oidjoins.sql b/src/test/regress/sql/oidjoins.sql index 6422da26ad..9b91683ea5 100644 --- a/src/test/regress/sql/oidjoins.sql +++ b/src/test/regress/sql/oidjoins.sql @@ -177,10 +177,6 @@ SELECT ctid, reltoastrelid FROM pg_catalog.pg_class fk WHERE reltoastrelid != 0 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.reltoastrelid); -SELECT ctid, reltoastidxid -FROM pg_catalog.pg_class fk -WHERE reltoastidxid != 0 AND - NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.reltoastidxid); SELECT ctid, collnamespace FROM pg_catalog.pg_collation fk WHERE collnamespace != 0 AND diff --git a/src/test/regress/sql/rules.sql b/src/test/regress/sql/rules.sql index d5a3571087..6361297155 100644 --- a/src/test/regress/sql/rules.sql +++ b/src/test/regress/sql/rules.sql @@ -872,7 +872,7 @@ create rule "_RETURN" as on select to fooview do instead select * from fooview; select xmin, * from fooview; -- fail, views don't have such a column -select reltoastrelid, reltoastidxid, relkind, relfrozenxid +select reltoastrelid, relkind, relfrozenxid from pg_class where oid = 'fooview'::regclass; drop view fooview; diff --git a/src/tools/findoidjoins/README b/src/tools/findoidjoins/README index b5c4d1b805..e3e8a2adde 100644 --- a/src/tools/findoidjoins/README +++ b/src/tools/findoidjoins/README @@ -86,7 +86,6 @@ Join pg_catalog.pg_class.relowner => pg_catalog.pg_authid.oid Join pg_catalog.pg_class.relam => pg_catalog.pg_am.oid Join pg_catalog.pg_class.reltablespace => pg_catalog.pg_tablespace.oid Join pg_catalog.pg_class.reltoastrelid => pg_catalog.pg_class.oid -Join pg_catalog.pg_class.reltoastidxid => pg_catalog.pg_class.oid Join pg_catalog.pg_collation.collnamespace => pg_catalog.pg_namespace.oid Join pg_catalog.pg_collation.collowner => pg_catalog.pg_authid.oid Join pg_catalog.pg_constraint.connamespace => pg_catalog.pg_namespace.oid