From: Michael Meskes <Michael_Meskes@topmail.de>

+
+ Tue Feb 23 17:32:25 CET 1999
+
+       - Other than a struct a union itself cannot be specified as variable.
+
+ Fri Feb 26 07:18:25 CET 1999
+
+       - Synced preproc.y with gram.y.
+
+ Sat Feb 27 20:30:03 CET 1999
+
+       - Added automatic allocating for NULL pointers.
This commit is contained in:
Marc G. Fournier 1999-02-28 07:25:34 +00:00
parent 51f0f6ddc8
commit d077c61492
12 changed files with 19946 additions and 248 deletions

View File

@ -475,5 +475,17 @@ Mon Feb 22 19:47:45 CET 1999
requires me to increase the major version number.
- Synced pgc.l with scan.l.
- Added support for unions.
Tue Feb 23 17:32:25 CET 1999
- Other than a struct a union itself cannot be specified as variable.
Fri Feb 26 07:18:25 CET 1999
- Synced preproc.y with gram.y.
Sat Feb 27 20:30:03 CET 1999
- Added automatic allocating for NULL pointers.
- Set library version to 3.0.0
- Set ecpg version to 3.0.0

View File

@ -11,9 +11,11 @@ DESCRIPTOR statement will be ignored.
it would be nice to be able to use :var[:index] as cvariable
support for dynamic SQL with unknown number of variables with SQLDA structure
it would also be nice to be able to work with varchar * (inculding automatic
allocating)
allocate memory for pointers as C input variables
support for dynamic SQL with unknown number of variables with SQLDA structure
or something similar
Missing statements:
- exec sql allocate

View File

@ -43,6 +43,7 @@ extern "C"
ECPGt_varchar, ECPGt_varchar2,
ECPGt_array,
ECPGt_struct,
ECPGt_union,
ECPGt_char_variable,
ECPGt_EOIT, /* End of insert types. */
ECPGt_EORT, /* End of result types. */

View File

