diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 8dc3054..6e241c1 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -10,6 +10,13 @@
  * an entry, one must hold the lock shared or exclusive (so the entry doesn't
  * disappear!) and also take the entry's mutex spinlock.
  *
+ * Statements go through a normalization process before being stored.
+ * Normalization is ignoring components of the query that don't normally
+ * make it significantly different.  For example, the statements
+ * 'SELECT * FROM t WHERE f=1' and 'SELECT * FROM t WHERE f=2' would both
+ * be considered equivalent after normalization.  This is implemented
+ * by generating a series of integers from the parsed query tree, into
+ * what's referred to as a query jumble here.
  *
  * Copyright (c) 2008-2011, PostgreSQL Global Development Group
  *
@@ -27,12 +34,15 @@
 #include "funcapi.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
+#include "optimizer/planner.h"
+#include "parser/parsetree.h"
 #include "pgstat.h"
 #include "storage/fd.h"
 #include "storage/ipc.h"
 #include "storage/spin.h"
 #include "tcop/utility.h"
 #include "utils/builtins.h"
+#include "utils/memutils.h"
 
 
 PG_MODULE_MAGIC;
@@ -48,6 +58,7 @@ static const uint32 PGSS_FILE_HEADER = 0x20100108;
 #define USAGE_INIT				(1.0)	/* including initial planning */
 #define USAGE_DECREASE_FACTOR	(0.99)	/* decreased every entry_dealloc */
 #define USAGE_DEALLOC_PERCENT	5		/* free this % of entries at once */
+#define JUMBLE_SIZE				512	    /* query serialization buffer size */
 
 /*
  * Hashtable key that defines the identity of a hashtable entry.  The
@@ -63,8 +74,7 @@ typedef struct pgssHashKey
 	Oid			userid;			/* user OID */
 	Oid			dbid;			/* database OID */
 	int			encoding;		/* query encoding */
-	int			query_len;		/* # of valid bytes in query string */
-	const char *query_ptr;		/* query string proper */
+	char		parsed_jumble[JUMBLE_SIZE]; /* Integers from a query tree */
 } pgssHashKey;
 
 /*
@@ -95,6 +105,7 @@ typedef struct pgssEntry
 {
 	pgssHashKey key;			/* hash key of entry - MUST BE FIRST */
 	Counters	counters;		/* the statistics for this query */
+	int			query_len;		/* # of valid bytes in query string */
 	slock_t		mutex;			/* protects the counters only */
 	char		query[1];		/* VARIABLE LENGTH ARRAY - MUST BE LAST */
 	/* Note: the allocated length of query[] is actually pgss->query_size */
@@ -109,7 +120,22 @@ typedef struct pgssSharedState
 	int			query_size;		/* max query length in bytes */
 } pgssSharedState;
 
+typedef struct pgssTokenOffset
+{
+	int offset; /* Token offset in query string */
+	int len;
+} pgssTokenOffset;
+
 /*---- Local variables ----*/
+/* Some integers jumbled from last query tree seen */
+static char *last_jumble = NULL;
+/*
+ * Array that represents where
+ * normalized characters will be
+ */
+static pgssTokenOffset *offsets = NULL;
+/* Current size of offsets array */
+static size_t offset_num = 0;
 
 /* Current nesting depth of ExecutorRun calls */
 static int	nested_level = 0;
@@ -121,6 +147,7 @@ static ExecutorRun_hook_type prev_ExecutorRun = NULL;
 static ExecutorFinish_hook_type prev_ExecutorFinish = NULL;
 static ExecutorEnd_hook_type prev_ExecutorEnd = NULL;
 static ProcessUtility_hook_type prev_ProcessUtility = NULL;
+static planner_hook_type prev_Planner = NULL;
 
 /* Links to shared memory state */
 static pgssSharedState *pgss = NULL;
@@ -148,7 +175,6 @@ static int	pgss_track;			/* tracking level */
 static bool pgss_track_utility; /* whether to track utility commands */
 static bool pgss_save;			/* whether to save stats across shutdown */
 
-
 #define pgss_enabled() \
 	(pgss_track == PGSS_TRACK_ALL || \
 	(pgss_track == PGSS_TRACK_TOP && nested_level == 0))
@@ -166,6 +192,16 @@ PG_FUNCTION_INFO_V1(pg_stat_statements);
 
 static void pgss_shmem_startup(void);
 static void pgss_shmem_shutdown(int code, Datum arg);
+static PlannedStmt *PluginPlanner(Query *parse, int cursorOptions, ParamListInfo boundParams);
+static int comp_offset(const void *a, const void *b);
+static void JumbleCurQuery(Query *parse);
+static bool AppendJumb(char* item, char jumble[], size_t size, int *i);
+static bool PerformJumble(const Query *parse, char jumble[], size_t size, int *i);
+static bool QualsNode(const OpExpr *node, char jumble[], size_t size, int *i, List *rtable);
+static bool SerLeafNodes(const Node *arg, char jumble[], size_t size, int *i, List *rtable);
+static bool LimitOffsetNode(const Node *node, char jumble[], size_t size, int *i, List *rtable);
+static bool JoinExprNode(JoinExpr *node, char jumble[], size_t size, int *i, List *rtable);
+static bool JoinExprNodeChild(const Node *node, char jumble[], size_t size, int *i, List *rtable);
 static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags);
 static void pgss_ExecutorRun(QueryDesc *queryDesc,
 				 ScanDirection direction,
@@ -177,10 +213,10 @@ static void pgss_ProcessUtility(Node *parsetree,
 					DestReceiver *dest, char *completionTag);
 static uint32 pgss_hash_fn(const void *key, Size keysize);
 static int	pgss_match_fn(const void *key1, const void *key2, Size keysize);
-static void pgss_store(const char *query, double total_time, uint64 rows,
+static void pgss_store(const char *query, char parsed_jumble[], double total_time, uint64 rows,
 		   const BufferUsage *bufusage);
 static Size pgss_memsize(void);
-static pgssEntry *entry_alloc(pgssHashKey *key);
+static pgssEntry *entry_alloc(pgssHashKey *key, const char* query, int new_query_len);
 static void entry_dealloc(void);
 static void entry_reset(void);
 
@@ -262,6 +298,14 @@ _PG_init(void)
 	RequestAddinShmemSpace(pgss_memsize());
 	RequestAddinLWLocks(1);
 
+	/* Allocate a buffer to store selective serialization of the query tree
+	 * for the purposes of query normalization.
+	 */
+	last_jumble = MemoryContextAlloc(TopMemoryContext, JUMBLE_SIZE);
+	/* Allocate space for bookkeeping information for query str normalization */
+	/* TODO: Use a more frugal memory allocation strategy than this */
+	offsets =  MemoryContextAlloc(TopMemoryContext, JUMBLE_SIZE * sizeof(pgssTokenOffset));
+
 	/*
 	 * Install hooks.
 	 */
@@ -277,6 +321,11 @@ _PG_init(void)
 	ExecutorEnd_hook = pgss_ExecutorEnd;
 	prev_ProcessUtility = ProcessUtility_hook;
 	ProcessUtility_hook = pgss_ProcessUtility;
+	/*
+	 * Install hooks for query normalization
+	 */
+	prev_Planner = planner_hook;
+	planner_hook = PluginPlanner;
 }
 
 /*
@@ -292,6 +341,10 @@ _PG_fini(void)
 	ExecutorFinish_hook = prev_ExecutorFinish;
 	ExecutorEnd_hook = prev_ExecutorEnd;
 	ProcessUtility_hook = prev_ProcessUtility;
+	prev_Planner = planner_hook;
+
+	pfree(last_jumble);
+	pfree(offsets);
 }
 
 /*
@@ -300,7 +353,7 @@ _PG_fini(void)
  */
 static void
 pgss_shmem_startup(void)
-{
+{	
 	bool		found;
 	HASHCTL		info;
 	FILE	   *file;
@@ -395,27 +448,28 @@ pgss_shmem_startup(void)
 		if (!PG_VALID_BE_ENCODING(temp.key.encoding))
 			goto error;
 
+
 		/* Previous incarnation might have had a larger query_size */
-		if (temp.key.query_len >= buffer_size)
+		if (temp.query_len >= buffer_size)
 		{
-			buffer = (char *) repalloc(buffer, temp.key.query_len + 1);
-			buffer_size = temp.key.query_len + 1;
+			buffer = (char *) repalloc(buffer, temp.query_len + 1);
+			buffer_size = temp.query_len + 1;
 		}
 
-		if (fread(buffer, 1, temp.key.query_len, file) != temp.key.query_len)
+		if (fread(buffer, 1, temp.query_len, file) != temp.query_len)
 			goto error;
-		buffer[temp.key.query_len] = '\0';
+		buffer[temp.query_len] = '\0';
+
 
 		/* Clip to available length if needed */
-		if (temp.key.query_len >= query_size)
-			temp.key.query_len = pg_encoding_mbcliplen(temp.key.encoding,
+		if (temp.query_len >= query_size)
+			temp.query_len = pg_encoding_mbcliplen(temp.key.encoding,
 													   buffer,
-													   temp.key.query_len,
+													   temp.query_len,
 													   query_size - 1);
-		temp.key.query_ptr = buffer;
 
 		/* make the hashtable entry (discards old entries if too many) */
-		entry = entry_alloc(&temp.key);
+		entry = entry_alloc(&temp.key, buffer, temp.query_len);
 
 		/* copy in the actual stats */
 		entry->counters = temp.counters;
@@ -477,7 +531,7 @@ pgss_shmem_shutdown(int code, Datum arg)
 	hash_seq_init(&hash_seq, pgss_hash);
 	while ((entry = hash_seq_search(&hash_seq)) != NULL)
 	{
-		int			len = entry->key.query_len;
+		int			len = entry->query_len;
 
 		if (fwrite(entry, offsetof(pgssEntry, mutex), 1, file) != 1 ||
 			fwrite(entry->query, 1, len, file) != len)
@@ -500,6 +554,768 @@ error:
 	if (file)
 		FreeFile(file);
 	unlink(PGSS_DUMP_FILE);
+
+}
+
+/*
+ * PluginPlanner: Selectively serialize the query tree for the purposes of 
+ * query normalization.
+ */
+PlannedStmt *
+PluginPlanner(Query *parse, int cursorOptions, ParamListInfo boundParams)
+{
+	JumbleCurQuery(parse);
+	/* Invoke the planner */
+	if (prev_Planner)
+		return prev_Planner(parse, cursorOptions, boundParams);	
+	else
+		return standard_planner(parse, cursorOptions, boundParams);	
+}
+
+static int comp_offset(const void *a, const void *b)
+{
+	int l = ((pgssTokenOffset*) a)->offset;
+	int r = ((pgssTokenOffset*) b)->offset;
+	if (l < r)
+		return -1;
+	else if (l > r)
+		return +1;
+	else
+		return 0;
+}
+
+/*
+ * JumbleCurQuery: Selectively serialize query tree, and store it until
+ * needed to hash the current query
+ */
+static void JumbleCurQuery(Query *parse)
+{
+	int i = 0;
+	offset_num = 0;
+	memset(last_jumble, 0, JUMBLE_SIZE);
+	PerformJumble(parse, last_jumble, JUMBLE_SIZE, &i);
+	/* Sort offsets for query string normalization */
+	qsort(offsets, offset_num, sizeof(pgssTokenOffset), comp_offset);
+}
+
+/*
+ * AppendJumb: Append a given value that is substantive to a given
+ * query to jumble, while incrementing the iterator. If we've run
+ * out of space in the buffer, simply give up
+ */
+static bool AppendJumb(char* item, char jumble[], size_t size, int *i)
+{
+	if (size + *i >= JUMBLE_SIZE)
+		return false;
+	memcpy(jumble + *i, item, size);
+	*i += size;
+
+	return true;
+}
+
+/*
+ * Wrapper around AppendJumb to encapsulate details of serialization
+ * of individual elements.
+ */ 
+#define APP_JUMB(item) \
+if (!AppendJumb((char*)&item, jumble, sizeof(item), i))\
+	return false;
+
+/* 
+ * Simple wrappers around functions so that they return upon reaching 
+ * end of buffer 
+ */
+
+
+#define DoPerformJumble(item, jumble, size, i) \
+if (!PerformJumble(item, jumble, size, i)) \
+	return false;\
+
+#define DoQualsNode(item, jumble, size, i, rtable) \
+if (!QualsNode(item, jumble, size, i, rtable)) \
+	return false;\
+
+#define DoSerLeafNodes(item, jumble, size, i, rtable) \
+if (!SerLeafNodes(item, jumble, size, i, rtable)) \
+	return false;\
+
+#define DoLimitOffsetNode(item, jumble, size, i, rtable) \
+if (!LimitOffsetNode(item, jumble, size, i, rtable)) \
+	return false;\
+
+#define DoJoinExprNode(item, jumble, size, i, rtable) \
+if (!JoinExprNode(item, jumble, size, i, rtable)) \
+	return false;\
+
+#define DoJoinExprNodeChild(item, jumble, size, i, rtable) \
+if (!JoinExprNodeChild(item, jumble, size, i, rtable)) \
+	return false;\
+
+/*
+ * PerformJumble: Serialize the query tree "parse" such that it
+ * is usefully normalized, excluding constants that are not
+ * essential to the query itself.
+ *
+ * A guiding principal as to whether two queries should
+ * be considered equivalent is whether whatever difference
+ * exists between the two queries could be expected to result
+ * in two different plans. A non-obvious example of such a
+ * differentiator is a change in the "limit" constant.
+ *
+ * The resulting "jumble" can be hashed to uniquely identify
+ * a query that may use different constants in successive calls.
+ */
+static bool PerformJumble(const Query *parse, char jumble[], size_t size, int *i)
+{
+	ListCell *l;
+	FromExpr* jt = (FromExpr *) parse->jointree;		/* table join tree (FROM and WHERE clauses) */
+	FuncExpr *off = (FuncExpr *) parse->limitOffset;	/* # of result tuples to skip (int8 expr) */
+	FuncExpr *limcount = (FuncExpr *) parse->limitCount;	/* # of result tuples to skip (int8 expr) */
+
+	APP_JUMB(parse->resultRelation);
+	if (parse->utilityStmt)
+	{
+		/*
+		 * non-null if this is DECLARE CURSOR
+		 * or a non-optimizable statement
+		 */
+
+		/* TODO: Serialize utilityStmt */
+	}
+
+	if (parse->intoClause)
+	{
+		IntoClause *ic = parse->intoClause;
+		APP_JUMB(ic->onCommit)
+		/* TODO: Serialize more from intoClause */
+	}
+
+	/* WITH list (of CommonTableExpr's) */
+	foreach(l, parse->cteList)
+	{
+		CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
+		Query *cte_query = (Query*) cte->ctequery;
+		if (cte_query)
+			DoPerformJumble(cte_query, jumble, size, i);
+	}
+	if (jt)
+	{
+		if (jt->quals)
+		{
+			if (IsA(jt->quals, OpExpr))
+			{
+				DoQualsNode((OpExpr*) jt->quals, jumble, size, i, parse->rtable);
+			}
+			else
+			{
+				DoSerLeafNodes((Node*) jt->quals, jumble, size, i, parse->rtable);
+			}
+		}
+		/* table join tree */
+		foreach(l, jt->fromlist)
+		{
+			Node* fr = lfirst(l);
+			if (IsA(fr, JoinExpr))
+			{
+				DoJoinExprNode((JoinExpr*) fr, jumble, size, i, parse->rtable);
+			}
+			else if (IsA(fr, RangeTblRef))
+			{
+				RangeTblRef *rtf = (RangeTblRef *) fr;
+				RangeTblEntry *rte = rt_fetch(rtf->rtindex, parse->rtable);
+				APP_JUMB(rte->relid);
+				APP_JUMB(rte->rtekind);
+				/* Subselection in where clause */
+				if (rte->subquery)
+					DoPerformJumble(rte->subquery, jumble, size, i);
+
+				/* Function call in where clause */
+				if (rte->funcexpr)
+					DoSerLeafNodes((Node*) rte->funcexpr, jumble, size, i, parse->rtable);
+			}
+			else
+			{
+				elog(ERROR, "unrecognized fromlist node type: %d",
+					 (int) nodeTag(fr));
+			}
+		}
+	}
+	/*
+	 * target list (of TargetEntry)
+	 * columns returned by query
+	 */
+	foreach(l, parse->targetList)
+	{
+		TargetEntry *tg = (TargetEntry *) lfirst(l);
+		Node *e = (Node*) tg->expr;
+		if (tg->ressortgroupref)
+			APP_JUMB(tg->ressortgroupref); /* nonzero if referenced by a sort/group - for ORDER BY */
+		APP_JUMB(tg->resno); /* column number for select */
+		/* Handle the various types of nodes in
+		 * the select list of this query
+		 */
+		DoSerLeafNodes(e, jumble, size, i, parse->rtable);
+	}
+	/* return-values list (of TargetEntry) */
+	foreach(l, parse->returningList)
+	{
+		TargetEntry *rt = (TargetEntry *) lfirst(l);
+		Expr *e = (Expr*) rt->expr;
+		/*
+		 * Handle the various types of nodes in
+		 * the select list of this query
+		 */
+		if (IsA(e, Var)) /* table column */
+		{
+			Var *v = (Var*) e;
+			RangeTblEntry *rte = rt_fetch(v->varno, parse->rtable);
+			APP_JUMB(rte->relid);
+			APP_JUMB(v->varattno);
+		}
+		else
+		{
+			elog(ERROR, "unrecognized node type for returnlist node: %d",
+					(int) nodeTag(e));
+		}
+	}
+	/* a list of SortGroupClause's */
+	foreach(l, parse->groupClause)
+	{
+		SortGroupClause *gc = (SortGroupClause *) lfirst(l);
+		APP_JUMB(gc->tleSortGroupRef);
+		APP_JUMB(gc->nulls_first);
+	}
+
+	if (parse->havingQual)
+	{
+		if (IsA(parse->havingQual, OpExpr))
+		{
+			OpExpr *na = (OpExpr *) parse->havingQual;
+			DoQualsNode(na,  jumble, size, i, parse->rtable);
+		}
+		else
+		{
+			elog(ERROR, "unrecognized node type for havingclause node: %d",
+					(int) nodeTag(parse->havingQual));
+		}
+	}
+	foreach(l, parse->windowClause)
+	{
+		WindowClause *wc = (WindowClause *) lfirst(l);
+		ListCell *il;
+		APP_JUMB(wc->frameOptions);
+		foreach(il, wc->partitionClause) /* PARTITION BY list */
+		{
+			Node *n = (Node *) lfirst(il);
+			DoSerLeafNodes(n, jumble, size, i, parse->rtable);
+		}
+		foreach(il, wc->orderClause) /* ORDER BY list */
+		{
+			Node *n = (Node *) lfirst(il);
+			DoSerLeafNodes(n, jumble, size, i, parse->rtable);
+		}
+	}
+
+	foreach(l, parse->distinctClause)
+	{
+		SortGroupClause *dc = (SortGroupClause *) lfirst(l);
+		APP_JUMB(dc->tleSortGroupRef);
+		APP_JUMB(dc->nulls_first);
+	}
+
+	/* Don't look at parse->sortClause,
+	 * because the value ressortgroupref is already
+	 * serialized when we iterate through targetList
+	 */
+
+	if (off)
+		DoLimitOffsetNode((Node*) off, jumble, size, i, parse->rtable);
+
+	if (limcount)
+		DoLimitOffsetNode((Node*) limcount, jumble, size, i, parse->rtable);
+
+	foreach(l, parse->rowMarks)
+	{
+		RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+		APP_JUMB(rc->rti);				/* range table index of target relation */
+		APP_JUMB(rc->forUpdate);			/* true = FOR UPDATE, false = FOR SHARE */
+		APP_JUMB(rc->noWait);				/* NOWAIT option */
+		APP_JUMB(rc->pushedDown);			/* pushed down from higher query level? */
+	}
+
+	if (parse->setOperations)
+	{
+		/*
+		 * set-operation tree if this is top
+		 * level of a UNION/INTERSECT/EXCEPT query
+		 */
+		SetOperationStmt *topop = (SetOperationStmt *) parse->setOperations;
+		APP_JUMB(topop->op);
+		APP_JUMB(topop->all);
+
+		/* leaf selects are RTE subselections */
+		foreach(l, parse->rtable)
+		{
+			RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
+			if (rte->subquery)
+				DoPerformJumble(rte->subquery, jumble, size, i);
+		}
+	}
+	return true;
+}
+
+/* 
+ * Perform selective serialization of "Quals" nodes when
+ * they're IsA(*, OpExpr)
+ */
+static bool QualsNode(const OpExpr *node, char jumble[], size_t size, int *i, List *rtable)
+{
+	ListCell *l;
+	APP_JUMB(node->opno);
+	foreach(l, node->args)
+	{
+		Node *arg = (Node *) lfirst(l);
+		DoSerLeafNodes(arg, jumble, size, i, rtable);
+	}
+	return true;
+}
+
+/*
+ * SerLeafNodes: Serialize a selection of parser/prim nodes that are frequently,
+ * though certainly not necesssarily leaf nodes, such as Variables (columns),
+ * constants and function calls
+ */
+static bool SerLeafNodes(const Node *arg, char jumble[], size_t size, int *i, List *rtable)
+{
+	ListCell *l;
+	if (IsA(arg, Const))
+	{
+		/*
+		 * Serialize generic magic value to
+		 * normalize constants
+		 */
+		char magic = 0xC0;
+		Const *c = (Const *) arg;
+		APP_JUMB(magic);
+		/* Datatype of the constant is a 
+		 * differentiator 
+		 */
+		APP_JUMB(c->consttype);
+		/* Some Const nodes naturally don't have a location.
+		 * Also, views that have constants in their
+		 * definitions will have a tok_len of 0
+		 */
+		elog(DEBUG1, " (Consttype %d) c->location: %d, c->tok_len: %d",c->consttype, c->location, c->tok_len);
+		if (c->location > 0 && c->tok_len > 0) 
+		{
+			offsets[offset_num].offset = c->location;
+			offsets[offset_num].len = c->tok_len;
+			/* should be added in the same order that as query string */
+			offset_num++;
+		}
+	}
+	else if (IsA(arg, Var))
+	{
+		char magic = 0xFA;
+		Var *v = (Var *) arg;
+		RangeTblEntry *rte = rt_fetch(v->varno, rtable);
+		APP_JUMB(magic);
+		APP_JUMB(rte->relid);
+		APP_JUMB(v->varattno); /* column number of table */
+	}
+	else if (IsA(arg, Param))
+	{
+		Param *p = ((Param *) arg);
+		APP_JUMB(p->paramkind);
+		APP_JUMB(p->paramid);
+	}
+	else if (IsA(arg, RelabelType))
+	{
+		APP_JUMB(((RelabelType *) arg)->resulttype);
+	}
+	else if (IsA(arg, WindowFunc))
+	{
+		WindowFunc *wf =  (WindowFunc *) arg;
+		APP_JUMB(wf->winfnoid);
+		foreach(l, wf->args)
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+	}
+	else if (IsA(arg, FuncExpr))
+	{
+		FuncExpr *f =  (FuncExpr *) arg;
+		APP_JUMB(f->funcid);
+		foreach(l, f->args)
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+	}
+	else if (IsA(arg, OpExpr))
+	{
+		DoQualsNode((OpExpr*) arg, jumble, size, i, rtable);
+	}
+	else if (IsA(arg, CoerceViaIO))
+	{
+		APP_JUMB(((CoerceViaIO*) arg)->coerceformat);
+		APP_JUMB(((CoerceViaIO*) arg)->resulttype);
+	}
+	else if (IsA(arg, Aggref))
+	{
+		Aggref *a =  (Aggref *) arg;
+		APP_JUMB(a->aggfnoid);
+		foreach(l, a->args)
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+	}
+	else if (IsA(arg, SubLink))
+	{
+		SubLink *s = (SubLink*) arg;
+		APP_JUMB(s->subLinkType);
+		/* Serialize select-list subselect recursively */
+		if (s->subselect)
+			DoPerformJumble( (Query*) s->subselect, jumble, size, i);
+	}
+	else if (IsA(arg, TargetEntry))
+	{
+		TargetEntry *rt = (TargetEntry *) arg;
+		Node *e = (Node*) rt->expr;
+		APP_JUMB(rt->resorigtbl); /* OID of column's source table */
+		APP_JUMB(rt->ressortgroupref); /*  nonzero if referenced by a sort/group - for ORDER BY */
+		DoSerLeafNodes(e, jumble, size, i, rtable);
+	}
+	else if (IsA(arg, BoolExpr))
+	{
+		BoolExpr *be = (BoolExpr *) arg;	
+		APP_JUMB(be->boolop);
+		foreach(l, be->args)
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+	}
+	else if (IsA(arg, NullTest))
+	{
+		NullTest *nt = (NullTest *) arg;
+		Node *arg = (Node *) nt->arg;
+		APP_JUMB(nt->nulltesttype);	/* IS NULL, IS NOT NULL */
+		APP_JUMB(nt->argisrow);		/* is input a composite type ? */
+		DoSerLeafNodes(arg, jumble, size, i, rtable);
+	}
+	else if (IsA(arg, ArrayExpr))
+	{
+		ArrayExpr *ae = (ArrayExpr *) arg;		
+		APP_JUMB(ae->array_typeid);	/* type of expression result */
+		APP_JUMB(ae->element_typeid);	/* common type of array elements */
+		foreach(l, ae->elements)
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+	}
+	else if (IsA(arg, CaseExpr))
+	{
+		CaseExpr *ce = (CaseExpr*) arg;	
+		Assert(ce->casetype != InvalidOid);
+		APP_JUMB(ce->casetype);
+		foreach(l, ce->args)
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+		if (ce->arg)	
+			DoSerLeafNodes((Node*) ce->arg, jumble, size, i, rtable);
+
+		if (ce->defresult)
+		{
+			/* Default result (ELSE clause) 
+			 * The ptr may be NULL, because no else clause
+			 * was actually specified, and thus the value is
+			 * equivalent to SQL ELSE NULL
+			 */
+			DoSerLeafNodes((Node*) ce->defresult, jumble, size, i, rtable); /* the default result (ELSE clause) */
+		}
+	}
+	else if (IsA(arg, CaseTestExpr))
+	{
+		CaseTestExpr *ct = (CaseTestExpr*) arg;
+		APP_JUMB(ct->typeId);
+	}
+	else if (IsA(arg, CaseWhen))
+	{
+		CaseWhen *cw = (CaseWhen*) arg;
+		Node *res = (Node*) cw->result;
+		Node *exp = (Node*) cw->expr;
+		if (res)
+			DoSerLeafNodes(res, jumble, size, i, rtable);
+		if (exp)
+			DoSerLeafNodes(exp, jumble, size, i, rtable);
+	}
+	else if (IsA(arg, MinMaxExpr))
+	{
+		MinMaxExpr *cw = (MinMaxExpr*) arg;
+		APP_JUMB(cw->minmaxtype);
+		APP_JUMB(cw->op);
+		foreach(l, cw->args)
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+	}
+	else if (IsA(arg, ScalarArrayOpExpr))
+	{
+		ScalarArrayOpExpr *sa = (ScalarArrayOpExpr*) arg;
+		APP_JUMB(sa->opfuncid);
+		APP_JUMB(sa->useOr);
+		foreach(l, sa->args)
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+	}
+	else if (IsA(arg, CoalesceExpr))
+	{
+		CoalesceExpr *ca = (CoalesceExpr*) arg;
+		foreach(l, ca->args)
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+	}
+	else if (IsA(arg, ArrayCoerceExpr))
+	{
+		ArrayCoerceExpr *ac = (ArrayCoerceExpr *) arg;
+		DoSerLeafNodes((Node*) ac->arg, jumble, size, i, rtable);
+	}
+	else if (IsA(arg, WindowClause))
+	{
+		WindowClause *wc = (WindowClause*) arg;
+		foreach(l, wc->partitionClause)
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+		foreach(l, wc->orderClause)
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+	}
+	else if (IsA(arg, SortGroupClause))
+	{
+		SortGroupClause *sgc = (SortGroupClause*) arg;
+		APP_JUMB(sgc->tleSortGroupRef);
+		APP_JUMB(sgc->nulls_first);
+	}
+	else if (IsA(arg, Integer) ||
+		  IsA(arg, Float) ||
+		  IsA(arg, String) ||
+		  IsA(arg, BitString) ||
+		  IsA(arg, Null)
+		)
+	{
+		/* It is not necessary to
+		 * serialize integral values
+		 */
+	}
+	else if (IsA(arg, BooleanTest))
+	{
+		BooleanTest *bt = (BooleanTest *) arg;
+		APP_JUMB(bt->booltesttype);
+		DoSerLeafNodes((Node*) bt->arg, jumble, size, i, rtable);
+	}
+	else if (IsA(arg, ArrayRef))
+	{
+		ArrayRef *ar = (ArrayRef*) arg;
+		APP_JUMB(ar->refarraytype);
+		foreach(l, ar->refupperindexpr)
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+		foreach(l, ar->reflowerindexpr)
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+	}
+	else if (IsA(arg, NullIfExpr))
+	{
+		/* NullIfExpr is just a typedef for OpExpr */
+		DoQualsNode((OpExpr*) arg, jumble, size, i, rtable);
+	}
+	else if (IsA(arg, RowExpr))
+	{
+		RowExpr *re = (RowExpr*) arg;
+		APP_JUMB(re->row_format);
+		foreach(l, re->args)
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+
+	}
+	else if (IsA(arg, XmlExpr))
+	{
+		XmlExpr *xml = (XmlExpr*) arg;
+		APP_JUMB(xml->op);
+		foreach(l, xml->args)
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+		foreach(l, xml->named_args) /* non-XML expressions for xml_attributes */
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+		foreach(l, xml->arg_names) /* parallel list of Value strings */
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+	}
+	else if (IsA(arg, RowCompareExpr))
+	{
+		RowCompareExpr *rc = (RowCompareExpr*) arg;
+		foreach(l, rc->largs)
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+		foreach(l, rc->rargs)
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}
+	}
+	else
+	{
+		elog(ERROR, "unrecognized node type for SerLeafNodes node: %d",
+				(int) nodeTag(arg));
+	}
+	return true;
+}
+
+/* 
+ * Perform selective serialization of limit or offset nodes
+ */
+static bool LimitOffsetNode(const Node *node, char jumble[], size_t size, int *i, List *rtable)
+{
+	ListCell *l;
+	if (IsA(node, FuncExpr))
+	{
+		foreach(l, ((FuncExpr*) node)->args)
+		{
+			Node *arg = (Node *) lfirst(l);
+			if (IsA(arg, Const))
+			{
+				/* 
+				 * A different limit and/or offset constitutes a different 
+				 * query, so this is one constant that we actually want to 
+				 * hash
+				 */
+				Datum	constvalue = ((Const*) arg)->constvalue;
+				int64 val = DatumGetInt64(constvalue);
+				APP_JUMB(val);
+			}
+			else
+			{
+				elog(ERROR, "unrecognized node type for FuncExpr limit/offset node: %d",
+						(int) nodeTag(arg));
+			}
+		}
+	}
+	else if (IsA(node, Const))
+	{
+		/* This should be a differentiator, as it results in the addition of a limit node */
+		char magic = 0xEA;
+		APP_JUMB(magic);
+		return true;	
+	}
+	else
+	{
+		/* Fall back on leaf node representation */
+		DoSerLeafNodes(node, jumble, size, i, rtable);
+	}
+	return true;
+}
+
+/*
+ * Perform selective serialization of JoinExpr nodes
+ */
+static bool JoinExprNode(JoinExpr *node, char jumble[], size_t size, int *i, List *rtable)
+{
+	Node	   *larg = node->larg;	/* left subtree */
+	Node	   *rarg = node->rarg;	/* right subtree */
+	ListCell *l;
+
+	Assert( IsA(node, JoinExpr));
+
+	APP_JUMB(node->jointype);
+	APP_JUMB(node->isNatural);
+
+	if (node->quals)
+	{
+		if ( IsA(node, OpExpr))
+		{
+			DoQualsNode((OpExpr*) node->quals, jumble, size, i, rtable);
+		}
+		else
+		{
+			DoSerLeafNodes((Node*) node->quals, jumble, size, i, rtable);
+		}
+
+	}	
+	foreach(l, node->usingClause) /* USING clause, if any (list of String) */
+	{
+		Node *arg = (Node *) lfirst(l);
+		DoSerLeafNodes(arg, jumble, size, i, rtable);
+	}
+	if (larg)
+		DoJoinExprNodeChild(larg, jumble, size, i, rtable);
+	if (rarg)
+		DoJoinExprNodeChild(rarg, jumble, size, i, rtable);
+
+	return true;
+}
+
+/*
+ * Serialize children of the JoinExpr node
+ */
+static bool JoinExprNodeChild(const Node *node, char jumble[], size_t size, int *i, List *rtable)
+{
+	if (IsA(node, RangeTblRef))
+	{
+		RangeTblRef *rt = (RangeTblRef*) node;
+		RangeTblEntry *rte = rt_fetch(rt->rtindex, rtable);
+		ListCell *l;
+
+		APP_JUMB(rte->relid);
+		APP_JUMB(rte->jointype);
+
+		if (rte->subquery)
+			DoPerformJumble(rte->subquery, jumble, size, i);
+		
+		foreach(l, rte->joinaliasvars)
+		{
+			Node *arg = (Node *) lfirst(l);
+			DoSerLeafNodes(arg, jumble, size, i, rtable);
+		}	
+	}
+	else if (IsA(node, JoinExpr))
+	{	
+		DoJoinExprNode((JoinExpr*) node, jumble, size, i, rtable);
+	}
+	else
+	{
+		DoSerLeafNodes(node, jumble, size, i, rtable);
+	}
+	return true;
 }
 
 /*
@@ -592,6 +1408,7 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
 		InstrEndLoop(queryDesc->totaltime);
 
 		pgss_store(queryDesc->sourceText,
+				   last_jumble,
 				   queryDesc->totaltime->total,
 				   queryDesc->estate->es_processed,
 				   &queryDesc->totaltime->bufusage);
@@ -665,7 +1482,8 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString,
 		bufusage.temp_blks_written =
 			pgBufferUsage.temp_blks_written - bufusage.temp_blks_written;
 
-		pgss_store(queryString, INSTR_TIME_GET_DOUBLE(duration), rows,
+		memset(last_jumble, 0, JUMBLE_SIZE); /* Utility statements are all equivalent */
+		pgss_store(queryString, last_jumble, INSTR_TIME_GET_DOUBLE(duration), rows,
 				   &bufusage);
 	}
 	else
@@ -690,8 +1508,7 @@ pgss_hash_fn(const void *key, Size keysize)
 	/* we don't bother to include encoding in the hash */
 	return hash_uint32((uint32) k->userid) ^
 		hash_uint32((uint32) k->dbid) ^
-		DatumGetUInt32(hash_any((const unsigned char *) k->query_ptr,
-								k->query_len));
+		DatumGetUInt32(hash_any((const unsigned char* ) k->parsed_jumble, JUMBLE_SIZE) );
 }
 
 /*
@@ -706,8 +1523,7 @@ pgss_match_fn(const void *key1, const void *key2, Size keysize)
 	if (k1->userid == k2->userid &&
 		k1->dbid == k2->dbid &&
 		k1->encoding == k2->encoding &&
-		k1->query_len == k2->query_len &&
-		memcmp(k1->query_ptr, k2->query_ptr, k1->query_len) == 0)
+		memcmp(k1->parsed_jumble, k2->parsed_jumble, JUMBLE_SIZE) == 0)
 		return 0;
 	else
 		return 1;
@@ -717,12 +1533,13 @@ pgss_match_fn(const void *key1, const void *key2, Size keysize)
  * Store some statistics for a statement.
  */
 static void
-pgss_store(const char *query, double total_time, uint64 rows,
+pgss_store(const char *query, char parsed_jumble[], double total_time, uint64 rows,
 		   const BufferUsage *bufusage)
 {
 	pgssHashKey key;
 	double		usage;
 	pgssEntry  *entry;
+	int new_query_len = strlen(query);
 
 	Assert(query != NULL);
 
@@ -734,14 +1551,13 @@ pgss_store(const char *query, double total_time, uint64 rows,
 	key.userid = GetUserId();
 	key.dbid = MyDatabaseId;
 	key.encoding = GetDatabaseEncoding();
-	key.query_len = strlen(query);
-	if (key.query_len >= pgss->query_size)
-		key.query_len = pg_encoding_mbcliplen(key.encoding,
+	memcpy(key.parsed_jumble, parsed_jumble, JUMBLE_SIZE);
+
+	if (new_query_len >= pgss->query_size)
+		new_query_len = pg_encoding_mbcliplen(key.encoding,
 											  query,
-											  key.query_len,
+											  new_query_len,
 											  pgss->query_size - 1);
-	key.query_ptr = query;
-
 	usage = USAGE_EXEC(duration);
 
 	/* Lookup the hash table entry with shared lock. */
@@ -750,10 +1566,71 @@ pgss_store(const char *query, double total_time, uint64 rows,
 	entry = (pgssEntry *) hash_search(pgss_hash, &key, HASH_FIND, NULL);
 	if (!entry)
 	{
-		/* Must acquire exclusive lock to add a new entry. */
-		LWLockRelease(pgss->lock);
-		LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
-		entry = entry_alloc(&key);
+		/*
+		 * It is necessary to generate a normalized version
+		 * of the query string that will be used for this query.
+		 *
+		 * Note that the representation seen by the user will
+		 * only have non-differentiating Const tokens swapped
+		 * with '?' characters, and does not for example take
+		 * account of the fact that alias names could vary
+		 * between successive calls of what we regard as the
+		 * same query.
+		 */
+		if (offset_num > 0)
+		{
+			int i,
+			  last_off = 0,
+			  quer_it = 0,
+			  n_quer_it = 0,
+			  off = 0,
+			  tok_len = 0,
+			  len_to_wrt = 0,
+			  last_tok_len = 0;
+			char *norm_query = palloc0(new_query_len + 1);
+			for(i = 0; i < offset_num; i++)
+			{
+				off = offsets[i].offset;
+				tok_len = offsets[i].len;
+				len_to_wrt = off - last_off;
+				len_to_wrt -= last_tok_len;
+				/*
+				 * Copy everything prior to the current offset/token to be
+				 * replaced, except previously copied things
+				 */
+
+				if (off + tok_len > new_query_len)
+					break;
+				memcpy(norm_query + n_quer_it, query + quer_it, len_to_wrt);
+				n_quer_it += len_to_wrt;
+				norm_query[n_quer_it++] = '?';
+				quer_it += len_to_wrt + tok_len;
+				last_off = off;
+				last_tok_len = tok_len;
+			}
+			/* Finish off last bit of query string */
+			memcpy(norm_query + n_quer_it, query + (off + tok_len), 
+				Min( strlen(query) - (off + tok_len ),
+				new_query_len - n_quer_it ) );
+			elog(DEBUG1, "norm_query: '%s', orig query: '%s'", norm_query, query);
+			/*
+			 * Must acquire exclusive lock to add a new entry.
+			 * Leave that until as late as possible.
+			 */
+			LWLockRelease(pgss->lock);
+			LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
+
+			entry = entry_alloc(&key, norm_query, strlen(norm_query));
+			pfree(norm_query);
+		}
+		else
+		{
+			/* Acquire exclusive lock as required by entry_alloc() */
+			LWLockRelease(pgss->lock);
+			LWLockAcquire(pgss->lock, LW_EXCLUSIVE);
+
+			entry = entry_alloc(&key, query, new_query_len);
+		}
 	}
 
 	/* Grab the spinlock while updating the counters. */
@@ -775,7 +1652,6 @@ pgss_store(const char *query, double total_time, uint64 rows,
 		e->counters.usage += usage;
 		SpinLockRelease(&e->mutex);
 	}
-
 	LWLockRelease(pgss->lock);
 }
 
@@ -857,13 +1733,22 @@ pg_stat_statements(PG_FUNCTION_ARGS)
 		values[i++] = ObjectIdGetDatum(entry->key.userid);
 		values[i++] = ObjectIdGetDatum(entry->key.dbid);
 
+		/* copy counters to a local variable to keep locking time short */
+		{
+			volatile pgssEntry *e = (volatile pgssEntry *) entry;
+
+			SpinLockAcquire(&e->mutex);
+			tmp = e->counters;
+			SpinLockRelease(&e->mutex);
+		}
+
 		if (is_superuser || entry->key.userid == userid)
 		{
 			char	   *qstr;
 
 			qstr = (char *)
 				pg_do_encoding_conversion((unsigned char *) entry->query,
-										  entry->key.query_len,
+										  entry->query_len,
 										  entry->key.encoding,
 										  GetDatabaseEncoding());
 			values[i++] = CStringGetTextDatum(qstr);
@@ -873,15 +1758,6 @@ pg_stat_statements(PG_FUNCTION_ARGS)
 		else
 			values[i++] = CStringGetTextDatum("<insufficient privilege>");
 
-		/* copy counters to a local variable to keep locking time short */
-		{
-			volatile pgssEntry *e = (volatile pgssEntry *) entry;
-
-			SpinLockAcquire(&e->mutex);
-			tmp = e->counters;
-			SpinLockRelease(&e->mutex);
-		}
-
 		values[i++] = Int64GetDatumFast(tmp.calls);
 		values[i++] = Float8GetDatumFast(tmp.total_time);
 		values[i++] = Int64GetDatumFast(tmp.rows);
@@ -933,14 +1809,11 @@ pgss_memsize(void)
  * have made the entry while we waited to get exclusive lock.
  */
 static pgssEntry *
-entry_alloc(pgssHashKey *key)
+entry_alloc(pgssHashKey *key, const char* query, int new_query_len)
 {
 	pgssEntry  *entry;
 	bool		found;
 
-	/* Caller must have clipped query properly */
-	Assert(key->query_len < pgss->query_size);
-
 	/* Make space if needed */
 	while (hash_get_num_entries(pgss_hash) >= pgss_max)
 		entry_dealloc();
@@ -950,19 +1823,23 @@ entry_alloc(pgssHashKey *key)
 
 	if (!found)
 	{
+		entry->query_len = new_query_len;
+		Assert(entry->query_len > 0);
 		/* New entry, initialize it */
 
-		/* dynahash tried to copy the key for us, but must fix query_ptr */
-		entry->key.query_ptr = entry->query;
 		/* reset the statistics */
 		memset(&entry->counters, 0, sizeof(Counters));
 		entry->counters.usage = USAGE_INIT;
 		/* re-initialize the mutex each time ... we assume no one using it */
 		SpinLockInit(&entry->mutex);
 		/* ... and don't forget the query text */
-		memcpy(entry->query, key->query_ptr, key->query_len);
-		entry->query[key->query_len] = '\0';
+		/* memset previous entry in the slot */
+		memset(entry->query, 0, pgss->query_size);
+		memcpy(entry->query, query, entry->query_len);
+		entry->query[entry->query_len] = '\0';
 	}
+	/* Caller must have clipped query properly */
+	Assert(entry->query_len < pgss->query_size);
 
 	return entry;
 }
@@ -971,7 +1848,7 @@ entry_alloc(pgssHashKey *key)
  * qsort comparator for sorting into increasing usage order
  */
 static int
-entry_cmp(const void *lhs, const void *rhs)
+entry_cmp_usage(const void *lhs, const void *rhs)
 {
 	double		l_usage = (*(pgssEntry * const *) lhs)->counters.usage;
 	double		r_usage = (*(pgssEntry * const *) rhs)->counters.usage;
@@ -1009,7 +1886,7 @@ entry_dealloc(void)
 		entry->counters.usage *= USAGE_DECREASE_FACTOR;
 	}
 
-	qsort(entries, i, sizeof(pgssEntry *), entry_cmp);
+	qsort(entries, i, sizeof(pgssEntry *), entry_cmp_usage);
 	nvictims = Max(10, i * USAGE_DEALLOC_PERCENT / 100);
 	nvictims = Min(nvictims, i);
 
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index e11d896..ccd92d3 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2441,7 +2441,7 @@ cookDefault(ParseState *pstate,
 									 atttypid, atttypmod,
 									 COERCION_ASSIGNMENT,
 									 COERCE_IMPLICIT_CAST,
-									 -1);
+									 -1, -1);
 		if (expr == NULL)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index a949215..e80d57f 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -365,7 +365,7 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
 									 expected_type_id, -1,
 									 COERCION_ASSIGNMENT,
 									 COERCE_IMPLICIT_CAST,
-									 -1);
+									 -1, -1);
 
 		if (expr == NULL)
 			ereport(ERROR,
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c4622c0..342e168 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4464,7 +4464,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 													typmod,
 													COERCION_ASSIGNMENT,
 													COERCE_IMPLICIT_CAST,
-													-1);
+													-1, -1);
 			if (defval == NULL) /* should not happen */
 				elog(ERROR, "failed to coerce base type to domain");
 		}
@@ -6958,7 +6958,7 @@ ATPrepAlterColumnType(List **wqueue,
 										  targettype, targettypmod,
 										  COERCION_ASSIGNMENT,
 										  COERCE_IMPLICIT_CAST,
-										  -1);
+										  -1, -1);
 		if (transform == NULL)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -7125,7 +7125,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 											targettype, targettypmod,
 											COERCION_ASSIGNMENT,
 											COERCE_IMPLICIT_CAST,
