From 3b8515aaea164174d18ef2450b19478de8e64298 Mon Sep 17 00:00:00 2001
From: csteam <mplageman@pivotal.io>
Date: Tue, 16 Jul 2019 16:19:49 -0700
Subject: [PATCH v1 2/3] Execution-time scan col extraction and comparison with
 plan-time cols

Extract the columns needed to scan directly before scanning from the
ScanState and compare this set of columns with those extracted at
plan-time.

In regress, there are two main queries where there is a difference in
the columns extracted at plan time vs execution time. They are both due
to the fact that UPDATE/DELETE on partition tables adds the contents of
the returningList to the PathTargets in the RelOptInfos. This manifests
as a difference in the column sets.
---
 src/backend/executor/execScan.c    | 63 ++++++++++++++++++++++++++++++
 src/backend/executor/nodeSeqscan.c | 24 ++++++++++++
 src/include/executor/executor.h    |  4 ++
 3 files changed, 91 insertions(+)

diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index c0e4a5376c..89146693a3 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -20,6 +20,7 @@
 
 #include "executor/executor.h"
 #include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
 #include "utils/memutils.h"
 
 
@@ -300,3 +301,65 @@ ExecScanReScan(ScanState *node)
 		}
 	}
 }
+
+typedef struct neededColumnContext
+{
+	Bitmapset **mask;
+	int n;
+} neededColumnContext;
+
+static bool
+neededColumnContextWalker(Node *node, neededColumnContext *c)
+{
+	if (node == NULL)
+		return false;
+
+	if (IsA(node, Var))
+	{
+		Var *var = (Var *)node;
+
+		if (var->varattno >= 0)
+		{
+			Assert(var->varattno <= c->n);
+			*(c->mask) = bms_add_member(*(c->mask), var->varattno);
+		}
+
+		return false;
+	}
+	return expression_tree_walker(node, neededColumnContextWalker, (void * )c);
+}
+
+/*
+ * n specifies the number of allowed entries in mask: we use
+ * it for bounds-checking in the walker above.
+ */
+void
+PopulateNeededColumnsForNode(Node *expr, int n, Bitmapset **scanCols)
+{
+	neededColumnContext c;
+
+	c.mask = scanCols;
+	c.n = n;
+
+	neededColumnContextWalker(expr, &c);
+}
+
+Bitmapset *
+PopulateNeededColumnsForScan(ScanState *scanstate, int ncol)
+{
+	Bitmapset *result = NULL;
+	Plan	   *plan = scanstate->ps.plan;
+
+	PopulateNeededColumnsForNode((Node *) plan->targetlist, ncol, &result);
+	PopulateNeededColumnsForNode((Node *) plan->qual, ncol, &result);
+
+	if (IsA(plan, IndexScan))
+	{
+		PopulateNeededColumnsForNode((Node *) ((IndexScan *) plan)->indexqualorig, ncol, &result);
+		PopulateNeededColumnsForNode((Node *) ((IndexScan *) plan)->indexorderbyorig, ncol, &result);
+	}
+	else if (IsA(plan, BitmapHeapScan))
+		PopulateNeededColumnsForNode((Node *) ((BitmapHeapScan *) plan)->bitmapqualorig, ncol, &result);
+
+	return result;
+}
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 436b43f8ca..c4b6a35554 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -64,6 +64,30 @@ SeqNext(SeqScanState *node)
 
 	if (scandesc == NULL)
 	{
+		/*
+		 * Print used cols extracted during planning as well as
+		 * used cols extracted now from the ScanState
+		 */
+		int plan_col_num;
+		Bitmapset *execution_cols = NULL;
+		Scan *planNode = (Scan *)node->ss.ps.plan;
+		int ncols = node->ss.ss_currentRelation->rd_att->natts;
+		int rti = planNode->scanrelid;
+		RangeTblEntry *rangeTblEntry = list_nth(estate->es_plannedstmt->rtable, rti - 1);
+
+		Bitmapset *plan_cols = rangeTblEntry->scanCols;
+#ifdef USE_ASSERT_CHECKING
+		while ((plan_col_num = bms_next_member(plan_cols, ncols)) >= 0)
+			Assert(plan_col_num <= ncols);
+#endif
+		execution_cols = PopulateNeededColumnsForScan(&node->ss, ncols);
+
+		if (bms_is_empty(bms_difference(plan_cols, execution_cols)) == false)
+			elog(NOTICE, "table: %s.\n exec-time cols: %s\n plan-time cols: %s",
+				RelationGetRelationName(node->ss.ss_currentRelation),
+				bmsToString(execution_cols),
+				bmsToString(plan_cols));
+
 		/*
 		 * We reach here if the scan is not parallel, or if we're serially
 		 * executing a scan that was planned to be parallel.
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 1fb28b4596..310fa2c1bb 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -596,5 +596,9 @@ extern void CheckCmdReplicaIdentity(Relation rel, CmdType cmd);
 
 extern void CheckSubscriptionRelkind(char relkind, const char *nspname,
 									 const char *relname);
+extern void
+PopulateNeededColumnsForNode(Node *expr, int n, Bitmapset **scanCols);
+extern Bitmapset *
+PopulateNeededColumnsForScan(ScanState *scanstate, int ncol);
 
 #endif							/* EXECUTOR_H  */
-- 
2.22.0

