>From 1b595089408f47bafa96f8c86ddbc7a5728f0e5e Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Tue, 27 Jan 2015 11:15:17 +0900
Subject: [PATCH 2/2] Make FETCH can accept per-tuple memory overhead.

---
 contrib/postgres_fdw/postgres_fdw.c     | 25 ++++++-----
 src/backend/commands/copy.c             |  2 +-
 src/backend/commands/createas.c         |  2 +-
 src/backend/commands/explain.c          |  2 +-
 src/backend/commands/extension.c        |  2 +-
 src/backend/commands/matview.c          |  2 +-
 src/backend/commands/portalcmds.c       |  3 +-
 src/backend/commands/prepare.c          |  2 +-
 src/backend/executor/execMain.c         | 16 ++++---
 src/backend/executor/functions.c        |  2 +-
 src/backend/executor/spi.c              |  4 +-
 src/backend/parser/gram.y               | 35 ++++++++++-----
 src/backend/tcop/postgres.c             |  4 +-
 src/backend/tcop/pquery.c               | 79 ++++++++++++++++++++-------------
 src/include/executor/executor.h         |  6 +--
 src/include/nodes/parsenodes.h          |  1 +
 src/include/tcop/pquery.h               |  6 +--
 src/interfaces/ecpg/preproc/Makefile    |  2 +-
 src/interfaces/ecpg/preproc/ecpg.addons | 64 +++++++++++++++++---------
 19 files changed, 159 insertions(+), 100 deletions(-)

diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index b3bf27e..6633912 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -47,10 +47,10 @@ PG_MODULE_MAGIC;
 #define DEFAULT_FDW_TUPLE_COST		0.01
 
 /* Maximum tuples per fetch */
-#define MAX_FETCH_SIZE				30000
+#define MAX_FETCH_SIZE				10000
 
 /* Maximum memory usable for retrieved data  */
-#define MAX_FETCH_MEM				(2 * 1024 * 1024)
+#define MAX_FETCH_MEM				(512 * 1024)
 /*
  * FDW-specific planner information kept in RelOptInfo.fdw_private for a
  * foreign table.  This information is collected by postgresGetForeignRelSize.
@@ -163,6 +163,7 @@ typedef struct PgFdwScanState
 	MemoryContext temp_cxt;		/* context for per-tuple temporary data */
 
 	long		max_palloced_mem; /* For test, remove me later */
+	int			max_numrows;
 } PgFdwScanState;
 
 /*
@@ -1104,7 +1105,7 @@ postgresEndForeignScan(ForeignScanState *node)
 	if (fsstate == NULL)
 		return;
 
-	elog(LOG, "Max memory fo tuple store = %ld", fsstate->max_palloced_mem);
+	elog(LOG, "Max memory for tuple store = %ld, max numrows = %d", fsstate->max_palloced_mem, fsstate->max_numrows);
 	/* Close the cursor if open, to prevent accumulation of cursors */
 	if (fsstate->cursor_exists)
 		close_cursor(fsstate->conn, fsstate->cursor_number);
@@ -2037,19 +2038,18 @@ fetch_more_data(ForeignScanState *node)
 		PGconn	   *conn = fsstate->conn;
 		char		sql[64];
 		int			fetch_size;
+		int			tuple_overhead;
 		int			numrows;
 		int			i;
 		long		alloc_size = 0;
 
 		/* The fetch size is arbitrary, but shouldn't be enormous. */
-		fetch_size = MAX_FETCH_MEM -
-			MAX_FETCH_SIZE *
-				estimate_tuple_overhead(fsstate->attinmeta->tupdesc,
-										fsstate->retrieved_attrs);
-
-		snprintf(sql, sizeof(sql), "FETCH %d LIMIT %ld FROM c%u",
+		tuple_overhead = estimate_tuple_overhead(fsstate->attinmeta->tupdesc,
+												 fsstate->retrieved_attrs);
+		fetch_size = MAX_FETCH_MEM - MAX_FETCH_SIZE * sizeof(HeapTuple);
+		snprintf(sql, sizeof(sql), "FETCH %d LIMIT %ld (%d) FROM c%u",
 				 MAX_FETCH_SIZE,
-				 fetch_size,
+				 fetch_size, tuple_overhead,
 				 fsstate->cursor_number);
 
 		res = PQexec(conn, sql);
@@ -2059,6 +2059,9 @@ fetch_more_data(ForeignScanState *node)
 
 		/* Convert the data into HeapTuples */
 		numrows = PQntuples(res);
+		if (fsstate->max_numrows < numrows)
+			fsstate->max_numrows = numrows;
+
 		if (numrows == 0)
 			fsstate->eof_reached;
 		else
@@ -2079,7 +2082,7 @@ fetch_more_data(ForeignScanState *node)
 				alloc_size += fsstate->tuples[i]->t_len;
 			}
 