-											-1);
+											-1, -1);
 		if (defaultexpr == NULL)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 63958c3..c372f20 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2129,6 +2129,7 @@ _copyAConst(A_Const *from)
 	}
 
 	COPY_LOCATION_FIELD(location);
+	COPY_LOCATION_FIELD(tok_len);
 
 	return newnode;
 }
@@ -2283,6 +2284,7 @@ _copyTypeCast(TypeCast *from)
 	COPY_NODE_FIELD(arg);
 	COPY_NODE_FIELD(typeName);
 	COPY_LOCATION_FIELD(location);
+	COPY_LOCATION_FIELD(tok_len);
 
 	return newnode;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c135465..2a3118d 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -65,7 +65,6 @@
 #include "utils/numeric.h"
 #include "utils/xml.h"
 
-
 /* Location tracking support --- simpler than bison's default */
 #define YYLLOC_DEFAULT(Current, Rhs, N) \
 	do { \
@@ -109,15 +108,15 @@ static void base_yyerror(YYLTYPE *yylloc, core_yyscan_t yyscanner,
 						 const char *msg);
 static Node *makeColumnRef(char *colname, List *indirection,
 						   int location, core_yyscan_t yyscanner);
-static Node *makeTypeCast(Node *arg, TypeName *typename, int location);
-static Node *makeStringConst(char *str, int location);
-static Node *makeStringConstCast(char *str, int location, TypeName *typename);
-static Node *makeIntConst(int val, int location);
-static Node *makeFloatConst(char *str, int location);
-static Node *makeBitStringConst(char *str, int location);
-static Node *makeNullAConst(int location);
-static Node *makeAConst(Value *v, int location);
-static Node *makeBoolAConst(bool state, int location);
+static Node *makeTypeCast(Node *arg, TypeName *typename, int location, int length);
+static Node *makeStringConst(char *str, int location, int length);
+static Node *makeStringConstCast(char *str, int location, int length, TypeName *typename);
+static Node *makeIntConst(int val, int location, int length);
+static Node *makeFloatConst(char *str, int location, int length);
+static Node *makeBitStringConst(char *str, int location, int length);
+static Node *makeNullAConst(int location, int length);
+static Node *makeAConst(Value *v, int location, int length);
+static Node *makeBoolAConst(bool state, int location, int length);
 static FuncCall *makeOverlaps(List *largs, List *rargs,
 							  int location, core_yyscan_t yyscanner);
 static void check_qualified_name(List *names, core_yyscan_t yyscanner);
@@ -131,7 +130,7 @@ static void insertSelectOptions(SelectStmt *stmt,
 								WithClause *withClause,
 								core_yyscan_t yyscanner);
 static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
-static Node *doNegate(Node *n, int location);
+static Node *doNegate(Node *n, int location, int length);
 static void doNegateFloat(Value *v);
 static Node *makeAArrayExpr(List *elements, int location);
 static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args,
@@ -913,7 +912,7 @@ AlterOptRoleElem:
 						ereport(ERROR,
 								(errcode(ERRCODE_SYNTAX_ERROR),
 								 errmsg("unrecognized role option \"%s\"", $1),
-									 parser_errposition(@1)));
+									 parser_errposition(@1.begins)));
 				}
 		;
 
@@ -1299,7 +1298,7 @@ set_rest:	/* Generic SET syntaxes: */
 					ereport(ERROR,
 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 							 errmsg("current database cannot be changed"),
-							 parser_errposition(@2)));
+							 parser_errposition(@2.begins)));
 					$$ = NULL; /*not reached*/
 				}
 			| SCHEMA Sconst
@@ -1307,7 +1306,7 @@ set_rest:	/* Generic SET syntaxes: */
 					VariableSetStmt *n = makeNode(VariableSetStmt);
 					n->kind = VAR_SET_VALUE;
 					n->name = "search_path";
-					n->args = list_make1(makeStringConst($2, @2));
+					n->args = list_make1(makeStringConst($2, @2.begins, @2.length));
 					$$ = n;
 				}
 			| NAMES opt_encoding
@@ -1316,7 +1315,7 @@ set_rest:	/* Generic SET syntaxes: */
 					n->kind = VAR_SET_VALUE;
 					n->name = "client_encoding";
 					if ($2 != NULL)
-						n->args = list_make1(makeStringConst($2, @2));
+						n->args = list_make1(makeStringConst($2, @2.begins, @2.length));
 					else
 						n->kind = VAR_SET_DEFAULT;
 					$$ = n;
@@ -1326,7 +1325,7 @@ set_rest:	/* Generic SET syntaxes: */
 					VariableSetStmt *n = makeNode(VariableSetStmt);
 					n->kind = VAR_SET_VALUE;
 					n->name = "role";
-					n->args = list_make1(makeStringConst($2, @2));
+					n->args = list_make1(makeStringConst($2, @2.begins, @2.length));
 					$$ = n;
 				}
 			| SESSION AUTHORIZATION ColId_or_Sconst
@@ -1334,7 +1333,7 @@ set_rest:	/* Generic SET syntaxes: */
 					VariableSetStmt *n = makeNode(VariableSetStmt);
 					n->kind = VAR_SET_VALUE;
 					n->name = "session_authorization";
