Re: More const-marking cleanup - Mailing list pgsql-hackers

From Tom Lane
Subject Re: More const-marking cleanup
Date
Msg-id 1782944.1764967960@sss.pgh.pa.us
Whole thread Raw
In response to Re: More const-marking cleanup  (Tom Lane <tgl@sss.pgh.pa.us>)
List pgsql-hackers
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++)

pgsql-hackers by date:

Previous
From: Robert Haas
Date:
Subject: Re: [PATCH] Allow complex data for GUC extra.
Next
From: Andrew Pogrebnoi
Date:
Subject: Re: Popcount optimization for the slow-path lookups