Yes. I'll try to put together a patch and submit it to the next CF.

Here it is. I'll add this to the next CF.

Oops... better without a stupid overflow. Shame on me!

--
Fabien.
diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out
index 3573c19..5a1b227 100644
--- a/contrib/pg_stat_statements/expected/pg_stat_statements.out
+++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out
@@ -1,21 +1,346 @@
 CREATE EXTENSION pg_stat_statements;
-CREATE TABLE test (a int, b char(20));
--- test the basic functionality of pg_stat_statements
+--
+--
+-- simple and compound statements
+--
+SET pg_stat_statements.track_utility = FALSE;
 SELECT pg_stat_statements_reset();
  pg_stat_statements_reset 
 --------------------------
  
 (1 row)
 
+SELECT 1 AS "int";
+ int 
+-----
+   1
+(1 row)
+
+SELECT 'hello'
+  -- multiline
+  AS "text";
+ text  
+-------
+ hello
+(1 row)
+
+SELECT 'world' AS "text";
+ text  
+-------
+ world
+(1 row)
+
+-- transaction
+BEGIN;
+SELECT 1 AS "int";
+ int 
+-----
+   1
+(1 row)
+
+SELECT 'hello' AS "text";
+ text  
+-------
+ hello
+(1 row)
+
+COMMIT;
+-- compound transaction
+BEGIN \;
+SELECT 2.0 AS "float" \;
+SELECT 'world' AS "text" \;
+COMMIT;
+-- compound with empty statements and spurious leading spacing
+\;\;   SELECT 3 + 3 \;\;\;   SELECT ' ' || ' !' \;\;   SELECT 1 + 4 \;;
+ ?column? 
+----------
+        5
+(1 row)
+
+-- non ;-terminated statements
+SELECT 1 + 1 + 1 AS "add" \gset
+SELECT :add + 1 + 1 AS "add" \;
+SELECT :add + 1 + 1 AS "add" \gset
+-- set operator
+SELECT 1 AS i UNION SELECT 2 ORDER BY i;
+ i 
+---
+ 1
+ 2
+(2 rows)
+
+-- cte
+WITH t(f) AS (
+  VALUES (1.0), (2.0)
+)
+  SELECT f FROM t ORDER BY f;
+  f  
+-----
+ 1.0
+ 2.0
+(2 rows)
+
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+                  query                  | calls | rows 
+-----------------------------------------+-------+------
+ SELECT ? || ?                           |     1 |    1
+ SELECT ? AS "float"                     |     1 |    1
+ SELECT pg_stat_statements_reset()       |     1 |    1
+ SELECT ? + ?                            |     2 |    2
+ SELECT ? AS "int"                       |     2 |    2
+ SELECT ? AS i UNION SELECT ? ORDER BY i |     1 |    2
+ WITH t(f) AS (                         +|     1 |    2
+   VALUES (?), (?)                      +|       | 
+ )                                      +|       | 
+   SELECT f FROM t ORDER BY f            |       | 
+ SELECT ? + ? + ? AS "add"               |     3 |    3
+ SELECT ?                               +|     4 |    4
+                                        +|       | 
+   AS "text"                             |       | 
+(9 rows)
+
+--
+--
+-- CRUD: INSERT SELECT UPDATE DELETE on test table
+--
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset 
+--------------------------
+ 
+(1 row)
+
+-- utility "create table" must not show
+CREATE TABLE test (a int, b char(20));
 INSERT INTO test VALUES(generate_series(1, 10), 'aaa');
-UPDATE test SET b = 'bbb' WHERE a > 5;
-SELECT query, calls, rows from pg_stat_statements ORDER BY rows;
-                       query                        | calls | rows 
-----------------------------------------------------+-------+------
- SELECT pg_stat_statements_reset();                 |     1 |    1
- UPDATE test SET b = ? WHERE a > ?;                 |     1 |    5
- INSERT INTO test VALUES(generate_series(?, ?), ?); |     1 |   10
+UPDATE test SET b = 'bbb' WHERE a > 7;
+DELETE FROM test WHERE a > 9;
+-- explicit transaction
+BEGIN;
+UPDATE test SET b = '111' WHERE a = 1 ;
+COMMIT;
+BEGIN \;
+UPDATE test SET b = '222' WHERE a = 2 \;
+COMMIT ;
+UPDATE test SET b = '333' WHERE a = 3 \;
+UPDATE test SET b = '444' WHERE a = 4 ;
+BEGIN \;
+UPDATE test SET b = '555' WHERE a = 5 \;
+UPDATE test SET b = '666' WHERE a = 6 \;
+COMMIT ;
+-- SELECT with constants
+SELECT * FROM test WHERE a > 5 ORDER BY a ;
+ a |          b           
+---+----------------------
+ 6 | 666                 
+ 7 | aaa                 
+ 8 | bbb                 
+ 9 | bbb                 
+(4 rows)
+
+SELECT *
+  FROM test
+  WHERE a > 9
+  ORDER BY a ;
+ a | b 
+---+---
+(0 rows)
+
+-- SELECT without constants
+SELECT * FROM test ORDER BY a;
+ a |          b           
+---+----------------------
+ 1 | 111                 
+ 2 | 222                 
+ 3 | 333                 
+ 4 | 444                 
+ 5 | 555                 
+ 6 | 666                 
+ 7 | aaa                 
+ 8 | bbb                 
+ 9 | bbb                 
+(9 rows)
+
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+                       query                       | calls | rows 
+---------------------------------------------------+-------+------
+ DELETE FROM test WHERE a > ?                      |     1 |    1
+ SELECT pg_stat_statements_reset()                 |     1 |    1
+ UPDATE test SET b = ? WHERE a > ?                 |     1 |    3
+ SELECT * FROM test WHERE a > ? ORDER BY a         |     2 |    4
+ UPDATE test SET b = ? WHERE a = ?                 |     6 |    6
+ SELECT * FROM test ORDER BY a                     |     1 |    9
+ INSERT INTO test VALUES(generate_series(?, ?), ?) |     1 |   10
+(7 rows)
+
+--
+--
+-- pg_stat_statements.track = none
+--
+SET pg_stat_statements.track = 'none';
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset 
+--------------------------
+ 
+(1 row)
+
+SELECT 1 AS "one";
+ one 
+-----
+   1
+(1 row)
+
+SELECT 1 + 1 AS "two";
+ two 
+-----
+   2
+(1 row)
+
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+ query | calls | rows 
+-------+-------+------
+(0 rows)
+
+--
+--
+-- pg_stat_statements.track = top
+--
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset 
+--------------------------
+ 
+(1 row)
+
+SET pg_stat_statements.track = 'top';
+DO LANGUAGE plpgsql $$
+BEGIN
+  -- this is a SELECT
+  PERFORM 'hello world'::TEXT;
+END;
+$$;
+-- PL/pgSQL function
+CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
+DECLARE
+  r INTEGER;
+BEGIN
+  SELECT (i + 1 + 1.0)::INTEGER INTO r;
+  RETURN r;
+END; $$ LANGUAGE plpgsql;
+SELECT PLUS_TWO(3);
+ plus_two 
+----------
+        5
+(1 row)
+
+SELECT PLUS_TWO(7);
+ plus_two 
+----------
+        9
+(1 row)
+
+-- SQL function
+CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS
+$$ SELECT (i + 1.0)::INTEGER $$ LANGUAGE SQL;
+SELECT PLUS_ONE(8);
+ plus_one 
+----------
+        9
+(1 row)
+
+SELECT PLUS_ONE(10);
+ plus_one 
+----------
+       11
+(1 row)
+
+DROP FUNCTION PLUS_ONE(INTEGER);
+DROP FUNCTION PLUS_TWO(INTEGER);
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+       query        | calls | rows 
+--------------------+-------+------
+ SELECT ?::TEXT     |     1 |    1
+ SELECT PLUS_ONE(?) |     2 |    2
+ SELECT PLUS_TWO(?) |     2 |    2
 (3 rows)
 