-					n->args = list_make1(makeStringConst($3, @3));
+					n->args = list_make1(makeStringConst($3, @3.begins, @3.length));
 					$$ = n;
 				}
 			| SESSION AUTHORIZATION DEFAULT
@@ -1349,7 +1348,7 @@ set_rest:	/* Generic SET syntaxes: */
 					VariableSetStmt *n = makeNode(VariableSetStmt);
 					n->kind = VAR_SET_VALUE;
 					n->name = "xmloption";
-					n->args = list_make1(makeStringConst($3 == XMLOPTION_DOCUMENT ? "DOCUMENT" : "CONTENT", @3));
+					n->args = list_make1(makeStringConst($3 == XMLOPTION_DOCUMENT ? "DOCUMENT" : "CONTENT", @3.begins, @3.length));
 					$$ = n;
 				}
 			/* Special syntaxes invented by PostgreSQL: */
@@ -1358,7 +1357,7 @@ set_rest:	/* Generic SET syntaxes: */
 					VariableSetStmt *n = makeNode(VariableSetStmt);
 					n->kind = VAR_SET_MULTI;
 					n->name = "TRANSACTION SNAPSHOT";
-					n->args = list_make1(makeStringConst($3, @3));
+					n->args = list_make1(makeStringConst($3, @3.begins, @3.length));
 					$$ = n;
 				}
 		;
@@ -1376,9 +1375,9 @@ var_list:	var_value								{ $$ = list_make1($1); }
 		;
 
 var_value:	opt_boolean_or_string
-				{ $$ = makeStringConst($1, @1); }
+				{ $$ = makeStringConst($1, @1.begins, @1.length); }
 			| NumericOnly
-				{ $$ = makeAConst($1, @1); }
+				{ $$ = makeAConst($1, @1.begins, @1.length); }
 		;
 
 iso_level:	READ UNCOMMITTED						{ $$ = "read uncommitted"; }
@@ -1410,11 +1409,11 @@ opt_boolean_or_string:
 zone_value:
 			Sconst
 				{
-					$$ = makeStringConst($1, @1);
+					$$ = makeStringConst($1, @1.begins, @1.length);
 				}
 			| IDENT
 				{
-					$$ = makeStringConst($1, @1);
+					$$ = makeStringConst($1, @1.begins, @1.length);
 				}
 			| ConstInterval Sconst opt_interval
 				{
@@ -1426,10 +1425,10 @@ zone_value:
 							ereport(ERROR,
 									(errcode(ERRCODE_SYNTAX_ERROR),
 									 errmsg("time zone interval must be HOUR or HOUR TO MINUTE"),
-									 parser_errposition(@3)));
+									 parser_errposition(@3.begins)));
 					}
 					t->typmods = $3;
-					$$ = makeStringConstCast($2, @2, t);
+					$$ = makeStringConstCast($2, @2.begins, @2.length, t);
 				}
 			| ConstInterval '(' Iconst ')' Sconst opt_interval
 				{
@@ -1441,20 +1440,20 @@ zone_value:
 							ereport(ERROR,
 									(errcode(ERRCODE_SYNTAX_ERROR),
 									 errmsg("time zone interval must be HOUR or HOUR TO MINUTE"),
-									 parser_errposition(@6)));
+									 parser_errposition(@6.begins)));
 						if (list_length($6) != 1)
 							ereport(ERROR,
 									(errcode(ERRCODE_SYNTAX_ERROR),
 									 errmsg("interval precision specified twice"),
-									 parser_errposition(@1)));
-						t->typmods = lappend($6, makeIntConst($3, @3));
+									 parser_errposition(@1.begins)));
+						t->typmods = lappend($6, makeIntConst($3, @3.begins, @3.length));
 					}
 					else
-						t->typmods = list_make2(makeIntConst(INTERVAL_FULL_RANGE, -1),
-												makeIntConst($3, @3));
-					$$ = makeStringConstCast($5, @5, t);
+						t->typmods = list_make2(makeIntConst(INTERVAL_FULL_RANGE, -1, -1),
+												makeIntConst($3, @3.begins, @3.length));
+					$$ = makeStringConstCast($5, @5.begins, @5.length, t);
 				}
-			| NumericOnly							{ $$ = makeAConst($1, @1); }
+			| NumericOnly							{ $$ = makeAConst($1, @1.begins, @1.length); }
 			| DEFAULT								{ $$ = NULL; }
 			| LOCAL									{ $$ = NULL; }
 		;
@@ -1966,7 +1965,7 @@ alter_table_cmd:
 				{
 					AlterTableCmd *n = makeNode(AlterTableCmd);
 					TypeName *def = makeTypeNameFromNameList($2);
-					def->location = @2;
+					def->location = @2.begins;
 					n->subtype = AT_AddOf;
 					n->def = (Node *) def;
 					$$ = (Node *)n;
@@ -2036,7 +2035,7 @@ opt_collate_clause:
 					CollateClause *n = makeNode(CollateClause);
 					n->arg = NULL;
 					n->collname = $2;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *) n;
 				}
 			| /* EMPTY */				{ $$ = NULL; }
@@ -2095,7 +2094,7 @@ AlterCompositeTypeStmt:
 					AlterTableStmt *n = makeNode(AlterTableStmt);
 
 					/* can't use qualified_name, sigh */
-					n->relation = makeRangeVarFromAnyName($3, @3, yyscanner);
+					n->relation = makeRangeVarFromAnyName($3, @3.begins, yyscanner);
 					n->cmds = $4;
 					n->relkind = OBJECT_TYPE;
 					$$ = (Node *)n;
@@ -2429,7 +2428,7 @@ CreateStmt:	CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
 					n->relation = $4;
 					n->tableElts = $7;
 					n->ofTypename = makeTypeNameFromNameList($6);
-					n->ofTypename->location = @6;
+					n->ofTypename->location = @6.begins;
 					n->constraints = NIL;
 					n->options = $8;
 					n->oncommit = $9;
@@ -2445,7 +2444,7 @@ CreateStmt:	CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
 					n->relation = $7;
 					n->tableElts = $10;
 					n->ofTypename = makeTypeNameFromNameList($9);
-					n->ofTypename->location = @9;
+					n->ofTypename->location = @9.begins;
 					n->constraints = NIL;
 					n->options = $11;
 					n->oncommit = $12;
@@ -2565,7 +2564,7 @@ ColConstraint:
 					Constraint *n = (Constraint *) $3;
 					Assert(IsA(n, Constraint));
 					n->conname = $2;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *) n;
 				}
 			| ColConstraintElem						{ $$ = $1; }
@@ -2580,7 +2579,7 @@ ColConstraint:
 					CollateClause *n = makeNode(CollateClause);
 					n->arg = NULL;
 					n->collname = $2;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *) n;
 				}
 		;
@@ -2605,21 +2604,21 @@ ColConstraintElem:
 				{
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_NOTNULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| NULL_P
 				{
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_NULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| UNIQUE opt_definition OptConsTableSpace
 				{
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_UNIQUE;
-					n->location = @1;
+					n->location = @1.begins;
 					n->keys = NULL;
 					n->options = $2;
 					n->indexname = NULL;
@@ -2630,7 +2629,7 @@ ColConstraintElem:
 				{
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_PRIMARY;
-					n->location = @1;
+					n->location = @1.begins;
 					n->keys = NULL;
 					n->options = $3;
 					n->indexname = NULL;
@@ -2641,7 +2640,7 @@ ColConstraintElem:
 				{
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_CHECK;
-					n->location = @1;
+					n->location = @1.begins;
 					n->raw_expr = $3;
 					n->cooked_expr = NULL;
 					$$ = (Node *)n;
@@ -2650,7 +2649,7 @@ ColConstraintElem:
 				{
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_DEFAULT;
-					n->location = @1;
+					n->location = @1.begins;
 					n->raw_expr = $2;
 					n->cooked_expr = NULL;
 					$$ = (Node *)n;
@@ -2659,7 +2658,7 @@ ColConstraintElem:
 				{
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_FOREIGN;
-					n->location = @1;
+					n->location = @1.begins;
 					n->pktable			= $2;
 					n->fk_attrs			= NIL;
 					n->pk_attrs			= $3;
@@ -2692,28 +2691,28 @@ ConstraintAttr:
 				{
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_ATTR_DEFERRABLE;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| NOT DEFERRABLE
 				{
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_ATTR_NOT_DEFERRABLE;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| INITIALLY DEFERRED
 				{
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_ATTR_DEFERRED;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| INITIALLY IMMEDIATE
 				{
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_ATTR_IMMEDIATE;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 		;
@@ -2763,7 +2762,7 @@ TableConstraint:
 					Constraint *n = (Constraint *) $3;
 					Assert(IsA(n, Constraint));
 					n->conname = $2;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *) n;
 				}
 			| ConstraintElem						{ $$ = $1; }
@@ -2774,10 +2773,10 @@ ConstraintElem:
 				{
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_CHECK;
-					n->location = @1;
+					n->location = @1.begins;
 					n->raw_expr = $3;
 					n->cooked_expr = NULL;
-					processCASbits($5, @5, "CHECK",
+					processCASbits($5, @5.begins, "CHECK",
 								   NULL, NULL, &n->skip_validation,
 								   yyscanner);
 					n->initially_valid = !n->skip_validation;
@@ -2788,12 +2787,12 @@ ConstraintElem:
 				{
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_UNIQUE;
-					n->location = @1;
+					n->location = @1.begins;
 					n->keys = $3;
 					n->options = $5;
 					n->indexname = NULL;
 					n->indexspace = $6;
-					processCASbits($7, @7, "UNIQUE",
+					processCASbits($7, @7.begins, "UNIQUE",
 								   &n->deferrable, &n->initdeferred, NULL,
 								   yyscanner);
 					$$ = (Node *)n;
@@ -2802,12 +2801,12 @@ ConstraintElem:
 				{
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_UNIQUE;
-					n->location = @1;
+					n->location = @1.begins;
 					n->keys = NIL;
 					n->options = NIL;
 					n->indexname = $2;
 					n->indexspace = NULL;
-					processCASbits($3, @3, "UNIQUE",
+					processCASbits($3, @3.begins, "UNIQUE",
 								   &n->deferrable, &n->initdeferred, NULL,
 								   yyscanner);
 					$$ = (Node *)n;
@@ -2817,12 +2816,12 @@ ConstraintElem:
 				{
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_PRIMARY;
-					n->location = @1;
+					n->location = @1.begins;
 					n->keys = $4;
 					n->options = $6;
 					n->indexname = NULL;
 					n->indexspace = $7;
-					processCASbits($8, @8, "PRIMARY KEY",
+					processCASbits($8, @8.begins, "PRIMARY KEY",
 								   &n->deferrable, &n->initdeferred, NULL,
 								   yyscanner);
 					$$ = (Node *)n;
@@ -2831,12 +2830,12 @@ ConstraintElem:
 				{
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_PRIMARY;
-					n->location = @1;
+					n->location = @1.begins;
 					n->keys = NIL;
 					n->options = NIL;
 					n->indexname = $3;
 					n->indexspace = NULL;
-					processCASbits($4, @4, "PRIMARY KEY",
+					processCASbits($4, @4.begins, "PRIMARY KEY",
 								   &n->deferrable, &n->initdeferred, NULL,
 								   yyscanner);
 					$$ = (Node *)n;
@@ -2847,14 +2846,14 @@ ConstraintElem:
 				{
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_EXCLUSION;
-					n->location = @1;
+					n->location = @1.begins;
 					n->access_method	= $2;
 					n->exclusions		= $4;
 					n->options			= $6;
 					n->indexname		= NULL;
 					n->indexspace		= $7;
 					n->where_clause		= $8;
-					processCASbits($9, @9, "EXCLUDE",
+					processCASbits($9, @9.begins, "EXCLUDE",
 								   &n->deferrable, &n->initdeferred, NULL,
 								   yyscanner);
 					$$ = (Node *)n;
@@ -2864,14 +2863,14 @@ ConstraintElem:
 				{
 					Constraint *n = makeNode(Constraint);
 					n->contype = CONSTR_FOREIGN;
-					n->location = @1;
+					n->location = @1.begins;
 					n->pktable			= $7;
 					n->fk_attrs			= $4;
 					n->pk_attrs			= $8;
 					n->fk_matchtype		= $9;
 					n->fk_upd_action	= (char) ($10 >> 8);
 					n->fk_del_action	= (char) ($10 & 0xFF);
-					processCASbits($11, @11, "FOREIGN KEY",
+					processCASbits($11, @11.begins, "FOREIGN KEY",
 								   &n->deferrable, &n->initdeferred,
 								   &n->skip_validation,
 								   yyscanner);
@@ -2905,7 +2904,7 @@ key_match:  MATCH FULL
 				ereport(ERROR,
 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 						 errmsg("MATCH PARTIAL not yet implemented"),
-						 parser_errposition(@1)));
+						 parser_errposition(@1.begins)));
 				$$ = FKCONSTR_MATCH_PARTIAL;
 			}
 		| MATCH SIMPLE
@@ -3028,7 +3027,7 @@ CreateAsStmt:
 					n->intoClause = $4;
 					/* Implement WITH NO DATA by forcing top-level LIMIT 0 */
 					if (!$7)
-						((SelectStmt *) $6)->limitCount = makeIntConst(0, -1);
+						((SelectStmt *) $6)->limitCount = makeIntConst(0, -1, -1);
 					$$ = $6;
 				}
 		;
@@ -4050,7 +4049,7 @@ CreateTrigStmt:
 					n->columns = (List *) lsecond($6);
 					n->whenClause = $14;
 					n->isconstraint  = TRUE;
-					processCASbits($10, @10, "TRIGGER",
+					processCASbits($10, @10.begins, "TRIGGER",
 								   &n->deferrable, &n->initdeferred, NULL,
 								   yyscanner);
 					n->constrrel = $9;
@@ -4171,14 +4170,14 @@ ConstraintAttributeSpec:
 						ereport(ERROR,
 								(errcode(ERRCODE_SYNTAX_ERROR),
 								 errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"),
-								 parser_errposition(@2)));
+								 parser_errposition(@2.begins)));
 					/* generic message for other conflicts */
 					if ((newspec & (CAS_NOT_DEFERRABLE | CAS_DEFERRABLE)) == (CAS_NOT_DEFERRABLE | CAS_DEFERRABLE) ||
 						(newspec & (CAS_INITIALLY_IMMEDIATE | CAS_INITIALLY_DEFERRED)) == (CAS_INITIALLY_IMMEDIATE | CAS_INITIALLY_DEFERRED))
 						ereport(ERROR,
 								(errcode(ERRCODE_SYNTAX_ERROR),
 								 errmsg("conflicting constraint properties"),
-								 parser_errposition(@2)));
+								 parser_errposition(@2.begins)));
 					$$ = newspec;
 				}
 		;
@@ -4232,7 +4231,7 @@ CreateAssertStmt:
 					n->trigname = $3;
 					n->args = list_make1($6);
 					n->isconstraint  = TRUE;
-					processCASbits($8, @8, "ASSERTION",
+					processCASbits($8, @8.begins, "ASSERTION",
 								   &n->deferrable, &n->initdeferred, NULL,
 								   yyscanner);
 
@@ -4325,7 +4324,7 @@ DefineStmt:
 					CompositeTypeStmt *n = makeNode(CompositeTypeStmt);
 
 					/* can't use qualified_name, sigh */
-					n->typevar = makeRangeVarFromAnyName($3, @3, yyscanner);
+					n->typevar = makeRangeVarFromAnyName($3, @3.begins, yyscanner);
 					n->coldeflist = $6;
 					$$ = (Node *)n;
 				}
@@ -4599,7 +4598,7 @@ opt_recheck:	RECHECK
 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 							 errmsg("RECHECK is no longer required"),
 							 errhint("Update your data type."),
-							 parser_errposition(@1)));
+							 parser_errposition(@1.begins)));
 					$$ = TRUE;
 				}
 			| /*EMPTY*/						{ $$ = FALSE; }
@@ -5841,7 +5840,7 @@ CreateFunctionStmt:
 					n->funcname = $4;
 					n->parameters = mergeTableFuncParameters($5, $9);
 					n->returnType = TableFuncTypeName($9);
-					n->returnType->location = @7;
+					n->returnType->location = @7.begins;
 					n->options = $11;
 					n->withClause = $12;
 					$$ = (Node *)n;
@@ -5982,14 +5981,14 @@ func_type:	Typename								{ $$ = $1; }
 				{
 					$$ = makeTypeNameFromNameList(lcons(makeString($1), $2));
 					$$->pct_type = true;
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 			| SETOF type_function_name attrs '%' TYPE_P
 				{
 					$$ = makeTypeNameFromNameList(lcons(makeString($2), $3));
 					$$->pct_type = true;
 					$$->setof = TRUE;
-					$$->location = @2;
+					$$->location = @2.begins;
 				}
 		;
 
@@ -6246,7 +6245,7 @@ oper_argtypes:
 						   (errcode(ERRCODE_SYNTAX_ERROR),
 							errmsg("missing argument"),
 							errhint("Use NONE to denote the missing argument of a unary operator."),
-							parser_errposition(@3)));
+							parser_errposition(@3.begins)));
 				}
 			| '(' Typename ',' Typename ')'
 					{ $$ = list_make2($2, $4); }
@@ -6660,7 +6659,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
 					RenameStmt *n = makeNode(RenameStmt);
 					n->renameType = OBJECT_ATTRIBUTE;
 					n->relationType = OBJECT_TYPE;
-					n->relation = makeRangeVarFromAnyName($3, @3, yyscanner);
+					n->relation = makeRangeVarFromAnyName($3, @3.begins, yyscanner);
 					n->subname = $6;
 					n->newname = $8;
 					n->behavior = $9;
@@ -7255,19 +7254,19 @@ opt_transaction:	WORK							{}
 transaction_mode_item:
 			ISOLATION LEVEL iso_level
 					{ $$ = makeDefElem("transaction_isolation",
-									   makeStringConst($3, @3)); }
+									   makeStringConst($3, @3.begins, @3.length)); }
 			| READ ONLY
 					{ $$ = makeDefElem("transaction_read_only",
-									   makeIntConst(TRUE, @1)); }
+									   makeIntConst(TRUE, @1.begins, @1.length)); }
 			| READ WRITE
 					{ $$ = makeDefElem("transaction_read_only",
-									   makeIntConst(FALSE, @1)); }
+									   makeIntConst(FALSE, @1.begins, @1.length)); }
 			| DEFERRABLE
 					{ $$ = makeDefElem("transaction_deferrable",
-									   makeIntConst(TRUE, @1)); }
+									   makeIntConst(TRUE, @1.begins, @1.length)); }
 			| NOT DEFERRABLE
 					{ $$ = makeDefElem("transaction_deferrable",
-									   makeIntConst(FALSE, @1)); }
+									   makeIntConst(FALSE, @1.begins, @1.length)); }
 		;
 
 /* Syntax with commas is SQL-spec, without commas is Postgres historical */
@@ -8117,7 +8116,7 @@ insert_column_item:
 					$$->name = $1;
 					$$->indirection = check_indirection($2, yyscanner);
 					$$->val = NULL;
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 		;
 
@@ -8247,7 +8246,7 @@ multiple_set_clause:
 						ereport(ERROR,
 								(errcode(ERRCODE_SYNTAX_ERROR),
 								 errmsg("number of columns does not match number of values"),
-								 parser_errposition(@1)));
+								 parser_errposition(@1.begins)));
 					forboth(col_cell, $2, val_cell, $5)
 					{
 						ResTarget *res_col = (ResTarget *) lfirst(col_cell);
@@ -8267,7 +8266,7 @@ set_target:
 					$$->name = $1;
 					$$->indirection = check_indirection($2, yyscanner);
 					$$->val = NULL;	/* upper production sets this */
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 		;
 
@@ -8524,14 +8523,14 @@ with_clause:
 				$$ = makeNode(WithClause);
 				$$->ctes = $2;
 				$$->recursive = false;
-				$$->location = @1;
+				$$->location = @1.begins;
 			}
 		| WITH RECURSIVE cte_list
 			{
 				$$ = makeNode(WithClause);
 				$$->ctes = $3;
 				$$->recursive = true;
-				$$->location = @1;
+				$$->location = @1.begins;
 			}
 		;
 
@@ -8546,7 +8545,7 @@ common_table_expr:  name opt_name_list AS '(' PreparableStmt ')'
 				n->ctename = $1;
 				n->aliascolnames = $2;
 				n->ctequery = $5;
-				n->location = @1;
+				n->location = @1.begins;
 				$$ = (Node *) n;
 			}
 		;
@@ -8662,7 +8661,7 @@ sortby:		a_expr USING qual_all_Op opt_nulls_order
 					$$->sortby_dir = SORTBY_USING;
 					$$->sortby_nulls = $4;
 					$$->useOp = $3;
-					$$->location = @3;
+					$$->location = @3.begins;
 				}
 			| a_expr opt_asc_desc opt_nulls_order
 				{
@@ -8698,7 +8697,7 @@ limit_clause:
 							(errcode(ERRCODE_SYNTAX_ERROR),
 							 errmsg("LIMIT #,# syntax is not supported"),
 							 errhint("Use separate LIMIT and OFFSET clauses."),
-							 parser_errposition(@1)));
+							 parser_errposition(@1.begins)));
 				}
 			/* SQL:2008 syntax */
 			| FETCH first_or_next opt_select_fetch_first_value row_or_rows ONLY
@@ -8718,7 +8717,7 @@ select_limit_value:
 			| ALL
 				{
 					/* LIMIT ALL is represented as a NULL constant */
-					$$ = makeNullAConst(@1);
+					$$ = makeNullAConst(@1.begins, @1.length);
 				}
 		;
 
@@ -8733,9 +8732,9 @@ select_offset_value:
  * default to 1.
  */
 opt_select_fetch_first_value:
-			SignedIconst						{ $$ = makeIntConst($1, @1); }
+			SignedIconst						{ $$ = makeIntConst($1, @1.begins, @1.length); }
 			| '(' a_expr ')'					{ $$ = $2; }
-			| /*EMPTY*/							{ $$ = makeIntConst(1, -1); }
+			| /*EMPTY*/							{ $$ = makeIntConst(1, -1, -1); }
 		;
 
 /*
@@ -8917,13 +8916,13 @@ table_ref:	relation_expr
 								(errcode(ERRCODE_SYNTAX_ERROR),
 								 errmsg("VALUES in FROM must have an alias"),
 								 errhint("For example, FROM (VALUES ...) [AS] foo."),
-								 parser_errposition(@1)));
+								 parser_errposition(@1.begins)));
 					else
 						ereport(ERROR,
 								(errcode(ERRCODE_SYNTAX_ERROR),
 								 errmsg("subquery in FROM must have an alias"),
 								 errhint("For example, FROM (SELECT ...) [AS] foo."),
-								 parser_errposition(@1)));
+								 parser_errposition(@1.begins)));
 					$$ = NULL;
 				}
 			| select_with_parens alias_clause
@@ -9283,12 +9282,12 @@ SimpleTypename:
 							ereport(ERROR,
 									(errcode(ERRCODE_SYNTAX_ERROR),
 									 errmsg("interval precision specified twice"),
-									 parser_errposition(@1)));
-						$$->typmods = lappend($5, makeIntConst($3, @3));
+									 parser_errposition(@1.begins)));
+						$$->typmods = lappend($5, makeIntConst($3, @3.begins, @3.length));
 					}
 					else
-						$$->typmods = list_make2(makeIntConst(INTERVAL_FULL_RANGE, -1),
-												 makeIntConst($3, @3));
+						$$->typmods = list_make2(makeIntConst(INTERVAL_FULL_RANGE, -1, -1),
+												 makeIntConst($3, @3.begins, @3.length));
 				}
 		;
 
@@ -9322,13 +9321,13 @@ GenericType:
 				{
 					$$ = makeTypeName($1);
 					$$->typmods = $2;
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 			| type_function_name attrs opt_type_modifiers
 				{
 					$$ = makeTypeNameFromNameList(lcons(makeString($1), $2));
 					$$->typmods = $3;
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 		;
 
@@ -9342,60 +9341,60 @@ opt_type_modifiers: '(' expr_list ')'				{ $$ = $2; }
 Numeric:	INT_P
 				{
 					$$ = SystemTypeName("int4");
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 			| INTEGER
 				{
 					$$ = SystemTypeName("int4");
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 			| SMALLINT
 				{
 					$$ = SystemTypeName("int2");
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 			| BIGINT
 				{
 					$$ = SystemTypeName("int8");
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 			| REAL
 				{
 					$$ = SystemTypeName("float4");
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 			| FLOAT_P opt_float
 				{
 					$$ = $2;
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 			| DOUBLE_P PRECISION
 				{
 					$$ = SystemTypeName("float8");
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 			| DECIMAL_P opt_type_modifiers
 				{
 					$$ = SystemTypeName("numeric");
 					$$->typmods = $2;
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 			| DEC opt_type_modifiers
 				{
 					$$ = SystemTypeName("numeric");
 					$$->typmods = $2;
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 			| NUMERIC opt_type_modifiers
 				{
 					$$ = SystemTypeName("numeric");
 					$$->typmods = $2;
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 			| BOOLEAN_P
 				{
 					$$ = SystemTypeName("bool");
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 		;
 
@@ -9409,7 +9408,7 @@ opt_float:	'(' Iconst ')'
 						ereport(ERROR,
 								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 								 errmsg("precision for type float must be at least 1 bit"),
-								 parser_errposition(@2)));
+								 parser_errposition(@2.begins)));
 					else if ($2 <= 24)
 						$$ = SystemTypeName("float4");
 					else if ($2 <= 53)
@@ -9418,7 +9417,7 @@ opt_float:	'(' Iconst ')'
 						ereport(ERROR,
 								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 								 errmsg("precision for type float must be less than 54 bits"),
-								 parser_errposition(@2)));
+								 parser_errposition(@2.begins)));
 				}
 			| /*EMPTY*/
 				{
@@ -9461,7 +9460,7 @@ BitWithLength:
 					typname = $2 ? "varbit" : "bit";
 					$$ = SystemTypeName(typname);
 					$$->typmods = $4;
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 		;
 
@@ -9476,9 +9475,9 @@ BitWithoutLength:
 					else
 					{
 						$$ = SystemTypeName("bit");
-						$$->typmods = list_make1(makeIntConst(1, -1));
+						$$->typmods = list_make1(makeIntConst(1, -1, -1));
 					}
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 		;
 
@@ -9528,8 +9527,8 @@ CharacterWithLength:  character '(' Iconst ')' opt_charset
 					}
 
 					$$ = SystemTypeName($1);
