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)
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to