commit b3ca8f3c651654cda697569358b32593080e846f
Author: Böszörményi Zoltán <zboszor@pr.hu>
Date:   Wed Apr 16 10:06:19 2014 +0200

    ECPG: Allow returning partial results in any order from the result set
    into an arbitrary index if the user variable is an array.

diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c
index b2990ca..5d86c53 100644
--- a/src/interfaces/ecpg/ecpglib/descriptor.c
+++ b/src/interfaces/ecpg/ecpglib/descriptor.c
@@ -474,21 +474,21 @@ ECPGget_desc(int lineno, const char *desc_name, int index,...)
 		/* Make sure we do NOT honor the locale for numeric input */
 		/* since the database gives the standard decimal point */
 		oldlocale = ecpg_strdup(setlocale(LC_NUMERIC, NULL), lineno);
 		setlocale(LC_NUMERIC, "C");
 
 		memset(&stmt, 0, sizeof stmt);
 		stmt.lineno = lineno;
 
 		/* desperate 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), LOOP_FORWARD, index, &stmt, &data_var, 0);
 
 		setlocale(LC_NUMERIC, oldlocale);
 		ecpg_free(oldlocale);
 	}
 	else if (data_var.ind_type != ECPGt_NO_INDICATOR && data_var.ind_pointer != NULL)
 
 		/*
 		 * ind_type != NO_INDICATOR should always have ind_pointer != NULL but
 		 * since this might be changed manually in the .c file let's play it
 		 * safe
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index fa0ae49..7068df8 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -300,26 +300,28 @@ ecpg_is_type_an_array(int type, const struct statement * stmt, const struct vari
 	else
 		return (ECPG_ARRAY_ERROR);
 
 	ecpg_type_infocache_push(&(stmt->connection->cache_head), type, isarray, stmt->lineno);
 	ecpg_log("ecpg_is_type_an_array on line %d: type (%d); C (%d); array (%s)\n", stmt->lineno, type, var->type, ECPG_IS_ARRAY(isarray) ? "yes" : "no");
 	return isarray;
 }
 
 
 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 ntuples, int direction,
+				  int act_field,
+				  const struct statement * stmt,
+				  struct variable * var, int var_index)
 {
 	enum ARRAY_TYPE isarray;
-	int			act_tuple,
-				ntuples = PQntuples(results);
+	int			tuples_left, act_tuple, act_index;
 	bool		status = true;
 
 	if ((isarray = ecpg_is_type_an_array(PQftype(results, act_field), stmt, var)) == ECPG_ARRAY_ERROR)
 	{
 		ecpg_raise(stmt->lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
 		return false;
 	}
 
 	if (isarray == ECPG_ARRAY_NONE)
 	{
@@ -356,51 +358,51 @@ ecpg_store_result(const PGresult *results, int act_field,
 		if (!PQfformat(results, act_field))
 		{
 			switch (var->type)
 			{
 				case ECPGt_char:
 				case ECPGt_unsigned_char:
 				case ECPGt_string:
 					if (!var->varcharsize && !var->arrsize)
 					{
 						/* special mode for handling char**foo=0 */
-						for (act_tuple = 0; act_tuple < ntuples; act_tuple++)
+						for (tuples_left = ntuples, act_tuple = start; tuples_left; tuples_left--, act_tuple += direction)
 							len += strlen(PQgetvalue(results, act_tuple, act_field)) + 1;
 						len *= var->offset;		/* should be 1, but YMNK */
 						len += (ntuples + 1) * sizeof(char *);
 					}
 					else
 					{
 						var->varcharsize = 0;
 						/* check strlen for each tuple */
-						for (act_tuple = 0; act_tuple < ntuples; act_tuple++)
+						for (tuples_left = ntuples, act_tuple = start; tuples_left; tuples_left--, act_tuple += direction)
 						{
 							int			len = strlen(PQgetvalue(results, act_tuple, act_field)) + 1;
 
 							if (len > var->varcharsize)
 								var->varcharsize = len;
 						}
 						var->offset *= var->varcharsize;
 						len = var->offset * ntuples;
 					}
 					break;
 				case ECPGt_varchar:
 					len = ntuples * (var->varcharsize + sizeof(int));
 					break;
 				default:
 					len = var->offset * ntuples;
 					break;
 			}
 		}
 		else
 		{
-			for (act_tuple = 0; act_tuple < ntuples; act_tuple++)
+			for (tuples_left = ntuples, act_tuple = start; tuples_left; tuples_left--, act_tuple += direction)
 				len += PQgetlength(results, act_tuple, act_field);
 		}
 
 		ecpg_log("ecpg_store_result on line %d: allocating memory for %d tuples\n", stmt->lineno, ntuples);
 		var->value = (char *) ecpg_alloc(len, stmt->lineno);
 		if (!var->value)
 			return false;
 		*((char **) var->pointer) = var->value;
 		ecpg_add_mem(var->value, stmt->lineno);
 	}