-					$$->typmods = list_make1(makeIntConst($3, @3));
-					$$->location = @1;
+					$$->typmods = list_make1(makeIntConst($3, @3.begins, @3.length));
+					$$->location = @1.begins;
 				}
 		;
 
@@ -9550,9 +9549,9 @@ CharacterWithoutLength:	 character opt_charset
 
 					/* char defaults to char(1), varchar to no limit */
 					if (strcmp($1, "bpchar") == 0)
-						$$->typmods = list_make1(makeIntConst(1, -1));
+						$$->typmods = list_make1(makeIntConst(1, -1, -1));
 
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 		;
 
@@ -9590,8 +9589,8 @@ ConstDatetime:
 						$$ = SystemTypeName("timestamptz");
 					else
 						$$ = SystemTypeName("timestamp");
-					$$->typmods = list_make1(makeIntConst($3, @3));
-					$$->location = @1;
+					$$->typmods = list_make1(makeIntConst($3, @3.begins, @3.length));
+					$$->location = @1.begins;
 				}
 			| TIMESTAMP opt_timezone
 				{
@@ -9599,7 +9598,7 @@ ConstDatetime:
 						$$ = SystemTypeName("timestamptz");
 					else
 						$$ = SystemTypeName("timestamp");
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 			| TIME '(' Iconst ')' opt_timezone
 				{
@@ -9607,8 +9606,8 @@ ConstDatetime:
 						$$ = SystemTypeName("timetz");
 					else
 						$$ = SystemTypeName("time");
-					$$->typmods = list_make1(makeIntConst($3, @3));
-					$$->location = @1;
+					$$->typmods = list_make1(makeIntConst($3, @3.begins, @3.length));
+					$$->location = @1.begins;
 				}
 			| TIME opt_timezone
 				{
@@ -9616,7 +9615,7 @@ ConstDatetime:
 						$$ = SystemTypeName("timetz");
 					else
 						$$ = SystemTypeName("time");
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 		;
 
@@ -9624,7 +9623,7 @@ ConstInterval:
 			INTERVAL
 				{
 					$$ = SystemTypeName("interval");
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 		;
 
@@ -9636,32 +9635,32 @@ opt_timezone:
 
 opt_interval:
 			YEAR_P
-				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(YEAR), @1)); }
+				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(YEAR), @1.begins, @1.length)); }
 			| MONTH_P
-				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(MONTH), @1)); }
+				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(MONTH), @1.begins, @1.length)); }
 			| DAY_P
-				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(DAY), @1)); }
+				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(DAY), @1.begins, @1.length)); }
 			| HOUR_P
-				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(HOUR), @1)); }
+				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(HOUR), @1.begins, @1.length)); }
 			| MINUTE_P
-				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(MINUTE), @1)); }
+				{ $$ = list_make1(makeIntConst(INTERVAL_MASK(MINUTE), @1.begins, @1.length)); }
 			| interval_second
 				{ $$ = $1; }
 			| YEAR_P TO MONTH_P
 				{
 					$$ = list_make1(makeIntConst(INTERVAL_MASK(YEAR) |
-												 INTERVAL_MASK(MONTH), @1));
+												 INTERVAL_MASK(MONTH), @1.begins, @1.length));
 				}
 			| DAY_P TO HOUR_P
 				{
 					$$ = list_make1(makeIntConst(INTERVAL_MASK(DAY) |
-												 INTERVAL_MASK(HOUR), @1));
+												 INTERVAL_MASK(HOUR), @1.begins, @1.length));
 				}
 			| DAY_P TO MINUTE_P
 				{
 					$$ = list_make1(makeIntConst(INTERVAL_MASK(DAY) |
 												 INTERVAL_MASK(HOUR) |
-												 INTERVAL_MASK(MINUTE), @1));
+												 INTERVAL_MASK(MINUTE), @1.begins, @1.length));
 				}
 			| DAY_P TO interval_second
 				{
@@ -9669,25 +9668,25 @@ opt_interval:
 					linitial($$) = makeIntConst(INTERVAL_MASK(DAY) |
 												INTERVAL_MASK(HOUR) |
 												INTERVAL_MASK(MINUTE) |
-												INTERVAL_MASK(SECOND), @1);
+												INTERVAL_MASK(SECOND), @1.begins, @1.length);
 				}
 			| HOUR_P TO MINUTE_P
 				{
 					$$ = list_make1(makeIntConst(INTERVAL_MASK(HOUR) |
-												 INTERVAL_MASK(MINUTE), @1));
+												 INTERVAL_MASK(MINUTE), @1.begins, @1.length));
 				}
 			| HOUR_P TO interval_second
 				{
 					$$ = $3;
 					linitial($$) = makeIntConst(INTERVAL_MASK(HOUR) |
 												INTERVAL_MASK(MINUTE) |
-												INTERVAL_MASK(SECOND), @1);
+												INTERVAL_MASK(SECOND), @1.begins, @1.length);
 				}
 			| MINUTE_P TO interval_second
 				{
 					$$ = $3;
 					linitial($$) = makeIntConst(INTERVAL_MASK(MINUTE) |
-												INTERVAL_MASK(SECOND), @1);
+												INTERVAL_MASK(SECOND), @1.begins, @1.length);
 				}
 			| /*EMPTY*/
 				{ $$ = NIL; }
@@ -9696,12 +9695,12 @@ opt_interval:
 interval_second:
 			SECOND_P
 				{
-					$$ = list_make1(makeIntConst(INTERVAL_MASK(SECOND), @1));
+					$$ = list_make1(makeIntConst(INTERVAL_MASK(SECOND), @1.begins, @1.length));
 				}
 			| SECOND_P '(' Iconst ')'
 				{
-					$$ = list_make2(makeIntConst(INTERVAL_MASK(SECOND), @1),
-									makeIntConst($3, @3));
+					$$ = list_make2(makeIntConst(INTERVAL_MASK(SECOND), @1.begins, @1.length),
+									makeIntConst($3, @3.begins, @3.length));
 				}
 		;
 
@@ -9730,13 +9729,13 @@ interval_second:
  */
 a_expr:		c_expr									{ $$ = $1; }
 			| a_expr TYPECAST Typename
-					{ $$ = makeTypeCast($1, $3, @2); }
+					{ $$ = makeTypeCast($1, $3, @2.begins, @2.length); }
 			| a_expr COLLATE any_name
 				{
 					CollateClause *n = makeNode(CollateClause);
 					n->arg = $1;
 					n->collname = $3;
-					n->location = @2;
+					n->location = @2.begins;
 					$$ = (Node *) n;
 				}
 			| a_expr AT TIME ZONE a_expr			%prec AT
@@ -9749,7 +9748,7 @@ a_expr:		c_expr									{ $$ = $1; }
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @2;
+					n->location = @2.begins;
 					$$ = (Node *) n;
 				}
 		/*
@@ -9762,44 +9761,44 @@ a_expr:		c_expr									{ $$ = $1; }
 		 * also to b_expr and to the MathOp list above.
 		 */
 			| '+' a_expr					%prec UMINUS
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", NULL, $2, @1); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", NULL, $2, @1.begins); }
 			| '-' a_expr					%prec UMINUS
-				{ $$ = doNegate($2, @1); }
+				{ $$ = doNegate($2, @1.begins, @1.length); }
 			| a_expr '+' a_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", $1, $3, @2.begins); }
 			| a_expr '-' a_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "-", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "-", $1, $3, @2.begins); }
 			| a_expr '*' a_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "*", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "*", $1, $3, @2.begins); }
 			| a_expr '/' a_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "/", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "/", $1, $3, @2.begins); }
 			| a_expr '%' a_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "%", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "%", $1, $3, @2.begins); }
 			| a_expr '^' a_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "^", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "^", $1, $3, @2.begins); }
 			| a_expr '<' a_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $3, @2.begins); }
 			| a_expr '>' a_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $3, @2.begins); }
 			| a_expr '=' a_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", $1, $3, @2.begins); }
 
 			| a_expr qual_Op a_expr				%prec Op
-				{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); }
+				{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2.begins); }
 			| qual_Op a_expr					%prec Op
-				{ $$ = (Node *) makeA_Expr(AEXPR_OP, $1, NULL, $2, @1); }
+				{ $$ = (Node *) makeA_Expr(AEXPR_OP, $1, NULL, $2, @1.begins); }
 			| a_expr qual_Op					%prec POSTFIXOP
-				{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, NULL, @2); }
+				{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, NULL, @2.begins); }
 
 			| a_expr AND a_expr
-				{ $$ = (Node *) makeA_Expr(AEXPR_AND, NIL, $1, $3, @2); }
+				{ $$ = (Node *) makeA_Expr(AEXPR_AND, NIL, $1, $3, @2.begins); }
 			| a_expr OR a_expr
-				{ $$ = (Node *) makeA_Expr(AEXPR_OR, NIL, $1, $3, @2); }
+				{ $$ = (Node *) makeA_Expr(AEXPR_OR, NIL, $1, $3, @2.begins); }
 			| NOT a_expr
-				{ $$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL, $2, @1); }
+				{ $$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL, $2, @1.begins); }
 
 			| a_expr LIKE a_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~", $1, $3, @2.begins); }
 			| a_expr LIKE a_expr ESCAPE a_expr
 				{
 					FuncCall *n = makeNode(FuncCall);
@@ -9810,11 +9809,11 @@ a_expr:		c_expr									{ $$ = $1; }
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @2;
-					$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~", $1, (Node *) n, @2);
+					n->location = @2.begins;
+					$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~", $1, (Node *) n, @2.begins);
 				}
 			| a_expr NOT LIKE a_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~", $1, $4, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~", $1, $4, @2.begins); }
 			| a_expr NOT LIKE a_expr ESCAPE a_expr
 				{
 					FuncCall *n = makeNode(FuncCall);
@@ -9825,11 +9824,11 @@ a_expr:		c_expr									{ $$ = $1; }
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @2;
-					$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~", $1, (Node *) n, @2);
+					n->location = @2.begins;
+					$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~", $1, (Node *) n, @2.begins);
 				}
 			| a_expr ILIKE a_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~*", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~*", $1, $3, @2.begins); }
 			| a_expr ILIKE a_expr ESCAPE a_expr
 				{
 					FuncCall *n = makeNode(FuncCall);
@@ -9840,11 +9839,11 @@ a_expr:		c_expr									{ $$ = $1; }
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @2;
-					$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~*", $1, (Node *) n, @2);
+					n->location = @2.begins;
+					$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~*", $1, (Node *) n, @2.begins);
 				}
 			| a_expr NOT ILIKE a_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~*", $1, $4, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~*", $1, $4, @2.begins); }
 			| a_expr NOT ILIKE a_expr ESCAPE a_expr
 				{
 					FuncCall *n = makeNode(FuncCall);
@@ -9855,22 +9854,22 @@ a_expr:		c_expr									{ $$ = $1; }
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @2;
-					$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~*", $1, (Node *) n, @2);
+					n->location = @2.begins;
+					$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~*", $1, (Node *) n, @2.begins);
 				}
 
 			| a_expr SIMILAR TO a_expr				%prec SIMILAR
 				{
 					FuncCall *n = makeNode(FuncCall);
 					n->funcname = SystemFuncName("similar_escape");
-					n->args = list_make2($4, makeNullAConst(-1));
+					n->args = list_make2($4, makeNullAConst(-1, -1));
 					n->agg_order = NIL;
 					n->agg_star = FALSE;
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @2;
-					$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~", $1, (Node *) n, @2);
+					n->location = @2.begins;
+					$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~", $1, (Node *) n, @2.begins);
 				}
 			| a_expr SIMILAR TO a_expr ESCAPE a_expr
 				{
@@ -9882,21 +9881,21 @@ a_expr:		c_expr									{ $$ = $1; }
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @2;
-					$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~", $1, (Node *) n, @2);
+					n->location = @2.begins;
+					$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~", $1, (Node *) n, @2.begins);
 				}
 			| a_expr NOT SIMILAR TO a_expr			%prec SIMILAR
 				{
 					FuncCall *n = makeNode(FuncCall);
 					n->funcname = SystemFuncName("similar_escape");
-					n->args = list_make2($5, makeNullAConst(-1));
+					n->args = list_make2($5, makeNullAConst(-1, -1));
 					n->agg_order = NIL;
 					n->agg_star = FALSE;
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @2;
-					$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~", $1, (Node *) n, @2);
+					n->location = @2.begins;
+					$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~", $1, (Node *) n, @2.begins);
 				}
 			| a_expr NOT SIMILAR TO a_expr ESCAPE a_expr
 				{
@@ -9908,8 +9907,8 @@ a_expr:		c_expr									{ $$ = $1; }
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @2;
-					$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~", $1, (Node *) n, @2);
+					n->location = @2.begins;
+					$$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~", $1, (Node *) n, @2.begins);
 				}
 
 			/* NullTest clause
@@ -9951,7 +9950,7 @@ a_expr:		c_expr									{ $$ = $1; }
 				}
 			| row OVERLAPS row
 				{
-					$$ = (Node *)makeOverlaps($1, $3, @2, yyscanner);
+					$$ = (Node *)makeOverlaps($1, $3, @2.begins, yyscanner);
 				}
 			| a_expr IS TRUE_P							%prec IS
 				{
@@ -9997,23 +9996,23 @@ a_expr:		c_expr									{ $$ = $1; }
 				}
 			| a_expr IS DISTINCT FROM a_expr			%prec IS
 				{
-					$$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5, @2);
+					$$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5, @2.begins);
 				}
 			| a_expr IS NOT DISTINCT FROM a_expr		%prec IS
 				{
 					$$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL,
 									(Node *) makeSimpleA_Expr(AEXPR_DISTINCT,
-															  "=", $1, $6, @2),
-											 @2);
+															  "=", $1, $6, @2.begins),
+											 @2.begins);
 
 				}
 			| a_expr IS OF '(' type_list ')'			%prec IS
 				{
-					$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "=", $1, (Node *) $5, @2);
+					$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "=", $1, (Node *) $5, @2.begins);
 				}
 			| a_expr IS NOT OF '(' type_list ')'		%prec IS
 				{
-					$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "<>", $1, (Node *) $6, @2);
+					$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "<>", $1, (Node *) $6, @2.begins);
 				}
 			/*
 			 *	Ideally we would not use hard-wired operators below but
@@ -10024,42 +10023,43 @@ a_expr:		c_expr									{ $$ = $1; }
 			| a_expr BETWEEN opt_asymmetric b_expr AND b_expr		%prec BETWEEN
 				{
 					$$ = (Node *) makeA_Expr(AEXPR_AND, NIL,
-						(Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $4, @2),
-						(Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $6, @2),
-											 @2);
+						(Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $4, @2.begins),
+						(Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $6, @2.begins),
+											 @2.begins);
 				}
 			| a_expr NOT BETWEEN opt_asymmetric b_expr AND b_expr	%prec BETWEEN
 				{
 					$$ = (Node *) makeA_Expr(AEXPR_OR, NIL,
-						(Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $5, @2),
-						(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $7, @2),
-											 @2);
+						(Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $5, @2.begins),
+						(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $7, @2.begins),
+											 @2.begins);
 				}
 			| a_expr BETWEEN SYMMETRIC b_expr AND b_expr			%prec BETWEEN
 				{
 					$$ = (Node *) makeA_Expr(AEXPR_OR, NIL,
 						(Node *) makeA_Expr(AEXPR_AND, NIL,
-							(Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $4, @2),
-							(Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $6, @2),
-											@2),
+
+						    (Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $4, @2.begins),
+						    (Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $6, @2.begins),
+											@2.begins),
 						(Node *) makeA_Expr(AEXPR_AND, NIL,
-							(Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $6, @2),
-							(Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $4, @2),
-											@2),
-											 @2);
+						    (Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $6, @2.begins),
+						    (Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $4, @2.begins),
+											@2.begins),
+											 @2.begins);
 				}
 			| a_expr NOT BETWEEN SYMMETRIC b_expr AND b_expr		%prec BETWEEN
 				{
 					$$ = (Node *) makeA_Expr(AEXPR_AND, NIL,
 						(Node *) makeA_Expr(AEXPR_OR, NIL,
-							(Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $5, @2),
-							(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $7, @2),
-											@2),
+						    (Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $5, @2.begins),
+						    (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $7, @2.begins),
+											@2.begins),
 						(Node *) makeA_Expr(AEXPR_OR, NIL,
-							(Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $7, @2),
-							(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $5, @2),
-											@2),
-											 @2);
+						    (Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $7, @2.begins),
+						    (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $5, @2.begins),
+											@2.begins),
+											 @2.begins);
 				}
 			| a_expr IN_P in_expr
 				{
@@ -10071,13 +10071,13 @@ a_expr:		c_expr									{ $$ = $1; }
 						n->subLinkType = ANY_SUBLINK;
 						n->testexpr = $1;
 						n->operName = list_make1(makeString("="));
-						n->location = @2;
+						n->location = @2.begins;
 						$$ = (Node *)n;
 					}
 					else
 					{
 						/* generate scalar IN expression */
-						$$ = (Node *) makeSimpleA_Expr(AEXPR_IN, "=", $1, $3, @2);
+						$$ = (Node *) makeSimpleA_Expr(AEXPR_IN, "=", $1, $3, @2.begins);
 					}
 				}
 			| a_expr NOT IN_P in_expr
@@ -10091,14 +10091,14 @@ a_expr:		c_expr									{ $$ = $1; }
 						n->subLinkType = ANY_SUBLINK;
 						n->testexpr = $1;
 						n->operName = list_make1(makeString("="));
-						n->location = @3;
+						n->location = @3.begins;
 						/* Stick a NOT on top */
-						$$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL, (Node *) n, @2);
+						$$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL, (Node *) n, @2.begins);
 					}
 					else
 					{
 						/* generate scalar NOT IN expression */
-						$$ = (Node *) makeSimpleA_Expr(AEXPR_IN, "<>", $1, $4, @2);
+						$$ = (Node *) makeSimpleA_Expr(AEXPR_IN, "<>", $1, $4, @2.begins);
 					}
 				}
 			| a_expr subquery_Op sub_type select_with_parens	%prec Op
@@ -10108,15 +10108,15 @@ a_expr:		c_expr									{ $$ = $1; }
 					n->testexpr = $1;
 					n->operName = $2;
 					n->subselect = $4;
-					n->location = @2;
+					n->location = @2.begins;
 					$$ = (Node *)n;
 				}
 			| a_expr subquery_Op sub_type '(' a_expr ')'		%prec Op
 				{
 					if ($3 == ANY_SUBLINK)
-						$$ = (Node *) makeA_Expr(AEXPR_OP_ANY, $2, $1, $5, @2);
+						$$ = (Node *) makeA_Expr(AEXPR_OP_ANY, $2, $1, $5, @2.begins);
 					else
-						$$ = (Node *) makeA_Expr(AEXPR_OP_ALL, $2, $1, $5, @2);
+						$$ = (Node *) makeA_Expr(AEXPR_OP_ALL, $2, $1, $5, @2.begins);
 				}
 			| UNIQUE select_with_parens
 				{
@@ -10132,19 +10132,19 @@ a_expr:		c_expr									{ $$ = $1; }
 					ereport(ERROR,
 							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 							 errmsg("UNIQUE predicate is not yet implemented"),
-							 parser_errposition(@1)));
+							 parser_errposition(@1.begins)));
 				}
 			| a_expr IS DOCUMENT_P					%prec IS
 				{
 					$$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL,
-									 list_make1($1), @2);
+									 list_make1($1), @2.begins);
 				}
 			| a_expr IS NOT DOCUMENT_P				%prec IS
 				{
 					$$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL,
 											 makeXmlExpr(IS_DOCUMENT, NULL, NIL,
-														 list_make1($1), @2),
-											 @2);
+														 list_make1($1), @2.begins),
+											 @2.begins);
 				}
 		;
 
@@ -10160,63 +10160,63 @@ a_expr:		c_expr									{ $$ = $1; }
 b_expr:		c_expr
 				{ $$ = $1; }
 			| b_expr TYPECAST Typename
-				{ $$ = makeTypeCast($1, $3, @2); }
+				{ $$ = makeTypeCast($1, $3, @2.begins, @2.length); }
 			| '+' b_expr					%prec UMINUS
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", NULL, $2, @1); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", NULL, $2, @1.begins); }
 			| '-' b_expr					%prec UMINUS
-				{ $$ = doNegate($2, @1); }
+				{ $$ = doNegate($2, @1.begins, @1.length); }
 			| b_expr '+' b_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", $1, $3, @2.begins); }
 			| b_expr '-' b_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "-", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "-", $1, $3, @2.begins); }
 			| b_expr '*' b_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "*", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "*", $1, $3, @2.begins); }
 			| b_expr '/' b_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "/", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "/", $1, $3, @2.begins); }
 			| b_expr '%' b_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "%", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "%", $1, $3, @2.begins); }
 			| b_expr '^' b_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "^", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "^", $1, $3, @2.begins); }
 			| b_expr '<' b_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $3, @2.begins); }
 			| b_expr '>' b_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $3, @2.begins); }
 			| b_expr '=' b_expr
-				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", $1, $3, @2); }
+				{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", $1, $3, @2.begins); }
 			| b_expr qual_Op b_expr				%prec Op
-				{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); }
+				{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2.begins); }
 			| qual_Op b_expr					%prec Op
-				{ $$ = (Node *) makeA_Expr(AEXPR_OP, $1, NULL, $2, @1); }
+				{ $$ = (Node *) makeA_Expr(AEXPR_OP, $1, NULL, $2, @1.begins); }
 			| b_expr qual_Op					%prec POSTFIXOP
-				{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, NULL, @2); }
+				{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, NULL, @2.begins); }
 			| b_expr IS DISTINCT FROM b_expr		%prec IS
 				{
-					$$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5, @2);
+					$$ = (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $5, @2.begins);
 				}
 			| b_expr IS NOT DISTINCT FROM b_expr	%prec IS
 				{
 					$$ = (Node *) makeA_Expr(AEXPR_NOT, NIL,
-						NULL, (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $6, @2), @2);
+						NULL, (Node *) makeSimpleA_Expr(AEXPR_DISTINCT, "=", $1, $6, @2.begins), @2.begins);
 				}
 			| b_expr IS OF '(' type_list ')'		%prec IS
 				{
-					$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "=", $1, (Node *) $5, @2);
+					$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "=", $1, (Node *) $5, @2.begins);
 				}
 			| b_expr IS NOT OF '(' type_list ')'	%prec IS
 				{
-					$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "<>", $1, (Node *) $6, @2);
+					$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "<>", $1, (Node *) $6, @2.begins);
 				}
 			| b_expr IS DOCUMENT_P					%prec IS
 				{
 					$$ = makeXmlExpr(IS_DOCUMENT, NULL, NIL,
-									 list_make1($1), @2);
+									 list_make1($1), @2.begins);
 				}
 			| b_expr IS NOT DOCUMENT_P				%prec IS
 				{
 					$$ = (Node *) makeA_Expr(AEXPR_NOT, NIL, NULL,
 											 makeXmlExpr(IS_DOCUMENT, NULL, NIL,
-														 list_make1($1), @2),
-											 @2);
+														 list_make1($1), @2.begins),
+											 @2.begins);
 				}
 		;
 