-			if (alloc_size > fsstate->max_palloced_mem)
+			if (fsstate->max_palloced_mem < alloc_size)
 				fsstate->max_palloced_mem = alloc_size;
 
 			/* Update fetch_ct_2 */
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index b6e6523..6ddae82 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1915,7 +1915,7 @@ CopyTo(CopyState cstate)
 	else
 	{
 		/* run the plan --- the dest receiver will send tuples */
-		ExecutorRun(cstate->queryDesc, ForwardScanDirection, 0L, 0L);
+		ExecutorRun(cstate->queryDesc, ForwardScanDirection, 0L, 0L, 0);
 		processed = ((DR_copy *) cstate->queryDesc->dest)->processed;
 	}
 
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c5c4478..1644f86 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -192,7 +192,7 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
 		dir = ForwardScanDirection;
 
 	/* run the plan */
-	ExecutorRun(queryDesc, dir, 0L, 0L);
+	ExecutorRun(queryDesc, dir, 0L, 0L, 0);
 
 	/* save the rowcount if we're given a completionTag to fill */
 	if (completionTag)
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 2c23e9b..1b423ee 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -489,7 +489,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
 			dir = ForwardScanDirection;
 
 		/* run the plan */
-		ExecutorRun(queryDesc, dir, 0L, 0L);
+		ExecutorRun(queryDesc, dir, 0L, 0L, 0);
 
 		/* run cleanup too */
 		ExecutorFinish(queryDesc);
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index f624567..2360ffa 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -736,7 +736,7 @@ execute_sql_string(const char *sql, const char *filename)
 										dest, NULL, 0);
 
 				ExecutorStart(qdesc, 0);
-				ExecutorRun(qdesc, ForwardScanDirection, 0, 0);
+				ExecutorRun(qdesc, ForwardScanDirection, 0L, 0L, 0);
 				ExecutorFinish(qdesc);
 				ExecutorEnd(qdesc);
 
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 6530ecb..54669c5 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -360,7 +360,7 @@ refresh_matview_datafill(DestReceiver *dest, Query *query,
 	ExecutorStart(queryDesc, EXEC_FLAG_WITHOUT_OIDS);
 
 	/* run the plan */
-	ExecutorRun(queryDesc, ForwardScanDirection, 0L, 0L);
+	ExecutorRun(queryDesc, ForwardScanDirection, 0L, 0L, 0);
 
 	/* and clean up */
 	ExecutorFinish(queryDesc);
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 255c86e..85fffc1 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -178,6 +178,7 @@ PerformPortalFetch(FetchStmt *stmt,
 								stmt->direction,
 								stmt->howMany,
 								stmt->howLarge,
+								stmt->tupoverhead,
 								dest);
 
 	/* Return command status if wanted */
@@ -376,7 +377,7 @@ PersistHoldablePortal(Portal portal)
 										true);
 
 		/* Fetch the result set into the tuplestore */
