Note that this still has some bugs. The functionality is there though, it's just a matter of fixing the bugs now.
Cleaned up error handling in preprocessor.
/* dynamic SQL support routines
*
- * $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/descriptor.c,v 1.7 2003/11/29 19:52:08 pgsql Exp $
+ * $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/descriptor.c,v 1.8 2004/06/30 15:01:56 meskes Exp $
*/
#define POSTGRES_ECPG_INTERNAL
return (true);
}
+static bool
+set_int_item(int lineno, int *target, const void *var, enum ECPGttype vartype)
+{
+ switch (vartype)
+ {
+ case ECPGt_short:
+ *target = *(short *) var;
+ break;
+ case ECPGt_int:
+ *target = *(int *) var;
+ break;
+ case ECPGt_long:
+ *target = *(long *) var;
+ break;
+ case ECPGt_unsigned_short:
+ *target = *(unsigned short *) var;
+ break;
+ case ECPGt_unsigned_int:
+ *target = *(unsigned int *) var;
+ break;
+ case ECPGt_unsigned_long:
+ *target = *(unsigned long *) var;
+ break;
+#ifdef HAVE_LONG_LONG_INT_64
+ case ECPGt_long_long:
+ *target = *(long long int *) var;
+ break;
+ case ECPGt_unsigned_long_long:
+ *target = *(unsigned long long int *) var;
+ break;
+#endif /* HAVE_LONG_LONG_INT_64 */
+ case ECPGt_float:
+ *target = *(float *) var;
+ break;
+ case ECPGt_double:
+ *target = *(double *) var;
+ break;
+ default:
+ ECPGraise(lineno, ECPG_VAR_NOT_NUMERIC, ECPG_SQLSTATE_RESTRICTED_DATA_TYPE_ATTRIBUTE_VIOLATION, NULL);
+ return (false);
+ }
+
+ return true;
+}
+
static bool
get_char_item(int lineno, void *var, enum ECPGttype vartype, char *value, int varcharsize)
{
return (true);
}
+bool
+ECPGset_desc(int lineno, char *desc_name, int index,...)
+{
+ va_list args;
+ struct descriptor *desc;
+ struct descriptor_item *desc_item, *last_di;
+
+ for (desc = all_descriptors; desc; desc = desc->next)
+ {
+ if (strcmp(desc_name, desc->name)==0)
+ break;
+ }
+
+ if (desc == NULL)
+ {
+ ECPGraise(lineno, ECPG_UNKNOWN_DESCRIPTOR, ECPG_SQLSTATE_INVALID_SQL_DESCRIPTOR_NAME, desc_name);
+ return false;
+ }
+
+ for (desc_item = desc->items; desc_item; desc_item = desc_item->next)
+ {
+ if (desc_item->num == index)
+ break;
+ }
+
+ if (desc_item == NULL)
+ {
+ desc_item = ECPGalloc(sizeof(*desc_item), lineno);
+ desc_item->num = index;
+ desc_item->next = desc->items;
+ desc->items = desc_item;
+ }
+
+ va_start(args, index);
+
+ do
+ {
+ enum ECPGdtype itemtype;
+ long varcharsize;
+ long offset;
+ long arrsize;
+ enum ECPGttype vartype;
+ void *var;
+
+ itemtype = va_arg(args, enum ECPGdtype);
+
+ if (itemtype == ECPGd_EODT)
+ break;
+
+ vartype = va_arg(args, enum ECPGttype);
+ var = va_arg(args, void *);
+ varcharsize = va_arg(args, long);
+ arrsize = va_arg(args, long);
+ offset = va_arg(args, long);
+
+ switch (itemtype)
+ {
+ case ECPGd_data:
+ {
+ // FIXME: how to do this in general?
+ switch (vartype)
+ {
+ case ECPGt_char:
+ desc_item->data = strdup((char *)var);
+ break;
+ case ECPGt_int:
+ {
+ char buf[20];
+ snprintf(buf, 20, "%d", *(int *)var);
+ desc_item->data = strdup(buf);
+ break;
+ }
+ default:
+ abort();
+ }
+ break;
+ }
+
+ case ECPGd_indicator:
+ set_int_item(lineno, &desc_item->indicator, var, vartype);
+ break;
+
+ case ECPGd_length:
+ set_int_item(lineno, &desc_item->length, var, vartype);
+ break;
+
+ case ECPGd_precision:
+ set_int_item(lineno, &desc_item->precision, var, vartype);
+ break;
+
+ case ECPGd_scale:
+ set_int_item(lineno, &desc_item->scale, var, vartype);
+ break;
+
+ case ECPGd_type:
+ set_int_item(lineno, &desc_item->type, var, vartype);
+ break;
+
+ default:
+ {
+ char type_str[20];
+ snprintf(type_str, sizeof(type_str), "%d", itemtype);
+ ECPGraise(lineno, ECPG_UNKNOWN_DESCRIPTOR_ITEM, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, type_str);
+ return false;
+ }
+ }
+
+ /*if (itemtype == ECPGd_data)
+ {
+ free(desc_item->data);
+ desc_item->data = NULL;
+ }*/
+ }
+ while (true);
+
+ return true;
+}
+
bool
ECPGdeallocate_desc(int line, const char *name)
{
ECPGfree(new);
return false;
}
+ new->items = NULL;
new->result = PQmakeEmptyPGresult(NULL, 0);
if (!new->result)
{
-/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/execute.c,v 1.34 2004/06/27 12:28:40 meskes Exp $ */
+/* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/execute.c,v 1.35 2004/06/30 15:01:56 meskes Exp $ */
/*
* The aim is to get a simpler inteface to the database routines.
free(str);
}
break;
+
+ case ECPGt_descriptor:
+ break;
default:
/* Not implemented yet */
PGresult *results;
PGnotify *notify;
struct variable *var;
+ int desc_counter = 0;
copiedquery = ECPGstrdup(stmt->command, stmt->lineno);
* so on.
*/
var = stmt->inlist;
+
while (var)
{
char *newcopy = NULL;
- const char *tobeinserted = NULL;
+ const char *tobeinserted;
char *p;
- bool malloced = FALSE;
- int hostvarl = 0;
-
- if (!ECPGstore_input(stmt, var, &tobeinserted, &malloced))
- return false;
+ bool malloced = FALSE;
+ int hostvarl = 0;
- /*
- * Now tobeinserted points to an area that is to be inserted at
- * the first %s
- */
- if (!(newcopy = (char *) ECPGalloc(strlen(copiedquery) + strlen(tobeinserted) + 1, stmt->lineno)))
- return false;
-
- strcpy(newcopy, copiedquery);
- if ((p = next_insert(newcopy + hostvarl)) == NULL)
+ tobeinserted = NULL;
+ /* A descriptor is a special case since it contains many variables but is listed only once. */
+ if (var->type == ECPGt_descriptor)
{
- /*
- * We have an argument but we dont have the matched up string
- * in the string
- */
- ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL);
- return false;
+ /* We create an additional variable list here, so the same logic applies. */
+ struct variable desc_inlist;
+ struct descriptor *desc;
+ struct descriptor_item *desc_item;
+ for (desc = all_descriptors; desc; desc = desc->next)
+ {
+ if (strcmp(var->pointer, desc->name) == 0)
+ break;
+ }
+
+ if (desc == NULL)
+ {
+ ECPGraise(stmt->lineno, ECPG_UNKNOWN_DESCRIPTOR, ECPG_SQLSTATE_INVALID_SQL_DESCRIPTOR_NAME, var->pointer);
+ return false;
+ }
+
+ desc_counter++;
+ for (desc_item = desc->items; desc_item; desc_item = desc_item->next)
+ {
+ if (desc_item->num == desc_counter)
+ {
+ desc_inlist.type = ECPGt_char;
+ desc_inlist.value = desc_item->data;
+ desc_inlist.pointer = &(desc_item->data);
+ desc_inlist.varcharsize = strlen(desc_item->data);
+ desc_inlist.arrsize = 1;
+ desc_inlist.offset = 0;
+ desc_inlist.ind_type = ECPGt_NO_INDICATOR;
+ desc_inlist.ind_value = desc_inlist.ind_pointer = NULL;
+ desc_inlist.ind_varcharsize = desc_inlist.ind_arrsize = desc_inlist.ind_offset = 0;
+
+ if (!ECPGstore_input(stmt, &desc_inlist, &tobeinserted, &malloced))
+ return false;
+
+ break;
+ }
+ }
+
+ if (!desc_item) /* no more entries found in descriptor */
+ desc_counter = 0;
}
else
{
- strcpy(p, tobeinserted);
- hostvarl = strlen(newcopy);
-
+ if (!ECPGstore_input(stmt, var, &tobeinserted, &malloced))
+ return false;
+ }
+ if (tobeinserted)
+ {
/*
- * The strange thing in the second argument is the rest of the
- * string from the old string
+ * Now tobeinserted points to an area that is to be inserted at
+ * the first %s
*/
- strcat(newcopy,
- copiedquery
- + (p - newcopy)
- + sizeof("?") - 1 /* don't count the '\0' */ );
- }
+ if (!(newcopy = (char *) ECPGalloc(strlen(copiedquery) + strlen(tobeinserted) + 1, stmt->lineno)))
+ return false;
- /*
- * Now everything is safely copied to the newcopy. Lets free the
- * oldcopy and let the copiedquery get the var->value from the
- * newcopy.
- */
- if (malloced)
- {
- ECPGfree((char *) tobeinserted);
- tobeinserted = NULL;
- }
+ strcpy(newcopy, copiedquery);
+ if ((p = next_insert(newcopy + hostvarl)) == NULL)
+ {
+ /*
+ * We have an argument but we dont have the matched up string
+ * in the string
+ */
+ ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL);
+ return false;
+ }
+ else
+ {
+ strcpy(p, tobeinserted);
+ hostvarl = strlen(newcopy);
+
+ /*
+ * The strange thing in the second argument is the rest of the
+ * string from the old string
+ */
+ strcat(newcopy,
+ copiedquery
+ + (p - newcopy)
+ + sizeof("?") - 1 /* don't count the '\0' */ );
+ }
- ECPGfree(copiedquery);
- copiedquery = newcopy;
+ /*
+ * Now everything is safely copied to the newcopy. Lets free the
+ * oldcopy and let the copiedquery get the var->value from the
+ * newcopy.
+ */
+ if (malloced)
+ {
+ ECPGfree((char *) tobeinserted);
+ tobeinserted = NULL;
+ }
- var = var->next;
+ ECPGfree(copiedquery);
+ copiedquery = newcopy;
+ }
+
+ if (desc_counter == 0)
+ var = var->next;
}
/* Check if there are unmatched things left. */
enum COMPAT_MODE
{
- ECPG_COMPAT_PGSQL = 0, ECPG_COMPAT_INFORMIX, ECPG_COMPAT_INFORMIX_SE
+ ECPG_COMPAT_PGSQL = 0, ECPG_COMPAT_INFORMIX, ECPG_COMPAT_INFORMIX_SE
};
#define INFORMIX_MODE(X) ((X) == ECPG_COMPAT_INFORMIX || (X) == ECPG_COMPAT_INFORMIX_SE)
enum ARRAY_TYPE
{
- ECPG_ARRAY_NOT_SET, ECPG_ARRAY_ARRAY, ECPG_ARRAY_VECTOR, ECPG_ARRAY_NONE
+ ECPG_ARRAY_NOT_SET, ECPG_ARRAY_ARRAY, ECPG_ARRAY_VECTOR, ECPG_ARRAY_NONE
};
/* Here are some methods used by the lib. */
/* Returns a pointer to a string containing a simple type name. */
-void ECPGadd_mem(void *ptr, int lineno);
-
-bool ECPGget_data(const PGresult *, int, int, int, enum ECPGttype type,
- enum ECPGttype, char *, char *, long, long, long, enum ARRAY_TYPE, enum COMPAT_MODE, bool);
-struct connection *ECPGget_connection(const char *);
-char *ECPGalloc(long, int);
-char *ECPGrealloc(void *, long, int);
-void ECPGfree(void *);
-bool ECPGinit(const struct connection *, const char *, const int);
-char *ECPGstrdup(const char *, int);
-const char *ECPGtype_name(enum ECPGttype);
-unsigned int ECPGDynamicType(Oid);
-void ECPGfree_auto_mem(void);
-void ECPGclear_auto_mem(void);
+void ECPGadd_mem (void *ptr, int lineno);
+
+bool ECPGget_data (const PGresult *, int, int, int, enum ECPGttype type,
+ enum ECPGttype, char *, char *, long, long, long,
+ enum ARRAY_TYPE, enum COMPAT_MODE, bool);
+struct connection *ECPGget_connection (const char *);
+char *ECPGalloc (long, int);
+char *ECPGrealloc (void *, long, int);
+void ECPGfree (void *);
+bool ECPGinit (const struct connection *, const char *, const int);
+char *ECPGstrdup (const char *, int);
+const char *ECPGtype_name (enum ECPGttype);
+unsigned int ECPGDynamicType (Oid);
+void ECPGfree_auto_mem (void);
+void ECPGclear_auto_mem (void);
+
+struct descriptor *ecpggetdescp (int, char *);
/* A generic varchar type. */
struct ECPGgeneric_varchar
{
- int len;
- char arr[1];
+ int len;
+ char arr[1];
};
/*
struct ECPGtype_information_cache
{
- struct ECPGtype_information_cache *next;
- int oid;
- bool isarray;
+ struct ECPGtype_information_cache *next;
+ int oid;
+ bool isarray;
};
/* structure to store one statement */
struct statement
{
- int lineno;
- char *command;
- struct connection *connection;
- enum COMPAT_MODE compat;
- bool force_indicator;
- struct variable *inlist;
- struct variable *outlist;
+ int lineno;
+ char *command;
+ struct connection *connection;
+ enum COMPAT_MODE compat;
+ bool force_indicator;
+ struct variable *inlist;
+ struct variable *outlist;
};
/* structure to store connections */
struct connection
{
- char *name;
- PGconn *connection;
- bool committed;
- int autocommit;
- struct ECPGtype_information_cache *cache_head;
- struct connection *next;
+ char *name;
+ PGconn *connection;
+ bool committed;
+ int autocommit;
+ struct ECPGtype_information_cache *cache_head;
+ struct connection *next;
};
/* structure to store descriptors */
struct descriptor
{
- char *name;
- PGresult *result;
- struct descriptor *next;
+ char *name;
+ PGresult *result;
+ struct descriptor *next;
+ int count;
+ struct descriptor_item *items;
+};
+
+extern struct descriptor *all_descriptors;
+
+struct descriptor_item
+{
+ int num;
+ char *data;
+ int indicator;
+ int length;
+ int precision;
+ int scale;
+ int type;
+ struct descriptor_item *next;
};
struct variable
{
- enum ECPGttype type;
- void *value;
- void *pointer;
- long varcharsize;
- long arrsize;
- long offset;
- enum ECPGttype ind_type;
- void *ind_value;
- void *ind_pointer;
- long ind_varcharsize;
- long ind_arrsize;
- long ind_offset;
- struct variable *next;
+ enum ECPGttype type;
+ void *value;
+ void *pointer;
+ long varcharsize;
+ long arrsize;
+ long offset;
+ enum ECPGttype ind_type;
+ void *ind_value;
+ void *ind_pointer;
+ long ind_varcharsize;
+ long ind_arrsize;
+ long ind_offset;
+ struct variable *next;
};
-PGresult **
- ECPGdescriptor_lvalue(int line, const char *descriptor);
+PGresult **ECPGdescriptor_lvalue (int line, const char *descriptor);
-bool ECPGstore_result(const PGresult *results, int act_field,
- const struct statement * stmt, struct variable * var);
+bool ECPGstore_result (const PGresult * results, int act_field,
+ const struct statement *stmt, struct variable *var);
/* SQLSTATE values generated or processed by ecpglib (intentionally
* not exported -- users should refer to the codes directly) */
#define ECPG_SQLSTATE_ECPG_INTERNAL_ERROR "YE000"
#define ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY "YE001"
-#endif /* _ECPG_LIB_EXTERN_H */
+#endif /* _ECPG_LIB_EXTERN_H */
void ECPGraise_backend(int line, PGresult *result, PGconn *conn, int compat);
bool ECPGget_desc_header(int, char *, int *);
bool ECPGget_desc(int, char *, int,...);
+bool ECPGset_desc_header(int, char *, int *);
+bool ECPGset_desc(int, char *, int,...);
void ECPGset_noind_null(enum ECPGttype, void *);
bool ECPGis_noind_null(enum ECPGttype, void *);
fputs(name, yyout);
break;
default:
- snprintf(errortext, sizeof errortext, "variable %s: numeric type needed"
- ,name);
- mmerror(PARSE_ERROR, ET_ERROR, errortext);
+ mmerror(PARSE_ERROR, ET_ERROR, "variable %s: numeric type needed", name);
break;
}
}
}
}
}
- snprintf(errortext, sizeof errortext, "unknown descriptor %s", name);
- mmerror(PARSE_ERROR, ET_WARNING, errortext);
+ mmerror(PARSE_ERROR, ET_WARNING, "unknown descriptor %s", name);
}
struct descriptor
return i;
}
}
- snprintf(errortext, sizeof errortext, "unknown descriptor %s", name);
- mmerror(PARSE_ERROR, ET_WARNING, errortext);
+ mmerror(PARSE_ERROR, ET_WARNING, "unknown descriptor %s", name);
return NULL;
}
{
struct assignment *results;
- fprintf(yyout, "{ ECPGget_desc_header(%d, %s, &(", yylineno, desc_name);
+ fprintf(yyout, "{ ECPGget_desc_header(__LINE__, %s, &(", desc_name);
for (results = assignments; results != NULL; results = results->next)
{
if (results->value == ECPGd_count)
ECPGnumeric_lvalue(yyout, results->variable);
else
- {
- snprintf(errortext, sizeof errortext, "unknown descriptor header item '%d'", results->value);
- mmerror(PARSE_ERROR, ET_WARNING, errortext);
- }
+ mmerror(PARSE_ERROR, ET_WARNING, "unknown descriptor header item '%d'", results->value);
}
drop_assignments();
{
struct assignment *results;
- fprintf(yyout, "{ ECPGget_desc(%d, %s, %s,", yylineno, desc_name, index);
+ fprintf(yyout, "{ ECPGget_desc(__LINE__, %s, %s,", desc_name, index);
for (results = assignments; results != NULL; results = results->next)
{
const struct variable *v = find_variable(results->variable);
whenever_action(2 | 1);
}
+void
+output_set_descr_header(char *desc_name)
+{
+ struct assignment *results;
+
+ fprintf(yyout, "{ ECPGset_desc_header(__LINE__, %s, &(", desc_name);
+ for (results = assignments; results != NULL; results = results->next)
+ {
+ if (results->value == ECPGd_count)
+ ECPGnumeric_lvalue(yyout, results->variable);
+ else
+ mmerror(PARSE_ERROR, ET_WARNING, "unknown descriptor header item '%d'", results->value);
+ }
+
+ drop_assignments();
+ fprintf(yyout, "));\n");
+ whenever_action(3);
+}
+
+static const char *
+descriptor_item_name(enum ECPGdtype itemcode)
+{
+ switch (itemcode)
+ {
+ case ECPGd_cardinality:
+ return "CARDINALITY";
+ case ECPGd_count:
+ return "COUNT";
+ case ECPGd_data:
+ return "DATA";
+ case ECPGd_di_code:
+ return "DATETIME_INTERVAL_CODE";
+ case ECPGd_di_precision:
+ return "DATETIME_INTERVAL_PRECISION";
+ case ECPGd_indicator:
+ return "INDICATOR";
+ case ECPGd_key_member:
+ return "KEY_MEMBER";
+ case ECPGd_length:
+ return "LENGTH";
+ case ECPGd_name:
+ return "NAME";
+ case ECPGd_nullable:
+ return "NULLABLE";
+ case ECPGd_octet:
+ return "OCTET_LENGTH";
+ case ECPGd_precision:
+ return "PRECISION";
+ case ECPGd_ret_length:
+ return "RETURNED_LENGTH";
+ case ECPGd_ret_octet:
+ return "RETURNED_OCTET_LENGTH";
+ case ECPGd_scale:
+ return "SCALE";
+ case ECPGd_type:
+ return "TYPE";
+ default:
+ return NULL;
+ }
+}
+
+void
+output_set_descr(char *desc_name, char *index)
+{
+ struct assignment *results;
+
+ fprintf(yyout, "{ ECPGset_desc(__LINE__, %s, %s,", desc_name, index);
+ for (results = assignments; results != NULL; results = results->next)
+ {
+ const struct variable *v = find_variable(results->variable);
+
+ switch (results->value)
+ {
+ case ECPGd_cardinality:
+ case ECPGd_di_code:
+ case ECPGd_di_precision:
+ case ECPGd_precision:
+ case ECPGd_scale:
+ mmerror(PARSE_ERROR, ET_FATAL, "descriptor item %s is not implemented",
+ descriptor_item_name(results->value));
+ break;
+
+ case ECPGd_key_member:
+ case ECPGd_name:
+ case ECPGd_nullable:
+ case ECPGd_octet:
+ case ECPGd_ret_length:
+ case ECPGd_ret_octet:
+ mmerror(PARSE_ERROR, ET_FATAL, "descriptor item %s cannot be set",
+ descriptor_item_name(results->value));
+ break;
+
+ case ECPGd_data:
+ case ECPGd_indicator:
+ case ECPGd_length:
+ case ECPGd_type:
+ fprintf(yyout, "%s,", get_dtype(results->value));
+ ECPGdump_a_type(yyout, v->name, v->type, NULL, NULL, NULL, NULL, make_str("0"), NULL, NULL);
+ break;
+
+ default:
+ ;
+ }
+ }
+ drop_assignments();
+ fputs("ECPGd_EODT);\n", yyout);
+
+ whenever_action(2 | 1);
+}
+
/* I consider dynamic allocation overkill since at most two descriptor
variables are possible per statement. (input and output descriptor)
And descriptors are no normal variables, so they don't belong into
descriptor_variable(const char *name, int input)
{
static char descriptor_names[2][MAX_DESCRIPTOR_NAMELEN];
- static const struct ECPGtype descriptor_type =
- {ECPGt_descriptor, 0};
- static const struct variable varspace[2] =
- {{descriptor_names[0], (struct ECPGtype *) & descriptor_type, 0, NULL},
- {descriptor_names[1], (struct ECPGtype *) & descriptor_type, 0, NULL}
+ static const struct ECPGtype descriptor_type = { ECPGt_descriptor, 0 };
+ static const struct variable varspace[2] = {
+ { descriptor_names[0], (struct ECPGtype *) & descriptor_type, 0, NULL },
+ { descriptor_names[1], (struct ECPGtype *) & descriptor_type, 0, NULL }
};
strncpy(descriptor_names[input], name, MAX_DESCRIPTOR_NAMELEN);
extern char *connection;
extern char *input_filename;
extern char *yytext,
- *token_start,
- errortext[128];
+ *token_start;
#ifdef YYDEBUG
extern int yydebug;
extern void yyerror(char *);
extern void *mm_alloc(size_t), *mm_realloc(void *, size_t);
extern char *mm_strdup(const char *);
-extern void mmerror(int, enum errortype, char *);
+extern void mmerror(int, enum errortype, char *, ...);
extern ScanKeyword *ScanECPGKeywordLookup(char *);
extern ScanKeyword *ScanCKeywordLookup(char *);
extern void output_get_descr_header(char *);
extern void output_get_descr(char *, char *);
+extern void output_set_descr_header(char *);
+extern void output_set_descr(char *, char *);
extern void push_assignment(char *, enum ECPGdtype);
extern struct variable *find_variable(char *);
extern void whenever_action(int);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/pgc.l,v 1.128 2004/05/05 15:03:04 meskes Exp $
+ * $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/pgc.l,v 1.129 2004/06/30 15:01:57 meskes Exp $
*
*-------------------------------------------------------------------------
*/
}
}
if (!yyin)
- {
- snprintf(errortext, sizeof(errortext), "Cannot open include file %s in line %d\n", yytext, yylineno);
- mmerror(NO_INCLUDE_FILE, ET_FATAL, errortext);
- }
+ mmerror(NO_INCLUDE_FILE, ET_FATAL, "Cannot open include file %s in line %d\n", yytext, yylineno);
input_filename = mm_strdup(inc_file);
yy_switch_to_buffer(yy_create_buffer(yyin,YY_BUF_SIZE ));
-/* $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/preproc.y,v 1.289 2004/06/27 12:28:42 meskes Exp $ */
+/* $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/preproc.y,v 1.290 2004/06/30 15:01:57 meskes Exp $ */
/* Copyright comment */
%{
int struct_level = 0;
int braces_open; /* brace level counter */
int ecpg_informix_var = 0;
-char errortext[128];
char *connection = NULL;
char *input_filename = NULL;
* Handle parsing errors and warnings
*/
void
-mmerror(int error_code, enum errortype type, char * error)
+mmerror(int error_code, enum errortype type, char * error, ...)
{
+ va_list ap;
+
+ fprintf(stderr, "%s:%d: ", input_filename, yylineno);
+
+ switch(type)
+ {
+ case ET_WARNING:
+ fprintf(stderr, "WARNING: ");
+ break;
+ case ET_ERROR:
+ case ET_FATAL:
+ fprintf(stderr, "ERROR: ");
+ break;
+ }
+
+ va_start(ap, error);
+ vfprintf(stderr, error, ap);
+ va_end(ap);
+
+ fprintf(stderr, "\n");
+
switch(type)
{
case ET_WARNING:
- fprintf(stderr, "%s:%d: WARNING: %s\n", input_filename, yylineno, error);
break;
case ET_ERROR:
- fprintf(stderr, "%s:%d: ERROR: %s\n", input_filename, yylineno, error);
ret_value = error_code;
break;
case ET_FATAL:
- fprintf(stderr, "%s:%d: ERROR: %s\n", input_filename, yylineno, error);
exit(error_code);
}
}
if (ptr == NULL)
{
- snprintf(errortext, sizeof(errortext), "trying to access an undeclared cursor %s\n", name);
- mmerror(PARSE_ERROR, ET_ERROR, errortext);
+ mmerror(PARSE_ERROR, ET_ERROR, "trying to access an undeclared cursor %s\n", name);
return NULL;
}
if (insert)
%type <str> col_name_keyword func_name_keyword precision opt_scale
%type <str> ECPGTypeName using_list ECPGColLabelCommon UsingConst
%type <str> inf_val_list inf_col_list using_descriptor into_descriptor
-%type <str> ecpg_into_using prepared_name struct_union_type_with_symbol
+%type <str> prepared_name struct_union_type_with_symbol
%type <str> ECPGunreserved ECPGunreserved_interval cvariable
%type <str> AlterOwnerStmt OptTableSpaceOwner CreateTableSpaceStmt
-%type <str> DropTableSpaceStmt indirection indirection_el
+%type <str> DropTableSpaceStmt indirection indirection_el ECPGSetDescriptorHeader
%type <struct_union> s_struct_union_symbol
-%type <descriptor> ECPGGetDescriptor
+%type <descriptor> ECPGGetDescriptor ECPGSetDescriptor
%type <type_enum> simple_type signed_type unsigned_type
whenever_action(2);
free($1);
}
+ | ECPGSetDescriptor
+ {
+ lookup_descriptor($1.name, connection);
+ output_set_descr($1.name, $1.str);
+ free($1.name);
+ free($1.str);
+ }
+ | ECPGSetDescriptorHeader
+ {
+ lookup_descriptor($1, connection);
+ output_set_descr_header($1);
+ free($1);
+ }
| ECPGTypedef
{
if (connection)
* embedded SQL implementations. So we accept their syntax as well and
* translate it to the PGSQL syntax. */
-FetchStmt: FETCH fetch_direction from_in name ecpg_into_using
+FetchStmt: FETCH fetch_direction from_in name ecpg_into
{
add_additional_variables($4, false);
$$ = cat_str(4, make_str("fetch"), $2, $3, $4);
}
- | FETCH fetch_direction name ecpg_into_using
+ | FETCH fetch_direction name ecpg_into
{
add_additional_variables($3, false);
$$ = cat_str(4, make_str("fetch"), $2, make_str("from"), $3);
}
- | FETCH from_in name ecpg_into_using
+ | FETCH from_in name ecpg_into
{
add_additional_variables($3, false);
$$ = cat_str(3, make_str("fetch"), $2, $3);
}
- | FETCH name ecpg_into_using
+ | FETCH name ecpg_into
{
add_additional_variables($2, false);
$$ = cat2_str(make_str("fetch"), $2);
for (ptr = cur; ptr != NULL; ptr = ptr->next)
{
if (strcmp($2, ptr->name) == 0)
- {
- /* re-definition is a bug */
- snprintf(errortext, sizeof(errortext), "cursor %s already defined", $2);
- mmerror(PARSE_ERROR, ET_ERROR, errortext);
- }
+ /* re-definition is a bug */
+ mmerror(PARSE_ERROR, ET_ERROR, "cursor %s already defined", $2);
}
this = (struct cursor *) mm_alloc(sizeof(struct cursor));
FoundInto = 1;
$$= cat2_str(make_str("into"), $2);
}
- | ecpg_into_using { $$ = EMPTY; }
+ | ecpg_into { $$ = EMPTY; }
| /*EMPTY*/ { $$ = EMPTY; }
;
{
/* old style: dbname[@server][:port] */
if (strlen($2) > 0 && *($2) != '@')
- {
- snprintf(errortext, sizeof(errortext),
- "Expected '@', found '%s'", $2);
- mmerror(PARSE_ERROR, ET_ERROR, errortext);
- }
+ mmerror(PARSE_ERROR, ET_ERROR, "Expected '@', found '%s'", $2);
$$ = make3_str(make_str("\""), make3_str($1, $2, $3), make_str("\""));
}
{
/* new style: <tcp|unix>:postgresql://server[:port][/dbname] */
if (strncmp($1, "unix:postgresql", strlen("unix:postgresql")) != 0 && strncmp($1, "tcp:postgresql", strlen("tcp:postgresql")) != 0)
- {
- snprintf(errortext, sizeof(errortext), "only protocols 'tcp' and 'unix' and database type 'postgresql' are supported");
- mmerror(PARSE_ERROR, ET_ERROR, errortext);
- }
+ mmerror(PARSE_ERROR, ET_ERROR, "only protocols 'tcp' and 'unix' and database type 'postgresql' are supported");
if (strncmp($3, "//", strlen("//")) != 0)
- {
- snprintf(errortext, sizeof(errortext), "Expected '://', found '%s'", $3);
- mmerror(PARSE_ERROR, ET_ERROR, errortext);
- }
+ mmerror(PARSE_ERROR, ET_ERROR, "Expected '://', found '%s'", $3);
if (strncmp($1, "unix", strlen("unix")) == 0 &&
strncmp($3 + strlen("//"), "localhost", strlen("localhost")) != 0 &&
strncmp($3 + strlen("//"), "127.0.0.1", strlen("127.0.0.1")) != 0)
- {
- snprintf(errortext, sizeof(errortext), "unix domain sockets only work on 'localhost' but not on '%9.9s'", $3 + strlen("//"));
- mmerror(PARSE_ERROR, ET_ERROR, errortext);
- }
+ mmerror(PARSE_ERROR, ET_ERROR, "unix domain sockets only work on 'localhost' but not on '%9.9s'", $3 + strlen("//"));
$$ = make3_str(make3_str(make_str("\""), $1, make_str(":")), $3, make3_str(make3_str($4, make_str("/"), $6), $7, make_str("\"")));
}
db_prefix: ident cvariable
{
if (strcmp($2, "postgresql") != 0 && strcmp($2, "postgres") != 0)
- {
- snprintf(errortext, sizeof(errortext), "Expected 'postgresql', found '%s'", $2);
- mmerror(PARSE_ERROR, ET_ERROR, errortext);
- }
+ mmerror(PARSE_ERROR, ET_ERROR, "Expected 'postgresql', found '%s'", $2);
if (strcmp($1, "tcp") != 0 && strcmp($1, "unix") != 0)
- {
- snprintf(errortext, sizeof(errortext), "Illegal connection type %s", $1);
- mmerror(PARSE_ERROR, ET_ERROR, errortext);
- }
+ mmerror(PARSE_ERROR, ET_ERROR, "Illegal connection type %s", $1);
$$ = make3_str($1, make_str(":"), $2);
}
server: Op server_name
{
if (strcmp($1, "@") != 0 && strcmp($1, "//") != 0)
- {
- snprintf(errortext, sizeof(errortext), "Expected '@' or '://', found '%s'", $1);
- mmerror(PARSE_ERROR, ET_ERROR, errortext);
- }
+ mmerror(PARSE_ERROR, ET_ERROR, "Expected '@' or '://', found '%s'", $1);
$$ = make2_str($1, $2);
}
mmerror(PARSE_ERROR, ET_ERROR, "incomplete statement");
if (strcmp($1, "?") != 0)
- {
- snprintf(errortext, sizeof(errortext), "unrecognised token '%s'", $1);
- mmerror(PARSE_ERROR, ET_ERROR, errortext);
- }
+ mmerror(PARSE_ERROR, ET_ERROR, "unrecognised token '%s'", $1);
$$ = make2_str(make_str("?"), $2);
}
for (ptr = cur; ptr != NULL; ptr = ptr->next)
{
if (strcmp($2, ptr->name) == 0)
- {
- /* re-definition is a bug */
- snprintf(errortext, sizeof(errortext), "cursor %s already defined", $2);
- mmerror(PARSE_ERROR, ET_ERROR, errortext);
- }
+ /* re-definition is a bug */
+ mmerror(PARSE_ERROR, ET_ERROR, "cursor %s already defined", $2);
}
this = (struct cursor *) mm_alloc(sizeof(struct cursor));
for (ptr = types; ptr != NULL; ptr = ptr->next)
{
if (strcmp($5, ptr->name) == 0)
- {
/* re-definition is a bug */
- snprintf(errortext, sizeof(errortext), "Type %s already defined", $5);
- mmerror(PARSE_ERROR, ET_ERROR, errortext);
- }
+ mmerror(PARSE_ERROR, ET_ERROR, "Type %s already defined", $5);
}
adjust_array($3.type_enum, &dimension, &length, $3.type_dimension, $3.type_index, *$4?1:0, true);
for (ptr = types; ptr != NULL; ptr = ptr->next)
{
if (strcmp(su_type.type_str, ptr->name) == 0)
- {
/* re-definition is a bug */
- snprintf(errortext, sizeof(errortext), "Type %s already defined", su_type.type_str);
- mmerror(PARSE_ERROR, ET_ERROR, errortext);
- }
+ mmerror(PARSE_ERROR, ET_ERROR, "Type %s already defined", su_type.type_str);
}
this = (struct typedefs *) mm_alloc(sizeof(struct typedefs));
;
ecpg_using: USING using_list { $$ = EMPTY; }
+ | using_descriptor { $$ = $1; }
;
using_descriptor: USING opt_sql SQL_DESCRIPTOR quoted_ident_stringvar
{
- add_variable_to_head(&argsresult, descriptor_variable($4,0), &no_indicator);
+ add_variable_to_head(&argsinsert, descriptor_variable($4,0), &no_indicator);
$$ = EMPTY;
}
;
into_descriptor: INTO opt_sql SQL_DESCRIPTOR quoted_ident_stringvar
{
- add_variable_to_head(&argsresult, descriptor_variable($4,0), &no_indicator);
+ add_variable_to_head(&argsresult, descriptor_variable($4,1), &no_indicator);
$$ = EMPTY;
}
;
opt_sql: /*EMPTY*/ | SQL_SQL;
-ecpg_into_using: ecpg_into { $$ = EMPTY; }
- | using_descriptor { $$ = $1; }
- ;
-
ecpg_into: INTO into_list { $$ = EMPTY; }
| into_descriptor { $$ = $1; }
;
/*
* dynamic SQL: descriptor based access
* written by Christof Petig <christof.petig@wtal.de>
+ * and Peter Eisentraut <peter.eisentraut@credativ.de>
*/
+/*
+ * allocate a descriptor
+ */
+ECPGAllocateDescr: SQL_ALLOCATE SQL_DESCRIPTOR quoted_ident_stringvar
+ {
+ add_descriptor($3,connection);
+ $$ = $3;
+ };
+
+
/*
* deallocate a descriptor
*/
;
/*
- * allocate a descriptor
+ * manipulate a descriptor header
*/
-ECPGAllocateDescr: SQL_ALLOCATE SQL_DESCRIPTOR quoted_ident_stringvar
- {
- add_descriptor($3,connection);
- $$ = $3;
- };
-/*
- * read from descriptor
- */
+ECPGGetDescriptorHeader: GET SQL_DESCRIPTOR quoted_ident_stringvar ECPGGetDescHeaderItems
+ { $$ = $3; }
+ ;
-ECPGGetDescHeaderItem: cvariable '=' desc_header_item
+ECPGGetDescHeaderItems: ECPGGetDescHeaderItem
+ | ECPGGetDescHeaderItems ',' ECPGGetDescHeaderItem
+ ;
+
+ECPGGetDescHeaderItem: CVARIABLE '=' desc_header_item
{ push_assignment($1, $3); }
;
-desc_header_item: SQL_COUNT { $$ = ECPGd_count; }
+
+ECPGSetDescriptorHeader: SET SQL_DESCRIPTOR quoted_ident_stringvar ECPGSetDescHeaderItems
+ { $$ = $3; }
+
+ECPGSetDescHeaderItems: ECPGSetDescHeaderItem
+ | ECPGSetDescHeaderItems ',' ECPGSetDescHeaderItem
+ ;
+
+ECPGSetDescHeaderItem: desc_header_item '=' CVARIABLE
+ { push_assignment($3, $1); }
;
-ECPGGetDescItem: cvariable '=' descriptor_item { push_assignment($1, $3); };
-descriptor_item: SQL_CARDINALITY { $$ = ECPGd_cardinality; }
- | SQL_DATA { $$ = ECPGd_data; }
- | SQL_DATETIME_INTERVAL_CODE { $$ = ECPGd_di_code; }
- | SQL_DATETIME_INTERVAL_PRECISION { $$ = ECPGd_di_precision; }
- | SQL_INDICATOR { $$ = ECPGd_indicator; }
- | SQL_KEY_MEMBER { $$ = ECPGd_key_member; }
- | SQL_LENGTH { $$ = ECPGd_length; }
- | SQL_NAME { $$ = ECPGd_name; }
- | SQL_NULLABLE { $$ = ECPGd_nullable; }
- | SQL_OCTET_LENGTH { $$ = ECPGd_octet; }
- | PRECISION { $$ = ECPGd_precision; }
- | SQL_RETURNED_LENGTH { $$ = ECPGd_length; }
- | SQL_RETURNED_OCTET_LENGTH { $$ = ECPGd_ret_octet; }
- | SQL_SCALE { $$ = ECPGd_scale; }
- | TYPE_P { $$ = ECPGd_type; }
+desc_header_item: SQL_COUNT { $$ = ECPGd_count; }
;
-ECPGGetDescHeaderItems: ECPGGetDescHeaderItem
- | ECPGGetDescHeaderItems ',' ECPGGetDescHeaderItem
+/*
+ * manipulate a descriptor
+ */
+
+ECPGGetDescriptor: GET SQL_DESCRIPTOR quoted_ident_stringvar SQL_VALUE CVARIABLE ECPGGetDescItems
+ { $$.str = $5; $$.name = $3; }
+ | GET SQL_DESCRIPTOR quoted_ident_stringvar SQL_VALUE Iconst ECPGGetDescItems
+ { $$.str = $5; $$.name = $3; }
;
ECPGGetDescItems: ECPGGetDescItem
| ECPGGetDescItems ',' ECPGGetDescItem
;
-ECPGGetDescriptorHeader: GET SQL_DESCRIPTOR quoted_ident_stringvar
- ECPGGetDescHeaderItems
- { $$ = $3; }
- ;
+ECPGGetDescItem: CVARIABLE '=' descriptor_item { push_assignment($1, $3); };
+
-ECPGGetDescriptor: GET SQL_DESCRIPTOR quoted_ident_stringvar SQL_VALUE cvariable ECPGGetDescItems
+ECPGSetDescriptor: SET SQL_DESCRIPTOR quoted_ident_stringvar SQL_VALUE CVARIABLE ECPGSetDescItems
{ $$.str = $5; $$.name = $3; }
- | GET SQL_DESCRIPTOR quoted_ident_stringvar SQL_VALUE Iconst ECPGGetDescItems
+ | SET SQL_DESCRIPTOR quoted_ident_stringvar SQL_VALUE Iconst ECPGSetDescItems
{ $$.str = $5; $$.name = $3; }
;
+ECPGSetDescItems: ECPGSetDescItem
+ | ECPGSetDescItems ',' ECPGSetDescItem
+ ;
+
+ECPGSetDescItem: descriptor_item '=' CVARIABLE { push_assignment($3, $1); };
+
+
+descriptor_item: SQL_CARDINALITY { $$ = ECPGd_cardinality; }
+ | SQL_DATA { $$ = ECPGd_data; }
+ | SQL_DATETIME_INTERVAL_CODE { $$ = ECPGd_di_code; }
+ | SQL_DATETIME_INTERVAL_PRECISION { $$ = ECPGd_di_precision; }
+ | SQL_INDICATOR { $$ = ECPGd_indicator; }
+ | SQL_KEY_MEMBER { $$ = ECPGd_key_member; }
+ | SQL_LENGTH { $$ = ECPGd_length; }
+ | SQL_NAME { $$ = ECPGd_name; }
+ | SQL_NULLABLE { $$ = ECPGd_nullable; }
+ | SQL_OCTET_LENGTH { $$ = ECPGd_octet; }
+ | PRECISION { $$ = ECPGd_precision; }
+ | SQL_RETURNED_LENGTH { $$ = ECPGd_length; }
+ | SQL_RETURNED_OCTET_LENGTH { $$ = ECPGd_ret_octet; }
+ | SQL_SCALE { $$ = ECPGd_scale; }
+ | TYPE_P { $$ = ECPGd_type; }
+ ;
+
+
/*
* for compatibility with ORACLE we will also allow the keyword RELEASE
* after a transaction statement to disconnect from the database.
for (ptr = types; ptr != NULL; ptr = ptr->next)
{
if (strcmp($3, ptr->name) == 0)
- {
/* re-definition is a bug */
- snprintf(errortext, sizeof(errortext), "Type %s already defined", $3);
- mmerror(PARSE_ERROR, ET_ERROR, errortext);
- }
+ mmerror(PARSE_ERROR, ET_ERROR, "Type %s already defined", $3);
}
adjust_array($5.type_enum, &dimension, &length, $5.type_dimension, $5.type_index, *$7?1:0, false);
return ("ECPGt_timestamp");
break;
default:
- sprintf(errortext, "illegal variable type %d\n", type);
- yyerror(errortext);
+ mmerror(PARSE_ERROR, ET_ERROR, "illegal variable type %d\n", type);
}
return NULL;
ECPGfree_struct_member(type->u.members);
break;
default:
- sprintf(errortext, "illegal variable type %d\n", type->type);
- yyerror(errortext);
+ mmerror(PARSE_ERROR, ET_ERROR, "illegal variable type %d\n", type->type);
break;
}
}
case ECPGd_cardinality:
return ("ECPGd_cardinality");
default:
- sprintf(errortext, "illegal descriptor item %d\n", type);
- yyerror(errortext);
+ mmerror(PARSE_ERROR, ET_ERROR, "illegal descriptor item %d\n", type);
}
return NULL;
case '\0': /* found the end, but this time it has to
* be an array element */
if (members->type->type != ECPGt_array)
- {
- snprintf(errortext, sizeof(errortext), "incorrectly formed variable %s", name);
- mmerror(PARSE_ERROR, ET_FATAL, errortext);
- }
+ mmerror(PARSE_ERROR, ET_FATAL, "incorrectly formed variable %s", name);
switch (members->type->u.element->type)
{
return (find_struct_member(name, end, members->type->u.members, brace_level));
break;
default:
- snprintf(errortext, sizeof(errortext), "incorrectly formed variable %s", name);
- mmerror(PARSE_ERROR, ET_FATAL, errortext);
+ mmerror(PARSE_ERROR, ET_FATAL, "incorrectly formed variable %s", name);
break;
}
}
if (c == '-')
{
if (p->type->type != ECPGt_array)
- {
- snprintf(errortext, sizeof(errortext), "variable %s is not a pointer", name);
- mmerror(PARSE_ERROR, ET_FATAL, errortext);
- }
+ mmerror(PARSE_ERROR, ET_FATAL, "variable %s is not a pointer", name);
if (p->type->u.element->type != ECPGt_struct && p->type->u.element->type != ECPGt_union)
- {
- snprintf(errortext, sizeof(errortext), "variable %s is not a pointer to a structure or a union", name);
- mmerror(PARSE_ERROR, ET_FATAL, errortext);
- }
+ mmerror(PARSE_ERROR, ET_FATAL, "variable %s is not a pointer to a structure or a union", name);
/* restore the name, we will need it later */
*next = c;
if (next == end)
{
if (p->type->type != ECPGt_struct && p->type->type != ECPGt_union)
- {
- snprintf(errortext, sizeof(errortext), "variable %s is neither a structure nor a union", name);
- mmerror(PARSE_ERROR, ET_FATAL, errortext);
- }
+ mmerror(PARSE_ERROR, ET_FATAL, "variable %s is neither a structure nor a union", name);
/* restore the name, we will need it later */
*next = c;
else
{
if (p->type->type != ECPGt_array)
- {
- snprintf(errortext, sizeof(errortext), "variable %s is not an array", name);
- mmerror(PARSE_ERROR, ET_FATAL, errortext);
- }
+ mmerror(PARSE_ERROR, ET_FATAL, "variable %s is not an array", name);
if (p->type->u.element->type != ECPGt_struct && p->type->u.element->type != ECPGt_union)
- {
- snprintf(errortext, sizeof(errortext), "variable %s is not a pointer to a structure or a union", name);
- mmerror(PARSE_ERROR, ET_FATAL, errortext);
- }
+ mmerror(PARSE_ERROR, ET_FATAL, "variable %s is not a pointer to a structure or a union", name);
/* restore the name, we will need it later */
*next = c;
*next = '\0';
p = find_simple(name);
if (p == NULL)
- {
- snprintf(errortext, sizeof(errortext), "The variable %s is not declared", name);
- mmerror(PARSE_ERROR, ET_FATAL, errortext);
- }
+ mmerror(PARSE_ERROR, ET_FATAL, "The variable %s is not declared", name);
+
*next = c;
switch (p->type->u.element->type)
{
p = find_simple(name);
if (p == NULL)
- {
- snprintf(errortext, sizeof(errortext), "The variable %s is not declared", name);
- mmerror(PARSE_ERROR, ET_FATAL, errortext);
- }
+ mmerror(PARSE_ERROR, ET_FATAL, "The variable %s is not declared", name);
return (p);
}
for (this = types; this && strcmp(this->name, name); this = this->next);
if (!this)
- {
- snprintf(errortext, sizeof(errortext), "invalid datatype '%s'", name);
- mmerror(PARSE_ERROR, ET_FATAL, errortext);
- }
+ mmerror(PARSE_ERROR, ET_FATAL, "invalid datatype '%s'", name);
return (this);
}
}
if (pointer_len > 2)
- {
- snprintf(errortext, sizeof(errortext), "No multilevel (more than 2) pointer supported %d", pointer_len);
- mmerror(PARSE_ERROR, ET_FATAL, errortext);
- }
+ mmerror(PARSE_ERROR, ET_FATAL, "No multilevel (more than 2) pointer supported %d", pointer_len);
if (pointer_len > 1 && type_enum != ECPGt_char && type_enum != ECPGt_unsigned_char)
mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");
--- /dev/null
+EXEC SQL WHENEVER SQLERROR SQLPRINT;
+
+int
+main()
+{
+ EXEC SQL BEGIN DECLARE SECTION;
+ char *stmt1 = "INSERT INTO test1 VALUES (?, ?)";
+ char *stmt2 = "SELECT * from test1 where a = ? and b = ?";
+
+ int val1 = 1;
+ char val2[] = "one", val2output[] = "AAA";
+ int val1output = 2, val2i = 0;
+ int val2null = 1;
+ EXEC SQL END DECLARE SECTION;
+ FILE *dbgs;
+
+ if ((dbgs = fopen("log", "w")) != NULL)
+ ECPGdebug(1, dbgs);
+
+ EXEC SQL ALLOCATE DESCRIPTOR indesc;
+ EXEC SQL ALLOCATE DESCRIPTOR outdesc;
+
+ EXEC SQL SET DESCRIPTOR indesc VALUE 1 DATA = :val1;
+ EXEC SQL SET DESCRIPTOR indesc VALUE 2 INDICATOR = :val2i, DATA = :val2;
+
+ EXEC SQL CONNECT TO mm;
+
+ EXEC SQL CREATE TABLE test1 (a int, b text);
+ EXEC SQL PREPARE foo1 FROM :stmt1;
+ EXEC SQL PREPARE foo2 FROM :stmt2;
+
+ EXEC SQL EXECUTE foo1 USING DESCRIPTOR indesc;
+
+ //EXEC SQL SET DESCRIPTOR indesc VALUE 1 DATA = 2;
+ EXEC SQL SET DESCRIPTOR indesc VALUE 1 DATA = :val1output;
+ EXEC SQL SET DESCRIPTOR indesc VALUE 2 INDICATOR = :val2null, DATA = :val2;
+
+ EXEC SQL EXECUTE foo1 USING DESCRIPTOR indesc;
+
+ EXEC SQL SET DESCRIPTOR indesc VALUE 1 DATA = :val1;
+ EXEC SQL SET DESCRIPTOR indesc VALUE 2 INDICATOR = :val2i, DATA = :val2;
+
+ EXEC SQL EXECUTE foo2 USING DESCRIPTOR indesc INTO DESCRIPTOR outdesc;
+
+ EXEC SQL GET DESCRIPTOR outdesc VALUE 1 :val2output = DATA;
+ printf("output = %s\n", val2output);
+
+ EXEC SQL DECLARE c CURSOR FOR foo2;
+ EXEC SQL OPEN c USING DESCRIPTOR indesc;
+
+ EXEC SQL FETCH next FROM c INTO :val1output, :val2output;
+ printf("val1=%d val2=%s\n", val1output, val2output);
+
+ EXEC SQL CLOSE c;
+
+ EXEC SQL SELECT * INTO :val1output, :val2output FROM test1 where a = 2;
+ printf("val1=%d val2=%s\n", val1output, val2output);
+
+ EXEC SQL DROP TABLE test1;
+ EXEC SQL DISCONNECT;
+
+ EXEC SQL DEALLOCATE DESCRIPTOR indesc;
+ EXEC SQL DEALLOCATE DESCRIPTOR outdesc;
+
+ if (dbgs != NULL)
+ fclose(dbgs);
+
+ return 0;
+}