@ -64,6 +64,7 @@ struct variable
{
enum ECPGttype type;
void *value;
void *pointer;
long varcharsize;
long arrsize;
long offset;
@ -91,6 +92,12 @@ struct prepared_statement
struct prepared_statement *next;
} *prep_stmts = NULL;
struct auto_mem
{
void *pointer;
struct auto_mem *next;
} *auto_allocs = NULL;
static int simple_debug = 0;
static FILE *debugstream = NULL;
@ -98,12 +105,25 @@ static void
register_error(long code, char *fmt,...)
{
va_list args;
struct auto_mem *am;
sqlca.sqlcode = code;
va_start(args, fmt);
vsprintf(sqlca.sqlerrm.sqlerrmc, fmt, args);
va_end(args);
sqlca.sqlerrm.sqlerrml = strlen(sqlca.sqlerrm.sqlerrmc);
/* free all memory we allocate for the user */
for (am = auto_allocs; am;)
{
struct auto_mem *act = am;
am = am->next;
free(act->pointer);
free(act);
}
auto_allocs = NULL;
}
static struct connection *
@ -166,7 +186,7 @@ ecpg_alloc(long size, int lineno)
register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno);
return NULL;
}
memset(new, '\0', size);
return (new);
}
@ -186,6 +206,15 @@ ecpg_strdup(const char *string, int lineno)
return (new);
}
static void
add_mem(void *ptr, int lineno)
{
struct auto_mem *am = (struct auto_mem *) ecpg_alloc(sizeof(struct auto_mem), lineno);
am->next = auto_allocs;
auto_allocs = am;
}
/* This function returns a newly malloced string that has the ' and \
in the argument quoted with \.
*/
@ -301,25 +330,32 @@ create_statement(int lineno, struct connection *connection, struct statement **
return false;
var->type = type;
var->value = va_arg(ap, void *);
var->varcharsize = va_arg(ap, long);
var->arrsize = va_arg(ap, long);
var->offset = va_arg(ap, long);
var->ind_type = va_arg(ap, enum ECPGttype);
var->ind_value = va_arg(ap, void *);
var->ind_varcharsize = va_arg(ap, long);
var->ind_arrsize = va_arg(ap, long);
var->ind_offset = va_arg(ap, long);
var->next = NULL;
var->pointer = va_arg(ap, void *);
/* if variable is NULL, the statement hasn't been prepared */
if (var->value == NULL)
if (var->pointer == NULL)
{
ECPGlog("create_statement: invalid statement name\n");
register_error(ECPG_INVALID_STMT, "Invalid statement name in line %d", lineno);
free(var);
return false;
}
var->varcharsize = va_arg(ap, long);
var->arrsize = va_arg(ap, long);
var->offset = va_arg(ap, long);
if (var->arrsize == 0 || var->varcharsize == 0)
var->value = *((void **)(var->pointer));
else
var->value = var->pointer;
var->ind_type = va_arg(ap, enum ECPGttype);
var->ind_value = va_arg(ap, void *);
var->ind_varcharsize = va_arg(ap, long);
var->ind_arrsize = va_arg(ap, long);
var->ind_offset = va_arg(ap, long);
var->next = NULL;
for (ptr = *list; ptr && ptr->next; ptr = ptr->next);
@ -478,8 +514,7 @@ ECPGexecute(struct statement * stmt)
break;
case ECPGt_char_variable:
{
/* set slen to string length if type is char * */
int slen = (var->varcharsize == 0) ? strlen((char *) var->value) : var->varcharsize;
int slen = strlen((char *) var->value);
char *tmp;
if (!(newcopy = ecpg_alloc(slen + 1, stmt->lineno)))
@ -632,13 +667,6 @@ ECPGexecute(struct statement * stmt)
act_field;
case PGRES_TUPLES_OK:
/*
* XXX Cheap Hack. For now, we see only the last group of
* tuples. This is clearly not the right way to do things
* !!
*/
nfields = PQnfields(results);
sqlca.sqlerrd[2] = ntuples = PQntuples(results);
status = true;
@ -676,18 +704,67 @@ ECPGexecute(struct statement * stmt)
status = false;
break;
}
for (act_tuple = 0; act_tuple < ntuples; act_tuple++)
/*
* allocate memory for NULL pointers
*/
if (var->arrsize == 0 || var->varcharsize == 0)
{
switch(var->type)
{
case ECPGt_char:
case ECPGt_unsigned_char:
if (var->value == NULL)
{
var->varcharsize = 0;
/* check strlen for each tuple */
for (act_tuple = 0; act_tuple < ntuples; act_tuple++)
{
int len = strlen(PQgetvalue(results, act_tuple, act_field));
if (len > var->varcharsize)
var->varcharsize = len;
}
var->offset *= var->varcharsize;
add_mem((void *)(var->value) = *((void **)(var->pointer)) = (void *) ecpg_alloc(var->offset * ntuples, stmt->lineno), stmt->lineno);
}
break;
#if 0
case ECPGt_varchar:
if (((struct ECPGgeneric_varchar *)var->value)->arr == NULL)
{
var->varcharsize = 0;
/* check strlen for each tuple */
for (act_tuple = 0; act_tuple < ntuples; act_tuple++)
{
int len = strlen(PQgetvalue(results, act_tuple, act_field));
if (len > var->varcharsize)
var->varcharsize = len;
((struct ECPGgeneric_varchar *) ((long) var->value + var->offset * act_tuple))->arr = (char *) ecpg_alloc(len, stmt->lineno);
}
}
break;
#endif
default:
if (var->value == NULL)
add_mem((void *)(var->value) = *((void **)(var->pointer)) = (void *) ecpg_alloc(var->offset * ntuples, stmt->lineno), stmt->lineno);
break;
}
}
for (act_tuple = 0; act_tuple < ntuples && status; act_tuple++)
{
pval = PQgetvalue(results, act_tuple, act_field);
ECPGlog("ECPGexecute line %d: RESULT: %s\n", stmt->lineno, pval ? pval : "");
/* Now the pval is a pointer to the var->value. */
/* We will have to decode the var->value */
/* Now the pval is a pointer to the value. */
/* We will have to decode the value */
/*
* check for null var->value and set indicator
* check for null value and set indicator
* accordingly
*/
switch (var->ind_type)
@ -840,37 +917,28 @@ ECPGexecute(struct statement * stmt)
case ECPGt_char:
case ECPGt_unsigned_char:
{
if (var->varcharsize == 0)
strncpy((char *) ((long) var->value + var->offset * act_tuple), pval, var->varcharsize);
if (var->varcharsize && var->varcharsize < strlen(pval))
{
/* char* */
strncpy(((char **) var->value)[act_tuple], pval, strlen(pval));
(((char **) var->value)[act_tuple])[strlen(pval)] = '\0';
}
else
{
strncpy((char *) ((long) var->value + var->offset * act_tuple), pval, var->varcharsize);
if (var->varcharsize < strlen(pval))
/* truncation */
switch (var->ind_type)
{
/* truncation */
switch (var->ind_type)
{
case ECPGt_short:
case ECPGt_unsigned_short:
((short *) var->ind_value)[act_tuple] = var->varcharsize;
break;
case ECPGt_int:
case ECPGt_unsigned_int:
((int *) var->ind_value)[act_tuple] = var->varcharsize;
break;
case ECPGt_long:
case ECPGt_unsigned_long:
((long *) var->ind_value)[act_tuple] = var->varcharsize;
break;
default:
break;
}
sqlca.sqlwarn[0] = sqlca.sqlwarn[1] = 'W';
case ECPGt_short:
case ECPGt_unsigned_short:
((short *) var->ind_value)[act_tuple] = var->varcharsize;
break;
case ECPGt_int:
case ECPGt_unsigned_int:
((int *) var->ind_value)[act_tuple] = var->varcharsize;
break;
case ECPGt_long:
case ECPGt_unsigned_long:
((long *) var->ind_value)[act_tuple] = var->varcharsize;
break;
default:
break;
}
sqlca.sqlwarn[0] = sqlca.sqlwarn[1] = 'W';
}
}
break;
@ -914,8 +982,7 @@ ECPGexecute(struct statement * stmt)
break;
default:
register_error(ECPG_UNSUPPORTED, "Unsupported type %s on line %d.",
ECPGtype_name(var->type), stmt->lineno);
register_error(ECPG_UNSUPPORTED, "Unsupported type %s on line %d.", ECPGtype_name(var->type), stmt->lineno);
status = false;
break;
}

View File

@ -20,7 +20,7 @@ preproc.c preproc.h: preproc.y
mv y.tab.h preproc.h
clean:
rm -f *.o core a.out ecpg$(X) *~
rm -f *.o core a.out ecpg$(X) *~ *.output *.tab.?
install: all
$(INSTALL) $(INSTL_EXE_OPTS) ecpg$(X) $(DESTDIR)$(BINDIR)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,295 @@
typedef union {
double dval;
int ival;
char * str;
struct when action;
struct index index;
int tagname;
struct this_type type;
enum ECPGttype type_enum;
} YYSTYPE;
#define SQL_AT 257
#define SQL_BOOL 258
#define SQL_BREAK 259
#define SQL_CALL 260
#define SQL_CONNECT 261
#define SQL_CONNECTION 262
#define SQL_CONTINUE 263
#define SQL_DEALLOCATE 264
#define SQL_DISCONNECT 265
#define SQL_ENUM 266
#define SQL_FOUND 267
#define SQL_FREE 268
#define SQL_GO 269
#define SQL_GOTO 270
#define SQL_IDENTIFIED 271
#define SQL_IMMEDIATE 272
#define SQL_INDICATOR 273
#define SQL_INT 274
#define SQL_LONG 275
#define SQL_OPEN 276
#define SQL_PREPARE 277
#define SQL_RELEASE 278
#define SQL_REFERENCE 279
#define SQL_SECTION 280
#define SQL_SEMI 281
#define SQL_SHORT 282
#define SQL_SIGNED 283
#define SQL_SQLERROR 284
#define SQL_SQLPRINT 285
#define SQL_SQLWARNING 286
#define SQL_START 287
#define SQL_STOP 288
#define SQL_STRUCT 289
#define SQL_UNSIGNED 290
#define SQL_VAR 291
#define SQL_WHENEVER 292
#define S_ANYTHING 293
#define S_AUTO 294
#define S_BOOL 295
#define S_CHAR 296
#define S_CONST 297
#define S_DOUBLE 298
#define S_ENUM 299
#define S_EXTERN 300
#define S_FLOAT 301
#define S_INT 302
#define S 303
#define S_LONG 304
#define S_REGISTER 305
#define S_SHORT 306
#define S_SIGNED 307
#define S_STATIC 308
#define S_STRUCT 309
#define S_UNION 310
#define S_UNSIGNED 311
#define S_VARCHAR 312
#define TYPECAST 313
#define ABSOLUTE 314
#define ACTION 315
#define ADD 316
#define ALL 317
#define ALTER 318
#define AND 319
#define ANY 320
#define AS 321
#define ASC 322
#define BEGIN_TRANS 323
#define BETWEEN 324
#define BOTH 325
#define BY 326
#define CASCADE 327
#define CASE 328
#define CAST 329
#define CHAR 330
#define CHARACTER 331
#define CHECK 332
#define CLOSE 333
#define COALESCE 334
#define COLLATE 335
#define COLUMN 336
#define COMMIT 337
#define CONSTRAINT 338
#define CREATE 339
#define CROSS 340
#define CURRENT 341
#define CURRENT_DATE 342
#define CURRENT_TIME 343
#define CURRENT_TIMESTAMP 344
#define CURRENT_USER 345
#define CURSOR 346
#define DAY_P 347
#define DECIMAL 348
#define DECLARE 349
#define DEFAULT 350
#define DELETE 351
#define DESC 352
#define DISTINCT 353
#define DOUBLE 354
#define DROP 355
#define ELSE 356
#define END_TRANS 357
#define EXCEPT 358
#define EXECUTE 359
#define EXISTS 360
#define EXTRACT 361
#define FALSE_P 362
#define FETCH 363
#define FLOAT 364
#define FOR 365
#define FOREIGN 366
#define FROM 367
#define FULL 368
#define GRANT 369
#define GROUP 370
#define HAVING 371
#define HOUR_P 372
#define IN 373
#define INNER_P 374
#define INSENSITIVE 375
#define INSERT 376
#define INTERSECT 377
#define INTERVAL 378
#define INTO 379
#define IS 380
#define ISOLATION 381
#define JOIN 382
#define KEY 383
#define LANGUAGE 384
#define LEADING 385
#define LEFT 386
#define LEVEL 387
#define LIKE 388
#define LOCAL 389
#define MATCH 390
#define MINUTE_P 391
#define MONTH_P 392
#define NAMES 393
#define NATIONAL 394
#define NATURAL 395
#define NCHAR 396
#define NEXT 397
#define NO 398
#define NOT 399
#define NULLIF 400
#define NULL_P 401
#define NUMERIC 402
#define OF 403
#define ON 404
#define ONLY 405
#define OPTION 406
#define OR 407
#define ORDER 408
#define OUTER_P 409
#define PARTIAL 410
#define POSITION 411
#define PRECISION 412
#define PRIMARY 413
#define PRIOR 414
#define PRIVILEGES 415
#define PROCEDURE 416
#define PUBLIC 417
#define READ 418
#define REFERENCES 419
#define RELATIVE 420
#define REVOKE 421
#define RIGHT 422
#define ROLLBACK 423
#define SCROLL 424
#define SECOND_P 425
#define SELECT 426
#define SET 427
#define SUBSTRING 428
#define TABLE 429
#define TEMP 430
#define THEN 431
#define TIME 432
#define TIMESTAMP 433
#define TIMEZONE_HOUR 434
#define TIMEZONE_MINUTE 435
#define TO 436
#define TRAILING 437
#define TRANSACTION 438
#define TRIM 439
#define TRUE_P 440
#define UNION 441
#define UNIQUE 442
#define UPDATE 443
#define USER 444
#define USING 445
#define VALUES 446
#define VARCHAR 447
#define VARYING 448
#define VIEW 449
#define WHEN 450
#define WHERE 451
#define WITH 452
#define WORK 453
#define YEAR_P 454
#define ZONE 455
#define TRIGGER 456
#define TYPE_P 457
#define ABORT_TRANS 458
#define AFTER 459
#define AGGREGATE 460
#define ANALYZE 461
#define BACKWARD 462
#define BEFORE 463
#define BINARY 464
#define CACHE 465
#define CLUSTER 466
#define COPY 467
#define CREATEDB 468
#define CREATEUSER 469
#define CYCLE 470
#define DATABASE 471
#define DELIMITERS 472
#define DO 473
#define EACH 474
#define ENCODING 475
#define EXPLAIN 476
#define EXTEND 477
#define FORWARD 478
#define FUNCTION 479
#define HANDLER 480
#define INCREMENT 481
#define INDEX 482
#define INHERITS 483
#define INSTEAD 484
#define ISNULL 485
#define LANCOMPILER 486
#define LIMIT 487
#define LISTEN 488
#define UNLISTEN 489
#define LOAD 490
#define LOCATION 491
#define LOCK_P 492
#define MAXVALUE 493
#define MINVALUE 494
#define MOVE 495
#define NEW 496
#define NOCREATEDB 497
#define NOCREATEUSER 498
#define NONE 499
#define NOTHING 500
#define NOTIFY 501
#define NOTNULL 502
#define OFFSET 503
#define OIDS 504
#define OPERATOR 505
#define PASSWORD 506
#define PROCEDURAL 507
#define RECIPE 508
#define RENAME 509
#define RESET 510
#define RETURNS 511
#define ROW 512
#define RULE 513
#define SERIAL 514
#define SEQUENCE 515
#define SETOF 516
#define SHOW 517
#define START 518
#define STATEMENT 519
#define STDIN 520
#define STDOUT 521
#define TRUSTED 522
#define UNTIL 523
#define VACUUM 524
#define VALID 525
#define VERBOSE 526
#define VERSION 527
#define IDENT 528
#define SCONST 529
#define Op 530
#define CSTRING 531
#define CVARIABLE 532
#define CPP_LINE 533
#define ICONST 534
#define PARAM 535
#define FCONST 536
#define OP 537
#define UMINUS 538
extern YYSTYPE yylval;

View File

@ -144,7 +144,8 @@ find_struct_member(char *name, char *str, struct ECPGstruct_member *members)
case ECPGt_array:
return(new_variable(name, ECPGmake_array_type(members->typ->u.element, members->typ->size)));
case ECPGt_struct:
return(new_variable(name, ECPGmake_struct_type(members->typ->u.members)));
case ECPGt_union:
return(new_variable(name, ECPGmake_struct_type(members->typ->u.members, members->typ->typ)));
default:
return(new_variable(name, ECPGmake_simple_type(members->typ->typ, members->typ->size)));
}
@ -175,14 +176,39 @@ find_struct(char * name, char *next)
*next = '\0';
p = find_variable(name);
/* restore the name, we will need it later on */
*next = c;
if (c == '-')
{
if (p->type->typ != ECPGt_struct && p->type->typ != ECPGt_union)
{
sprintf(errortext, "variable %s is not a pointer", name);
yyerror (errortext);
}
if (p->type->u.element->typ != ECPGt_struct && p->type->u.element->typ != ECPGt_union)
{
sprintf(errortext, "variable %s is not a pointer to a structure or a union", name);
yyerror (errortext);
}
/* restore the name, we will need it later on */
*next = c;
next++;
return find_struct_member(name, next, p->type->u.element->u.members);
}
else return find_struct_member(name, next, p->type->u.members);
else
{
if (p->type->typ != ECPGt_struct && p->type->typ != ECPGt_union)
{
sprintf(errortext, "variable %s is neither a structure nor a union", name);
yyerror (errortext);
}
/* restore the name, we will need it later on */
*next = c;
return find_struct_member(name, next, p->type->u.members);
}
}
static struct variable *
@ -323,6 +349,7 @@ check_indicator(struct ECPGtype *var)
break;
case ECPGt_struct:
case ECPGt_union:
for (p = var->u.members; p; p = p->next)
check_indicator(p->typ);
break;
@ -549,6 +576,7 @@ adjust_array(enum ECPGttype type_enum, int *dimension, int *length, int type_dim
switch (type_enum)
{
case ECPGt_struct:
case ECPGt_union:
/* pointer has to get dimension 0 */
if (pointer)
{
@ -740,8 +768,7 @@ adjust_array(enum ECPGttype type_enum, int *dimension, int *length, int type_dim
%type <str> opt_table opt_union opt_unique sort_clause sortby_list
%type <str> sortby OptUseOp opt_inh_star relation_name_list name_list
%type <str> group_clause having_clause from_clause c_list
%type <str> from_list from_val join_expr join_outer join_spec join_list
%type <str> join_using where_clause relation_expr row_op sub_type
%type <str> table_list join_outer where_clause relation_expr row_op sub_type
%type <str> opt_column_list insert_rest InsertStmt OptimizableStmt
%type <str> columnList DeleteStmt LockStmt UpdateStmt CursorStmt
%type <str> NotifyStmt columnElem copy_dirn c_expr UnlistenStmt
@ -754,7 +781,7 @@ adjust_array(enum ECPGttype type_enum, int *dimension, int *length, int type_dim
%type <str> func_args_list func_args opt_with ProcedureStmt def_arg
%type <str> def_elem def_list definition def_name def_type DefineStmt
%type <str> opt_instead event event_object RuleActionList,
%type <str> RuleActionBlock RuleActionMulti
%type <str> RuleActionBlock RuleActionMulti join_list
%type <str> RuleStmt opt_column opt_name oper_argtypes
%type <str> MathOp RemoveFuncStmt aggr_argtype for_update_clause
%type <str> RemoveAggrStmt remove_type RemoveStmt ExtendStmt RecipeStmt
@ -773,8 +800,10 @@ adjust_array(enum ECPGttype type_enum, int *dimension, int *length, int type_dim
%type <str> GrantStmt privileges operation_commalist operation
%type <str> cursor_clause opt_cursor opt_readonly opt_of opt_lmode
%type <str> case_expr when_clause_list case_default case_arg when_clause
%type <str> select_w_o_sort opt_select_limit select_limit_value,
%type <str> select_offset_value
%type <str> select_clause opt_select_limit select_limit_value,
%type <str> select_offset_value table_list using_expr join_expr
%type <str> using_list from_expr table_expr join_clause join_type
%type <str> join_qual update_list join_clause join_clause_with_union
%type <str> ECPGWhenever ECPGConnect connection_target ECPGOpen opt_using
%type <str> indicator ECPGExecute ecpg_expr dotext ECPGPrepare
@ -790,7 +819,7 @@ adjust_array(enum ECPGttype type_enum, int *dimension, int *length, int type_dim
%type <str> ECPGFree ECPGDeclare ECPGVar sql_variable_declarations
%type <str> sql_declaration sql_variable_list sql_variable opt_at
%type <str> struct_type s_struct declaration variable_declarations
%type <str> s_struct_or_union sql_struct_or_union
%type <str> s_struct s_union union_type
%type <type_enum> simple_type varchar_type
@ -1320,20 +1349,15 @@ ColConstraint:
{ $$ = $1; }
;
/* The column constraint WITH NULL gives a shift/reduce error
* because it requires yacc to look more than one token ahead to
* resolve WITH TIME ZONE and WITH NULL.
* So, leave it out of the syntax for now.
| WITH NULL_P
{
$$ = NULL;
}
* - thomas 1998-09-12
*
* DEFAULT NULL is already the default for Postgres.
/* DEFAULT NULL is already the default for Postgres.
* Bue define it here and carry it forward into the system
* to make it explicit.
* - thomas 1998-09-13
* WITH NULL and NULL are not SQL92-standard syntax elements,
* so leave them out. Use DEFAULT NULL to explicitly indicate
* that a column may have that value. WITH NULL leads to
* shift/reduce conflicts with WITH TIME ZONE anyway.
* - thomas 1999-01-08
*/
ColConstraintElem: CHECK '(' constraint_expr ')'
{
@ -2801,8 +2825,11 @@ opt_of: OF columnList { $$ = make2_str(make1_str("of"), $2); }
/***S*I***/
/* The new 'SelectStmt' rule adapted for the optional use of INTERSECT EXCEPT a nd UNION
* accepts the use of '(' and ')' to select an order of set operations.
* The rule returns a SelectStmt Node having the set operations attached to
* unionClause and intersectClause (NIL if no set operations were present)
*/
SelectStmt: select_w_o_sort sort_clause for_update_clause opt_select_limit
SelectStmt: select_clause sort_clause for_update_clause opt_select_limit
{
if (strlen($3) > 0 && ForUpdateNotAllowed != 0)
yyerror("SELECT FOR UPDATE is not allowed in this context");
@ -2819,7 +2846,7 @@ SelectStmt: select_w_o_sort sort_clause for_update_clause opt_select_limit
*
* The sort_clause is not handled here!
*/
select_w_o_sort: '(' select_w_o_sort ')'
select_clause: '(' select_clause ')'
{
$$ = make3_str(make1_str("("), $2, make1_str(")"));
}
@ -2827,17 +2854,17 @@ select_w_o_sort: '(' select_w_o_sort ')'
{
$$ = $1;
}
| select_w_o_sort EXCEPT select_w_o_sort
| select_clause EXCEPT select_clause
{
$$ = cat3_str($1, make1_str("except"), $3);
ForUpdateNotAllowed = 1;
}
| select_w_o_sort UNION opt_union select_w_o_sort
| select_clause UNION opt_union select_clause
{
$$ = cat3_str($1, make1_str("union"), $3);
ForUpdateNotAllowed = 1;
}
| select_w_o_sort INTERSECT opt_union select_w_o_sort
| select_clause INTERSECT opt_union select_clause
{
$$ = cat3_str($1, make1_str("intersect"), $3);
ForUpdateNotAllowed = 1;
@ -2949,20 +2976,24 @@ having_clause: HAVING a_expr
| /*EMPTY*/ { $$ = make1_str(""); }
;
for_update_clause:
FOR UPDATE
{
$$ = make1_str("for update");
}
| FOR UPDATE OF va_list
{
$$ = cat2_str(make1_str("for update of"), $4);
}
| /* EMPTY */
{
$$ = make1_str("");
}
for_update_clause: FOR UPDATE update_list
{
$$ = make1_str("for update");
}
| /* EMPTY */
{
$$ = make1_str("");
}
;
update_list: OF va_list
{
$$ = cat2_str(make1_str("of"), $2);
}
| /* EMPTY */
{
$$ = make1_str("");
}
;
/*****************************************************************************
*
@ -2972,78 +3003,144 @@ for_update_clause:
*
*****************************************************************************/
from_clause: FROM '(' relation_expr join_expr JOIN relation_expr join_spec ')'
{
yyerror("JOIN not yet implemented");
}
| FROM from_list { $$ = cat2_str(make1_str("from"), $2); }
| /*EMPTY*/ { $$ = make1_str(""); }
;
from_clause: FROM from_expr
{
$$ = cat2_str(make1_str("from"), $2);
}
| /* EMPTY */
{
$$ = make1_str("");
}
from_list: from_list ',' from_val
{ $$ = cat3_str($1, make1_str(","), $3); }
| from_val CROSS JOIN from_val
{ yyerror("CROSS JOIN not yet implemented"); }
| from_val
{ $$ = $1; }
;
from_val: relation_expr AS ColLabel
{
$$ = cat3_str($1, make1_str("as"), $3);
}
| relation_expr ColId
{
from_expr: '(' join_clause_with_union ')'
{ $$ = make3_str(make1_str("("), $2, make1_str(")")); }
| join_clause
{ $$ = $1; }
| table_list
{ $$ = $1; }
;
table_list: table_list ',' table_expr
{ $$ = make3_str($1, make1_str(","), $3); }
| table_expr
{ $$ = $1; }
;
table_expr: relation_expr AS ColLabel
{
$$ = cat3_str($1, make1_str("as"), $3);
}
| relation_expr ColId
{
$$ = cat2_str($1, $2);
}
| relation_expr
{
$$ = $1;
}
;
/* A UNION JOIN is the same as a FULL OUTER JOIN which *omits*
* all result rows which would have matched on an INNER JOIN.
* Let's reject this for now. - thomas 1999-01-08
*/
join_clause_with_union: join_clause
{ $$ = $1; }
| table_expr UNION JOIN table_expr
{ yyerror("UNION JOIN not yet implemented"); }
;
join_clause: table_expr join_list
{
$$ = cat2_str($1, $2);
}
| relation_expr
{
$$ = $1;
}
;
}
;
join_list: join_list join_expr
{
$$ = cat2_str($1, $2);
}
| join_expr
{
$$ = $1;
}
;
/* This is everything but the left side of a join.
* Note that a CROSS JOIN is the same as an unqualified
* inner join, so just pass back the right-side table.
* A NATURAL JOIN implicitly matches column names between
* tables, so we'll collect those during the later transformation.
*/
join_expr: join_type JOIN table_expr join_qual
{
$$ = cat4_str($1, make1_str("join"), $3, $4);
}
| NATURAL join_type JOIN table_expr
{
$$ = cat4_str(make1_str("natural"), $2, make1_str("join"), $4);
}
| CROSS JOIN table_expr
{ $$ = cat2_str(make1_str("cross join"), $3); }
;
/* OUTER is just noise... */
join_type: FULL join_outer
{
$$ = cat2_str(make1_str("full"), $2);
fprintf(stderr,"FULL OUTER JOIN not yet implemented\n");
}
| LEFT join_outer
{
$$ = cat2_str(make1_str("left"), $2);
fprintf(stderr,"LEFT OUTER JOIN not yet implemented\n");
}
| RIGHT join_outer
{
$$ = cat2_str(make1_str("right"), $2);
fprintf(stderr,"RIGHT OUTER JOIN not yet implemented\n");
}
| OUTER_P
{
$$ = make1_str("outer");
fprintf(stderr,"OUTER JOIN not yet implemented\n");
}
| INNER_P
{
$$ = make1_str("inner");
}
| /* EMPTY */
{
$$ = make1_str("");
}
join_expr: NATURAL join_expr { $$ = cat2_str(make1_str("natural"), $2); }
| FULL join_outer
{ yyerror("FULL OUTER JOIN not yet implemented"); }
| LEFT join_outer
{ yyerror("LEFT OUTER JOIN not yet implemented"); }
| RIGHT join_outer
{ yyerror("RIGHT OUTER JOIN not yet implemented"); }
| OUTER_P
{ yyerror("OUTER JOIN not yet implemented"); }
| INNER_P
{ yyerror("INNER JOIN not yet implemented"); }
| UNION
{ yyerror("UNION JOIN not yet implemented"); }
| /*EMPTY*/
{ yyerror("INNER JOIN not yet implemented"); }
;
join_outer: OUTER_P { $$ = make1_str("outer"); }
| /*EMPTY*/ { $$ = make1_str(""); /* no qualifiers */ }
;
join_spec: ON '(' a_expr ')' { $$ = make3_str(make1_str("on ("), $3, make1_str(")")); }
| USING '(' join_list ')' { $$ = make3_str(make1_str("using ("), $3, make1_str(")")); }
| /*EMPTY*/ { $$ = make1_str(""); /* no qualifiers */ }
;
/* JOIN qualification clauses
* Possibilities are:
* USING ( column list ) allows only unqualified column names,
* which must match between tables.
* ON expr allows more general qualifications.
* - thomas 1999-01-07
*/
join_list: join_using { $$ = $1; }
| join_list ',' join_using { $$ = cat3_str($1, make1_str(","), $3); }
;
join_qual: USING '(' using_list ')' { $$ = make3_str(make1_str("using ("), $3, make1_str(")")); }
| ON a_expr { $$ = cat2_str(make1_str("on"), $2); }
;
join_using: ColId
using_list: using_list ',' using_expr { $$ = make3_str($1, make1_str(","), $3); }
| using_expr { $$ = $1; }
;
using_expr: ColId
{
$$ = $1;
}
| ColId '.' ColId
{
$$ = make3_str($1, make1_str("."), $3);
}
| Iconst
{
$$ = $1;;
}
;
where_clause: WHERE a_expr { $$ = cat2_str(make1_str("where"), $2); }
@ -4053,8 +4150,6 @@ case_expr: CASE case_arg when_clause_list case_default END_TRANS
| COALESCE '(' expr_list ')'
{
$$ = cat3_str(make1_str("coalesce("), $3, make1_str(")"));
fprintf(stderr, "COALESCE() not yet fully implemented");
}
;
@ -4390,7 +4485,6 @@ ColLabel: ColId { $$ = $1; }
| COALESCE { $$ = make1_str("coalesce"); }
| CONSTRAINT { $$ = make1_str("constraint"); }
| COPY { $$ = make1_str("copy"); }
| CROSS { $$ = make1_str("cross"); }
| CURRENT { $$ = make1_str("current"); }
| DO { $$ = make1_str("do"); }
| ELSE { $$ = make1_str("else"); }
@ -4733,6 +4827,13 @@ type: simple_type
$$.type_dimension = -1;
$$.type_index = -1;
}
| union_type
{
$$.type_enum = ECPGt_union;
$$.type_str = $1;
$$.type_dimension = -1;
$$.type_index = -1;
}
| enum_type
{
$$.type_str = $1;
@ -4766,16 +4867,28 @@ struct_type: s_struct '{' variable_declarations '}'
$$ = cat4_str($1, make1_str("{"), $3, make1_str("}"));
}
s_struct : s_struct_or_union opt_symbol
union_type: s_union '{' variable_declarations '}'
{
ECPGfree_struct_member(struct_member_list[struct_level]);
free(actual_storage[struct_level--]);
$$ = cat4_str($1, make1_str("{"), $3, make1_str("}"));
}
s_struct : S_STRUCT opt_symbol
{
struct_member_list[struct_level++] = NULL;
if (struct_level >= STRUCT_DEPTH)
yyerror("Too many levels in nested structure definition");
$$ = cat2_str($1, $2);
$$ = cat2_str(make1_str("struct"), $2);
}
s_struct_or_union: S_STRUCT { $$ = make1_str("struct"); }
| S_UNION { $$ = make1_str("union"); }
s_union : S_UNION opt_symbol
{
struct_member_list[struct_level++] = NULL;
if (struct_level >= STRUCT_DEPTH)
yyerror("Too many levels in nested structure definition");
$$ = cat2_str(make1_str("union"), $2);
}
opt_symbol: /* empty */ { $$ = make1_str(""); }
| symbol { $$ = $1; }
@ -4815,10 +4928,11 @@ variable: opt_pointer symbol opt_array_bounds opt_initializer
switch (actual_type[struct_level].type_enum)
{
case ECPGt_struct:
case ECPGt_union:
if (dimension < 0)
type = ECPGmake_struct_type(struct_member_list[struct_level]);
type = ECPGmake_struct_type(struct_member_list[struct_level], actual_type[struct_level].type_enum);
else
type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level]), dimension);
type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level], actual_type[struct_level].type_enum), dimension);
$$ = make4_str($1, mm_strdup($2), $3.str, $4);
break;
@ -4846,7 +4960,8 @@ variable: opt_pointer symbol opt_array_bounds opt_initializer
if (length > 0)
$$ = make4_str(make5_str(mm_strdup(actual_storage[struct_level]), make1_str(" struct varchar_"), mm_strdup($2), make1_str(" { int len; char arr["), mm_strdup(ascii_len)), make1_str("]; } "), mm_strdup($2), mm_strdup(dim));
else
$$ = make4_str(make3_str(mm_strdup(actual_storage[struct_level]), make1_str(" struct varchar_"), mm_strdup($2)), make1_str(" { int len; char *arr; } "), mm_strdup($2), mm_strdup(dim));
yyerror ("pointer to varchar are not implemented yet");
/* $$ = make4_str(make3_str(mm_strdup(actual_storage[struct_level]), make1_str(" struct varchar_"), mm_strdup($2)), make1_str(" { int len; char *arr; }"), mm_strdup($2), mm_strdup(dim));*/
break;
case ECPGt_char:
case ECPGt_unsigned_char:
@ -5189,7 +5304,7 @@ ctype: CHAR
$$.type_index = -1;
$$.type_dimension = -1;
}
| sql_struct_or_union
| SQL_STRUCT
{
struct_member_list[struct_level++] = NULL;
if (struct_level >= STRUCT_DEPTH)
@ -5197,11 +5312,24 @@ ctype: CHAR
} '{' sql_variable_declarations '}'
{
ECPGfree_struct_member(struct_member_list[struct_level--]);
$$.type_str = cat4_str($1, make1_str("{"), $4, make1_str("}"));
$$.type_str = cat3_str(make1_str("struct {"), $4, make1_str("}"));
$$.type_enum = ECPGt_struct;
$$.type_index = -1;
$$.type_dimension = -1;
}
| UNION
{
struct_member_list[struct_level++] = NULL;
if (struct_level >= STRUCT_DEPTH)
yyerror("Too many levels in nested structure definition");
} '{' sql_variable_declarations '}'
{
ECPGfree_struct_member(struct_member_list[struct_level--]);
$$.type_str = cat3_str(make1_str("union {"), $4, make1_str("}"));
$$.type_enum = ECPGt_union;
$$.type_index = -1;
$$.type_dimension = -1;
}
| symbol
{
struct typedefs *this = get_typedef($1);
@ -5213,9 +5341,6 @@ ctype: CHAR
struct_member_list[struct_level] = this->struct_member_list;
}
sql_struct_or_union: SQL_STRUCT { $$ = make1_str("struct"); }
| UNION { $$ = make1_str("union"); }
opt_signed: SQL_SIGNED | /* empty */
sql_variable_declarations: /* empty */
@ -5260,10 +5385,11 @@ sql_variable: opt_pointer symbol opt_array_bounds
switch (actual_type[struct_level].type_enum)
{
case ECPGt_struct:
case ECPGt_union:
if (dimension < 0)
type = ECPGmake_struct_type(struct_member_list[struct_level]);
type = ECPGmake_struct_type(struct_member_list[struct_level], actual_type[struct_level].type_enum);
else
type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level]), dimension);
type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level], actual_type[struct_level].type_enum), dimension);
break;
case ECPGt_varchar:
@ -5330,10 +5456,11 @@ ECPGVar: SQL_VAR symbol IS ctype opt_type_array_bounds opt_reference
switch ($4.type_enum)
{
case ECPGt_struct:
case ECPGt_union:
if (dimension < 0)
type = ECPGmake_struct_type(struct_member_list[struct_level]);
type = ECPGmake_struct_type(struct_member_list[struct_level], $4.type_enum);
else
type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level]), dimension);
type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level], $4.type_enum), dimension);
break;
case ECPGt_varchar:
if (dimension == -1)