+--
+--
+-- pg_stat_statements.track = all
+--
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset 
+--------------------------
+ 
+(1 row)
+
+SET pg_stat_statements.track = 'all';
+-- recreate PL/pgSQL function
+CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
+DECLARE
+  r INTEGER;
+BEGIN
+  SELECT (i + 1 + 1.0)::INTEGER INTO r;
+  RETURN r;
+END; $$ LANGUAGE plpgsql;
+SELECT PLUS_TWO(-1);
+ plus_two 
+----------
+        1
+(1 row)
+
+SELECT PLUS_TWO(2);
+ plus_two 
+----------
+        4
+(1 row)
+
+-- SQL function nesting
+CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS
+$$ SELECT (i + 1.0)::INTEGER $$ LANGUAGE SQL;
+SELECT PLUS_ONE(3);
+ plus_one 
+----------
+        4
+(1 row)
+
+SELECT PLUS_ONE(1);
+ plus_one 
+----------
+        2
+(1 row)
+
+-- bug? PLUS_ONE expansion is missing
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+               query               | calls | rows 
+-----------------------------------+-------+------
+ SELECT pg_stat_statements_reset() |     1 |    1
+ SELECT (i + ? + ?)::INTEGER       |     2 |    2
+ SELECT PLUS_ONE(?)                |     2 |    2
+ SELECT PLUS_TWO(?)                |     2 |    2
+(4 rows)
+
+--
+--
+-- utility commands
+--
+SELECT pg_stat_statements_reset();
+ pg_stat_statements_reset 
+--------------------------
+ 
+(1 row)
+
+SET pg_stat_statements.track_utility = TRUE;
+CREATE INDEX test_b ON test(b);
 DROP TABLE test;
+DROP FUNCTION PLUS_ONE(INTEGER);
+DROP FUNCTION PLUS_TWO(INTEGER);
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+               query               | calls | rows 
+-----------------------------------+-------+------
+ CREATE INDEX test_b ON test(b)    |     1 |    0
+ DROP FUNCTION PLUS_ONE(INTEGER)   |     1 |    0
+ DROP FUNCTION PLUS_TWO(INTEGER)   |     1 |    0
+ DROP TABLE test                   |     1 |    0
+ SELECT pg_stat_statements_reset() |     1 |    1
+(5 rows)
+
 DROP EXTENSION pg_stat_statements;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 8ce24e0..d265de9 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -298,10 +298,9 @@ static void pgss_ProcessUtility(Node *parsetree, const char *queryString,
 static uint32 pgss_hash_fn(const void *key, Size keysize);
 static int	pgss_match_fn(const void *key1, const void *key2, Size keysize);
 static uint32 pgss_hash_string(const char *str);
-static void pgss_store(const char *query, uint32 queryId,
-		   double total_time, uint64 rows,
-		   const BufferUsage *bufusage,
-		   pgssJumbleState *jstate);
+static void pgss_store(const char *query, int query_loc, int query_len,
+			   uint32 queryId, double total_time, uint64 rows,
+			   const BufferUsage *bufusage, pgssJumbleState *jstate);
 static void pg_stat_statements_internal(FunctionCallInfo fcinfo,
 							pgssVersion api_version,
 							bool showtext);
@@ -324,7 +323,7 @@ static void JumbleRangeTable(pgssJumbleState *jstate, List *rtable);
 static void JumbleExpr(pgssJumbleState *jstate, Node *node);
 static void RecordConstLocation(pgssJumbleState *jstate, int location);
 static char *generate_normalized_query(pgssJumbleState *jstate, const char *query,
-						  int *query_len_p, int encoding);
+							   int query_len, int *query_len_p, int encoding);
 static void fill_in_constant_lengths(pgssJumbleState *jstate, const char *query);
 static int	comp_location(const void *a, const void *b);
 
@@ -820,7 +819,7 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query)
 	 * there's no need for an early entry.
 	 */
 	if (jstate.clocations_count > 0)
