summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/Makefile1
-rw-r--r--contrib/oscache/Makefile15
-rw-r--r--contrib/oscache/oscache.c151
-rw-r--r--doc/src/sgml/catalogs.sgml20
-rw-r--r--src/backend/access/hash/hash.c4
-rw-r--r--src/backend/catalog/heap.c2
-rw-r--r--src/backend/commands/analyze.c128
-rw-r--r--src/backend/commands/vacuum.c58
-rw-r--r--src/backend/optimizer/util/plancat.c19
-rw-r--r--src/backend/parser/gram.y22
-rw-r--r--src/backend/storage/buffer/bufmgr.c41
-rw-r--r--src/backend/utils/cache/relcache.c4
-rw-r--r--src/include/catalog/pg_class.h46
-rw-r--r--src/include/commands/vacuum.h5
-rw-r--r--src/include/nodes/parsenodes.h3
-rw-r--r--src/include/nodes/relation.h4
-rw-r--r--src/include/optimizer/plancat.h3
-rw-r--r--src/include/parser/kwlist.h1
-rw-r--r--src/include/storage/bufmgr.h14
-rw-r--r--src/tools/pgindent/typedefs.list2
20 files changed, 508 insertions, 35 deletions
diff --git a/contrib/Makefile b/contrib/Makefile
index 696776795e..47652d536b 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -27,6 +27,7 @@ SUBDIRS = \
lo \
ltree \
oid2name \
+ oscache \
pageinspect \
passwordcheck \
pg_archivecleanup \
diff --git a/contrib/oscache/Makefile b/contrib/oscache/Makefile
new file mode 100644
index 0000000000..8d8dcc5dd4
--- /dev/null
+++ b/contrib/oscache/Makefile
@@ -0,0 +1,15 @@
+# contrib/oscache/Makefile
+
+MODULE_big = oscache
+OBJS = oscache.o
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/oscache
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/oscache/oscache.c b/contrib/oscache/oscache.c
new file mode 100644
index 0000000000..1ad7dc23b6
--- /dev/null
+++ b/contrib/oscache/oscache.c
@@ -0,0 +1,151 @@
+/*-------------------------------------------------------------------------
+ *
+ * oscache.c
+ *
+ *
+ * Copyright (c) 2011, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * contrib/oscache/oscache.c
+ *
+ *-------------------------------------------------------------------------
+ */
+/* { POSIX stuff */
+#include <stdlib.h> /* exit, calloc, free */
+#include <sys/stat.h> /* stat, fstat */
+#include <sys/types.h> /* size_t, mincore */
+#include <unistd.h> /* sysconf, close */
+#include <sys/mman.h> /* mmap, mincore */
+/* } */
+
+/* { PostgreSQL stuff */
+#include "postgres.h" /* general Postgres declarations */
+#include "utils/rel.h" /* Relation */
+#include "storage/bufmgr.h"
+#include "catalog/catalog.h" /* relpath */
+/* } */
+
+PG_MODULE_MAGIC;
+
+void _PG_init(void);
+
+float4 oscache(Relation, ForkNumber);
+
+/*
+ * Module load callback
+ */
+void
+_PG_init(void)
+{
+ /* Install hook. */
+ OSCache_hook = &oscache;
+}
+
+/*
+ * oscache process the os cache inspection for the relation.
+ * It returns the percentage of blocks in OS cache.
+ */
+float4
+oscache(Relation relation, ForkNumber forkNum)
+{
+ int segment = 0;
+ char *relationpath;
+ char filename[MAXPGPATH];
+ int fd;
+ int64 total_block_disk = 0;
+ int64 total_block_mem = 0;
+
+ /* OS things */
+ int64 pageSize = sysconf(_SC_PAGESIZE); /* Page size */
+ register int64 pageIndex;
+
+ relationpath = relpathperm(relation->rd_node, forkNum);
+
+ /*
+ * For each segment of the relation
+ */
+ snprintf(filename, MAXPGPATH, "%s", relationpath);
+ while ((fd = open(filename, O_RDONLY)) != -1)
+ {
+ // for stat file
+ struct stat st;
+ // for mmap file
+ void *pa = (char *)0;
+ // for calloc file
+ unsigned char *vec = (unsigned char *)0;
+ int64 block_disk = 0;
+ int64 block_mem = 0;
+
+ if (fstat(fd, &st) == -1)
+ {
+ close(fd);
+ elog(ERROR, "Can not stat object file : %s",
+ filename);
+ return 0;
+ }
+
+ /*
+ * if file ok
+ * then process
+ */
+ if (st.st_size != 0)
+ {
+ /* number of block in the current file */
+ block_disk = st.st_size/pageSize;
+
+ /* TODO We need to split mmap size to be sure (?) to be able to mmap */
+ pa = mmap(NULL, st.st_size, PROT_NONE, MAP_SHARED, fd, 0);
+ if (pa == MAP_FAILED)
+ {
+ close(fd);
+ elog(ERROR, "Can not mmap object file : %s, errno = %i,%s\nThis error can happen if there is not enought space in memory to do the projection. Please mail cedric@2ndQuadrant.fr with '[oscache] ENOMEM' as subject.",
+ filename, errno, strerror(errno));
+ return 0;
+ }
+
+ /* Prepare our vector containing all blocks information */
+ vec = calloc(1, (st.st_size+pageSize-1)/pageSize);
+ if ((void *)0 == vec)
+ {
+ munmap(pa, st.st_size);
+ close(fd);
+ elog(ERROR, "Can not calloc object file : %s",
+ filename);
+ return 0;
+ }
+
+ /* Affect vec with mincore */
+ if (mincore(pa, st.st_size, vec) != 0)
+ {
+ free(vec);
+ munmap(pa, st.st_size);
+ close(fd);
+ elog(ERROR, "mincore(%p, %lld, %p): %s\n",
+ pa, (int64)st.st_size, vec, strerror(errno));
+ return 0;
+ }
+
+ /* handle the results */
+ for (pageIndex = 0; pageIndex <= st.st_size/pageSize; pageIndex++)
+ {
+ // block in memory
+ if (vec[pageIndex] & 1)
+ {
+ block_mem++;
+ }
+ }
+ }
+ elog(DEBUG1, "oscache %s: %lld of %lld block in linux cache",
+ filename, block_mem, block_disk);
+
+ // free things
+ free(vec);
+ munmap(pa, st.st_size);
+ close(fd);
+ total_block_mem += block_mem;
+ total_block_disk += block_disk;
+
+ snprintf(filename, MAXPGPATH, "%s.%u", relationpath, segment++);
+ }
+ return (float4)(total_block_mem*100/(total_block_disk+1));
+}
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 7b62818ce4..25338d0ddc 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1634,6 +1634,26 @@
</row>
<row>
+ <entry><structfield>reloscache</structfield></entry>
+ <entry><type>float4</type></entry>
+ <entry></entry>
+ <entry>
+ Percentage of the files in OS cache. This is only an estimate used by
+ the planner. It is updated by <command>ANALYZE OSCACHE</command>.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>relpgcache</structfield></entry>
+ <entry><type>float4</type></entry>
+ <entry></entry>
+ <entry>
+ Percentage of the files in PostgreSQL cache. This is only an estimate used by
+ the planner. It is updated by <command>ANALYZE PGCACHE</command>.
+ </entry>
+ </row>
+
+ <row>
<entry><structfield>reltoastrelid</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 4cb29b2bb4..7f39a93a73 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -54,6 +54,8 @@ hashbuild(PG_FUNCTION_ARGS)
IndexBuildResult *result;
BlockNumber relpages;
double reltuples;
+ float4 reloscache;
+ float4 relpgcache;
uint32 num_buckets;
HashBuildState buildstate;
@@ -66,7 +68,7 @@ hashbuild(PG_FUNCTION_ARGS)
RelationGetRelationName(index));
/* Estimate the number of rows currently present in the table */
- estimate_rel_size(heap, NULL, &relpages, &reltuples);
+ estimate_rel_size(heap, NULL, &relpages, &reltuples, &reloscache, &relpgcache);
/* Initialize the hash index metadata page and initial buckets */
num_buckets = _hash_metapinit(index, reltuples, MAIN_FORKNUM);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 71c9931834..73ba67bbb7 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -756,6 +756,8 @@ InsertPgClassTuple(Relation pg_class_desc,
values[Anum_pg_class_reltablespace - 1] = ObjectIdGetDatum(rd_rel->reltablespace);
values[Anum_pg_class_relpages - 1] = Int32GetDatum(rd_rel->relpages);
values[Anum_pg_class_reltuples - 1] = Float4GetDatum(rd_rel->reltuples);
+ values[Anum_pg_class_reloscache - 1] = Float4GetDatum(rd_rel->reloscache);
+ values[Anum_pg_class_relpgcache - 1] = Float4GetDatum(rd_rel->relpgcache);
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);
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 0568a1bcf8..284ab5db06 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -86,6 +86,8 @@ static BufferAccessStrategy vac_strategy;
static void do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
bool update_reltuples, bool inh);
+static void do_cache_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
+ bool update_reltuples, bool inh);
static void BlockSampler_Init(BlockSampler bs, BlockNumber nblocks,
int samplesize);
static bool BlockSampler_HasMore(BlockSampler bs);
@@ -238,13 +240,21 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
/*
* Do the normal non-recursive ANALYZE.
*/
- do_analyze_rel(onerel, vacstmt, update_reltuples, false);
+ if (vacstmt->options & (VACOPT_CACHE))
+ do_cache_analyze_rel(onerel, vacstmt, update_reltuples, false);
+ else
+ do_analyze_rel(onerel, vacstmt, update_reltuples, false);
/*
* If there are child tables, do recursive ANALYZE.
*/
if (onerel->rd_rel->relhassubclass)
- do_analyze_rel(onerel, vacstmt, false, true);
+ {
+ if (vacstmt->options & (VACOPT_CACHE))
+ do_cache_analyze_rel(onerel, vacstmt, false, true);
+ else
+ do_analyze_rel(onerel, vacstmt, false, true);
+ }
/*
* Close source relation now, but keep lock so that no one deletes it
@@ -640,6 +650,120 @@ cleanup:
}
/*
+ * do_analyze_rel() -- analyze one relation, recursively or not
+ */
+static void
+do_cache_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
+ bool update_relcache, bool inh)
+{
+ int ind;
+ Relation *Irel;
+ int nindexes;
+ bool hasindex;
+ AnlIndexData *indexdata;
+ PGRUsage ru0;
+ TimestampTz starttime = 0;
+ MemoryContext caller_context;
+ int save_nestlevel;
+
+ if (inh)
+ ereport(elevel,
+ (errmsg("cache analyzing \"%s.%s\" inheritance tree",
+ get_namespace_name(RelationGetNamespace(onerel)),
+ RelationGetRelationName(onerel))));
+ else
+ ereport(elevel,
+ (errmsg("cache analyzing \"%s.%s\"",
+ get_namespace_name(RelationGetNamespace(onerel)),
+ RelationGetRelationName(onerel))));
+
+ /*
+ * Set up a working context so that we can easily free whatever junk gets
+ * created.
+ */
+ anl_context = AllocSetContextCreate(CurrentMemoryContext,
+ "Analyze",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ caller_context = MemoryContextSwitchTo(anl_context);
+
+ /*
+ * Arrange to make GUC variable changes local to this command.
+ */
+ save_nestlevel = NewGUCNestLevel();
+
+ /* measure elapsed time iff autovacuum logging requires it */
+ if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+ {
+ pg_rusage_init(&ru0);
+ if (Log_autovacuum_min_duration > 0)
+ starttime = GetCurrentTimestamp();
+ }
+
+ /*
+ * Open all indexes of the relation, and see if there are any analyzable
+ * columns in the indexes. We do not analyze index columns if there was
+ * an explicit column list in the ANALYZE command, however. If we are
+ * doing a recursive scan, we don't want to touch the parent's indexes at
+ * all.
+ */
+ if (!inh)
+ vac_open_indexes(onerel, AccessShareLock, &nindexes, &Irel);
+ else
+ {
+ Irel = NULL;
+ nindexes = 0;
+ }
+ hasindex = (nindexes > 0);
+ indexdata = NULL;
+
+ /*
+ * Update cache stats in pg_class.
+ */
+ cache_update_relstats(onerel,
+ RelationGetRelationOSCacheInFork(onerel, MAIN_FORKNUM),
+ RelationGetRelationPGCacheInFork(onerel, MAIN_FORKNUM),
+ InvalidTransactionId);
+
+ /*
+ * Same for indexes.
+ */
+ for (ind = 0; ind < nindexes; ind++)
+ {
+ cache_update_relstats(Irel[ind],
+ RelationGetRelationOSCacheInFork(Irel[ind], MAIN_FORKNUM),
+ RelationGetRelationPGCacheInFork(Irel[ind], MAIN_FORKNUM),
+ InvalidTransactionId);
+ }
+
+ /* Done with indexes */
+ vac_close_indexes(nindexes, Irel, NoLock);
+
+ /* Log the action if appropriate */
+ if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
+ {
+ if (Log_autovacuum_min_duration == 0 ||
+ TimestampDifferenceExceeds(starttime, GetCurrentTimestamp(),
+ Log_autovacuum_min_duration))
+ ereport(LOG,
+ (errmsg("automatic cache analyze of table \"%s.%s.%s\" system usage: %s",
+ get_database_name(MyDatabaseId),
+ get_namespace_name(RelationGetNamespace(onerel)),
+ RelationGetRelationName(onerel),
+ pg_rusage_show(&ru0))));
+ }
+
+ /* Roll back any GUC changes executed by index functions */
+ AtEOXact_GUC(false, save_nestlevel);
+
+ /* Restore current context and release memory */
+ MemoryContextSwitchTo(caller_context);
+ MemoryContextDelete(anl_context);
+ anl_context = NULL;
+}
+
+/*
* Compute statistics about indexes of a relation
*/
static void
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 9606569617..b45f012f9c 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -1096,3 +1096,61 @@ vacuum_delay_point(void)
CHECK_FOR_INTERRUPTS();
}
}
+
+
+/*
+ * cache_update_relstats() -- update cache statistics for one relation
+ *
+ * /!\ Same comment as function vac_update_relstats()
+ */
+void
+cache_update_relstats(Relation relation,
+ float4 per_oscache, float4 per_pgcache,
+ TransactionId frozenxid)
+{
+ Oid relid = RelationGetRelid(relation);
+ Relation rd;
+ HeapTuple ctup;
+ Form_pg_class pgcform;
+ bool dirty;
+
+ rd = heap_open(RelationRelationId, RowExclusiveLock);
+
+ /* Fetch a copy of the tuple to scribble on */
+ ctup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(ctup))
+ elog(ERROR, "pg_class entry for relid %u vanished during cache analyze",
+ relid);
+ pgcform = (Form_pg_class) GETSTRUCT(ctup);
+
+ /* Apply required updates, if any, to copied tuple */
+
+ dirty = false;
+ if (pgcform->reloscache != (float4) per_oscache)
+ {
+ pgcform->reloscache = (float4) per_oscache;
+ dirty = true;
+ }
+ if (pgcform->relpgcache != (float4) per_pgcache)
+ {
+ pgcform->relpgcache = (float4) per_pgcache;
+ dirty = true;
+ }
+
+ /*
+ * relfrozenxid should never go backward. Caller can pass
+ * InvalidTransactionId if it has no new data.
+ */
+ if (TransactionIdIsNormal(frozenxid) &&
+ TransactionIdPrecedes(pgcform->relfrozenxid, frozenxid))
+ {
+ pgcform->relfrozenxid = frozenxid;
+ dirty = true;
+ }
+
+ /* If anything changed, write out the tuple. */
+ if (dirty)
+ heap_inplace_update(rd, ctup);
+
+ heap_close(rd, RowExclusiveLock);
+}
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index fd8ea45b4a..39f9eabacd 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -108,7 +108,8 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
*/
if (!inhparent)
estimate_rel_size(relation, rel->attr_widths - rel->min_attr,
- &rel->pages, &rel->tuples);
+ &rel->pages, &rel->tuples,
+ &rel->oscache, &rel->pgcache);
/*
* Make list of indexes. Ignore indexes on system catalogs if told to.
@@ -323,11 +324,14 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
{
info->pages = RelationGetNumberOfBlocks(indexRelation);
info->tuples = rel->tuples;
+ info->oscache = 0;
+ info->pgcache = 0;
}
else
{
estimate_rel_size(indexRelation, NULL,
- &info->pages, &info->tuples);
+ &info->pages, &info->tuples,
+ &info->oscache, &info->pgcache);
if (info->tuples > rel->tuples)
info->tuples = rel->tuples;
}
@@ -362,7 +366,8 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
*/
void
estimate_rel_size(Relation rel, int32 *attr_widths,
- BlockNumber *pages, double *tuples)
+ BlockNumber *pages, double *tuples,
+ float4 *oscache, float4 *pgcache)
{
BlockNumber curpages;
BlockNumber relpages;
@@ -451,21 +456,29 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
density = (BLCKSZ - SizeOfPageHeaderData) / tuple_width;
}
*tuples = rint(density * (double) curpages);
+ *oscache = (float4) rel->rd_rel->reloscache;
+ *pgcache = (float4) rel->rd_rel->relpgcache;
break;
case RELKIND_SEQUENCE:
/* Sequences always have a known size */
*pages = 1;
*tuples = 1;
+ *oscache = 0;
+ *pgcache = 0;
break;
case RELKIND_FOREIGN_TABLE:
/* Just use whatever's in pg_class */
*pages = rel->rd_rel->relpages;
*tuples = rel->rd_rel->reltuples;
+ *oscache = 0;
+ *pgcache = 0;
break;
default:
/* else it has no disk storage; probably shouldn't get here? */
*pages = 0;
*tuples = 0;
+ *oscache = 0;
+ *pgcache = 0;
break;
}
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1d39674de4..cc0d6f5557 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -342,7 +342,7 @@ static void SplitColQualList(List *qualList,
%type <boolean> opt_instead
%type <boolean> opt_unique opt_concurrently opt_verbose opt_full
-%type <boolean> opt_freeze opt_default opt_recheck
+%type <boolean> opt_freeze opt_oscache opt_default opt_recheck
%type <defelt> opt_binary opt_oids copy_delimiter
%type <boolean> copy_from
@@ -529,7 +529,7 @@ static void SplitColQualList(List *qualList,
NULLS_P NUMERIC
OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTION OPTIONS OR
- ORDER OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER
+ ORDER OSCACHE OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER
PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POSITION
PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
@@ -7801,11 +7801,13 @@ vacuum_option_elem:
;
AnalyzeStmt:
- analyze_keyword opt_verbose
+ analyze_keyword opt_oscache opt_verbose
{
VacuumStmt *n = makeNode(VacuumStmt);
n->options = VACOPT_ANALYZE;
if ($2)
+ n->options |= VACOPT_CACHE;
+ if ($3)
n->options |= VACOPT_VERBOSE;
n->freeze_min_age = -1;
n->freeze_table_age = -1;
@@ -7813,16 +7815,18 @@ AnalyzeStmt:
n->va_cols = NIL;
$$ = (Node *)n;
}
- | analyze_keyword opt_verbose qualified_name opt_name_list
+ | analyze_keyword opt_oscache opt_verbose qualified_name opt_name_list
{
VacuumStmt *n = makeNode(VacuumStmt);
n->options = VACOPT_ANALYZE;
if ($2)
+ n->options |= VACOPT_CACHE;
+ if ($3)
n->options |= VACOPT_VERBOSE;
n->freeze_min_age = -1;
n->freeze_table_age = -1;
- n->relation = $3;
- n->va_cols = $4;
+ n->relation = $4;
+ n->va_cols = $5;
$$ = (Node *)n;
}
;
@@ -7845,6 +7849,11 @@ opt_freeze: FREEZE { $$ = TRUE; }
| /*EMPTY*/ { $$ = FALSE; }
;
+opt_oscache:
+ OSCACHE { $$ = TRUE; }
+ | /*EMPTY*/ { $$ = FALSE; }
+ ;
+
opt_name_list:
'(' name_list ')' { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; }
@@ -12158,6 +12167,7 @@ type_func_name_keyword:
| LIKE
| NATURAL
| NOTNULL
+ | OSCACHE
| OUTER_P
| OVER
| OVERLAPS
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index f96685db50..5cea9297e4 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -106,6 +106,13 @@ static volatile BufferDesc *BufferAlloc(SMgrRelation smgr,
static void FlushBuffer(volatile BufferDesc *buf, SMgrRelation reln);
static void AtProcExit_Buffers(int code, Datum arg);
+/*
+ * Hooks for plugins to get control in
+ * RelationGetRelationOSCacheInFork
+ * RelationGetRelationPGCacheInFork
+ */
+oscache_hook_type OSCache_hook = NULL;
+pgcache_hook_type PGCache_hook = NULL;
/*
* PrefetchBuffer -- initiate asynchronous read of a block of a relation
@@ -1922,6 +1929,40 @@ RelationGetNumberOfBlocksInFork(Relation relation, ForkNumber forkNum)
return smgrnblocks(relation->rd_smgr, forkNum);
}
+/*
+ * RelationGetRelationOSCacheInFork
+ * Determines the current percentage of pages in OS cache for the
+ * relation.
+ */
+float4
+RelationGetRelationOSCacheInFork(Relation relation, ForkNumber forkNum)
+{
+ float4 percent = 0;
+
+ /* if a plugin is present, let it manage things */
+ if (OSCache_hook)
+ percent = (*OSCache_hook) (relation, forkNum);
+
+ return percent;
+}
+
+/*
+ * RelationGetRelationPGCacheInFork
+ * Determines the current percentage of pages in PostgreSQL cache
+ * for the relation.
+ */
+float4
+RelationGetRelationPGCacheInFork(Relation relation, ForkNumber forkNum)
+{
+ float4 percent = 0;
+
+ /* if a plugin is present, let it manage things */
+ if (PGCache_hook)
+ percent = (*PGCache_hook) (relation, forkNum);
+
+ return percent;
+}
+
/* ---------------------------------------------------------------------
* DropRelFileNodeBuffers
*
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index d7e94ffc12..159096a29a 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -1417,6 +1417,8 @@ formrdesc(const char *relationName, Oid relationReltype,
relation->rd_rel->relpages = 1;
relation->rd_rel->reltuples = 1;
+ relation->rd_rel->reloscache = 0;
+ relation->rd_rel->relpgcache = 0;
relation->rd_rel->relkind = RELKIND_RELATION;
relation->rd_rel->relhasoids = hasoids;
relation->rd_rel->relnatts = (int16) natts;
@@ -2661,6 +2663,8 @@ RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid)
{
classform->relpages = 0; /* it's empty until further notice */
classform->reltuples = 0;
+ classform->reloscache = 0;
+ classform->relpgcache = 0;
}
classform->relfrozenxid = freezeXid;
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index ffcce3c87c..dc79df51ca 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -45,6 +45,8 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO
Oid reltablespace; /* identifier of table space for relation */
int4 relpages; /* # of blocks (not always up-to-date) */
float4 reltuples; /* # of tuples (not always up-to-date) */
+ float4 reloscache; /* % of files in OS cache (not always up-to-date) */
+ float4 relpgcache; /* % of files in PostgreSQL cache (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 */
@@ -92,7 +94,7 @@ typedef FormData_pg_class *Form_pg_class;
* ----------------
*/
-#define Natts_pg_class 26
+#define Natts_pg_class 28
#define Anum_pg_class_relname 1
#define Anum_pg_class_relnamespace 2
#define Anum_pg_class_reltype 3
@@ -103,22 +105,24 @@ typedef FormData_pg_class *Form_pg_class;
#define Anum_pg_class_reltablespace 8
#define Anum_pg_class_relpages 9
#define Anum_pg_class_reltuples 10
-#define Anum_pg_class_reltoastrelid 11
-#define Anum_pg_class_reltoastidxid 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_relfrozenxid 24
-#define Anum_pg_class_relacl 25
-#define Anum_pg_class_reloptions 26
+#define Anum_pg_class_reloscache 11
+#define Anum_pg_class_relpgcache 12
+#define Anum_pg_class_reltoastrelid 13
+#define Anum_pg_class_reltoastidxid 14
+#define Anum_pg_class_relhasindex 15
+#define Anum_pg_class_relisshared 16
+#define Anum_pg_class_relpersistence 17
+#define Anum_pg_class_relkind 18
+#define Anum_pg_class_relnatts 19
+#define Anum_pg_class_relchecks 20
+#define Anum_pg_class_relhasoids 21
+#define Anum_pg_class_relhaspkey 22
+#define Anum_pg_class_relhasrules 23
+#define Anum_pg_class_relhastriggers 24
+#define Anum_pg_class_relhassubclass 25
+#define Anum_pg_class_relfrozenxid 26
+#define Anum_pg_class_relacl 27
+#define Anum_pg_class_reloptions 28
/* ----------------
* initial contents of pg_class
@@ -130,13 +134,13 @@ typedef FormData_pg_class *Form_pg_class;
*/
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
-DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 29 0 t f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 0 0 f f p r 29 0 t f f f f 3 _null_ _null_ ));
DESCR("");
-DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 20 0 f f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 0 0 f f p r 20 0 f f f f f 3 _null_ _null_ ));
DESCR("");
-DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 25 0 t f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 0 0 f f p r 25 0 t f f f f 3 _null_ _null_ ));
DESCR("");
-DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f p r 26 0 t f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 0 0 f f p r 28 0 t f f f f 3 _null_ _null_ ));
DESCR("");
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index 79c9f5d90f..7f1801ab35 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -155,6 +155,11 @@ extern void vacuum_set_xid_limits(int freeze_min_age, int freeze_table_age,
extern void vac_update_datfrozenxid(void);
extern void vacuum_delay_point(void);
+extern void cache_update_relstats(Relation relation,
+ float4 per_oscache,
+ float4 per_pgcache,
+ TransactionId frozenxid);
+
/* in commands/vacuumlazy.c */
extern void lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
BufferAccessStrategy bstrategy, bool *scanned_all);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ee1881b630..bc7a3010da 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2417,7 +2417,8 @@ typedef enum VacuumOption
VACOPT_VERBOSE = 1 << 2, /* print progress info */
VACOPT_FREEZE = 1 << 3, /* FREEZE option */
VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */
- VACOPT_NOWAIT = 1 << 5
+ VACOPT_NOWAIT = 1 << 5,
+ VACOPT_CACHE = 1 << 6 /* do CACHE stats analyze */
} VacuumOption;
typedef struct VacuumStmt
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index f6592697e4..3f08bb00e3 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -408,6 +408,8 @@ typedef struct RelOptInfo
List *indexlist; /* list of IndexOptInfo */
BlockNumber pages;
double tuples;
+ float4 oscache;
+ float4 pgcache;
struct Plan *subplan; /* if subquery */
List *subrtable; /* if subquery */
List *subrowmark; /* if subquery */
@@ -466,6 +468,8 @@ typedef struct IndexOptInfo
/* statistics from pg_class */
BlockNumber pages; /* number of disk pages in index */
double tuples; /* number of index tuples in index */
+ float4 oscache;
+ float4 pgcache;
/* index descriptor information */
int ncolumns; /* number of columns in index */
diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h
index c0b8eda813..1dc78d5bbb 100644
--- a/src/include/optimizer/plancat.h
+++ b/src/include/optimizer/plancat.h
@@ -29,7 +29,8 @@ extern void get_relation_info(PlannerInfo *root, Oid relationObjectId,
bool inhparent, RelOptInfo *rel);
extern void estimate_rel_size(Relation rel, int32 *attr_widths,
- BlockNumber *pages, double *tuples);
+ BlockNumber *pages, double *tuples,
+ float4 *oscache, float4 *pgcache);
extern int32 get_relation_data_width(Oid relid, int32 *attr_widths);
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 12c2faf3de..95a7e3d320 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -264,6 +264,7 @@ PG_KEYWORD("option", OPTION, UNRESERVED_KEYWORD)
PG_KEYWORD("options", OPTIONS, UNRESERVED_KEYWORD)
PG_KEYWORD("or", OR, RESERVED_KEYWORD)
PG_KEYWORD("order", ORDER, RESERVED_KEYWORD)
+PG_KEYWORD("oscache", OSCACHE, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("out", OUT_P, COL_NAME_KEYWORD)
PG_KEYWORD("outer", OUTER_P, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("over", OVER, TYPE_FUNC_NAME_KEYWORD)
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index b8fc87ec57..8b621de791 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -179,6 +179,10 @@ extern void CheckPointBuffers(int flags);
extern BlockNumber BufferGetBlockNumber(Buffer buffer);
extern BlockNumber RelationGetNumberOfBlocksInFork(Relation relation,
ForkNumber forkNum);
+extern float4 RelationGetRelationOSCacheInFork(Relation relation,
+ ForkNumber forkNum);
+extern float4 RelationGetRelationPGCacheInFork(Relation relation,
+ ForkNumber forkNum);
extern void FlushRelationBuffers(Relation rel);
extern void FlushDatabaseBuffers(Oid dbid);
extern void DropRelFileNodeBuffers(RelFileNodeBackend rnode,
@@ -215,4 +219,14 @@ extern void AtProcExit_LocalBuffers(void);
extern BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype);
extern void FreeAccessStrategy(BufferAccessStrategy strategy);
+/*
+* Hooks for plugins to get control in
+* RelationGetRelationOSCacheInFork
+* RelationGetRelationPGCacheInFork
+*/
+typedef float4 (*oscache_hook_type) (Relation relation, ForkNumber forkNum);
+extern PGDLLIMPORT oscache_hook_type OSCache_hook;
+typedef float4 (*pgcache_hook_type) (Relation relation, ForkNumber forkNum);
+extern PGDLLIMPORT pgcache_hook_type PGCache_hook;
+
#endif
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 5e28289614..64ef53fd00 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -963,6 +963,7 @@ OprCacheKey
OprInfo
OprProofCacheEntry
OprProofCacheKey
+OSCache_hook_type
OutputContext
OverrideSearchPath
OverrideStackEntry
@@ -973,6 +974,7 @@ PBOOL
PCtxtHandle
PFN
PGAsyncStatusType
+PGCache_hook_type
PGCALL2
PGEvent
PGEventConnDestroy