@@ -10234,7 +10234,7 @@ c_expr:		columnref								{ $$ = $1; }
 				{
 					ParamRef *p = makeNode(ParamRef);
 					p->number = $1;
-					p->location = @1;
+					p->location = @1.begins;
 					if ($2)
 					{
 						A_Indirection *n = makeNode(A_Indirection);
@@ -10268,7 +10268,7 @@ c_expr:		columnref								{ $$ = $1; }
 					n->testexpr = NULL;
 					n->operName = NIL;
 					n->subselect = $1;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| EXISTS select_with_parens
@@ -10278,7 +10278,7 @@ c_expr:		columnref								{ $$ = $1; }
 					n->testexpr = NULL;
 					n->operName = NIL;
 					n->subselect = $2;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| ARRAY select_with_parens
@@ -10288,7 +10288,7 @@ c_expr:		columnref								{ $$ = $1; }
 					n->testexpr = NULL;
 					n->operName = NIL;
 					n->subselect = $2;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| ARRAY array_expr
@@ -10296,7 +10296,7 @@ c_expr:		columnref								{ $$ = $1; }
 					A_ArrayExpr *n = (A_ArrayExpr *) $2;
 					Assert(IsA(n, A_ArrayExpr));
 					/* point outermost A_ArrayExpr to the ARRAY keyword */
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| row
@@ -10304,7 +10304,7 @@ c_expr:		columnref								{ $$ = $1; }
 					RowExpr *r = makeNode(RowExpr);
 					r->args = $1;
 					r->row_typeid = InvalidOid;	/* not analyzed yet */
-					r->location = @1;
+					r->location = @1.begins;
 					$$ = (Node *)r;
 				}
 		;
@@ -10327,7 +10327,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = $4;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| func_name '(' func_arg_list ')' over_clause
@@ -10340,7 +10340,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = $5;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| func_name '(' VARIADIC func_arg_expr ')' over_clause
@@ -10353,7 +10353,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = TRUE;
 					n->over = $6;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| func_name '(' func_arg_list ',' VARIADIC func_arg_expr ')' over_clause
@@ -10366,7 +10366,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = TRUE;
 					n->over = $8;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| func_name '(' func_arg_list sort_clause ')' over_clause
@@ -10379,7 +10379,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = $6;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| func_name '(' ALL func_arg_list opt_sort_clause ')' over_clause
@@ -10396,7 +10396,7 @@ func_expr:	func_name '(' ')' over_clause
 					 */
 					n->func_variadic = FALSE;
 					n->over = $7;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| func_name '(' DISTINCT func_arg_list opt_sort_clause ')' over_clause
@@ -10409,7 +10409,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = TRUE;
 					n->func_variadic = FALSE;
 					n->over = $7;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| func_name '(' '*' ')' over_clause
@@ -10432,7 +10432,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = $5;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| CURRENT_DATE
@@ -10453,8 +10453,8 @@ func_expr:	func_name '(' ')' over_clause
 					 * to rely on it.)
 					 */
 					Node *n;
-					n = makeStringConstCast("now", @1, SystemTypeName("text"));
-					$$ = makeTypeCast(n, SystemTypeName("date"), -1);
+					n = makeStringConstCast("now", @1.begins, @1.length, SystemTypeName("text"));
+					$$ = makeTypeCast(n, SystemTypeName("date"), -1, -1);
 				}
 			| CURRENT_TIME
 				{
@@ -10463,8 +10463,8 @@ func_expr:	func_name '(' ')' over_clause
 					 * See comments for CURRENT_DATE.
 					 */
 					Node *n;
-					n = makeStringConstCast("now", @1, SystemTypeName("text"));
-					$$ = makeTypeCast(n, SystemTypeName("timetz"), -1);
+					n = makeStringConstCast("now", @1.begins, @1.length, SystemTypeName("text"));
+					$$ = makeTypeCast(n, SystemTypeName("timetz"), -1, -1);
 				}
 			| CURRENT_TIME '(' Iconst ')'
 				{
@@ -10474,10 +10474,10 @@ func_expr:	func_name '(' ')' over_clause
 					 */
 					Node *n;
 					TypeName *d;
-					n = makeStringConstCast("now", @1, SystemTypeName("text"));
+					n = makeStringConstCast("now", @1.begins, @1.length, SystemTypeName("text"));
 					d = SystemTypeName("timetz");
-					d->typmods = list_make1(makeIntConst($3, @3));
-					$$ = makeTypeCast(n, d, -1);
+					d->typmods = list_make1(makeIntConst($3, @3.begins, @3.length));
+					$$ = makeTypeCast(n, d, -1, -1);
 				}
 			| CURRENT_TIMESTAMP
 				{
@@ -10493,7 +10493,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| CURRENT_TIMESTAMP '(' Iconst ')'
@@ -10504,10 +10504,10 @@ func_expr:	func_name '(' ')' over_clause
 					 */
 					Node *n;
 					TypeName *d;
-					n = makeStringConstCast("now", @1, SystemTypeName("text"));
+					n = makeStringConstCast("now", @1.begins, @1.length, SystemTypeName("text"));
 					d = SystemTypeName("timestamptz");
-					d->typmods = list_make1(makeIntConst($3, @3));
-					$$ = makeTypeCast(n, d, -1);
+					d->typmods = list_make1(makeIntConst($3, @3.begins, @3.length));
+					$$ = makeTypeCast(n, d, -1, -1);
 				}
 			| LOCALTIME
 				{
@@ -10516,8 +10516,8 @@ func_expr:	func_name '(' ')' over_clause
 					 * See comments for CURRENT_DATE.
 					 */
 					Node *n;
-					n = makeStringConstCast("now", @1, SystemTypeName("text"));
-					$$ = makeTypeCast((Node *)n, SystemTypeName("time"), -1);
+					n = makeStringConstCast("now", @1.begins, @1.length, SystemTypeName("text"));
+					$$ = makeTypeCast((Node *)n, SystemTypeName("time"), -1, -1);
 				}
 			| LOCALTIME '(' Iconst ')'
 				{
@@ -10527,10 +10527,10 @@ func_expr:	func_name '(' ')' over_clause
 					 */
 					Node *n;
 					TypeName *d;
-					n = makeStringConstCast("now", @1, SystemTypeName("text"));
+					n = makeStringConstCast("now", @1.begins, @1.length, SystemTypeName("text"));
 					d = SystemTypeName("time");
-					d->typmods = list_make1(makeIntConst($3, @3));
-					$$ = makeTypeCast((Node *)n, d, -1);
+					d->typmods = list_make1(makeIntConst($3, @3.begins, @3.length));
+					$$ = makeTypeCast((Node *)n, d, -1, -1);
 				}
 			| LOCALTIMESTAMP
 				{
@@ -10539,8 +10539,8 @@ func_expr:	func_name '(' ')' over_clause
 					 * See comments for CURRENT_DATE.
 					 */
 					Node *n;
-					n = makeStringConstCast("now", @1, SystemTypeName("text"));
-					$$ = makeTypeCast(n, SystemTypeName("timestamp"), -1);
+					n = makeStringConstCast("now", @1.begins, @1.length, SystemTypeName("text"));
+					$$ = makeTypeCast(n, SystemTypeName("timestamp"), -1, -1);
 				}
 			| LOCALTIMESTAMP '(' Iconst ')'
 				{
@@ -10550,10 +10550,10 @@ func_expr:	func_name '(' ')' over_clause
 					 */
 					Node *n;
 					TypeName *d;
-					n = makeStringConstCast("now", @1, SystemTypeName("text"));
+					n = makeStringConstCast("now", @1.begins, @1.length, SystemTypeName("text"));
 					d = SystemTypeName("timestamp");
-					d->typmods = list_make1(makeIntConst($3, @3));
-					$$ = makeTypeCast(n, d, -1);
+					d->typmods = list_make1(makeIntConst($3, @3.begins, @3.length));
+					$$ = makeTypeCast(n, d, -1, -1);
 				}
 			| CURRENT_ROLE
 				{
@@ -10565,7 +10565,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| CURRENT_USER
@@ -10578,7 +10578,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| SESSION_USER
@@ -10591,7 +10591,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| USER
@@ -10604,7 +10604,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| CURRENT_CATALOG
@@ -10617,7 +10617,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| CURRENT_SCHEMA
@@ -10630,11 +10630,11 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| CAST '(' a_expr AS Typename ')'
-				{ $$ = makeTypeCast($3, $5, @1); }
+				{ $$ = makeTypeCast($3, $5, @1.begins, @1.length); }
 			| EXTRACT '(' extract_list ')'
 				{
 					FuncCall *n = makeNode(FuncCall);
@@ -10645,7 +10645,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| OVERLAY '(' overlay_list ')'
@@ -10663,7 +10663,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| POSITION '(' position_list ')'
@@ -10677,7 +10677,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| SUBSTRING '(' substr_list ')'
@@ -10693,7 +10693,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| TREAT '(' a_expr AS Typename ')'
@@ -10715,7 +10715,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| TRIM '(' BOTH trim_list ')'
@@ -10731,7 +10731,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| TRIM '(' LEADING trim_list ')'
@@ -10744,7 +10744,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| TRIM '(' TRAILING trim_list ')'
@@ -10757,7 +10757,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| TRIM '(' trim_list ')'
@@ -10770,18 +10770,18 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| NULLIF '(' a_expr ',' a_expr ')'
 				{
-					$$ = (Node *) makeSimpleA_Expr(AEXPR_NULLIF, "=", $3, $5, @1);
+					$$ = (Node *) makeSimpleA_Expr(AEXPR_NULLIF, "=", $3, $5, @1.begins);
 				}
 			| COALESCE '(' expr_list ')'
 				{
 					CoalesceExpr *c = makeNode(CoalesceExpr);
 					c->args = $3;
-					c->location = @1;
+					c->location = @1.begins;
 					$$ = (Node *)c;
 				}
 			| GREATEST '(' expr_list ')'
@@ -10789,7 +10789,7 @@ func_expr:	func_name '(' ')' over_clause
 					MinMaxExpr *v = makeNode(MinMaxExpr);
 					v->args = $3;
 					v->op = IS_GREATEST;
-					v->location = @1;
+					v->location = @1.begins;
 					$$ = (Node *)v;
 				}
 			| LEAST '(' expr_list ')'
@@ -10797,28 +10797,28 @@ func_expr:	func_name '(' ')' over_clause
 					MinMaxExpr *v = makeNode(MinMaxExpr);
 					v->args = $3;
 					v->op = IS_LEAST;
-					v->location = @1;
+					v->location = @1.begins;
 					$$ = (Node *)v;
 				}
 			| XMLCONCAT '(' expr_list ')'
 				{
-					$$ = makeXmlExpr(IS_XMLCONCAT, NULL, NIL, $3, @1);
+					$$ = makeXmlExpr(IS_XMLCONCAT, NULL, NIL, $3, @1.begins);
 				}
 			| XMLELEMENT '(' NAME_P ColLabel ')'
 				{
-					$$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL, @1);
+					$$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, NIL, @1.begins);
 				}
 			| XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ')'
 				{
-					$$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NIL, @1);
+					$$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NIL, @1.begins);
 				}
 			| XMLELEMENT '(' NAME_P ColLabel ',' expr_list ')'
 				{
-					$$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, $6, @1);
+					$$ = makeXmlExpr(IS_XMLELEMENT, $4, NIL, $6, @1.begins);
 				}
 			| XMLELEMENT '(' NAME_P ColLabel ',' xml_attributes ',' expr_list ')'
 				{
-					$$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, @1);
+					$$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8, @1.begins);
 				}
 			| XMLEXISTS '(' c_expr xmlexists_argument ')'
 				{
@@ -10832,34 +10832,34 @@ func_expr:	func_name '(' ')' over_clause
 					n->agg_distinct = FALSE;
 					n->func_variadic = FALSE;
 					n->over = NULL;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 			| XMLFOREST '(' xml_attribute_list ')'
 				{
-					$$ = makeXmlExpr(IS_XMLFOREST, NULL, $3, NIL, @1);
+					$$ = makeXmlExpr(IS_XMLFOREST, NULL, $3, NIL, @1.begins);
 				}
 			| XMLPARSE '(' document_or_content a_expr xml_whitespace_option ')'
 				{
 					XmlExpr *x = (XmlExpr *)
 						makeXmlExpr(IS_XMLPARSE, NULL, NIL,
-									list_make2($4, makeBoolAConst($5, -1)),
-									@1);
+									list_make2($4, makeBoolAConst($5, -1, -1)),
+									@1.begins);
 					x->xmloption = $3;
 					$$ = (Node *)x;
 				}
 			| XMLPI '(' NAME_P ColLabel ')'
 				{
-					$$ = makeXmlExpr(IS_XMLPI, $4, NULL, NIL, @1);
+					$$ = makeXmlExpr(IS_XMLPI, $4, NULL, NIL, @1.begins);
 				}
 			| XMLPI '(' NAME_P ColLabel ',' a_expr ')'
 				{
-					$$ = makeXmlExpr(IS_XMLPI, $4, NULL, list_make1($6), @1);
+					$$ = makeXmlExpr(IS_XMLPI, $4, NULL, list_make1($6), @1.begins);
 				}
 			| XMLROOT '(' a_expr ',' xml_root_version opt_xml_root_standalone ')'
 				{
 					$$ = makeXmlExpr(IS_XMLROOT, NULL, NIL,
-									 list_make3($3, $5, $6), @1);
+									 list_make3($3, $5, $6), @1.begins);
 				}
 			| XMLSERIALIZE '(' document_or_content a_expr AS SimpleTypename ')'
 				{
@@ -10867,7 +10867,7 @@ func_expr:	func_name '(' ')' over_clause
 					n->xmloption = $3;
 					n->expr = $4;
 					n->typeName = $6;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *)n;
 				}
 		;
@@ -10878,17 +10878,17 @@ func_expr:	func_name '(' ')' over_clause
 xml_root_version: VERSION_P a_expr
 				{ $$ = $2; }
 			| VERSION_P NO VALUE_P
-				{ $$ = makeNullAConst(-1); }
+				{ $$ = makeNullAConst(-1, -1); }
 		;
 
 opt_xml_root_standalone: ',' STANDALONE_P YES_P
-				{ $$ = makeIntConst(XML_STANDALONE_YES, -1); }
+				{ $$ = makeIntConst(XML_STANDALONE_YES, -1, -1); }
 			| ',' STANDALONE_P NO
-				{ $$ = makeIntConst(XML_STANDALONE_NO, -1); }
+				{ $$ = makeIntConst(XML_STANDALONE_NO, -1, -1); }
 			| ',' STANDALONE_P NO VALUE_P
-				{ $$ = makeIntConst(XML_STANDALONE_NO_VALUE, -1); }
+				{ $$ = makeIntConst(XML_STANDALONE_NO_VALUE, -1, -1); }
 			| /*EMPTY*/
-				{ $$ = makeIntConst(XML_STANDALONE_OMITTED, -1); }
+				{ $$ = makeIntConst(XML_STANDALONE_OMITTED, -1, -1); }
 		;
 
 xml_attributes: XMLATTRIBUTES '(' xml_attribute_list ')'	{ $$ = $3; }
@@ -10904,7 +10904,7 @@ xml_attribute_el: a_expr AS ColLabel
 					$$->name = $3;
 					$$->indirection = NIL;
 					$$->val = (Node *) $1;
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 			| a_expr
 				{
@@ -10912,7 +10912,7 @@ xml_attribute_el: a_expr AS ColLabel
 					$$->name = NULL;
 					$$->indirection = NIL;
 					$$->val = (Node *) $1;
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 		;
 
@@ -10981,7 +10981,7 @@ over_clause: OVER window_specification
 					n->frameOptions = FRAMEOPTION_DEFAULTS;
 					n->startOffset = NULL;
 					n->endOffset = NULL;
-					n->location = @2;
+					n->location = @2.begins;
 					$$ = n;
 				}
 			| /*EMPTY*/
@@ -11000,7 +11000,7 @@ window_specification: '(' opt_existing_window_name opt_partition_clause
 					n->frameOptions = $5->frameOptions;
 					n->startOffset = $5->startOffset;
 					n->endOffset = $5->endOffset;
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = n;
 				}
 		;
@@ -11040,13 +11040,13 @@ opt_frame_clause:
 						ereport(ERROR,
 								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 								 errmsg("RANGE PRECEDING is only supported with UNBOUNDED"),
-								 parser_errposition(@1)));
+								 parser_errposition(@1.begins)));
 					if (n->frameOptions & (FRAMEOPTION_START_VALUE_FOLLOWING |
 										   FRAMEOPTION_END_VALUE_FOLLOWING))
 						ereport(ERROR,
 								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 								 errmsg("RANGE FOLLOWING is only supported with UNBOUNDED"),
-								 parser_errposition(@1)));
+								 parser_errposition(@1.begins)));
 					$$ = n;
 				}
 			| ROWS frame_extent
@@ -11073,12 +11073,12 @@ frame_extent: frame_bound
 						ereport(ERROR,
 								(errcode(ERRCODE_WINDOWING_ERROR),
 								 errmsg("frame start cannot be UNBOUNDED FOLLOWING"),
-								 parser_errposition(@1)));
+								 parser_errposition(@1.begins)));
 					if (n->frameOptions & FRAMEOPTION_START_VALUE_FOLLOWING)
 						ereport(ERROR,
 								(errcode(ERRCODE_WINDOWING_ERROR),
 								 errmsg("frame starting from following row cannot end with current row"),
-								 parser_errposition(@1)));
+								 parser_errposition(@1.begins)));
 					n->frameOptions |= FRAMEOPTION_END_CURRENT_ROW;
 					$$ = n;
 				}
@@ -11096,25 +11096,25 @@ frame_extent: frame_bound
 						ereport(ERROR,
 								(errcode(ERRCODE_WINDOWING_ERROR),
 								 errmsg("frame start cannot be UNBOUNDED FOLLOWING"),
-								 parser_errposition(@2)));
+								 parser_errposition(@2.begins)));
 					if (frameOptions & FRAMEOPTION_END_UNBOUNDED_PRECEDING)
 						ereport(ERROR,
 								(errcode(ERRCODE_WINDOWING_ERROR),
 								 errmsg("frame end cannot be UNBOUNDED PRECEDING"),
-								 parser_errposition(@4)));
+								 parser_errposition(@4.begins)));
 					if ((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
 						(frameOptions & FRAMEOPTION_END_VALUE_PRECEDING))
 						ereport(ERROR,
 								(errcode(ERRCODE_WINDOWING_ERROR),
 								 errmsg("frame starting from current row cannot have preceding rows"),
-								 parser_errposition(@4)));
+								 parser_errposition(@4.begins)));
 					if ((frameOptions & FRAMEOPTION_START_VALUE_FOLLOWING) &&
 						(frameOptions & (FRAMEOPTION_END_VALUE_PRECEDING |
 										 FRAMEOPTION_END_CURRENT_ROW)))
 						ereport(ERROR,
 								(errcode(ERRCODE_WINDOWING_ERROR),
 								 errmsg("frame starting from following row cannot have preceding rows"),
-								 parser_errposition(@4)));
+								 parser_errposition(@4.begins)));
 					n1->frameOptions = frameOptions;
 					n1->endOffset = n2->startOffset;
 					$$ = n1;
@@ -11272,7 +11272,7 @@ func_arg_expr:  a_expr
 					na->name = $1;
 					na->arg = (Expr *) $3;
 					na->argnumber = -1;		/* until determined */
-					na->location = @1;
+					na->location = @1.begins;
 					$$ = (Node *) na;
 				}
 		;
@@ -11283,15 +11283,15 @@ type_list:	Typename								{ $$ = list_make1($1); }
 
 array_expr: '[' expr_list ']'
 				{
-					$$ = makeAArrayExpr($2, @1);
+					$$ = makeAArrayExpr($2, @1.begins);
 				}
 			| '[' array_expr_list ']'
 				{
-					$$ = makeAArrayExpr($2, @1);
+					$$ = makeAArrayExpr($2, @1.begins);
 				}
 			| '[' ']'
 				{
-					$$ = makeAArrayExpr(NIL, @1);
+					$$ = makeAArrayExpr(NIL, @1.begins);
 				}
 		;
 
@@ -11303,7 +11303,7 @@ array_expr_list: array_expr							{ $$ = list_make1($1); }
 extract_list:
 			extract_arg FROM a_expr
 				{
-					$$ = list_make2(makeStringConst($1, @1), $3);
+					$$ = list_make2(makeStringConst($1, @1.begins, @1.length), $3);
 				}
 			| /*EMPTY*/								{ $$ = NIL; }
 		;
@@ -11388,9 +11388,9 @@ substr_list:
 					 * which it is likely to do if the second argument
 					 * is unknown or doesn't have an implicit cast to int4.
 					 */
-					$$ = list_make3($1, makeIntConst(1, -1),
+					$$ = list_make3($1, makeIntConst(1, -1, -1),
 									makeTypeCast($2,
-												 SystemTypeName("int4"), -1));
+												 SystemTypeName("int4"), -1, -1));
 				}
 			| expr_list
 				{
@@ -11436,7 +11436,7 @@ case_expr:	CASE case_arg when_clause_list case_default END_P
 					c->arg = (Expr *) $2;
 					c->args = $3;
 					c->defresult = (Expr *) $4;
-					c->location = @1;
+					c->location = @1.begins;
 					$$ = (Node *)c;
 				}
 		;
@@ -11453,7 +11453,7 @@ when_clause:
 					CaseWhen *w = makeNode(CaseWhen);
 					w->expr = (Expr *) $2;
 					w->result = (Expr *) $4;
-					w->location = @1;
+					w->location = @1.begins;
 					$$ = (Node *)w;
 				}
 		;
@@ -11469,11 +11469,11 @@ case_arg:	a_expr									{ $$ = $1; }
 
 columnref:	ColId
 				{
-					$$ = makeColumnRef($1, NIL, @1, yyscanner);
+					$$ = makeColumnRef($1, NIL, @1.begins, yyscanner);
 				}
 			| ColId indirection
 				{
-					$$ = makeColumnRef($1, $2, @1, yyscanner);
+					$$ = makeColumnRef($1, $2, @1.begins, yyscanner);
 				}
 		;
 
@@ -11528,7 +11528,7 @@ ctext_expr:
 			| DEFAULT
 				{
 					SetToDefault *n = makeNode(SetToDefault);
-					n->location = @1;
+					n->location = @1.begins;
 					$$ = (Node *) n;
 				}
 		;
@@ -11564,7 +11564,7 @@ target_el:	a_expr AS ColLabel
 					$$->name = $3;
 					$$->indirection = NIL;
 					$$->val = (Node *)$1;
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 			/*
 			 * We support omitting AS only for column labels that aren't
@@ -11580,7 +11580,7 @@ target_el:	a_expr AS ColLabel
 					$$->name = $2;
 					$$->indirection = NIL;
 					$$->val = (Node *)$1;
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 			| a_expr
 				{
@@ -11588,19 +11588,19 @@ target_el:	a_expr AS ColLabel
 					$$->name = NULL;
 					$$->indirection = NIL;
 					$$->val = (Node *)$1;
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 			| '*'
 				{
 					ColumnRef *n = makeNode(ColumnRef);
 					n->fields = list_make1(makeNode(A_Star));
-					n->location = @1;
+					n->location = @1.begins;
 
 					$$ = makeNode(ResTarget);
 					$$->name = NULL;
 					$$->indirection = NIL;
 					$$->val = (Node *)n;
-					$$->location = @1;
+					$$->location = @1.begins;
 				}
 		;
 
@@ -11626,12 +11626,12 @@ qualified_name_list:
 qualified_name:
 			ColId
 				{
-					$$ = makeRangeVar(NULL, $1, @1);
+					$$ = makeRangeVar(NULL, $1, @1.begins);
 				}
 			| ColId indirection
 				{
 					check_qualified_name($2, yyscanner);
-					$$ = makeRangeVar(NULL, NULL, @1);
+					$$ = makeRangeVar(NULL, NULL, @1.begins);
 					switch (list_length($2))
 					{
 						case 1:
@@ -11649,7 +11649,7 @@ qualified_name:
 									(errcode(ERRCODE_SYNTAX_ERROR),
 									 errmsg("improper qualified name (too many dotted names): %s",
 											NameListToString(lcons(makeString($1), $2))),
-									 parser_errposition(@1)));
+									 parser_errposition(@1.begins)));
 							break;
 					}
 				}
@@ -11699,19 +11699,19 @@ func_name:	type_function_name
  */
 AexprConst: Iconst
 				{
-					$$ = makeIntConst($1, @1);
+					$$ = makeIntConst($1, @1.begins, @1.length);
 				}
 			| FCONST
 				{
-					$$ = makeFloatConst($1, @1);
+					$$ = makeFloatConst($1, @1.begins, @1.length);
 				}
 			| Sconst
 				{
-					$$ = makeStringConst($1, @1);
+					$$ = makeStringConst($1, @1.begins, @1.length);
 				}
 			| BCONST
 				{
-					$$ = makeBitStringConst($1, @1);
+					$$ = makeBitStringConst($1, @1.begins, @1.length);
 				}
 			| XCONST
 				{
@@ -11720,14 +11720,14 @@ AexprConst: Iconst
 					 * a <general literal> shall not be a
 					 * <bit string literal> or a <hex string literal>.
 					 */
-					$$ = makeBitStringConst($1, @1);
+					$$ = makeBitStringConst($1, @1.begins, @1.length);
 				}
 			| func_name Sconst
 				{
 					/* generic type 'literal' syntax */
 					TypeName *t = makeTypeNameFromNameList($1);
-					t->location = @1;
-					$$ = makeStringConstCast($2, @2, t);
+					t->location = @1.begins;
+					$$ = makeStringConstCast($2, @2.begins, @1.length, t);
 				}
 			| func_name '(' func_arg_list ')' Sconst
 				{
@@ -11751,18 +11751,18 @@ AexprConst: Iconst
 									 parser_errposition(arg->location)));
 					}
 					t->typmods = $3;