-		pgss_store(pstate->p_sourcetext,
+		pgss_store(pstate->p_sourcetext, query->qlocation, query->qlength,
 				   query->queryId,
 				   0,
 				   0,
@@ -925,6 +924,8 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
 		InstrEndLoop(queryDesc->totaltime);
 
 		pgss_store(queryDesc->sourceText,
+				   queryDesc->plannedstmt->qlocation,
+				   queryDesc->plannedstmt->qlength,
 				   queryId,
 				   queryDesc->totaltime->total * 1000.0,		/* convert to msec */
 				   queryDesc->estate->es_processed,
@@ -971,6 +972,9 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString,
 		BufferUsage bufusage_start,
 					bufusage;
 		uint32		queryId;
+		ParseNode  *parsenode = (ParseNode *) parsetree;
+
+		Assert(isParseNode(node));
 
 		bufusage_start = pgBufferUsage;
 		INSTR_TIME_SET_CURRENT(start);
@@ -1034,7 +1038,7 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString,
 		/* For utility statements, we just hash the query string directly */
 		queryId = pgss_hash_string(queryString);
 
-		pgss_store(queryString,
+		pgss_store(queryString, parsenode->qlocation, parsenode->qlength,
 				   queryId,
 				   INSTR_TIME_GET_MILLISEC(duration),
 				   rows,
@@ -1103,7 +1107,7 @@ pgss_hash_string(const char *str)
  * query string.  total_time, rows, bufusage are ignored in this case.
  */
 static void
-pgss_store(const char *query, uint32 queryId,
+pgss_store(const char *query, int query_loc, int query_len, uint32 queryId,
 		   double total_time, uint64 rows,
 		   const BufferUsage *bufusage,
 		   pgssJumbleState *jstate)
@@ -1112,15 +1116,15 @@ pgss_store(const char *query, uint32 queryId,
 	pgssEntry  *entry;
 	char	   *norm_query = NULL;
 	int			encoding = GetDatabaseEncoding();
-	int			query_len;
 
-	Assert(query != NULL);
+	Assert(query != NULL && query_loc >= 0 && query_len >= 0);
 
 	/* Safety check... */
 	if (!pgss || !pgss_hash)
 		return;
 
-	query_len = strlen(query);
+	if (query_len == 0)
+		query_len = strlen(query);
 
 	/* Set up key for hashtable search */
 	key.userid = GetUserId();
@@ -1140,6 +1144,11 @@ pgss_store(const char *query, uint32 queryId,
 		bool		stored;
 		bool		do_gc;
 
+		/* skip leading spaces */
+		while (isspace(query[query_loc]))
+			query_loc++, query_len--;
+		/* should skip trailing spaces? encoding dependence? */
+
 		/*
 		 * Create a new, normalized query string if caller asked.  We don't
 		 * need to hold the lock while doing this work.  (Note: in any case,
@@ -1150,11 +1159,17 @@ pgss_store(const char *query, uint32 queryId,
 		if (jstate)
 		{
 			LWLockRelease(pgss->lock);
-			norm_query = generate_normalized_query(jstate, query,
+			norm_query = generate_normalized_query(jstate, query, query_loc,
 												   &query_len,
 												   encoding);
 			LWLockAcquire(pgss->lock, LW_SHARED);
 		}
+		else if (strlen(query) != query_len)
+		{
+			norm_query = palloc(query_len + 1);
+			memcpy(norm_query, query + query_loc, query_len);
+			norm_query[query_len] = '\0';
+		}
 
 		/* Append new query text to file with only shared lock held */
 		stored = qtext_store(norm_query ? norm_query : query, query_len,
@@ -2882,15 +2897,15 @@ RecordConstLocation(pgssJumbleState *jstate, int location)
  */
 static char *
 generate_normalized_query(pgssJumbleState *jstate, const char *query,
-						  int *query_len_p, int encoding)
+						  int query_loc, int *query_len_p, int encoding)
 {
 	char	   *norm_query;
 	int			query_len = *query_len_p;
 	int			i,
 				len_to_wrt,		/* Length (in bytes) to write */
-				quer_loc = 0,	/* Source query byte location */
+				quer_loc = query_loc, /* Source query byte location */
 				n_quer_loc = 0, /* Normalized query byte location */
-				last_off = 0,	/* Offset from start for previous tok */
+				last_off = query_loc,	/* Offset from start for previous tok */
 				last_tok_len = 0;		/* Length (in bytes) of that tok */
 
 	/*
@@ -2933,7 +2948,7 @@ generate_normalized_query(pgssJumbleState *jstate, const char *query,
 	 * We've copied up until the last ignorable constant.  Copy over the
 	 * remaining bytes of the original query string.
 	 */
-	len_to_wrt = query_len - quer_loc;
+	len_to_wrt = query_loc + query_len - quer_loc;
 
 	Assert(len_to_wrt >= 0);
 	memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
index 7e2b263..5a30436 100644
--- a/contrib/pg_stat_statements/sql/pg_stat_statements.sql
+++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql
@@ -1,15 +1,183 @@
 CREATE EXTENSION pg_stat_statements;
 
-CREATE TABLE test (a int, b char(20));
+--
+--
+-- simple and compound statements
+--
+SET pg_stat_statements.track_utility = FALSE;
+SELECT pg_stat_statements_reset();
+
+SELECT 1 AS "int";
+
+
+SELECT 'hello'
+  -- multiline
+  AS "text";
+SELECT 'world' AS "text";
+
+-- transaction
+BEGIN;
+SELECT 1 AS "int";
+SELECT 'hello' AS "text";
+COMMIT;
+
+-- compound transaction
+BEGIN \;
+SELECT 2.0 AS "float" \;
+SELECT 'world' AS "text" \;
+COMMIT;
+
+-- compound with empty statements and spurious leading spacing
+\;\;   SELECT 3 + 3 \;\;\;   SELECT ' ' || ' !' \;\;   SELECT 1 + 4 \;;
+
+-- non ;-terminated statements
+SELECT 1 + 1 + 1 AS "add" \gset
+SELECT :add + 1 + 1 AS "add" \;
+SELECT :add + 1 + 1 AS "add" \gset
+
+-- set operator
+SELECT 1 AS i UNION SELECT 2 ORDER BY i;
+
+-- cte
+WITH t(f) AS (
+  VALUES (1.0), (2.0)
+)
+  SELECT f FROM t ORDER BY f;
 
--- test the basic functionality of pg_stat_statements
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+
+--
+--
+-- CRUD: INSERT SELECT UPDATE DELETE on test table
+--
 SELECT pg_stat_statements_reset();
 
+-- utility "create table" must not show
+CREATE TABLE test (a int, b char(20));
+
 INSERT INTO test VALUES(generate_series(1, 10), 'aaa');
-UPDATE test SET b = 'bbb' WHERE a > 5;
+UPDATE test SET b = 'bbb' WHERE a > 7;
+DELETE FROM test WHERE a > 9;
+
+-- explicit transaction
+BEGIN;
+UPDATE test SET b = '111' WHERE a = 1 ;
+COMMIT;
+
+BEGIN \;
+UPDATE test SET b = '222' WHERE a = 2 \;
+COMMIT ;
+
+UPDATE test SET b = '333' WHERE a = 3 \;
+UPDATE test SET b = '444' WHERE a = 4 ;
+
+BEGIN \;
+UPDATE test SET b = '555' WHERE a = 5 \;
+UPDATE test SET b = '666' WHERE a = 6 \;
+COMMIT ;
+
+-- SELECT with constants
+SELECT * FROM test WHERE a > 5 ORDER BY a ;
+SELECT *
+  FROM test
+  WHERE a > 9
+  ORDER BY a ;
+
+-- SELECT without constants
+SELECT * FROM test ORDER BY a;
+
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
 
-SELECT query, calls, rows from pg_stat_statements ORDER BY rows;
+--
+--
+-- pg_stat_statements.track = none
+--
+SET pg_stat_statements.track = 'none';
+SELECT pg_stat_statements_reset();
+SELECT 1 AS "one";
+SELECT 1 + 1 AS "two";
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+
+--
+--
+-- pg_stat_statements.track = top
+--
+SELECT pg_stat_statements_reset();
+SET pg_stat_statements.track = 'top';
+
+DO LANGUAGE plpgsql $$
+BEGIN
+  -- this is a SELECT
+  PERFORM 'hello world'::TEXT;
+END;
+$$;
+
+-- PL/pgSQL function
+CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
+DECLARE
+  r INTEGER;
+BEGIN
+  SELECT (i + 1 + 1.0)::INTEGER INTO r;
+  RETURN r;
+END; $$ LANGUAGE plpgsql;
+
+SELECT PLUS_TWO(3);
+SELECT PLUS_TWO(7);
 
+-- SQL function
+CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS
+$$ SELECT (i + 1.0)::INTEGER $$ LANGUAGE SQL;
+
+SELECT PLUS_ONE(8);
+SELECT PLUS_ONE(10);
+
+DROP FUNCTION PLUS_ONE(INTEGER);
+DROP FUNCTION PLUS_TWO(INTEGER);
+
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+
+--
+--
+-- pg_stat_statements.track = all
+--
+SELECT pg_stat_statements_reset();
+SET pg_stat_statements.track = 'all';
+
+-- recreate PL/pgSQL function
+CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$
+DECLARE
+  r INTEGER;
+BEGIN
+  SELECT (i + 1 + 1.0)::INTEGER INTO r;
+  RETURN r;
+END; $$ LANGUAGE plpgsql;
+
+SELECT PLUS_TWO(-1);
+SELECT PLUS_TWO(2);
+
+-- SQL function nesting
+CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS
+$$ SELECT (i + 1.0)::INTEGER $$ LANGUAGE SQL;
+
+SELECT PLUS_ONE(3);
+SELECT PLUS_ONE(1);
+
+-- bug? PLUS_ONE expansion is missing
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+
+--
+--
+-- utility commands
+--
+SELECT pg_stat_statements_reset();
+SET pg_stat_statements.track_utility = TRUE;
+
+CREATE INDEX test_b ON test(b);
 DROP TABLE test;
+DROP FUNCTION PLUS_ONE(INTEGER);
+DROP FUNCTION PLUS_TWO(INTEGER);
+
+SELECT query, calls, rows FROM pg_stat_statements ORDER BY rows, query;
+
 
 DROP EXTENSION pg_stat_statements;
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 41dde50..e652feb 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -177,6 +177,11 @@ planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 		result = (*planner_hook) (parse, cursorOptions, boundParams);
 	else
 		result = standard_planner(parse, cursorOptions, boundParams);
+
+	/* copy query location to planned statement */
+	result->qlocation = parse->qlocation;
+	result->qlength = parse->qlength;
+
 	return result;
 }
 
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index a558083..bdff79c 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -307,6 +307,11 @@ transformStmt(ParseState *pstate, Node *parseTree)
 	result->querySource = QSRC_ORIGINAL;
 	result->canSetTag = true;
 
+	/* keep track of location & length */
+	Assert(isParseNode(parseTree));
+	result->qlocation = ((ParseNode*) parseTree)->qlocation;
+	result->qlength = ((ParseNode*) parseTree)->qlength;
+
 	return result;
 }
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 834a009..b0657f4 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -178,6 +178,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
 			   bool *deferrable, bool *initdeferred, bool *not_valid,
 			   bool *no_inherit, core_yyscan_t yyscanner);
 static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
+static void setQueryLocation(Node *node, core_yyscan_t yyscanner, int start);
 
 %}
 
@@ -761,14 +762,20 @@ stmtblock:	stmtmulti
 stmtmulti:	stmtmulti ';' stmt
 				{
 					if ($3 != NULL)
+					{
+						setQueryLocation($3, yyscanner, @2+1);
 						$$ = lappend($1, $3);
+					}
 					else
 						$$ = $1;
 				}
 			| stmt
 				{
 					if ($1 != NULL)
+					{
+						setQueryLocation($1, yyscanner, 0);
 						$$ = list_make1($1);
+					}
 					else
 						$$ = NIL;
 				}
@@ -15283,6 +15290,23 @@ makeRecursiveViewSelect(char *relname, List *aliases, Node *query)
 	return (Node *) s;
 }
 
+/* set qlocation & qlength for statements starting at "start" */
+static void
+setQueryLocation(Node *node, core_yyscan_t yyscanner, int start)
+{
+	base_yy_extra_type *extra = pg_yyget_extra(yyscanner);
+	ParseNode *pn = (ParseNode *) node;
+	Assert(isParseNode(node));
+
+	pn->qlocation = start;
+	if (extra->last_semicolon == -1 || start == (extra->last_semicolon + 1))
+		/* reduction triggered by end of input */
+		pn->qlength = strlen(extra->core_yy_extra.scanbuf) - start;
+	else
+		/* reduction triggered by ';' */
+		pn->qlength = extra->last_semicolon - start;
+}
+
 /* parser_init()
  * Initialize to parse one query string
  */
@@ -15290,4 +15314,5 @@ void
 parser_init(base_yy_extra_type *yyext)
 {
 	yyext->parsetree = NIL;		/* in case grammar forgets to set it */
+	yyext->last_semicolon = -1;
 }
diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l
index acd9269..470a697 100644
--- a/src/backend/parser/scan.l
+++ b/src/backend/parser/scan.l
@@ -351,7 +351,8 @@ not_equals		"!="
  * If you change either set, adjust the character lists appearing in the
  * rule for "operator"!
  */
-self			[,()\[\].;\:\+\-\*\/\%\^\<\>\=]
+semicolon		;
+self			[,()\[\].\:\+\-\*\/\%\^\<\>\=]
 op_chars		[\~\!\@\#\^\&\|\`\?\+\-\*\/\%\<\>\=]
 operator		{op_chars}+
 
@@ -845,7 +846,11 @@ other			.
 					SET_YYLLOC();
 					return NOT_EQUALS;
 				}
-
+{semicolon}		{
+					SET_YYLLOC();
+					pg_yyget_extra(yyscanner)->last_semicolon = *yylloc;
+					return yytext[0];
+				}
 {self}			{
 					SET_YYLLOC();
 					return yytext[0];
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index c514d3f..1a5e68d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -308,7 +308,6 @@ typedef enum NodeTag
 	T_UpdateStmt,
 	T_SelectStmt,
 	T_AlterTableStmt,
-	T_AlterTableCmd,
 	T_AlterDomainStmt,
 	T_SetOperationStmt,
 	T_GrantStmt,
@@ -406,7 +405,6 @@ typedef enum NodeTag
 	T_AlterPolicyStmt,
 	T_CreateTransformStmt,
 	T_CreateAmStmt,
-	T_PartitionCmd,
 
 	/*
 	 * TAGS FOR PARSE TREE NODES (parsenodes.h)
@@ -459,6 +457,8 @@ typedef enum NodeTag
 	T_PartitionSpec,
 	T_PartitionBoundSpec,
 	T_PartitionRangeDatum,
+	T_AlterTableCmd,
+	T_PartitionCmd,
 
 	/*
 	 * TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index fc532fb..704694c 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -78,6 +78,24 @@ 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 qlocation is within to the initial query string.
+ */
+typedef struct ParseNode
+{
+	NodeTag		type;
+	int			qlocation;
+	int			qlength;
+} 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) isParseNodeTag(nodeTag(nodeptr))
 
 /*****************************************************************************
  *	Query Tree
@@ -99,6 +117,8 @@ typedef uint32 AclMode;			/* a bitmask of privilege bits */
 typedef struct Query
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 
 	CmdType		commandType;	/* select|insert|update|delete|utility */
 
@@ -1322,6 +1342,8 @@ typedef struct TriggerTransition
 typedef struct InsertStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	RangeVar   *relation;		/* relation to insert into */
 	List	   *cols;			/* optional: names of the target columns */
 	Node	   *selectStmt;		/* the source SELECT/VALUES, or NULL */
@@ -1337,6 +1359,8 @@ typedef struct InsertStmt
 typedef struct DeleteStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	RangeVar   *relation;		/* relation to delete from */
 	List	   *usingClause;	/* optional using clause for more tables */
 	Node	   *whereClause;	/* qualifications */
@@ -1351,6 +1375,8 @@ typedef struct DeleteStmt
 typedef struct UpdateStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	RangeVar   *relation;		/* relation to update */
 	List	   *targetList;		/* the target list (of ResTarget) */
 	Node	   *whereClause;	/* qualifications */
@@ -1383,6 +1409,8 @@ typedef enum SetOperation
 typedef struct SelectStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 
 	/*
 	 * These fields are used only in "leaf" SelectStmts.
@@ -1450,6 +1478,8 @@ typedef struct SelectStmt
 typedef struct SetOperationStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	SetOperation op;			/* type of set op */
 	bool		all;			/* ALL specified? */
 	Node	   *larg;			/* left child */
@@ -1541,6 +1571,8 @@ typedef enum ObjectType
 typedef struct CreateSchemaStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *schemaname;		/* the name of the schema to create */
 	Node	   *authrole;		/* the owner of the created schema */
 	List	   *schemaElts;		/* schema components (list of parsenodes) */
@@ -1560,6 +1592,8 @@ typedef enum DropBehavior
 typedef struct AlterTableStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	RangeVar   *relation;		/* table to work on */
 	List	   *cmds;			/* list of subcommands */
 	ObjectType	relkind;		/* type of object */
@@ -1637,6 +1671,8 @@ typedef enum AlterTableType
 typedef struct ReplicaIdentityStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char		identity_type;
 	char	   *name;
 } ReplicaIdentityStmt;
@@ -1665,6 +1701,8 @@ typedef struct AlterTableCmd	/* one subcommand of an ALTER TABLE */
 typedef struct AlterDomainStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char		subtype;		/*------------
 								 *	T = alter column default
 								 *	N = alter column drop not null
@@ -1712,6 +1750,8 @@ typedef enum GrantObjectType
 typedef struct GrantStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	bool		is_grant;		/* true = GRANT, false = REVOKE */
 	GrantTargetType targtype;	/* type of the grant target */
 	GrantObjectType objtype;	/* kind of object being operated on */
@@ -1762,6 +1802,8 @@ typedef struct AccessPriv
 typedef struct GrantRoleStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *granted_roles;	/* list of roles to be granted/revoked */
 	List	   *grantee_roles;	/* list of member roles to add/delete */
 	bool		is_grant;		/* true = GRANT, false = REVOKE */
@@ -1777,6 +1819,8 @@ typedef struct GrantRoleStmt
 typedef struct AlterDefaultPrivilegesStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *options;		/* list of DefElem */
 	GrantStmt  *action;			/* GRANT/REVOKE action (with objects=NIL) */
 } AlterDefaultPrivilegesStmt;
@@ -1792,6 +1836,8 @@ typedef struct AlterDefaultPrivilegesStmt
 typedef struct CopyStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	RangeVar   *relation;		/* the relation to copy */
 	Node	   *query;			/* the query (SELECT or DML statement with
 								 * RETURNING) to copy */
@@ -1823,6 +1869,8 @@ typedef enum
 typedef struct VariableSetStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	VariableSetKind kind;
 	char	   *name;			/* variable to be set */
 	List	   *args;			/* List of A_Const nodes */
@@ -1836,6 +1884,8 @@ typedef struct VariableSetStmt
 typedef struct VariableShowStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *name;
 } VariableShowStmt;
 
@@ -1853,6 +1903,8 @@ typedef struct VariableShowStmt
 typedef struct CreateStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	RangeVar   *relation;		/* relation to create */
 	List	   *tableElts;		/* column definitions (list of ColumnDef) */
 	List	   *inhRelations;	/* relations to inherit from (list of
@@ -1980,6 +2032,8 @@ typedef struct Constraint
 typedef struct CreateTableSpaceStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *tablespacename;
 	Node	   *owner;
 	char	   *location;
@@ -1989,6 +2043,8 @@ typedef struct CreateTableSpaceStmt
 typedef struct DropTableSpaceStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *tablespacename;
 	bool		missing_ok;		/* skip error if missing? */
 } DropTableSpaceStmt;
