From 76ed77241453adfb10894eed6b03ea799a36923b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?C=C3=A9dric=20Villemain?= <cedric@2ndquadrant.fr>
Date: Thu, 26 May 2011 00:35:13 +0200
Subject: [PATCH 3/7] Add "ANALYZE OSCACHE [VERBOSE] [relation];"

it updates the oscache column in pg_class
with, currently, dummy functions
---
 src/backend/commands/analyze.c      |  124 ++++++++++++++++++++++++++++++++++-
 src/backend/parser/gram.y           |   22 +++++--
 src/backend/storage/buffer/bufmgr.c |   13 ++++
 src/include/nodes/parsenodes.h      |    3 +-
 src/include/parser/kwlist.h         |    1 +
 src/include/storage/bufmgr.h        |    2 +
 6 files changed, 156 insertions(+), 9 deletions(-)

diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
new file mode 100644
index 57188bc..a78f46c
*** a/src/backend/commands/analyze.c
--- b/src/backend/commands/analyze.c
*************** static BufferAccessStrategy vac_strategy
*** 85,90 ****
--- 85,91 ----
  
  
  static void do_analyze_rel(Relation onerel, VacuumStmt *vacstmt, bool inh);
+ static void do_oscache_analyze_rel(Relation onerel, bool inh);
  static void BlockSampler_Init(BlockSampler bs, BlockNumber nblocks,
  				  int samplesize);
  static bool BlockSampler_HasMore(BlockSampler bs);