-					t->location = @1;
-					$$ = makeStringConstCast($5, @5, t);
+					t->location = @1.begins;
+					$$ = makeStringConstCast($5, @5.begins, @5.length, t);
 				}
 			| ConstTypename Sconst
 				{
-					$$ = makeStringConstCast($2, @2, $1);
+					$$ = makeStringConstCast($2, @2.begins, @2.length, $1);
 				}
 			| ConstInterval Sconst opt_interval
 				{
 					TypeName *t = $1;
 					t->typmods = $3;
-					$$ = makeStringConstCast($2, @2, t);
+					$$ = makeStringConstCast($2, @2.begins, @2.length, t);
 				}
 			| ConstInterval '(' Iconst ')' Sconst opt_interval
 				{
@@ -11773,25 +11773,25 @@ AexprConst: Iconst
 							ereport(ERROR,
 									(errcode(ERRCODE_SYNTAX_ERROR),
 									 errmsg("interval precision specified twice"),
-									 parser_errposition(@1)));
-						t->typmods = lappend($6, makeIntConst($3, @3));
+									 parser_errposition(@1.begins)));
+						t->typmods = lappend($6, makeIntConst($3, @3.begins, @3.length));
 					}
 					else
-						t->typmods = list_make2(makeIntConst(INTERVAL_FULL_RANGE, -1),
-												makeIntConst($3, @3));
-					$$ = makeStringConstCast($5, @5, t);
+						t->typmods = list_make2(makeIntConst(INTERVAL_FULL_RANGE, -1, -1),
+												makeIntConst($3, @3.begins, @3.length));
+					$$ = makeStringConstCast($5, @5.begins, @5.length, t);
 				}
 			| TRUE_P
 				{
-					$$ = makeBoolAConst(TRUE, @1);
+					$$ = makeBoolAConst(TRUE, @1.begins, @1.length);
 				}
 			| FALSE_P
 				{
-					$$ = makeBoolAConst(FALSE, @1);
+					$$ = makeBoolAConst(FALSE, @1.begins, @1.length);
 				}
 			| NULL_P
 				{
-					$$ = makeNullAConst(@1);
+					$$ = makeNullAConst(@1.begins, @1.length);
 				}
 		;
 
@@ -12352,100 +12352,106 @@ makeColumnRef(char *colname, List *indirection,
 }
 
 static Node *
-makeTypeCast(Node *arg, TypeName *typename, int location)
+makeTypeCast(Node *arg, TypeName *typename, int location, int length)
 {
 	TypeCast *n = makeNode(TypeCast);
 	n->arg = arg;
 	n->typeName = typename;
 	n->location = location;
+	n->tok_len = length;
 	return (Node *) n;
 }
 
 static Node *
-makeStringConst(char *str, int location)
+makeStringConst(char *str, int location, int length)
 {
 	A_Const *n = makeNode(A_Const);
 
 	n->val.type = T_String;
 	n->val.val.str = str;
 	n->location = location;
+	n->tok_len = length;
 
 	return (Node *)n;
 }
 
 static Node *
-makeStringConstCast(char *str, int location, TypeName *typename)
+makeStringConstCast(char *str, int location, int length, TypeName *typename)
 {
-	Node *s = makeStringConst(str, location);
+	Node *s = makeStringConst(str, location, length);
 
-	return makeTypeCast(s, typename, -1);
+	return makeTypeCast(s, typename, -1, -1);
 }
 
 static Node *
-makeIntConst(int val, int location)
+makeIntConst(int val, int location, int length)
 {
 	A_Const *n = makeNode(A_Const);
 
 	n->val.type = T_Integer;
 	n->val.val.ival = val;
 	n->location = location;
+	n->tok_len = length;
 
 	return (Node *)n;
 }
 
 static Node *
-makeFloatConst(char *str, int location)
+makeFloatConst(char *str, int location, int length)
 {
 	A_Const *n = makeNode(A_Const);
 
 	n->val.type = T_Float;
 	n->val.val.str = str;
 	n->location = location;
+	n->tok_len = length;
 
 	return (Node *)n;
 }
 
 static Node *
-makeBitStringConst(char *str, int location)
+makeBitStringConst(char *str, int location, int length)
 {
 	A_Const *n = makeNode(A_Const);
 
 	n->val.type = T_BitString;
 	n->val.val.str = str;
 	n->location = location;
+	n->tok_len = length;
 
 	return (Node *)n;
 }
 
 static Node *
-makeNullAConst(int location)
+makeNullAConst(int location, int length)
 {
 	A_Const *n = makeNode(A_Const);
 
 	n->val.type = T_Null;
 	n->location = location;
+	n->tok_len = length;
 
 	return (Node *)n;
 }
 
 static Node *
-makeAConst(Value *v, int location)
+makeAConst(Value *v, int location, int length)
 {
 	Node *n;
 
 	switch (v->type)
 	{
 		case T_Float:
-			n = makeFloatConst(v->val.str, location);
+			n = makeFloatConst(v->val.str, location, length);
 			break;
 
 		case T_Integer:
-			n = makeIntConst(v->val.ival, location);
+			n = makeIntConst(v->val.ival, location, length);
 			break;
 
 		case T_String:
 		default:
-			n = makeStringConst(v->val.str, location);
+			n = makeStringConst(v->val.str, location, length);
 			break;
 	}
 
@@ -12456,15 +12462,16 @@ makeAConst(Value *v, int location)
  * Create an A_Const string node and put it inside a boolean cast.
  */
 static Node *
-makeBoolAConst(bool state, int location)
+makeBoolAConst(bool state, int location, int length)
 {
 	A_Const *n = makeNode(A_Const);
 
 	n->val.type = T_String;
 	n->val.val.str = (state ? "t" : "f");
 	n->location = location;
+	n->tok_len = length;
 
-	return makeTypeCast((Node *)n, SystemTypeName("bool"), -1);
+	return makeTypeCast((Node *)n, SystemTypeName("bool"), -1, -1);
 }
 
 /* makeOverlaps()
@@ -12696,7 +12703,7 @@ SystemTypeName(char *name)
  * until we know what the desired type is.
  */
 static Node *
-doNegate(Node *n, int location)
+doNegate(Node *n, int location, int length)
 {
 	if (IsA(n, A_Const))
 	{
@@ -12704,15 +12711,19 @@ doNegate(Node *n, int location)
 
 		/* report the constant's location as that of the '-' sign */
 		con->location = location;
+		/* TODO: A more elegant job of calculating length than this */
+		/*con->tok_len = length; */
 
 		if (con->val.type == T_Integer)
 		{
 			con->val.val.ival = -con->val.val.ival;
+			con->tok_len = (con->val.val.ival==0? 2: ((int) log10(fabs(con->val.val.ival)) + 2 ) );
 			return n;
 		}
 		if (con->val.type == T_Float)
 		{
 			doNegateFloat(&con->val);
+			con->tok_len = strlen(con->val.val.str);
 			return n;
 		}
 	}
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index e8177bc..6aaa4d2 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -1073,7 +1073,7 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
 	if (l_colvar->vartype != outcoltype)
 		l_node = coerce_type(pstate, (Node *) l_colvar, l_colvar->vartype,
 							 outcoltype, outcoltypmod,
-							 COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1);
+							 COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1, -1);
 	else if (l_colvar->vartypmod != outcoltypmod)
 		l_node = (Node *) makeRelabelType((Expr *) l_colvar,
 										  outcoltype, outcoltypmod,
@@ -1085,7 +1085,7 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
 	if (r_colvar->vartype != outcoltype)
 		r_node = coerce_type(pstate, (Node *) r_colvar, r_colvar->vartype,
 							 outcoltype, outcoltypmod,
-							 COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1);
+							 COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1, -1);
 	else if (r_colvar->vartypmod != outcoltypmod)
 		r_node = (Node *) makeRelabelType((Expr *) r_colvar,
 										  outcoltype, outcoltypmod,
@@ -1965,7 +1965,7 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
 										 restype, TEXTOID, -1,
 										 COERCION_IMPLICIT,
 										 COERCE_IMPLICIT_CAST,
-										 -1);
+										 -1, -1);
 		restype = TEXTOID;
 	}
 
@@ -2108,7 +2108,7 @@ addTargetToGroupList(ParseState *pstate, TargetEntry *tle,
 										 restype, TEXTOID, -1,
 										 COERCION_IMPLICIT,
 										 COERCE_IMPLICIT_CAST,
-										 -1);
+										 -1, -1);
 		restype = TEXTOID;
 	}
 
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index a461ac9..cfc5106 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -76,7 +76,8 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
 					  Oid targettype, int32 targettypmod,
 					  CoercionContext ccontext,
 					  CoercionForm cformat,
-					  int location)
+					  int location,
+					  int tok_len)
 {
 	Node	   *result;
 
@@ -85,7 +86,7 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
 
 	result = coerce_type(pstate, expr, exprtype,
 						 targettype, targettypmod,
-						 ccontext, cformat, location);
+						 ccontext, cformat, location, tok_len);
 
 	/*
 	 * If the target is a fixed-length type, it may need a length coercion as
@@ -127,7 +128,7 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
 Node *
 coerce_type(ParseState *pstate, Node *node,
 			Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
-			CoercionContext ccontext, CoercionForm cformat, int location)
+			CoercionContext ccontext, CoercionForm cformat, int location, int tok_len)
 {
 	Node	   *result;
 	CoercionPathType pathtype;
@@ -255,11 +256,20 @@ coerce_type(ParseState *pstate, Node *node,
 		newcon->constisnull = con->constisnull;
 		/* Use the leftmost of the constant's and coercion's locations */
 		if (location < 0)
+		{
 			newcon->location = con->location;
+			newcon->tok_len = con->tok_len;
+		}
 		else if (con->location >= 0 && con->location < location)
+		{
 			newcon->location = con->location;
+			newcon->tok_len = con->tok_len;
+		}
 		else
+		{
 			newcon->location = location;
+			newcon->tok_len = tok_len;
+		}
 
 		/*
 		 * Set up to point at the constant's text if the input routine throws
@@ -325,7 +335,7 @@ coerce_type(ParseState *pstate, Node *node,
 		newcoll->arg = (Expr *)
 			coerce_type(pstate, (Node *) coll->arg,
 						inputTypeId, targetTypeId, targetTypeMod,
-						ccontext, cformat, location);
+						ccontext, cformat, location, tok_len);
 		newcoll->collOid = coll->collOid;
 		newcoll->location = coll->location;
 		return (Node *) newcoll;
@@ -946,7 +956,7 @@ coerce_record_to_complex(ParseState *pstate, Node *node,
 									  tupdesc->attrs[i]->atttypmod,
 									  ccontext,
 									  COERCE_IMPLICIT_CAST,
-									  -1);
+									  -1, -1);
 		if (cexpr == NULL)
 			ereport(ERROR,
 					(errcode(ERRCODE_CANNOT_COERCE),
@@ -1006,7 +1016,7 @@ coerce_to_boolean(ParseState *pstate, Node *node,
 										BOOLOID, -1,
 										COERCION_ASSIGNMENT,
 										COERCE_IMPLICIT_CAST,
-										-1);
+										-1, -1);
 		if (newnode == NULL)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -1053,7 +1063,7 @@ coerce_to_specific_type(ParseState *pstate, Node *node,
 										targetTypeId, -1,
 										COERCION_ASSIGNMENT,
 										COERCE_IMPLICIT_CAST,
-										-1);
+										-1, -1);
 		if (newnode == NULL)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -1253,7 +1263,7 @@ coerce_to_common_type(ParseState *pstate, Node *node,
 		return node;			/* no work */
 	if (can_coerce_type(1, &inputTypeId, &targetTypeId, COERCION_IMPLICIT))
 		node = coerce_type(pstate, node, inputTypeId, targetTypeId, -1,
-						   COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1);
+						   COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1, -1);
 	else
 		ereport(ERROR,
 				(errcode(ERRCODE_CANNOT_COERCE),
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 79328c9..22e67f2 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -125,7 +125,7 @@ transformExpr(ParseState *pstate, Node *expr)
 				A_Const    *con = (A_Const *) expr;
 				Value	   *val = &con->val;
 
-				result = (Node *) make_const(pstate, val, con->location);
+				result = (Node *) make_const(pstate, val, con->location, con->tok_len);
 				break;
 			}
 
@@ -1347,6 +1347,7 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c)
 
 		n->val.type = T_Null;
 		n->location = -1;
+		n->tok_len = -1;
 		defresult = (Node *) n;
 	}
 	newc->defresult = (Expr *) transformExpr(pstate, defresult);
@@ -1663,7 +1664,7 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
 										 typmod,
 										 COERCION_EXPLICIT,
 										 COERCE_EXPLICIT_CAST,
-										 -1);
+										 -1, -1);
 			if (newe == NULL)
 				ereport(ERROR,
 						(errcode(ERRCODE_CANNOT_COERCE),
@@ -1942,7 +1943,7 @@ transformXmlSerialize(ParseState *pstate, XmlSerialize *xs)
 								   TEXTOID, targetType, targetTypmod,
 								   COERCION_IMPLICIT,
 								   COERCE_IMPLICIT_CAST,
-								   -1);
+								   -1, -1);
 	if (result == NULL)
 		ereport(ERROR,
 				(errcode(ERRCODE_CANNOT_COERCE),
@@ -2085,7 +2086,7 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
 	Oid			inputType = exprType(expr);
 	Oid			targetType;
 	int32		targetTypmod;
-	int			location;
+	int			location, tok_len;
 
 	typenameTypeIdAndMod(pstate, tc->typeName, &targetType, &targetTypmod);
 
@@ -2098,6 +2099,7 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
 	 * name (this can happen in TypeName 'string' syntax, for instance).
 	 */
 	location = tc->location;
+	tok_len = tc->tok_len;
 	if (location < 0)
 		location = tc->typeName->location;
 
@@ -2105,7 +2107,7 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
 								   targetType, targetTypmod,
 								   COERCION_EXPLICIT,
 								   COERCE_EXPLICIT_CAST,
-								   location);
+								   location, tok_len);
 	if (result == NULL)
 		ereport(ERROR,
 				(errcode(ERRCODE_CANNOT_COERCE),
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 75f1e20..b7f9b8d 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -222,7 +222,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 		 */
 		return coerce_type(pstate, linitial(fargs),
 						   actual_arg_types[0], rettype, -1,
-						   COERCION_EXPLICIT, COERCE_EXPLICIT_CALL, location);
+						   COERCION_EXPLICIT, COERCE_EXPLICIT_CALL, location, -1);
 	}
 	else if (fdresult == FUNCDETAIL_NORMAL)
 	{
@@ -1275,7 +1275,7 @@ make_fn_arguments(ParseState *pstate,
 								   declared_arg_types[i], -1,
 								   COERCION_IMPLICIT,
 								   COERCE_IMPLICIT_CAST,
-								   -1);
+								   -1, -1);
 				na->arg = (Expr *) node;
 			}
 			else
@@ -1286,7 +1286,7 @@ make_fn_arguments(ParseState *pstate,
 								   declared_arg_types[i], -1,
 								   COERCION_IMPLICIT,
 								   COERCE_IMPLICIT_CAST,
-								   -1);
+								   -1, -1);
 				lfirst(current_fargs) = node;
 			}
 		}
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 7b5c040..2d0e610 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -335,7 +335,7 @@ transformArraySubscripts(ParseState *pstate,
 												INT4OID, -1,
 												COERCION_ASSIGNMENT,
 												COERCE_IMPLICIT_CAST,
-												-1);
+												-1, -1);
 				if (subexpr == NULL)
 					ereport(ERROR,
 							(errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -362,7 +362,7 @@ transformArraySubscripts(ParseState *pstate,
 										INT4OID, -1,
 										COERCION_ASSIGNMENT,
 										COERCE_IMPLICIT_CAST,
-										-1);
+										-1, -1);
 		if (subexpr == NULL)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -386,7 +386,7 @@ transformArraySubscripts(ParseState *pstate,
 										typeneeded, arrayTypMod,
 										COERCION_ASSIGNMENT,
 										COERCE_IMPLICIT_CAST,
-										-1);
+										-1, -1);
 		if (newFrom == NULL)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -434,7 +434,7 @@ transformArraySubscripts(ParseState *pstate,
  *	too many examples that fail if we try.
  */
 Const *
-make_const(ParseState *pstate, Value *value, int location)
+make_const(ParseState *pstate, Value *value, int location, int tok_len)
 {
 	Const	   *con;
 	Datum		val;
@@ -533,6 +533,7 @@ make_const(ParseState *pstate, Value *value, int location)
 							true,
 							false);
 			con->location = location;
+			con->tok_len = tok_len;
 			return con;
 
 		default:
@@ -548,6 +549,7 @@ make_const(ParseState *pstate, Value *value, int location)
 					false,
 					typebyval);
 	con->location = location;
+	con->tok_len = tok_len;
 
 	return con;
 }
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 2f2e87f..651f72a 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -478,7 +478,7 @@ transformAssignedExpr(ParseState *pstate,
 								  attrtype, attrtypmod,
 								  COERCION_ASSIGNMENT,
 								  COERCE_IMPLICIT_CAST,
-								  -1);
+								  -1, -1);
 		if (expr == NULL)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
@@ -722,7 +722,7 @@ transformAssignmentIndirection(ParseState *pstate,
 								   targetTypeId, targetTypMod,
 								   COERCION_ASSIGNMENT,
 								   COERCE_IMPLICIT_CAST,
-								   -1);
+								   -1, -1);
 	if (result == NULL)
 	{
 		if (targetIsArray)
@@ -822,7 +822,7 @@ transformAssignmentSubscripts(ParseState *pstate,
 									   targetTypeId, targetTypMod,
 									   COERCION_ASSIGNMENT,
 									   COERCE_IMPLICIT_CAST,
-									   -1);
+									   -1, -1);
 		/* probably shouldn't fail, but check */
 		if (result == NULL)
 			ereport(ERROR,
diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l
index 12db190..32744fd 100644
--- a/src/backend/parser/scan.l
+++ b/src/backend/parser/scan.l
@@ -73,12 +73,14 @@ bool			standard_conforming_strings = true;
  * this should be done in the first such rule, else yylloc will point
  * into the middle of the token.
  */
-#define SET_YYLLOC()  (*(yylloc) = yytext - yyextra->scanbuf)
+#define SET_YYLLOC_BEGINS() yylloc->length = 0; yylloc->begins = yytext - yyextra->scanbuf;
+#define SET_YYLLOC_LEN()  yylloc->length += strlen(yytext);
+
 
 /*
  * Advance yylloc by the given number of bytes.
  */
-#define ADVANCE_YYLLOC(delta)  ( *(yylloc) += (delta) )
+#define ADVANCE_YYLLOC(delta)  ( yylloc->begins += (delta) )
 
 #define startlit()  ( yyextra->literallen = 0 )
 static void addlit(char *ytext, int yleng, core_yyscan_t yyscanner);
@@ -94,7 +96,7 @@ static void addunicode(pg_wchar c, yyscan_t yyscanner);
 
 #define yyerror(msg)  scanner_yyerror(msg, yyscanner)
 
-#define lexer_errposition()  scanner_errposition(*(yylloc), yyscanner)
+#define lexer_errposition()  scanner_errposition(yylloc->begins, yyscanner)
 
 static void check_string_escape_warning(unsigned char ychar, core_yyscan_t yyscanner);
 static void check_escape_warning(core_yyscan_t yyscanner);
@@ -377,17 +379,19 @@ other			.
 
 {xcstart}		{
 					/* Set location in case of syntax error in comment */
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
 					yyextra->xcdepth = 0;
 					BEGIN(xc);
 					/* Put back any characters past slash-star; see above */
 					yyless(2);
+					SET_YYLLOC_LEN();
 				}
 
 <xc>{xcstart}	{
 					(yyextra->xcdepth)++;
 					/* Put back any characters past slash-star; see above */
 					yyless(2);
+					SET_YYLLOC_LEN();
 				}
 
 <xc>{xcstop}	{
@@ -395,6 +399,7 @@ other			.
 						BEGIN(INITIAL);
 					else
 						(yyextra->xcdepth)--;
+					SET_YYLLOC_LEN();
 				}
 
 <xc>{xcinside}	{
@@ -418,21 +423,24 @@ other			.
 					 * In the meantime, place a leading "b" on the string
 					 * to mark it for the input routine as a binary string.
 					 */
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
 					BEGIN(xb);
 					startlit();
 					addlitchar('b', yyscanner);
+					SET_YYLLOC_LEN();
 				}
 <xb>{quotestop}	|
 <xb>{quotefail} {
 					yyless(1);
 					BEGIN(INITIAL);
 					yylval->str = litbufdup(yyscanner);
+					SET_YYLLOC_LEN();
 					return BCONST;
 				}
 <xh>{xhinside}	|
 <xb>{xbinside}	{
 					addlit(yytext, yyleng, yyscanner);
+					SET_YYLLOC_LEN();
 				}
 <xh>{quotecontinue}	|
 <xb>{quotecontinue}	{
@@ -447,16 +455,18 @@ other			.
 					 * In the meantime, place a leading "x" on the string
 					 * to mark it for the input routine as a hex string.
 					 */
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
 					BEGIN(xh);
 					startlit();
 					addlitchar('x', yyscanner);
+					SET_YYLLOC_LEN();
 				}
 <xh>{quotestop}	|
 <xh>{quotefail} {
 					yyless(1);
 					BEGIN(INITIAL);
 					yylval->str = litbufdup(yyscanner);
+					SET_YYLLOC_LEN();
 					return XCONST;
 				}
 <xh><<EOF>>		{ yyerror("unterminated hexadecimal string literal"); }
@@ -468,7 +478,8 @@ other			.
 					 */
 					const ScanKeyword *keyword;
 
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
+					SET_YYLLOC_LEN();
 					yyless(1);				/* eat only 'n' this time */
 
 					keyword = ScanKeywordLookup("nchar",
@@ -490,22 +501,25 @@ other			.
 {xqstart}		{
 					yyextra->warn_on_first_escape = true;
 					yyextra->saw_non_ascii = false;
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
 					if (standard_conforming_strings)
 						BEGIN(xq);
 					else
 						BEGIN(xe);
 					startlit();
+					yylloc->length = 0;
+					SET_YYLLOC_LEN();
 				}
 {xestart}		{
 					yyextra->warn_on_first_escape = false;
 					yyextra->saw_non_ascii = false;
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
 					BEGIN(xe);
 					startlit();
+					SET_YYLLOC_LEN();
 				}
 {xusstart}		{
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
 					if (!standard_conforming_strings)
 						ereport(ERROR,
 								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -513,6 +527,7 @@ other			.
 								 errdetail("String constants with Unicode escapes cannot be used when standard_conforming_strings is off."),
 								 lexer_errposition()));
 					BEGIN(xus);
+					SET_YYLLOC_LEN();
 					startlit();
 				}
 <xq,xe>{quotestop}	|
@@ -528,6 +543,7 @@ other			.
 									   yyextra->literallen,
 									   false);
 					yylval->str = litbufdup(yyscanner);
+					SET_YYLLOC_LEN();
 					return SCONST;
 				}
 <xus>{xusstop1} {
@@ -535,21 +551,26 @@ other			.
 					yyless(1);
 					BEGIN(INITIAL);
 					yylval->str = litbuf_udeescape('\\', yyscanner);
+					SET_YYLLOC_LEN();
 					return SCONST;
 				}
 <xus>{xusstop2} {
 					BEGIN(INITIAL);
 					yylval->str = litbuf_udeescape(yytext[yyleng-2], yyscanner);
+					SET_YYLLOC_LEN();
 					return SCONST;
 				}
 <xq,xe,xus>{xqdouble} {
 					addlitchar('\'', yyscanner);
+					SET_YYLLOC_LEN();
 				}
 <xq,xus>{xqinside}  {
 					addlit(yytext, yyleng, yyscanner);
+					SET_YYLLOC_LEN();
 				}
 <xe>{xeinside}  {
 					addlit(yytext, yyleng, yyscanner);
+					SET_YYLLOC_LEN();
 				}
 <xe>{xeunicode} {
 					pg_wchar c = strtoul(yytext+2, NULL, 16);
@@ -565,6 +586,7 @@ other			.
 						yyerror("invalid Unicode surrogate pair");
 					else
 						addunicode(c, yyscanner);
+					SET_YYLLOC_LEN();
 				}
 <xeu>{xeunicode} {
 					pg_wchar c = strtoul(yytext+2, NULL, 16);
@@ -577,6 +599,7 @@ other			.
 					addunicode(c, yyscanner);
 
 					BEGIN(xe);
+					SET_YYLLOC_LEN();
 				}
 <xeu>.			{ yyerror("invalid Unicode surrogate pair"); }
 <xeu>\n			{ yyerror("invalid Unicode surrogate pair"); }
@@ -603,6 +626,7 @@ other			.
 					check_string_escape_warning(yytext[1], yyscanner);
 					addlitchar(unescape_single_char(yytext[1], yyscanner),
 							   yyscanner);
