ECPG support for struct in INTO list - Mailing list pgsql-hackers

From Boszormenyi Zoltan
Subject ECPG support for struct in INTO list
Date
Msg-id 4A605225.4050205@cybertec.at
Whole thread Raw
Responses Re: ECPG support for struct in INTO list
List pgsql-hackers
Hi,

one of our clients wants to port their application suite
from Informix to PostgreSQL, they use constructs like

SELECT * INTO :tablerec FROM table ...

where "tablerec" mirrors the table fields in a C struct.

Currently ECPG dumps core on this, more exactly aborts on it
in ecpg_type_name().

Patch is attached that solves it by introducing add_struct_to_head()
called  from rule "coutputvariable"  that also catches C unions now,
emitting an error because unions cannot be unambiguously unrolled.
It tries to handle NULL indicator, expecting a struct with at least
the same amount of members.

Best regards,
Zoltán Böszörményi

--
Bible has answers for everything. Proof:
"But let your communication be, Yea, yea; Nay, nay: for whatsoever is more
than these cometh of evil." (Matthew 5:37) - basics of digital technology.
"May your kingdom come" - superficial description of plate tectonics

----------------------------------
Zoltán Böszörményi
Cybertec Schönig & Schönig GmbH
http://www.postgresql.at/

diff -dcrpN pgsql85dev.4string/src/interfaces/ecpg/preproc/ecpg.trailer
pgsql85dev.5struct/src/interfaces/ecpg/preproc/ecpg.trailer
*** pgsql85dev.4string/src/interfaces/ecpg/preproc/ecpg.trailer    2009-07-14 21:36:58.000000000 +0200
--- pgsql85dev.5struct/src/interfaces/ecpg/preproc/ecpg.trailer    2009-07-17 12:24:30.000000000 +0200
*************** c_args: /*EMPTY*/        { $$ = EMPTY; }
*** 1835,1843 ****
          ;

  coutputvariable: cvariable indicator
!             { add_variable_to_head(&argsresult, find_variable($1), find_variable($2)); }
          | cvariable
!             { add_variable_to_head(&argsresult, find_variable($1), &no_indicator); }
          ;


--- 1835,1873 ----
          ;

  coutputvariable: cvariable indicator
!         {
!             struct variable *var = find_variable($1);
!
!             switch (var->type->type)
!             {
!                 case ECPGt_struct:
!                     add_struct_to_head(&argsresult, var, find_variable($2));
!                     break;
!                 case ECPGt_union:
!                     mmerror(PARSE_ERROR, ET_FATAL, "variable \"%s\" is a union", var->name);
!                     break;
!                 default:
!                     add_variable_to_head(&argsresult, var, find_variable($2));
!                     break;
!             }
!         }
          | cvariable
!         {
!             struct variable *var = find_variable($1);
!
!             switch (var->type->type)
!             {
!                 case ECPGt_struct:
!                     add_struct_to_head(&argsresult, var, &no_indicator);
!                     break;
!                 case ECPGt_union:
!                     mmerror(PARSE_ERROR, ET_FATAL, "variable \"%s\" is a union", var->name);
!                     break;
!                 default:
!                     add_variable_to_head(&argsresult, var, &no_indicator);
!                     break;
!             }
!         }
          ;


diff -dcrpN pgsql85dev.4string/src/interfaces/ecpg/preproc/extern.h
pgsql85dev.5struct/src/interfaces/ecpg/preproc/extern.h
*** pgsql85dev.4string/src/interfaces/ecpg/preproc/extern.h    2009-07-14 21:36:58.000000000 +0200
--- pgsql85dev.5struct/src/interfaces/ecpg/preproc/extern.h    2009-07-17 12:24:30.000000000 +0200
*************** extern struct descriptor *lookup_descrip
*** 91,96 ****
--- 91,97 ----
  extern struct variable *descriptor_variable(const char *name, int input);
  extern struct variable *sqlda_variable(const char *name);
  extern void add_variable_to_head(struct arguments **, struct variable *, struct variable *);
