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.
pull/6/head
Fujii Masao 12 years ago
parent f71939cd1a
commit 2ef085d0e6
  1. 15
      contrib/pg_upgrade/info.c
  2. 9
      doc/src/sgml/catalogs.sgml
  3. 26
      doc/src/sgml/diskusage.sgml
  4. 4
      doc/src/sgml/monitoring.sgml
  5. 225
      src/backend/access/heap/tuptoaster.c
  6. 1
      src/backend/catalog/heap.c
  7. 23
      src/backend/catalog/index.c
  8. 10
      src/backend/catalog/system_views.sql
  9. 34
      src/backend/commands/cluster.c
  10. 20
      src/backend/commands/tablecmds.c
  11. 5
      src/backend/rewrite/rewriteDefine.c
  12. 24
      src/backend/utils/adt/dbsize.c
  13. 14
      src/bin/pg_dump/pg_dump.c
  14. 9
      src/include/access/tuptoaster.h
  15. 2
      src/include/catalog/catversion.h
  16. 44
      src/include/catalog/pg_class.h
  17. 8
      src/test/regress/expected/oidjoins.out
  18. 16
      src/test/regress/expected/rules.out
  19. 4
      src/test/regress/sql/oidjoins.sql
  20. 2
      src/test/regress/sql/rules.sql
  21. 1
      src/tools/findoidjoins/README

@ -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, "

@ -1744,15 +1744,6 @@
</entry>
</row>
<row>
<entry><structfield>reltoastidxid</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
<entry>
For a TOAST table, the OID of its index. 0 if not a TOAST table.
</entry>
</row>
<row>
<entry><structfield>relhasindex</structfield></entry>
<entry><type>bool</type></entry>

@ -20,12 +20,12 @@
stored. If the table has any columns with potentially-wide values,
there also might be a <acronym>TOAST</> file associated with the table,
which is used to store values too wide to fit comfortably in the main
table (see <xref linkend="storage-toast">). There will be one index on the
<acronym>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 &mdash; possibly more than one file, if the file would exceed one
gigabyte. Naming conventions for these files are described in <xref
linkend="storage-file-layout">.
table (see <xref linkend="storage-toast">). There will be one valid index
on the <acronym>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 &mdash; possibly more than one file, if the file would
exceed one gigabyte. Naming conventions for these files are described
in <xref linkend="storage-file-layout">.
</para>
<para>
@ -44,7 +44,7 @@
<programlisting>
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
</programlisting>
@ -101,7 +101,7 @@ SELECT relname, relpages
FROM pg_class
ORDER BY relpages DESC;
relname | relpages
relname | relpages
----------------------+----------
bigtable | 3290
customer | 3144

@ -1163,12 +1163,12 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re
<row>
<entry><structfield>tidx_blks_read</></entry>
<entry><type>bigint</></entry>
<entry>Number of disk blocks read from this table's TOAST table index (if any)</entry>
<entry>Number of disk blocks read from this table's TOAST table indexes (if any)</entry>
</row>
<row>
<entry><structfield>tidx_blks_hit</></entry>
<entry><type>bigint</></entry>
<entry>Number of buffer hits in this table's TOAST table index (if any)</entry>
<entry>Number of buffer hits in this table's TOAST table indexes (if any)</entry>
</row>
</tbody>
</tgroup>

@ -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);
}

@ -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);

@ -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 */

@ -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

@ -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);
}

@ -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);
}
/*

@ -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;

@ -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;

@ -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]));
}

@ -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 */

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201306121
#define CATALOG_VERSION_NO 201307031
#endif

@ -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("");

@ -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

@ -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;

@ -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

@ -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;

@ -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

Loading…
Cancel
Save