diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/utils/adt/dbsize.c | 28 | ||||
-rw-r--r-- | src/backend/utils/cache/Makefile | 3 | ||||
-rw-r--r-- | src/backend/utils/cache/inval.c | 2 | ||||
-rw-r--r-- | src/backend/utils/cache/relfilenodemap.c | 247 | ||||
-rw-r--r-- | src/backend/utils/cache/relmapper.c | 53 | ||||
-rw-r--r-- | src/include/catalog/catversion.h | 2 | ||||
-rw-r--r-- | src/include/catalog/indexing.h | 2 | ||||
-rw-r--r-- | src/include/catalog/pg_proc.h | 2 | ||||
-rw-r--r-- | src/include/utils/builtins.h | 1 | ||||
-rw-r--r-- | src/include/utils/relfilenodemap.h | 18 | ||||
-rw-r--r-- | src/include/utils/relmapper.h | 2 | ||||
-rw-r--r-- | src/test/regress/expected/alter_table.out | 18 | ||||
-rw-r--r-- | src/test/regress/sql/alter_table.sql | 14 |
13 files changed, 389 insertions, 3 deletions
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c index 34482abee3e..21d1c946abe 100644 --- a/src/backend/utils/adt/dbsize.c +++ b/src/backend/utils/adt/dbsize.c @@ -28,6 +28,7 @@ #include "utils/builtins.h" #include "utils/numeric.h" #include "utils/rel.h" +#include "utils/relfilenodemap.h" #include "utils/relmapper.h" #include "utils/syscache.h" @@ -756,6 +757,33 @@ pg_relation_filenode(PG_FUNCTION_ARGS) } /* + * Get the relation via (reltablespace, relfilenode) + * + * This is expected to be used when somebody wants to match an individual file + * on the filesystem back to its table. Thats not trivially possible via + * pg_class because that doesn't contain the relfilenodes of shared and nailed + * tables. + * + * We don't fail but return NULL if we cannot find a mapping. + * + * Instead of knowing DEFAULTTABLESPACE_OID you can pass 0. + */ +Datum +pg_filenode_relation(PG_FUNCTION_ARGS) +{ + Oid reltablespace = PG_GETARG_OID(0); + Oid relfilenode = PG_GETARG_OID(1); + Oid heaprel = InvalidOid; + + heaprel = RelidByRelfilenode(reltablespace, relfilenode); + + if (!OidIsValid(heaprel)) + PG_RETURN_NULL(); + else + PG_RETURN_OID(heaprel); +} + +/* * Get the pathname (relative to $PGDATA) of a relation * * See comments for pg_relation_filenode. diff --git a/src/backend/utils/cache/Makefile b/src/backend/utils/cache/Makefile index 32d722e34f6..a943f8ea4bc 100644 --- a/src/backend/utils/cache/Makefile +++ b/src/backend/utils/cache/Makefile @@ -13,6 +13,7 @@ top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global OBJS = attoptcache.o catcache.o evtcache.o inval.o plancache.o relcache.o \ - relmapper.o spccache.o syscache.o lsyscache.o typcache.o ts_cache.o + relmapper.o relfilenodemap.o spccache.o syscache.o lsyscache.o \ + typcache.o ts_cache.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c index 3356d0fe1e2..bfe7d787b7b 100644 --- a/src/backend/utils/cache/inval.c +++ b/src/backend/utils/cache/inval.c @@ -178,7 +178,7 @@ static int maxSharedInvalidMessagesArray; */ #define MAX_SYSCACHE_CALLBACKS 32 -#define MAX_RELCACHE_CALLBACKS 5 +#define MAX_RELCACHE_CALLBACKS 10 static struct SYSCACHECALLBACK { diff --git a/src/backend/utils/cache/relfilenodemap.c b/src/backend/utils/cache/relfilenodemap.c new file mode 100644 index 00000000000..372cb33c003 --- /dev/null +++ b/src/backend/utils/cache/relfilenodemap.c @@ -0,0 +1,247 @@ +/*------------------------------------------------------------------------- + * + * relfilenodemap.c + * relfilenode to oid mapping cache. + * + * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/utils/cache/relfilenode.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/heapam.h" +#include "access/htup_details.h" +#include "catalog/indexing.h" +#include "catalog/pg_class.h" +#include "catalog/pg_tablespace.h" +#include "miscadmin.h" +#include "utils/builtins.h" +#include "utils/catcache.h" +#include "utils/hsearch.h" +#include "utils/inval.h" +#include "utils/fmgroids.h" +#include "utils/rel.h" +#include "utils/relfilenodemap.h" +#include "utils/relmapper.h" + +/* Hash table for informations about each relfilenode <-> oid pair */ +static HTAB *RelfilenodeMapHash = NULL; + +/* built first time through in InitializeRelfilenodeMap */ +ScanKeyData relfilenode_skey[2]; + +typedef struct +{ + Oid reltablespace; + Oid relfilenode; +} RelfilenodeMapKey; + +typedef struct +{ + RelfilenodeMapKey key; /* lookup key - must be first */ + Oid relid; /* pg_class.oid */ +} RelfilenodeMapEntry; + +/* + * RelfilenodeMapInvalidateCallback + * Flush mapping entries when pg_class is updated in a relevant fashion. + */ +static void +RelfilenodeMapInvalidateCallback(Datum arg, Oid relid) +{ + HASH_SEQ_STATUS status; + RelfilenodeMapEntry *entry; + + /* nothing to do if not active or deleted */ + if (RelfilenodeMapHash == NULL) + return; + + /* if relid is InvalidOid, we must invalidate the entire cache */ + if (relid == InvalidOid) + { + hash_destroy(RelfilenodeMapHash); + RelfilenodeMapHash = NULL; + return; + } + + hash_seq_init(&status, RelfilenodeMapHash); + while ((entry = (RelfilenodeMapEntry *) hash_seq_search(&status)) != NULL) + { + /* Same OID may occur in more than one tablespace. */ + if (entry->relid == relid) + { + if (hash_search(RelfilenodeMapHash, + (void *) &entry->key, + HASH_REMOVE, + NULL) == NULL) + elog(ERROR, "hash table corrupted"); + } + } +} + +/* + * RelfilenodeMapInvalidateCallback + * Initialize cache, either on first use or after a reset. + */ +static void +InitializeRelfilenodeMap(void) +{ + HASHCTL ctl; + static bool initial_init_done = false; + int i; + + /* Make sure we've initialized CacheMemoryContext. */ + if (CacheMemoryContext == NULL) + CreateCacheMemoryContext(); + + /* Initialize the hash table. */ + MemSet(&ctl, 0, sizeof(ctl)); + ctl.keysize = sizeof(RelfilenodeMapKey); + ctl.entrysize = sizeof(RelfilenodeMapEntry); + ctl.hash = tag_hash; + ctl.hcxt = CacheMemoryContext; + + RelfilenodeMapHash = + hash_create("RelfilenodeMap cache", 1024, &ctl, + HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); + + /* + * For complete resets we simply delete the entire hash, but there's no + * need to do the other stuff multiple times. Especially the initialization + * of the relcche invalidation should only be done once. + */ + if (initial_init_done) + return; + + /* build skey */ + MemSet(&relfilenode_skey, 0, sizeof(relfilenode_skey)); + + for (i = 0; i < 2; i++) + { + fmgr_info_cxt(F_OIDEQ, + &relfilenode_skey[i].sk_func, + CacheMemoryContext); + relfilenode_skey[i].sk_strategy = BTEqualStrategyNumber; + relfilenode_skey[i].sk_subtype = InvalidOid; + relfilenode_skey[i].sk_collation = InvalidOid; + } + + relfilenode_skey[0].sk_attno = Anum_pg_class_reltablespace; + relfilenode_skey[1].sk_attno = Anum_pg_class_relfilenode; + + /* Watch for invalidation events. */ + CacheRegisterRelcacheCallback(RelfilenodeMapInvalidateCallback, + (Datum) 0); + initial_init_done = true; +} + +/* + * Map a relation's (tablespace, filenode) to a relation's oid and cache the + * result. + * + * Returns InvalidOid if no relation matching the criteria could be found. + */ +Oid +RelidByRelfilenode(Oid reltablespace, Oid relfilenode) +{ + RelfilenodeMapKey key; + RelfilenodeMapEntry *entry; + bool found; + SysScanDesc scandesc; + Relation relation; + HeapTuple ntp; + ScanKeyData skey[2]; + + if (RelfilenodeMapHash == NULL) + InitializeRelfilenodeMap(); + + /* pg_class will show 0 when the value is actually MyDatabaseTableSpace */ + if (reltablespace == MyDatabaseTableSpace) + reltablespace = 0; + + MemSet(&key, 0, sizeof(key)); + key.reltablespace = reltablespace; + key.relfilenode = relfilenode; + + /* + * Check cache and enter entry if nothing could be found. Even if no target + * relation can be found later on we store the negative match and return a + * InvalidOid from cache. That's not really necessary for performance since + * querying invalid values isn't supposed to be a frequent thing, but the + * implementation is simpler this way. + */ + entry = hash_search(RelfilenodeMapHash, (void *) &key, HASH_ENTER, &found); + + if (found) + return entry->relid; + + /* ok, no previous cache entry, do it the hard way */ + + /* check shared tables */ + if (reltablespace == GLOBALTABLESPACE_OID) + { + entry->relid = RelationMapFilenodeToOid(relfilenode, true); + return entry->relid; + } + + /* check plain relations by looking in pg_class */ + relation = heap_open(RelationRelationId, AccessShareLock); + + /* copy scankey to local copy, it will be modified during the scan */ + memcpy(skey, relfilenode_skey, sizeof(skey)); + + /* set scan arguments */ + skey[0].sk_argument = ObjectIdGetDatum(reltablespace); + skey[1].sk_argument = ObjectIdGetDatum(relfilenode); + + scandesc = systable_beginscan(relation, + ClassTblspcRelfilenodeIndexId, + true, + NULL, + 2, + skey); + + found = false; + + while (HeapTupleIsValid(ntp = systable_getnext(scandesc))) + { + bool isnull; + + if (found) + elog(ERROR, + "unexpected duplicate for tablespace %u, relfilenode %u", + reltablespace, relfilenode); + found = true; + +#ifdef USE_ASSERT_CHECKING + if (assert_enabled) + { + Oid check; + check = fastgetattr(ntp, Anum_pg_class_reltablespace, + RelationGetDescr(relation), + &isnull); + Assert(!isnull && check == reltablespace); + + check = fastgetattr(ntp, Anum_pg_class_relfilenode, + RelationGetDescr(relation), + &isnull); + Assert(!isnull && check == relfilenode); + } +#endif + entry->relid = HeapTupleGetOid(ntp); + } + + systable_endscan(scandesc); + heap_close(relation, AccessShareLock); + + /* check for tables that are mapped but not shared */ + if (!found) + entry->relid = RelationMapFilenodeToOid(relfilenode, false); + + return entry->relid; +} diff --git a/src/backend/utils/cache/relmapper.c b/src/backend/utils/cache/relmapper.c index 2c7d9f3287b..18f0342a7de 100644 --- a/src/backend/utils/cache/relmapper.c +++ b/src/backend/utils/cache/relmapper.c @@ -181,6 +181,59 @@ RelationMapOidToFilenode(Oid relationId, bool shared) } /* + * RelationMapFilenodeToOid + * + * Do the reverse of the normal direction of mapping done in + * RelationMapOidToFilenode. + * + * This is not supposed to be used during normal running but rather for + * information purposes when looking at the filesystem or xlog. + * + * Returns InvalidOid if the OID is not known; this can easily happen if the + * relfilenode doesn't pertain to a mapped relation. + */ +Oid +RelationMapFilenodeToOid(Oid filenode, bool shared) +{ + const RelMapFile *map; + int32 i; + + /* If there are active updates, believe those over the main maps */ + if (shared) + { + map = &active_shared_updates; + for (i = 0; i < map->num_mappings; i++) + { + if (filenode == map->mappings[i].mapfilenode) + return map->mappings[i].mapoid; + } + map = &shared_map; + for (i = 0; i < map->num_mappings; i++) + { + if (filenode == map->mappings[i].mapfilenode) + return map->mappings[i].mapoid; + } + } + else + { + map = &active_local_updates; + for (i = 0; i < map->num_mappings; i++) + { + if (filenode == map->mappings[i].mapfilenode) + return map->mappings[i].mapoid; + } + map = &local_map; + for (i = 0; i < map->num_mappings; i++) + { + if (filenode == map->mappings[i].mapfilenode) + return map->mappings[i].mapoid; + } + } + + return InvalidOid; +} + +/* * RelationMapUpdateMap * * Install a new relfilenode mapping for the specified relation. diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index d9404dafc16..2e51039c24b 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201307181 +#define CATALOG_VERSION_NO 201307221 #endif diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index 19268fbe648..4860e98ca55 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -106,6 +106,8 @@ DECLARE_UNIQUE_INDEX(pg_class_oid_index, 2662, on pg_class using btree(oid oid_o #define ClassOidIndexId 2662 DECLARE_UNIQUE_INDEX(pg_class_relname_nsp_index, 2663, on pg_class using btree(relname name_ops, relnamespace oid_ops)); #define ClassNameNspIndexId 2663 +DECLARE_INDEX(pg_class_tblspc_relfilenode_index, 3455, on pg_class using btree(reltablespace oid_ops, relfilenode oid_ops)); +#define ClassTblspcRelfilenodeIndexId 3455 DECLARE_UNIQUE_INDEX(pg_collation_name_enc_nsp_index, 3164, on pg_collation using btree(collname name_ops, collencoding int4_ops, collnamespace oid_ops)); #define CollationNameEncNspIndexId 3164 diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 90aff3d0484..f03dd0b7da4 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3448,6 +3448,8 @@ DATA(insert OID = 2998 ( pg_indexes_size PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 DESCR("disk space usage for all indexes attached to the specified table"); DATA(insert OID = 2999 ( pg_relation_filenode PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 26 "2205" _null_ _null_ _null_ _null_ pg_relation_filenode _null_ _null_ _null_ )); DESCR("filenode identifier of relation"); +DATA(insert OID = 3454 ( pg_filenode_relation PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 2205 "26 26" _null_ _null_ _null_ _null_ pg_filenode_relation _null_ _null_ _null_ )); +DESCR("relation OID for filenode and tablespace"); DATA(insert OID = 3034 ( pg_relation_filepath PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 25 "2205" _null_ _null_ _null_ _null_ pg_relation_filepath _null_ _null_ _null_ )); DESCR("file path of relation"); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 667c58b5d0c..a5a0561a4a5 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -460,6 +460,7 @@ extern Datum pg_size_pretty_numeric(PG_FUNCTION_ARGS); extern Datum pg_table_size(PG_FUNCTION_ARGS); extern Datum pg_indexes_size(PG_FUNCTION_ARGS); extern Datum pg_relation_filenode(PG_FUNCTION_ARGS); +extern Datum pg_filenode_relation(PG_FUNCTION_ARGS); extern Datum pg_relation_filepath(PG_FUNCTION_ARGS); /* genfile.c */ diff --git a/src/include/utils/relfilenodemap.h b/src/include/utils/relfilenodemap.h new file mode 100644 index 00000000000..324ff69f8ef --- /dev/null +++ b/src/include/utils/relfilenodemap.h @@ -0,0 +1,18 @@ +/*------------------------------------------------------------------------- + * + * relfilenodemap.h + * relfilenode to oid mapping cache. + * + * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/utils/relfilenodemap.h + * + *------------------------------------------------------------------------- + */ +#ifndef RELFILENODEMAP_H +#define RELFILENODEMAP_H + +extern Oid RelidByRelfilenode(Oid reltablespace, Oid relfilenode); + +#endif /* RELFILENODEMAP_H */ diff --git a/src/include/utils/relmapper.h b/src/include/utils/relmapper.h index 8f0b438a119..071bc988a5f 100644 --- a/src/include/utils/relmapper.h +++ b/src/include/utils/relmapper.h @@ -36,6 +36,8 @@ typedef struct xl_relmap_update extern Oid RelationMapOidToFilenode(Oid relationId, bool shared); +extern Oid RelationMapFilenodeToOid(Oid relationId, bool shared); + extern void RelationMapUpdateMap(Oid relationId, Oid fileNode, bool shared, bool immediate); diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 18daf95c668..7cc0084b920 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2305,3 +2305,21 @@ Check constraints: DROP TABLE alter2.tt8; DROP SCHEMA alter2; +-- Check that we map relation oids to filenodes and back correctly. +-- Don't display all the mappings so the test output doesn't change +-- all the time, but make sure we actually do test some values. +SELECT + SUM((mapped_oid != oid OR mapped_oid IS NULL)::int) incorrectly_mapped, + count(*) > 200 have_mappings +FROM ( + SELECT + oid, reltablespace, relfilenode, relname, + pg_filenode_relation(reltablespace, pg_relation_filenode(oid)) mapped_oid + FROM pg_class + WHERE relkind IN ('r', 'i', 'S', 't', 'm') + ) mapped; + incorrectly_mapped | have_mappings +--------------------+--------------- + 0 | t +(1 row) + diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index dcf8121d70c..a546ba74af3 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -1544,3 +1544,17 @@ ALTER TABLE IF EXISTS tt8 SET SCHEMA alter2; DROP TABLE alter2.tt8; DROP SCHEMA alter2; + +-- Check that we map relation oids to filenodes and back correctly. +-- Don't display all the mappings so the test output doesn't change +-- all the time, but make sure we actually do test some values. +SELECT + SUM((mapped_oid != oid OR mapped_oid IS NULL)::int) incorrectly_mapped, + count(*) > 200 have_mappings +FROM ( + SELECT + oid, reltablespace, relfilenode, relname, + pg_filenode_relation(reltablespace, pg_relation_filenode(oid)) mapped_oid + FROM pg_class + WHERE relkind IN ('r', 'i', 'S', 't', 'm') + ) mapped; |