@@ -1996,6 +2052,8 @@ typedef struct DropTableSpaceStmt
 typedef struct AlterTableSpaceOptionsStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *tablespacename;
 	List	   *options;
 	bool		isReset;
@@ -2004,6 +2062,8 @@ typedef struct AlterTableSpaceOptionsStmt
 typedef struct AlterTableMoveAllStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *orig_tablespacename;
 	ObjectType	objtype;		/* Object type to move */
 	List	   *roles;			/* List of roles to move objects of */
@@ -2019,6 +2079,8 @@ typedef struct AlterTableMoveAllStmt
 typedef struct CreateExtensionStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *extname;
 	bool		if_not_exists;	/* just do nothing if it already exists? */
 	List	   *options;		/* List of DefElem nodes */
@@ -2028,6 +2090,8 @@ typedef struct CreateExtensionStmt
 typedef struct AlterExtensionStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *extname;
 	List	   *options;		/* List of DefElem nodes */
 } AlterExtensionStmt;
@@ -2035,6 +2099,8 @@ typedef struct AlterExtensionStmt
 typedef struct AlterExtensionContentsStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *extname;		/* Extension's name */
 	int			action;			/* +1 = add object, -1 = drop object */
 	ObjectType	objtype;		/* Object's type */
@@ -2050,6 +2116,8 @@ typedef struct AlterExtensionContentsStmt
 typedef struct CreateFdwStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *fdwname;		/* foreign-data wrapper name */
 	List	   *func_options;	/* HANDLER/VALIDATOR options */
 	List	   *options;		/* generic options to FDW */