*************** analyze_rel(Oid relid, VacuumStmt *vacst
*** 228,240 ****
  	/*
  	 * Do the normal non-recursive ANALYZE.
  	 */
! 	do_analyze_rel(onerel, vacstmt, false);
  
  	/*
  	 * If there are child tables, do recursive ANALYZE.
  	 */
  	if (onerel->rd_rel->relhassubclass)
! 		do_analyze_rel(onerel, vacstmt, true);
  
  	/*
  	 * Close source relation now, but keep lock so that no one deletes it
--- 229,249 ----
  	/*
  	 * Do the normal non-recursive ANALYZE.
  	 */
! 	if (vacstmt->options & (VACOPT_OSCACHE))
! 		do_oscache_analyze_rel(onerel, false);
! 	else
! 		do_analyze_rel(onerel, vacstmt, false);
  
  	/*
  	 * If there are child tables, do recursive ANALYZE.
  	 */
  	if (onerel->rd_rel->relhassubclass)
! 	{
! 		if (vacstmt->options & (VACOPT_OSCACHE))
! 			do_oscache_analyze_rel(onerel, true);
! 		else
! 			do_analyze_rel(onerel, vacstmt, true);
! 	}
  
  	/*
  	 * Close source relation now, but keep lock so that no one deletes it
*************** cleanup:
*** 623,628 ****
--- 632,748 ----
  
  	/* Restore current context and release memory */
  	MemoryContextSwitchTo(caller_context);
+ 	MemoryContextDelete(anl_context);
+ 	anl_context = NULL;
+ }
+ 
+ /*
+  *	do_analyze_rel() -- analyze one relation, recursively or not
+  */
+ static void
+ do_oscache_analyze_rel(Relation onerel, 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.
+ 	 */
+ 	oscache_update_relstats(onerel,
+ 							RelationGetRelationOSCacheInFork(onerel, MAIN_FORKNUM),
+ 							InvalidTransactionId);
+ 
+ 	/*
+ 	 * Same for indexes.
+ 	 */
+ 	for (ind = 0; ind < nindexes; ind++)
+ 	{
+ 		oscache_update_relstats(Irel[ind],
+ 								RelationGetRelationOSCacheInFork(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;
  }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
new file mode 100644
index 1d39674..c312227
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** static void SplitColQualList(List *qualL
*** 342,348 ****
  
  %type <boolean> opt_instead
  %type <boolean> opt_unique opt_concurrently opt_verbose opt_full
! %type <boolean> opt_freeze opt_default opt_recheck
  %type <defelt>	opt_binary opt_oids copy_delimiter
  
  %type <boolean> copy_from
--- 342,348 ----
  
  %type <boolean> opt_instead
  %type <boolean> opt_unique opt_concurrently opt_verbose opt_full
! %type <boolean> opt_freeze opt_oscache opt_default opt_recheck
  %type <defelt>	opt_binary opt_oids copy_delimiter
  
  %type <boolean> copy_from
*************** static void SplitColQualList(List *qualL
*** 529,535 ****
  	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
  
  	PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POSITION
  	PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
--- 529,535 ----
  	NULLS_P NUMERIC
  
  	OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTION OPTIONS OR
! 	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
*************** vacuum_option_elem:
*** 7801,7811 ****
  		;
  
  AnalyzeStmt:
! 			analyze_keyword opt_verbose
  				{
  					VacuumStmt *n = makeNode(VacuumStmt);
  					n->options = VACOPT_ANALYZE;
  					if ($2)
  						n->options |= VACOPT_VERBOSE;
  					n->freeze_min_age = -1;
  					n->freeze_table_age = -1;
--- 7801,7813 ----
  		;
  
  AnalyzeStmt:
! 			analyze_keyword opt_oscache opt_verbose
  				{
  					VacuumStmt *n = makeNode(VacuumStmt);
  					n->options = VACOPT_ANALYZE;
  					if ($2)
+ 						n->options |= VACOPT_OSCACHE;
+ 					if ($3)
  						n->options |= VACOPT_VERBOSE;
  					n->freeze_min_age = -1;
  					n->freeze_table_age = -1;
*************** AnalyzeStmt:
*** 7813,7828 ****
  					n->va_cols = NIL;
  					$$ = (Node *)n;
  				}
! 			| analyze_keyword opt_verbose qualified_name opt_name_list
  				{
  					VacuumStmt *n = makeNode(VacuumStmt);
  					n->options = VACOPT_ANALYZE;
  					if ($2)
  						n->options |= VACOPT_VERBOSE;
  					n->freeze_min_age = -1;
  					n->freeze_table_age = -1;
! 					n->relation = $3;
! 					n->va_cols = $4;
  					$$ = (Node *)n;
  				}
  		;
--- 7815,7832 ----
  					n->va_cols = NIL;
  					$$ = (Node *)n;
  				}
! 			| analyze_keyword opt_oscache opt_verbose qualified_name opt_name_list
  				{
  					VacuumStmt *n = makeNode(VacuumStmt);
  					n->options = VACOPT_ANALYZE;
  					if ($2)
+ 						n->options |= VACOPT_OSCACHE;
+ 					if ($3)
  						n->options |= VACOPT_VERBOSE;
  					n->freeze_min_age = -1;
  					n->freeze_table_age = -1;
! 					n->relation = $4;
! 					n->va_cols = $5;
  					$$ = (Node *)n;
  				}
  		;
*************** opt_freeze: FREEZE									{ $$ = TRUE;
*** 7845,7850 ****
--- 7849,7859 ----
  			| /*EMPTY*/								{ $$ = FALSE; }
  		;
  
+ opt_oscache:
+ 			OSCACHE									{ $$ = TRUE; }
+ 			| /*EMPTY*/                             { $$ = FALSE; }
+ 		;
+ 
  opt_name_list:
  			'(' name_list ')'						{ $$ = $2; }
  			| /*EMPTY*/								{ $$ = NIL; }
*************** type_func_name_keyword:
*** 12158,12163 ****
--- 12167,12173 ----
  			| LIKE
  			| NATURAL
  			| NOTNULL
+ 			| OSCACHE
  			| OUTER_P
  			| OVER
  			| OVERLAPS
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
new file mode 100644
index b12348b..670dbdf
*** a/src/backend/storage/buffer/bufmgr.c
--- b/src/backend/storage/buffer/bufmgr.c
*************** RelationGetNumberOfBlocksInFork(Relation
*** 1928,1933 ****
--- 1928,1946 ----
  	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;
+ 
+ 	return percent;
+ }
+ 
  /* ---------------------------------------------------------------------
   *		DropRelFileNodeBuffers
   *
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
new file mode 100644
index 14937d4..bc191c2
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef enum VacuumOption
*** 2417,2423 ****
  	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
  } VacuumOption;
  
  typedef struct VacuumStmt
--- 2417,2424 ----
  	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_OSCACHE = 1 << 6		/* do OSCACHE stats analyze */
  } VacuumOption;
  
  typedef struct VacuumStmt
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
new file mode 100644
index 12c2faf..95a7e3d
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
*************** PG_KEYWORD("option", OPTION, UNRESERVED_
*** 264,269 ****
--- 264,270 ----
  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
new file mode 100644
index b8fc87e..7d22b5a
*** a/src/include/storage/bufmgr.h
--- b/src/include/storage/bufmgr.h
*************** extern void CheckPointBuffers(int flags)
*** 179,184 ****
--- 179,186 ----
  extern BlockNumber BufferGetBlockNumber(Buffer buffer);
  extern BlockNumber RelationGetNumberOfBlocksInFork(Relation relation,
  								ForkNumber forkNum);
+ extern float4 RelationGetRelationOSCacheInFork(Relation relation,
+ 								ForkNumber forkNum);
  extern void FlushRelationBuffers(Relation rel);
  extern void FlushDatabaseBuffers(Oid dbid);
  extern void DropRelFileNodeBuffers(RelFileNodeBackend rnode,
-- 
1.7.5.3

