From 5d8145dd0c47fb891139568f82ac87bc2403cfec Mon Sep 17 00:00:00 2001
From: Craig Ringer <craig@2ndquadrant.com>
Date: Mon, 20 Jan 2014 13:44:45 +0800
Subject: [PATCH 01/10] RLS: Make plan caching dependent on user ID.

---
 src/backend/optimizer/plan/planner.c |  2 ++
 src/backend/utils/cache/plancache.c  | 32 ++++++++++++++++++++++++++++++++
 src/include/nodes/plannodes.h        |  2 ++
 src/include/nodes/relation.h         |  2 ++
 src/include/utils/plancache.h        |  2 ++
 5 files changed, 40 insertions(+)

diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 35bda67..8c12eb9 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -177,6 +177,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 	glob->lastPHId = 0;
 	glob->lastRowMarkId = 0;
 	glob->transientPlan = false;
+	glob->planUserId = InvalidOid;
 
 	/* Determine what fraction of the plan is likely to be scanned */
 	if (cursorOptions & CURSOR_OPT_FAST_PLAN)
@@ -254,6 +255,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 	result->relationOids = glob->relationOids;
 	result->invalItems = glob->invalItems;
 	result->nParamExec = glob->nParamExec;
+	result->planUserId = glob->planUserId;
 
 	return result;
 }
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index d492cbb..77de2df 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -53,6 +53,7 @@
 #include "catalog/namespace.h"
 #include "executor/executor.h"
 #include "executor/spi.h"
+#include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/cost.h"
 #include "optimizer/planmain.h"
@@ -795,6 +796,16 @@ CheckCachedPlan(CachedPlanSource *plansource)
 		AcquireExecutorLocks(plan->stmt_list, true);
 
 		/*
+		 * If plan was constructed with assumption of a particular user-id,
+		 * and it is different from the current one, the cached-plan shall
+		 * be invalidated to construct suitable query plan.
+		 */
+		if (plan->is_valid &&
+			OidIsValid(plan->planUserId) &&
+			plan->planUserId == GetUserId())
+			plan->is_valid = false;
+
+		/*
 		 * If plan was transient, check to see if TransactionXmin has
 		 * advanced, and if so invalidate it.
 		 */
@@ -847,6 +858,8 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
 {
 	CachedPlan *plan;
 	List	   *plist;
+	ListCell   *cell;
+	Oid			planUserId = InvalidOid;
 	bool		snapshot_set;
 	bool		spi_pushed;
 	MemoryContext plan_context;
@@ -914,6 +927,24 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
 		PopActiveSnapshot();
 
 	/*
+	 * Check whether the generated plan assumes a particular user-id, or not.
+	 * In case when a valid user-id is recorded on PlannedStmt->planUserId,
+	 * it should be kept and used to validation check of the cached plan
+	 * under the "current" user-id.
+	 */
+	foreach (cell, plist)
+	{
+		PlannedStmt	*pstmt = lfirst(cell);
+
+		if (IsA(pstmt, PlannedStmt) && OidIsValid(pstmt->planUserId))
+		{
+			Assert(!OidIsValid(planUserId) || planUserId == pstmt->planUserId);
+
+			planUserId = pstmt->planUserId;
+		}
+	}
+
+	/*
 	 * Normally we make a dedicated memory context for the CachedPlan and its
 	 * subsidiary data.  (It's probably not going to be large, but just in
 	 * case, use the default maxsize parameter.  It's transient for the
@@ -956,6 +987,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
 	plan->is_oneshot = plansource->is_oneshot;
 	plan->is_saved = false;
 	plan->is_valid = true;
+	plan->planUserId = planUserId;
 
 	/* assign generation number to new plan */
 	plan->generation = ++(plansource->generation);
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 38c039c..35c88cc 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -67,6 +67,8 @@ typedef struct PlannedStmt
 	List	   *invalItems;		/* other dependencies, as PlanInvalItems */
 
 	int			nParamExec;		/* number of PARAM_EXEC Params used */
+
+	Oid			planUserId;		/* user-id this plan assumed, or InvalidOid */
 } PlannedStmt;
 
 /* macro for fetching the Plan associated with a SubPlan node */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 8aa40d0..74d0e12 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -99,6 +99,8 @@ typedef struct PlannerGlobal
 	Index		lastRowMarkId;	/* highest PlanRowMark ID assigned */
 
 	bool		transientPlan;	/* redo plan when TransactionXmin changes? */
+
+	Oid			planUserId;		/* User-Id to be assumed on this plan */
 } PlannerGlobal;
 
 /* macro for fetching the Plan associated with a SubPlan node */
diff --git a/src/include/utils/plancache.h b/src/include/utils/plancache.h
index b8ca643..ff89700 100644
--- a/src/include/utils/plancache.h
+++ b/src/include/utils/plancache.h
@@ -128,6 +128,8 @@ typedef struct CachedPlan
 	bool		is_oneshot;		/* is it a "oneshot" plan? */
 	bool		is_saved;		/* is CachedPlan in a long-lived context? */
 	bool		is_valid;		/* is the stmt_list currently valid? */
+	Oid			planUserId;		/* is user-id that is assumed on this cached
+								   plan, or InvalidOid if portable for anybody */
 	TransactionId saved_xmin;	/* if valid, replan when TransactionXmin
 								 * changes from this value */
 	int			generation;		/* parent's generation number for this plan */
-- 
1.8.3.1