@@ -2058,6 +2126,8 @@ typedef struct CreateFdwStmt
 typedef struct AlterFdwStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *fdwname;		/* foreign-data wrapper name */
 	List	   *func_options;	/* HANDLER/VALIDATOR options */
 	List	   *options;		/* generic options to FDW */
@@ -2071,6 +2141,8 @@ typedef struct AlterFdwStmt
 typedef struct CreateForeignServerStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *servername;		/* server name */
 	char	   *servertype;		/* optional server type */
 	char	   *version;		/* optional server version */
@@ -2081,6 +2153,8 @@ typedef struct CreateForeignServerStmt
 typedef struct AlterForeignServerStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *servername;		/* server name */
 	char	   *version;		/* optional server version */
 	List	   *options;		/* generic options to server */
@@ -2094,7 +2168,7 @@ typedef struct AlterForeignServerStmt
 
 typedef struct CreateForeignTableStmt
 {
-	CreateStmt	base;
+	CreateStmt	base;			/* is a ParseNode */
 	char	   *servername;
 	List	   *options;
 } CreateForeignTableStmt;
@@ -2107,6 +2181,8 @@ typedef struct CreateForeignTableStmt
 typedef struct CreateUserMappingStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	Node	   *user;			/* user role */
 	char	   *servername;		/* server name */
 	List	   *options;		/* generic options to server */
