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

    ECPG: Allow appending a new result set to descriptors.

diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index 7068df8..85ef44f 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -1518,33 +1518,37 @@ ecpg_execute(struct statement * stmt)
  *	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
+ *	append_result
+ *		    the user variable is an SQL or SQLDA descriptor,
+ *		    may already contain data, append to it.
  *
  * Returns success as boolean. Also an SQL error is raised in case of failure.
  *-------
  */
 bool
-ecpg_process_output(struct statement * stmt, int start, int ntuples, int direction, int var_index, bool clear_result)
+ecpg_process_output(struct statement * stmt, int start, int ntuples, int direction, int var_index, bool clear_result, bool append_result)
 {
 	struct variable *var;
 	bool		status = false;
 	char	   *cmdstat;
 	PGnotify   *notify;
 	struct sqlca_t *sqlca = ECPGget_sqlca();
 	int			nfields,
+				tuples_left,
 				act_field;
 
 	var = stmt->outlist;
 	switch (PQresultStatus(stmt->results))
 	{
 		case PGRES_TUPLES_OK:
 			nfields = PQnfields(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);
@@ -1561,50 +1565,82 @@ ecpg_process_output(struct statement * stmt, int start, int ntuples, int directi
 			}
 
 			if (var != NULL && var->type == ECPGt_descriptor)
 			{
 				struct descriptor *desc = ecpg_find_desc(stmt->lineno, var->pointer);
 
 				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, ntuples, (const char *) var->pointer);
+					if (append_result && desc->result)
+					{
+						int		act_tuple, col,
+								row = PQntuples(desc->result);
+
+						for (tuples_left = ntuples, act_tuple = start; tuples_left; tuples_left--, act_tuple += direction, row++)
+							for (col = 0; col < nfields; col++)
+							{
+								bool	isnull = PQgetisnull(stmt->results, act_tuple, col);
+
+								if (!PQsetvalue(desc->result, row, col,
+										isnull ? NULL : PQgetvalue(stmt->results, act_tuple, col),
+										isnull ? -1 : PQgetlength(stmt->results, act_tuple, col)))
+								{
+									ecpg_raise(stmt->lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+									status = false;
+									break;
+								}
+							}
+					}
+					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, 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;
 					struct sqlda_compat *sqlda_new;
 					int			i;
 
-					/*
-					 * If we are passed in a previously existing sqlda (chain)
-					 * then free it.
-					 */
-					while (sqlda)
+					if (append_result)
+					{
+						sqlda_last = sqlda;
+						while (sqlda_last && sqlda_last->desc_next)
+							sqlda_last = sqlda_last->desc_next;
+					}
+					else
 					{
-						sqlda_new = sqlda->desc_next;
-						free(sqlda);
-						sqlda = sqlda_new;
+						/*
+						 * 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 = NULL;
 					}
-					*_sqlda = sqlda = sqlda_last = sqlda_new = NULL;
 					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)
 						{
@@ -1642,31 +1678,40 @@ ecpg_process_output(struct statement * stmt, int start, int ntuples, int directi
 					}
 				}
 				else
 				{
 					struct sqlda_struct **_sqlda = (struct sqlda_struct **) var->pointer;
 					struct sqlda_struct *sqlda = *_sqlda;
 					struct sqlda_struct *sqlda_last;
 					struct sqlda_struct *sqlda_new;
 					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;
+					}
+					else
+					{
+						/*
+						 * 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 = NULL;
 					}
-					*_sqlda = sqlda = sqlda_last = sqlda_new = NULL;
 					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)
 						{
@@ -2060,21 +2105,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, 0, PQntuples(stmt->results), LOOP_FORWARD, 0, true))
+	if (!ecpg_process_output(stmt, 0, PQntuples(stmt->results), LOOP_FORWARD, 0, true, false))
 		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 e09e351..ff12acb 100644
--- a/src/interfaces/ecpg/ecpglib/extern.h
+++ b/src/interfaces/ecpg/ecpglib/extern.h
@@ -173,21 +173,21 @@ bool ecpg_store_result(const PGresult *results,
 				  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 *, int, int, int, int, bool);
+bool		ecpg_process_output(struct statement *, int, int, int, int, bool, 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)));
