diff --git a/doc/src/sgml/ecpg.sgml b/doc/src/sgml/ecpg.sgml
index 68833ca..55feac2 100644
--- a/doc/src/sgml/ecpg.sgml
+++ b/doc/src/sgml/ecpg.sgml
@@ -454,6 +454,32 @@ EXEC SQL COMMIT;
    details.
   </para>
 
+  <para>
+   ECPG may use cursor readahead to improve performance of programs
+   that use single-row FETCH statements. Option <literal>-r fetch_readahead</literal>
+   option for ECPG modifies the default for all cursors from <literal>NO READAHEAD</literal>
+   to <literal>READAHEAD</literal>. Explicit <literal>READAHEAD</literal> or
+   <literal>NO READAHEAD</literal> turns cursor readahead on or off for the specified cursor,
+   respectively. The default readahead size is 256 rows, this may be modified by setting
+   the <literal>ECPGFETCHSZ</literal> environment variable to a different value.
+  </para>
+
+  <para>
+   Scrolling behaviour differs from the documented one at <xref linkend="sql-declare">
+   when readahead is used for a cursor. ECPG treats cursors as <literal>NO SCROLL</literal>
+   when neither <literal>SCROLL</literal> nor <literal>NO SCROLL</literal> are specified.
+   When backward fetching or positioning is attempted on a <literal>NO SCROLL READAHEAD</literal>
+   cursor, error code 55000 (Object not in prerequisite state) is returned to the application.
+  </para>
+
+  <para>
+   For cursors used in <command>UPDATE</command> or <command>DELETE</command>
+   with the <literal>WHERE CURRENT OF</literal> clause, cursor readahead must be
+   turned off, otherwise <command>ecpg</command> throws an error. This restriction
+   is because when the client side uses readahead, the idea about the cursor position
+   in the dataset may differ between the server and the client.
+  </para>
+
    <note>
     <para>
      The ECPG <command>DECLARE</command> command does not actually
@@ -5289,6 +5315,17 @@ while (1)
     </varlistentry>
 
     <varlistentry>
+     <term>-231 (<symbol>ECPG_INVALID_CURSOR</symbol>)</term>
+     <listitem>
+      <para>
+       The cursor you are trying to use with readahead has not been opened yet (SQLSTATE 34000),
+       invalid values were passed to libecpg (SQLSTATE 42P11) hor not in prerequisite state, i.e.
+       FETCH or MOVE BACKWARD was attempted on a non-SCROLL cursor (SQLSTATE 55000).
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
      <term>-239 (<symbol>ECPG_INFORMIX_DUPLICATE_KEY</symbol>)</term>
      <listitem>
       <para>
@@ -6583,8 +6620,8 @@ EXEC SQL DEALLOCATE DESCRIPTOR mydesc;
 
    <refsynopsisdiv>
 <synopsis>
-DECLARE <replaceable class="PARAMETER">cursor_name</replaceable> [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ] CURSOR [ { WITH | WITHOUT } HOLD ] FOR <replaceable class="PARAMETER">prepared_name</replaceable>
-DECLARE <replaceable class="PARAMETER">cursor_name</replaceable> [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ] CURSOR [ { WITH | WITHOUT } HOLD ] FOR <replaceable class="PARAMETER">query</replaceable>
+DECLARE <replaceable class="PARAMETER">cursor_name</replaceable> [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ] [ [ NO ] READAHEAD ] CURSOR [ { WITH | WITHOUT } HOLD ] FOR <replaceable class="PARAMETER">prepared_name</replaceable>
+DECLARE <replaceable class="PARAMETER">cursor_name</replaceable> [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ] [ [ NO ] READAHEAD ] CURSOR [ { WITH | WITHOUT } HOLD ] FOR <replaceable class="PARAMETER">query</replaceable>
 </synopsis>
    </refsynopsisdiv>
 
@@ -6639,11 +6676,36 @@ DECLARE <replaceable class="PARAMETER">cursor_name</replaceable> [ BINARY ] [ IN
       </listitem>
      </varlistentry>
     </variablelist>
+   </refsect1>
+
+   <refsect1>
+    <title>Cursor options</title>
 
     <para>
-     For the meaning of the cursor options,
-     see <xref linkend="sql-declare">.
+     For the meaning of other cursor options, see <xref linkend="sql-declare">.
     </para>
+
+    <variablelist>
+     <varlistentry>
+      <term><literal>READAHEAD</literal></term>   
+      <term><literal>NO READAHEAD</literal></term>   
+       <listitem>
+        <para>
+         <literal>READAHEAD</literal> makes the ECPG preprocessor and runtime library
+         use a client-side cursor accounting and data readahead during
+         <command>FETCH</command>. This improves performance for programs that use
+         single-row <command>FETCH</command> statements.
+        </para>
+
+        <para>
+         <literal>NO READAHEAD</literal> disables data readahead in case
+         <parameter>-r fetch_readahead</parameter> is used for compiling the file.
+        </para>
+       </listitem>
+      </varlistentry>
+
+    </variablelist>
+
    </refsect1>
 
    <refsect1>
diff --git a/doc/src/sgml/ref/ecpg-ref.sgml b/doc/src/sgml/ref/ecpg-ref.sgml
index 9c13e93..22dfe44 100644
--- a/doc/src/sgml/ref/ecpg-ref.sgml
+++ b/doc/src/sgml/ref/ecpg-ref.sgml
@@ -166,6 +166,14 @@ PostgreSQL documentation
          </para>
          </listitem>
         </varlistentry>
+        <varlistentry>
+         <term><option>fetch_readahead</option></term>
+         <listitem>
+         <para>
+         Turn on cursor readahead by default.
+         </para>
+         </listitem>
+        </varlistentry>
        </variablelist></para>
      </listitem>
     </varlistentry>
diff --git a/src/interfaces/ecpg/ecpglib/Makefile b/src/interfaces/ecpg/ecpglib/Makefile
index 2b2ffb6..1f46ff6 100644
--- a/src/interfaces/ecpg/ecpglib/Makefile
+++ b/src/interfaces/ecpg/ecpglib/Makefile
@@ -25,7 +25,7 @@ override CFLAGS += $(PTHREAD_CFLAGS)
 LIBS := $(filter-out -lpgport, $(LIBS))
 
 OBJS= execute.o typename.o descriptor.o sqlda.o data.o error.o prepare.o memory.o \
-	connect.o misc.o path.o pgstrcasecmp.o \
+	connect.o misc.o path.o pgstrcasecmp.o cursor.o \
 	$(filter snprintf.o strlcpy.o win32setlocale.o, $(LIBOBJS))
 
 # thread.c is needed only for non-WIN32 implementation of path.c
diff --git a/src/interfaces/ecpg/ecpglib/connect.c b/src/interfaces/ecpg/ecpglib/connect.c
index 997046b..7eda052 100644
--- a/src/interfaces/ecpg/ecpglib/connect.c
+++ b/src/interfaces/ecpg/ecpglib/connect.c
@@ -456,6 +456,7 @@ ECPGconnect(int lineno, int c, const char *name, const char *user, const char *p
 
 	this->cache_head = NULL;
 	this->prep_stmts = NULL;
+	this->cursor_desc = NULL;
 
 	if (all_connections == NULL)
 		this->next = NULL;
diff --git a/src/interfaces/ecpg/ecpglib/cursor.c b/src/interfaces/ecpg/ecpglib/cursor.c
new file mode 100644
index 0000000..bcaf913
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/cursor.c
@@ -0,0 +1,730 @@
+/*
+ * FETCH readahead support routines
+ */
+
+#define POSTGRES_ECPG_INTERNAL
+#include "postgres_fe.h"
+
+#include <limits.h>
+
+#include "ecpgtype.h"
+#include "ecpglib.h"
+#include "ecpgerrno.h"
+#include "extern.h"
+
+static struct cursor_descriptor *add_cursor(int lineno, struct connection *con, const char *name, bool scrollable, int64 n_tuples, bool *existing);
+static struct cursor_descriptor *find_cursor(struct connection *con, const char *name);
+static void del_cursor(struct connection *con, const char *name);
+
+/* default fetch size, set on the first call to ECPGopen()  */
+#define DEFAULTFETCHSIZE	(256)
+static int	fetch_size = 0;
+
+/*
+ * Add a new cursor descriptor, maintain alphabetic order
+ */
+static struct cursor_descriptor *
+add_cursor(int lineno, struct connection *con, const char *name, bool scrollable, int64 n_tuples, bool *existing)
+{
+	struct cursor_descriptor *desc,
+				*ptr, *prev = NULL;
+	bool	found = false;
+
+	if (!name || name[0] == '\0')
+	{
+		if (existing)
+			*existing = false;
+		return NULL;
+	}
+
+	ptr = con->cursor_desc;
+	while (ptr)
+	{
+		int ret = strcasecmp(ptr->name, name);
+
+		if (ret == 0)
+		{
+			found = true;
+			break;
+		}
+		if (ret > 0)
+			break;
+
+		prev = ptr;
+		ptr = ptr->next;
+	}
+
+	if (found)
+	{
+		if (existing)
+			*existing = true;
+		return ptr;
+	}
+
+	desc = (struct cursor_descriptor *)ecpg_alloc(sizeof(struct cursor_descriptor), lineno);
+	if (!desc)
+		return NULL;
+	desc->name = ecpg_strdup(name, lineno);
+	if (!desc->name)
+	desc->res = NULL;
+	desc->scrollable = scrollable;
+	desc->n_tuples = n_tuples;
+	desc->start_pos = 0;
+	desc->cache_pos = 0;
+	desc->next = ptr;
+
+	if (prev)
+		prev->next = desc;
+	else
+		con->cursor_desc = desc;
+
+	if (existing)
+		*existing = false;
+	return desc;
+}
+
+static struct cursor_descriptor *
+find_cursor(struct connection *con, const char *name)
+{
+	struct cursor_descriptor *desc = con->cursor_desc;
+	bool	found = false;
+
+	if (!name)
+		return NULL;
+
+	while (desc)
+	{
+		int ret = strcasecmp(desc->name, name);
+
+		if (ret == 0)
+		{
+			found = true;
+			break;
+		}
+		if (ret > 0)
+			break;
+		desc = desc->next;
+	}
+
+	return (found ? desc : NULL);
+}
+
+static void
+del_cursor(struct connection *con, const char *name)
+{
+	struct cursor_descriptor *ptr, *prev = NULL;
+	bool	found = false;
+
+	ptr = con->cursor_desc;
+	while (ptr)
+	{
+		int ret = strcasecmp(ptr->name, name);
+
+		if (ret == 0)
+		{
+			found = true;
+			break;
+		}
+		if (ret > 0)
+			break;
+
+		prev = ptr;
+		ptr = ptr->next;
+	}
+
+	if (found)
+	{
+		if (prev)
+			prev->next = ptr->next;
+		else
+			con->cursor_desc = ptr->next;
+
+		ecpg_free(ptr->name);
+		if (ptr->res)
+			PQclear(ptr->res);
+		ecpg_free(ptr);
+	}
+}
+
+bool
+ECPGopen(const int lineno, const int compat, const int force_indicator,
+		const char *connection_name, const bool questionmarks,
+		const char *curname, const int st, const char *query, ...)
+{
+	va_list		args;
+	bool		ret, scrollable;
+	char	   *new_query, *ptr, *whold, *noscroll, *scroll, *dollar0;
+	struct sqlca_t *sqlca = ECPGget_sqlca();
+
+	if (!query)
+	{
+		ecpg_raise(lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL);
+		return false;
+	}
+	ptr = strstr(query, "for ");
+	if (!ptr)
+	{
+		ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL);
+		return false;
+	}
+	whold = strstr(query, "with hold ");
+	dollar0 = strstr(query, "$0");
+
+	noscroll = strstr(query, "no scroll ");
+	scroll = strstr(query, "scroll ");
+	scrollable = (noscroll == NULL) && (scroll != NULL) && (scroll < ptr);
+
+	new_query = ecpg_alloc(strlen(curname) + strlen(ptr) + (whold ? 10 : 0) + 32, lineno);
+	if (!new_query)
+		return false;
+	sprintf(new_query, "declare %s %s cursor %s%s",
+					(dollar0 && (dollar0 < ptr) ? "$0" : curname),
+					(scrollable ? "scroll" : "no scroll"),
+					(whold ? "with hold " : ""),
+					ptr);
+
+	/* Set the fetch size the first time we are called. */
+	if (fetch_size == 0)
+	{
+		char	   *fsize_str = getenv("ECPGFETCHSZ");
+		char	   *endptr = NULL;
+		int		fsize;
+
+		if (fsize_str)
+		{
+			fsize = strtoul(fsize_str, &endptr, 10);
+			if (endptr || (fsize < 4))
+				fetch_size = DEFAULTFETCHSIZE;
+			else
+				fetch_size = fsize;
+		}
+		else
+			fetch_size = DEFAULTFETCHSIZE;
+	}
+
+	va_start(args, query);
+	ret = ecpg_do(lineno, compat, force_indicator, connection_name, questionmarks, st, new_query, args);
+	va_end(args);
+
+	ecpg_free(new_query);
+
+	/*
+	 * If statement went OK, add the cursor and discover the
+	 * number of rows in the recordset. This will slow down OPEN
+	 * but we gain a lot with caching.
+	 */
+	if (ret /* && sqlca->sqlerrd[2] == 0 */)
+	{
+		struct connection *con = ecpg_get_connection(connection_name);
+		struct cursor_descriptor *cur;
+		bool	existing;
+		int64	n_tuples;
+
+		if (scrollable)
+		{
+			PGresult   *res;
+			char	   *query;
+			char	   *endptr = NULL;
+
+			query = ecpg_alloc(strlen(curname) + strlen("move all in ") + 2, lineno);
+			sprintf(query, "move all in %s", curname);
+			res = PQexec(con->connection, query);
+			n_tuples = strtoull(PQcmdTuples(res), &endptr, 10);
+			PQclear(res);
+			ecpg_free(query);
+
+			/* Go back to the beginning of the resultset. */
+			query = ecpg_alloc(strlen(curname) + strlen("move absolute 0 in ") + 2, lineno);
+			sprintf(query, "move absolute 0 in %s", curname);
+			res = PQexec(con->connection, query);
+			PQclear(res);
+			ecpg_free(query);
+		}
+		else
+		{
+			n_tuples = 0;
+		}
+
+		/* Add the cursor */
+		cur = add_cursor(lineno, con, curname, scrollable, n_tuples, &existing);
+
+		/*
+		 * Report the number of tuples for the [scrollable] cursor.
+		 * The server didn't do it for us.
+		 */
+		sqlca->sqlerrd[2] = (cur->n_tuples < LONG_MAX ? cur->n_tuples : LONG_MAX);
+	}
+
+	return ret;
+}
+
+static bool
+ecpg_cursor_execute(struct statement * stmt, struct cursor_descriptor *cur)
+{
+	char		tmp[64];
+	char	   *query;
+	int64		start_pos;
+
+	if ((cur->cache_pos >= cur->start_pos) && cur->res && (cur->cache_pos < cur->start_pos + PQntuples(cur->res)))
+	{
+		stmt->results = cur->res;
+		ecpg_free_params(stmt, true, stmt->lineno);
+		return true;
+	}
+	else if (!cur->scrollable && cur->res && (PQntuples(cur->res) < fetch_size) && (cur->cache_pos >= cur->start_pos + PQntuples(cur->res)))
+	{
+		cur->endoftuples = true;
+		stmt->results = cur->res;
+		ecpg_free_params(stmt, true, stmt->lineno);
+		return true;
+	}
+
+	if ((PQtransactionStatus(stmt->connection->connection) == PQTRANS_IDLE) && !stmt->connection->autocommit)
+	{
+		stmt->results = PQexec(stmt->connection->connection, "begin transaction");
+		if (!ecpg_check_PQresult(stmt->results, stmt->lineno, stmt->connection->connection, stmt->compat))
+		{
+			ecpg_free_params(stmt, false, stmt->lineno);
+			return false;
+		}
+		PQclear(stmt->results);
+	}
+
+	/*
+	 * Compute the tuple position before the resultset. E.g.:
+	 * MOVE ABSOLUTE 0 + FETCH NEXT <fetch_size> will result
+	 * in a recordset having tuples 1 ... fetch_size
+	 */
+	start_pos = (cur->cache_pos - 1) / fetch_size;
+	start_pos *= fetch_size;
+
+	if (cur->scrollable)
+	{
+		sprintf(tmp, "%lld", (long long)start_pos);
+		query = ecpg_alloc(strlen(tmp) + strlen(cur->name) + 20, stmt->lineno);
+		sprintf(query, "move absolute %s in %s", tmp, cur->name);
+
+		ecpg_log("ecpg_cursor_execute on line %d: query: %s; on connection %s\n", stmt->lineno, query, stmt->connection->name);
+
+		stmt->results = PQexec(stmt->connection->connection, query);
+
+		if (!ecpg_check_PQresult(stmt->results, stmt->lineno, stmt->connection->connection, stmt->compat))
+		{
+			ecpg_free_params(stmt, false, stmt->lineno);
+			return false;
+		}
+
+		PQclear(stmt->results);
+		ecpg_free(query);
+	}
+
+	sprintf(tmp, "%lld", (long long)fetch_size);
+	query = ecpg_alloc(strlen(tmp) + strlen(cur->name) + 24, stmt->lineno);
+	sprintf(query, "fetch forward %s from %s", tmp, cur->name);
+
+	if (cur->res)
+		PQclear(cur->res);
+
+	ecpg_log("ecpg_cursor_execute on line %d: query: %s; with %d parameter(s) on connection %s\n", stmt->lineno, query, stmt->nparams, stmt->connection->name);
+
+	if (stmt->nparams == 0)
+	{
+		cur->res = PQexec(stmt->connection->connection, query);
+		ecpg_log("ecpg_cursor_execute on line %d: using PQexec\n", stmt->lineno);
+	}
+	else
+	{
+		/* shouldn't happen */
+		cur->res = PQexecParams(stmt->connection->connection, query, stmt->nparams, NULL, stmt->param_values, NULL, NULL, 0);
+		ecpg_log("ecpg_cursor_execute on line %d: using PQexecParams\n", stmt->lineno);
+	}
+
+	stmt->results = cur->res;
+
+	ecpg_free_params(stmt, true, stmt->lineno);
+
+	if (!ecpg_check_PQresult(cur->res, stmt->lineno, stmt->connection->connection, stmt->compat))
+	{
+		stmt->results = cur->res = NULL;
+		cur->start_pos = 0;
+		return false;
+	}
+
+	/* The tuple position in the cursor is 1 based. */
+	cur->start_pos = start_pos + 1;
+
+	if (!cur->scrollable && PQntuples(cur->res) == 0)
+		cur->endoftuples = true;
+
+	ecpg_free(query);
+
+	return true;
+}
+
+bool
+ECPGfetch(const int lineno, const int compat, const int force_indicator,
+		const char *connection_name, const bool questionmarks,
+		const char *curname, enum ECPG_cursor_direction direction, const char *amount,
+		const int st, const char *query, ...)
+{
+	struct cursor_descriptor *cur;
+	struct statement *stmt;
+	va_list			args;
+	bool			move, fetchall = false, negate = false;
+	const char	   *amount1 = amount;
+	int			step;
+	int64			n_amount, count, start_idx;
+	struct sqlca_t		   *sqlca = ECPGget_sqlca();
+
+	if (!query)
+	{
+		ecpg_raise(lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL);
+		return false;
+	}
+
+	move = (strncmp(query, "move ", 5) == 0);
+
+	cur = find_cursor(ecpg_get_connection(connection_name), curname);
+	if (!cur)
+	{
+		ecpg_raise(lineno, ECPG_INVALID_CURSOR, ECPG_SQLSTATE_INVALID_CURSOR_NAME, (connection_name) ? connection_name : ecpg_gettext("<empty>"));
+		return false;
+	}
+
+	va_start(args, query);
+
+	if (!ecpg_do_prologue(lineno, compat, force_indicator, connection_name, questionmarks, (enum ECPG_statement_type) st, query, args, &stmt))
+	{
+		va_end(args);
+		return false;
+	}
+
+	if (!ecpg_build_params(stmt))
+	{
+		ecpg_do_epilogue(stmt);
+		va_end(args);
+		return false;
+	}
+
+	if (amount[0] == '$')
+		amount1 = stmt->dollarzero[0];
+	else if (amount[0] == '-' || amount[0] == '+')
+	{
+		/*
+		 * Handle negative and explicitely positive constants in
+		 * FETCH/MOVE ABSOLUTE/RELATIVE const.
+		 * E.g. '-2' arrives as '- 2', '+2' arrives as '+ 2'.
+		 * strtoll() under Linux stops processing at the space.
+		 */
+		if (amount[0] == '-')
+			negate = true;
+		amount1 = amount + 1;
+		while (*amount1 == ' ')
+			amount1++;
+	}
+
+	if (strcmp(amount, "all") == 0)
+	{
+		fetchall = true;
+		if (cur->scrollable)
+		{
+			switch (direction)
+			{
+				case ECPGc_forward:
+					n_amount = cur->n_tuples - cur->cache_pos;
+					break;
+				case ECPGc_backward:
+					n_amount = cur->cache_pos;
+					break;
+				default:
+					ecpg_free_params(stmt, true, stmt->lineno);
+					ecpg_do_epilogue(stmt);
+					va_end(args);
+					ecpg_raise(lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL);
+					return false;
+			}
+		}
+		else
+			n_amount = LONG_MAX;
+	}
+	else
+	{
+		char	   *endptr;
+
+		n_amount = strtoll(amount1, &endptr, 10);
+		if (negate)
+			n_amount = -n_amount;
+	}
+
+	switch (direction)
+	{
+		case ECPGc_absolute:
+			if (cur->scrollable)
+			{
+				if (n_amount < 0)
+					n_amount = 1 + cur->n_tuples + n_amount;
+				else if (n_amount > cur->n_tuples)
+					n_amount = cur->n_tuples + 1;
+			}
+			else
+			{
+				if (n_amount < 0)
+				{
+					ecpg_free_params(stmt, true, stmt->lineno);
+					ecpg_do_epilogue(stmt);
+					va_end(args);
+					ecpg_raise(lineno, ECPG_INVALID_CURSOR, ECPG_SQLSTATE_OBJECT_NOT_IN_PREREQUISITE_STATE, NULL);
+					return false;
+				}
+				if (n_amount < cur->cache_pos)
+				{
+					ecpg_free_params(stmt, true, stmt->lineno);
+					ecpg_do_epilogue(stmt);
+					va_end(args);
+					ecpg_raise(lineno, ECPG_INVALID_CURSOR, ECPG_SQLSTATE_OBJECT_NOT_IN_PREREQUISITE_STATE, NULL);
+					return false;
+				}
+			}
+			cur->cache_pos = n_amount;
+
+			if (!move)
+			{
+				if (cur->cache_pos > 0 && ((cur->scrollable && cur->cache_pos <= cur->n_tuples) || (!cur->scrollable && !cur->endoftuples)))
+				{
+					if (!ecpg_cursor_execute(stmt, cur))
+					{
+						ecpg_free_params(stmt, true, stmt->lineno);
+						ecpg_do_epilogue(stmt);
+						va_end(args);
+						return false;
+					}
+
+					if (!cur->scrollable && cur->endoftuples)
+					{
+						ecpg_free_params(stmt, true, stmt->lineno);
+						ecpg_do_epilogue(stmt);
+						va_end(args);
+						ecpg_raise(lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL);
+						return false;
+					}
+
+					start_idx = cur->cache_pos - cur->start_pos;
+
+					if (!ecpg_process_output(stmt, start_idx, start_idx + 1, 0, true, false))
+					{
+						ecpg_free_params(stmt, true, stmt->lineno);
+						ecpg_do_epilogue(stmt);
+						va_end(args);
+						return false;
+					}
+				}
+				else
+				{
+					ecpg_free_params(stmt, true, stmt->lineno);
+					ecpg_do_epilogue(stmt);
+					va_end(args);
+					ecpg_raise(lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL);
+					return false;
+				}
+			}
+
+			sqlca->sqlerrd[2] = (cur->cache_pos && cur->cache_pos <= cur->n_tuples ? 1 : 0);
+			break;
+
+		case ECPGc_relative:
+relative:
+			if (!cur->scrollable)
+			{
+				if (n_amount < 0)
+				{
+					ecpg_free_params(stmt, true, stmt->lineno);
+					ecpg_do_epilogue(stmt);
+					va_end(args);
+					ecpg_raise(lineno, ECPG_INVALID_CURSOR, ECPG_SQLSTATE_OBJECT_NOT_IN_PREREQUISITE_STATE, NULL);
+					return false;
+				}
+			}
+
+			cur->cache_pos += n_amount;
+			if (cur->cache_pos < 0)
+				cur->cache_pos = 0;
+			else if (cur->cache_pos > cur->n_tuples)
+				cur->cache_pos = cur->n_tuples + 1;
+
+			if (!move)
+			{
+				if (cur->cache_pos > 0 && ((cur->scrollable && cur->cache_pos <= cur->n_tuples) || (!cur->scrollable && !cur->endoftuples)))
+				{
+					if (!ecpg_cursor_execute(stmt, cur))
+					{
+						ecpg_free_params(stmt, true, stmt->lineno);
+						ecpg_do_epilogue(stmt);
+						va_end(args);
+						return false;
+					}
+
+					if (!cur->scrollable && cur->endoftuples)
+					{
+						ecpg_free_params(stmt, true, stmt->lineno);
+						ecpg_do_epilogue(stmt);
+						va_end(args);
+						ecpg_raise(lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL);
+						return false;
+					}
+
+					start_idx = cur->cache_pos - cur->start_pos;
+
+					if (!ecpg_process_output(stmt, start_idx, start_idx + 1, 0, true, false))
+					{
+						ecpg_free_params(stmt, true, stmt->lineno);
+						ecpg_do_epilogue(stmt);
+						va_end(args);
+						return false;
+					}
+				}
+				else
+				{
+					ecpg_free_params(stmt, true, stmt->lineno);
+					ecpg_do_epilogue(stmt);
+					va_end(args);
+					ecpg_raise(lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL);
+					return false;
+				}
+			}
+
+			sqlca->sqlerrd[2] = (cur->cache_pos && cur->cache_pos <= cur->n_tuples ? 1 : 0);
+			break;
+
+		case ECPGc_forward:
+		case ECPGc_backward:
+			if (n_amount == 0)
+				goto relative;
+
+			step = (n_amount > 0 ? 1 : -1);
+			if (direction == ECPGc_backward)
+				step = -step;
+			if (n_amount < 0)
+				n_amount = -n_amount;
+
+			if (!cur->scrollable && step < 0)
+			{
+				ecpg_free_params(stmt, true, stmt->lineno);
+				ecpg_do_epilogue(stmt);
+				va_end(args);
+				ecpg_raise(lineno, ECPG_INVALID_CURSOR, ECPG_SQLSTATE_OBJECT_NOT_IN_PREREQUISITE_STATE, NULL);
+				return false;
+			}
+
+			if (move)
+			{
+				int64 new_pos = cur->cache_pos + step * n_amount;
+				int64 diff;
+
+				if (new_pos < 1)
+					new_pos = 0;
+				if (new_pos > cur->n_tuples)
+					new_pos = cur->n_tuples + 1;
+
+				diff = new_pos - cur->cache_pos;
+				sqlca->sqlerrd[2] = (diff >= 0 ? diff : -diff);
+				cur->cache_pos = new_pos;
+
+				break;
+			}
+
+			for (count = 0; (cur->scrollable && count < n_amount) ||
+					(!cur->scrollable && ((fetchall && !cur->endoftuples) || (!fetchall && count < n_amount))); count++)
+			{
+				cur->cache_pos += step;
+				if (cur->cache_pos < 1)
+				{
+					cur->cache_pos = 0;
+					break;
+				}
+				else
+				{
+					if (cur->scrollable && cur->cache_pos > cur->n_tuples)
+					{
+						cur->cache_pos = cur->n_tuples + 1;
+						break;
+					}
+					else if (!cur->scrollable && cur->endoftuples)
+					{
+						break;
+					}
+				}
+
+				if (!ecpg_cursor_execute(stmt, cur))
+				{
+					ecpg_free_params(stmt, true, stmt->lineno);
+					ecpg_do_epilogue(stmt);
+					va_end(args);
+					return false;
+				}
+
+				if (!cur->scrollable && cur->endoftuples)
+					break;
+
+				start_idx = cur->cache_pos - cur->start_pos;
+
+				if (!ecpg_process_output(stmt, start_idx, start_idx + 1, count, true, (count > 0)))
+				{
+					ecpg_free_params(stmt, true, stmt->lineno);
+					ecpg_do_epilogue(stmt);
+					va_end(args);
+					return false;
+				}
+			}
+
+			if (count == 0)
+			{
+				ecpg_free_params(stmt, true, stmt->lineno);
+				ecpg_do_epilogue(stmt);
+				va_end(args);
+				ecpg_raise(lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL);
+				return false;
+			}
+
+			sqlca->sqlerrd[2] = count;
+
+			break;
+
+		default:
+			ecpg_free_params(stmt, true, stmt->lineno);
+			ecpg_do_epilogue(stmt);
+			va_end(args);
+			ecpg_raise(lineno, ECPG_INVALID_CURSOR, ECPG_SQLSTATE_INVALID_CURSOR_DEFINITION, (connection_name) ? connection_name : ecpg_gettext("<empty>"));
+			return false;
+	}
+
+	ecpg_free_params(stmt, true, stmt->lineno);
+	ecpg_do_epilogue(stmt);
+	va_end(args);
+	return true;
+}
+
+bool
+ECPGclose(const int lineno, const int compat, const int force_indicator,
+		const char *connection_name, const bool questionmarks,
+		const char *curname, const int st, const char *query, ...)
+{
+	struct connection  *con;
+	va_list			args;
+	bool			ret;
+
+	con = ecpg_get_connection(connection_name);
+
+	if (!find_cursor(con, curname))
+	{
+		ecpg_raise(lineno, ECPG_INVALID_CURSOR, ECPG_SQLSTATE_INVALID_CURSOR_NAME, (connection_name) ? connection_name : ecpg_gettext("<empty>"));
+		return false;
+	}
+
+	va_start(args, query);
+	ret = ecpg_do(lineno, compat, force_indicator, connection_name, questionmarks, st, query, args);
+	va_end(args);
+
+	del_cursor(con, curname);
+
+	return ret;
+}
diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c
index db97503..467be57 100644
--- a/src/interfaces/ecpg/ecpglib/data.c
+++ b/src/interfaces/ecpg/ecpglib/data.c
@@ -120,7 +120,7 @@ check_special_value(char *ptr, double *retval, char **endptr)
 }
 
 bool
-ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
+ecpg_get_data(const PGresult *results, int var_index, int act_tuple, int act_field, int lineno,
 			  enum ECPGttype type, enum ECPGttype ind_type,
 			  char *var, char *ind, long varcharsize, long offset,
 			  long ind_offset, enum ARRAY_TYPE isarray, enum COMPAT_MODE compat, bool force_indicator)
@@ -167,20 +167,20 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 	{
 		case ECPGt_short:
 		case ECPGt_unsigned_short:
-			*((short *) (ind + ind_offset * act_tuple)) = value_for_indicator;
+			*((short *) (ind + ind_offset * var_index)) = value_for_indicator;
 			break;
 		case ECPGt_int:
 		case ECPGt_unsigned_int:
-			*((int *) (ind + ind_offset * act_tuple)) = value_for_indicator;
+			*((int *) (ind + ind_offset * var_index)) = value_for_indicator;
 			break;
 		case ECPGt_long:
 		case ECPGt_unsigned_long:
-			*((long *) (ind + ind_offset * act_tuple)) = value_for_indicator;
+			*((long *) (ind + ind_offset * var_index)) = value_for_indicator;
 			break;
 #ifdef HAVE_LONG_LONG_INT
 		case ECPGt_long_long:
 		case ECPGt_unsigned_long_long:
-			*((long long int *) (ind + ind_offset * act_tuple)) = value_for_indicator;
+			*((long long int *) (ind + ind_offset * var_index)) = value_for_indicator;
 			break;
 #endif   /* HAVE_LONG_LONG_INT */
 		case ECPGt_NO_INDICATOR:
@@ -192,7 +192,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					 * Informix has an additional way to specify NULLs note
 					 * that this uses special values to denote NULL
 					 */
-					ECPGset_noind_null(type, var + offset * act_tuple);
+					ECPGset_noind_null(type, var + offset * var_index);
 				}
 				else
 				{
@@ -243,10 +243,10 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 		if (binary)
 		{
 			if (varcharsize == 0 || varcharsize * offset >= size)
-				memcpy(var + offset * act_tuple, pval, size);
+				memcpy(var + offset * var_index, pval, size);
 			else
 			{
-				memcpy(var + offset * act_tuple, pval, varcharsize * offset);
+				memcpy(var + offset * var_index, pval, varcharsize * offset);
 
 				if (varcharsize * offset < size)
 				{
@@ -255,20 +255,20 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					{
 						case ECPGt_short:
 						case ECPGt_unsigned_short:
-							*((short *) (ind + ind_offset * act_tuple)) = size;
+							*((short *) (ind + ind_offset * var_index)) = size;
 							break;
 						case ECPGt_int:
 						case ECPGt_unsigned_int:
-							*((int *) (ind + ind_offset * act_tuple)) = size;
+							*((int *) (ind + ind_offset * var_index)) = size;
 							break;
 						case ECPGt_long:
 						case ECPGt_unsigned_long:
-							*((long *) (ind + ind_offset * act_tuple)) = size;
+							*((long *) (ind + ind_offset * var_index)) = size;
 							break;
 #ifdef HAVE_LONG_LONG_INT
 						case ECPGt_long_long:
 						case ECPGt_unsigned_long_long:
-							*((long long int *) (ind + ind_offset * act_tuple)) = size;
+							*((long long int *) (ind + ind_offset * var_index)) = size;
 							break;
 #endif   /* HAVE_LONG_LONG_INT */
 						default:
@@ -307,13 +307,13 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					switch (type)
 					{
 						case ECPGt_short:
-							*((short *) (var + offset * act_tuple)) = (short) res;
+							*((short *) (var + offset * var_index)) = (short) res;
 							break;
 						case ECPGt_int:
-							*((int *) (var + offset * act_tuple)) = (int) res;
+							*((int *) (var + offset * var_index)) = (int) res;
 							break;
 						case ECPGt_long:
-							*((long *) (var + offset * act_tuple)) = (long) res;
+							*((long *) (var + offset * var_index)) = (long) res;
 							break;
 						default:
 							/* Cannot happen */
@@ -336,13 +336,13 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					switch (type)
 					{
 						case ECPGt_unsigned_short:
-							*((unsigned short *) (var + offset * act_tuple)) = (unsigned short) ures;
+							*((unsigned short *) (var + offset * var_index)) = (unsigned short) ures;
 							break;
 						case ECPGt_unsigned_int:
-							*((unsigned int *) (var + offset * act_tuple)) = (unsigned int) ures;
+							*((unsigned int *) (var + offset * var_index)) = (unsigned int) ures;
 							break;
 						case ECPGt_unsigned_long:
-							*((unsigned long *) (var + offset * act_tuple)) = (unsigned long) ures;
+							*((unsigned long *) (var + offset * var_index)) = (unsigned long) ures;
 							break;
 						default:
 							/* Cannot happen */
@@ -353,7 +353,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 #ifdef HAVE_LONG_LONG_INT
 #ifdef HAVE_STRTOLL
 				case ECPGt_long_long:
-					*((long long int *) (var + offset * act_tuple)) = strtoll(pval, &scan_length, 10);
+					*((long long int *) (var + offset * var_index)) = strtoll(pval, &scan_length, 10);
 					if (garbage_left(isarray, scan_length, compat))
 					{
 						ecpg_raise(lineno, ECPG_INT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
@@ -365,7 +365,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 #endif   /* HAVE_STRTOLL */
 #ifdef HAVE_STRTOULL
 				case ECPGt_unsigned_long_long:
-					*((unsigned long long int *) (var + offset * act_tuple)) = strtoull(pval, &scan_length, 10);
+					*((unsigned long long int *) (var + offset * var_index)) = strtoull(pval, &scan_length, 10);
 					if ((isarray && *scan_length != ',' && *scan_length != '}')
 						|| (!isarray && !(INFORMIX_MODE(compat) && *scan_length == '.') && *scan_length != '\0' && *scan_length != ' '))		/* Garbage left */
 					{
@@ -400,10 +400,10 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					switch (type)
 					{
 						case ECPGt_float:
-							*((float *) (var + offset * act_tuple)) = dres;
+							*((float *) (var + offset * var_index)) = dres;
 							break;
 						case ECPGt_double:
-							*((double *) (var + offset * act_tuple)) = dres;
+							*((double *) (var + offset * var_index)) = dres;
 							break;
 						default:
 							/* Cannot happen */
@@ -415,9 +415,9 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					if (pval[0] == 'f' && pval[1] == '\0')
 					{
 						if (offset == sizeof(char))
-							*((char *) (var + offset * act_tuple)) = false;
+							*((char *) (var + offset * var_index)) = false;
 						else if (offset == sizeof(int))
-							*((int *) (var + offset * act_tuple)) = false;
+							*((int *) (var + offset * var_index)) = false;
 						else
 							ecpg_raise(lineno, ECPG_CONVERT_BOOL,
 									   ECPG_SQLSTATE_DATATYPE_MISMATCH,
@@ -427,16 +427,16 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					else if (pval[0] == 't' && pval[1] == '\0')
 					{
 						if (offset == sizeof(char))
-							*((char *) (var + offset * act_tuple)) = true;
+							*((char *) (var + offset * var_index)) = true;
 						else if (offset == sizeof(int))
-							*((int *) (var + offset * act_tuple)) = true;
+							*((int *) (var + offset * var_index)) = true;
 						else
 							ecpg_raise(lineno, ECPG_CONVERT_BOOL,
 									   ECPG_SQLSTATE_DATATYPE_MISMATCH,
 									   NULL);
 						break;
 					}
-					else if (pval[0] == '\0' && PQgetisnull(results, act_tuple, act_field))
+					else if (pval[0] == '\0' && PQgetisnull(results, var_index, act_field))
 					{
 						/* NULL is valid */
 						break;
@@ -451,7 +451,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 				case ECPGt_unsigned_char:
 				case ECPGt_string:
 					{
-						char	   *str = (char *) (var + offset * act_tuple);
+						char	   *str = (char *) (var + offset * var_index);
 
 						if (varcharsize == 0 || varcharsize > size)
 						{
@@ -479,20 +479,20 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 								{
 									case ECPGt_short:
 									case ECPGt_unsigned_short:
-										*((short *) (ind + ind_offset * act_tuple)) = size;
+										*((short *) (ind + ind_offset * var_index)) = size;
 										break;
 									case ECPGt_int:
 									case ECPGt_unsigned_int:
-										*((int *) (ind + ind_offset * act_tuple)) = size;
+										*((int *) (ind + ind_offset * var_index)) = size;
 										break;
 									case ECPGt_long:
 									case ECPGt_unsigned_long:
-										*((long *) (ind + ind_offset * act_tuple)) = size;
+										*((long *) (ind + ind_offset * var_index)) = size;
 										break;
 #ifdef HAVE_LONG_LONG_INT
 									case ECPGt_long_long:
 									case ECPGt_unsigned_long_long:
-										*((long long int *) (ind + ind_offset * act_tuple)) = size;
+										*((long long int *) (ind + ind_offset * var_index)) = size;
 										break;
 #endif   /* HAVE_LONG_LONG_INT */
 									default:
@@ -508,7 +508,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 				case ECPGt_varchar:
 					{
 						struct ECPGgeneric_varchar *variable =
-						(struct ECPGgeneric_varchar *) (var + offset * act_tuple);
+						(struct ECPGgeneric_varchar *) (var + offset * var_index);
 
 						variable->len = size;
 						if (varcharsize == 0)
@@ -524,20 +524,20 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 								{
 									case ECPGt_short:
 									case ECPGt_unsigned_short:
-										*((short *) (ind + offset * act_tuple)) = variable->len;
+										*((short *) (ind + offset * var_index)) = variable->len;
 										break;
 									case ECPGt_int:
 									case ECPGt_unsigned_int:
-										*((int *) (ind + offset * act_tuple)) = variable->len;
+										*((int *) (ind + offset * var_index)) = variable->len;
 										break;
 									case ECPGt_long:
 									case ECPGt_unsigned_long:
-										*((long *) (ind + offset * act_tuple)) = variable->len;
+										*((long *) (ind + offset * var_index)) = variable->len;
 										break;
 #ifdef HAVE_LONG_LONG_INT
 									case ECPGt_long_long:
 									case ECPGt_unsigned_long_long:
-										*((long long int *) (ind + ind_offset * act_tuple)) = variable->len;
+										*((long long int *) (ind + ind_offset * var_index)) = variable->len;
 										break;
 #endif   /* HAVE_LONG_LONG_INT */
 									default:
@@ -604,9 +604,9 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					pval = scan_length;
 
 					if (type == ECPGt_numeric)
-						PGTYPESnumeric_copy(nres, (numeric *) (var + offset * act_tuple));
+						PGTYPESnumeric_copy(nres, (numeric *) (var + offset * var_index));
 					else
-						PGTYPESnumeric_to_decimal(nres, (decimal *) (var + offset * act_tuple));
+						PGTYPESnumeric_to_decimal(nres, (decimal *) (var + offset * var_index));
 
 					PGTYPESnumeric_free(nres);
 					break;
@@ -657,7 +657,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 					}
 					pval = scan_length;
 
-					PGTYPESinterval_copy(ires, (interval *) (var + offset * act_tuple));
+					PGTYPESinterval_copy(ires, (interval *) (var + offset * var_index));
 					free(ires);
 					break;
 
@@ -701,7 +701,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 						}
 					}
 
-					*((date *) (var + offset * act_tuple)) = ddres;
+					*((date *) (var + offset * var_index)) = ddres;
 					pval = scan_length;
 					break;
 
@@ -745,7 +745,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 						}
 					}
 
-					*((timestamp *) (var + offset * act_tuple)) = tres;
+					*((timestamp *) (var + offset * var_index)) = tres;
 					pval = scan_length;
 					break;
 
@@ -762,6 +762,7 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 
 				/* set array to next entry */
 				++act_tuple;
+				++var_index;
 
 				/* set pval to the next entry */
 
diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c
index 1dde52c..a2a4a9c 100644
--- a/src/interfaces/ecpg/ecpglib/descriptor.c
+++ b/src/interfaces/ecpg/ecpglib/descriptor.c
@@ -438,7 +438,7 @@ ECPGget_desc(int lineno, const char *desc_name, int index,...)
 
 		/* desparate try to guess something sensible */
 		stmt.connection = ecpg_get_connection(NULL);
-		ecpg_store_result(ECPGresult, index, &stmt, &data_var);
+		ecpg_store_result(ECPGresult, 0, PQntuples(ECPGresult), index, &stmt, &data_var, 0);
 
 		setlocale(LC_NUMERIC, oldlocale);
 		ecpg_free(oldlocale);
diff --git a/src/interfaces/ecpg/ecpglib/error.c b/src/interfaces/ecpg/ecpglib/error.c
index ee553fd..7148f50 100644
--- a/src/interfaces/ecpg/ecpglib/error.c
+++ b/src/interfaces/ecpg/ecpglib/error.c
@@ -268,6 +268,20 @@ ecpg_raise(int line, int code, const char *sqlstate, const char *str)
 					 ecpg_gettext("could not connect to database \"%s\" on line %d"), str, line);
 			break;
 
+		case ECPG_INVALID_CURSOR:
+			if (strcmp(sqlstate, ECPG_SQLSTATE_OBJECT_NOT_IN_PREREQUISITE_STATE) == 0)
+				snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+
+			/*
+			 * translator: this string will be truncated at 149 characters
+			 * expanded.
+			 */
+				ecpg_gettext("cursor can only scan forward"));
+			else
+				snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), str);
+
+			break;
+
 		default:
 			snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
 
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index f468147..cfe5a5e 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -109,6 +109,7 @@ free_statement(struct statement * stmt)
 	free_variable(stmt->outlist);
 	ecpg_free(stmt->command);
 	ecpg_free(stmt->name);
+	ecpg_free(stmt->oldlocale);
 	ecpg_free(stmt);
 }
 
@@ -311,12 +312,12 @@ ecpg_is_type_an_array(int type, const struct statement * stmt, const struct vari
 
 
 bool
-ecpg_store_result(const PGresult *results, int act_field,
-				  const struct statement * stmt, struct variable * var)
+ecpg_store_result(const PGresult *results, int start, int end, int act_field,
+				  const struct statement * stmt, struct variable * var, int var_index)
 {
 	enum ARRAY_TYPE isarray;
 	int			act_tuple,
-				ntuples = PQntuples(results);
+				ntuples = end - start;
 	bool		status = true;
 
 	if ((isarray = ecpg_is_type_an_array(PQftype(results, act_field), stmt, var)) == ECPG_ARRAY_ERROR)
@@ -367,7 +368,7 @@ ecpg_store_result(const PGresult *results, int act_field,
 					if (!var->varcharsize && !var->arrsize)
 					{
 						/* special mode for handling char**foo=0 */
-						for (act_tuple = 0; act_tuple < ntuples; act_tuple++)
+						for (act_tuple = start; act_tuple < end; act_tuple++)
 							len += strlen(PQgetvalue(results, act_tuple, act_field)) + 1;
 						len *= var->offset;		/* should be 1, but YMNK */
 						len += (ntuples + 1) * sizeof(char *);
@@ -376,7 +377,7 @@ ecpg_store_result(const PGresult *results, int act_field,
 					{
 						var->varcharsize = 0;
 						/* check strlen for each tuple */
-						for (act_tuple = 0; act_tuple < ntuples; act_tuple++)
+						for (act_tuple = start; act_tuple < end; act_tuple++)
 						{
 							int			len = strlen(PQgetvalue(results, act_tuple, act_field)) + 1;
 
@@ -397,7 +398,7 @@ ecpg_store_result(const PGresult *results, int act_field,
 		}
 		else
 		{
-			for (act_tuple = 0; act_tuple < ntuples; act_tuple++)
+			for (act_tuple = start; act_tuple < end; act_tuple++)
 				len += PQgetlength(results, act_tuple, act_field);
 		}
 
@@ -433,11 +434,11 @@ ecpg_store_result(const PGresult *results, int act_field,
 		/* storing the data (after the last array element) */
 		char	   *current_data_location = (char *) &current_string[ntuples + 1];
 
-		for (act_tuple = 0; act_tuple < ntuples && status; act_tuple++)
+		for (act_tuple = start; act_tuple < end && status; act_tuple++, var_index++)
 		{
 			int			len = strlen(PQgetvalue(results, act_tuple, act_field)) + 1;
 
-			if (!ecpg_get_data(results, act_tuple, act_field, stmt->lineno,
+			if (!ecpg_get_data(results, var_index, act_tuple, act_field, stmt->lineno,
 							 var->type, var->ind_type, current_data_location,
 							   var->ind_value, len, 0, var->ind_offset, isarray, stmt->compat, stmt->force_indicator))
 				status = false;
@@ -454,9 +455,9 @@ ecpg_store_result(const PGresult *results, int act_field,
 	}
 	else
 	{
-		for (act_tuple = 0; act_tuple < ntuples && status; act_tuple++)
+		for (act_tuple = start; act_tuple < end && status; act_tuple++, var_index++)
 		{
-			if (!ecpg_get_data(results, act_tuple, act_field, stmt->lineno,
+			if (!ecpg_get_data(results, var_index, act_tuple, act_field, stmt->lineno,
 							   var->type, var->ind_type, var->value,
 							   var->ind_value, var->varcharsize, var->offset, var->ind_offset, isarray, stmt->compat, stmt->force_indicator))
 				status = false;
@@ -1082,18 +1083,26 @@ ecpg_store_input(const int lineno, const bool force_indicator, const struct vari
 	return true;
 }
 
-static void
-free_params(const char **paramValues, int nParams, bool print, int lineno)
+void
+ecpg_free_params(struct statement *stmt, bool print, int lineno)
 {
 	int			n;
 
-	for (n = 0; n < nParams; n++)
+	for (n = 0; n < stmt->nparams; n++)
 	{
 		if (print)
-			ecpg_log("free_params on line %d: parameter %d = %s\n", lineno, n + 1, paramValues[n] ? paramValues[n] : "null");
-		ecpg_free((void *) (paramValues[n]));
+			ecpg_log("free_params on line %d: parameter %d = %s\n", lineno, n + 1, stmt->param_values[n] ? stmt->param_values[n] : "null");
+		ecpg_free((void *) (stmt->param_values[n]));
 	}
-	ecpg_free(paramValues);
+	ecpg_free(stmt->param_values);
+	stmt->nparams = 0;
+	stmt->param_values = NULL;
+
+	for (n = 0; n < stmt->ndollarzero; n++)
+		ecpg_free((void *) (stmt->dollarzero[n]));
+	ecpg_free(stmt->dollarzero);
+	stmt->ndollarzero = 0;
+	stmt->dollarzero = NULL;
 }
 
 
@@ -1129,20 +1138,12 @@ insert_tobeinserted(int position, int ph_len, struct statement * stmt, char *tob
 	return true;
 }
 
-static bool
-ecpg_execute(struct statement * stmt)
+bool
+ecpg_build_params(struct statement * stmt)
 {
-	bool		status = false;
-	char	   *cmdstat;
-	PGresult   *results;
-	PGnotify   *notify;
 	struct variable *var;
 	int			desc_counter = 0;
-	const char **paramValues = NULL;
-	int			nParams = 0;
 	int			position = 0;
-	struct sqlca_t *sqlca = ECPGget_sqlca();
-	bool		clear_result = true;
 
 	/*
 	 * If the type is one of the fill in types then we take the argument and
@@ -1342,7 +1343,7 @@ ecpg_execute(struct statement * stmt)
 			ecpg_raise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS,
 					   ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS,
 					   NULL);
-			free_params(paramValues, nParams, false, stmt->lineno);
+			ecpg_free_params(stmt, false, stmt->lineno);
 			return false;
 		}
 
@@ -1357,7 +1358,7 @@ ecpg_execute(struct statement * stmt)
 
 			if (!insert_tobeinserted(position, ph_len, stmt, tobeinserted))
 			{
-				free_params(paramValues, nParams, false, stmt->lineno);
+				ecpg_free_params(stmt, false, stmt->lineno);
 				return false;
 			}
 			tobeinserted = NULL;
@@ -1370,23 +1371,38 @@ ecpg_execute(struct statement * stmt)
 		 */
 		else if (stmt->command[position] == '0')
 		{
+			const char **dollarzero;
+
+			if (!(dollarzero = (const char **) ecpg_realloc(stmt->dollarzero, sizeof(const char *) * (stmt->ndollarzero + 1), stmt->lineno)))
+			{
+				ecpg_free_params(stmt, false, stmt->lineno);
+				return false;
+			}
+			stmt->ndollarzero++;
+			stmt->dollarzero = dollarzero;
+			stmt->dollarzero[stmt->ndollarzero - 1] = strdup(tobeinserted);
+
 			if (!insert_tobeinserted(position, 2, stmt, tobeinserted))
 			{
-				free_params(paramValues, nParams, false, stmt->lineno);
+				ecpg_free_params(stmt, false, stmt->lineno);
 				return false;
 			}
 			tobeinserted = NULL;
 		}
 		else
 		{
-			nParams++;
-			if (!(paramValues = (const char **) ecpg_realloc(paramValues, sizeof(const char *) * nParams, stmt->lineno)))
+			const char **paramValues;
+		
+			if (!(paramValues = (const char **) ecpg_realloc(stmt->param_values, sizeof(const char *) * (stmt->nparams + 1), stmt->lineno)))
 			{
 				ecpg_free(paramValues);
 				return false;
 			}
 
-			paramValues[nParams - 1] = tobeinserted;
+			stmt->nparams++;
+			stmt->param_values = paramValues;
+
+			stmt->param_values[stmt->nparams - 1] = tobeinserted;
 
 			/* let's see if this was an old style placeholder */
 			if (stmt->command[position] == '?')
@@ -1397,7 +1413,7 @@ ecpg_execute(struct statement * stmt)
 
 				if (!(tobeinserted = (char *) ecpg_alloc(buffersize, stmt->lineno)))
 				{
-					free_params(paramValues, nParams, false, stmt->lineno);
+					ecpg_free_params(stmt, false, stmt->lineno);
 					return false;
 				}
 
@@ -1405,7 +1421,7 @@ ecpg_execute(struct statement * stmt)
 
 				if (!insert_tobeinserted(position, 2, stmt, tobeinserted))
 				{
-					free_params(paramValues, nParams, false, stmt->lineno);
+					ecpg_free_params(stmt, false, stmt->lineno);
 					return false;
 				}
 				tobeinserted = NULL;
@@ -1421,58 +1437,76 @@ ecpg_execute(struct statement * stmt)
 	{
 		ecpg_raise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS,
 				 ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL);
-		free_params(paramValues, nParams, false, stmt->lineno);
+		ecpg_free_params(stmt, false, stmt->lineno);
 		return false;
 	}
 
-	/* The request has been build. */
+	return true;
+}
+
 
+static bool
+ecpg_execute(struct statement * stmt)
+{
 	if (PQtransactionStatus(stmt->connection->connection) == PQTRANS_IDLE && !stmt->connection->autocommit)
 	{
-		results = PQexec(stmt->connection->connection, "begin transaction");
-		if (!ecpg_check_PQresult(results, stmt->lineno, stmt->connection->connection, stmt->compat))
+		stmt->results = PQexec(stmt->connection->connection, "begin transaction");
+		if (!ecpg_check_PQresult(stmt->results, stmt->lineno, stmt->connection->connection, stmt->compat))
 		{
-			free_params(paramValues, nParams, false, stmt->lineno);
+			ecpg_free_params(stmt, false, stmt->lineno);
 			return false;
 		}
-		PQclear(results);
+		PQclear(stmt->results);
+		stmt->results = NULL;
 	}
 
-	ecpg_log("ecpg_execute on line %d: query: %s; with %d parameter(s) on connection %s\n", stmt->lineno, stmt->command, nParams, stmt->connection->name);
+	ecpg_log("ecpg_execute on line %d: query: %s; with %d parameter(s) on connection %s\n", stmt->lineno, stmt->command, stmt->nparams, stmt->connection->name);
 	if (stmt->statement_type == ECPGst_execute)
 	{
-		results = PQexecPrepared(stmt->connection->connection, stmt->name, nParams, paramValues, NULL, NULL, 0);
+		stmt->results = PQexecPrepared(stmt->connection->connection, stmt->name, stmt->nparams, stmt->param_values, NULL, NULL, 0);
 		ecpg_log("ecpg_execute on line %d: using PQexecPrepared for \"%s\"\n", stmt->lineno, stmt->command);
 	}
 	else
 	{
-		if (nParams == 0)
+		if (stmt->nparams == 0)
 		{
-			results = PQexec(stmt->connection->connection, stmt->command);
+			stmt->results = PQexec(stmt->connection->connection, stmt->command);
 			ecpg_log("ecpg_execute on line %d: using PQexec\n", stmt->lineno);
 		}
 		else
 		{
-			results = PQexecParams(stmt->connection->connection, stmt->command, nParams, NULL, paramValues, NULL, NULL, 0);
+			stmt->results = PQexecParams(stmt->connection->connection, stmt->command, stmt->nparams, NULL, stmt->param_values, NULL, NULL, 0);
 			ecpg_log("ecpg_execute on line %d: using PQexecParams\n", stmt->lineno);
 		}
 	}
 
-	free_params(paramValues, nParams, true, stmt->lineno);
+	ecpg_free_params(stmt, true, stmt->lineno);
 
-	if (!ecpg_check_PQresult(results, stmt->lineno, stmt->connection->connection, stmt->compat))
+	if (!ecpg_check_PQresult(stmt->results, stmt->lineno, stmt->connection->connection, stmt->compat))
 		return (false);
 
+	return (true);
+}
+
+bool
+ecpg_process_output(struct statement * stmt, int start, int end, int var_index, bool keep_result, bool append_result)
+{
+	char		*cmdstat;
+	PGnotify	*notify;
+	bool		status = false;
+	struct variable *var;
+	struct sqlca_t *sqlca = ECPGget_sqlca();
+
 	var = stmt->outlist;
-	switch (PQresultStatus(results))
+	switch (PQresultStatus(stmt->results))
 	{
 			int			nfields,
 						ntuples,
 						act_field;
 
 		case PGRES_TUPLES_OK:
-			nfields = PQnfields(results);
-			sqlca->sqlerrd[2] = ntuples = PQntuples(results);
+			nfields = PQnfields(stmt->results);
+			sqlca->sqlerrd[2] += ntuples = (end - start);
 			ecpg_log("ecpg_execute on line %d: correctly got %d tuples with %d fields\n", stmt->lineno, ntuples, nfields);
 			status = true;
 
@@ -1494,12 +1528,34 @@ ecpg_execute(struct statement * stmt)
 					status = false;
 				else
 				{
-					if (desc->result)
-						PQclear(desc->result);
-					desc->result = results;
-					clear_result = false;
+					int	row, srcrow, col;
+
+					if (append_result)
+						row = PQntuples(desc->result);
+					else
+					{
+						if (desc->result)
+							PQclear(desc->result);
+						desc->result = PQcopyResult(stmt->results, PG_COPYRES_ATTRS | PG_COPYRES_EVENTS | PG_COPYRES_NOTICEHOOKS);
+						row = 0;
+					}
+
+					for (srcrow = start; srcrow < end; srcrow++, row++)
+						for (col = 0; col < nfields; col++)
+						{
+							bool	isnull = PQgetisnull(stmt->results, srcrow, col);
+							if (!PQsetvalue(desc->result, row, col,
+										isnull ? NULL : PQgetvalue(stmt->results, srcrow, col),
+										isnull ? -1 : PQgetlength(stmt->results, srcrow, col)))
+							{
+								ecpg_raise(stmt->lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+								status = false;
+								break;
+							}
+						}
+
 					ecpg_log("ecpg_execute on line %d: putting result (%d tuples) into descriptor %s\n",
-							 stmt->lineno, PQntuples(results), (const char *) var->pointer);
+							 stmt->lineno, PQntuples(stmt->results), (const char *) var->pointer);
 				}
 				var = var->next;
 			}
@@ -1509,36 +1565,52 @@ ecpg_execute(struct statement * stmt)
 				{
 					struct sqlda_compat **_sqlda = (struct sqlda_compat **) var->pointer;
 					struct sqlda_compat *sqlda = *_sqlda;
-					struct sqlda_compat *sqlda_new;
+					struct sqlda_compat *sqlda_last, *sqlda_new = NULL;
 					int			i;
 
-					/*
-					 * If we are passed in a previously existing sqlda (chain)
-					 * then free it.
-					 */
-					while (sqlda)
+					if (append_result)
 					{
-						sqlda_new = sqlda->desc_next;
-						free(sqlda);
-						sqlda = sqlda_new;
+						sqlda_last = sqlda;
+						while (sqlda_last && sqlda_last->desc_next)
+							sqlda_last = sqlda_last->desc_next;
 					}
-					*_sqlda = sqlda = sqlda_new = NULL;
-					for (i = ntuples - 1; i >= 0; i--)
+					else
+					{
+						/*
+						 * If we are passed in a previously existing sqlda (chain)
+						 * then free it.
+						 */
+						while (sqlda)
+						{
+							sqlda_last = sqlda->desc_next;
+							free(sqlda);
+							sqlda = sqlda_last;
+						}
+						*_sqlda = sqlda = sqlda_last = NULL;
+					}
+					for (i = end - 1; i >= start; i--)
 					{
+						struct sqlda_compat *tmp;
+
 						/*
-						 * Build a new sqlda structure. Note that only
-						 * fetching 1 record is supported
+						 * Build a new sqlda structure. 
 						 */
-						sqlda_new = ecpg_build_compat_sqlda(stmt->lineno, results, i, stmt->compat);
+						tmp = ecpg_build_compat_sqlda(stmt->lineno, stmt->results, i, stmt->compat);
 
-						if (!sqlda_new)
+						if (!tmp)
 						{
 							/* cleanup all SQLDAs we created up */
+							while (sqlda_new)
+							{
+								tmp = sqlda_new->desc_next;
+								free(sqlda_new);
+								sqlda_new = tmp;
+							}
 							while (sqlda)
 							{
-								sqlda_new = sqlda->desc_next;
+								tmp = sqlda->desc_next;
 								free(sqlda);
-								sqlda = sqlda_new;
+								sqlda = tmp;
 							}
 							*_sqlda = NULL;
 
@@ -1550,51 +1622,74 @@ ecpg_execute(struct statement * stmt)
 						{
 							ecpg_log("ecpg_execute on line %d: new sqlda was built\n", stmt->lineno);
 
-							*_sqlda = sqlda_new;
+							if (sqlda_new == NULL)
+								sqlda_new = tmp;
+							else
+							{
+								tmp->desc_next = sqlda_new;
+								sqlda_new = tmp;
+							}
 
-							ecpg_set_compat_sqlda(stmt->lineno, _sqlda, results, i, stmt->compat);
+							ecpg_set_compat_sqlda(stmt->lineno, &tmp, stmt->results, i, stmt->compat);
 							ecpg_log("ecpg_execute on line %d: putting result (1 tuple %d fields) into sqlda descriptor\n",
-									 stmt->lineno, PQnfields(results));
-
-							sqlda_new->desc_next = sqlda;
-							sqlda = sqlda_new;
+									 stmt->lineno, PQnfields(stmt->results));
 						}
 					}
+					if (sqlda_last)
+						sqlda_last->desc_next = sqlda_new;
+					else
+						*_sqlda = sqlda_new;
 				}
 				else
 				{
 					struct sqlda_struct **_sqlda = (struct sqlda_struct **) var->pointer;
 					struct sqlda_struct *sqlda = *_sqlda;
-					struct sqlda_struct *sqlda_new;
+					struct sqlda_struct *sqlda_last, *sqlda_new = NULL;
 					int			i;
 
-					/*
-					 * If we are passed in a previously existing sqlda (chain)
-					 * then free it.
-					 */
-					while (sqlda)
+					if (append_result)
 					{
-						sqlda_new = sqlda->desc_next;
-						free(sqlda);
-						sqlda = sqlda_new;
+						sqlda_last = sqlda;
+						while (sqlda_last && sqlda_last->desc_next)
+							sqlda_last = sqlda_last->desc_next;
 					}
-					*_sqlda = sqlda = sqlda_new = NULL;
-					for (i = ntuples - 1; i >= 0; i--)
+					else
 					{
 						/*
-						 * Build a new sqlda structure. Note that only
-						 * fetching 1 record is supported
+						 * If we are passed in a previously existing sqlda (chain)
+						 * then free it.
 						 */
-						sqlda_new = ecpg_build_native_sqlda(stmt->lineno, results, i, stmt->compat);
+						while (sqlda)
+						{
+							sqlda_last = sqlda->desc_next;
+							free(sqlda);
+							sqlda = sqlda_last;
+						}
+						*_sqlda = sqlda = sqlda_last = NULL;
+					}
+					for (i = end - 1; i >= start; i--)
+					{
+						struct sqlda_struct *tmp;
+
+						/*
+						 * Build a new sqlda structure. 
+						 */
+						tmp = ecpg_build_native_sqlda(stmt->lineno, stmt->results, i, stmt->compat);
 
-						if (!sqlda_new)
+						if (!tmp)
 						{
 							/* cleanup all SQLDAs we created up */
+							while (sqlda_new)
+							{
+								tmp = sqlda_new->desc_next;
+								free(sqlda_new);
+								sqlda_new = tmp;
+							}
 							while (sqlda)
 							{
-								sqlda_new = sqlda->desc_next;
+								tmp = sqlda->desc_next;
 								free(sqlda);
-								sqlda = sqlda_new;
+								sqlda = tmp;
 							}
 							*_sqlda = NULL;
 
@@ -1606,16 +1701,23 @@ ecpg_execute(struct statement * stmt)
 						{
 							ecpg_log("ecpg_execute on line %d: new sqlda was built\n", stmt->lineno);
 
-							*_sqlda = sqlda_new;
+							if (sqlda_new == NULL)
+								sqlda_new = tmp;
+							else
+							{
+								tmp->desc_next = sqlda_new;
+								sqlda_new = tmp;
+							}
 
-							ecpg_set_native_sqlda(stmt->lineno, _sqlda, results, i, stmt->compat);
+							ecpg_set_native_sqlda(stmt->lineno, &tmp, stmt->results, i, stmt->compat);
 							ecpg_log("ecpg_execute on line %d: putting result (1 tuple %d fields) into sqlda descriptor\n",
-									 stmt->lineno, PQnfields(results));
-
-							sqlda_new->desc_next = sqlda;
-							sqlda = sqlda_new;
+									 stmt->lineno, PQnfields(stmt->results));
 						}
 					}
+					if (sqlda_last)
+						sqlda_last->desc_next = sqlda_new;
+					else
+						*_sqlda = sqlda_new;
 				}
 
 				var = var->next;
@@ -1625,7 +1727,7 @@ ecpg_execute(struct statement * stmt)
 				{
 					if (var != NULL)
 					{
-						status = ecpg_store_result(results, act_field, stmt, var);
+						status = ecpg_store_result(stmt->results, start, end, act_field, stmt, var, var_index);
 						var = var->next;
 					}
 					else if (!INFORMIX_MODE(stmt->compat))
@@ -1644,9 +1746,9 @@ ecpg_execute(struct statement * stmt)
 			break;
 		case PGRES_COMMAND_OK:
 			status = true;
-			cmdstat = PQcmdStatus(results);
-			sqlca->sqlerrd[1] = PQoidValue(results);
-			sqlca->sqlerrd[2] = atol(PQcmdTuples(results));
+			cmdstat = PQcmdStatus(stmt->results);
+			sqlca->sqlerrd[1] = PQoidValue(stmt->results);
+			sqlca->sqlerrd[2] = atol(PQcmdTuples(stmt->results));
 			ecpg_log("ecpg_execute on line %d: OK: %s\n", stmt->lineno, cmdstat);
 			if (stmt->compat != ECPG_COMPAT_INFORMIX_SE &&
 				!sqlca->sqlerrd[2] &&
@@ -1670,12 +1772,12 @@ ecpg_execute(struct statement * stmt)
 				if (res == -1)
 				{
 					/* COPY done */
-					PQclear(results);
-					results = PQgetResult(stmt->connection->connection);
-					if (PQresultStatus(results) == PGRES_COMMAND_OK)
+					PQclear(stmt->results);
+					stmt->results = PQgetResult(stmt->connection->connection);
+					if (PQresultStatus(stmt->results) == PGRES_COMMAND_OK)
 						ecpg_log("ecpg_execute on line %d: got PGRES_COMMAND_OK after PGRES_COPY_OUT\n", stmt->lineno);
 					else
-						ecpg_log("ecpg_execute on line %d: got error after PGRES_COPY_OUT: %s", stmt->lineno, PQresultErrorMessage(results));
+						ecpg_log("ecpg_execute on line %d: got error after PGRES_COPY_OUT: %s", stmt->lineno, PQresultErrorMessage(stmt->results));
 				}
 				break;
 			}
@@ -1687,12 +1789,12 @@ ecpg_execute(struct statement * stmt)
 			 */
 			ecpg_log("ecpg_execute on line %d: unknown execution status type\n",
 					 stmt->lineno);
-			ecpg_raise_backend(stmt->lineno, results, stmt->connection->connection, stmt->compat);
+			ecpg_raise_backend(stmt->lineno, stmt->results, stmt->connection->connection, stmt->compat);
 			status = false;
 			break;
 	}
-	if (clear_result)
-		PQclear(results);
+	if (!keep_result)
+		PQclear(stmt->results);
 
 	/* check for asynchronous returns */
 	notify = PQnotifies(stmt->connection->connection);
@@ -1707,46 +1809,20 @@ ecpg_execute(struct statement * stmt)
 }
 
 bool
-ECPGdo(const int lineno, const int compat, const int force_indicator, const char *connection_name, const bool questionmarks, const int st, const char *query,...)
+ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
+		const char *connection_name, const bool questionmarks,
+		enum ECPG_statement_type statement_type, const char *query,
+		va_list args, struct statement **stmt_out)
 {
-	va_list		args;
 	struct statement *stmt;
 	struct connection *con;
-	bool		status;
-	char	   *oldlocale;
 	enum ECPGttype type;
 	struct variable **list;
-	enum ECPG_statement_type statement_type = (enum ECPG_statement_type) st;
-	char	   *prepname;
-
-	if (!query)
-	{
-		ecpg_raise(lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL);
-		return (false);
-	}
-
-	/* Make sure we do NOT honor the locale for numeric input/output */
-	/* since the database wants the standard decimal point */
-	oldlocale = ecpg_strdup(setlocale(LC_NUMERIC, NULL), lineno);
-	setlocale(LC_NUMERIC, "C");
-
-#ifdef ENABLE_THREAD_SAFETY
-	ecpg_pthreads_init();
-#endif
-
-	con = ecpg_get_connection(connection_name);
-
-	if (!ecpg_init(con, connection_name, lineno))
-	{
-		setlocale(LC_NUMERIC, oldlocale);
-		ecpg_free(oldlocale);
-		return (false);
-	}
+	char	*prepname;
 
-	/* construct statement in our own structure */
-	va_start(args, query);
+	*stmt_out = NULL;
 
-	/*
+    	/*
 	 * create a list of variables The variables are listed with input
 	 * variables preceding outputvariables The end of each group is marked by
 	 * an end marker. per variable we list: type - as defined in ecpgtype.h
@@ -1759,11 +1835,24 @@ ECPGdo(const int lineno, const int compat, const int force_indicator, const char
 	 * arraysize of indicator array ind_offset - indicator offset
 	 */
 	if (!(stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno)))
-	{
-		setlocale(LC_NUMERIC, oldlocale);
-		ecpg_free(oldlocale);
-		va_end(args);
 		return false;
+
+	/* Make sure we do NOT honor the locale for numeric input/output */
+	/* since the database wants the standard decimal point */
+	stmt->oldlocale = ecpg_strdup(setlocale(LC_NUMERIC, NULL), lineno);
+	setlocale(LC_NUMERIC, "C");
+
+#ifdef ENABLE_THREAD_SAFETY
+	ecpg_pthreads_init();
+#endif
+
+	con = ecpg_get_connection(connection_name);
+
+	if (!ecpg_init(con, connection_name, lineno))
+	{
+		setlocale(LC_NUMERIC, stmt->oldlocale);
+		free_statement(stmt);
+		return (false);
 	}
 
 	/*
@@ -1774,9 +1863,8 @@ ECPGdo(const int lineno, const int compat, const int force_indicator, const char
 	{
 		if (!ecpg_auto_prepare(lineno, connection_name, compat, &prepname, query))
 		{
-			setlocale(LC_NUMERIC, oldlocale);
-			ecpg_free(oldlocale);
-			va_end(args);
+			setlocale(LC_NUMERIC, stmt->oldlocale);
+			free_statement(stmt);
 			return (false);
 		}
 
@@ -1805,9 +1893,8 @@ ECPGdo(const int lineno, const int compat, const int force_indicator, const char
 		else
 		{
 			ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, stmt->command);
-			setlocale(LC_NUMERIC, oldlocale);
-			ecpg_free(oldlocale);
-			va_end(args);
+			setlocale(LC_NUMERIC, stmt->oldlocale);
+			free_statement(stmt);
 			return (false);
 		}
 	}
@@ -1834,10 +1921,8 @@ ECPGdo(const int lineno, const int compat, const int force_indicator, const char
 
 			if (!(var = (struct variable *) ecpg_alloc(sizeof(struct variable), lineno)))
 			{
-				setlocale(LC_NUMERIC, oldlocale);
-				ecpg_free(oldlocale);
+				setlocale(LC_NUMERIC, stmt->oldlocale);
 				free_statement(stmt);
-				va_end(args);
 				return false;
 			}
 
@@ -1892,10 +1977,8 @@ ECPGdo(const int lineno, const int compat, const int force_indicator, const char
 			{
 				ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, NULL);
 				ecpg_free(var);
-				setlocale(LC_NUMERIC, oldlocale);
-				ecpg_free(oldlocale);
+				setlocale(LC_NUMERIC, stmt->oldlocale);
 				free_statement(stmt);
-				va_end(args);
 				return false;
 			}
 
@@ -1910,29 +1993,80 @@ ECPGdo(const int lineno, const int compat, const int force_indicator, const char
 		type = va_arg(args, enum ECPGttype);
 	}
 
-	va_end(args);
-
 	/* are we connected? */
 	if (con == NULL || con->connection == NULL)
 	{
-		free_statement(stmt);
 		ecpg_raise(lineno, ECPG_NOT_CONN, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, (con) ? con->name : ecpg_gettext("<empty>"));
-		setlocale(LC_NUMERIC, oldlocale);
-		ecpg_free(oldlocale);
+		setlocale(LC_NUMERIC, stmt->oldlocale);
+		free_statement(stmt);
 		return false;
 	}
 
 	/* initialize auto_mem struct */
 	ecpg_clear_auto_mem();
 
-	status = ecpg_execute(stmt);
+	*stmt_out = stmt;
+
+	return (true);
+}
+
+
+void
+ecpg_do_epilogue(struct statement *stmt)
+{
+	/* reset locale value so our application is not affected */
+	setlocale(LC_NUMERIC, stmt->oldlocale);
 	free_statement(stmt);
+}
+
+bool
+ecpg_do(const int lineno, const int compat, const int force_indicator, const char *connection_name, const bool questionmarks, const int st, const char *query, va_list args)
+{
+	struct statement *stmt;
+
+	if (!query)
+	{
+		ecpg_raise(lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL);
+		return false;
+	}
 
-	/* and reset locale value so our application is not affected */
-	setlocale(LC_NUMERIC, oldlocale);
-	ecpg_free(oldlocale);
+	if (!ecpg_do_prologue(lineno, compat, force_indicator, connection_name, questionmarks, (enum ECPG_statement_type) st, query, args, &stmt))
+		return false;
+
+	if (!ecpg_build_params(stmt))
+	{
+		ecpg_do_epilogue(stmt);
+		return false;
+	}
+
+	if (!ecpg_execute(stmt))
+	{
+		ecpg_do_epilogue(stmt);
+		return false;
+	}
+
+	if (!ecpg_process_output(stmt, 0, PQntuples(stmt->results), 0, false, false))
+	{
+		ecpg_do_epilogue(stmt);
+		return false;  
+	}
+
+	ecpg_do_epilogue(stmt);
+
+	return true;
+}
+
+bool
+ECPGdo(const int lineno, const int compat, const int force_indicator, const char *connection_name, const bool questionmarks, const int st, const char *query,...)
+{
+	va_list		args;
+	bool		ret;
+
+	va_start(args, query);
+	ret = ecpg_do(lineno, compat, force_indicator, connection_name, questionmarks, st, query, args);
+	va_end(args);
 
-	return (status);
+	return ret;
 }
 
 /* old descriptor interface */
diff --git a/src/interfaces/ecpg/ecpglib/exports.txt b/src/interfaces/ecpg/ecpglib/exports.txt
index 69e9617..f05e4f1 100644
--- a/src/interfaces/ecpg/ecpglib/exports.txt
+++ b/src/interfaces/ecpg/ecpglib/exports.txt
@@ -29,3 +29,6 @@ ECPGget_PGconn			 26
 ECPGtransactionStatus		 27
 ECPGset_var			 28
 ECPGget_var			 29
+ECPGopen			 30
+ECPGfetch			 31
+ECPGclose			 32
diff --git a/src/interfaces/ecpg/ecpglib/extern.h b/src/interfaces/ecpg/ecpglib/extern.h
index 96d49a4..2920a3c 100644
--- a/src/interfaces/ecpg/ecpglib/extern.h
+++ b/src/interfaces/ecpg/ecpglib/extern.h
@@ -60,6 +60,12 @@ struct statement
 	bool		questionmarks;
 	struct variable *inlist;
 	struct variable *outlist;
+	char		*oldlocale;
+	const char	**dollarzero;
+	int		ndollarzero;
+	const char	**param_values;
+	int		nparams;
+	PGresult	*results;
 };
 
 /* structure to store prepared statements for a connection */
@@ -71,6 +77,17 @@ struct prepared_statement
 	struct prepared_statement *next;
 };
 
+struct cursor_descriptor {
+	char	   *name;
+	PGresult   *res;
+	bool		scrollable;
+	bool		endoftuples;	/* valid if ->scrollable == false and there is no more tuples */
+	int64		n_tuples;	/* valid if ->scrollable == true */
+	int64		start_pos;
+	int64		cache_pos;
+	struct cursor_descriptor *next;
+};
+
 /* structure to store connections */
 struct connection
 {
@@ -79,6 +96,7 @@ struct connection
 	bool		autocommit;
 	struct ECPGtype_information_cache *cache_head;
 	struct prepared_statement *prep_stmts;
+	struct cursor_descriptor *cursor_desc;
 	struct connection *next;
 };
 
@@ -126,7 +144,7 @@ struct variable
 /* Returns a pointer to a string containing a simple type name. */
 void		ecpg_add_mem(void *ptr, int lineno);
 
-bool ecpg_get_data(const PGresult *, int, int, int, enum ECPGttype type,
+bool ecpg_get_data(const PGresult *, int, int, int, int, enum ECPGttype type,
 			  enum ECPGttype, char *, char *, long, long, long,
 			  enum ARRAY_TYPE, enum COMPAT_MODE, bool);
 
@@ -152,9 +170,16 @@ struct descriptor *ecpg_find_desc(int line, const char *name);
 struct prepared_statement *ecpg_find_prepared_statement(const char *,
 						  struct connection *, struct prepared_statement **);
 
-bool ecpg_store_result(const PGresult *results, int act_field,
-				  const struct statement * stmt, struct variable * var);
+bool ecpg_store_result(const PGresult *results, int start, int end, int act_field,
+				  const struct statement * stmt, struct variable * var, int var_index);
 bool		ecpg_store_input(const int, const bool, const struct variable *, char **, bool);
+bool		ecpg_do_prologue(int, const int, const int, const char *, const bool,
+				  enum ECPG_statement_type, const char *, va_list, struct statement **);
+bool		ecpg_build_params(struct statement *);
+bool		ecpg_process_output(struct statement *, int, int, int, bool, bool);
+void		ecpg_free_params(struct statement *, bool, int);
+void		ecpg_do_epilogue(struct statement *);
+bool		ecpg_do(const int, const int, const int, const char *, const bool, const int, const char *, va_list);
 
 bool		ecpg_check_PQresult(PGresult *, int, PGconn *, enum COMPAT_MODE);
 void		ecpg_raise(int line, int code, const char *sqlstate, const char *str);
@@ -191,6 +216,8 @@ void		ecpg_set_native_sqlda(int, struct sqlda_struct **, const PGresult *, int,
 #define ECPG_SQLSTATE_SYNTAX_ERROR			"42601"
 #define ECPG_SQLSTATE_DATATYPE_MISMATCH		"42804"
 #define ECPG_SQLSTATE_DUPLICATE_CURSOR		"42P03"
+#define ECPG_SQLSTATE_INVALID_CURSOR_DEFINITION	"42P11"
+#define ECPG_SQLSTATE_OBJECT_NOT_IN_PREREQUISITE_STATE	"55000"
 
 /* implementation-defined internal errors of ecpg */
 #define ECPG_SQLSTATE_ECPG_INTERNAL_ERROR	"YE000"
diff --git a/src/interfaces/ecpg/ecpglib/sqlda.c b/src/interfaces/ecpg/ecpglib/sqlda.c
index 343a793..9577ee7 100644
--- a/src/interfaces/ecpg/ecpglib/sqlda.c
+++ b/src/interfaces/ecpg/ecpglib/sqlda.c
@@ -393,7 +393,7 @@ ecpg_set_compat_sqlda(int lineno, struct sqlda_compat ** _sqlda, const PGresult
 		if (!isnull)
 		{
 			if (set_data)
-				ecpg_get_data(res, row, i, lineno,
+				ecpg_get_data(res, 0, row, i, lineno,
 							  sqlda->sqlvar[i].sqltype, ECPGt_NO_INDICATOR,
 							  sqlda->sqlvar[i].sqldata, NULL, 0, 0, 0,
 							  ECPG_ARRAY_NONE, compat, false);
@@ -578,7 +578,7 @@ ecpg_set_native_sqlda(int lineno, struct sqlda_struct ** _sqlda, const PGresult
 		if (!isnull)
 		{
 			if (set_data)
-				ecpg_get_data(res, row, i, lineno,
+				ecpg_get_data(res, 0, row, i, lineno,
 							  sqlda->sqlvar[i].sqltype, ECPGt_NO_INDICATOR,
 							  sqlda->sqlvar[i].sqldata, NULL, 0, 0, 0,
 							  ECPG_ARRAY_NONE, compat, false);
diff --git a/src/interfaces/ecpg/include/ecpgerrno.h b/src/interfaces/ecpg/include/ecpgerrno.h
index 36b15b7..f21dad2 100644
--- a/src/interfaces/ecpg/include/ecpgerrno.h
+++ b/src/interfaces/ecpg/include/ecpgerrno.h
@@ -37,6 +37,7 @@
 #define ECPG_NOT_CONN			-221
 
 #define ECPG_INVALID_STMT		-230
+#define ECPG_INVALID_CURSOR		-231
 
 /* dynamic SQL related */
 #define ECPG_UNKNOWN_DESCRIPTOR		-240
diff --git a/src/interfaces/ecpg/include/ecpglib.h b/src/interfaces/ecpg/include/ecpglib.h
index 3b8ed4c..236f797 100644
--- a/src/interfaces/ecpg/include/ecpglib.h
+++ b/src/interfaces/ecpg/include/ecpglib.h
@@ -63,6 +63,13 @@ PGTransactionStatusType ECPGtransactionStatus(const char *);
 
 char	   *ECPGerrmsg(void);
 
+/* Readahead cursor functions */
+bool		ECPGopen(const int, const int, const int, const char *, const bool, const char *, const int, const char *, ...);
+bool		ECPGfetch(const int, const int, const int, const char *, const bool,
+				const char *, enum ECPG_cursor_direction, const char *,
+				const int,const char *, ...);
+bool		ECPGclose(const int, const int, const int, const char *, const bool, const char *, const int, const char *, ...);
+
  /* print an error message */
 void		sqlprint(void);
 
diff --git a/src/interfaces/ecpg/include/ecpgtype.h b/src/interfaces/ecpg/include/ecpgtype.h
index 7cc47e9..dc82457 100644
--- a/src/interfaces/ecpg/include/ecpgtype.h
+++ b/src/interfaces/ecpg/include/ecpgtype.h
@@ -99,6 +99,14 @@ enum ECPG_statement_type
 	ECPGst_prepnormal
 };
 
+enum ECPG_cursor_direction
+{
+	ECPGc_absolute,
+	ECPGc_relative,
+	ECPGc_forward, 
+	ECPGc_backward 
+};
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/interfaces/ecpg/preproc/check_rules.pl b/src/interfaces/ecpg/preproc/check_rules.pl
index 991c40c..8560eb0 100644
--- a/src/interfaces/ecpg/preproc/check_rules.pl
+++ b/src/interfaces/ecpg/preproc/check_rules.pl
@@ -43,7 +43,10 @@ my %replace_line = (
 		'CREATE OptTemp TABLE create_as_target AS EXECUTE prepared_name execute_param_clause',
 
 	'PrepareStmtPREPAREnameprep_type_clauseASPreparableStmt' =>
-		'PREPARE prepared_name prep_type_clause AS PreparableStmt'
+		'PREPARE prepared_name prep_type_clause AS PreparableStmt',
+
+	'DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectStmt' =>
+		'DECLARE cursor_name cursor_options opt_readahead CURSOR opt_hold FOR SelectStmt'
 );
 
 my $block        = '';
diff --git a/src/interfaces/ecpg/preproc/ecpg.addons b/src/interfaces/ecpg/preproc/ecpg.addons
index 5c5adf7..e80044e 100644
--- a/src/interfaces/ecpg/preproc/ecpg.addons
+++ b/src/interfaces/ecpg/preproc/ecpg.addons
@@ -15,7 +15,10 @@ ECPG: stmtClosePortalStmt block
 			}
 		}
 
-		output_statement($1, 0, ECPGst_normal);
+		if (use_fetch_readahead)
+			output_close_statement($1, 0, ECPGst_normal);
+		else
+			output_statement($1, 0, ECPGst_normal);
 	}
 ECPG: stmtDeallocateStmt block
 	{
@@ -24,8 +27,14 @@ ECPG: stmtDeallocateStmt block
 ECPG: stmtDeclareCursorStmt block
 	{ output_simple_statement($1); }
 ECPG: stmtDiscardStmt block
-ECPG: stmtFetchStmt block
 	{ output_statement($1, 1, ECPGst_normal); }
+ECPG: stmtFetchStmt block
+	{
+		if (use_fetch_readahead)
+			output_fetch_statement($1, 1, ECPGst_normal);
+		else
+			output_statement($1, 1, ECPGst_normal);
+	}
 ECPG: stmtDeleteStmt block
 ECPG: stmtInsertStmt block
 ECPG: stmtSelectStmt block
@@ -132,7 +141,10 @@ ECPG: stmtViewStmt rule
 		if ((ptr = add_additional_variables($1, true)) != NULL)
 		{
 			connection = ptr->connection ? mm_strdup(ptr->connection) : NULL;
-			output_statement(mm_strdup(ptr->command), 0, ECPGst_normal);
+			if (use_fetch_readahead)
+				output_open_statement(mm_strdup(ptr->command), 0, ECPGst_normal);
+			else
+				output_statement(mm_strdup(ptr->command), 0, ECPGst_normal);
 			ptr->opened = true;
 		}
 	}
@@ -190,6 +202,21 @@ ECPG: stmtViewStmt rule
 ECPG: where_or_current_clauseWHERECURRENT_POFcursor_name block
 	{
 		char *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
+		struct cursor *ptr;
+
+		for (ptr = cur; ptr != NULL; ptr = ptr->next)
+		{
+			if (strcmp(ptr->name, $4) == 0)
+				break;
+		}
+		if (!ptr)
+			mmerror(PARSE_ERROR, ET_FATAL, "cursor \"%s\" does not exist", $4);
+
+		if (ptr->fetch_readahead)
+		{
+			mmerror(PARSE_ERROR, ET_ERROR,
+				"\"WHERE CURRENT OF\" is incompatible with a READAHEAD cursor\n");
+		}
 		$$ = cat_str(2,mm_strdup("where current of"), cursor_marker);
 	}
 ECPG: CopyStmtCOPYopt_binaryqualified_nameopt_column_listopt_oidscopy_fromcopy_file_namecopy_delimiteropt_withcopy_options addon
@@ -210,31 +237,77 @@ ECPG: var_valueNumericOnly addon
 		}
 ECPG: fetch_argscursor_name addon
 		add_additional_variables($1, false);
+		set_cursor_readahead($1);
 		if ($1[0] == ':')
 		{
 			free($1);
 			$1 = mm_strdup("$0");
 		}
+		current_cursor_direction = ECPGc_forward;
+		current_cursor_amount = mm_strdup("1");
 ECPG: fetch_argsfrom_incursor_name addon
 		add_additional_variables($2, false);
+		set_cursor_readahead($2);
 		if ($2[0] == ':')
 		{
 			free($2);
 			$2 = mm_strdup("$0");
 		}
+		current_cursor_direction = ECPGc_forward;
+		current_cursor_amount = mm_strdup("1");
 ECPG: fetch_argsNEXTopt_from_incursor_name addon
+		add_additional_variables($3, false);
+		set_cursor_readahead($3);
+		if ($3[0] == ':')
+		{
+			free($3);
+			$3 = mm_strdup("$0");
+		}
+		current_cursor_direction = ECPGc_forward;
+		current_cursor_amount = mm_strdup("1");
 ECPG: fetch_argsPRIORopt_from_incursor_name addon
+		add_additional_variables($3, false);
+		set_cursor_readahead($3);
+		if ($3[0] == ':')
+		{
+			free($3);
+			$3 = mm_strdup("$0");
+		}
+		current_cursor_direction = ECPGc_backward;
+		current_cursor_amount = mm_strdup("1"); 
 ECPG: fetch_argsFIRST_Popt_from_incursor_name addon
+		add_additional_variables($3, false);
+		set_cursor_readahead($3);
+		if ($3[0] == ':')
+		{
+			free($3);
+			$3 = mm_strdup("$0");
+		}
+		current_cursor_direction = ECPGc_absolute;
+		current_cursor_amount = mm_strdup("1");
 ECPG: fetch_argsLAST_Popt_from_incursor_name addon
+		add_additional_variables($3, false);
+		set_cursor_readahead($3);
+		if ($3[0] == ':')
+		{
+			free($3);
+			$3 = mm_strdup("$0");
+		}
+		current_cursor_direction = ECPGc_absolute;
+		current_cursor_amount = mm_strdup("-1");
 ECPG: fetch_argsALLopt_from_incursor_name addon
 		add_additional_variables($3, false);
+		set_cursor_readahead($3);
 		if ($3[0] == ':')
 		{
 			free($3);
 			$3 = mm_strdup("$0");
 		}
+		current_cursor_direction = ECPGc_forward;
+		current_cursor_amount = mm_strdup("all");
 ECPG: fetch_argsSignedIconstopt_from_incursor_name addon
 		add_additional_variables($3, false);
+		set_cursor_readahead($3);
 		if ($3[0] == ':')
 		{
 			free($3);
@@ -245,19 +318,76 @@ ECPG: fetch_argsSignedIconstopt_from_incursor_name addon
 			free($1);
 			$1 = mm_strdup("$0");
 		}
+		current_cursor_direction = ECPGc_forward;
+		current_cursor_amount = mm_strdup($1);
 ECPG: fetch_argsFORWARDALLopt_from_incursor_name addon
+		add_additional_variables($4, false);
+		set_cursor_readahead($4);
+		if ($4[0] == ':')
+		{
+			free($4);
+			$4 = mm_strdup("$0");
+		}
+		current_cursor_direction = ECPGc_forward;
+		current_cursor_amount = mm_strdup("all");
 ECPG: fetch_argsBACKWARDALLopt_from_incursor_name addon
 		add_additional_variables($4, false);
+		set_cursor_readahead($4);
 		if ($4[0] == ':')
 		{
 			free($4);
 			$4 = mm_strdup("$0");
 		}
+		current_cursor_direction = ECPGc_backward;
+		current_cursor_amount = mm_strdup("all");
 ECPG: fetch_argsABSOLUTE_PSignedIconstopt_from_incursor_name addon
+		add_additional_variables($4, false);
+		set_cursor_readahead($4);
+		if ($4[0] == ':')
+		{
+			free($4);
+			$4 = mm_strdup("$0");
+		}
+		if ($2[0] == '$')
+		{
+			free($2);
+			$2 = mm_strdup("$0");
+		}
+		current_cursor_direction = ECPGc_absolute;
+		current_cursor_amount = mm_strdup($2);
 ECPG: fetch_argsRELATIVE_PSignedIconstopt_from_incursor_name addon
+		add_additional_variables($4, false);
+		set_cursor_readahead($4);
+		if ($4[0] == ':')
+		{
+			free($4);
+			$4 = mm_strdup("$0");
+		}
+		if ($2[0] == '$')
+		{
+			free($2);
+			$2 = mm_strdup("$0");
+		}
+		current_cursor_direction = ECPGc_relative;
+		current_cursor_amount = mm_strdup($2);
 ECPG: fetch_argsFORWARDSignedIconstopt_from_incursor_name addon
+		add_additional_variables($4, false);
+		set_cursor_readahead($4);
+		if ($4[0] == ':')
+		{
+			free($4);
+			$4 = mm_strdup("$0");
+		}
+		if ($2[0] == '$')
+		{
+			free($2);
+			$2 = mm_strdup("$0");
+		}
+		current_cursor_direction = ECPGc_forward;
+		current_cursor_amount = mm_strdup($2);
 ECPG: fetch_argsBACKWARDSignedIconstopt_from_incursor_name addon
 		add_additional_variables($4, false);
+		set_cursor_readahead($4);
 		if ($4[0] == ':')
 		{
 			free($4);
@@ -268,7 +398,15 @@ ECPG: fetch_argsBACKWARDSignedIconstopt_from_incursor_name addon
 			free($2);
 			$2 = mm_strdup("$0");
 		}
-ECPG: cursor_namename rule
+		current_cursor_direction = ECPGc_backward;
+		current_cursor_amount = mm_strdup($2);
+ECPG: cursor_namename block
+		{
+			if (current_cursor)
+				free(current_cursor);
+			current_cursor = make3_str(mm_strdup("\""), mm_strdup($1), mm_strdup("\""));
+			$$ = $1;
+		}
 	| char_civar
 		{
 			char *curname = mm_alloc(strlen($1) + 2);
@@ -291,7 +429,7 @@ ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block
 	}
 ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block
 	{ $$ = $2; }
-ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectStmt block
+ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsopt_readaheadCURSORopt_holdFORSelectStmt block
 	{
 		struct cursor *ptr, *this;
 		char *cursor_marker = $2[0] == ':' ? mm_strdup("$0") : mm_strdup($2);
@@ -316,7 +454,13 @@ ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectSt
 		this->function = (current_function ? mm_strdup(current_function) : NULL);
 		this->connection = connection;
 		this->opened = false;
-		this->command =  cat_str(7, mm_strdup("declare"), cursor_marker, $3, mm_strdup("cursor"), $5, mm_strdup("for"), $7);
+		if (!strcmp($4, "1"))
+			this->fetch_readahead = true;
+		else if (!strcmp($4, "0"))
+			this->fetch_readahead = false;
+		else
+			this->fetch_readahead = fetch_readahead;
+		this->command =  cat_str(7, mm_strdup("declare"), cursor_marker, $3, mm_strdup("cursor"), $6, mm_strdup("for"), $8);
 		this->argsinsert = argsinsert;
 		this->argsinsert_oos = NULL;
 		this->argsresult = argsresult;
@@ -343,6 +487,8 @@ ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectSt
 ECPG: ClosePortalStmtCLOSEcursor_name block
 	{
 		char *cursor_marker = $2[0] == ':' ? mm_strdup("$0") : $2;
+		if (!INFORMIX_MODE || pg_strcasecmp($2, "database") != 0)
+			set_cursor_readahead($2);
 		$$ = cat2_str(mm_strdup("close"), cursor_marker);
 	}
 ECPG: opt_hold block
diff --git a/src/interfaces/ecpg/preproc/ecpg.c b/src/interfaces/ecpg/preproc/ecpg.c
index 6f73148..afb898b 100644
--- a/src/interfaces/ecpg/preproc/ecpg.c
+++ b/src/interfaces/ecpg/preproc/ecpg.c
@@ -18,7 +18,8 @@ bool		autocommit = false,
 			force_indicator = true,
 			questionmarks = false,
 			regression_mode = false,
-			auto_prepare = false;
+			auto_prepare = false,
+			fetch_readahead = false;
 
 char	   *output_filename;
 
@@ -51,7 +52,7 @@ help(const char *progname)
 	printf(_("  -I DIRECTORY   search DIRECTORY for include files\n"));
 	printf(_("  -o OUTFILE     write result to OUTFILE\n"));
 	printf(_("  -r OPTION      specify run-time behavior; OPTION can be:\n"
-	 "                 \"no_indicator\", \"prepare\", \"questionmarks\"\n"));
+	 "                 \"no_indicator\", \"prepare\", \"questionmarks\", \"fetch_readahead\"\n"));
 	printf(_("  --regression   run in regression testing mode\n"));
 	printf(_("  -t             turn on autocommit of transactions\n"));
 	printf(_("  --help         show this help, then exit\n"));
@@ -229,6 +230,8 @@ main(int argc, char *const argv[])
 					auto_prepare = true;
 				else if (strcmp(optarg, "questionmarks") == 0)
 					questionmarks = true;
+				else if (strcmp(optarg, "fetch_readahead") == 0)
+					fetch_readahead = true;
 				else
 				{
 					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), argv[0]);
diff --git a/src/interfaces/ecpg/preproc/ecpg.header b/src/interfaces/ecpg/preproc/ecpg.header
index 94c45c8..6ca3d3e 100644
--- a/src/interfaces/ecpg/preproc/ecpg.header
+++ b/src/interfaces/ecpg/preproc/ecpg.header
@@ -34,7 +34,11 @@
  */
 int struct_level = 0;
 int braces_open; /* brace level counter */
+bool use_fetch_readahead = false;
 char *current_function;
+char *current_cursor = NULL;
+enum ECPG_cursor_direction current_cursor_direction;
+char *current_cursor_amount = NULL;
 int ecpg_internal_var = 0;
 char	*connection = NULL;
 char	*input_filename = NULL;
@@ -111,6 +115,26 @@ mmerror(int error_code, enum errortype type, const char *error, ...)
 }
 
 /*
+ * set use_fetch_readahead based on the current cursor
+ * doesn't return if the cursor is not declared
+ */
+static void
+set_cursor_readahead(const char *curname)
+{
+	struct cursor *ptr;
+
+	for (ptr = cur; ptr != NULL; ptr = ptr->next)
+	{
+		if (strcmp(ptr->name, curname) == 0)
+			break;
+	}
+	if (!ptr)
+		mmerror(PARSE_ERROR, ET_FATAL, "cursor \"%s\" does not exist", curname);
+
+	use_fetch_readahead = ptr->fetch_readahead;
+}
+
+/*
  * string concatenation
  */
 
diff --git a/src/interfaces/ecpg/preproc/ecpg.tokens b/src/interfaces/ecpg/preproc/ecpg.tokens
index b55138a..3995b59 100644
--- a/src/interfaces/ecpg/preproc/ecpg.tokens
+++ b/src/interfaces/ecpg/preproc/ecpg.tokens
@@ -10,7 +10,7 @@
                 SQL_FREE SQL_GET SQL_GO SQL_GOTO SQL_IDENTIFIED
                 SQL_INDICATOR SQL_KEY_MEMBER SQL_LENGTH
                 SQL_LONG SQL_NULLABLE SQL_OCTET_LENGTH
-                SQL_OPEN SQL_OUTPUT SQL_REFERENCE
+                SQL_OPEN SQL_OUTPUT SQL_READAHEAD SQL_REFERENCE
                 SQL_RETURNED_LENGTH SQL_RETURNED_OCTET_LENGTH SQL_SCALE
                 SQL_SECTION SQL_SHORT SQL_SIGNED SQL_SQL SQL_SQLERROR
                 SQL_SQLPRINT SQL_SQLWARNING SQL_START SQL_STOP
diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer
index a362aff..2bf960f 100644
--- a/src/interfaces/ecpg/preproc/ecpg.trailer
+++ b/src/interfaces/ecpg/preproc/ecpg.trailer
@@ -287,7 +287,7 @@ prepared_name: name
  * Declare a prepared cursor. The syntax is different from the standard
  * declare statement, so we create a new rule.
  */
-ECPGCursorStmt:  DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared_name
+ECPGCursorStmt:  DECLARE cursor_name cursor_options opt_readahead CURSOR opt_hold FOR prepared_name
 		{
 			struct cursor *ptr, *this;
 			char *cursor_marker = $2[0] == ':' ? mm_strdup("$0") : mm_strdup($2);
@@ -315,15 +315,22 @@ ECPGCursorStmt:  DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared
 			this->name = $2;
 			this->function = (current_function ? mm_strdup(current_function) : NULL);
 			this->connection = connection;
-			this->command =  cat_str(6, mm_strdup("declare"), cursor_marker, $3, mm_strdup("cursor"), $5, mm_strdup("for $1"));
+			this->opened = false;
+			if (!strcmp($4, "1"))
+				this->fetch_readahead = true;
+			else if (!strcmp($4, "0"))
+				this->fetch_readahead = false;
+			else
+				this->fetch_readahead = fetch_readahead;
+			this->command =  cat_str(6, mm_strdup("declare"), cursor_marker, $3, mm_strdup("cursor"), $6, mm_strdup("for $1"));
 			this->argsresult = NULL;
 			this->argsresult_oos = NULL;
 
 			thisquery->type = &ecpg_query;
 			thisquery->brace_level = 0;
 			thisquery->next = NULL;
-			thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(, , __LINE__)") + strlen(con) + strlen($7));
-			sprintf(thisquery->name, "ECPGprepared_statement(%s, %s, __LINE__)", con, $7);
+			thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(, , __LINE__)") + strlen(con) + strlen($8));
+			sprintf(thisquery->name, "ECPGprepared_statement(%s, %s, __LINE__)", con, $8);
 
 			this->argsinsert = NULL;
 			this->argsinsert_oos = NULL;
@@ -348,6 +355,11 @@ ECPGCursorStmt:  DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared
 		}
 		;
 
+opt_readahead:	SQL_READAHEAD				{ $$ = mm_strdup("1"); }
+		| NO SQL_READAHEAD			{ $$ = mm_strdup("0"); }
+		| /* EMPTY */			{ $$ = mm_strdup("default"); }
+		;
+
 ECPGExecuteImmediateStmt: EXECUTE IMMEDIATE execstring
 			{
 			  /* execute immediate means prepare the statement and
@@ -988,6 +1000,7 @@ ECPGOpen: SQL_OPEN cursor_name opt_ecpg_using
 		{
 			if ($2[0] == ':')
 				remove_variable_from_list(&argsinsert, find_variable($2 + 1));
+			set_cursor_readahead($2);
 			$$ = $2;
 		}
 		;
@@ -1656,6 +1669,10 @@ char_civar: char_variable
 		{
 			char *ptr = strstr($1, ".arr");
 
+			if (current_cursor)
+				free(current_cursor);
+			current_cursor = mm_strdup($1);
+
 			if (ptr) /* varchar, we need the struct name here, not the struct element */
 				*ptr = '\0';
 			add_variable_to_head(&argsinsert, find_variable($1), &no_indicator);
diff --git a/src/interfaces/ecpg/preproc/ecpg.type b/src/interfaces/ecpg/preproc/ecpg.type
index ac6aa00..2662372 100644
--- a/src/interfaces/ecpg/preproc/ecpg.type
+++ b/src/interfaces/ecpg/preproc/ecpg.type
@@ -85,6 +85,7 @@
 %type <str> opt_output
 %type <str> opt_pointer
 %type <str> opt_port
+%type <str> opt_readahead
 %type <str> opt_reference
 %type <str> opt_scale
 %type <str> opt_server
diff --git a/src/interfaces/ecpg/preproc/ecpg_keywords.c b/src/interfaces/ecpg/preproc/ecpg_keywords.c
index 8032c30..cbd37c6 100644
--- a/src/interfaces/ecpg/preproc/ecpg_keywords.c
+++ b/src/interfaces/ecpg/preproc/ecpg_keywords.c
@@ -56,6 +56,7 @@ static const ScanKeyword ScanECPGKeywords[] = {
 	{"octet_length", SQL_OCTET_LENGTH, 0},
 	{"open", SQL_OPEN, 0},
 	{"output", SQL_OUTPUT, 0},
+	{"readahead", SQL_READAHEAD, 0},
 	{"reference", SQL_REFERENCE, 0},
 	{"returned_length", SQL_RETURNED_LENGTH, 0},
 	{"returned_octet_length", SQL_RETURNED_OCTET_LENGTH, 0},
diff --git a/src/interfaces/ecpg/preproc/extern.h b/src/interfaces/ecpg/preproc/extern.h
index ccf5548..3d22d3a 100644
--- a/src/interfaces/ecpg/preproc/extern.h
+++ b/src/interfaces/ecpg/preproc/extern.h
@@ -24,12 +24,17 @@ extern bool autocommit,
 			force_indicator,
 			questionmarks,
 			regression_mode,
-			auto_prepare;
+			auto_prepare,
+			fetch_readahead;
+extern bool	use_fetch_readahead;
 extern int	braces_open,
 			ret_value,
 			struct_level,
 			ecpg_internal_var;
 extern char *current_function;
+extern char *current_cursor;  
+extern enum ECPG_cursor_direction current_cursor_direction;
+extern char *current_cursor_amount;
 extern char *descriptor_index;
 extern char *descriptor_name;
 extern char *connection;
@@ -67,6 +72,9 @@ extern void output_statement(char *, int, enum ECPG_statement_type);
 extern void output_prepare_statement(char *, char *);
 extern void output_deallocate_prepare_statement(char *);
 extern void output_simple_statement(char *);
+extern void output_open_statement(char *, int, enum ECPG_statement_type);
+extern void output_fetch_statement(char *, int, enum ECPG_statement_type);
+extern void output_close_statement(char *, int, enum ECPG_statement_type);
 extern char *hashline_number(void);
 extern int	base_yyparse(void);
 extern int	base_yylex(void);
diff --git a/src/interfaces/ecpg/preproc/output.c b/src/interfaces/ecpg/preproc/output.c
index 389a527..68ad718 100644
--- a/src/interfaces/ecpg/preproc/output.c
+++ b/src/interfaces/ecpg/preproc/output.c
@@ -112,10 +112,16 @@ static char *ecpg_statement_type_name[] = {
 	"ECPGst_prepnormal"
 };
 
-void
-output_statement(char *stmt, int whenever_mode, enum ECPG_statement_type st)
+static char *ecpg_cursor_direction_name[] = {
+	"ECPGc_absolute",
+	"ECPGc_relative",
+	"ECPGc_forward", 
+	"ECPGc_backward" 
+};
+
+static void
+output_statement_epilogue(char *stmt, int whenever_mode, enum ECPG_statement_type st)
 {
-	fprintf(yyout, "{ ECPGdo(__LINE__, %d, %d, %s, %d, ", compat, force_indicator, connection ? connection : "NULL", questionmarks);
 	if (st == ECPGst_execute || st == ECPGst_exec_immediate)
 	{
 		fprintf(yyout, "%s, %s, ", ecpg_statement_type_name[st], stmt);
@@ -145,6 +151,13 @@ output_statement(char *stmt, int whenever_mode, enum ECPG_statement_type st)
 }
 
 void
+output_statement(char *stmt, int whenever_mode, enum ECPG_statement_type st)
+{
+	fprintf(yyout, "{ ECPGdo(__LINE__, %d, %d, %s, %d, ", compat, force_indicator, connection ? connection : "NULL", questionmarks);
+	output_statement_epilogue(stmt, whenever_mode, st);
+}
+
+void
 output_prepare_statement(char *name, char *stmt)
 {
 	fprintf(yyout, "{ ECPGprepare(__LINE__, %s, %d, ", connection ? connection : "NULL", questionmarks);
@@ -178,6 +191,51 @@ output_deallocate_prepare_statement(char *name)
 		free(connection);
 }
 
+void
+output_open_statement(char *stmt, int whenever_mode, enum ECPG_statement_type st)
+{
+	fprintf(yyout, "{ ECPGopen(__LINE__, %d, %d, %s, %d, %s, ",
+			compat,
+			force_indicator,
+			connection ? connection : "NULL",
+			questionmarks,
+			current_cursor);
+	output_statement_epilogue(stmt, whenever_mode, st);
+}
+
+void
+output_fetch_statement(char *stmt, int whenever_mode, enum ECPG_statement_type st)
+{
+	char	*amount = mm_alloc(strlen(current_cursor_amount) + 3);
+
+	if (!amount)
+		return;
+
+	sprintf(amount, "\"%s\"", current_cursor_amount);
+	fprintf(yyout, "{ ECPGfetch(__LINE__, %d, %d, %s, %d, %s, %s, %s, ",
+			compat,
+			force_indicator,
+			connection ? connection : "NULL",
+			questionmarks,
+			current_cursor,
+			ecpg_cursor_direction_name[current_cursor_direction],
+			amount);
+	output_statement_epilogue(stmt, whenever_mode, st);
+	free(amount);
+}
+
+void
+output_close_statement(char *stmt, int whenever_mode, enum ECPG_statement_type st)
+{
+	fprintf(yyout, "{ ECPGclose(__LINE__, %d, %d, %s, %d, %s, ",
+			compat,
+			force_indicator,
+			connection ? connection : "NULL",
+			questionmarks,
+			current_cursor);
+	output_statement_epilogue(stmt, whenever_mode, st);
+}
+
 static void
 output_escaped_str(char *str, bool quoted)
 {
diff --git a/src/interfaces/ecpg/preproc/parse.pl b/src/interfaces/ecpg/preproc/parse.pl
index 515470e..aebcaf4 100644
--- a/src/interfaces/ecpg/preproc/parse.pl
+++ b/src/interfaces/ecpg/preproc/parse.pl
@@ -90,6 +90,8 @@ my %replace_line = (
 	'fetch_argsFORWARDopt_from_incursor_name'      => 'ignore',
 	'fetch_argsBACKWARDopt_from_incursor_name'     => 'ignore',
 	"opt_array_boundsopt_array_bounds'['Iconst']'" => 'ignore',
+	'DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectStmt' =>
+			'DECLARE cursor_name cursor_options opt_readahead CURSOR opt_hold FOR SelectStmt',
 	'VariableShowStmtSHOWvar_name'                 => 'SHOW var_name ecpg_into',
 	'VariableShowStmtSHOWTIMEZONE' => 'SHOW TIME ZONE ecpg_into',
 	'VariableShowStmtSHOWTRANSACTIONISOLATIONLEVEL' => 'SHOW TRANSACTION ISOLATION LEVEL ecpg_into',
diff --git a/src/interfaces/ecpg/preproc/type.h b/src/interfaces/ecpg/preproc/type.h
index 68e0d1a..89c920f 100644
--- a/src/interfaces/ecpg/preproc/type.h
+++ b/src/interfaces/ecpg/preproc/type.h
@@ -130,6 +130,7 @@ struct cursor
 	char	   *command;
 	char	   *connection;
 	bool		opened;
+	bool		fetch_readahead;
 	struct arguments *argsinsert;
 	struct arguments *argsinsert_oos;
 	struct arguments *argsresult;
diff --git a/src/interfaces/ecpg/test/ecpg_schedule b/src/interfaces/ecpg/test/ecpg_schedule
index c07ea93..a15b7d9 100644
--- a/src/interfaces/ecpg/test/ecpg_schedule
+++ b/src/interfaces/ecpg/test/ecpg_schedule
@@ -20,6 +20,7 @@ test: preproc/array_of_struct
 test: preproc/autoprep
 test: preproc/comment
 test: preproc/cursor
+test: preproc/cursor-readahead
 test: preproc/define
 test: preproc/init
 test: preproc/strings
diff --git a/src/interfaces/ecpg/test/ecpg_schedule_tcp b/src/interfaces/ecpg/test/ecpg_schedule_tcp
index 77481b5..60994e0 100644
--- a/src/interfaces/ecpg/test/ecpg_schedule_tcp
+++ b/src/interfaces/ecpg/test/ecpg_schedule_tcp
@@ -20,6 +20,7 @@ test: preproc/array_of_struct
 test: preproc/autoprep
 test: preproc/comment
 test: preproc/cursor
+test: preproc/cursor-readahead
 test: preproc/define
 test: preproc/init
 test: preproc/strings
diff --git a/src/interfaces/ecpg/test/expected/preproc-cursor-readahead.c b/src/interfaces/ecpg/test/expected/preproc-cursor-readahead.c
new file mode 100644
index 0000000..ef39815
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/preproc-cursor-readahead.c
@@ -0,0 +1,663 @@
+/* Processed by ecpg (regression mode) */
+/* These include files are added by the preprocessor */
+#include <ecpglib.h>
+#include <ecpgerrno.h>
+#include <sqlca.h>
+/* End of automatic include section */
+#define ECPGdebug(X,Y) ECPGdebug((X)+100,(Y))
+
+#line 1 "cursor-readahead.pgc"
+#include <stdio.h>
+#include <malloc.h>
+
+/* test automatic prepare for all statements */
+
+#line 1 "regression.h"
+
+
+
+
+
+
+#line 5 "cursor-readahead.pgc"
+
+
+
+#line 1 "sqlda.h"
+#ifndef ECPG_SQLDA_H
+#define ECPG_SQLDA_H
+
+#ifdef _ECPG_INFORMIX_H
+
+#include "sqlda-compat.h"
+typedef struct sqlvar_compat	sqlvar_t;
+typedef struct sqlda_compat	sqlda_t;
+
+#else
+
+#include "sqlda-native.h"
+typedef struct sqlvar_struct	sqlvar_t;
+typedef struct sqlda_struct	sqlda_t;
+
+#endif
+
+#endif /* ECPG_SQLDA_H */
+
+#line 7 "cursor-readahead.pgc"
+
+
+/* exec sql whenever sqlerror  sqlprint ; */
+#line 9 "cursor-readahead.pgc"
+
+/* exec sql whenever sql_warning  sqlprint ; */
+#line 10 "cursor-readahead.pgc"
+
+
+#define MAXID	(513)
+
+int main(void)
+{
+	int	counts[2] = { 1, 5 };
+	/* exec sql begin declare section */
+		
+		
+		   
+		  
+	
+#line 18 "cursor-readahead.pgc"
+ char * curname ;
+ 
+#line 19 "cursor-readahead.pgc"
+ int maxid ;
+ 
+#line 20 "cursor-readahead.pgc"
+ int i , j , count , rows ;
+ 
+#line 21 "cursor-readahead.pgc"
+ int id , id1 [ 5 ] , id2 [ 5 ] ;
+/* exec sql end declare section */
+#line 22 "cursor-readahead.pgc"
+
+	sqlda_t	*sqlda;
+
+	/*
+	 * Intentionally don't create a 2MB stderr file for this test.
+	 * Enable it manually if you're interested in it.
+	 */
+#if 0
+	ECPGdebug(1, stderr);
+#endif
+
+	{ ECPGconnect(__LINE__, 0, "regress1" , NULL, NULL , NULL, 0); 
+#line 33 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 33 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 33 "cursor-readahead.pgc"
+
+
+	{ ECPGtrans(__LINE__, NULL, "begin");
+#line 35 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 35 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 35 "cursor-readahead.pgc"
+
+
+	maxid = MAXID;
+
+	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table ra_test ( id integer primary key )", ECPGt_EOIT, ECPGt_EORT);
+#line 39 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 39 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 39 "cursor-readahead.pgc"
+
+	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into ra_test select i . i from generate_series ( 1 , $1  ) as i", 
+	ECPGt_int,&(maxid),(long)1,(long)1,sizeof(int), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 40 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 40 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 40 "cursor-readahead.pgc"
+
+
+	{ ECPGtrans(__LINE__, NULL, "commit");
+#line 42 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 42 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 42 "cursor-readahead.pgc"
+
+
+	{ ECPGtrans(__LINE__, NULL, "begin");
+#line 44 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 44 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 44 "cursor-readahead.pgc"
+
+
+	curname = "xx";
+	ECPGset_var( 0, &( curname ), __LINE__);\
+ /* declare $0 scroll cursor for select * from ra_test */
+#line 47 "cursor-readahead.pgc"
+
+	/* declare xcur scroll cursor for select * from ra_test */
+#line 48 "cursor-readahead.pgc"
+
+
+	/* exec sql whenever not found  break ; */
+#line 50 "cursor-readahead.pgc"
+
+
+	for (i = 0; i < 2; i++)
+	{
+		count = counts[i];
+
+		{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare $0 scroll cursor for select * from ra_test", 
+	ECPGt_char,&(curname),(long)0,(long)1,(1)*sizeof(char), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 56 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 56 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 56 "cursor-readahead.pgc"
+
+		{ ECPGopen(__LINE__, 0, 1, NULL, 0, "xcur", ECPGst_normal, "declare xcur scroll cursor for select * from ra_test", ECPGt_EOIT, ECPGt_EORT);
+#line 57 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 57 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 57 "cursor-readahead.pgc"
+
+
+		id = 0;
+		while (1)
+		{
+			for (j = 0; j < count; j++)
+			{
+				id1[j] = -1;
+				id2[j] = -1;
+			}
+
+			{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch $0 from $0", 
+	ECPGt_int,&(count),(long)1,(long)1,sizeof(int), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
+	ECPGt_char,&(curname),(long)0,(long)1,(1)*sizeof(char), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, 
+	ECPGt_int,(id1),(long)1,(long)5,sizeof(int), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 68 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode == ECPG_NOT_FOUND) break;
+#line 68 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 68 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 68 "cursor-readahead.pgc"
+
+			{ ECPGfetch(__LINE__, 0, 1, NULL, 0, "xcur", ECPGc_forward, "$0", ECPGst_normal, "fetch $0 from xcur", 
+	ECPGt_int,&(count),(long)1,(long)1,sizeof(int), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, 
+	ECPGt_int,(id2),(long)1,(long)5,sizeof(int), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 69 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode == ECPG_NOT_FOUND) break;
+#line 69 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 69 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 69 "cursor-readahead.pgc"
+
+
+			rows = sqlca.sqlerrd[2];
+
+			for (j = 0; j < rows; j++)
+			{
+				id++;
+
+				if (id != id1[j] || id != id2[j] || id1[j] != id2[j])
+				{
+					printf("Reading two cursors forward: ERROR. id = %d id1 = %d id2 = %d\n", id, id1[j], id2[j]);
+					break;
+				}
+			}
+			if (j != rows)
+				break;
+		}
+
+		if (sqlca.sqlwarn[0] == 'W') sqlprint();
+		if (sqlca.sqlcode < 0) sqlprint();
+
+		if (id == maxid)
+			printf("Reading readahead and non-readahead cursors simultaneously forward by %d record: SUCCESS\n", count);
+		else
+			printf("Reading readahead and non-readahead cursors simultaneously forward by %d record: FAILED at %d, id1 %d id2 %d\n", count, id, id1[j], id2[j]);
+
+		{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close $0", 
+	ECPGt_char,&(curname),(long)0,(long)1,(1)*sizeof(char), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 95 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 95 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 95 "cursor-readahead.pgc"
+
+		{ ECPGclose(__LINE__, 0, 1, NULL, 0, "xcur", ECPGst_normal, "close xcur", ECPGt_EOIT, ECPGt_EORT);
+#line 96 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 96 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 96 "cursor-readahead.pgc"
+
+
+		/* exec sql whenever not found  continue ; */
+#line 98 "cursor-readahead.pgc"
+
+
+		{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare $0 scroll cursor for select * from ra_test", 
+	ECPGt_char,&(curname),(long)0,(long)1,(1)*sizeof(char), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 100 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 100 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 100 "cursor-readahead.pgc"
+
+		{ ECPGopen(__LINE__, 0, 1, NULL, 0, "xcur", ECPGst_normal, "declare xcur scroll cursor for select * from ra_test", ECPGt_EOIT, ECPGt_EORT);
+#line 101 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 101 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 101 "cursor-readahead.pgc"
+
+		{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch last from $0", 
+	ECPGt_char,&(curname),(long)0,(long)1,(1)*sizeof(char), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, 
+	ECPGt_int,(id1),(long)1,(long)5,sizeof(int), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 102 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 102 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 102 "cursor-readahead.pgc"
+
+		{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch from $0", 
+	ECPGt_char,&(curname),(long)0,(long)1,(1)*sizeof(char), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, 
+	ECPGt_int,(id1),(long)1,(long)5,sizeof(int), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 103 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 103 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 103 "cursor-readahead.pgc"
+
+		if (sqlca.sqlcode == ECPG_NOT_FOUND)
+			printf("After last record in cursor '%s' (value %d), fetching backwards.\n", curname, id1[0]);
+		else
+			goto err;
+		{ ECPGfetch(__LINE__, 0, 1, NULL, 0, "xcur", ECPGc_absolute, "-1", ECPGst_normal, "fetch last from xcur", ECPGt_EOIT, 
+	ECPGt_int,(id2),(long)1,(long)5,sizeof(int), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 108 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 108 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 108 "cursor-readahead.pgc"
+
+		{ ECPGfetch(__LINE__, 0, 1, NULL, 0, "xcur", ECPGc_forward, "1", ECPGst_normal, "fetch from xcur", ECPGt_EOIT, 
+	ECPGt_int,(id2),(long)1,(long)5,sizeof(int), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 109 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 109 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 109 "cursor-readahead.pgc"
+
+		if (sqlca.sqlcode == ECPG_NOT_FOUND)
+			printf("After last record in cursor 'xcur' (value %d), fetching backwards.\n", id2[0]);
+		else
+			goto err;
+
+		/* exec sql whenever not found  break ; */
+#line 115 "cursor-readahead.pgc"
+
+
+		id = maxid;
+		while (1)
+		{
+			for (j = 0; j < count; j++)
+			{
+				id1[j] = -1;
+				id2[j] = -1;
+			}
+
+			{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch backward $0 from $0", 
+	ECPGt_int,&(count),(long)1,(long)1,sizeof(int), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
+	ECPGt_char,&(curname),(long)0,(long)1,(1)*sizeof(char), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, 
+	ECPGt_int,(id1),(long)1,(long)5,sizeof(int), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 126 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode == ECPG_NOT_FOUND) break;
+#line 126 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 126 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 126 "cursor-readahead.pgc"
+
+			{ ECPGfetch(__LINE__, 0, 1, NULL, 0, "xcur", ECPGc_backward, "$0", ECPGst_normal, "fetch backward $0 from xcur", 
+	ECPGt_int,&(count),(long)1,(long)1,sizeof(int), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, 
+	ECPGt_int,(id2),(long)1,(long)5,sizeof(int), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 127 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode == ECPG_NOT_FOUND) break;
+#line 127 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 127 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 127 "cursor-readahead.pgc"
+
+
+			rows = sqlca.sqlerrd[2];
+
+			for (j = 0; j < rows; j++)
+			{
+				if (id != id1[j] || id != id2[j] || id1[j] != id2[j])
+				{
+					printf("Reading two cursors backward: ERROR. id = %d id1 = %d id2 = %d\n", id, id1[j], id2[j]);
+					break;
+				}
+				id--;
+			}
+			if (j != rows)
+				break;
+		}
+
+		if (id == 0)
+			printf("Reading readahead and non-readahead cursors simultaneously backwards by %d record(s): SUCCESS\n", count);
+		else
+			printf("Reading readahead and non-readahead cursors simultaneously backwards by %d record(s): FAILED at %d, id1 %d id2 %d\n", count, id, id1[j], id2[j]);
+
+		{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close $0", 
+	ECPGt_char,&(curname),(long)0,(long)1,(1)*sizeof(char), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 149 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 149 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 149 "cursor-readahead.pgc"
+
+		{ ECPGclose(__LINE__, 0, 1, NULL, 0, "xcur", ECPGst_normal, "close xcur", ECPGt_EOIT, ECPGt_EORT);
+#line 150 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 150 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 150 "cursor-readahead.pgc"
+
+
+		/* exec sql whenever not found  continue ; */
+#line 152 "cursor-readahead.pgc"
+
+
+	}
+
+	sqlda = NULL;
+	{ ECPGopen(__LINE__, 0, 1, NULL, 0, "xcur", ECPGst_normal, "declare xcur scroll cursor for select * from ra_test", ECPGt_EOIT, ECPGt_EORT);
+#line 157 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 157 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 157 "cursor-readahead.pgc"
+
+	{ ECPGfetch(__LINE__, 0, 1, NULL, 0, "xcur", ECPGc_forward, "all", ECPGst_normal, "fetch all xcur", ECPGt_EOIT, 
+	ECPGt_sqlda, &sqlda, 0L, 0L, 0L, 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 158 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 158 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 158 "cursor-readahead.pgc"
+
+
+	/* exec sql whenever not found  break ; */
+#line 160 "cursor-readahead.pgc"
+
+
+	id = 0;
+	id1[0] = -1;
+	while (sqlda)
+	{
+		sqlda_t *tmp;
+
+		id++;
+		id1[0] = *(int *)sqlda->sqlvar[0].sqldata;
+
+		if (id != id1[0])
+		{
+			printf("Reading readahead cursors forward into sqlda chain: ERROR. id = %d id1 = %d\n", id, id1[0]);
+			break;
+		}
+		tmp = sqlda->desc_next;
+		free(sqlda);
+		sqlda = tmp;
+	}
+
+	if (id == maxid)
+		printf("Reading all records from a readahead cursor forward into sqlda chain: SUCCESS\n");
+	else
+		printf("Reading all records from a readahead cursor forward into sqlda chain: FAILED. id %d, id1 %d\n", id, id1[0]);
+
+	{ ECPGclose(__LINE__, 0, 1, NULL, 0, "xcur", ECPGst_normal, "close xcur", ECPGt_EOIT, ECPGt_EORT);
+#line 186 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 186 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 186 "cursor-readahead.pgc"
+
+
+	/* exec sql whenever not found  continue ; */
+#line 188 "cursor-readahead.pgc"
+
+
+	sqlda = NULL;
+	{ ECPGopen(__LINE__, 0, 1, NULL, 0, "xcur", ECPGst_normal, "declare xcur scroll cursor for select * from ra_test", ECPGt_EOIT, ECPGt_EORT);
+#line 191 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 191 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 191 "cursor-readahead.pgc"
+
+	{ ECPGfetch(__LINE__, 0, 1, NULL, 0, "xcur", ECPGc_absolute, "-1", ECPGst_normal, "fetch last from xcur", ECPGt_EOIT, 
+	ECPGt_int,(id1),(long)1,(long)5,sizeof(int), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 192 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 192 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 192 "cursor-readahead.pgc"
+
+	{ ECPGfetch(__LINE__, 0, 1, NULL, 0, "xcur", ECPGc_forward, "1", ECPGst_normal, "fetch from xcur", ECPGt_EOIT, 
+	ECPGt_int,(id1),(long)1,(long)5,sizeof(int), 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 193 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 193 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 193 "cursor-readahead.pgc"
+
+	if (sqlca.sqlcode == ECPG_NOT_FOUND)
+		printf("After last record in cursor 'xcur' (value %d), fetching backwards.\n", id1[0]);
+	else
+		goto err;
+
+	{ ECPGfetch(__LINE__, 0, 1, NULL, 0, "xcur", ECPGc_backward, "all", ECPGst_normal, "fetch backward all xcur", ECPGt_EOIT, 
+	ECPGt_sqlda, &sqlda, 0L, 0L, 0L, 
+	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
+#line 199 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 199 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 199 "cursor-readahead.pgc"
+
+
+	/* exec sql whenever not found  break ; */
+#line 201 "cursor-readahead.pgc"
+
+
+	id = maxid;
+	id1[0] = -1;
+	while (sqlda)
+	{
+		sqlda_t	*tmp;
+
+		id1[0] = *(int *)sqlda->sqlvar[0].sqldata;
+
+		if (id != id1[0])
+		{
+			printf("Reading readahead cursors backward into sqlda chain: ERROR. id = %d id1 = %d\n", id, id1[0]);
+			break;
+		}
+
+		tmp = sqlda->desc_next;
+		free(sqlda);
+		sqlda = tmp;
+
+		id--;
+	}
+
+	if (id == 0)
+		printf("Reading all records from readahead cursor backwards into sqlda chain: SUCCESS\n");
+	else
+		printf("Reading all records from readahead cursors backwards into sqlda chain: FAILED, id %d, id1 %d\n", id, id1[0]);
+
+	{ ECPGclose(__LINE__, 0, 1, NULL, 0, "xcur", ECPGst_normal, "close xcur", ECPGt_EOIT, ECPGt_EORT);
+#line 229 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 229 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 229 "cursor-readahead.pgc"
+
+
+err:
+
+	{ ECPGtrans(__LINE__, NULL, "commit");
+#line 233 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 233 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 233 "cursor-readahead.pgc"
+
+
+	{ ECPGtrans(__LINE__, NULL, "begin");
+#line 235 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 235 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 235 "cursor-readahead.pgc"
+
+
+	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "drop table ra_test", ECPGt_EOIT, ECPGt_EORT);
+#line 237 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 237 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 237 "cursor-readahead.pgc"
+
+
+	{ ECPGtrans(__LINE__, NULL, "commit");
+#line 239 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 239 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 239 "cursor-readahead.pgc"
+
+
+	{ ECPGdisconnect(__LINE__, "ALL");
+#line 241 "cursor-readahead.pgc"
+
+if (sqlca.sqlwarn[0] == 'W') sqlprint();
+#line 241 "cursor-readahead.pgc"
+
+if (sqlca.sqlcode < 0) sqlprint();}
+#line 241 "cursor-readahead.pgc"
+
+
+	return 0;
+}
diff --git a/src/interfaces/ecpg/test/expected/preproc-cursor-readahead.stderr b/src/interfaces/ecpg/test/expected/preproc-cursor-readahead.stderr
new file mode 100644
index 0000000..e69de29
diff --git a/src/interfaces/ecpg/test/expected/preproc-cursor-readahead.stdout b/src/interfaces/ecpg/test/expected/preproc-cursor-readahead.stdout
new file mode 100644
index 0000000..5e70dd7
--- /dev/null
+++ b/src/interfaces/ecpg/test/expected/preproc-cursor-readahead.stdout
@@ -0,0 +1,11 @@
+Reading readahead and non-readahead cursors simultaneously forward by 1 record: SUCCESS
+After last record in cursor 'xx' (value 513), fetching backwards.
+After last record in cursor 'xcur' (value 513), fetching backwards.
+Reading readahead and non-readahead cursors simultaneously backwards by 1 record(s): SUCCESS
+Reading readahead and non-readahead cursors simultaneously forward by 5 record: SUCCESS
+After last record in cursor 'xx' (value 513), fetching backwards.
+After last record in cursor 'xcur' (value 513), fetching backwards.
+Reading readahead and non-readahead cursors simultaneously backwards by 5 record(s): SUCCESS
+Reading all records from a readahead cursor forward into sqlda chain: SUCCESS
+After last record in cursor 'xcur' (value 513), fetching backwards.
+Reading all records from readahead cursor backwards into sqlda chain: SUCCESS
diff --git a/src/interfaces/ecpg/test/preproc/Makefile b/src/interfaces/ecpg/test/preproc/Makefile
index 3bcb63a..dfe4166 100644
--- a/src/interfaces/ecpg/test/preproc/Makefile
+++ b/src/interfaces/ecpg/test/preproc/Makefile
@@ -8,6 +8,7 @@ TESTS = array_of_struct array_of_struct.c \
 	autoprep autoprep.c \
 	comment comment.c \
 	cursor cursor.c \
+	cursor-readahead cursor-readahead.c \
 	define define.c \
 	init init.c \
 	strings strings.c \
diff --git a/src/interfaces/ecpg/test/preproc/cursor-readahead.pgc b/src/interfaces/ecpg/test/preproc/cursor-readahead.pgc
new file mode 100644
index 0000000..076f9b3
--- /dev/null
+++ b/src/interfaces/ecpg/test/preproc/cursor-readahead.pgc
@@ -0,0 +1,244 @@
+#include <stdio.h>
+#include <malloc.h>
+
+/* test automatic prepare for all statements */
+EXEC SQL INCLUDE ../regression;
+
+EXEC SQL INCLUDE sqlda.h;
+
+EXEC SQL WHENEVER SQLERROR SQLPRINT;
+EXEC SQL WHENEVER SQLWARNING SQLPRINT;
+
+#define MAXID	(513)
+
+int main(void)
+{
+	int	counts[2] = { 1, 5 };
+	EXEC SQL BEGIN DECLARE SECTION;
+	char	*curname;
+	int	maxid;
+	int	i, j, count, rows;
+	int	id, id1[5], id2[5];
+	EXEC SQL END DECLARE SECTION;
+	sqlda_t	*sqlda;
+
+	/*
+	 * Intentionally don't create a 2MB stderr file for this test.
+	 * Enable it manually if you're interested in it.
+	 */
+#if 0
+	ECPGdebug(1, stderr);
+#endif
+
+	EXEC SQL CONNECT TO REGRESSDB1;
+
+	EXEC SQL BEGIN;
+
+	maxid = MAXID;
+
+	EXEC SQL CREATE TABLE ra_test (id INTEGER PRIMARY KEY);
+	EXEC SQL INSERT INTO ra_test SELECT i.i FROM generate_series(1, :maxid) as i;
+
+	EXEC SQL COMMIT;
+
+	EXEC SQL BEGIN;
+
+	curname = "xx";
+	EXEC SQL DECLARE :curname SCROLL CURSOR FOR SELECT * FROM ra_test;
+	EXEC SQL DECLARE xcur SCROLL READAHEAD CURSOR FOR SELECT * FROM ra_test;
+
+	EXEC SQL WHENEVER NOT FOUND DO BREAK;
+
+	for (i = 0; i < 2; i++)
+	{
+		count = counts[i];
+
+		EXEC SQL OPEN :curname;
+		EXEC SQL OPEN xcur;
+
+		id = 0;
+		while (1)
+		{
+			for (j = 0; j < count; j++)
+			{
+				id1[j] = -1;
+				id2[j] = -1;
+			}
+
+			EXEC SQL FETCH :count FROM :curname INTO :id1;
+			EXEC SQL FETCH :count FROM xcur INTO :id2;
+
+			rows = sqlca.sqlerrd[2];
+
+			for (j = 0; j < rows; j++)
+			{
+				id++;
+
+				if (id != id1[j] || id != id2[j] || id1[j] != id2[j])
+				{
+					printf("Reading two cursors forward: ERROR. id = %d id1 = %d id2 = %d\n", id, id1[j], id2[j]);
+					break;
+				}
+			}
+			if (j != rows)
+				break;
+		}
+
+		if (sqlca.sqlwarn[0] == 'W') sqlprint();
+		if (sqlca.sqlcode < 0) sqlprint();
+
+		if (id == maxid)
+			printf("Reading readahead and non-readahead cursors simultaneously forward by %d record: SUCCESS\n", count);
+		else
+			printf("Reading readahead and non-readahead cursors simultaneously forward by %d record: FAILED at %d, id1 %d id2 %d\n", count, id, id1[j], id2[j]);
+
+		EXEC SQL CLOSE :curname;
+		EXEC SQL CLOSE xcur;
+
+		EXEC SQL WHENEVER NOT FOUND CONTINUE;
+
+		EXEC SQL OPEN :curname;
+		EXEC SQL OPEN xcur;
+		EXEC SQL FETCH LAST FROM :curname INTO :id1;
+		EXEC SQL FETCH FROM :curname INTO :id1;
+		if (sqlca.sqlcode == ECPG_NOT_FOUND)
+			printf("After last record in cursor '%s' (value %d), fetching backwards.\n", curname, id1[0]);
+		else
+			goto err;
+		EXEC SQL FETCH LAST FROM xcur INTO :id2;
+		EXEC SQL FETCH FROM xcur INTO :id2;
+		if (sqlca.sqlcode == ECPG_NOT_FOUND)
+			printf("After last record in cursor 'xcur' (value %d), fetching backwards.\n", id2[0]);
+		else
+			goto err;
+
+		EXEC SQL WHENEVER NOT FOUND DO BREAK;
+
+		id = maxid;
+		while (1)
+		{
+			for (j = 0; j < count; j++)
+			{
+				id1[j] = -1;
+				id2[j] = -1;
+			}
+
+			EXEC SQL FETCH BACKWARD :count FROM :curname INTO :id1;
+			EXEC SQL FETCH BACKWARD :count FROM xcur INTO :id2;
+
+			rows = sqlca.sqlerrd[2];
+
+			for (j = 0; j < rows; j++)
+			{
+				if (id != id1[j] || id != id2[j] || id1[j] != id2[j])
+				{
+					printf("Reading two cursors backward: ERROR. id = %d id1 = %d id2 = %d\n", id, id1[j], id2[j]);
+					break;
+				}
+				id--;
+			}
+			if (j != rows)
+				break;
+		}
+
+		if (id == 0)
+			printf("Reading readahead and non-readahead cursors simultaneously backwards by %d record(s): SUCCESS\n", count);
+		else
+			printf("Reading readahead and non-readahead cursors simultaneously backwards by %d record(s): FAILED at %d, id1 %d id2 %d\n", count, id, id1[j], id2[j]);
+
+		EXEC SQL CLOSE :curname;
+		EXEC SQL CLOSE xcur;
+
+		EXEC SQL WHENEVER NOT FOUND CONTINUE;
+
+	}
+
+	sqlda = NULL;
+	EXEC SQL OPEN xcur;
+	EXEC SQL FETCH ALL xcur INTO DESCRIPTOR sqlda;
+
+	EXEC SQL WHENEVER NOT FOUND DO BREAK;
+
+	id = 0;
+	id1[0] = -1;
+	while (sqlda)
+	{
+		sqlda_t *tmp;
+
+		id++;
+		id1[0] = *(int *)sqlda->sqlvar[0].sqldata;
+
+		if (id != id1[0])
+		{
+			printf("Reading readahead cursors forward into sqlda chain: ERROR. id = %d id1 = %d\n", id, id1[0]);
+			break;
+		}
+		tmp = sqlda->desc_next;
+		free(sqlda);
+		sqlda = tmp;
+	}
+
+	if (id == maxid)
+		printf("Reading all records from a readahead cursor forward into sqlda chain: SUCCESS\n");
+	else
+		printf("Reading all records from a readahead cursor forward into sqlda chain: FAILED. id %d, id1 %d\n", id, id1[0]);
+
+	EXEC SQL CLOSE xcur;
+
+	EXEC SQL WHENEVER NOT FOUND CONTINUE;
+
+	sqlda = NULL;
+	EXEC SQL OPEN xcur;
+	EXEC SQL FETCH LAST FROM xcur INTO :id1;
+	EXEC SQL FETCH FROM xcur INTO :id1;
+	if (sqlca.sqlcode == ECPG_NOT_FOUND)
+		printf("After last record in cursor 'xcur' (value %d), fetching backwards.\n", id1[0]);
+	else
+		goto err;
+
+	EXEC SQL FETCH BACKWARD ALL xcur INTO DESCRIPTOR sqlda;
+
+	EXEC SQL WHENEVER NOT FOUND DO BREAK;
+
+	id = maxid;
+	id1[0] = -1;
+	while (sqlda)
+	{
+		sqlda_t	*tmp;
+
+		id1[0] = *(int *)sqlda->sqlvar[0].sqldata;
+
+		if (id != id1[0])
+		{
+			printf("Reading readahead cursors backward into sqlda chain: ERROR. id = %d id1 = %d\n", id, id1[0]);
+			break;
+		}
+
+		tmp = sqlda->desc_next;
+		free(sqlda);
+		sqlda = tmp;
+
+		id--;
+	}
+
+	if (id == 0)
+		printf("Reading all records from readahead cursor backwards into sqlda chain: SUCCESS\n");
+	else
+		printf("Reading all records from readahead cursors backwards into sqlda chain: FAILED, id %d, id1 %d\n", id, id1[0]);
+
+	EXEC SQL CLOSE xcur;
+
+err:
+
+	EXEC SQL COMMIT;
+
+	EXEC SQL BEGIN;
+
+	EXEC SQL DROP TABLE ra_test;
+
+	EXEC SQL COMMIT;
+
+	EXEC SQL DISCONNECT ALL;
+
+	return 0;
+}