+					SET_YYLLOC_LEN();
 				}
 <xe>{xeoctesc}  {
 					unsigned char c = strtoul(yytext+1, NULL, 8);
@@ -611,6 +635,7 @@ other			.
 					addlitchar(c, yyscanner);
 					if (c == '\0' || IS_HIGHBIT_SET(c))
 						yyextra->saw_non_ascii = true;
+					SET_YYLLOC_LEN();
 				}
 <xe>{xehexesc}  {
 					unsigned char c = strtoul(yytext+2, NULL, 16);
@@ -619,6 +644,7 @@ other			.
 					addlitchar(c, yyscanner);
 					if (c == '\0' || IS_HIGHBIT_SET(c))
 						yyextra->saw_non_ascii = true;
+					SET_YYLLOC_LEN();
 				}
 <xq,xe,xus>{quotecontinue} {
 					/* ignore */
@@ -626,19 +652,23 @@ other			.
 <xe>.			{
 					/* This is only needed for \ just before EOF */
 					addlitchar(yytext[0], yyscanner);
+					SET_YYLLOC_LEN();
 				}
 <xq,xe,xus><<EOF>>		{ yyerror("unterminated quoted string"); }
 
 {dolqdelim}		{
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
 					yyextra->dolqstart = pstrdup(yytext);
 					BEGIN(xdolq);
 					startlit();
+					yylloc->length = 0;
+					SET_YYLLOC_LEN();
 				}
 {dolqfailed}	{
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
 					/* throw back all but the initial "$" */
 					yyless(1);
+					SET_YYLLOC_LEN();
 					/* and treat it as {other} */
 					return yytext[0];
 				}
@@ -649,6 +679,7 @@ other			.
 						yyextra->dolqstart = NULL;
 						BEGIN(INITIAL);
 						yylval->str = litbufdup(yyscanner);
+						SET_YYLLOC_LEN();
 						return SCONST;
 					}
 					else
@@ -660,29 +691,35 @@ other			.
 						 */
 						addlit(yytext, yyleng-1, yyscanner);
 						yyless(yyleng-1);
+						SET_YYLLOC_LEN();
 					}
 				}
 <xdolq>{dolqinside} {
 					addlit(yytext, yyleng, yyscanner);
+					SET_YYLLOC_LEN();
 				}
 <xdolq>{dolqfailed} {
 					addlit(yytext, yyleng, yyscanner);
+					SET_YYLLOC_LEN();
 				}
 <xdolq>.		{
 					/* This is only needed for $ inside the quoted text */
 					addlitchar(yytext[0], yyscanner);
+					SET_YYLLOC_LEN();
 				}
 <xdolq><<EOF>>	{ yyerror("unterminated dollar-quoted string"); }
 
 {xdstart}		{
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
 					BEGIN(xd);
 					startlit();
+					SET_YYLLOC_LEN();
 				}
 {xuistart}		{
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
 					BEGIN(xui);
 					startlit();
+					SET_YYLLOC_LEN();
 				}
 <xd>{xdstop}	{
 					char		   *ident;
@@ -694,6 +731,7 @@ other			.
 					if (yyextra->literallen >= NAMEDATALEN)
 						truncate_identifier(ident, yyextra->literallen, true);
 					yylval->str = ident;
+					SET_YYLLOC_LEN();
 					return IDENT;
 				}
 <xui>{xuistop1}	{
@@ -708,6 +746,7 @@ other			.
 					yylval->str = ident;
 					/* throw back all but the quote */
 					yyless(1);
+					SET_YYLLOC_LEN();
 					return IDENT;
 				}
 <xui>{xuistop2}	{
@@ -720,45 +759,53 @@ other			.
 					if (yyextra->literallen >= NAMEDATALEN)
 						truncate_identifier(ident, yyextra->literallen, true);
 					yylval->str = ident;
+					SET_YYLLOC_LEN();
 					return IDENT;
 				}
 <xd,xui>{xddouble}	{
 					addlitchar('"', yyscanner);
+					SET_YYLLOC_LEN();
 				}
 <xd,xui>{xdinside}	{
 					addlit(yytext, yyleng, yyscanner);
+					SET_YYLLOC_LEN();
 				}
 <xd,xui><<EOF>>		{ yyerror("unterminated quoted identifier"); }
 
 {xufailed}	{
 					char		   *ident;
 
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
 					/* throw back all but the initial u/U */
 					yyless(1);
 					/* and treat it as {identifier} */
 					ident = downcase_truncate_identifier(yytext, yyleng, true);
 					yylval->str = ident;
+					SET_YYLLOC_LEN();
 					return IDENT;
 				}
 
 {typecast}		{
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
+					SET_YYLLOC_LEN();
 					return TYPECAST;
 				}
 
 {dot_dot}		{
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
+					SET_YYLLOC_LEN();
 					return DOT_DOT;
 				}
 
 {colon_equals}	{
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
+					SET_YYLLOC_LEN();
 					return COLON_EQUALS;
 				}
 
 {self}			{
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
+					SET_YYLLOC_LEN();
 					return yytext[0];
 				}
 
@@ -808,7 +855,7 @@ other			.
 						nchars--; /* else remove the +/-, and check again */
 					}
 
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
 
 					if (nchars < yyleng)
 					{
@@ -839,33 +886,39 @@ other			.
 						yylval->str = pstrdup("<>");
 					else
 						yylval->str = pstrdup(yytext);
+					SET_YYLLOC_LEN();
 					return Op;
 				}
 
 {param}			{
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
 					yylval->ival = atol(yytext + 1);
+					SET_YYLLOC_LEN();
 					return PARAM;
 				}
 
 {integer}		{
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
+					SET_YYLLOC_LEN();
 					return process_integer_literal(yytext, yylval);
 				}
 {decimal}		{
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
 					yylval->str = pstrdup(yytext);
+					SET_YYLLOC_LEN();
 					return FCONST;
 				}
 {decimalfail}	{
 					/* throw back the .., and treat as integer */
 					yyless(yyleng-2);
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
+					SET_YYLLOC_LEN();
 					return process_integer_literal(yytext, yylval);
 				}
 {real}			{
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
 					yylval->str = pstrdup(yytext);
+					SET_YYLLOC_LEN();
 					return FCONST;
 				}
 {realfail1}		{
@@ -876,15 +929,17 @@ other			.
 					 * syntax error anyway, we don't bother to distinguish.
 					 */
 					yyless(yyleng-1);
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
 					yylval->str = pstrdup(yytext);
+					SET_YYLLOC_LEN();
 					return FCONST;
 				}
 {realfail2}		{
 					/* throw back the [Ee][+-], and proceed as above */
 					yyless(yyleng-2);
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
 					yylval->str = pstrdup(yytext);
+					SET_YYLLOC_LEN();
 					return FCONST;
 				}
 
@@ -893,8 +948,9 @@ other			.
 					const ScanKeyword *keyword;
 					char		   *ident;
 
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
 
+					yylloc->length = 0;
 					/* Is it a keyword? */
 					keyword = ScanKeywordLookup(yytext,
 												yyextra->keywords,
@@ -902,6 +958,7 @@ other			.
 					if (keyword != NULL)
 					{
 						yylval->keyword = keyword->name;
+						SET_YYLLOC_LEN();
 						return keyword->value;
 					}
 
@@ -911,16 +968,19 @@ other			.
 					 */
 					ident = downcase_truncate_identifier(yytext, yyleng, true);
 					yylval->str = ident;
+					SET_YYLLOC_LEN();
 					return IDENT;
 				}
 
 {other}			{
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
+					SET_YYLLOC_LEN();
 					return yytext[0];
 				}
 
 <<EOF>>			{
-					SET_YYLLOC();
+					SET_YYLLOC_BEGINS();
+					SET_YYLLOC_LEN();
 					yyterminate();
 				}
 
@@ -982,7 +1042,7 @@ scanner_errposition(int location, core_yyscan_t yyscanner)
 void
 scanner_yyerror(const char *message, core_yyscan_t yyscanner)
 {
-	const char *loc = yyextra->scanbuf + *yylloc;
+	const char *loc = yyextra->scanbuf + yylloc->begins;
 
 	if (*loc == YY_END_OF_BUFFER_CHAR)
 	{
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 3b31108..2c9bc38 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1023,7 +1023,7 @@ build_column_default(Relation rel, int attrno)
 								 atttype, atttypmod,
 								 COERCION_ASSIGNMENT,
 								 COERCE_IMPLICIT_CAST,
-								 -1);
+								 -1, -1);
 	if (expr == NULL)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATATYPE_MISMATCH),
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index af6565e..ebbceaf 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -252,6 +252,7 @@ typedef struct A_Const
 	NodeTag		type;
 	Value		val;			/* value (includes type info, see value.h) */
 	int			location;		/* token location, or -1 if unknown */
+	int			tok_len;		/* token length (in bytes) */
 } A_Const;
 
 /*
@@ -263,6 +264,7 @@ typedef struct TypeCast
 	Node	   *arg;			/* the expression being casted */
 	TypeName   *typeName;		/* the target type */
 	int			location;		/* token location, or -1 if unknown */
+	int			tok_len;		/* token length (in bytes) */
 } TypeCast;
 
 /*
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index cedf022..c170060 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -170,6 +170,7 @@ typedef struct Const
 								 * in the Datum. If false, then the Datum
 								 * contains a pointer to the information. */
 	int			location;		/* token location, or -1 if unknown */
+	int			tok_len;		/* token length (in bytes) */
 } Const;
 
 /* ----------------
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index ceaff2f..85f3fdd 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -40,12 +40,13 @@ extern Node *coerce_to_target_type(ParseState *pstate,
 					  Oid targettype, int32 targettypmod,
 					  CoercionContext ccontext,
 					  CoercionForm cformat,
-					  int location);
+					  int location,
+					  int tok_len);
 extern bool can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
 				CoercionContext ccontext);
 extern Node *coerce_type(ParseState *pstate, Node *node,
 			Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
-			CoercionContext ccontext, CoercionForm cformat, int location);
+			CoercionContext ccontext, CoercionForm cformat, int location, int tok_len);
 extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod,
 				 Oid typeId,
 				 CoercionForm cformat, int location,
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 0ca7914..fde2a93 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -148,6 +148,6 @@ extern ArrayRef *transformArraySubscripts(ParseState *pstate,
 						 int32 arrayTypMod,
 						 List *indirection,
 						 Node *assignFrom);
-extern Const *make_const(ParseState *pstate, Value *value, int location);
+extern Const *make_const(ParseState *pstate, Value *value, int location, int tok_len);
 
 #endif   /* PARSE_NODE_H */
diff --git a/src/include/parser/scanner.h b/src/include/parser/scanner.h
index 2a88e97..e12ccf4 100644
--- a/src/include/parser/scanner.h
+++ b/src/include/parser/scanner.h
@@ -36,12 +36,16 @@ typedef union core_YYSTYPE
 /*
  * We track token locations in terms of byte offsets from the start of the
  * source string, not the column number/line number representation that
- * bison uses by default.  Also, to minimize overhead we track only one
- * location (usually the first token location) for each construct, not
- * the beginning and ending locations as bison does by default.  It's
- * therefore sufficient to make YYLTYPE an int.
+ * bison uses by default. We track the beginning of the token, as well
+ * as its total length.
  */
-#define YYLTYPE  int
+typedef struct my_yyltype
+{
+	int begins;
+	int length;
+} my_yyltype;
+
+#define YYLTYPE my_yyltype
 
 /*
  * Another important component of the scanner's API is the token code numbers.
diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y
index 8c4c2f7..ce932c4 100644
--- a/src/pl/plpgsql/src/gram.y
+++ b/src/pl/plpgsql/src/gram.y
@@ -367,14 +367,14 @@ pl_block		: decl_sect K_BEGIN proc_sect exception_sect K_END opt_label
 						new = palloc0(sizeof(PLpgSQL_stmt_block));
 
 						new->cmd_type	= PLPGSQL_STMT_BLOCK;
-						new->lineno		= plpgsql_location_to_lineno(@2);
+						new->lineno		= plpgsql_location_to_lineno(@2.begins);
 						new->label		= $1.label;
 						new->n_initvars = $1.n_initvars;
 						new->initvarnos = $1.initvarnos;
 						new->body		= $3;
 						new->exceptions	= $4;
 
-						check_labels($1.label, $6, @6);
+						check_labels($1.label, $6, @6.begins);
 						plpgsql_ns_pop();
 
 						$$ = (PLpgSQL_stmt *)new;
@@ -436,7 +436,7 @@ decl_stmt		: decl_statement
 						ereport(ERROR,
 								(errcode(ERRCODE_SYNTAX_ERROR),
 								 errmsg("block label must be placed before DECLARE, not after"),
-								 parser_errposition(@1)));
+								 parser_errposition(@1.begins)));
 					}
 				;
 
@@ -457,7 +457,7 @@ decl_statement	: decl_varname decl_const decl_datatype decl_collate decl_notnull
 										(errcode(ERRCODE_DATATYPE_MISMATCH),
 										 errmsg("collations are not supported by type %s",
 												format_type_be($3->typoid)),
-										 parser_errposition(@4)));
+										 parser_errposition(@4.begins)));
 							$3->collation = $4;
 						}
 
@@ -471,7 +471,7 @@ decl_statement	: decl_varname decl_const decl_datatype decl_collate decl_notnull
 								ereport(ERROR,
 										(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 										 errmsg("row or record variable cannot be CONSTANT"),
-										 parser_errposition(@2)));
+										 parser_errposition(@2.begins)));
 						}
 						if ($5)
 						{
@@ -481,7 +481,7 @@ decl_statement	: decl_varname decl_const decl_datatype decl_collate decl_notnull
 								ereport(ERROR,
 										(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 										 errmsg("row or record variable cannot be NOT NULL"),
-										 parser_errposition(@4)));
+										 parser_errposition(@4.begins)));
 
 						}
 						if ($6 != NULL)
@@ -492,7 +492,7 @@ decl_statement	: decl_varname decl_const decl_datatype decl_collate decl_notnull
 								ereport(ERROR,
 										(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 										 errmsg("default value for row or record variable is not supported"),
-										 parser_errposition(@5)));
+										 parser_errposition(@5.begins)));
 						}
 					}
 				| decl_varname K_ALIAS K_FOR decl_aliasitem ';'
@@ -584,7 +584,7 @@ decl_cursor_args :
 
 						new = palloc0(sizeof(PLpgSQL_row));
 						new->dtype = PLPGSQL_DTYPE_ROW;
-						new->lineno = plpgsql_location_to_lineno(@1);
+						new->lineno = plpgsql_location_to_lineno(@1.begins);
 						new->rowtupdesc = NULL;
 						new->nfields = list_length($2);
 						new->fieldnames = palloc(new->nfields * sizeof(char *));
@@ -638,7 +638,7 @@ decl_aliasitem	: T_WORD
 									(errcode(ERRCODE_UNDEFINED_OBJECT),
 									 errmsg("variable \"%s\" does not exist",
 											$1.ident),
-									 parser_errposition(@1)));
+									 parser_errposition(@1.begins)));
 						$$ = nsi;
 					}
 				| T_CWORD
@@ -664,7 +664,7 @@ decl_aliasitem	: T_WORD
 									(errcode(ERRCODE_UNDEFINED_OBJECT),
 									 errmsg("variable \"%s\" does not exist",
 											NameListToString($1.idents)),
-									 parser_errposition(@1)));
+									 parser_errposition(@1.begins)));
 						$$ = nsi;
 					}
 				;
@@ -672,7 +672,7 @@ decl_aliasitem	: T_WORD
 decl_varname	: T_WORD
 					{
 						$$.name = $1.ident;
-						$$.lineno = plpgsql_location_to_lineno(@1);
+						$$.lineno = plpgsql_location_to_lineno(@1.begins);
 						/*
 						 * Check to make sure name isn't already declared
 						 * in the current block.
@@ -685,7 +685,7 @@ decl_varname	: T_WORD
 				| unreserved_keyword
 					{
 						$$.name = pstrdup($1);
-						$$.lineno = plpgsql_location_to_lineno(@1);
+						$$.lineno = plpgsql_location_to_lineno(@1.begins);
 						/*
 						 * Check to make sure name isn't already declared
 						 * in the current block.
@@ -819,7 +819,7 @@ stmt_perform	: K_PERFORM expr_until_semi
 
 						new = palloc0(sizeof(PLpgSQL_stmt_perform));
 						new->cmd_type = PLPGSQL_STMT_PERFORM;
-						new->lineno   = plpgsql_location_to_lineno(@1);
+						new->lineno   = plpgsql_location_to_lineno(@1.begins);
 						new->expr  = $2;
 
 						$$ = (PLpgSQL_stmt *)new;
@@ -832,7 +832,7 @@ stmt_assign		: assign_var assign_operator expr_until_semi
 
 						new = palloc0(sizeof(PLpgSQL_stmt_assign));
 						new->cmd_type = PLPGSQL_STMT_ASSIGN;
-						new->lineno   = plpgsql_location_to_lineno(@1);
+						new->lineno   = plpgsql_location_to_lineno(@1.begins);
 						new->varno = $1;
 						new->expr  = $3;
 
@@ -847,7 +847,7 @@ stmt_getdiag	: K_GET getdiag_area_opt K_DIAGNOSTICS getdiag_list ';'
 
 						new = palloc0(sizeof(PLpgSQL_stmt_getdiag));
 						new->cmd_type = PLPGSQL_STMT_GETDIAG;
-						new->lineno   = plpgsql_location_to_lineno(@1);
+						new->lineno   = plpgsql_location_to_lineno(@1.begins);
 						new->is_stacked = $2;
 						new->diag_items = $4;
 
@@ -868,7 +868,7 @@ stmt_getdiag	: K_GET getdiag_area_opt K_DIAGNOSTICS getdiag_list ';'
 												(errcode(ERRCODE_SYNTAX_ERROR),
 												 errmsg("diagnostics item %s is not allowed in GET STACKED DIAGNOSTICS",
 														plpgsql_getdiag_kindname(ditem->kind)),
-												 parser_errposition(@1)));
+												 parser_errposition(@1.begins)));
 									break;
 								/* these fields are disallowed in current case */
 								case PLPGSQL_GETDIAG_ERROR_CONTEXT:
@@ -881,7 +881,7 @@ stmt_getdiag	: K_GET getdiag_area_opt K_DIAGNOSTICS getdiag_list ';'
 												(errcode(ERRCODE_SYNTAX_ERROR),
 												 errmsg("diagnostics item %s is not allowed in GET CURRENT DIAGNOSTICS",
 														plpgsql_getdiag_kindname(ditem->kind)),
-												 parser_errposition(@1)));
+												 parser_errposition(@1.begins)));
 									break;
 								default:
 									elog(ERROR, "unrecognized diagnostic item kind: %d",
@@ -962,32 +962,32 @@ getdiag_item :
 
 getdiag_target	: T_DATUM
 					{
-						check_assignable($1.datum, @1);
+						check_assignable($1.datum, @1.begins);
 						if ($1.datum->dtype == PLPGSQL_DTYPE_ROW ||
 							$1.datum->dtype == PLPGSQL_DTYPE_REC)
 							ereport(ERROR,
 									(errcode(ERRCODE_SYNTAX_ERROR),
 									 errmsg("\"%s\" is not a scalar variable",
 											NameOfDatum(&($1))),
-									 parser_errposition(@1)));
+									 parser_errposition(@1.begins)));
 						$$ = $1.datum->dno;
 					}
 				| T_WORD
 					{
 						/* just to give a better message than "syntax error" */
-						word_is_not_variable(&($1), @1);
+						word_is_not_variable(&($1), @1.begins);
 					}
 				| T_CWORD
 					{
 						/* just to give a better message than "syntax error" */
-						cword_is_not_variable(&($1), @1);
+						cword_is_not_variable(&($1), @1.begins);
 					}
 				;
 
 
 assign_var		: T_DATUM
 					{
-						check_assignable($1.datum, @1);
+						check_assignable($1.datum, @1.begins);
 						$$ = $1.datum->dno;
 					}
 				| assign_var '[' expr_until_rightbracket
@@ -1013,7 +1013,7 @@ stmt_if			: K_IF expr_until_then proc_sect stmt_elsifs stmt_else K_END K_IF ';'
 
 						new = palloc0(sizeof(PLpgSQL_stmt_if));
 						new->cmd_type	= PLPGSQL_STMT_IF;
-						new->lineno		= plpgsql_location_to_lineno(@1);
+						new->lineno		= plpgsql_location_to_lineno(@1.begins);
 						new->cond		= $2;
 						new->then_body	= $3;
 						new->elsif_list = $4;
@@ -1032,7 +1032,7 @@ stmt_elsifs		:
 						PLpgSQL_if_elsif *new;
 
 						new = palloc0(sizeof(PLpgSQL_if_elsif));
-						new->lineno = plpgsql_location_to_lineno(@2);
+						new->lineno = plpgsql_location_to_lineno(@2.begins);
 						new->cond   = $3;
 						new->stmts  = $4;
 
@@ -1052,7 +1052,7 @@ stmt_else		:
 
 stmt_case		: K_CASE opt_expr_until_when case_when_list opt_case_else K_END K_CASE ';'
 					{
-						$$ = make_case(@1, $2, $3, $4);
+						$$ = make_case(@1.begins, $2, $3, $4);
 					}
 				;
 
@@ -1085,7 +1085,7 @@ case_when		: K_WHEN expr_until_then proc_sect
 					{
 						PLpgSQL_case_when *new = palloc(sizeof(PLpgSQL_case_when));
 
-						new->lineno	= plpgsql_location_to_lineno(@1);
+						new->lineno	= plpgsql_location_to_lineno(@1.begins);
 						new->expr	= $2;
 						new->stmts	= $3;
 						$$ = new;
@@ -1117,7 +1117,7 @@ stmt_loop		: opt_block_label K_LOOP loop_body
 
 						new = palloc0(sizeof(PLpgSQL_stmt_loop));
 						new->cmd_type = PLPGSQL_STMT_LOOP;
-						new->lineno   = plpgsql_location_to_lineno(@2);
+						new->lineno   = plpgsql_location_to_lineno(@2.begins);
 						new->label	  = $1;
 						new->body	  = $3.stmts;
 
@@ -1134,7 +1134,7 @@ stmt_while		: opt_block_label K_WHILE expr_until_loop loop_body
 
 						new = palloc0(sizeof(PLpgSQL_stmt_while));
 						new->cmd_type = PLPGSQL_STMT_WHILE;
-						new->lineno   = plpgsql_location_to_lineno(@2);
+						new->lineno   = plpgsql_location_to_lineno(@2.begins);
 						new->label	  = $1;
 						new->cond	  = $3;
 						new->body	  = $4.stmts;
@@ -1154,7 +1154,7 @@ stmt_for		: opt_block_label K_FOR for_control loop_body
 							PLpgSQL_stmt_fori		*new;
 
 							new = (PLpgSQL_stmt_fori *) $3;
-							new->lineno   = plpgsql_location_to_lineno(@2);
+							new->lineno   = plpgsql_location_to_lineno(@2.begins);
 							new->label	  = $1;
 							new->body	  = $4.stmts;
 							$$ = (PLpgSQL_stmt *) new;
@@ -1168,7 +1168,7 @@ stmt_for		: opt_block_label K_FOR for_control loop_body
 								   $3->cmd_type == PLPGSQL_STMT_DYNFORS);
 							/* forq is the common supertype of all three */
 							new = (PLpgSQL_stmt_forq *) $3;
-							new->lineno   = plpgsql_location_to_lineno(@2);
+							new->lineno   = plpgsql_location_to_lineno(@2.begins);
 							new->label	  = $1;
 							new->body	  = $4.stmts;
 							$$ = (PLpgSQL_stmt *) new;
@@ -1183,7 +1183,7 @@ stmt_for		: opt_block_label K_FOR for_control loop_body
 for_control		: for_variable K_IN
 					{
 						int			tok = yylex();
-						int			tokloc = yylloc;
+						int			tokloc = yylloc.begins;
 
 						if (tok == K_EXECUTE)
 						{
@@ -1201,18 +1201,18 @@ for_control		: for_variable K_IN
 							if ($1.rec)
 							{
 								new->rec = $1.rec;
-								check_assignable((PLpgSQL_datum *) new->rec, @1);
+								check_assignable((PLpgSQL_datum *) new->rec, @1.begins);
 							}
 							else if ($1.row)
 							{
 								new->row = $1.row;
-								check_assignable((PLpgSQL_datum *) new->row, @1);
+								check_assignable((PLpgSQL_datum *) new->row, @1.begins);
 							}
 							else if ($1.scalar)
 							{
 								/* convert single scalar to list */
 								new->row = make_scalar_list1($1.name, $1.scalar,
-															 $1.lineno, @1);
+															 $1.lineno, @1.begins);
 								/* no need for check_assignable */
 							}
 							else
@@ -1220,7 +1220,7 @@ for_control		: for_variable K_IN
 								ereport(ERROR,
 										(errcode(ERRCODE_DATATYPE_MISMATCH),
 										 errmsg("loop variable of loop over rows must be a record or row variable or list of scalar variables"),
-										 parser_errposition(@1)));
+										 parser_errposition(@1.begins)));
 							}
 							new->query = expr;
 
@@ -1254,7 +1254,7 @@ for_control		: for_variable K_IN
 								ereport(ERROR,
 										(errcode(ERRCODE_SYNTAX_ERROR),
 										 errmsg("cursor FOR loop must have only one target variable"),
-										 parser_errposition(@1)));
+										 parser_errposition(@1.begins)));
 
 							/* can't use an unbound cursor this way */
 							if (cursor->cursor_explicit_expr == NULL)