View File

@ -47,7 +47,8 @@ ECPGstruct_member_dup(struct ECPGstruct_member * rm)
switch (rm->typ->typ)
{
case ECPGt_struct:
type = ECPGmake_struct_type(rm->typ->u.members);
case ECPGt_union:
type = ECPGmake_struct_type(rm->typ->u.members, rm->typ->typ);
break;
case ECPGt_array:
type = ECPGmake_array_type(ECPGmake_simple_type(rm->typ->u.element->typ, rm->typ->u.element->size), rm->typ->size);
@ -108,9 +109,9 @@ ECPGmake_array_type(struct ECPGtype * typ, long siz)
}
struct ECPGtype *
ECPGmake_struct_type(struct ECPGstruct_member * rm)
ECPGmake_struct_type(struct ECPGstruct_member * rm, enum ECPGttype type)
{
struct ECPGtype *ne = ECPGmake_simple_type(ECPGt_struct, 1);
struct ECPGtype *ne = ECPGmake_simple_type(type, 1);
ne->u.members = ECPGstruct_member_dup(rm);
@ -122,7 +123,7 @@ get_type(enum ECPGttype typ)
{
switch (typ)
{
case ECPGt_char:
case ECPGt_char:
return ("ECPGt_char");
break;
case ECPGt_unsigned_char:
@ -167,6 +168,8 @@ get_type(enum ECPGttype typ)
sprintf(errortext, "illegal variable type %d\n", typ);
yyerror(errortext);
}
return NULL;
}
/* Dump a type.
@ -202,38 +205,45 @@ ECPGdump_a_type(FILE *o, const char *name, struct ECPGtype * typ, const char *in
switch (typ->typ)
{
case ECPGt_array:
if (IS_SIMPLE_TYPE(typ->u.element->typ))
switch (typ->u.element->typ)
{
ECPGdump_a_simple(o, name, typ->u.element->typ,
case ECPGt_array:
yyerror("No nested arrays allowed (except strings)"); /* array of array */
break;
case ECPGt_struct:
case ECPGt_union:
ECPGdump_a_struct(o, name, ind_name, typ->size, typ->u.element, ind_typ->u.element, NULL, prefix, ind_prefix);
break;
default:
if (!IS_SIMPLE_TYPE(typ->u.element->typ))
yyerror("Internal error: unknown datatype, please inform pgsql-bugs@postgresql.org");
ECPGdump_a_simple(o, name, typ->u.element->typ,
typ->u.element->size, typ->size, NULL, prefix);
if (ind_typ->typ == ECPGt_NO_INDICATOR)
ECPGdump_a_simple(o, ind_name, ind_typ->typ, ind_typ->size, -1, NULL, ind_prefix);
else
{
if (ind_typ->typ != ECPGt_array)
if (ind_typ->typ == ECPGt_NO_INDICATOR)
ECPGdump_a_simple(o, ind_name, ind_typ->typ, ind_typ->size, -1, NULL, ind_prefix);
else
{
fprintf(stderr, "Indicator for an array has to be array too.\n");
exit(INDICATOR_NOT_ARRAY);
}
ECPGdump_a_simple(o, ind_name, ind_typ->u.element->typ,
if (ind_typ->typ != ECPGt_array)
{
fprintf(stderr, "Indicator for an array has to be array too.\n");
exit(INDICATOR_NOT_ARRAY);
}
ECPGdump_a_simple(o, ind_name, ind_typ->u.element->typ,
ind_typ->u.element->size, ind_typ->size, NULL, prefix);
}
}
}
else if (typ->u.element->typ == ECPGt_array)
{
yyerror("No nested arrays allowed (except strings)"); /* array of array */
}
else if (typ->u.element->typ == ECPGt_struct)
{
/* Array of structs */
ECPGdump_a_struct(o, name, ind_name, typ->size, typ->u.element, ind_typ->u.element, NULL, prefix, ind_prefix);
}
else
yyerror("Internal error: unknown datatype, please inform pgsql-bugs@postgresql.org");
break;
case ECPGt_struct:
ECPGdump_a_struct(o, name, ind_name, 1, typ, ind_typ, NULL, prefix, ind_prefix);
break;
case ECPGt_union: /* cannot dump a complete union */
yyerror("Type of union has to be specified");
break;
case ECPGt_char_variable:
ECPGdump_a_simple(o, name, typ->typ, 1, 1, NULL, prefix);
ECPGdump_a_simple(o, ind_name, ind_typ->typ, ind_typ->size, -1, NULL, ind_prefix);
break;
default:
ECPGdump_a_simple(o, name, typ->typ, typ->size, -1, NULL, prefix);
ECPGdump_a_simple(o, ind_name, ind_typ->typ, ind_typ->size, -1, NULL, ind_prefix);
@ -259,7 +269,9 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ,
char *variable = (char *) mm_alloc(strlen(name) + ((prefix == NULL) ? 0 : strlen(prefix)) + 4);
char *offset = (char *) mm_alloc(strlen(name) + strlen("sizeof(struct varchar_)") + 1);
if (varcharsize == 0 || arrsize >= 0)
/* if (varcharsize == 0 || arrsize >= 0)*/
/* we have to use the pointer except for arrays with given bounds */
if (arrsize > 0)
sprintf(variable, "(%s%s)", prefix ? prefix : "", name);
else
sprintf(variable, "&(%s%s)", prefix ? prefix : "", name);
@ -272,7 +284,7 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ,
case ECPGt_char:
case ECPGt_unsigned_char:
case ECPGt_char_variable:
sprintf(offset, "%ld*sizeof(char)", varcharsize);
sprintf(offset, "%ld*sizeof(char)", varcharsize == 0 ? 1 : varcharsize);
break;
default:
sprintf(offset, "sizeof(%s)", ECPGtype_name(typ));
@ -353,34 +365,36 @@ ECPGfree_type(struct ECPGtype * typ)
{
if (!IS_SIMPLE_TYPE(typ->typ))
{
if (typ->typ == ECPGt_array)
switch(typ->typ)
{
if (IS_SIMPLE_TYPE(typ->u.element->typ))
free(typ->u.element);
else if (typ->u.element->typ == ECPGt_array)
/* Array of array, */
yyerror("internal error, found multi-dimensional array\n");
else if (typ->u.element->typ == ECPGt_struct)
{
/* Array of structs. */
ECPGfree_struct_member(typ->u.element->u.members);
case ECPGt_array:
switch (typ->u.element->typ)
{
case ECPGt_array:
yyerror("internal error, found multi-dimensional array\n");
break;
case ECPGt_struct:
case ECPGt_union:
/* Array of structs. */
ECPGfree_struct_member(typ->u.element->u.members);
free(typ->u.members);
break;
default:
if (!IS_SIMPLE_TYPE(typ->u.element->typ))
yyerror("Internal error: unknown datatype, please inform pgsql-bugs@postgresql.org");
free(typ->u.element);
}
break;
case ECPGt_struct:
case ECPGt_union:
ECPGfree_struct_member(typ->u.members);
free(typ->u.members);
}
else
{
sprintf(errortext, "illegal variable type %d\n", typ);
break;
default:
sprintf(errortext, "illegal variable type %d\n", typ->typ);
yyerror(errortext);
}
}
else if (typ->typ == ECPGt_struct)
{
ECPGfree_struct_member(typ->u.members);
free(typ->u.members);
}
else
{
sprintf(errortext, "illegal variable type %d\n", typ);
yyerror(errortext);
break;
}
}
free(typ);

View File

@ -29,7 +29,7 @@ void ECPGmake_struct_member(char *, struct ECPGtype *, struct ECPGstruct_member
struct ECPGtype *ECPGmake_simple_type(enum ECPGttype, long);
struct ECPGtype *ECPGmake_varchar_type(enum ECPGttype, long);
struct ECPGtype *ECPGmake_array_type(struct ECPGtype *, long);
struct ECPGtype *ECPGmake_struct_type(struct ECPGstruct_member *);
struct ECPGtype *ECPGmake_struct_type(struct ECPGstruct_member *, enum ECPGttype type);
struct ECPGstruct_member * ECPGstruct_member_dup(struct ECPGstruct_member *);
/* Frees a type. */

View File

@ -5,8 +5,8 @@ exec sql include header_test;
exec sql type c is char reference;
typedef char* c;
exec sql type ind is union { int integer; short smallinteger; };
typedef union { int integer; short smallinteger; } ind;
exec sql type ind is union { int integer; short smallint; };
typedef union { int integer; short smallint; } ind;
int
main ()
@ -23,7 +23,7 @@ exec sql begin declare section;
int ind_married;
ind children;
ind ind_children;
char married[9];
char *married = NULL;
c testname="Petra";
char *query="select name, born, age, married, children from meskes where name = :var1";
exec sql end declare section;
@ -43,7 +43,7 @@ exec sql end declare section;
exec sql connect to unix:postgresql://localhost:5432/mm;
strcpy(msg, "create");
exec sql create table meskes(name char(8), born integer, age smallint, married char(8), children integer);
exec sql create table meskes(name char(8), born integer, age smallint, married date, children integer);
strcpy(msg, "insert");
exec sql insert into meskes(name, married, children) values ('Petra', '19900404', 3);
@ -62,17 +62,20 @@ exec sql end declare section;
while (1) {
strcpy(msg, "fetch");
exec sql fetch in cur into :personal:ind_personal, :married:ind_married, :children.integer:ind_children.smallinteger;
exec sql fetch in cur into :personal:ind_personal, :married:ind_married, :children.integer:ind_children.smallint;
printf("%8.8s", personal.name.arr);
if (!ind_personal.ind_birth.born)
if (ind_personal.ind_birth.born >= 0)
printf(", born %d", personal.birth.born);
if (!ind_personal.ind_birth.age)
if (ind_personal.ind_birth.age >= 0)
printf(", age = %d", personal.birth.age);
if (!ind_married)
printf(", married %s", married);
if (!ind_children.smallinteger)
if (ind_married >= 0)
printf(", married %10.10s", married);
if (ind_children.smallint >= 0)
printf(", children = %d", children.integer);
putchar('\n');
free(married);
married = NULL;
}
strcpy(msg, "close");
@ -89,19 +92,21 @@ exec sql end declare section;
while (1) {
strcpy(msg, "fetch");
exec sql fetch in prep into :personal:ind_personal, :married:ind_married, :children.integer:ind_children.smallinteger;
exec sql fetch in prep into :personal:ind_personal, :married:ind_married, :children.integer:ind_children.smallint;
printf("%8.8s", personal.name.arr);
if (!ind_personal.ind_birth.born)
if (ind_personal.ind_birth.born >= 0)
printf(", born %d", personal.birth.born);
if (!ind_personal.ind_birth.age)
if (ind_personal.ind_birth.age >= 0)
printf(", age = %d", personal.birth.age);
if (!ind_married)
printf(", married %s", married);
if (!ind_children.smallinteger)
if (ind_married >= 0)
printf(", married %10.10s", married);
if (ind_children.smallint >= 0)
printf(", children = %d", children.integer);
putchar('\n');
}
free(married);
strcpy(msg, "close");
exec sql close prep;