Обсуждение: More const-marking cleanup

Поиск
Список
Период
Сортировка

More const-marking cleanup

От
Tom Lane
Дата:
I noticed that our two buildfarm animals that are running Fedora
rawhide (caiman, midge) are emitting warnings like

compression.c: In function "parse_compress_options":
compression.c:458:13: warning: assignment discards "const" qualifier from pointer target type [-Wdiscarded-qualifiers]
  458 |         sep = strchr(option, ':');
      |             ^

Apparently, latest gcc is able to notice that constructions like

    const char *str = ...;
    char       *ptr = strchr(str, ':');

are effectively casting away const.  This is a good thing and long
overdue, but we have some work to do to clean up the places where
we are doing that.

Attached is a patch that cleans up all the cases I saw on a local
rawhide installation.  Most of it is unremarkable, but the changes
in ecpg are less so.  In particular, variable.c is a mess, because
somebody const-ified some of the char* arguments to find_struct()
and find_struct_member() without regard for the fact that all their
char* arguments point at the same string.  (Not that you could
discover that from their nonexistent documentation.)  I considered
just reverting that change, but was able to fix things up reasonably
well at the price of a few extra strdup's.  It turned out that
find_struct_member()'s modification of its input string is actually
entirely useless, because it undoes that before consulting the
string again.  find_struct() and find_variable() need to make
copies, though.

            regards, tom lane

diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 257c7da8568..4a69a81b9fb 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -948,7 +948,7 @@ char *
 makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
 {
     char       *buf;
-    char       *rangestr;
+    const char *rangestr;

     /*
      * If the range type name contains "range" then change that to
diff --git a/src/backend/tsearch/spell.c b/src/backend/tsearch/spell.c
index 146801885d7..0dab31f0ddb 100644
--- a/src/backend/tsearch/spell.c
+++ b/src/backend/tsearch/spell.c
@@ -2320,7 +2320,7 @@ CheckCompoundAffixes(CMPDAffix **ptr, const char *word, int len, bool CheckInPla
     }
     else
     {
-        char       *affbegin;
+        const char *affbegin;

         while ((*ptr)->affix)
         {
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index 5bfeda2ffde..c3cb022a400 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -1046,8 +1046,9 @@ typedef struct NUMProc
     char       *number,            /* string with number    */
                *number_p,        /* pointer to current number position */
                *inout,            /* in / out buffer    */
-               *inout_p,        /* pointer to current inout position */
-               *last_relevant,    /* last relevant number after decimal point */
+               *inout_p;        /* pointer to current inout position */
+
+    const char *last_relevant,    /* last relevant number after decimal point */

                *L_negative_sign,    /* Locale */
                *L_positive_sign,
@@ -1118,7 +1119,7 @@ static FormatNode *NUM_cache(int len, NUMDesc *Num, const text *pars_str, bool *
 static char *int_to_roman(int number);
 static int    roman_to_int(NUMProc *Np, size_t input_len);
 static void NUM_prepare_locale(NUMProc *Np);
-static char *get_last_relevant_decnum(const char *num);
+static const char *get_last_relevant_decnum(const char *num);
 static void NUM_numpart_from_char(NUMProc *Np, int id, size_t input_len);
 static void NUM_numpart_to_char(NUMProc *Np, int id);
 static char *NUM_processor(FormatNode *node, NUMDesc *Num, char *inout,
@@ -5297,10 +5298,10 @@ NUM_prepare_locale(NUMProc *Np)
  * If there is no decimal point, return NULL (which will result in same
  * behavior as if FM hadn't been specified).
  */
-static char *
+static const char *
 get_last_relevant_decnum(const char *num)
 {
-    char       *result,
+    const char *result,
                *p = strchr(num, '.');

 #ifdef DEBUG_TO_FROM_CHAR
diff --git a/src/backend/utils/adt/xid8funcs.c b/src/backend/utils/adt/xid8funcs.c
index a211a107767..4b3f7a69b3b 100644
--- a/src/backend/utils/adt/xid8funcs.c
+++ b/src/backend/utils/adt/xid8funcs.c
@@ -194,7 +194,7 @@ is_visible_fxid(FullTransactionId value, const pg_snapshot *snap)
 #ifdef USE_BSEARCH_IF_NXIP_GREATER
     else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
     {
-        void       *res;
+        const void *res;

         res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
                       cmp_fxid);
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index c6d6ba79e44..1c1ccf59f65 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -160,7 +160,7 @@ create_fullpage_directory(char *path)
 static void
 split_path(const char *path, char **dir, char **fname)
 {
-    char       *sep;
+    const char *sep;

     /* split filepath into directory & filename */
     sep = strrchr(path, '/');
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 68774a59efd..00593fab5e1 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -6254,7 +6254,7 @@ findBuiltin(const char *name)
 static int
 parseScriptWeight(const char *option, char **script)
 {
-    char       *sep;
+    const char *sep;
     int            weight;

     if ((sep = strrchr(option, WSEP)))
diff --git a/src/common/compression.c b/src/common/compression.c
index 4c3c9fd7b50..eb434542fbd 100644
--- a/src/common/compression.c
+++ b/src/common/compression.c
@@ -425,7 +425,7 @@ validate_compress_specification(pg_compress_specification *spec)
 void
 parse_compress_options(const char *option, char **algorithm, char **detail)
 {
-    char       *sep;
+    const char *sep;
     char       *endp;
     long        result;

diff --git a/src/interfaces/ecpg/pgtypeslib/datetime.c b/src/interfaces/ecpg/pgtypeslib/datetime.c
index 1b253747fc4..f43343b4594 100644
--- a/src/interfaces/ecpg/pgtypeslib/datetime.c
+++ b/src/interfaces/ecpg/pgtypeslib/datetime.c
@@ -335,8 +335,8 @@ PGTYPESdate_defmt_asc(date * d, const char *fmt, const char *str)
      */
     int            token[3][2];
     int            token_values[3] = {-1, -1, -1};
-    char       *fmt_token_order;
-    char       *fmt_ystart,
+    const char *fmt_token_order;
+    const char *fmt_ystart,
                *fmt_mstart,
                *fmt_dstart;
     unsigned int i;
diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer
index 6f94b832a03..be94ca5bbfa 100644
--- a/src/interfaces/ecpg/preproc/ecpg.trailer
+++ b/src/interfaces/ecpg/preproc/ecpg.trailer
@@ -1975,12 +1975,14 @@ civarind: cvariable indicator

 char_civar: char_variable
     {
-        char       *ptr = strstr(@1, ".arr");
+        char       *var = mm_strdup(@1);
+        char       *ptr = strstr(var, ".arr");

         if (ptr)                /* varchar, we need the struct name here, not
                                  * the struct element */
             *ptr = '\0';
-        add_variable_to_head(&argsinsert, find_variable(@1), &no_indicator);
+        add_variable_to_head(&argsinsert, find_variable(var), &no_indicator);
+        @$ = var;
     }
     ;

diff --git a/src/interfaces/ecpg/preproc/variable.c b/src/interfaces/ecpg/preproc/variable.c
index 2c67e33e92e..350e780c000 100644
--- a/src/interfaces/ecpg/preproc/variable.c
+++ b/src/interfaces/ecpg/preproc/variable.c
@@ -6,6 +6,19 @@

 static struct variable *allvariables = NULL;

+/* this probably belongs in util.c, but for now it's only needed here */
+static char *
+mm_nstrdup(const char *in, size_t len)
+{
+    char       *out;
+
+    out = mm_alloc(len + 1);
+    memcpy(out, in, len);
+    out[len] = '\0';
+
+    return out;
+}
+
 struct variable *
 new_variable(const char *name, struct ECPGtype *type, int brace_level)
 {
@@ -22,17 +35,11 @@ new_variable(const char *name, struct ECPGtype *type, int brace_level)
 }

 static struct variable *
-find_struct_member(const char *name, char *str, struct ECPGstruct_member *members, int brace_level)
+find_struct_member(const char *name, const char *str,
+                   struct ECPGstruct_member *members, int brace_level)
 {
-    char       *next = strpbrk(++str, ".-["),
-               *end,
-                c = '\0';
-
-    if (next != NULL)
-    {
-        c = *next;
-        *next = '\0';
-    }
+    const char *next = strpbrk(++str, ".-["),
+               *end;

     for (; members; members = members->next)
     {
@@ -54,8 +61,7 @@ find_struct_member(const char *name, char *str, struct ECPGstruct_member *member
             }
             else
             {
-                *next = c;
-                if (c == '[')
+                if (*next == '[')
                 {
                     int            count;

@@ -123,25 +129,24 @@ find_struct_member(const char *name, char *str, struct ECPGstruct_member *member
 }

 static struct variable *
-find_struct(const char *name, char *next, char *end)
+find_struct(const char *name, const char *next, const char *end)
 {
+    char       *prefix;
     struct variable *p;
-    char        c = *next;

     /* first get the mother structure entry */
-    *next = '\0';
-    p = find_variable(name);
+    prefix = mm_nstrdup(name, next - name);
+    p = find_variable(prefix);

-    if (c == '-')
+    if (*next == '-')
     {
         if (p->type->type != ECPGt_array)
-            mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer", name);
+            mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer", prefix);

         if (p->type->u.element->type != ECPGt_struct && p->type->u.element->type != ECPGt_union)
-            mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer to a structure or a union", name);
+            mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer to a structure or a union", prefix);

-        /* restore the name, we will need it later */
-        *next = c;
+        free(prefix);

         return find_struct_member(name, ++end, p->type->u.element->u.members, p->brace_level);
     }
@@ -150,23 +155,21 @@ find_struct(const char *name, char *next, char *end)
         if (next == end)
         {
             if (p->type->type != ECPGt_struct && p->type->type != ECPGt_union)
-                mmfatal(PARSE_ERROR, "variable \"%s\" is neither a structure nor a union", name);
+                mmfatal(PARSE_ERROR, "variable \"%s\" is neither a structure nor a union", prefix);

-            /* restore the name, we will need it later */
-            *next = c;
+            free(prefix);

             return find_struct_member(name, end, p->type->u.members, p->brace_level);
         }
         else
         {
             if (p->type->type != ECPGt_array)
-                mmfatal(PARSE_ERROR, "variable \"%s\" is not an array", name);
+                mmfatal(PARSE_ERROR, "variable \"%s\" is not an array", prefix);

             if (p->type->u.element->type != ECPGt_struct && p->type->u.element->type != ECPGt_union)
-                mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer to a structure or a union", name);
+                mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer to a structure or a union", prefix);

-            /* restore the name, we will need it later */
-            *next = c;
+            free(prefix);

             return find_struct_member(name, end, p->type->u.element->u.members, p->brace_level);
         }
@@ -192,7 +195,7 @@ find_simple(const char *name)
 struct variable *
 find_variable(const char *name)
 {
-    char       *next,
+    const char *next,
                *end;
     struct variable *p;
     int            count;
@@ -227,15 +230,14 @@ find_variable(const char *name)
                 p = find_struct(name, next, end);
             else
             {
-                char        c = *next;
+                char       *prefix = mm_nstrdup(name, next - name);

-                *next = '\0';
-                p = find_simple(name);
+                p = find_simple(prefix);
                 if (p == NULL)
-                    mmfatal(PARSE_ERROR, "variable \"%s\" is not declared", name);
+                    mmfatal(PARSE_ERROR, "variable \"%s\" is not declared", prefix);
                 if (p->type->type != ECPGt_array)
-                    mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer", name);
-                *next = c;
+                    mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer", prefix);
+                free(prefix);
                 switch (p->type->u.element->type)
                 {
                     case ECPGt_array:
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index efc41fca2ba..58d41207605 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -693,7 +693,7 @@ static
 const char *
 get_expectfile(const char *testname, const char *file)
 {
-    char       *file_type;
+    const char *file_type;
     _resultmap *rm;

     /*
diff --git a/src/timezone/zic.c b/src/timezone/zic.c
index a8c1de9910d..8dcc7b337a7 100644
--- a/src/timezone/zic.c
+++ b/src/timezone/zic.c
@@ -2620,7 +2620,7 @@ doabbr(char *abbr, struct zone const *zp, char const *letters,
        bool isdst, zic_t save, bool doquotes)
 {
     char       *cp;
-    char       *slashp;
+    char const *slashp;
     size_t        len;
     char const *format = zp->z_format;


Re: More const-marking cleanup

От
Thomas Munro
Дата:
On Fri, Dec 5, 2025 at 11:09 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Apparently, latest gcc is able to notice that constructions like
>
>         const char *str = ...;
>         char       *ptr = strchr(str, ':');
>
> are effectively casting away const.  This is a good thing and long
> overdue, but we have some work to do to clean up the places where
> we are doing that.

Yeah, one of the qualifier-preserving generic functions that C23
invented: bsearch, memchr, strchr, strpbrk, strrchr, strstr, wcschr,
wcspbrk, wcsrchr, wmemchr, and wcsstr.  The synopses use QVoid or
QChar to mean "same qualifier", a bit like C++ function templates.  We
could probably benefit from some of that in our own code node, list,
tree etc code, as it only requires C11 _Generic to implement.

https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf



Re: More const-marking cleanup

От
Bertrand Drouvot
Дата:
Hi,

On Thu, Dec 04, 2025 at 05:09:30PM -0500, Tom Lane wrote:
> Attached is a patch that cleans up all the cases I saw on a local
> rawhide installation. 

What about adding the 4 in the attached?

Regards,

-- 
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com

Вложения

Re: More const-marking cleanup

От
Tom Lane
Дата:
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> writes:
> What about adding the 4 in the attached?

Cool, how'd you find these?

            regards, tom lane



Re: More const-marking cleanup

От
Bertrand Drouvot
Дата:
Hi,

On Fri, Dec 05, 2025 at 09:47:56AM -0500, Tom Lane wrote:
> Bertrand Drouvot <bertranddrouvot.pg@gmail.com> writes:
> > What about adding the 4 in the attached?
> 
> Cool, how'd you find these?

With the help of a coccinelle script. Will polish and share.

Regards,

-- 
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com



Re: More const-marking cleanup

От
Tom Lane
Дата:
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> writes:
> On Fri, Dec 05, 2025 at 09:47:56AM -0500, Tom Lane wrote:
>> Cool, how'd you find these?

> With the help of a coccinelle script. Will polish and share.

Ah.  Your script didn't notice the need for const'ification of
additional variables in map_locale() :-(.  I fixed that and
pushed everything except the ecpg/preproc/variable.c changes,
which I'm not too comfortable about yet.  (The code coverage
report shows that large chunks of those functions are untested,
so I wonder if they worked before let alone after.)

            regards, tom lane



Re: More const-marking cleanup

От
Tom Lane
Дата:
I wrote:
> Ah.  Your script didn't notice the need for const'ification of
> additional variables in map_locale() :-(.  I fixed that and
> pushed everything except the ecpg/preproc/variable.c changes,
> which I'm not too comfortable about yet.  (The code coverage
> report shows that large chunks of those functions are untested,
> so I wonder if they worked before let alone after.)

I was right to be suspicious about variable.c: I'd misunderstood
what was happening in find_struct_member(), with the consequence
that I broke some cases that were not being tested.  Here's v2,
with that repaired, more commentary, and more test cases.

Adding these comments feels a bit like putting lipstick on a pig.
find_variable and its subroutines are an inelegant, duplicative
mess that fails to handle cases it easily could handle if it were
rewritten.  But I've put enough brain cells into this already,
and also it appears that there are restrictions elsewhere in ecpg
that'd have to be lifted before it'd make a difference.

            regards, tom lane

diff --git a/src/interfaces/ecpg/preproc/variable.c b/src/interfaces/ecpg/preproc/variable.c
index 2c67e33e92e..ad5201a222f 100644
--- a/src/interfaces/ecpg/preproc/variable.c
+++ b/src/interfaces/ecpg/preproc/variable.c
@@ -6,6 +6,19 @@

 static struct variable *allvariables = NULL;

+/* this probably belongs in util.c, but for now it's only needed here */
+static char *
+loc_nstrdup(const char *in, size_t len)
+{
+    char       *out;
+
+    out = loc_alloc(len + 1);
+    memcpy(out, in, len);
+    out[len] = '\0';
+
+    return out;
+}
+
 struct variable *
 new_variable(const char *name, struct ECPGtype *type, int brace_level)
 {
@@ -21,22 +34,34 @@ new_variable(const char *name, struct ECPGtype *type, int brace_level)
     return p;
 }

+/*
+ * Perform lookup of a field within a struct
+ *
+ * 'name' is the entire C variable name
+ * 'str' points just before the next field name to parse
+ * 'members' and 'brace_level' describe the struct we are looking into
+ *
+ * Returns NULL if field is not found.
+ *
+ * This recurses if needed to handle sub-fields.
+ */
 static struct variable *
-find_struct_member(const char *name, char *str, struct ECPGstruct_member *members, int brace_level)
+find_struct_member(const char *name, const char *str,
+                   struct ECPGstruct_member *members, int brace_level)
 {
-    char       *next = strpbrk(++str, ".-["),
+    /* ++ here skips over the '.', or the '>' of '->' */
+    const char *next = strpbrk(++str, ".-["),
                *end,
-                c = '\0';
+               *field;

     if (next != NULL)
-    {
-        c = *next;
-        *next = '\0';
-    }
+        field = loc_nstrdup(str, next - str);
+    else
+        field = str;

     for (; members; members = members->next)
     {
-        if (strcmp(members->name, str) == 0)
+        if (strcmp(members->name, field) == 0)
         {
             if (next == NULL)
             {
@@ -54,14 +79,13 @@ find_struct_member(const char *name, char *str, struct ECPGstruct_member *member
             }
             else
             {
-                *next = c;
-                if (c == '[')
+                if (*next == '[')
                 {
                     int            count;

                     /*
                      * We don't care about what's inside the array brackets so
-                     * just eat up the character
+                     * just scan to find the matching right bracket.
                      */
                     for (count = 1, end = next + 1; count; end++)
                     {
@@ -122,26 +146,35 @@ find_struct_member(const char *name, char *str, struct ECPGstruct_member *member
     return NULL;
 }

+/*
+ * Do struct lookup when we have found var.field, var->field, or var[n].field
+ *
+ * 'name' is the entire C variable name
+ * 'next' points at the character after the base name
+ * 'end' points at the character after the subscript, if there was a
+ * subscript, else it's the same as 'next'.
+ *
+ * This is used only at the first level of field reference; sub-fields will
+ * be handled by internal recursion in find_struct_member.
+ */
 static struct variable *
-find_struct(const char *name, char *next, char *end)
+find_struct(const char *name, const char *next, const char *end)
 {
+    char       *prefix;
     struct variable *p;
-    char        c = *next;

     /* first get the mother structure entry */
-    *next = '\0';
-    p = find_variable(name);
+    prefix = loc_nstrdup(name, next - name);
+    p = find_variable(prefix);

-    if (c == '-')
+    if (*next == '-')
     {
+        /* We have var->field */
         if (p->type->type != ECPGt_array)
-            mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer", name);
+            mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer", prefix);

         if (p->type->u.element->type != ECPGt_struct && p->type->u.element->type != ECPGt_union)
-            mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer to a structure or a union", name);
-
-        /* restore the name, we will need it later */
-        *next = c;
+            mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer to a structure or a union", prefix);

         return find_struct_member(name, ++end, p->type->u.element->u.members, p->brace_level);
     }
@@ -149,30 +182,27 @@ find_struct(const char *name, char *next, char *end)
     {
         if (next == end)
         {
+            /* We have var.field */
             if (p->type->type != ECPGt_struct && p->type->type != ECPGt_union)
-                mmfatal(PARSE_ERROR, "variable \"%s\" is neither a structure nor a union", name);
-
-            /* restore the name, we will need it later */
-            *next = c;
+                mmfatal(PARSE_ERROR, "variable \"%s\" is neither a structure nor a union", prefix);

             return find_struct_member(name, end, p->type->u.members, p->brace_level);
         }
         else
         {
+            /* We have var[n].field */
             if (p->type->type != ECPGt_array)
-                mmfatal(PARSE_ERROR, "variable \"%s\" is not an array", name);
+                mmfatal(PARSE_ERROR, "variable \"%s\" is not an array", prefix);

             if (p->type->u.element->type != ECPGt_struct && p->type->u.element->type != ECPGt_union)
-                mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer to a structure or a union", name);
-
-            /* restore the name, we will need it later */
-            *next = c;
+                mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer to a structure or a union", prefix);

             return find_struct_member(name, end, p->type->u.element->u.members, p->brace_level);
         }
     }
 }

+/* Look up a variable given its base name */
 static struct variable *
 find_simple(const char *name)
 {
@@ -187,12 +217,24 @@ find_simple(const char *name)
     return NULL;
 }

-/* Note that this function will end the program in case of an unknown */
-/* variable */
+/*
+ * Build a "struct variable" for a C variable reference.
+ *
+ * The given "name" string is a CVARIABLE per pgc.l, so it can include not
+ * only a base variable name but also ".field", "->field", or "[subscript]"
+ * decoration.  We don't need to understand that fully, because we always
+ * duplicate the whole string into the name field of the result variable
+ * and emit it literally to the output file; the C compiler will make sense
+ * of it later.  What we do need to do here is identify the type of the
+ * target field or array element so that we can attach a correct ECPGtype
+ * struct to the result.
+ *
+ * Note that this function will end the program in case of an unknown variable.
+ */
 struct variable *
 find_variable(const char *name)
 {
-    char       *next,
+    const char *next,
                *end;
     struct variable *p;
     int            count;
@@ -200,11 +242,12 @@ find_variable(const char *name)
     next = strpbrk(name, ".[-");
     if (next)
     {
+        /* Deal with field/subscript decoration */
         if (*next == '[')
         {
             /*
              * We don't care about what's inside the array brackets so just
-             * eat up the characters
+             * scan to find the matching right bracket.
              */
             for (count = 1, end = next + 1; count; end++)
             {
@@ -224,18 +267,25 @@ find_variable(const char *name)
                 }
             }
             if (*end == '.')
+            {
+                /* We have var[n].field */
                 p = find_struct(name, next, end);
+            }
             else
             {
-                char        c = *next;
+                /*
+                 * Note: this part assumes we must just have var[n] without
+                 * any further decoration, which fails to handle cases such as
+                 * var[n]->field.  For now, that's okay because
+                 * pointer-to-pointer variables are rejected elsewhere.
+                 */
+                char       *prefix = loc_nstrdup(name, next - name);

-                *next = '\0';
-                p = find_simple(name);
+                p = find_simple(prefix);
                 if (p == NULL)
-                    mmfatal(PARSE_ERROR, "variable \"%s\" is not declared", name);
+                    mmfatal(PARSE_ERROR, "variable \"%s\" is not declared", prefix);
                 if (p->type->type != ECPGt_array)
-                    mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer", name);
-                *next = c;
+                    mmfatal(PARSE_ERROR, "variable \"%s\" is not a pointer", prefix);
                 switch (p->type->u.element->type)
                 {
                     case ECPGt_array:
@@ -249,7 +299,10 @@ find_variable(const char *name)
             }
         }
         else
+        {
+            /* Must be var.field or var->field */
             p = find_struct(name, next, next);
+        }
     }
     else
         p = find_simple(name);
diff --git a/src/interfaces/ecpg/test/expected/preproc-pointer_to_struct.c
b/src/interfaces/ecpg/test/expected/preproc-pointer_to_struct.c
index 7b1f58e835f..5d72990a847 100644
--- a/src/interfaces/ecpg/test/expected/preproc-pointer_to_struct.c
+++ b/src/interfaces/ecpg/test/expected/preproc-pointer_to_struct.c
@@ -51,6 +51,13 @@ typedef  struct ind {
 #line 22 "pointer_to_struct.pgc"


+typedef  struct {
+#line 27 "pointer_to_struct.pgc"
+ customer customers [ 10 ] ;
+ } company ;
+#line 28 "pointer_to_struct.pgc"
+
+
 int main()
 {
     /* exec sql begin declare section */
@@ -61,14 +68,14 @@ int main()


        typedef struct {
-#line 31 "pointer_to_struct.pgc"
+#line 37 "pointer_to_struct.pgc"
   struct varchar_2  { int len; char arr[ 50 ]; }  name ;

-#line 32 "pointer_to_struct.pgc"
+#line 38 "pointer_to_struct.pgc"
  int phone ;
  }  customer2 ;

-#line 33 "pointer_to_struct.pgc"
+#line 39 "pointer_to_struct.pgc"



@@ -85,105 +92,132 @@ int main()



+
+


-#line 27 "pointer_to_struct.pgc"
+#line 33 "pointer_to_struct.pgc"
  customer * custs1 = ( customer * ) malloc ( sizeof ( customer ) * 10 ) ;

-#line 28 "pointer_to_struct.pgc"
+#line 34 "pointer_to_struct.pgc"
  cust_ind * inds = ( cust_ind * ) malloc ( sizeof ( cust_ind ) * 10 ) ;

-#line 34 "pointer_to_struct.pgc"
+#line 40 "pointer_to_struct.pgc"
  customer2 * custs2 = ( customer2 * ) malloc ( sizeof ( customer2 ) * 10 ) ;

-#line 40 "pointer_to_struct.pgc"
+#line 46 "pointer_to_struct.pgc"
  struct customer3 {
-#line 38 "pointer_to_struct.pgc"
+#line 44 "pointer_to_struct.pgc"
  char name [ 50 ] ;

-#line 39 "pointer_to_struct.pgc"
+#line 45 "pointer_to_struct.pgc"
  int phone ;
  } * custs3 = ( struct customer3 * ) malloc ( sizeof ( struct customer3 ) * 10 ) ;

-#line 46 "pointer_to_struct.pgc"
+#line 52 "pointer_to_struct.pgc"
  struct customer4 {
-#line 44 "pointer_to_struct.pgc"
+#line 50 "pointer_to_struct.pgc"
   struct varchar_3  { int len; char arr[ 50 ]; }  name ;

-#line 45 "pointer_to_struct.pgc"
+#line 51 "pointer_to_struct.pgc"
  int phone ;
  } * custs4 = ( struct customer4 * ) malloc ( sizeof ( struct customer4 ) ) ;

-#line 48 "pointer_to_struct.pgc"
+#line 54 "pointer_to_struct.pgc"
+ company acme ;
+
+#line 56 "pointer_to_struct.pgc"
  int r ;

-#line 49 "pointer_to_struct.pgc"
+#line 57 "pointer_to_struct.pgc"
   struct varchar_4  { int len; char arr[ 50 ]; }  onlyname [ 2 ] ;
 /* exec sql end declare section */
-#line 50 "pointer_to_struct.pgc"
+#line 58 "pointer_to_struct.pgc"


     ECPGdebug(1, stderr);

     { ECPGconnect(__LINE__, 0, "ecpg1_regression" , NULL, NULL , NULL, 0);
-#line 54 "pointer_to_struct.pgc"
+#line 62 "pointer_to_struct.pgc"

 if (sqlca.sqlwarn[0] == 'W') sqlprint();
-#line 54 "pointer_to_struct.pgc"
+#line 62 "pointer_to_struct.pgc"

 if (sqlca.sqlcode < 0) sqlprint();}
-#line 54 "pointer_to_struct.pgc"
+#line 62 "pointer_to_struct.pgc"


     { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "create table customers ( c varchar ( 50 ) , p int )",
ECPGt_EOIT,ECPGt_EORT); 
-#line 56 "pointer_to_struct.pgc"
+#line 64 "pointer_to_struct.pgc"

 if (sqlca.sqlwarn[0] == 'W') sqlprint();
-#line 56 "pointer_to_struct.pgc"
+#line 64 "pointer_to_struct.pgc"

 if (sqlca.sqlcode < 0) sqlprint();}
-#line 56 "pointer_to_struct.pgc"
+#line 64 "pointer_to_struct.pgc"

-    { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into customers values ( 'John Doe' , '12345' )",
ECPGt_EOIT,ECPGt_EORT); 
-#line 57 "pointer_to_struct.pgc"
+
+    /* First we'll insert some data using C variable references */
+    strcpy(custs1[0].name.arr, "John Doe");
+    custs1[0].name.len = strlen(custs1[0].name.arr);
+    custs1[0].phone = 12345;
+
+    strcpy(acme.customers[1].name.arr, "Jane Doe");
+    acme.customers[1].name.len = strlen(acme.customers[1].name.arr);
+    acme.customers[1].phone = 67890;
+
+    { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into customers values ( $1  , $2  )",
+    ECPGt_varchar,&(custs1->name),(long)50,(long)1,sizeof(struct varchar_1),
+    ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+    ECPGt_int,&(custs1[0].phone),(long)1,(long)1,sizeof(int),
+    ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 76 "pointer_to_struct.pgc"

 if (sqlca.sqlcode == ECPG_NOT_FOUND) sqlprint();
-#line 57 "pointer_to_struct.pgc"
+#line 76 "pointer_to_struct.pgc"

 if (sqlca.sqlwarn[0] == 'W') sqlprint();
-#line 57 "pointer_to_struct.pgc"
+#line 76 "pointer_to_struct.pgc"

 if (sqlca.sqlcode < 0) sqlprint();}
-#line 57 "pointer_to_struct.pgc"
+#line 76 "pointer_to_struct.pgc"

-    { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into customers values ( 'Jane Doe' , '67890' )",
ECPGt_EOIT,ECPGt_EORT); 
-#line 58 "pointer_to_struct.pgc"
+    { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into customers values ( $1  , $2  )",
+    ECPGt_varchar,&(acme.customers[1].name),(long)50,(long)1,sizeof(struct varchar_1),
+    ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L,
+    ECPGt_int,&(acme.customers[1].phone),(long)1,(long)1,sizeof(int),
+    ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
+#line 78 "pointer_to_struct.pgc"

 if (sqlca.sqlcode == ECPG_NOT_FOUND) sqlprint();
-#line 58 "pointer_to_struct.pgc"
+#line 78 "pointer_to_struct.pgc"

 if (sqlca.sqlwarn[0] == 'W') sqlprint();
-#line 58 "pointer_to_struct.pgc"
+#line 78 "pointer_to_struct.pgc"

 if (sqlca.sqlcode < 0) sqlprint();}
-#line 58 "pointer_to_struct.pgc"
+#line 78 "pointer_to_struct.pgc"
+

+    /* Clear the array, to be sure reading back into it actually gets data */
+    memset(custs1, 0, sizeof(customer) * 10);

+    /* Now read back the data */
     { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select * from customers limit 2", ECPGt_EOIT,
     ECPGt_varchar,&(custs1->name),(long)50,(long)-1,sizeof( customer ),
     ECPGt_short,&(inds->name_ind),(long)1,(long)-1,sizeof( struct ind ),
     ECPGt_int,&(custs1->phone),(long)1,(long)-1,sizeof( customer ),
     ECPGt_short,&(inds->phone_ind),(long)1,(long)-1,sizeof( struct ind ), ECPGt_EORT);
-#line 60 "pointer_to_struct.pgc"
+#line 84 "pointer_to_struct.pgc"

 if (sqlca.sqlcode == ECPG_NOT_FOUND) sqlprint();
-#line 60 "pointer_to_struct.pgc"
+#line 84 "pointer_to_struct.pgc"

 if (sqlca.sqlwarn[0] == 'W') sqlprint();
-#line 60 "pointer_to_struct.pgc"
+#line 84 "pointer_to_struct.pgc"

 if (sqlca.sqlcode < 0) sqlprint();}
-#line 60 "pointer_to_struct.pgc"
+#line 84 "pointer_to_struct.pgc"

     printf("custs1:\n");
     for (r = 0; r < 2; r++)
@@ -197,16 +231,16 @@ if (sqlca.sqlcode < 0) sqlprint();}
     ECPGt_short,&(inds->name_ind),(long)1,(long)-1,sizeof( struct ind ),
     ECPGt_int,&(custs2->phone),(long)1,(long)-1,sizeof( customer2 ),
     ECPGt_short,&(inds->phone_ind),(long)1,(long)-1,sizeof( struct ind ), ECPGt_EORT);
-#line 68 "pointer_to_struct.pgc"
+#line 92 "pointer_to_struct.pgc"

 if (sqlca.sqlcode == ECPG_NOT_FOUND) sqlprint();
-#line 68 "pointer_to_struct.pgc"
+#line 92 "pointer_to_struct.pgc"

 if (sqlca.sqlwarn[0] == 'W') sqlprint();
-#line 68 "pointer_to_struct.pgc"
+#line 92 "pointer_to_struct.pgc"

 if (sqlca.sqlcode < 0) sqlprint();}
-#line 68 "pointer_to_struct.pgc"
+#line 92 "pointer_to_struct.pgc"

     printf("\ncusts2:\n");
     for (r = 0; r < 2; r++)
@@ -220,16 +254,16 @@ if (sqlca.sqlcode < 0) sqlprint();}
     ECPGt_short,&(inds->name_ind),(long)1,(long)-1,sizeof( struct ind ),
     ECPGt_int,&(custs3->phone),(long)1,(long)-1,sizeof( struct customer3 ),
     ECPGt_short,&(inds->phone_ind),(long)1,(long)-1,sizeof( struct ind ), ECPGt_EORT);
-#line 76 "pointer_to_struct.pgc"
+#line 100 "pointer_to_struct.pgc"

 if (sqlca.sqlcode == ECPG_NOT_FOUND) sqlprint();
-#line 76 "pointer_to_struct.pgc"
+#line 100 "pointer_to_struct.pgc"

 if (sqlca.sqlwarn[0] == 'W') sqlprint();
-#line 76 "pointer_to_struct.pgc"
+#line 100 "pointer_to_struct.pgc"

 if (sqlca.sqlcode < 0) sqlprint();}
-#line 76 "pointer_to_struct.pgc"
+#line 100 "pointer_to_struct.pgc"

     printf("\ncusts3:\n");
     for (r = 0; r < 2; r++)
@@ -243,16 +277,16 @@ if (sqlca.sqlcode < 0) sqlprint();}
     ECPGt_short,&(inds->name_ind),(long)1,(long)-1,sizeof( struct ind ),
     ECPGt_int,&(custs4->phone),(long)1,(long)-1,sizeof( struct customer4 ),
     ECPGt_short,&(inds->phone_ind),(long)1,(long)-1,sizeof( struct ind ), ECPGt_EORT);
-#line 84 "pointer_to_struct.pgc"
+#line 108 "pointer_to_struct.pgc"

 if (sqlca.sqlcode == ECPG_NOT_FOUND) sqlprint();
-#line 84 "pointer_to_struct.pgc"
+#line 108 "pointer_to_struct.pgc"

 if (sqlca.sqlwarn[0] == 'W') sqlprint();
-#line 84 "pointer_to_struct.pgc"
+#line 108 "pointer_to_struct.pgc"

 if (sqlca.sqlcode < 0) sqlprint();}
-#line 84 "pointer_to_struct.pgc"
+#line 108 "pointer_to_struct.pgc"

     printf("\ncusts4:\n");
     printf( "name  - %s\n", custs4->name.arr );
@@ -261,16 +295,16 @@ if (sqlca.sqlcode < 0) sqlprint();}
     { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select c from customers limit 2", ECPGt_EOIT,
     ECPGt_varchar,(onlyname),(long)50,(long)2,sizeof(struct varchar_4),
     ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);
-#line 89 "pointer_to_struct.pgc"
+#line 113 "pointer_to_struct.pgc"

 if (sqlca.sqlcode == ECPG_NOT_FOUND) sqlprint();
-#line 89 "pointer_to_struct.pgc"
+#line 113 "pointer_to_struct.pgc"

 if (sqlca.sqlwarn[0] == 'W') sqlprint();
-#line 89 "pointer_to_struct.pgc"
+#line 113 "pointer_to_struct.pgc"

 if (sqlca.sqlcode < 0) sqlprint();}
-#line 89 "pointer_to_struct.pgc"
+#line 113 "pointer_to_struct.pgc"

     printf("\nname:\n");
     for (r = 0; r < 2; r++)
@@ -279,13 +313,13 @@ if (sqlca.sqlcode < 0) sqlprint();}
     }

     { ECPGdisconnect(__LINE__, "ALL");
-#line 96 "pointer_to_struct.pgc"
+#line 120 "pointer_to_struct.pgc"

 if (sqlca.sqlwarn[0] == 'W') sqlprint();
-#line 96 "pointer_to_struct.pgc"
+#line 120 "pointer_to_struct.pgc"

 if (sqlca.sqlcode < 0) sqlprint();}
-#line 96 "pointer_to_struct.pgc"
+#line 120 "pointer_to_struct.pgc"


     /* All the memory will anyway be freed at the end */
diff --git a/src/interfaces/ecpg/test/expected/preproc-pointer_to_struct.stderr
b/src/interfaces/ecpg/test/expected/preproc-pointer_to_struct.stderr
index 707640860b8..a2248eab81b 100644
--- a/src/interfaces/ecpg/test/expected/preproc-pointer_to_struct.stderr
+++ b/src/interfaces/ecpg/test/expected/preproc-pointer_to_struct.stderr
@@ -2,85 +2,93 @@
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ECPGconnect: opening database ecpg1_regression on <DEFAULT> port <DEFAULT>
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 56: query: create table customers ( c varchar ( 50 ) , p int ); with 0 parameter(s) on
connectionecpg1_regression 
+[NO_PID]: ecpg_execute on line 64: query: create table customers ( c varchar ( 50 ) , p int ); with 0 parameter(s) on
connectionecpg1_regression 
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 56: using PQexec
+[NO_PID]: ecpg_execute on line 64: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 56: OK: CREATE TABLE
+[NO_PID]: ecpg_process_output on line 64: OK: CREATE TABLE
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 57: query: insert into customers values ( 'John Doe' , '12345' ); with 0 parameter(s)
onconnection ecpg1_regression 
+[NO_PID]: ecpg_execute on line 75: query: insert into customers values ( $1  , $2  ); with 2 parameter(s) on
connectionecpg1_regression 
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 57: using PQexec
+[NO_PID]: ecpg_execute on line 75: using PQexecParams
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 57: OK: INSERT 0 1
+[NO_PID]: ecpg_free_params on line 75: parameter 1 = John Doe
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 58: query: insert into customers values ( 'Jane Doe' , '67890' ); with 0 parameter(s)
onconnection ecpg1_regression 
+[NO_PID]: ecpg_free_params on line 75: parameter 2 = 12345
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 58: using PQexec
+[NO_PID]: ecpg_process_output on line 75: OK: INSERT 0 1
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 58: OK: INSERT 0 1
+[NO_PID]: ecpg_execute on line 77: query: insert into customers values ( $1  , $2  ); with 2 parameter(s) on
connectionecpg1_regression 
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 60: query: select * from customers limit 2; with 0 parameter(s) on connection
ecpg1_regression
+[NO_PID]: ecpg_execute on line 77: using PQexecParams
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 60: using PQexec
+[NO_PID]: ecpg_free_params on line 77: parameter 1 = Jane Doe
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 60: correctly got 2 tuples with 2 fields
+[NO_PID]: ecpg_free_params on line 77: parameter 2 = 67890
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 60: RESULT: John Doe offset: -1; array: no
+[NO_PID]: ecpg_process_output on line 77: OK: INSERT 0 1
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 60: RESULT: Jane Doe offset: -1; array: no
+[NO_PID]: ecpg_execute on line 84: query: select * from customers limit 2; with 0 parameter(s) on connection
ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 60: RESULT: 12345 offset: -1; array: no
+[NO_PID]: ecpg_execute on line 84: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 60: RESULT: 67890 offset: -1; array: no
+[NO_PID]: ecpg_process_output on line 84: correctly got 2 tuples with 2 fields
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 68: query: select * from customers limit 2; with 0 parameter(s) on connection
ecpg1_regression
+[NO_PID]: ecpg_get_data on line 84: RESULT: John Doe offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 68: using PQexec
+[NO_PID]: ecpg_get_data on line 84: RESULT: Jane Doe offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 68: correctly got 2 tuples with 2 fields
+[NO_PID]: ecpg_get_data on line 84: RESULT: 12345 offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 68: RESULT: John Doe offset: -1; array: no
+[NO_PID]: ecpg_get_data on line 84: RESULT: 67890 offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 68: RESULT: Jane Doe offset: -1; array: no
+[NO_PID]: ecpg_execute on line 92: query: select * from customers limit 2; with 0 parameter(s) on connection
ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 68: RESULT: 12345 offset: -1; array: no
+[NO_PID]: ecpg_execute on line 92: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 68: RESULT: 67890 offset: -1; array: no
+[NO_PID]: ecpg_process_output on line 92: correctly got 2 tuples with 2 fields
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 76: query: select * from customers limit 2; with 0 parameter(s) on connection
ecpg1_regression
+[NO_PID]: ecpg_get_data on line 92: RESULT: John Doe offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 76: using PQexec
+[NO_PID]: ecpg_get_data on line 92: RESULT: Jane Doe offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 76: correctly got 2 tuples with 2 fields
+[NO_PID]: ecpg_get_data on line 92: RESULT: 12345 offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 76: RESULT: John Doe offset: -1; array: no
+[NO_PID]: ecpg_get_data on line 92: RESULT: 67890 offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 76: RESULT: Jane Doe offset: -1; array: no
+[NO_PID]: ecpg_execute on line 100: query: select * from customers limit 2; with 0 parameter(s) on connection
ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 76: RESULT: 12345 offset: -1; array: no
+[NO_PID]: ecpg_execute on line 100: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 76: RESULT: 67890 offset: -1; array: no
+[NO_PID]: ecpg_process_output on line 100: correctly got 2 tuples with 2 fields
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 84: query: select * from customers limit 1; with 0 parameter(s) on connection
ecpg1_regression
+[NO_PID]: ecpg_get_data on line 100: RESULT: John Doe offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 84: using PQexec
+[NO_PID]: ecpg_get_data on line 100: RESULT: Jane Doe offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 84: correctly got 1 tuples with 2 fields
+[NO_PID]: ecpg_get_data on line 100: RESULT: 12345 offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 84: RESULT: John Doe offset: -1; array: no
+[NO_PID]: ecpg_get_data on line 100: RESULT: 67890 offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 84: RESULT: 12345 offset: -1; array: no
+[NO_PID]: ecpg_execute on line 108: query: select * from customers limit 1; with 0 parameter(s) on connection
ecpg1_regression
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_execute on line 108: using PQexec
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_process_output on line 108: correctly got 1 tuples with 2 fields
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 108: RESULT: John Doe offset: -1; array: no
+[NO_PID]: sqlca: code: 0, state: 00000
+[NO_PID]: ecpg_get_data on line 108: RESULT: 12345 offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 89: query: select c from customers limit 2; with 0 parameter(s) on connection
ecpg1_regression
+[NO_PID]: ecpg_execute on line 113: query: select c from customers limit 2; with 0 parameter(s) on connection
ecpg1_regression
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_execute on line 89: using PQexec
+[NO_PID]: ecpg_execute on line 113: using PQexec
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_process_output on line 89: correctly got 2 tuples with 1 fields
+[NO_PID]: ecpg_process_output on line 113: correctly got 2 tuples with 1 fields
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 89: RESULT: John Doe offset: -1; array: no
+[NO_PID]: ecpg_get_data on line 113: RESULT: John Doe offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
-[NO_PID]: ecpg_get_data on line 89: RESULT: Jane Doe offset: -1; array: no
+[NO_PID]: ecpg_get_data on line 113: RESULT: Jane Doe offset: -1; array: no
 [NO_PID]: sqlca: code: 0, state: 00000
 [NO_PID]: ecpg_finish: connection ecpg1_regression closed
 [NO_PID]: sqlca: code: 0, state: 00000
diff --git a/src/interfaces/ecpg/test/preproc/pointer_to_struct.pgc
b/src/interfaces/ecpg/test/preproc/pointer_to_struct.pgc
index 1ec651e3fc3..f21d33bae3e 100644
--- a/src/interfaces/ecpg/test/preproc/pointer_to_struct.pgc
+++ b/src/interfaces/ecpg/test/preproc/pointer_to_struct.pgc
@@ -21,6 +21,12 @@ EXEC SQL TYPE cust_ind IS
         short   phone_ind;
     };

+EXEC SQL TYPE company IS
+    struct
+    {
+        customer customers[10];
+    };
+
 int main()
 {
     EXEC SQL begin declare section;
@@ -45,6 +51,8 @@ int main()
         int     phone;
       } *custs4 = (struct customer4 *) malloc(sizeof(struct customer4));

+      company acme;
+
       int r;
       varchar onlyname[2][50];
     EXEC SQL end declare section;
@@ -54,9 +62,25 @@ int main()
     EXEC SQL connect to REGRESSDB1;

     EXEC SQL create table customers (c varchar(50), p int);
-    EXEC SQL insert into customers values ('John Doe', '12345');
-    EXEC SQL insert into customers values ('Jane Doe', '67890');

+    /* First we'll insert some data using C variable references */
+    strcpy(custs1[0].name.arr, "John Doe");
+    custs1[0].name.len = strlen(custs1[0].name.arr);
+    custs1[0].phone = 12345;
+
+    strcpy(acme.customers[1].name.arr, "Jane Doe");
+    acme.customers[1].name.len = strlen(acme.customers[1].name.arr);
+    acme.customers[1].phone = 67890;
+
+    EXEC SQL insert into customers values (:custs1->name,
+                                           :custs1[0].phone);
+    EXEC SQL insert into customers values (:acme.customers[1].name,
+                                           :acme.customers[1].phone);
+
+    /* Clear the array, to be sure reading back into it actually gets data */
+    memset(custs1, 0, sizeof(customer) * 10);
+
+    /* Now read back the data */
     EXEC SQL select * INTO :custs1:inds from customers limit 2;
     printf("custs1:\n");
     for (r = 0; r < 2; r++)