@@ -1344,7 +1344,7 @@ for_control		: for_variable K_IN
 									ereport(ERROR,
 											(errcode(ERRCODE_SYNTAX_ERROR),
 											 errmsg("integer FOR loop must have only one target variable"),
-											 parser_errposition(@1)));
+											 parser_errposition(@1.begins)));
 
 								/* create loop's private variable */
 								fvar = (PLpgSQL_var *)
@@ -1394,18 +1394,18 @@ for_control		: for_variable K_IN
 								if ($1.rec)
 								{
 									new->rec = $1.rec;
-									check_assignable((PLpgSQL_datum *) new->rec, @1);
+									check_assignable((PLpgSQL_datum *) new->rec, @1.begins);
 								}
 								else if ($1.row)
 								{
 									new->row = $1.row;
-									check_assignable((PLpgSQL_datum *) new->row, @1);
+									check_assignable((PLpgSQL_datum *) new->row, @1.begins);
 								}
 								else if ($1.scalar)
 								{
 									/* convert single scalar to list */
 									new->row = make_scalar_list1($1.name, $1.scalar,
-																 $1.lineno, @1);
+																 $1.lineno, @1.begins);
 									/* no need for check_assignable */
 								}
 								else
@@ -1413,7 +1413,7 @@ for_control		: for_variable K_IN
 									ereport(ERROR,
 											(errcode(ERRCODE_SYNTAX_ERROR),
 											 errmsg("loop variable of loop over rows must be a record or row variable or list of scalar variables"),
-											 parser_errposition(@1)));
+											 parser_errposition(@1.begins)));
 								}
 
 								new->query = expr1;
@@ -1444,7 +1444,7 @@ for_control		: for_variable K_IN
 for_variable	: T_DATUM
 					{
 						$$.name = NameOfDatum(&($1));
-						$$.lineno = plpgsql_location_to_lineno(@1);
+						$$.lineno = plpgsql_location_to_lineno(@1.begins);
 						if ($1.datum->dtype == PLPGSQL_DTYPE_ROW)
 						{
 							$$.scalar = NULL;
@@ -1470,7 +1470,7 @@ for_variable	: T_DATUM
 							if (tok == ',')
 								$$.row = read_into_scalar_list($$.name,
 															   $$.scalar,
-															   @1);
+															   @1.begins);
 						}
 					}
 				| T_WORD
@@ -1478,7 +1478,7 @@ for_variable	: T_DATUM
 						int			tok;
 
 						$$.name = $1.ident;
-						$$.lineno = plpgsql_location_to_lineno(@1);
+						$$.lineno = plpgsql_location_to_lineno(@1.begins);
 						$$.scalar = NULL;
 						$$.rec = NULL;
 						$$.row = NULL;
@@ -1486,12 +1486,12 @@ for_variable	: T_DATUM
 						tok = yylex();
 						plpgsql_push_back_token(tok);
 						if (tok == ',')
-							word_is_not_variable(&($1), @1);
+							word_is_not_variable(&($1), @1.begins);
 					}
 				| T_CWORD
 					{
 						/* just to give a better message than "syntax error" */
-						cword_is_not_variable(&($1), @1);
+						cword_is_not_variable(&($1), @1.begins);
 					}
 				;
 
@@ -1501,7 +1501,7 @@ stmt_foreach_a	: opt_block_label K_FOREACH for_variable foreach_slice K_IN K_ARR
 
 						new = palloc0(sizeof(PLpgSQL_stmt_foreach_a));
 						new->cmd_type = PLPGSQL_STMT_FOREACH_A;
-						new->lineno = plpgsql_location_to_lineno(@2);
+						new->lineno = plpgsql_location_to_lineno(@2.begins);
 						new->label = $1;
 						new->slice = $4;
 						new->expr = $7;
@@ -1510,24 +1510,24 @@ stmt_foreach_a	: opt_block_label K_FOREACH for_variable foreach_slice K_IN K_ARR
 						if ($3.rec)
 						{
 							new->varno = $3.rec->dno;
-							check_assignable((PLpgSQL_datum *) $3.rec, @3);
+							check_assignable((PLpgSQL_datum *) $3.rec, @3.begins);
 						}
 						else if ($3.row)
 						{
 							new->varno = $3.row->dno;
-							check_assignable((PLpgSQL_datum *) $3.row, @3);
+							check_assignable((PLpgSQL_datum *) $3.row, @3.begins);
 						}
 						else if ($3.scalar)
 						{
 							new->varno = $3.scalar->dno;
-							check_assignable($3.scalar, @3);
+							check_assignable($3.scalar, @3.begins);
 						}
 						else
 						{
 							ereport(ERROR,
 									(errcode(ERRCODE_SYNTAX_ERROR),
 									 errmsg("loop variable of FOREACH must be a known variable or list of variables"),
-											 parser_errposition(@3)));
+											 parser_errposition(@3.begins)));
 						}
 
 						check_labels($1, $8.end_label, $8.end_label_location);
@@ -1554,7 +1554,7 @@ stmt_exit		: exit_type opt_label opt_exitcond
 						new = palloc0(sizeof(PLpgSQL_stmt_exit));
 						new->cmd_type = PLPGSQL_STMT_EXIT;
 						new->is_exit  = $1;
-						new->lineno	  = plpgsql_location_to_lineno(@1);
+						new->lineno	  = plpgsql_location_to_lineno(@1.begins);
 						new->label	  = $2;
 						new->cond	  = $3;
 
@@ -1583,17 +1583,17 @@ stmt_return		: K_RETURN
 						if (tok_is_keyword(tok, &yylval,
 										   K_NEXT, "next"))
 						{
-							$$ = make_return_next_stmt(@1);
+							$$ = make_return_next_stmt(@1.begins);
 						}
 						else if (tok_is_keyword(tok, &yylval,
 												K_QUERY, "query"))
 						{
-							$$ = make_return_query_stmt(@1);
+							$$ = make_return_query_stmt(@1.begins);
 						}
 						else
 						{
 							plpgsql_push_back_token(tok);
-							$$ = make_return_stmt(@1);
+							$$ = make_return_stmt(@1.begins);
 						}
 					}
 				;
@@ -1606,7 +1606,7 @@ stmt_raise		: K_RAISE
 						new = palloc(sizeof(PLpgSQL_stmt_raise));
 
 						new->cmd_type	= PLPGSQL_STMT_RAISE;
-						new->lineno		= plpgsql_location_to_lineno(@1);
+						new->lineno		= plpgsql_location_to_lineno(@1.begins);
 						new->elog_level = ERROR;	/* default */
 						new->condname	= NULL;
 						new->message	= NULL;
@@ -1741,7 +1741,7 @@ loop_body		: proc_sect K_END K_LOOP opt_label ';'
 					{
 						$$.stmts = $1;
 						$$.end_label = $4;
-						$$.end_label_location = @4;
+						$$.end_label_location = @4.begins;
 					}
 				;
 
@@ -1757,7 +1757,7 @@ loop_body		: proc_sect K_END K_LOOP opt_label ';'
  */
 stmt_execsql	: K_INSERT
 					{
-						$$ = make_execsql_stmt(K_INSERT, @1);
+						$$ = make_execsql_stmt(K_INSERT, @1.begins);
 					}
 				| T_WORD
 					{
@@ -1766,8 +1766,8 @@ stmt_execsql	: K_INSERT
 						tok = yylex();
 						plpgsql_push_back_token(tok);
 						if (tok == '=' || tok == COLON_EQUALS || tok == '[')
-							word_is_not_variable(&($1), @1);
-						$$ = make_execsql_stmt(T_WORD, @1);
+							word_is_not_variable(&($1), @1.begins);
+						$$ = make_execsql_stmt(T_WORD, @1.begins);
 					}
 				| T_CWORD
 					{
@@ -1776,8 +1776,8 @@ stmt_execsql	: K_INSERT
 						tok = yylex();
 						plpgsql_push_back_token(tok);
 						if (tok == '=' || tok == COLON_EQUALS || tok == '[')
-							cword_is_not_variable(&($1), @1);
-						$$ = make_execsql_stmt(T_CWORD, @1);
+							cword_is_not_variable(&($1), @1.begins);
+						$$ = make_execsql_stmt(T_CWORD, @1.begins);
 					}
 				;
 
@@ -1795,7 +1795,7 @@ stmt_dynexecute : K_EXECUTE
 
 						new = palloc(sizeof(PLpgSQL_stmt_dynexecute));
 						new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
-						new->lineno = plpgsql_location_to_lineno(@1);
+						new->lineno = plpgsql_location_to_lineno(@1.begins);
 						new->query = expr;
 						new->into = false;
 						new->strict = false;
@@ -1852,7 +1852,7 @@ stmt_open		: K_OPEN cursor_variable
 
 						new = palloc0(sizeof(PLpgSQL_stmt_open));
 						new->cmd_type = PLPGSQL_STMT_OPEN;
-						new->lineno = plpgsql_location_to_lineno(@1);
+						new->lineno = plpgsql_location_to_lineno(@1.begins);
 						new->curvar = $2->dno;
 						new->cursor_options = CURSOR_OPT_FAST_PLAN;
 
@@ -1942,9 +1942,9 @@ stmt_fetch		: K_FETCH opt_fetch_direction cursor_variable K_INTO
 							ereport(ERROR,
 									(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 									 errmsg("FETCH statement cannot return multiple rows"),
-									 parser_errposition(@1)));
+									 parser_errposition(@1.begins)));
 
-						fetch->lineno = plpgsql_location_to_lineno(@1);
+						fetch->lineno = plpgsql_location_to_lineno(@1.begins);
 						fetch->rec		= rec;
 						fetch->row		= row;
 						fetch->curvar	= $3->dno;
@@ -1958,7 +1958,7 @@ stmt_move		: K_MOVE opt_fetch_direction cursor_variable ';'
 					{
 						PLpgSQL_stmt_fetch *fetch = $2;
 
-						fetch->lineno = plpgsql_location_to_lineno(@1);
+						fetch->lineno = plpgsql_location_to_lineno(@1.begins);
 						fetch->curvar	= $3->dno;
 						fetch->is_move	= true;
 
@@ -1978,7 +1978,7 @@ stmt_close		: K_CLOSE cursor_variable ';'
 
 						new = palloc(sizeof(PLpgSQL_stmt_close));
 						new->cmd_type = PLPGSQL_STMT_CLOSE;
-						new->lineno = plpgsql_location_to_lineno(@1);
+						new->lineno = plpgsql_location_to_lineno(@1.begins);
 						new->curvar = $2->dno;
 
 						$$ = (PLpgSQL_stmt *)new;
@@ -1998,25 +1998,25 @@ cursor_variable	: T_DATUM
 							ereport(ERROR,
 									(errcode(ERRCODE_DATATYPE_MISMATCH),
 									 errmsg("cursor variable must be a simple variable"),
-									 parser_errposition(@1)));
+									 parser_errposition(@1.begins)));
 
 						if (((PLpgSQL_var *) $1.datum)->datatype->typoid != REFCURSOROID)
 							ereport(ERROR,
 									(errcode(ERRCODE_DATATYPE_MISMATCH),
 									 errmsg("variable \"%s\" must be of type cursor or refcursor",
 											((PLpgSQL_var *) $1.datum)->refname),
-									 parser_errposition(@1)));
+									 parser_errposition(@1.begins)));
 						$$ = (PLpgSQL_var *) $1.datum;
 					}
 				| T_WORD
 					{
 						/* just to give a better message than "syntax error" */
-						word_is_not_variable(&($1), @1);
+						word_is_not_variable(&($1), @1.begins);
 					}
 				| T_CWORD
 					{
 						/* just to give a better message than "syntax error" */
-						cword_is_not_variable(&($1), @1);
+						cword_is_not_variable(&($1), @1.begins);
 					}
 				;
 
@@ -2031,7 +2031,7 @@ exception_sect	:
 						 * scope of the names extends to the end of the
 						 * current block.
 						 */
-						int			lineno = plpgsql_location_to_lineno(@1);
+						int			lineno = plpgsql_location_to_lineno(@1.begins);
 						PLpgSQL_exception_block *new = palloc(sizeof(PLpgSQL_exception_block));
 						PLpgSQL_variable *var;
 
@@ -2077,7 +2077,7 @@ proc_exception	: K_WHEN proc_conditions K_THEN proc_sect
 						PLpgSQL_exception *new;
 
 						new = palloc0(sizeof(PLpgSQL_exception));
-						new->lineno = plpgsql_location_to_lineno(@1);
+						new->lineno = plpgsql_location_to_lineno(@1.begins);
 						new->conditions = $2;
 						new->action = $4;
 
@@ -2310,9 +2310,9 @@ static void
 current_token_is_not_variable(int tok)
 {
 	if (tok == T_WORD)
-		word_is_not_variable(&(yylval.word), yylloc);
+		word_is_not_variable(&(yylval.word), yylloc.begins);
 	else if (tok == T_CWORD)
-		cword_is_not_variable(&(yylval.cword), yylloc);
+		cword_is_not_variable(&(yylval.cword), yylloc.begins);
 	else
 		yyerror("syntax error");
 }
@@ -2385,7 +2385,7 @@ read_sql_construct(int until,
 	{
 		tok = yylex();
 		if (startlocation < 0)			/* remember loc of first token */
-			startlocation = yylloc;
+			startlocation = yylloc.begins;
 		if (tok == until && parenlevel == 0)
 			break;
 		if (tok == until2 && parenlevel == 0)
@@ -2414,13 +2414,13 @@ read_sql_construct(int until,
 						(errcode(ERRCODE_SYNTAX_ERROR),
 						 errmsg("missing \"%s\" at end of SQL expression",
 								expected),
-						 parser_errposition(yylloc)));
+						 parser_errposition(yylloc.begins)));
 			else
 				ereport(ERROR,
 						(errcode(ERRCODE_SYNTAX_ERROR),
 						 errmsg("missing \"%s\" at end of SQL statement",
 								expected),
-						 parser_errposition(yylloc)));
+						 parser_errposition(yylloc.begins)));
 		}
 	}
 
@@ -2432,7 +2432,7 @@ read_sql_construct(int until,
 		*endtoken = tok;
 
 	/* give helpful complaint about empty input */
-	if (startlocation >= yylloc)
+	if (startlocation >= yylloc.begins)
 	{
 		if (isexpression)
 			yyerror("missing expression");
@@ -2440,7 +2440,7 @@ read_sql_construct(int until,
 			yyerror("missing SQL statement");
 	}
 
-	plpgsql_append_source_text(&ds, startlocation, yylloc);
+	plpgsql_append_source_text(&ds, startlocation, yylloc.begins);
 
 	/* trim any trailing whitespace, for neatness */
 	while (ds.len > 0 && scanner_isspace(ds.data[ds.len - 1]))
@@ -2476,7 +2476,7 @@ read_datatype(int tok)
 	if (tok == YYEMPTY)
 		tok = yylex();
 
-	startlocation = yylloc;
+	startlocation = yylloc.begins;
 
 	/*
 	 * If we have a simple or composite identifier, check for %TYPE
@@ -2557,7 +2557,7 @@ read_datatype(int tok)
 
 	/* set up ds to contain complete typename text */
 	initStringInfo(&ds);
-	plpgsql_append_source_text(&ds, startlocation, yylloc);
+	plpgsql_append_source_text(&ds, startlocation, yylloc.begins);
 	type_name = ds.data;
 
 	if (type_name[0] == '\0')
@@ -2608,7 +2608,7 @@ make_execsql_stmt(int firsttoken, int location)
 		prev_tok = tok;
 		tok = yylex();
 		if (have_into && into_end_loc < 0)
-			into_end_loc = yylloc;		/* token after the INTO part */
+			into_end_loc = yylloc.begins;		/* token after the INTO part */
 		if (tok == ';')
 			break;
 		if (tok == 0)
@@ -2619,7 +2619,7 @@ make_execsql_stmt(int firsttoken, int location)
 			if (have_into)
 				yyerror("INTO specified more than once");
 			have_into = true;
-			into_start_loc = yylloc;
+			into_start_loc = yylloc.begins;
 			plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
 			read_into_target(&rec, &row, &have_strict);
 			plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_EXPR;
@@ -2637,10 +2637,10 @@ make_execsql_stmt(int firsttoken, int location)
 		 */
 		plpgsql_append_source_text(&ds, location, into_start_loc);
 		appendStringInfoSpaces(&ds, into_end_loc - into_start_loc);
-		plpgsql_append_source_text(&ds, into_end_loc, yylloc);
+		plpgsql_append_source_text(&ds, into_end_loc, yylloc.begins);
 	}
 	else
-		plpgsql_append_source_text(&ds, location, yylloc);
+		plpgsql_append_source_text(&ds, location, yylloc.begins);
 
 	/* trim any trailing whitespace, for neatness */
 	while (ds.len > 0 && scanner_isspace(ds.data[ds.len - 1]))
@@ -2847,7 +2847,7 @@ make_return_stmt(int location)
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
 					 errmsg("RETURN cannot have a parameter in function returning set"),
 					 errhint("Use RETURN NEXT or RETURN QUERY."),
-					 parser_errposition(yylloc)));
+					 parser_errposition(yylloc.begins)));
 	}
 	else if (plpgsql_curr_compile->out_param_varno >= 0)
 	{
@@ -2855,7 +2855,7 @@ make_return_stmt(int location)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
 					 errmsg("RETURN cannot have a parameter in function with OUT parameters"),
-					 parser_errposition(yylloc)));
+					 parser_errposition(yylloc.begins)));
 		new->retvarno = plpgsql_curr_compile->out_param_varno;
 	}
 	else if (plpgsql_curr_compile->fn_rettype == VOIDOID)
@@ -2864,7 +2864,7 @@ make_return_stmt(int location)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
 					 errmsg("RETURN cannot have a parameter in function returning void"),
-					 parser_errposition(yylloc)));
+					 parser_errposition(yylloc.begins)));
 	}
 	else if (plpgsql_curr_compile->fn_retistuple)
 	{
@@ -2882,14 +2882,14 @@ make_return_stmt(int location)
 					ereport(ERROR,
 							(errcode(ERRCODE_DATATYPE_MISMATCH),
 							 errmsg("RETURN must specify a record or row variable in function returning row"),
-							 parser_errposition(yylloc)));
+							 parser_errposition(yylloc.begins)));
 				break;
 
 			default:
 				ereport(ERROR,
 						(errcode(ERRCODE_DATATYPE_MISMATCH),
 						 errmsg("RETURN must specify a record or row variable in function returning row"),
-						 parser_errposition(yylloc)));
+						 parser_errposition(yylloc.begins)));
 				break;
 		}
 		if (yylex() != ';')
@@ -2931,7 +2931,7 @@ make_return_next_stmt(int location)
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
 					 errmsg("RETURN NEXT cannot have a parameter in function with OUT parameters"),
-					 parser_errposition(yylloc)));
+					 parser_errposition(yylloc.begins)));
 		new->retvarno = plpgsql_curr_compile->out_param_varno;
 	}
 	else if (plpgsql_curr_compile->fn_retistuple)
@@ -2946,14 +2946,14 @@ make_return_next_stmt(int location)
 					ereport(ERROR,
 							(errcode(ERRCODE_DATATYPE_MISMATCH),
 							 errmsg("RETURN NEXT must specify a record or row variable in function returning row"),
-							 parser_errposition(yylloc)));
+							 parser_errposition(yylloc.begins)));
 				break;
 
 			default:
 				ereport(ERROR,
 						(errcode(ERRCODE_DATATYPE_MISMATCH),
 						 errmsg("RETURN NEXT must specify a record or row variable in function returning row"),
-						 parser_errposition(yylloc)));
+						 parser_errposition(yylloc.begins)));
 				break;
 		}
 		if (yylex() != ';')
@@ -3087,32 +3087,32 @@ read_into_target(PLpgSQL_rec **rec, PLpgSQL_row **row, bool *strict)
 		case T_DATUM:
 			if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW)
 			{
-				check_assignable(yylval.wdatum.datum, yylloc);
+				check_assignable(yylval.wdatum.datum, yylloc.begins);
 				*row = (PLpgSQL_row *) yylval.wdatum.datum;
 
 				if ((tok = yylex()) == ',')
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
 							 errmsg("record or row variable cannot be part of multiple-item INTO list"),
-							 parser_errposition(yylloc)));
+							 parser_errposition(yylloc.begins)));
 				plpgsql_push_back_token(tok);
 			}
 			else if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
 			{
-				check_assignable(yylval.wdatum.datum, yylloc);
+				check_assignable(yylval.wdatum.datum, yylloc.begins);
 				*rec = (PLpgSQL_rec *) yylval.wdatum.datum;
 
 				if ((tok = yylex()) == ',')
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
 							 errmsg("record or row variable cannot be part of multiple-item INTO list"),
-							 parser_errposition(yylloc)));
+							 parser_errposition(yylloc.begins)));
 				plpgsql_push_back_token(tok);
 			}
 			else
 			{
 				*row = read_into_scalar_list(NameOfDatum(&(yylval.wdatum)),
-											 yylval.wdatum.datum, yylloc);
+											 yylval.wdatum.datum, yylloc.begins);
 			}
 			break;
 
@@ -3151,20 +3151,20 @@ read_into_scalar_list(char *initial_name,
 			ereport(ERROR,
 					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 					 errmsg("too many INTO variables specified"),
-					 parser_errposition(yylloc)));
+					 parser_errposition(yylloc.begins)));
 
 		tok = yylex();
 		switch (tok)
 		{
 			case T_DATUM:
-				check_assignable(yylval.wdatum.datum, yylloc);
+				check_assignable(yylval.wdatum.datum, yylloc.begins);
 				if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
 					yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
 					ereport(ERROR,
 							(errcode(ERRCODE_SYNTAX_ERROR),
 							 errmsg("\"%s\" is not a scalar variable",
 									NameOfDatum(&(yylval.wdatum))),
-							 parser_errposition(yylloc)));
+							 parser_errposition(yylloc.begins)));
 				fieldnames[nfields] = NameOfDatum(&(yylval.wdatum));
 				varnos[nfields++]	= yylval.wdatum.datum->dno;
 				break;
@@ -3394,7 +3394,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, const char *expected)
 					(errcode(ERRCODE_SYNTAX_ERROR),
 					 errmsg("cursor \"%s\" has no arguments",
 							cursor->refname),
-					 parser_errposition(yylloc)));
+					 parser_errposition(yylloc.begins)));
 
 		if (tok != until)
 			yyerror("syntax error");
@@ -3408,7 +3408,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, const char *expected)
 				(errcode(ERRCODE_SYNTAX_ERROR),
 				 errmsg("cursor \"%s\" has arguments",
 						cursor->refname),
-				 parser_errposition(yylloc)));
+				 parser_errposition(yylloc.begins)));
 
 	/*
 	 * Read expressions until the matching ')'.
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
index 76e8436..7f18560 100644
--- a/src/pl/plpgsql/src/pl_scanner.c
+++ b/src/pl/plpgsql/src/pl_scanner.c
@@ -283,7 +283,7 @@ plpgsql_yylex(void)
 				push_back_token(tok3, &aux3);
 				push_back_token(tok2, &aux2);
 				if (plpgsql_parse_word(aux1.lval.str,
-									   core_yy.scanbuf + aux1.lloc,
+									   core_yy.scanbuf + aux1.lloc.begins,
 									   &aux1.lval.wdatum,
 									   &aux1.lval.word))
 					tok1 = T_DATUM;
@@ -304,7 +304,7 @@ plpgsql_yylex(void)
 			/* not A.B, so just process A */
 			push_back_token(tok2, &aux2);
 			if (plpgsql_parse_word(aux1.lval.str,
-								   core_yy.scanbuf + aux1.lloc,
+								   core_yy.scanbuf + aux1.lloc.begins,
 								   &aux1.lval.wdatum,
 								   &aux1.lval.word))
 				tok1 = T_DATUM;
@@ -356,7 +356,7 @@ internal_yylex(TokenAuxData *auxdata)
 						   yyscanner);
 
 		/* remember the length of yytext before it gets changed */
-		yytext = core_yy.scanbuf + auxdata->lloc;
+		yytext = core_yy.scanbuf + auxdata->lloc.begins;
 		auxdata->leng = strlen(yytext);
 
 		/* Check for << >> and #, which the core considers operators */
@@ -464,7 +464,7 @@ plpgsql_scanner_errposition(int location)
 void
 plpgsql_yyerror(const char *message)
 {
-	char	   *yytext = core_yy.scanbuf + plpgsql_yylloc;
+	char	   *yytext = core_yy.scanbuf + plpgsql_yylloc.begins;
 
 	if (*yytext == '\0')
 	{
@@ -472,7 +472,7 @@ plpgsql_yyerror(const char *message)
 				(errcode(ERRCODE_SYNTAX_ERROR),
 		/* translator: %s is typically the translation of "syntax error" */
 				 errmsg("%s at end of input", _(message)),
-				 plpgsql_scanner_errposition(plpgsql_yylloc)));
+				 plpgsql_scanner_errposition(plpgsql_yylloc.begins)));
 	}
 	else
 	{
@@ -488,7 +488,7 @@ plpgsql_yyerror(const char *message)
 				(errcode(ERRCODE_SYNTAX_ERROR),
 		/* translator: first %s is typically the translation of "syntax error" */
 				 errmsg("%s at or near \"%s\"", _(message), yytext),
-				 plpgsql_scanner_errposition(plpgsql_yylloc)));
+				 plpgsql_scanner_errposition(plpgsql_yylloc.begins)));
 	}
 }
 
