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 test_char_select.pgc
cc -I -g -c -o test_char_select.o
test_char_select.c
cc -g test_char_select.o -L -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 && strcm