@@ -422,44 +424,44 @@ ecpg_store_result(const PGresult *results, int act_field,
 		(var->type == ECPGt_char || var->type == ECPGt_unsigned_char || var->type == ECPGt_string))
 	{
 		/* special mode for handling char**foo=0 */
 
 		/* filling the array of (char*)s */
 		char	  **current_string = (char **) var->value;
 
 		/* 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 (tuples_left = ntuples, act_tuple = start, act_index = var_index; tuples_left && status; tuples_left--, act_tuple += direction, act_index++)
 		{
 			int			len = strlen(PQgetvalue(results, act_tuple, act_field)) + 1;
 
-			if (!ecpg_get_data(results, act_tuple, act_field, act_tuple, stmt->lineno,
+			if (!ecpg_get_data(results, act_tuple, act_field, act_index, 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;
 			else
 			{
 				*current_string = current_data_location;
 				current_data_location += len;
 				current_string++;
 			}
 		}
 
 		/* terminate the list */
 		*current_string = NULL;
 	}
 	else
 	{
-		for (act_tuple = 0; act_tuple < ntuples && status; act_tuple++)
+		for (tuples_left = ntuples, act_tuple = start, act_index = var_index; tuples_left && status; tuples_left--, act_tuple += direction, act_index++)
 		{
-			if (!ecpg_get_data(results, act_tuple, act_field, act_tuple, stmt->lineno,
+			if (!ecpg_get_data(results, act_tuple, act_field, act_index, 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;
 		}
 	}
 	return status;
 }
 
 static void
 sprintf_double_value(char *ptr, double value, const char *delim)
@@ -1506,44 +1508,51 @@ ecpg_execute(struct statement * stmt)
  * ecpg_process_output
  *
  *	Process the statement result and store it into application variables.  This
  *	function can be called repeatedly during the same statement in case cursor
  *	readahead is used and the application does FETCH N which overflows the
  *	readahead window.
  *
  * Parameters
  *	stmt	statement structure holding the PGresult and
  *		    the list of output variables
+ *	start
+ *		    start index in PGresult
+ *	ntuples
+ *		    number of tuples to process
+ *	direction
+ *		    forward (+1) or backward (-1)
+ *	var_index
+ *		    start index in the user variable if it's an array
  *	clear_result
  *		    PQclear() the result upon returning from this function
  *
  * Returns success as boolean. Also an SQL error is raised in case of failure.
  *-------
  */
 bool
-ecpg_process_output(struct statement * stmt, bool clear_result)
+ecpg_process_output(struct statement * stmt, int start, int ntuples, int direction, int var_index, bool clear_result)
 {
 	struct variable *var;
 	bool		status = false;
 	char	   *cmdstat;
 	PGnotify   *notify;
 	struct sqlca_t *sqlca = ECPGget_sqlca();
 	int			nfields,
-				ntuples,
 				act_field;
 
 	var = stmt->outlist;
 	switch (PQresultStatus(stmt->results))
 	{
 		case PGRES_TUPLES_OK:
 			nfields = PQnfields(stmt->results);
-			sqlca->sqlerrd[2] = ntuples = PQntuples(stmt->results);
+			sqlca->sqlerrd[2] = ntuples;
 
 			ecpg_log("ecpg_process_output on line %d: correctly got %d tuples with %d fields\n", stmt->lineno, ntuples, nfields);
 			status = true;
 
 			if (ntuples < 1)
 			{
 				if (ntuples)
 					ecpg_log("ecpg_process_output on line %d: incorrect number of matches (%d)\n",
 							 stmt->lineno, ntuples);
 				ecpg_raise(stmt->lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL);
@@ -1557,21 +1566,21 @@ ecpg_process_output(struct statement * stmt, bool clear_result)
 
 				if (desc == NULL)
 					status = false;
 				else
 				{
 					if (desc->result)
 						PQclear(desc->result);
 					desc->result = stmt->results;
 					clear_result = false;
 					ecpg_log("ecpg_process_output on line %d: putting result (%d tuples) into descriptor %s\n",
-							 stmt->lineno, PQntuples(stmt->results), (const char *) var->pointer);
+							 stmt->lineno, ntuples, (const char *) var->pointer);
 				}
 				var = var->next;
 			}
 			else if (var != NULL && var->type == ECPGt_sqlda)
 			{
 				if (INFORMIX_MODE(stmt->compat))
 				{
 					struct sqlda_compat **_sqlda = (struct sqlda_compat **) var->pointer;
 					struct sqlda_compat *sqlda = *_sqlda;
 					struct sqlda_compat *sqlda_last;
@@ -1582,21 +1591,21 @@ ecpg_process_output(struct statement * stmt, bool clear_result)
 					 * If we are passed in a previously existing sqlda (chain)
 					 * then free it.
 					 */
 					while (sqlda)
 					{
 						sqlda_new = sqlda->desc_next;
 						free(sqlda);
 						sqlda = sqlda_new;
 					}
 					*_sqlda = sqlda = sqlda_last = sqlda_new = NULL;
-					for (i = 0; i < ntuples; i++)
+					for (i = start; ntuples; ntuples--, i += direction)
 					{
 						/*
 						 * Build a new sqlda structure. Note that only
 						 * fetching 1 record is supported
 						 */
 						sqlda_new = ecpg_build_compat_sqlda(stmt->lineno, stmt->results, i, stmt->compat);
 
 						if (!sqlda_new)
 						{
 							/* cleanup all SQLDAs we created up */
@@ -1644,21 +1653,21 @@ ecpg_process_output(struct statement * stmt, bool clear_result)
 					 * If we are passed in a previously existing sqlda (chain)
 					 * then free it.
 					 */
 					while (sqlda)
 					{
 						sqlda_new = sqlda->desc_next;
 						free(sqlda);
 						sqlda = sqlda_new;
 					}
 					*_sqlda = sqlda = sqlda_last = sqlda_new = NULL;
-					for (i = 0; i < ntuples; i++)
+					for (i = start; ntuples; ntuples--, i += direction)
 					{
 						/*
 						 * Build a new sqlda structure. Note that only
 						 * fetching 1 record is supported
 						 */
 						sqlda_new = ecpg_build_native_sqlda(stmt->lineno, stmt->results, i, stmt->compat);
 
 						if (!sqlda_new)
 						{
 							/* cleanup all SQLDAs we created up */
@@ -1695,21 +1704,21 @@ ecpg_process_output(struct statement * stmt, bool clear_result)
 					}
 				}
 
 				var = var->next;
 			}
 			else
 				for (act_field = 0; act_field < nfields && status; act_field++)
 				{
 					if (var != NULL)
 					{
-						status = ecpg_store_result(stmt->results, act_field, stmt, var);
+						status = ecpg_store_result(stmt->results, start, ntuples, direction, act_field, stmt, var, var_index);
 						var = var->next;
 					}
 					else if (!INFORMIX_MODE(stmt->compat))
 					{
 						ecpg_raise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS, NULL);
 						return (false);
 					}
 				}
 
 			if (status && var != NULL)
@@ -2051,21 +2060,21 @@ ecpg_do(const int lineno, const int compat, const int force_indicator, const cha
 
 	if (!ecpg_build_params(stmt))
 		goto fail;
 
 	if (!ecpg_autostart_transaction(stmt))
 		goto fail;
 
 	if (!ecpg_execute(stmt))
 		goto fail;
 
-	if (!ecpg_process_output(stmt, true))
+	if (!ecpg_process_output(stmt, 0, PQntuples(stmt->results), LOOP_FORWARD, 0, true))
 		goto fail;
 
 	ecpg_do_epilogue(stmt);
 	return true;
 
 fail:
 	ecpg_do_epilogue(stmt);
 	return false;
 }
 
diff --git a/src/interfaces/ecpg/ecpglib/extern.h b/src/interfaces/ecpg/ecpglib/extern.h
index f91867c..e09e351 100644
--- a/src/interfaces/ecpg/ecpglib/extern.h
+++ b/src/interfaces/ecpg/ecpglib/extern.h
@@ -22,20 +22,23 @@ extern bool ecpg_internal_regression_mode;
 
 #define INFORMIX_MODE(X) ((X) == ECPG_COMPAT_INFORMIX || (X) == ECPG_COMPAT_INFORMIX_SE)
 
 enum ARRAY_TYPE
 {
 	ECPG_ARRAY_ERROR, ECPG_ARRAY_NOT_SET, ECPG_ARRAY_ARRAY, ECPG_ARRAY_VECTOR, ECPG_ARRAY_NONE
 };
 
 #define ECPG_IS_ARRAY(X) ((X) == ECPG_ARRAY_ARRAY || (X) == ECPG_ARRAY_VECTOR)
 
+#define LOOP_FORWARD	(1)
+#define LOOP_BACKWARD	(-1)
+
 /* A generic varchar type. */
 struct ECPGgeneric_varchar
 {
 	int			len;
 	char		arr[1];
 };
 
 /*
  * type information cache
  */
@@ -158,31 +161,33 @@ int			sqlda_dynamic_type(Oid, enum COMPAT_MODE);
 void		ecpg_free_auto_mem(void);
 void		ecpg_clear_auto_mem(void);
 
 struct descriptor *ecpggetdescp(int, char *);
 
 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 ntuples, int direction, 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);
 void		ecpg_free_params(struct statement *stmt, bool print);
 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_autostart_transaction(struct statement * stmt);
 bool		ecpg_execute(struct statement * stmt);
-bool		ecpg_process_output(struct statement *, bool);
+bool		ecpg_process_output(struct statement *, int, int, int, int, bool);
 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);
 void		ecpg_raise_backend(int line, PGresult *result, PGconn *conn, int compat);
 char	   *ecpg_prepared(const char *, struct connection *);
 bool		ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection * conn);
 void		ecpg_log(const char *format,...) __attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