-		ExecutorRun(queryDesc, ForwardScanDirection, 0L, 0L);
+		ExecutorRun(queryDesc, ForwardScanDirection, 0L, 0L, 0);
 
 		(*queryDesc->dest->rDestroy) (queryDesc->dest);
 		queryDesc->dest = NULL;
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 31799f5..e46367a 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -291,7 +291,7 @@ ExecuteQuery(ExecuteStmt *stmt, IntoClause *intoClause,
 	 */
 	PortalStart(portal, paramLI, eflags, GetActiveSnapshot());
 
-	(void) PortalRun(portal, count, 0L, false, dest, dest, completionTag);
+	(void) PortalRun(portal, count, 0L, 0, false, dest, dest, completionTag);
 
 	PortalDrop(portal, false);
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index d976bf3..b40702c 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -79,6 +79,7 @@ static void ExecutePlan(EState *estate, PlanState *planstate,
 			bool sendTuples,
 			long numberTuples,
 			long sizeTuples,
+			int  tupleOverhead,
 			ScanDirection direction,
 			DestReceiver *dest);
 static bool ExecCheckRTEPerms(RangeTblEntry *rte);
@@ -249,17 +250,20 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
  */
 void
 ExecutorRun(QueryDesc *queryDesc,
-			ScanDirection direction, long count, long size)
+			ScanDirection direction, long count, long size, int tupoverhead)
 {
 	if (ExecutorRun_hook)
-		(*ExecutorRun_hook) (queryDesc, direction, count, size);
+		(*ExecutorRun_hook) (queryDesc, direction,
+							 count, size, tupoverhead);
 	else
-		standard_ExecutorRun(queryDesc, direction, count, size);
+		standard_ExecutorRun(queryDesc, direction,
+							 count, size, tupoverhead);
 }
 
 void
 standard_ExecutorRun(QueryDesc *queryDesc,
-					 ScanDirection direction, long count, long size)
+					 ScanDirection direction,
+					 long count, long size, int tupoverhead)
 {
 	EState	   *estate;
 	CmdType		operation;
@@ -312,6 +316,7 @@ standard_ExecutorRun(QueryDesc *queryDesc,
 					sendTuples,
 					count,
 					size,
+					tupoverhead,
 					direction,
 					dest);
 
@@ -1453,6 +1458,7 @@ ExecutePlan(EState *estate,
 			bool sendTuples,
 			long numberTuples,
 			long sizeTuples,
+			int  tupleOverhead,
 			ScanDirection direction,
 			DestReceiver *dest)
 {
@@ -1534,7 +1540,7 @@ ExecutePlan(EState *estate,
 			 *
 			 * This needs all attributes deformed so a bit slow on some cases.
 			 */
-			sent_size += slot_compute_attr_size(slot);
+			sent_size += slot_compute_attr_size(slot) + tupleOverhead;
 
 			/* Quit when the size limit will be exceeded by this tuple */
 			if (sizeTuples < sent_size)
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index d64e908..9b46c95 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -850,7 +850,7 @@ postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
 		/* Run regular commands to completion unless lazyEval */
 		long		count = (es->lazyEval) ? 1L : 0L;
 
-		ExecutorRun(es->qd, ForwardScanDirection, count, 0L);
+		ExecutorRun(es->qd, ForwardScanDirection, count, 0L, 0);
 
 		/*
 		 * If we requested run to completion OR there was no tuple returned,
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index cb30cfb..889a65c 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -2369,7 +2369,7 @@ _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, long tcount)
 
 	ExecutorStart(queryDesc, eflags);
 
-	ExecutorRun(queryDesc, ForwardScanDirection, tcount, 0L);
+	ExecutorRun(queryDesc, ForwardScanDirection, tcount, 0L, 0);
 
 	_SPI_current->processed = queryDesc->estate->es_processed;
 	_SPI_current->lastoid = queryDesc->estate->es_lastoid;
@@ -2447,7 +2447,7 @@ _SPI_cursor_operation(Portal portal, FetchDirection direction, long count,
 	/* Run the cursor */
 	nfetched = PortalRunFetch(portal,
 							  direction,
-							  count, 0L,
+							  count, 0L, 0,
 							  dest);
 
 	/*
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index e559d1a..4507ea2 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -520,6 +520,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <str>		opt_existing_window_name
 %type <boolean> opt_if_not_exists
 
+%type <ival>	opt_overhead
+
 /*
  * Non-keyword token types.  These are hard-wired into the "flex" lexer.
  * They must be listed first so that their numeric codes do not depend on
@@ -6021,13 +6023,14 @@ fetch_args:	cursor_name
 					n->howMany = $1;
 					$$ = (Node *)n;
 				}
-			| SignedIconst LIMIT Iconst opt_from_in cursor_name
+			| SignedIconst LIMIT Iconst opt_overhead opt_from_in cursor_name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
-					n->portalname = $5;
+					n->portalname = $6;
 					n->direction = FETCH_FORWARD;
 					n->howMany = $1;
 					n->howLarge = $3;
+					n->tupoverhead = $4;
 					$$ = (Node *)n;
 				}
 			| ALL opt_from_in cursor_name
@@ -6038,13 +6041,14 @@ fetch_args:	cursor_name
 					n->howMany = FETCH_ALL;
 					$$ = (Node *)n;
 				}
-			| ALL LIMIT Iconst opt_from_in cursor_name
+			| ALL LIMIT Iconst opt_overhead opt_from_in cursor_name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
-					n->portalname = $5;
+					n->portalname = $6;
 					n->direction = FETCH_FORWARD;
 					n->howMany = FETCH_ALL;
 					n->howLarge = $3;
+					n->tupoverhead = $4;
 					$$ = (Node *)n;
 				}
 			| FORWARD opt_from_in cursor_name
@@ -6063,13 +6067,14 @@ fetch_args:	cursor_name
 					n->howMany = $2;
 					$$ = (Node *)n;
 				}
-			| FORWARD SignedIconst LIMIT Iconst opt_from_in cursor_name
+			| FORWARD SignedIconst LIMIT Iconst opt_overhead opt_from_in cursor_name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
-					n->portalname = $6;
+					n->portalname = $7;
 					n->direction = FETCH_FORWARD;
 					n->howMany = $2;
 					n->howLarge = $4;
+					n->tupoverhead = $5;
 					$$ = (Node *)n;
 				}
 			| FORWARD ALL opt_from_in cursor_name
@@ -6080,13 +6085,14 @@ fetch_args:	cursor_name
 					n->howMany = FETCH_ALL;
 					$$ = (Node *)n;
 				}
-			| FORWARD ALL LIMIT Iconst opt_from_in cursor_name
+			| FORWARD ALL LIMIT Iconst  opt_overhead  opt_from_in cursor_name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
-					n->portalname = $6;
+					n->portalname = $7;
 					n->direction = FETCH_FORWARD;
 					n->howMany = FETCH_ALL;
 					n->howLarge = $4;
+					n->tupoverhead = $5;
 					$$ = (Node *)n;
 				}
 			| BACKWARD opt_from_in cursor_name
@@ -6105,13 +6111,14 @@ fetch_args:	cursor_name
 					n->howMany = $2;
 					$$ = (Node *)n;
 				}
-			| BACKWARD SignedIconst LIMIT Iconst opt_from_in cursor_name
+			| BACKWARD SignedIconst LIMIT Iconst  opt_overhead opt_from_in cursor_name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
-					n->portalname = $6;
+					n->portalname = $7;
 					n->direction = FETCH_BACKWARD;
 					n->howMany = $2;
 					n->howLarge = $4;
+					n->tupoverhead = $5;
 					$$ = (Node *)n;
 				}
 			| BACKWARD ALL opt_from_in cursor_name
@@ -6122,13 +6129,14 @@ fetch_args:	cursor_name
 					n->howMany = FETCH_ALL;
 					$$ = (Node *)n;
 				}
-			| BACKWARD ALL LIMIT Iconst opt_from_in cursor_name
+			| BACKWARD ALL LIMIT Iconst  opt_overhead opt_from_in cursor_name
 				{
 					FetchStmt *n = makeNode(FetchStmt);
-					n->portalname = $6;
+					n->portalname = $7;
 					n->direction = FETCH_BACKWARD;
 					n->howMany = FETCH_ALL;
 					n->howLarge = $4;
+					n->tupoverhead = $5;
 					$$ = (Node *)n;
 				}
 		;
@@ -6141,6 +6149,9 @@ opt_from_in:	from_in								{}
 			| /* EMPTY */							{}
 		;
 
+opt_overhead:	'(' Iconst ')'						{ $$ = $2;}
+			| /* EMPTY */							{ $$ = 0; }
+		;
 
 /*****************************************************************************
  *
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 55f062b..5261197 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -1043,7 +1043,7 @@ exec_simple_query(const char *query_string)
 		 */
 		(void) PortalRun(portal,
 						 FETCH_ALL,
-						 0,
+						 0L, 0,
 						 isTopLevel,
 						 receiver,
 						 receiver,
@@ -1929,7 +1929,7 @@ exec_execute_message(const char *portal_name, long max_rows)
 
 	completed = PortalRun(portal,
 						  max_rows,
-						  0,
+						  0L, 0,
 						  true, /* always top level */
 						  receiver,
 						  receiver,
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 1456c5a..6628b19 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -41,9 +41,10 @@ static void ProcessQuery(PlannedStmt *plan,
 			 char *completionTag);
 static void FillPortalStore(Portal portal, bool isTopLevel);
 static uint32 RunFromStore(Portal portal, ScanDirection direction,
-		     long count, long size, bool *stoppedbysize,
+		     long count, long size, int tupoverhead, bool *stoppedbysize,
 			 DestReceiver *dest);
-static long PortalRunSelect(Portal portal, bool forward, long count, long size,
+static long PortalRunSelect(Portal portal, bool forward,
+				long count, long size, int tupoverhead,
 				DestReceiver *dest);
 static void PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
 				 DestReceiver *dest, char *completionTag);
@@ -54,6 +55,7 @@ static long DoPortalRunFetch(Portal portal,
 				 FetchDirection fdirection,
 				 long count,
 				 long size,
+				 int tupoverehad,
 				 DestReceiver *dest);
 static void DoPortalRewind(Portal portal);
 
@@ -185,7 +187,7 @@ ProcessQuery(PlannedStmt *plan,
 	/*
 	 * Run the plan to completion.
 	 */
-	ExecutorRun(queryDesc, ForwardScanDirection, 0L, 0L);
+	ExecutorRun(queryDesc, ForwardScanDirection, 0L, 0L, 0);
 
 	/*
 	 * Build command completion status string, if caller wants one.
@@ -706,8 +708,8 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
  * suspended due to exhaustion of the count parameter.
  */
 bool
-PortalRun(Portal portal, long count, long size, bool isTopLevel,
-		  DestReceiver *dest, DestReceiver *altdest,
+PortalRun(Portal portal, long count, long size, int tupoverhead,
+		  bool isTopLevel, DestReceiver *dest, DestReceiver *altdest,
 		  char *completionTag)
 {
 	bool		result;
@@ -790,7 +792,8 @@ PortalRun(Portal portal, long count, long size, bool isTopLevel,
 				/*
 				 * Now fetch desired portion of results.
 				 */
-				nprocessed = PortalRunSelect(portal, true, count, size, dest);
+				nprocessed = PortalRunSelect(portal, true,
+											 count, size, tupoverhead, dest);
 
 				/*
 				 * If the portal result contains a command tag and the caller
@@ -896,6 +899,7 @@ PortalRunSelect(Portal portal,
 				bool forward,
 				long count,
 				long size,
+				int  tupoverhead,
 				DestReceiver *dest)
 {
 	QueryDesc  *queryDesc;
@@ -944,12 +948,13 @@ PortalRunSelect(Portal portal,
 			count = 0;
 
 		if (portal->holdStore)
-			nprocessed = RunFromStore(portal, direction, count,
-									  size, &stoppedbysize, dest);
+			nprocessed = RunFromStore(portal, direction,
+									  count, size, tupoverhead,
+									  &stoppedbysize, dest);
 		else
 		{
 			PushActiveSnapshot(queryDesc->snapshot);
-			ExecutorRun(queryDesc, direction, count, size);
+			ExecutorRun(queryDesc, direction, count, size, tupoverhead);
 			nprocessed = queryDesc->estate->es_processed;
 			stoppedbysize = queryDesc->estate->es_stoppedbysize;
 			PopActiveSnapshot();
@@ -990,12 +995,13 @@ PortalRunSelect(Portal portal,
 			count = 0;
 
 		if (portal->holdStore)
-			nprocessed = RunFromStore(portal, direction, count,
-									  size, &stoppedbysize, dest);
+			nprocessed = RunFromStore(portal, direction,
+									  count, size, tupoverhead,
+									  &stoppedbysize, dest);
 		else
 		{
 			PushActiveSnapshot(queryDesc->snapshot);
-			ExecutorRun(queryDesc, direction, count, size);
+			ExecutorRun(queryDesc, direction, count, size, tupoverhead);
 			nprocessed = queryDesc->estate->es_processed;
 			stoppedbysize = queryDesc->estate->es_stoppedbysize;
 			PopActiveSnapshot();
@@ -1099,8 +1105,9 @@ FillPortalStore(Portal portal, bool isTopLevel)
  * out for memory leaks.
  */
 static uint32
-RunFromStore(Portal portal, ScanDirection direction, long count,
-			 long size_limit, bool *stoppedbysize, DestReceiver *dest)
+RunFromStore(Portal portal, ScanDirection direction,
+			 long count, long size_limit, int tupoverhead,
+			 bool *stoppedbysize, DestReceiver *dest)
 {
 	long		current_tuple_count = 0;
 	TupleTableSlot *slot;
@@ -1138,7 +1145,7 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
 			(*dest->receiveSlot) (slot, dest);
 
 			/* Count the size of tuples we've sent */
-			sent_size += slot_compute_attr_size(slot);
+			sent_size += slot_compute_attr_size(slot) + tupoverhead;
 
 			ExecClearTuple(slot);
 
@@ -1411,6 +1418,7 @@ PortalRunFetch(Portal portal,
 			   FetchDirection fdirection,
 			   long count,
 			   long size,
+			   int  tupoverhead,
 			   DestReceiver *dest)
 {
 	long		result;
@@ -1448,7 +1456,8 @@ PortalRunFetch(Portal portal,
 		switch (portal->strategy)
 		{
 			case PORTAL_ONE_SELECT:
-				result = DoPortalRunFetch(portal, fdirection, count, size, dest);
+				result = DoPortalRunFetch(portal, fdirection,
+										  count, size, tupoverhead, dest);
 				break;
 
 			case PORTAL_ONE_RETURNING:
@@ -1465,7 +1474,8 @@ PortalRunFetch(Portal portal,
 				/*
 				 * Now fetch desired portion of results.
 				 */
-				result = DoPortalRunFetch(portal, fdirection, count, size, dest);
+				result = DoPortalRunFetch(portal, fdirection,
+										  count, size, tupoverhead, dest);
 				break;
 
 			default:
@@ -1511,6 +1521,7 @@ DoPortalRunFetch(Portal portal,
 				 FetchDirection fdirection,
 				 long count,
 				 long size,
+				 int  tupoverhead,
 				 DestReceiver *dest)
 {
 	bool		forward;
@@ -1553,7 +1564,7 @@ DoPortalRunFetch(Portal portal,
 				{
 					DoPortalRewind(portal);
 					if (count > 1)
-						PortalRunSelect(portal, true, count - 1, 0L,
+						PortalRunSelect(portal, true, count - 1, 0L, 0,
 										None_Receiver);
 				}
 				else
@@ -1563,13 +1574,15 @@ DoPortalRunFetch(Portal portal,
 					if (portal->atEnd)
 						pos++;	/* need one extra fetch if off end */
 					if (count <= pos)
-						PortalRunSelect(portal, false, pos - count + 1, 0L,
+						PortalRunSelect(portal, false,
+										pos - count + 1, 0L, 0,
 										None_Receiver);
 					else if (count > pos + 1)
-						PortalRunSelect(portal, true, count - pos - 1, 0L,
+						PortalRunSelect(portal, true,
+										count - pos - 1, 0L, 0,
 										None_Receiver);
 				}
-				return PortalRunSelect(portal, true, 1L, 0L, dest);
+				return PortalRunSelect(portal, true, 1L, 0L, 0, dest);
 			}
 			else if (count < 0)
 			{
@@ -1580,17 +1593,19 @@ DoPortalRunFetch(Portal portal,
 				 * (Is it worth considering case where count > half of size of
 				 * query?  We could rewind once we know the size ...)
 				 */
-				PortalRunSelect(portal, true, FETCH_ALL, 0L, None_Receiver);
+				PortalRunSelect(portal, true,
+								FETCH_ALL, 0L, 0, None_Receiver);
 				if (count < -1)
-					PortalRunSelect(portal, false, -count - 1, 0, None_Receiver);
-				return PortalRunSelect(portal, false, 1L, 0L, dest);
+					PortalRunSelect(portal, false,
+									-count - 1, 0, 0, None_Receiver);
+				return PortalRunSelect(portal, false, 1L, 0L, 0, dest);
 			}
 			else
 			{
 				/* count == 0 */
 				/* Rewind to start, return zero rows */
 				DoPortalRewind(portal);
-				return PortalRunSelect(portal, true, 0L, 0L, dest);
+				return PortalRunSelect(portal, true, 0L, 0L, 0, dest);
 			}
 			break;
 		case FETCH_RELATIVE:
@@ -1600,8 +1615,9 @@ DoPortalRunFetch(Portal portal,
 				 * Definition: advance count-1 rows, return next row (if any).
 				 */
 				if (count > 1)
-					PortalRunSelect(portal, true, count - 1, 0L, None_Receiver);
-				return PortalRunSelect(portal, true, 1L, 0L, dest);
+					PortalRunSelect(portal, true,
+									count - 1, 0L, 0, None_Receiver);
+				return PortalRunSelect(portal, true, 1L, 0L, 0, dest);
 			}
 			else if (count < 0)
 			{
@@ -1610,8 +1626,9 @@ DoPortalRunFetch(Portal portal,
 				 * any).
 				 */
 				if (count < -1)
-					PortalRunSelect(portal, false, -count - 1, 0L, None_Receiver);
-				return PortalRunSelect(portal, false, 1L, 0L, dest);
+					PortalRunSelect(portal, false,
+									-count - 1, 0L, 0, None_Receiver);
+				return PortalRunSelect(portal, false, 1L, 0L, 0, dest);
 			}
 			else
 			{
@@ -1657,7 +1674,7 @@ DoPortalRunFetch(Portal portal,
 			 */
 			if (on_row)
 			{
-				PortalRunSelect(portal, false, 1L, 0L, None_Receiver);
+				PortalRunSelect(portal, false, 1L, 0L, 0, None_Receiver);
 				/* Set up to fetch one row forward */
 				count = 1;
 				forward = true;
@@ -1679,7 +1696,7 @@ DoPortalRunFetch(Portal portal,
 		return result;
 	}
 
-	return PortalRunSelect(portal, forward, count, size, dest);
+	return PortalRunSelect(portal, forward, count, size, tupoverhead, dest);
 }
 
 /*
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 64a02c3..316568f 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -81,7 +81,7 @@ extern PGDLLIMPORT ExecutorStart_hook_type ExecutorStart_hook;
 /* Hook for plugins to get control in ExecutorRun() */
 typedef void (*ExecutorRun_hook_type) (QueryDesc *queryDesc,
 									   ScanDirection direction,
-									   long count, long size);
+									   long count, long size, int tupoverhead);
 extern PGDLLIMPORT ExecutorRun_hook_type ExecutorRun_hook;
 
 /* Hook for plugins to get control in ExecutorFinish() */
@@ -176,9 +176,9 @@ extern TupleTableSlot *ExecFilterJunk(JunkFilter *junkfilter,
 extern void ExecutorStart(QueryDesc *queryDesc, int eflags);
 extern void standard_ExecutorStart(QueryDesc *queryDesc, int eflags);
 extern void ExecutorRun(QueryDesc *queryDesc,
-			ScanDirection direction, long count, long size);
+		ScanDirection direction, long count, long size, int tupoverhead);
 extern void standard_ExecutorRun(QueryDesc *queryDesc,
-		    ScanDirection direction, long count, long size);
+		 ScanDirection direction, long count, long size, int tupoverhead);
 extern void ExecutorFinish(QueryDesc *queryDesc);
 extern void standard_ExecutorFinish(QueryDesc *queryDesc);
 extern void ExecutorEnd(QueryDesc *queryDesc);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 9e18331..f86694b 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2224,6 +2224,7 @@ typedef struct FetchStmt
 	FetchDirection direction;	/* see above */
 	long		howMany;		/* number of rows, or position argument */
 	long		howLarge;		/* total bytes of rows */
+	int			tupoverhead;	/* declared overhead per tuple in client */
 	char	   *portalname;		/* name of portal (cursor) */
 	bool		ismove;			/* TRUE if MOVE */
 } FetchStmt;
diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h
index afffe86..021532c 100644
--- a/src/include/tcop/pquery.h
+++ b/src/include/tcop/pquery.h
@@ -17,7 +17,6 @@
 #include "nodes/parsenodes.h"
 #include "utils/portal.h"
 
-
 extern PGDLLIMPORT Portal ActivePortal;
 
 
@@ -33,14 +32,15 @@ extern void PortalStart(Portal portal, ParamListInfo params,
 extern void PortalSetResultFormat(Portal portal, int nFormats,
 					  int16 *formats);
 
-extern bool PortalRun(Portal portal, long count, long size, bool isTopLevel,
-		  DestReceiver *dest, DestReceiver *altdest,
+extern bool PortalRun(Portal portal, long count, long size, int tupoverhead,
+		  bool isTopLevel, DestReceiver *dest, DestReceiver *altdest,
 		  char *completionTag);
 
 extern long PortalRunFetch(Portal portal,
 			   FetchDirection fdirection,
 			   long count,
 			   long size,
+			   int tupoverhead,
 			   DestReceiver *dest);
 
 #endif   /* PQUERY_H */
diff --git a/src/interfaces/ecpg/preproc/Makefile b/src/interfaces/ecpg/preproc/Makefile
index b492fa7..1ecc405 100644
--- a/src/interfaces/ecpg/preproc/Makefile
+++ b/src/interfaces/ecpg/preproc/Makefile
@@ -48,7 +48,7 @@ ecpg: $(OBJS) | submake-libpgport
 preproc.o: pgc.c
 
 preproc.h: preproc.c ;
-preproc.c: BISONFLAGS += -r all -d
+preproc.c: BISONFLAGS += -d
 
 preproc.y: ../../../backend/parser/gram.y parse.pl ecpg.addons ecpg.header ecpg.tokens ecpg.trailer ecpg.type
 	$(PERL) $(srcdir)/parse.pl $(srcdir) < $< > $@
diff --git a/src/interfaces/ecpg/preproc/ecpg.addons b/src/interfaces/ecpg/preproc/ecpg.addons
index bdccb68..424f412 100644
--- a/src/interfaces/ecpg/preproc/ecpg.addons
+++ b/src/interfaces/ecpg/preproc/ecpg.addons
@@ -235,31 +235,41 @@ ECPG: fetch_argsBACKWARDopt_from_incursor_name addon
 			free($3);
 			$3 = mm_strdup("$0");
 		}
-ECPG: fetch_argsALLLIMITIconstopt_from_incursor_name addon
-		add_additional_variables($5, false);
-		if ($5[0] == ':')
+ECPG: fetch_argsALLLIMITIconstopt_overheadopt_from_incursor_name addon
+		add_additional_variables($6, false);
+		if ($6[0] == ':')
 		{
-			free($5);
-			$5 = mm_strdup("$0");
+			free($6);
+			$6 = mm_strdup("$0");
 		}
 		if ($3[0] == '$')
 		{
 			free($3);
 			$3 = mm_strdup("$0");
 		}
-ECPG: fetch_argsFORWARDALLLIMITIconstopt_from_incursor_name addon
-ECPG: fetch_argsBACKWARDALLLIMITIconstopt_from_incursor_name addon
-		add_additional_variables($6, false);
-		if ($6[0] == ':')
+		if ($4[0] == '$')
 		{
-			free($6);
-			$6 = mm_strdup("$0");
+			free($4);
+			$4 = mm_strdup("$0");
+		}
+ECPG: fetch_argsFORWARDALLLIMITIconstopt_overheadopt_from_incursor_name addon
+ECPG: fetch_argsBACKWARDALLLIMITIconstopt_overheadopt_from_incursor_name addon
+		add_additional_variables($7, false);
+		if ($7[0] == ':')
+		{
+			free($7);
+			$7 = mm_strdup("$0");
 		}
 		if ($4[0] == '$')
 		{
 			free($4);
 			$4 = mm_strdup("$0");
 		}
+		if ($5[0] == '$')
+		{
+			free($5);
+			$5 = mm_strdup("$0");
+		}
 ECPG: fetch_argsSignedIconstopt_from_incursor_name addon
 		add_additional_variables($3, false);
 		if ($3[0] == ':')
@@ -267,12 +277,12 @@ ECPG: fetch_argsSignedIconstopt_from_incursor_name addon
 			free($3);
 			$3 = mm_strdup("$0");
 		}
-ECPG: fetch_argsSignedIconstLIMITIconstopt_from_incursor_name addon
-		add_additional_variables($5, false);
-		if ($5[0] == ':')
+ECPG: fetch_argsSignedIconstLIMITIconstopt_overheadopt_from_incursor_name addon
+		add_additional_variables($6, false);
+		if ($6[0] == ':')
 		{
-			free($5);
-			$5 = mm_strdup("$0");
+			free($6);
+			$6 = mm_strdup("$0");
 		}
 		if ($1[0] == '$')
 		{
@@ -284,13 +294,18 @@ ECPG: fetch_argsSignedIconstLIMITIconstopt_from_incursor_name addon
 			free($3);
 			$3 = mm_strdup("$0");
 		}
-ECPG: fetch_argsFORWARDSignedIconstLIMITIconstopt_from_incursor_name addon
-ECPG: fetch_argsBACKWARDSignedIconstLIMITIconstopt_from_incursor_name addon
-		add_additional_variables($6, false);
-		if ($6[0] == ':')
+		if ($4[0] == '$')
 		{
-			free($6);
-			$6 = mm_strdup("$0");
+			free($4);
+			$4 = mm_strdup("$0");
+		}
+ECPG: fetch_argsFORWARDSignedIconstLIMITIconstopt_overheadopt_from_incursor_name addon
+ECPG: fetch_argsBACKWARDSignedIconstLIMITIconstopt_overheadopt_from_incursor_name addon
+		add_additional_variables($7, false);
+		if ($7[0] == ':')
+		{
+			free($7);
+			$7 = mm_strdup("$0");
 		}
 		if ($2[0] == '$')
 		{
@@ -302,6 +317,11 @@ ECPG: fetch_argsBACKWARDSignedIconstLIMITIconstopt_from_incursor_name addon
 			free($4);
 			$4 = mm_strdup("$0");
 		}
+		if ($5[0] == '$')
+		{
+			free($5);
+			$5 = mm_strdup("$0");
+		}
 ECPG: fetch_argsFORWARDALLopt_from_incursor_name addon
 ECPG: fetch_argsBACKWARDALLopt_from_incursor_name addon
 		add_additional_variables($4, false);
-- 
2.1.0.GIT