@@ -2115,6 +2191,8 @@ typedef struct CreateUserMappingStmt
 typedef struct AlterUserMappingStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	Node	   *user;			/* user role */
 	char	   *servername;		/* server name */
 	List	   *options;		/* generic options to server */
@@ -2123,6 +2201,8 @@ typedef struct AlterUserMappingStmt
 typedef struct DropUserMappingStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	Node	   *user;			/* user role */
 	char	   *servername;		/* server name */
 	bool		missing_ok;		/* ignore missing mappings */
@@ -2143,6 +2223,8 @@ typedef enum ImportForeignSchemaType
 typedef struct ImportForeignSchemaStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *server_name;	/* FDW server name */
 	char	   *remote_schema;	/* remote schema name to query */
 	char	   *local_schema;	/* local schema to create objects in */
@@ -2158,6 +2240,8 @@ typedef struct ImportForeignSchemaStmt
 typedef struct CreatePolicyStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *policy_name;	/* Policy's name */
 	RangeVar   *table;			/* the table name the policy applies to */
 	char	   *cmd_name;		/* the command name the policy applies to */
@@ -2174,6 +2258,8 @@ typedef struct CreatePolicyStmt
 typedef struct AlterPolicyStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *policy_name;	/* Policy's name */
 	RangeVar   *table;			/* the table name the policy applies to */
 	List	   *roles;			/* the roles associated with the policy */
@@ -2188,6 +2274,8 @@ typedef struct AlterPolicyStmt
 typedef struct CreateAmStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *amname;			/* access method name */
 	List	   *handler_name;	/* handler function name */
 	char		amtype;			/* type of access method */
@@ -2200,6 +2288,8 @@ typedef struct CreateAmStmt
 typedef struct CreateTrigStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *trigname;		/* TRIGGER's name */
 	RangeVar   *relation;		/* relation trigger is on */
 	List	   *funcname;		/* qual. name of function to call */
@@ -2227,6 +2317,8 @@ typedef struct CreateTrigStmt
 typedef struct CreateEventTrigStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *trigname;		/* TRIGGER's name */
 	char	   *eventname;		/* event's identifier */
 	List	   *whenclause;		/* list of DefElems indicating filtering */
@@ -2240,6 +2332,8 @@ typedef struct CreateEventTrigStmt
 typedef struct AlterEventTrigStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *trigname;		/* TRIGGER's name */
 	char		tgenabled;		/* trigger's firing configuration WRT
 								 * session_replication_role */
@@ -2253,6 +2347,8 @@ typedef struct AlterEventTrigStmt
 typedef struct CreatePLangStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	bool		replace;		/* T => replace if already exists */
 	char	   *plname;			/* PL name */
 	List	   *plhandler;		/* PL call handler function (qual. name) */
@@ -2280,6 +2376,8 @@ typedef enum RoleStmtType
 typedef struct CreateRoleStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	RoleStmtType stmt_type;		/* ROLE/USER/GROUP */
 	char	   *role;			/* role name */
 	List	   *options;		/* List of DefElem nodes */
@@ -2288,6 +2386,8 @@ typedef struct CreateRoleStmt
 typedef struct AlterRoleStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	Node	   *role;			/* role */
 	List	   *options;		/* List of DefElem nodes */
 	int			action;			/* +1 = add members, -1 = drop members */
@@ -2296,6 +2396,8 @@ typedef struct AlterRoleStmt
 typedef struct AlterRoleSetStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	Node	   *role;			/* role */
 	char	   *database;		/* database name, or NULL */
 	VariableSetStmt *setstmt;	/* SET or RESET subcommand */
@@ -2304,6 +2406,8 @@ typedef struct AlterRoleSetStmt
 typedef struct DropRoleStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *roles;			/* List of roles to remove */
 	bool		missing_ok;		/* skip error if a role is missing? */
 } DropRoleStmt;
@@ -2316,6 +2420,8 @@ typedef struct DropRoleStmt
 typedef struct CreateSeqStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	RangeVar   *sequence;		/* the sequence to create */
 	List	   *options;
 	Oid			ownerId;		/* ID of owner, or InvalidOid for default */
@@ -2325,6 +2431,8 @@ typedef struct CreateSeqStmt
 typedef struct AlterSeqStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	RangeVar   *sequence;		/* the sequence to alter */
 	List	   *options;
 	bool		missing_ok;		/* skip error if a role is missing? */
@@ -2337,6 +2445,8 @@ typedef struct AlterSeqStmt
 typedef struct DefineStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	ObjectType	kind;			/* aggregate, operator, type */
 	bool		oldstyle;		/* hack to signal old CREATE AGG syntax */
 	List	   *defnames;		/* qualified name (list of Value strings) */
@@ -2351,6 +2461,8 @@ typedef struct DefineStmt
 typedef struct CreateDomainStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *domainname;		/* qualified name (list of Value strings) */
 	TypeName   *typeName;		/* the base type */
 	CollateClause *collClause;	/* untransformed COLLATE spec, if any */
@@ -2364,6 +2476,8 @@ typedef struct CreateDomainStmt
 typedef struct CreateOpClassStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *opclassname;	/* qualified name (list of Value strings) */
 	List	   *opfamilyname;	/* qualified name (ditto); NIL if omitted */
 	char	   *amname;			/* name of index AM opclass is for */
@@ -2397,6 +2511,8 @@ typedef struct CreateOpClassItem
 typedef struct CreateOpFamilyStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *opfamilyname;	/* qualified name (list of Value strings) */
 	char	   *amname;			/* name of index AM opfamily is for */
 } CreateOpFamilyStmt;
@@ -2408,6 +2524,8 @@ typedef struct CreateOpFamilyStmt
 typedef struct AlterOpFamilyStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *opfamilyname;	/* qualified name (list of Value strings) */
 	char	   *amname;			/* name of index AM opfamily is for */
 	bool		isDrop;			/* ADD or DROP the items? */
@@ -2422,6 +2540,8 @@ typedef struct AlterOpFamilyStmt
 typedef struct DropStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *objects;		/* list of sublists of names (as Values) */
 	List	   *arguments;		/* list of sublists of arguments (as Values) */
 	ObjectType	removeType;		/* object type */
@@ -2437,6 +2557,8 @@ typedef struct DropStmt
 typedef struct TruncateStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *relations;		/* relations (RangeVars) to be truncated */
 	bool		restart_seqs;	/* restart owned sequences? */
 	DropBehavior behavior;		/* RESTRICT or CASCADE behavior */
