Hi,
When array of char * is used as target for the FETCH statement returning
more than one row, it tries to store all the result in the first element.
PFA test_char_select.pgc, which fetches first 3 relnames from pg_class
ordered by relname. The program prints following result

steps to compile and build the program
ecpg -c -I<ecpg_include_dir> test_char_select.pgc
cc -I<pg installation include dir> -g   -c -o test_char_select.o
test_char_select.c
cc -g  test_char_select.o  -L<pg installation lib dir> -lecpg -lpq
-lpgtypes -o test_char_select

output
./test_char_select
relname=___pg_foreign_table_columns
relname=
relname=

The first three relnames should have been
postgres=# select relname from pg_class order by relname limit 3;
          relname
---------------------------
 _pg_foreign_data_wrappers
 _pg_foreign_servers
 _pg_foreign_table_columns

It's obvious that the first element of the array is being overwritten with
an offset of 1.

This happens because, the array of char pointer is dumped as
 /* Fetch multiple columns into one structure. */
{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch 3 from cur1",
ECPGt_EOIT,
ECPGt_char,(strings),(long)0,(long)3,*(1)*sizeof(char)*,
ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);

Since the offset is 1, the next result overwrites the previous result
except for the first byte.

PFA patch ecpg_char_ptr_arr.patch to fix this issue. It has changes as
follows
1. Dump array of char pointer with right offset i.e. sizeof(char *)
2. While reading array of char pointer in ecpg_do_prologue(), use the
address instead of the value at that address
3. The pointer arithmetic should treat such variable as char **, instead of
char *

ECPG regression tests do not show any failures with this patch.
-- 
Best Wishes,
Ashutosh Bapat
EnterpriseDB Corporation
The Postgres Database Company
diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c
index 5f9a3d4..3ec774c 100644
--- a/src/interfaces/ecpg/ecpglib/data.c
+++ b/src/interfaces/ecpg/ecpglib/data.c
@@ -448,20 +448,28 @@ ecpg_get_data(const PGresult *results, int act_tuple, int act_field, int lineno,
 							   ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
 					return (false);
 					break;
 
 				case ECPGt_char:
 				case ECPGt_unsigned_char:
 				case ECPGt_string:
 					{
 						char	   *str = (char *) (var + offset * act_tuple);
 
+						/*
+						 * If varcharsize is unknown and the offset is that of
+						 * char *, then this variable represents the array of
+						 * character pointers. So, use extra indirection.
+						 */
+						if (varcharsize == 0 && offset == sizeof(char *))
+							str = *(char **)str;
+
 						if (varcharsize == 0 || varcharsize > size)
 						{
 							strncpy(str, pval, size + 1);
 							/* do the rtrim() */
 							if (type == ECPGt_string)
 							{
 								char	   *last = str + size;
 
 								while (last > str && (*last == ' ' || *last == '\0'))
 								{
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index a90fb41..aa917b9 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -1923,25 +1923,33 @@ ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
 				return false;
 			}
 
 			var->type = type;
 			var->pointer = va_arg(args, char *);
 
 			var->varcharsize = va_arg(args, long);
 			var->arrsize = va_arg(args, long);
 			var->offset = va_arg(args, long);
 
-			if (var->arrsize == 0 || var->varcharsize == 0)
+			if ((var->arrsize == 0 || var->varcharsize == 0))
 				var->value = *((char **) (var->pointer));
 			else
 				var->value = var->pointer;
 
+			/* 
+			 * If the type is character without known varcharsize but with known array
+			 * size, it is an array of pointers to char, so use the pointer as
+			 * it is.
+			 */
+			if ((var->type == ECPGt_char || type == ECPGt_unsigned_char) &&
+					var->arrsize > 1 && var->varcharsize == 0)
+				var->value = var->pointer;
 			/*
 			 * negative values are used to indicate an array without given
 			 * bounds
 			 */
 			/* reset to zero for us */
 			if (var->arrsize < 0)
 				var->arrsize = 0;
 			if (var->varcharsize < 0)
 				var->varcharsize = 0;
 
diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c
index 308660e..e54f05d5 100644
--- a/src/interfaces/ecpg/preproc/type.c
+++ b/src/interfaces/ecpg/preproc/type.c
@@ -441,36 +441,49 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type,
 				 */
 				if (counter)
 					sprintf(offset, "sizeof(struct varchar_%d)", counter);
 				else
 					sprintf(offset, "sizeof(struct varchar)");
 				break;
 			case ECPGt_char:
 			case ECPGt_unsigned_char:
 			case ECPGt_char_variable:
 			case ECPGt_string:
-
+			{
+				char	*sizeof_name = "char";
 				/*
 				 * we have to use the pointer except for arrays with given
 				 * bounds, ecpglib will distinguish between * and []
 				 */
 				if ((atoi(varcharsize) > 1 ||
 					 (atoi(arrsize) > 0) ||
 				 (atoi(varcharsize) == 0 && strcmp(varcharsize, "0") != 0) ||
 					 (atoi(arrsize) == 0 && strcmp(arrsize, "0") != 0))
 					&& siz == NULL)
+				{
 					sprintf(variable, "(%s%s)", prefix ? prefix : "", name);
+					if ((type == ECPGt_char || type == ECPGt_unsigned_char) &&
+						strcmp(varcharsize, "0") == 0)
+					{
+						/*
+						 * If this is an array of char *, the offset would be
+						 * sizeof(char *) and not sizeof(char).
+						 */
+						sizeof_name = "char *";
+					}
+				}
 				else
 					sprintf(variable, "&(%s%s)", prefix ? prefix : "", name);
 
-				sprintf(offset, "(%s)*sizeof(char)", strcmp(varcharsize, "0") == 0 ? "1" : varcharsize);
+				sprintf(offset, "(%s)*sizeof(%s)", strcmp(varcharsize, "0") == 0 ? "1" : varcharsize, sizeof_name);
 				break;
+			}
 			case ECPGt_numeric:
 
 				/*
 				 * we have to use a pointer here
 				 */
 				sprintf(variable, "&(%s%s)", prefix ? prefix : "", name);
 				sprintf(offset, "sizeof(numeric)");
 				break;
 			case ECPGt_interval:
 

Attachment: test_char_select.pgc
Description: Binary data

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to