+ extern void add_struct_to_head(struct arguments **, struct variable *, struct variable *);
  extern void add_variable_to_tail(struct arguments **, struct variable *, struct variable *);
  extern void remove_variable_from_list(struct arguments ** list, struct variable * var);
  extern void dump_variables(struct arguments *, int);
diff -dcrpN pgsql85dev.4string/src/interfaces/ecpg/preproc/variable.c
pgsql85dev.5struct/src/interfaces/ecpg/preproc/variable.c
*** pgsql85dev.4string/src/interfaces/ecpg/preproc/variable.c    2009-07-14 21:36:58.000000000 +0200
--- pgsql85dev.5struct/src/interfaces/ecpg/preproc/variable.c    2009-07-17 12:24:30.000000000 +0200
*************** add_variable_to_head(struct arguments **
*** 382,387 ****
--- 382,461 ----
      *list = p;
  }

+ /*
+  * Insert a struct's members unrolled into our request list.
+  * This is needed for the case when the user says
+  *
+  * SELECT * INTO :mystruct FROM ...
+  * or
+  * SELECT a.*, b.* INTO :struct_a, :struct_b FROM a, b ...
+  *
+  * Just in case, implement it recursively.
+  */
+ void
+ add_struct_to_head(struct arguments ** list, struct variable * var, struct variable * ind)
+ {
+     struct ECPGstruct_member *member;
+     struct ECPGstruct_member *ind_member = NULL;
+     bool no_ind;
+
+     if (var->type->type != ECPGt_struct)
+         mmerror(PARSE_ERROR, ET_FATAL, "variable \"%s\" is not a struct", var->name);
+
+     no_ind = (ind == &no_indicator);
+
+     if (!no_ind && ind->type->type != ECPGt_struct)
+         mmerror(INDICATOR_NOT_STRUCT, ET_FATAL, "struct variable \"%s\" was associated with a non-struct indicator
variable", var->name); 
+
+     member = var->type->u.members;
+     if (!no_ind)
+         ind_member = ind->type->u.members;
+
+     while (member && (no_ind || ind_member))
+     {
+         char *newvarname;
+         char *newindname;
+         struct variable *newvar;
+         struct variable *newind = &no_indicator;
+
+         newvarname = mm_alloc(strlen(var->name) + strlen(member->name) + 2);
+         sprintf(newvarname, "%s.%s", var->name, member->name);
+         newvar = find_variable(newvarname);
+         if (newvar == NULL)
+             mmerror(PARSE_ERROR, ET_FATAL, "internal error: variable \"%s\" is not found", newvarname);
+
+         if (!no_ind)
+         {
+             newindname = mm_alloc(strlen(ind->name) + strlen(ind_member->name) + 2);
+             sprintf(newindname, "%s.%s", ind->name, ind_member->name);
+             newind = find_variable(newindname);
+             if (newind == NULL)
+                 mmerror(PARSE_ERROR, ET_FATAL, "internal error: variable \"%s\" is not found", newvarname);
+         }
+
+         switch (newvar->type->type)
+         {
+             case ECPGt_struct:
+                 add_struct_to_head(list, newvar, newind);
+                 break;
+             case ECPGt_union:
+                 mmerror(PARSE_ERROR, ET_FATAL, "variable \"%s\" is a union", newvarname);
+                 break;
+             default:
+                 add_variable_to_head(list, newvar, newind);
+                 break;
+         }
+         free(newvarname);
+
+         member = member->next;
+         if (!no_ind)
+             ind_member = ind_member->next;
+     }
+
+     if (member != NULL && !no_ind && ind_member == NULL)
+         mmerror(PARSE_ERROR, ET_FATAL, "indicator struct has less members than variable struct");
+ }
+
  /* Append a new variable to our request list. */
  void
  add_variable_to_tail(struct arguments ** list, struct variable * var, struct variable * ind)

pgsql-hackers by date:

Previous
From: Petr Jelinek
Date:
Subject: Re: GRANT ON ALL IN schema
Next
From: Petr Jelinek
Date:
Subject: Re: [PATCH] DefaultACLs