From 20b34be19e51b878c33306d13a453bdd05da23bc Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Thu, 12 Jan 2017 21:51:34 +0900
Subject: [PATCH 1/2] Fix current query string of query tree.

---
 src/backend/catalog/pg_proc.c      |  2 +-
 src/backend/commands/extension.c   |  2 +-
 src/backend/commands/foreigncmds.c |  3 ++-
 src/backend/commands/prepare.c     |  2 +-
 src/backend/commands/tablecmds.c   |  2 +-
 src/backend/executor/functions.c   |  2 +-
 src/backend/executor/spi.c         |  4 ++--
 src/backend/nodes/copyfuncs.c      | 15 +++++++++++++++
 src/backend/nodes/equalfuncs.c     | 13 +++++++++++++
 src/backend/parser/analyze.c       |  4 ++++
 src/backend/parser/gram.y          | 20 ++++++++++++++++++--
 src/backend/parser/parse_type.c    |  2 +-
 src/backend/tcop/postgres.c        | 22 ++++++++++++++++------
 src/backend/tcop/pquery.c          |  1 +
 src/include/nodes/nodes.h          |  1 +
 src/include/nodes/parsenodes.h     | 20 ++++++++++++++++++++
 src/include/nodes/plannodes.h      |  2 ++
 17 files changed, 100 insertions(+), 17 deletions(-)

diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 6d8f17d..6d9e639 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -934,7 +934,7 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
 			querytree_list = NIL;
 			foreach(lc, raw_parsetree_list)
 			{
-				Node	   *parsetree = (Node *) lfirst(lc);
+				Node	   *parsetree = (Node *) stripParseNode(lfirst(lc));
 				List	   *querytree_sublist;
 
 				querytree_sublist = pg_analyze_and_rewrite_params(parsetree,
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index be52148..36ebf93 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -712,7 +712,7 @@ execute_sql_string(const char *sql, const char *filename)
 	 */
 	foreach(lc1, raw_parsetree_list)
 	{
-		Node	   *parsetree = (Node *) lfirst(lc1);
+		Node	   *parsetree = (Node *) stripParseNode(lfirst(lc1));
 		List	   *stmt_list;
 		ListCell   *lc2;
 
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 06b4bc3..12fa20f 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -1572,7 +1572,8 @@ ImportForeignSchema(ImportForeignSchemaStmt *stmt)
 		 */
 		foreach(lc2, raw_parsetree_list)
 		{
-			CreateForeignTableStmt *cstmt = lfirst(lc2);
+			CreateForeignTableStmt *cstmt =
+				(CreateForeignTableStmt *) stripParseNode(lfirst(lc2));
 
 			/*
 			 * Because we only allow CreateForeignTableStmt, we can skip parse
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index d768cf8..5f6e8e4 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -255,7 +255,7 @@ ExecuteQuery(ExecuteStmt *stmt, IntoClause *intoClause,
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 					 errmsg("prepared statement is not a SELECT")));
-		pstmt = (PlannedStmt *) linitial(plan_list);
+		pstmt = (PlannedStmt *) stripParseNode(linitial(plan_list));
 		if (!IsA(pstmt, PlannedStmt) ||
 			pstmt->commandType != CMD_SELECT ||
 			pstmt->utilityStmt != NULL)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 42558ec..5b01cf7 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9285,7 +9285,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
 	querytree_list = NIL;
 	foreach(list_item, raw_parsetree_list)
 	{
-		Node	   *stmt = (Node *) lfirst(list_item);
+		Node	   *stmt = (Node *) stripParseNode(lfirst(list_item));
 
 		if (IsA(stmt, IndexStmt))
 			querytree_list = lappend(querytree_list,
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 0a1bb9d..46c2388 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -695,7 +695,7 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
 	flat_query_list = NIL;
 	foreach(lc, raw_parsetree_list)
 	{
-		Node	   *parsetree = (Node *) lfirst(lc);
+		Node	   *parsetree = (Node *) stripParseNode(lfirst(lc));
 		List	   *queryTree_sublist;
 
 		queryTree_sublist = pg_analyze_and_rewrite_params(parsetree,
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index ee7a7e2..2f35f6b 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1757,7 +1757,7 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
 
 	foreach(list_item, raw_parsetree_list)
 	{
-		Node	   *parsetree = (Node *) lfirst(list_item);
+		Node	   *parsetree = (Node *) stripParseNode(lfirst(list_item));
 		List	   *stmt_list;
 		CachedPlanSource *plansource;
 
@@ -1859,7 +1859,7 @@ _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan)
 
 	foreach(list_item, raw_parsetree_list)
 	{
-		Node	   *parsetree = (Node *) lfirst(list_item);
+		Node	   *parsetree = (Node *) stripParseNode(lfirst(list_item));
 		CachedPlanSource *plansource;
 
 		plansource = CreateOneShotCachedPlan(parsetree,
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 930f2f1..47d6934 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2771,6 +2771,18 @@ _copyQuery(const Query *from)
 	return newnode;
 }
 
+static ParseNode *
+_copyParseNode(const ParseNode *from)
+{
+	ParseNode	   *newnode = makeNode(ParseNode);
+
+	COPY_SCALAR_FIELD(location);
+	COPY_SCALAR_FIELD(length);
+	COPY_NODE_FIELD(stmt);
+
+	return newnode;
+}
+
 static InsertStmt *
 _copyInsertStmt(const InsertStmt *from)
 {
@@ -4728,6 +4740,9 @@ copyObject(const void *from)
 		case T_Query:
 			retval = _copyQuery(from);
 			break;
+		case T_ParseNode:
+			retval = _copyParseNode(from);
+			break;
 		case T_InsertStmt:
 			retval = _copyInsertStmt(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index a27e5ed..a81bf87 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -951,6 +951,16 @@ _equalQuery(const Query *a, const Query *b)
 }
 
 static bool
+_equalParseNode(const ParseNode *a, const ParseNode *b)
+{
+	COMPARE_SCALAR_FIELD(location);
+	COMPARE_SCALAR_FIELD(length);
+	COMPARE_NODE_FIELD(stmt);
+
+	return true;
+}
+
+static bool
 _equalInsertStmt(const InsertStmt *a, const InsertStmt *b)
 {
 	COMPARE_NODE_FIELD(relation);
@@ -3015,6 +3025,9 @@ equal(const void *a, const void *b)
 		case T_Query:
 			retval = _equalQuery(a, b);
 			break;
+		case T_ParseNode:
+			retval = _equalParseNode(a, b);
+			break;
 		case T_InsertStmt:
 			retval = _equalInsertStmt(a, b);
 			break;
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 5116cbb..909734d 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -183,6 +183,8 @@ parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
 Query *
 transformTopLevelStmt(ParseState *pstate, Node *parseTree)
 {
+	parseTree = stripParseNode(parseTree);
+
 	if (IsA(parseTree, SelectStmt))
 	{
 		SelectStmt *stmt = (SelectStmt *) parseTree;
@@ -224,6 +226,8 @@ transformStmt(ParseState *pstate, Node *parseTree)
 {
 	Query	   *result;
 
+	Assert(!IsA(parseTree, ParseNode));
+
 	/*
 	 * We apply RAW_EXPRESSION_COVERAGE_TEST testing to basic DML statements;
 	 * we can't just run it on everything because raw_expression_tree_walker()
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 9eef550..45ac3ae 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -761,15 +761,31 @@ stmtblock:	stmtmulti
 /* the thrashing around here is to discard "empty" statements... */
 stmtmulti:	stmtmulti ';' stmt
 				{
+					if ($1 != NULL)
+					{
+						ParseNode *last = (ParseNode *) llast($1);
+						last->length = @2 - last->location + 1;
+					}
 					if ($3 != NULL)
-						$$ = lappend($1, $3);
+					{
+						ParseNode *pn = makeNode(ParseNode);
+						pn->stmt = $3;
+						pn->location = @3;
+						$$ = lappend($1, pn);
+					}
 					else
 						$$ = $1;
 				}
 			| stmt
 				{
 					if ($1 != NULL)
-						$$ = list_make1($1);
+					{
+						ParseNode *pn = makeNode(ParseNode);
+						pn->location = @1;
+						pn->length = 0;
+						pn->stmt = $1;
+						$$ = list_make1(pn);
+					}
 					else
 						$$ = NIL;
 				}
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index e2c884c..f9001a0 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -720,7 +720,7 @@ typeStringToTypeName(const char *str)
 	 */
 	if (list_length(raw_parsetree_list) != 1)
 		goto fail;
-	stmt = (SelectStmt *) linitial(raw_parsetree_list);
+	stmt = (SelectStmt *) stripParseNode(linitial(raw_parsetree_list));
 	if (stmt == NULL ||
 		!IsA(stmt, SelectStmt) ||
 		stmt->distinctClause != NIL ||
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 05b2e57..1e5b50f 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -881,7 +881,6 @@ exec_simple_query(const char *query_string)
 	bool		isTopLevel;
 	char		msec_str[32];
 
-
 	/*
 	 * Report query to various monitoring facilities.
 	 */
@@ -955,7 +954,8 @@ exec_simple_query(const char *query_string)
 	 */
 	foreach(parsetree_item, parsetree_list)
 	{
-		Node	   *parsetree = (Node *) lfirst(parsetree_item);
+		ParseNode  *raw_parsetree = (ParseNode *) lfirst(parsetree_item);
+		Node	   *parsetree = stripParseNode(raw_parsetree);
 		bool		snapshot_set = false;
 		const char *commandTag;
 		char		completionTag[COMPLETION_TAG_BUFSIZE];
@@ -964,7 +964,16 @@ exec_simple_query(const char *query_string)
 		Portal		portal;
 		DestReceiver *receiver;
 		int16		format;
+		char	   *current_query_string = (char *)query_string;
 
+		if (list_length(parsetree_list) > 1)
+		{
+			current_query_string = palloc(raw_parsetree->length + 1);
+			memcpy(current_query_string,
+				   query_string + raw_parsetree->location,
+				   raw_parsetree->length + 1);
+			current_query_string[raw_parsetree->length] = 0;
+		}
 		/*
 		 * Get the command name for use in status display (it also becomes the
 		 * default completion tag, down inside PortalRun).  Set ps_status and
@@ -1016,7 +1025,8 @@ exec_simple_query(const char *query_string)
 		 */
 		oldcontext = MemoryContextSwitchTo(MessageContext);
 
-		querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
+		querytree_list = pg_analyze_and_rewrite(parsetree,
+												current_query_string,
 												NULL, 0);
 
 		plantree_list = pg_plan_queries(querytree_list,
@@ -1044,7 +1054,7 @@ exec_simple_query(const char *query_string)
 		 */
 		PortalDefineQuery(portal,
 						  NULL,
-						  query_string,
+						  current_query_string,
 						  commandTag,
 						  plantree_list,
 						  NULL);
@@ -1279,7 +1289,7 @@ exec_parse_message(const char *query_string,	/* string to execute */
 		bool		snapshot_set = false;
 		int			i;
 
-		raw_parse_tree = (Node *) linitial(parsetree_list);
+		raw_parse_tree = (Node *) stripParseNode(linitial(parsetree_list));
 
 		/*
 		 * Get the command name for possible use in status display.
@@ -2064,7 +2074,7 @@ check_log_statement(List *stmt_list)
 	/* Else we have to inspect the statement(s) to see whether to log */
 	foreach(stmt_item, stmt_list)
 	{
-		Node	   *stmt = (Node *) lfirst(stmt_item);
+		Node	   *stmt = stripParseNode(lfirst(stmt_item));
 
 		if (GetCommandLogLevel(stmt) <= log_statement)
 			return true;
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 3b5da73..9ddfc55 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -31,6 +31,7 @@
  * if there are several).
  */
 Portal		ActivePortal = NULL;
+char *portal_query_string = NULL;
 
 
 static void ProcessQuery(PlannedStmt *plan,
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index a1bb0ac..928da44 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -302,6 +302,7 @@ typedef enum NodeTag
 	 * TAGS FOR STATEMENT NODES (mostly in parsenodes.h)
 	 */
 	T_Query,
+	T_ParseNode,
 	T_PlannedStmt,
 	T_InsertStmt,
 	T_DeleteStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 7ceaa22..23bbfc8 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -78,6 +78,26 @@ typedef uint32 AclMode;			/* a bitmask of privilege bits */
 /* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
 #define ACL_SELECT_FOR_UPDATE	ACL_UPDATE
 
+/*
+ * A ParseNode is a Node with additional location information.
+ * Zero qlengh means not set.
+ * If non-zero, then location is within to the initial query string.
+ */
+typedef struct ParseNode
+{
+	NodeTag		type;
+	int			location;
+	int			length;
+	Node		*stmt;
+} ParseNode;
+
+/*
+ * All high-level statements coming out of the parser are ParseNode,
+ * plus Query & PlannedStmt.
+ */
+#define isParseNodeTag(tag)	((T_Query <= (tag)) && ((tag) < T_A_Expr))
+#define isParseNode(nodeptr) IsA((nodeptr), ParseNode)
+#define	stripParseNode(nodeptr) (isParseNode(nodeptr)? ((ParseNode *)(nodeptr))->stmt : (Node *) (nodeptr))
 
 /*****************************************************************************
  *	Query Tree
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 692a626..921d04c 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -36,6 +36,8 @@
 typedef struct PlannedStmt
 {
 	NodeTag		type;
+	int			location;		/* query location */
+	int			length;			/* query length, 0 if unset */
 
 	CmdType		commandType;	/* select|insert|update|delete */
 
-- 
2.9.2

