*** a/contrib/pg_upgrade/info.c --- b/contrib/pg_upgrade/info.c *************** *** 321,332 **** 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")); PQclear(executeQueryOrDie(conn, "INSERT INTO info_rels " ! "SELECT reltoastidxid " ! "FROM info_rels i JOIN pg_catalog.pg_class c " ! " ON i.reloid = c.oid")); snprintf(query, sizeof(query), "SELECT c.oid, n.nspname, c.relname, " --- 321,337 ---- "INSERT INTO info_rels " "SELECT reltoastrelid " "FROM info_rels i JOIN pg_catalog.pg_class c " ! " ON i.reloid = c.oid " ! " AND c.reltoastrelid != %u", InvalidOid)); PQclear(executeQueryOrDie(conn, "INSERT INTO info_rels " ! "SELECT indexrelid " ! "FROM pg_index " ! "WHERE indrelid IN (SELECT reltoastrelid " ! " FROM pg_class " ! " WHERE oid >= %u " ! " AND reltoastrelid != %u)", ! FirstNormalObjectId, InvalidOid)); snprintf(query, sizeof(query), "SELECT c.oid, n.nspname, c.relname, " *** a/doc/src/sgml/catalogs.sgml --- b/doc/src/sgml/catalogs.sgml *************** *** 1745,1759 **** - reltoastidxid - oid - pg_class.oid - - For a TOAST table, the OID of its index. 0 if not a TOAST table. - - - - relhasindex bool --- 1745,1750 ---- *** a/doc/src/sgml/diskusage.sgml --- b/doc/src/sgml/diskusage.sgml *************** *** 44,50 **** SELECT pg_relation_filepath(oid), relpages FROM pg_class WHERE relname = 'customer'; ! pg_relation_filepath | relpages ----------------------+---------- base/16384/16806 | 60 (1 row) --- 44,50 ---- SELECT pg_relation_filepath(oid), relpages FROM pg_class WHERE relname = 'customer'; ! pg_relation_filepath | relpages ----------------------+---------- base/16384/16806 | 60 (1 row) *************** *** 65,76 **** 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) ORDER BY relname; ! relname | relpages ----------------------+---------- pg_toast_16806 | 0 pg_toast_16806_index | 1 --- 65,76 ---- FROM pg_class WHERE relname = 'customer') AS ss WHERE oid = ss.reltoastrelid OR ! oid = (SELECT indexrelid ! FROM pg_index ! WHERE indrelid = ss.reltoastrelid) ORDER BY relname; ! relname | relpages ----------------------+---------- pg_toast_16806 | 0 pg_toast_16806_index | 1 *************** *** 87,93 **** WHERE c.relname = 'customer' AND c2.oid = i.indexrelid ORDER BY c2.relname; ! relname | relpages ----------------------+---------- customer_id_indexdex | 26 --- 87,93 ---- c2.oid = i.indexrelid ORDER BY c2.relname; ! relname | relpages ----------------------+---------- customer_id_indexdex | 26 *************** *** 101,107 **** SELECT relname, relpages FROM pg_class ORDER BY relpages DESC; ! relname | relpages ----------------------+---------- bigtable | 3290 customer | 3144 --- 101,107 ---- FROM pg_class ORDER BY relpages DESC; ! relname | relpages ----------------------+---------- bigtable | 3290 customer | 3144 *** a/src/backend/access/heap/tuptoaster.c --- b/src/backend/access/heap/tuptoaster.c *************** *** 76,86 **** do { \ static void toast_delete_datum(Relation rel, Datum value); static Datum toast_save_datum(Relation rel, Datum value, struct varlena * oldexternal, int options); ! static bool toastrel_valueid_exists(Relation toastrel, Oid valueid); 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); /* ---------- --- 76,88 ---- static void toast_delete_datum(Relation rel, Datum value); static Datum toast_save_datum(Relation rel, Datum value, struct varlena * oldexternal, int options); ! static bool toastrel_valueid_exists(Relation toastrel, ! Oid valueid, LOCKMODE lockmode); 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 Relation toast_index_fetch_valid(Relation *toastidxs, int num_indexes); /* ---------- *************** *** 1237,1244 **** static Datum toast_save_datum(Relation rel, Datum value, struct varlena * oldexternal, int options) { ! Relation toastrel; ! Relation toastidx; HeapTuple toasttup; TupleDesc toasttupDesc; Datum t_values[3]; --- 1239,1246 ---- toast_save_datum(Relation rel, Datum value, struct varlena * oldexternal, int options) { ! Relation toastrel, validtoastidx; ! Relation *toastidxs; HeapTuple toasttup; TupleDesc toasttupDesc; Datum t_values[3]; *************** *** 1257,1271 **** toast_save_datum(Relation rel, Datum value, char *data_p; int32 data_todo; Pointer dval = DatumGetPointer(value); /* * Open the toast relation and its index. 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); /* * Get the data pointer and length, and compute va_rawsize and va_extsize. --- 1259,1287 ---- char *data_p; int32 data_todo; Pointer dval = DatumGetPointer(value); + ListCell *lc; + int i = 0; + int num_indexes; /* * Open the toast relation and its index. 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. A toast table can have multiple identical ! * indexes associated to it. */ toastrel = heap_open(rel->rd_rel->reltoastrelid, RowExclusiveLock); toasttupDesc = toastrel->rd_att; ! RelationGetIndexListIfValid(toastrel); ! num_indexes = list_length(toastrel->rd_indexlist); ! ! toastidxs = (Relation *) palloc(num_indexes * sizeof(Relation)); ! ! /* Open all the indexes of toast relation with similar lock */ ! foreach(lc, toastrel->rd_indexlist) ! toastidxs[i++] = index_open(lfirst_oid(lc), RowExclusiveLock); ! ! /* Fetch relation used for process */ ! validtoastidx = toast_index_fetch_valid(toastidxs, num_indexes); /* * Get the data pointer and length, and compute va_rawsize and va_extsize. *************** *** 1330,1336 **** toast_save_datum(Relation rel, Datum value, /* normal case: just choose an unused OID */ toast_pointer.va_valueid = GetNewOidWithIndex(toastrel, ! RelationGetRelid(toastidx), (AttrNumber) 1); } else --- 1346,1352 ---- /* normal case: just choose an unused OID */ toast_pointer.va_valueid = GetNewOidWithIndex(toastrel, ! RelationGetRelid(validtoastidx), (AttrNumber) 1); } else *************** *** 1367,1373 **** toast_save_datum(Relation rel, Datum value, * be reclaimed by VACUUM. */ if (toastrel_valueid_exists(toastrel, ! toast_pointer.va_valueid)) { /* Match, so short-circuit the data storage loop below */ data_todo = 0; --- 1383,1390 ---- * be reclaimed by VACUUM. */ if (toastrel_valueid_exists(toastrel, ! toast_pointer.va_valueid, ! RowExclusiveLock)) { /* Match, so short-circuit the data storage loop below */ data_todo = 0; *************** *** 1384,1390 **** toast_save_datum(Relation rel, Datum value, { toast_pointer.va_valueid = GetNewOidWithIndex(toastrel, ! RelationGetRelid(toastidx), (AttrNumber) 1); } while (toastid_valueid_exists(rel->rd_toastoid, toast_pointer.va_valueid)); --- 1401,1407 ---- { toast_pointer.va_valueid = GetNewOidWithIndex(toastrel, ! RelationGetRelid(validtoastidx), (AttrNumber) 1); } while (toastid_valueid_exists(rel->rd_toastoid, toast_pointer.va_valueid)); *************** *** 1423,1438 **** 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. * * 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); /* * Free memory --- 1440,1457 ---- /* * 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 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. */ ! for (i = 0; i < num_indexes; i++) ! 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 *************** *** 1447,1456 **** toast_save_datum(Relation rel, Datum value, } /* ! * Done - close toast relation */ ! index_close(toastidx, RowExclusiveLock); heap_close(toastrel, RowExclusiveLock); /* * Create the TOAST pointer value that we'll return --- 1466,1477 ---- } /* ! * Done - close toast relations */ ! for (i = 0; i < num_indexes; i++) ! index_close(toastidxs[i], RowExclusiveLock); heap_close(toastrel, RowExclusiveLock); + pfree(toastidxs); /* * Create the TOAST pointer value that we'll return *************** *** 1474,1484 **** toast_delete_datum(Relation rel, Datum value) { struct varlena *attr = (struct varlena *) DatumGetPointer(value); struct varatt_external toast_pointer; ! Relation toastrel; ! Relation toastidx; ScanKeyData toastkey; SysScanDesc toastscan; HeapTuple toasttup; if (!VARATT_IS_EXTERNAL(attr)) return; --- 1495,1508 ---- { struct varlena *attr = (struct varlena *) DatumGetPointer(value); struct varatt_external toast_pointer; ! Relation toastrel, validtoastidx; ! Relation *toastidxs; ScanKeyData toastkey; SysScanDesc toastscan; HeapTuple toasttup; + ListCell *lc; + int num_indexes; + int i = 0; if (!VARATT_IS_EXTERNAL(attr)) return; *************** *** 1487,1496 **** toast_delete_datum(Relation rel, Datum value) VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr); /* ! * Open the toast relation and its index */ toastrel = heap_open(toast_pointer.va_toastrelid, RowExclusiveLock); ! toastidx = index_open(toastrel->rd_rel->reltoastidxid, RowExclusiveLock); /* * Setup a scan key to find chunks with matching va_valueid --- 1511,1532 ---- VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr); /* ! * Open the toast relation and its indexes */ toastrel = heap_open(toast_pointer.va_toastrelid, RowExclusiveLock); ! RelationGetIndexListIfValid(toastrel); ! num_indexes = list_length(toastrel->rd_indexlist); ! toastidxs = (Relation *) palloc(num_indexes * sizeof(Relation)); ! ! /* ! * We actually use only the first valid index but taking a lock on all is ! * necessary. ! */ ! foreach(lc, toastrel->rd_indexlist) ! toastidxs[i++] = index_open(lfirst_oid(lc), RowExclusiveLock); ! ! /* Fetch relation used for process */ ! validtoastidx = toast_index_fetch_valid(toastidxs, num_indexes); /* * Setup a scan key to find chunks with matching va_valueid *************** *** 1505,1511 **** 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, SnapshotToast, 1, &toastkey); while ((toasttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL) { --- 1541,1547 ---- * sequence or not, but since we've already locked the index we might as * well use systable_beginscan_ordered.) */ ! toastscan = systable_beginscan_ordered(toastrel, validtoastidx, SnapshotToast, 1, &toastkey); while ((toasttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL) { *************** *** 1519,1526 **** toast_delete_datum(Relation rel, Datum value) * End scan and close relations */ systable_endscan_ordered(toastscan); ! index_close(toastidx, RowExclusiveLock); heap_close(toastrel, RowExclusiveLock); } --- 1555,1564 ---- * End scan and close relations */ systable_endscan_ordered(toastscan); ! for (i = 0; i < num_indexes; i++) ! index_close(toastidxs[i], RowExclusiveLock); heap_close(toastrel, RowExclusiveLock); + pfree(toastidxs); } *************** *** 1531,1541 **** toast_delete_datum(Relation rel, Datum value) * ---------- */ static bool ! toastrel_valueid_exists(Relation toastrel, Oid valueid) { bool result = false; ScanKeyData toastkey; SysScanDesc toastscan; /* * Setup a scan key to find chunks with matching va_valueid --- 1569,1596 ---- * ---------- */ static bool ! toastrel_valueid_exists(Relation toastrel, Oid valueid, LOCKMODE lockmode) { bool result = false; ScanKeyData toastkey; SysScanDesc toastscan; + int i = 0; + int num_indexes; + Relation *toastidxs; + Relation validtoastidx; + ListCell *lc; + + /* Ensure that the list of indexes of toast relation is computed */ + RelationGetIndexListIfValid(toastrel); + num_indexes = list_length(toastrel->rd_indexlist); + + /* Open each index relation necessary */ + toastidxs = (Relation *) palloc(num_indexes * sizeof(Relation)); + foreach(lc, toastrel->rd_indexlist) + toastidxs[i++] = index_open(lfirst_oid(lc), lockmode); + + /* Fetch a valid index relation */ + validtoastidx = toast_index_fetch_valid(toastidxs, num_indexes); /* * Setup a scan key to find chunks with matching va_valueid *************** *** 1548,1554 **** toastrel_valueid_exists(Relation toastrel, Oid valueid) /* * Is there any such chunk? */ ! toastscan = systable_beginscan(toastrel, toastrel->rd_rel->reltoastidxid, true, SnapshotToast, 1, &toastkey); if (systable_getnext(toastscan) != NULL) --- 1603,1610 ---- /* * Is there any such chunk? */ ! toastscan = systable_beginscan(toastrel, ! RelationGetRelid(validtoastidx), true, SnapshotToast, 1, &toastkey); if (systable_getnext(toastscan) != NULL) *************** *** 1556,1561 **** toastrel_valueid_exists(Relation toastrel, Oid valueid) --- 1612,1622 ---- systable_endscan(toastscan); + /* Clean up */ + for (i = 0; i < num_indexes; i++) + index_close(toastidxs[i], lockmode); + pfree(toastidxs); + return result; } *************** *** 1573,1579 **** toastid_valueid_exists(Oid toastrelid, Oid valueid) toastrel = heap_open(toastrelid, AccessShareLock); ! result = toastrel_valueid_exists(toastrel, valueid); heap_close(toastrel, AccessShareLock); --- 1634,1640 ---- toastrel = heap_open(toastrelid, AccessShareLock); ! result = toastrel_valueid_exists(toastrel, valueid, AccessShareLock); heap_close(toastrel, AccessShareLock); *************** *** 1591,1598 **** toastid_valueid_exists(Oid toastrelid, Oid valueid) static struct varlena * toast_fetch_datum(struct varlena * attr) { ! Relation toastrel; ! Relation toastidx; ScanKeyData toastkey; SysScanDesc toastscan; HeapTuple ttup; --- 1652,1659 ---- static struct varlena * toast_fetch_datum(struct varlena * attr) { ! Relation toastrel, validtoastidx; ! Relation *toastidxs; ScanKeyData toastkey; SysScanDesc toastscan; HeapTuple ttup; *************** *** 1607,1612 **** toast_fetch_datum(struct varlena * attr) --- 1668,1676 ---- bool isnull; char *chunkdata; int32 chunksize; + ListCell *lc; + int num_indexes; + int i = 0; /* Must copy to access aligned fields */ VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr); *************** *** 1622,1632 **** toast_fetch_datum(struct varlena * attr) SET_VARSIZE(result, ressize + VARHDRSZ); /* ! * Open the toast relation and its index */ toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock); toasttupDesc = toastrel->rd_att; ! toastidx = index_open(toastrel->rd_rel->reltoastidxid, AccessShareLock); /* * Setup a scan key to fetch from the index by va_valueid --- 1686,1706 ---- SET_VARSIZE(result, ressize + VARHDRSZ); /* ! * Open the toast relation and its indexes */ toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock); toasttupDesc = toastrel->rd_att; ! RelationGetIndexListIfValid(toastrel); ! num_indexes = list_length(toastrel->rd_indexlist); ! ! toastidxs = (Relation *) palloc(num_indexes * sizeof(Relation)); ! ! /* Open all the indexes of toast relation with similar lock */ ! foreach(lc, toastrel->rd_indexlist) ! toastidxs[i++] = index_open(lfirst_oid(lc), AccessShareLock); ! ! /* Fetch relation used for process */ ! validtoastidx = toast_index_fetch_valid(toastidxs, num_indexes); /* * Setup a scan key to fetch from the index by va_valueid *************** *** 1645,1651 **** toast_fetch_datum(struct varlena * attr) */ nextidx = 0; ! toastscan = systable_beginscan_ordered(toastrel, toastidx, SnapshotToast, 1, &toastkey); while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL) { --- 1719,1725 ---- */ nextidx = 0; ! toastscan = systable_beginscan_ordered(toastrel, validtoastidx, SnapshotToast, 1, &toastkey); while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL) { *************** *** 1734,1741 **** toast_fetch_datum(struct varlena * attr) * End scan and close relations */ systable_endscan_ordered(toastscan); ! index_close(toastidx, AccessShareLock); heap_close(toastrel, AccessShareLock); return result; } --- 1808,1817 ---- * End scan and close relations */ systable_endscan_ordered(toastscan); ! for (i = 0; i < num_indexes; i++) ! index_close(toastidxs[i], AccessShareLock); heap_close(toastrel, AccessShareLock); + pfree(toastidxs); return result; } *************** *** 1750,1757 **** toast_fetch_datum(struct varlena * attr) static struct varlena * toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length) { ! Relation toastrel; ! Relation toastidx; ScanKeyData toastkey[3]; int nscankeys; SysScanDesc toastscan; --- 1826,1833 ---- static struct varlena * toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length) { ! Relation toastrel, validtoastidx; ! Relation *toastidxs; ScanKeyData toastkey[3]; int nscankeys; SysScanDesc toastscan; *************** *** 1774,1779 **** toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length) --- 1850,1858 ---- int32 chunksize; int32 chcpystrt; int32 chcpyend; + int num_indexes; + int i = 0; + ListCell *lc; Assert(VARATT_IS_EXTERNAL(attr)); *************** *** 1816,1826 **** 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 */ toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock); toasttupDesc = toastrel->rd_att; ! toastidx = index_open(toastrel->rd_rel->reltoastidxid, AccessShareLock); /* * Setup a scan key to fetch from the index. This is either two keys or --- 1895,1912 ---- endoffset = (sliceoffset + length - 1) % TOAST_MAX_CHUNK_SIZE; /* ! * Open the toast relation and its indexes */ toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock); toasttupDesc = toastrel->rd_att; ! RelationGetIndexListIfValid(toastrel); ! num_indexes = list_length(toastrel->rd_indexlist); ! ! toastidxs = (Relation *) palloc(num_indexes * sizeof(Relation)); ! ! foreach(lc, toastrel->rd_indexlist) ! toastidxs[i++] = index_open(lfirst_oid(lc), AccessShareLock); ! validtoastidx = toast_index_fetch_valid(toastidxs, num_indexes); /* * Setup a scan key to fetch from the index. This is either two keys or *************** *** 1861,1867 **** 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, SnapshotToast, nscankeys, toastkey); while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL) { --- 1947,1953 ---- * The index is on (valueid, chunkidx) so they will come in order */ nextidx = startchunk; ! toastscan = systable_beginscan_ordered(toastrel, validtoastidx, SnapshotToast, nscankeys, toastkey); while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL) { *************** *** 1958,1965 **** toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length) * End scan and close relations */ systable_endscan_ordered(toastscan); ! index_close(toastidx, AccessShareLock); heap_close(toastrel, AccessShareLock); return result; } --- 2044,2079 ---- * End scan and close relations */ systable_endscan_ordered(toastscan); ! for (i = 0; i < num_indexes; i++) ! index_close(toastidxs[i], AccessShareLock); heap_close(toastrel, AccessShareLock); + pfree(toastidxs); return result; } + + /* ---------- + * toast_index_fetch_valid + * + * Get a valid index in list of indexes for a toast relation. Those relations + * need to be already open prior calling this routine. + */ + static Relation + toast_index_fetch_valid(Relation *toastidxs, int num_indexes) + { + int i; + Relation res = NULL; + + /* Fetch the first valid index in list */ + for (i = 0; i < num_indexes; i++) + { + if (toastidxs[i]->rd_index->indisvalid) + { + res = toastidxs[i]; + break; + } + } + + Assert(res); + return res; + } *** a/src/backend/catalog/heap.c --- b/src/backend/catalog/heap.c *************** *** 781,787 **** 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); --- 781,786 ---- *** a/src/backend/catalog/index.c --- b/src/backend/catalog/index.c *************** *** 103,109 **** static void UpdateIndexRelation(Oid indexoid, Oid heapoid, bool isvalid); static void index_update_stats(Relation rel, bool hasindex, bool isprimary, ! Oid reltoastidxid, double reltuples); static void IndexCheckExclusion(Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo); --- 103,109 ---- bool isvalid); static void index_update_stats(Relation rel, bool hasindex, bool isprimary, ! double reltuples); static void IndexCheckExclusion(Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo); *************** *** 1072,1078 **** index_create(Relation heapRelation, index_update_stats(heapRelation, true, isprimary, - InvalidOid, -1.0); /* Make the above update visible */ CommandCounterIncrement(); --- 1072,1077 ---- *************** *** 1254,1260 **** index_constraint_create(Relation heapRelation, index_update_stats(heapRelation, true, true, - InvalidOid, -1.0); /* --- 1253,1258 ---- *************** *** 1764,1771 **** 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 --- 1762,1767 ---- *************** *** 1781,1788 **** FormIndexDatum(IndexInfo *indexInfo, */ static void index_update_stats(Relation rel, ! bool hasindex, bool isprimary, ! Oid reltoastidxid, double reltuples) { Oid relid = RelationGetRelid(rel); Relation pg_class; --- 1777,1785 ---- */ static void index_update_stats(Relation rel, ! bool hasindex, ! bool isprimary, ! double reltuples) { Oid relid = RelationGetRelid(rel); Relation pg_class; *************** *** 1876,1890 **** 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) { --- 1873,1878 ---- *************** *** 2072,2085 **** 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 */ --- 2060,2070 ---- *** a/src/backend/catalog/system_views.sql --- b/src/backend/catalog/system_views.sql *************** *** 473,488 **** 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 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_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; CREATE VIEW pg_statio_sys_tables AS SELECT * FROM pg_statio_all_tables --- 473,488 ---- 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.indrelid) - ! pg_stat_get_blocks_hit(X.indrelid) AS tidx_blks_read, ! pg_stat_get_blocks_hit(X.indrelid) 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_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.indrelid; CREATE VIEW pg_statio_sys_tables AS SELECT * FROM pg_statio_all_tables *** a/src/backend/commands/cluster.c --- b/src/backend/commands/cluster.c *************** *** 1172,1179 **** 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 --- 1172,1177 ---- *************** *** 1392,1410 **** 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. */ if (swap_toast_by_content && ! relform1->reltoastidxid && relform2->reltoastidxid) ! swap_relation_files(relform1->reltoastidxid, ! relform2->reltoastidxid, ! target_is_pg_class, ! swap_toast_by_content, ! is_internal, ! InvalidTransactionId, ! InvalidMultiXactId, ! mapped_tables); /* Clean up. */ heap_freetuple(reltup1); --- 1390,1451 ---- } /* ! * If we're swapping two toast tables by content, do the same for all of ! * their indexes. The swap can actually be safely done only if the ! * relations have indexes. */ if (swap_toast_by_content && ! relform1->reltoastrelid && ! relform2->reltoastrelid) ! { ! Relation toastRel1, toastRel2; ! ! /* Open relations */ ! toastRel1 = heap_open(relform1->reltoastrelid, AccessExclusiveLock); ! toastRel2 = heap_open(relform2->reltoastrelid, AccessExclusiveLock); ! ! /* Obtain index list */ ! RelationGetIndexList(toastRel1); ! RelationGetIndexList(toastRel2); ! ! /* Check if the swap is possible for all the toast indexes */ ! if (list_length(toastRel1->rd_indexlist) == 1 && ! list_length(toastRel2->rd_indexlist) == 1) ! { ! ListCell *lc1, *lc2; ! ! /* Now swap each couple */ ! lc2 = list_head(toastRel2->rd_indexlist); ! foreach(lc1, toastRel1->rd_indexlist) ! { ! Oid indexOid1 = lfirst_oid(lc1); ! Oid indexOid2 = lfirst_oid(lc2); ! swap_relation_files(indexOid1, ! indexOid2, ! target_is_pg_class, ! swap_toast_by_content, ! is_internal, ! InvalidTransactionId, ! InvalidMultiXactId, ! mapped_tables); ! lc2 = lnext(lc2); ! } ! } ! else ! { ! /* ! * As this code path is only taken by shared catalogs, who cannot ! * have multiple indexes on their toast relation, simply return ! * an error. ! */ ! ereport(ERROR, ! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("cannot swap relation files of a shared catalog with multiple indexes on toast relation"))); ! } ! ! heap_close(toastRel1, AccessExclusiveLock); ! heap_close(toastRel2, AccessExclusiveLock); ! } /* Clean up. */ heap_freetuple(reltup1); *************** *** 1529,1540 **** finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, 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); /* rename the toast table ... */ --- 1570,1582 ---- if (OidIsValid(newrel->rd_rel->reltoastrelid)) { Relation toastrel; char NewToastName[NAMEDATALEN]; + ListCell *lc; + int count = 0; toastrel = relation_open(newrel->rd_rel->reltoastrelid, AccessShareLock); ! RelationGetIndexList(toastrel); relation_close(toastrel, AccessShareLock); /* rename the toast table ... */ *************** *** 1543,1553 **** finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, RenameRelationInternal(newrel->rd_rel->reltoastrelid, NewToastName, true); ! /* ... and its index too */ ! snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index", ! OIDOldHeap); ! RenameRelationInternal(toastidx, ! NewToastName, true); } relation_close(newrel, NoLock); } --- 1585,1607 ---- RenameRelationInternal(newrel->rd_rel->reltoastrelid, NewToastName, true); ! /* ... and its indexes too */ ! foreach(lc, toastrel->rd_indexlist) ! { ! /* ! * The first index keeps the former toast name and the ! * following entries have a suffix appended. ! */ ! if (count == 0) ! snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index", ! OIDOldHeap); ! else ! snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index_%d", ! OIDOldHeap, count); ! RenameRelationInternal(lfirst_oid(lc), ! NewToastName, true); ! count++; ! } } relation_close(newrel, NoLock); } *** a/src/backend/commands/tablecmds.c --- b/src/backend/commands/tablecmds.c *************** *** 8728,8734 **** ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode) Relation rel; Oid oldTableSpace; Oid reltoastrelid; - Oid reltoastidxid; Oid newrelfilenode; RelFileNode newrnode; SMgrRelation dstrel; --- 8728,8733 ---- *************** *** 8736,8741 **** ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode) --- 8735,8742 ---- 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 *************** *** 8782,8788 **** 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; /* Get a modifiable copy of the relation's pg_class row */ pg_class = heap_open(RelationRelationId, RowExclusiveLock); --- 8783,8795 ---- errmsg("cannot move temporary tables of other sessions"))); reltoastrelid = rel->rd_rel->reltoastrelid; ! /* 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); *************** *** 8863,8870 **** ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode) /* Move associated toast relation and/or index, too */ if (OidIsValid(reltoastrelid)) ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode); ! if (OidIsValid(reltoastidxid)) ! ATExecSetTableSpace(reltoastidxid, newTableSpace, lockmode); } /* --- 8870,8884 ---- /* Move associated toast relation and/or index, too */ if (OidIsValid(reltoastrelid)) ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode); ! foreach(lc, reltoastidxids) ! { ! Oid toastidxid = lfirst_oid(lc); ! if (OidIsValid(toastidxid)) ! ATExecSetTableSpace(toastidxid, newTableSpace, lockmode); ! } ! ! /* Clean up */ ! list_free(reltoastidxids); } /* *** a/src/backend/rewrite/rewriteDefine.c --- b/src/backend/rewrite/rewriteDefine.c *************** *** 575,582 **** 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. */ classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(event_relid)); if (!HeapTupleIsValid(classTup)) --- 575,582 ---- /* * Fix pg_class entry to look like a normal view's, including setting ! * the correct relkind and removal of reltoastrelid of the toast table ! * we potentially removed above. */ classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(event_relid)); if (!HeapTupleIsValid(classTup)) *************** *** 588,594 **** 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; --- 588,593 ---- *** a/src/backend/utils/adt/dbsize.c --- b/src/backend/utils/adt/dbsize.c *************** *** 332,338 **** pg_relation_size(PG_FUNCTION_ARGS) } /* ! * Calculate total on-disk size of a TOAST relation, including its index. * Must not be applied to non-TOAST relations. */ static int64 --- 332,338 ---- } /* ! * Calculate total on-disk size of a TOAST relation, including its indexes. * Must not be applied to non-TOAST relations. */ static int64 *************** *** 340,347 **** calculate_toast_table_size(Oid toastrelid) { int64 size = 0; Relation toastRel; - Relation toastIdxRel; ForkNumber forkNum; toastRel = relation_open(toastrelid, AccessShareLock); --- 340,347 ---- { int64 size = 0; Relation toastRel; ForkNumber forkNum; + ListCell *lc; toastRel = relation_open(toastrelid, AccessShareLock); *************** *** 351,362 **** 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); ! relation_close(toastIdxRel, AccessShareLock); relation_close(toastRel, AccessShareLock); return size; --- 351,370 ---- toastRel->rd_backend, forkNum); /* toast index size, including FSM and VM size */ ! RelationGetIndexList(toastRel); ! /* Size is calculated using all the indexes available */ ! foreach(lc, toastRel->rd_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); ! } relation_close(toastRel, AccessShareLock); return size; *** a/src/bin/pg_dump/pg_dump.c --- b/src/bin/pg_dump/pg_dump.c *************** *** 2781,2796 **** binary_upgrade_set_pg_class_oids(Archive *fout, Oid pg_class_reltoastidxid; appendPQExpBuffer(upgrade_query, ! "SELECT c.reltoastrelid, t.reltoastidxid " "FROM pg_catalog.pg_class c LEFT JOIN " ! "pg_catalog.pg_class t ON (c.reltoastrelid = t.oid) " ! "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"))); appendPQExpBuffer(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_class oids\n"); --- 2781,2797 ---- Oid pg_class_reltoastidxid; appendPQExpBuffer(upgrade_query, ! "SELECT c.reltoastrelid, t.indexrelid " "FROM pg_catalog.pg_class c LEFT JOIN " ! "pg_catalog.pg_index t ON (c.reltoastrelid = t.indrelid) " ! "WHERE c.oid = '%u'::pg_catalog.oid AND t.indisvalid " ! "LIMIT 1", 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, "indexrelid"))); appendPQExpBuffer(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_class oids\n"); *************** *** 2816,2822 **** binary_upgrade_set_pg_class_oids(Archive *fout, "SELECT binary_upgrade.set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n", pg_class_reltoastrelid); ! /* 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); --- 2817,2823 ---- "SELECT binary_upgrade.set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n", pg_class_reltoastrelid); ! /* every toast table has at least one valid index */ appendPQExpBuffer(upgrade_buffer, "SELECT binary_upgrade.set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n", pg_class_reltoastidxid); *** a/src/include/catalog/pg_class.h --- b/src/include/catalog/pg_class.h *************** *** 48,54 **** 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 */ --- 48,53 ---- *************** *** 94,100 **** typedef FormData_pg_class *Form_pg_class; * ---------------- */ ! #define Natts_pg_class 29 #define Anum_pg_class_relname 1 #define Anum_pg_class_relnamespace 2 #define Anum_pg_class_reltype 3 --- 93,99 ---- * ---------------- */ ! #define Natts_pg_class 28 #define Anum_pg_class_relname 1 #define Anum_pg_class_relnamespace 2 #define Anum_pg_class_reltype 3 *************** *** 107,129 **** 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 /* ---------------- * initial contents of pg_class --- 106,127 ---- #define Anum_pg_class_reltuples 10 #define Anum_pg_class_relallvisible 11 #define Anum_pg_class_reltoastrelid 12 ! #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,150 **** 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_ )); 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_ )); 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_ )); 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_ )); DESCR(""); --- 136,148 ---- * 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 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 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 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 f f p r 28 0 t f f f f t 3 1 _null_ _null_ )); DESCR(""); *** a/src/include/utils/relcache.h --- b/src/include/utils/relcache.h *************** *** 29,34 **** typedef struct RelationData *Relation; --- 29,44 ---- typedef Relation *RelationPtr; /* + * RelationGetIndexListIfValid + * Get index list of relation without recomputing it. + */ + #define RelationGetIndexListIfValid(rel) \ + do { \ + if (rel->rd_indexvalid == 0) \ + RelationGetIndexList(rel); \ + } while(0) + + /* * Routines to open (lookup) and close a relcache entry */ extern Relation RelationIdGetRelation(Oid relationId); *** a/src/test/regress/expected/oidjoins.out --- b/src/test/regress/expected/oidjoins.out *************** *** 353,366 **** 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 --- 353,358 ---- *** a/src/test/regress/expected/rules.out --- b/src/test/regress/expected/rules.out *************** *** 1852,1866 **** 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 + | 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_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; pg_statio_sys_indexes | SELECT pg_statio_all_indexes.relid, + | pg_statio_all_indexes.indexrelid, + | pg_statio_all_indexes.schemaname, + --- 1852,1866 ---- | (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.indrelid) - pg_stat_get_blocks_hit(x.indrelid)) AS tidx_blks_read, + ! | pg_stat_get_blocks_hit(x.indrelid) 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_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.indrelid; pg_statio_sys_indexes | SELECT pg_statio_all_indexes.relid, + | pg_statio_all_indexes.indexrelid, + | pg_statio_all_indexes.schemaname, + *************** *** 2347,2357 **** 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 from pg_class where oid = 'fooview'::regclass; ! reltoastrelid | reltoastidxid | relkind | relfrozenxid ! ---------------+---------------+---------+-------------- ! 0 | 0 | v | 0 (1 row) drop view fooview; --- 2347,2357 ---- ERROR: column "xmin" does not exist LINE 1: select xmin, * from fooview; ^ ! select reltoastrelid, relkind, relfrozenxid from pg_class where oid = 'fooview'::regclass; ! reltoastrelid | relkind | relfrozenxid ! ---------------+---------+-------------- ! 0 | v | 0 (1 row) drop view fooview; *** a/src/test/regress/sql/oidjoins.sql --- b/src/test/regress/sql/oidjoins.sql *************** *** 177,186 **** 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 --- 177,182 ---- *** a/src/test/regress/sql/rules.sql --- b/src/test/regress/sql/rules.sql *************** *** 872,878 **** 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 from pg_class where oid = 'fooview'::regclass; drop view fooview; --- 872,878 ---- select * from fooview; select xmin, * from fooview; -- fail, views don't have such a column ! select reltoastrelid, relkind, relfrozenxid from pg_class where oid = 'fooview'::regclass; drop view fooview; *** a/src/tools/findoidjoins/README --- b/src/tools/findoidjoins/README *************** *** 86,92 **** 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 --- 86,91 ----