@@ -2449,6 +2571,8 @@ typedef struct TruncateStmt
 typedef struct CommentStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	ObjectType	objtype;		/* Object's type */
 	List	   *objname;		/* Qualified name of the object */
 	List	   *objargs;		/* Arguments if needed (eg, for functions) */
@@ -2462,6 +2586,8 @@ typedef struct CommentStmt
 typedef struct SecLabelStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	ObjectType	objtype;		/* Object's type */
 	List	   *objname;		/* Qualified name of the object */
 	List	   *objargs;		/* Arguments if needed (eg, for functions) */
@@ -2491,6 +2617,8 @@ typedef struct SecLabelStmt
 typedef struct DeclareCursorStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *portalname;		/* name of the portal (cursor) */
 	int			options;		/* bitmask of options (see above) */
 	Node	   *query;			/* the raw SELECT query */
@@ -2503,6 +2631,8 @@ typedef struct DeclareCursorStmt
 typedef struct ClosePortalStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *portalname;		/* name of the portal (cursor) */
 	/* NULL means CLOSE ALL */
 } ClosePortalStmt;
@@ -2526,6 +2656,8 @@ typedef enum FetchDirection
 typedef struct FetchStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	FetchDirection direction;	/* see above */
 	long		howMany;		/* number of rows, or position argument */
 	char	   *portalname;		/* name of portal (cursor) */
@@ -2546,6 +2678,8 @@ typedef struct FetchStmt
 typedef struct IndexStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *idxname;		/* name of new index, or NULL for default */
 	RangeVar   *relation;		/* relation to build index on */
 	char	   *accessMethod;	/* name of access method (eg. btree) */
@@ -2574,6 +2708,8 @@ typedef struct IndexStmt
 typedef struct CreateFunctionStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	bool		replace;		/* T => replace if already exists */
 	List	   *funcname;		/* qualified name of function to create */
 	List	   *parameters;		/* a list of FunctionParameter */
@@ -2604,6 +2740,8 @@ typedef struct FunctionParameter
 typedef struct AlterFunctionStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	FuncWithArgs *func;			/* name and args of function */
 	List	   *actions;		/* list of DefElem */
 } AlterFunctionStmt;
@@ -2617,6 +2755,8 @@ typedef struct AlterFunctionStmt
 typedef struct DoStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *args;			/* List of DefElem nodes */
 } DoStmt;
 
@@ -2635,6 +2775,8 @@ typedef struct InlineCodeBlock
 typedef struct RenameStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	ObjectType	renameType;		/* OBJECT_TABLE, OBJECT_COLUMN, etc */
 	ObjectType	relationType;	/* if column name, associated relation type */
 	RangeVar   *relation;		/* in case it's a table */
@@ -2654,6 +2796,8 @@ typedef struct RenameStmt
 typedef struct AlterObjectDependsStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	ObjectType	objectType;		/* OBJECT_FUNCTION, OBJECT_TRIGGER, etc */
 	RangeVar   *relation;		/* in case a table is involved */
 	List	   *objname;		/* name of the object */
@@ -2668,6 +2812,8 @@ typedef struct AlterObjectDependsStmt
 typedef struct AlterObjectSchemaStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	ObjectType	objectType;		/* OBJECT_TABLE, OBJECT_TYPE, etc */
 	RangeVar   *relation;		/* in case it's a table */
 	List	   *object;			/* in case it's some other object */
@@ -2683,6 +2829,8 @@ typedef struct AlterObjectSchemaStmt
 typedef struct AlterOwnerStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	ObjectType	objectType;		/* OBJECT_TABLE, OBJECT_TYPE, etc */
 	RangeVar   *relation;		/* in case it's a table */
 	List	   *object;			/* in case it's some other object */
@@ -2698,6 +2846,8 @@ typedef struct AlterOwnerStmt
 typedef struct AlterOperatorStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *opername;		/* operator name */
 	List	   *operargs;		/* operator's argument TypeNames */
 	List	   *options;		/* List of DefElem nodes */
@@ -2711,6 +2861,8 @@ typedef struct AlterOperatorStmt
 typedef struct RuleStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	RangeVar   *relation;		/* relation the rule is for */
 	char	   *rulename;		/* name of the rule */
 	Node	   *whereClause;	/* qualifications */
@@ -2727,6 +2879,8 @@ typedef struct RuleStmt
 typedef struct NotifyStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *conditionname;	/* condition name to notify */
 	char	   *payload;		/* the payload string, or NULL if none */
 } NotifyStmt;
@@ -2738,6 +2892,8 @@ typedef struct NotifyStmt
 typedef struct ListenStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *conditionname;	/* condition name to listen on */
 } ListenStmt;
 
@@ -2748,6 +2904,8 @@ typedef struct ListenStmt
 typedef struct UnlistenStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *conditionname;	/* name to unlisten on, or NULL for all */
 } UnlistenStmt;
 
@@ -2772,6 +2930,8 @@ typedef enum TransactionStmtKind
 typedef struct TransactionStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	TransactionStmtKind kind;	/* see above */
 	List	   *options;		/* for BEGIN/START and savepoint commands */
 	char	   *gid;			/* for two-phase-commit related commands */
@@ -2784,6 +2944,8 @@ typedef struct TransactionStmt
 typedef struct CompositeTypeStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	RangeVar   *typevar;		/* the composite type to be created */
 	List	   *coldeflist;		/* list of ColumnDef nodes */
 } CompositeTypeStmt;
@@ -2795,6 +2957,8 @@ typedef struct CompositeTypeStmt
 typedef struct CreateEnumStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *typeName;		/* qualified name (list of Value strings) */
 	List	   *vals;			/* enum values (list of Value strings) */
 } CreateEnumStmt;
@@ -2806,6 +2970,8 @@ typedef struct CreateEnumStmt
 typedef struct CreateRangeStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *typeName;		/* qualified name (list of Value strings) */
 	List	   *params;			/* range parameters (list of DefElem) */
 } CreateRangeStmt;
@@ -2817,6 +2983,8 @@ typedef struct CreateRangeStmt
 typedef struct AlterEnumStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *typeName;		/* qualified name (list of Value strings) */
 	char	   *oldVal;			/* old enum value's name, if renaming */
 	char	   *newVal;			/* new enum value's name */
@@ -2839,6 +3007,8 @@ typedef enum ViewCheckOption
 typedef struct ViewStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	RangeVar   *view;			/* the view to be created */
 	List	   *aliases;		/* target column names */
 	Node	   *query;			/* the SELECT query */
@@ -2854,6 +3024,8 @@ typedef struct ViewStmt
 typedef struct LoadStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *filename;		/* file to load */
 } LoadStmt;
 
@@ -2864,6 +3036,8 @@ typedef struct LoadStmt
 typedef struct CreatedbStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *dbname;			/* name of database to create */
 	List	   *options;		/* List of DefElem nodes */
 } CreatedbStmt;
