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:
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