@@ -2875,6 +3049,8 @@ typedef struct CreatedbStmt
 typedef struct AlterDatabaseStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *dbname;			/* name of database to alter */
 	List	   *options;		/* List of DefElem nodes */
 } AlterDatabaseStmt;
@@ -2882,6 +3058,8 @@ typedef struct AlterDatabaseStmt
 typedef struct AlterDatabaseSetStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *dbname;			/* database name */
 	VariableSetStmt *setstmt;	/* SET or RESET subcommand */
 } AlterDatabaseSetStmt;
@@ -2893,6 +3071,8 @@ typedef struct AlterDatabaseSetStmt
 typedef struct DropdbStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *dbname;			/* database to drop */
 	bool		missing_ok;		/* skip error if db is missing? */
 } DropdbStmt;
@@ -2904,6 +3084,8 @@ typedef struct DropdbStmt
 typedef struct AlterSystemStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	VariableSetStmt *setstmt;	/* SET subcommand */
 } AlterSystemStmt;
 
@@ -2914,6 +3096,8 @@ typedef struct AlterSystemStmt
 typedef struct ClusterStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	RangeVar   *relation;		/* relation being indexed, or NULL if all */
 	char	   *indexname;		/* original index defined */
 	bool		verbose;		/* print progress info */
@@ -2942,6 +3126,8 @@ typedef enum VacuumOption
 typedef struct VacuumStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	int			options;		/* OR of VacuumOption flags */
 	RangeVar   *relation;		/* single table to process, or NULL */
 	List	   *va_cols;		/* list of column names, or NIL for all */
@@ -2958,6 +3144,8 @@ typedef struct VacuumStmt
 typedef struct ExplainStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	Node	   *query;			/* the query (see comments above) */
 	List	   *options;		/* list of DefElem nodes */
 } ExplainStmt;
@@ -2978,6 +3166,8 @@ typedef struct ExplainStmt
 typedef struct CreateTableAsStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	Node	   *query;			/* the query (see comments above) */
 	IntoClause *into;			/* destination table */
 	ObjectType	relkind;		/* OBJECT_TABLE or OBJECT_MATVIEW */
@@ -2992,6 +3182,8 @@ typedef struct CreateTableAsStmt
 typedef struct RefreshMatViewStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	bool		concurrent;		/* allow concurrent access? */
 	bool		skipData;		/* true for WITH NO DATA */
 	RangeVar   *relation;		/* relation to insert into */
@@ -3004,6 +3196,8 @@ typedef struct RefreshMatViewStmt
 typedef struct CheckPointStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 } CheckPointStmt;
 
 /* ----------------------
@@ -3022,6 +3216,8 @@ typedef enum DiscardMode
 typedef struct DiscardStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	DiscardMode target;
 } DiscardStmt;
 
@@ -3032,6 +3228,8 @@ typedef struct DiscardStmt
 typedef struct LockStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *relations;		/* relations to lock */
 	int			mode;			/* lock mode */
 	bool		nowait;			/* no wait mode */
@@ -3044,6 +3242,8 @@ typedef struct LockStmt
 typedef struct ConstraintsSetStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *constraints;	/* List of names as RangeVars */
 	bool		deferred;
 } ConstraintsSetStmt;
@@ -3068,6 +3268,8 @@ typedef enum ReindexObjectType
 typedef struct ReindexStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	ReindexObjectType kind;		/* REINDEX_OBJECT_INDEX, REINDEX_OBJECT_TABLE,
 								 * etc. */
 	RangeVar   *relation;		/* Table or index to reindex */
@@ -3082,6 +3284,8 @@ typedef struct ReindexStmt
 typedef struct CreateConversionStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *conversion_name;	/* Name of the conversion */
 	char	   *for_encoding_name;		/* source encoding name */
 	char	   *to_encoding_name;		/* destination encoding name */
@@ -3096,6 +3300,8 @@ typedef struct CreateConversionStmt
 typedef struct CreateCastStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	TypeName   *sourcetype;
 	TypeName   *targettype;
 	FuncWithArgs *func;
@@ -3110,6 +3316,8 @@ typedef struct CreateCastStmt
 typedef struct CreateTransformStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	bool		replace;
 	TypeName   *type_name;
 	char	   *lang;
@@ -3124,6 +3332,8 @@ typedef struct CreateTransformStmt
 typedef struct PrepareStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *name;			/* Name of plan, arbitrary */
 	List	   *argtypes;		/* Types of parameters (List of TypeName) */
 	Node	   *query;			/* The query itself (as a raw parsetree) */
@@ -3138,6 +3348,8 @@ typedef struct PrepareStmt
 typedef struct ExecuteStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *name;			/* The name of the plan to execute */
 	List	   *params;			/* Values to assign to parameters */
 } ExecuteStmt;
@@ -3150,6 +3362,8 @@ typedef struct ExecuteStmt
 typedef struct DeallocateStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	char	   *name;			/* The name of the plan to remove */
 	/* NULL means DEALLOCATE ALL */
 } DeallocateStmt;
@@ -3160,6 +3374,8 @@ typedef struct DeallocateStmt
 typedef struct DropOwnedStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *roles;
 	DropBehavior behavior;
 } DropOwnedStmt;
@@ -3170,6 +3386,8 @@ typedef struct DropOwnedStmt
 typedef struct ReassignOwnedStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *roles;
 	Node	   *newrole;
 } ReassignOwnedStmt;
@@ -3180,6 +3398,8 @@ typedef struct ReassignOwnedStmt
 typedef struct AlterTSDictionaryStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	List	   *dictname;		/* qualified name (list of Value strings) */
 	List	   *options;		/* List of DefElem nodes */
 } AlterTSDictionaryStmt;
@@ -3199,6 +3419,8 @@ typedef enum AlterTSConfigType
 typedef struct AlterTSConfigurationStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location */
+	int			qlength;		/* query length, 0 if unset */
 	AlterTSConfigType kind;		/* ALTER_TSCONFIG_ADD_MAPPING, etc */
 	List	   *cfgname;		/* qualified name (list of Value strings) */
 
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index e2fbc7d..bf9cae4 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -36,6 +36,8 @@
 typedef struct PlannedStmt
 {
 	NodeTag		type;
+	int			qlocation;		/* query location, or -1 if unknown */
+	int			qlength;		/* query length, 0 if unknown */
 
 	CmdType		commandType;	/* select|insert|update|delete */
 
diff --git a/src/include/parser/gramparse.h b/src/include/parser/gramparse.h
index 6d8e493..e373ab2 100644
--- a/src/include/parser/gramparse.h
+++ b/src/include/parser/gramparse.h
@@ -49,6 +49,8 @@ typedef struct base_yy_extra_type
 	char	   *lookahead_end;	/* end of current token */
 	char		lookahead_hold_char;	/* to be put back at *lookahead_end */
 
+	int			last_semicolon;	/* for query length computation */
+
 	/*
 	 * State variables that belong to the grammar.
 	 */
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to