/* src/interfaces/ecpg/preproc/ecpg.header */ /* Copyright comment */ %{ #include "postgres_fe.h" #include "preproc_extern.h" #include "preproc.h" #include "ecpg_config.h" #include /* silence -Wmissing-variable-declarations */ extern int base_yychar; extern int base_yynerrs; /* * The %name-prefix option below will make bison call base_yylex, but we * really want it to call filtered_base_yylex (see parser.c). */ #define base_yylex filtered_base_yylex /* * This is only here so the string gets into the POT. Bison uses it * internally. */ #define bison_gettext_dummy gettext_noop("syntax error") /* * Variables containing simple states. */ int struct_level = 0; int braces_open; /* brace level counter */ char *current_function; int ecpg_internal_var = 0; char *connection = NULL; char *input_filename = NULL; static int FoundInto = 0; static int initializer = 0; static int pacounter = 1; static struct this_type actual_type[STRUCT_DEPTH]; static char *actual_startline[STRUCT_DEPTH]; static int varchar_counter = 1; static int bytea_counter = 1; /* * We temporarily store struct members here while parsing struct declarations. * The struct_member_list (at a given nesting depth) is constructed while * scanning the fields within "struct { .... }", but we can't remove it upon * seeing the right brace. It's kept around and copied into the variables * or typedefs that follow, in order to handle cases like * "struct foo { ... } foovar1, foovar2;". We recycle the storage only * upon closing the current nesting level or starting the next struct * declaration within the same nesting level. * For cases like "struct foo foovar1, foovar2;", we copy the saved struct * field list for the typedef or struct tag into the struct_member_list * global variable, and then copy it again to the newly-declared variables. */ struct ECPGstruct_member *struct_member_list[STRUCT_DEPTH] = {NULL}; /* also store struct type so we can do a sizeof() later */ static char *ECPGstruct_sizeof = NULL; /* for forward declarations we have to store some data as well */ static char *forward_name = NULL; struct ECPGtype ecpg_no_indicator = {ECPGt_NO_INDICATOR, NULL, NULL, NULL, {NULL}, 0}; struct variable no_indicator = {"no_indicator", &ecpg_no_indicator, 0, NULL}; static struct ECPGtype ecpg_query = {ECPGt_char_variable, NULL, NULL, NULL, {NULL}, 0}; static bool check_declared_list(const char *name); static void update_connection(const char *newconn); /* * "Location tracking" support. We commandeer Bison's location tracking * mechanism to manage the output string for productions that ordinarily would * return a result. This allows the majority of those productions to * have default semantic actions, reducing the size of the parser, and also * greatly reducing its compilation time on some versions of clang. * * To do this, we make YYLTYPE be a pointer to a malloc'd string, and then * merge the location strings of the input tokens in the default YYLLOC * computation. Productions that are okay with the standard merge need not * do anything more; otherwise, they can override it by assigning to @$. */ #define YYLLOC_DEFAULT(Current, Rhs, N) yylloc_default(&(Current), Rhs, N) static void yylloc_default(YYLTYPE *target, YYLTYPE *rhs, int N) { if (N > 1) { /* Concatenate non-empty inputs with one space between them */ char *result, *ptr; size_t needed = 0; for (int i = 1; i <= N; i++) { size_t thislen = strlen(rhs[i]); if (needed > 0 && thislen > 0) needed++; needed += thislen; } result = (char *) loc_alloc(needed + 1); ptr = result; for (int i = 1; i <= N; i++) { size_t thislen = strlen(rhs[i]); if (ptr > result && thislen > 0) *ptr++ = ' '; memcpy(ptr, rhs[i], thislen); ptr += thislen; } *ptr = '\0'; *target = result; } else if (N == 1) { /* Just re-use the single input */ *target = rhs[1]; } else { /* No need to allocate any space */ *target = ""; } } /* and the rest */ static char * create_questionmarks(const char *name, bool array) { struct variable *p = find_variable(name); int count; char *result = ""; /* * In case we have a struct, we have to print as many "?" as there are * attributes in the struct * * An array is only allowed together with an element argument * * This is essentially only used for inserts, but using a struct as input * parameter is an error anywhere else so we don't have to worry here. */ if (p->type->type == ECPGt_struct || (array && p->type->type == ECPGt_array && p->type->u.element->type == ECPGt_struct)) { struct ECPGstruct_member *m; if (p->type->type == ECPGt_struct) m = p->type->u.members; else m = p->type->u.element->u.members; for (count = 0; m != NULL; m = m->next, count++); } else count = 1; for (; count > 0; count--) { char buf[32]; snprintf(buf, sizeof(buf), "$%d", pacounter++); result = cat_str(3, result, buf, " , "); } /* remove the trailing " ," */ result[strlen(result) - 3] = '\0'; return result; } static char * adjust_outofscope_cursor_vars(struct cursor *cur) { /* * Informix accepts DECLARE with variables that are out of scope when OPEN * is called. For instance you can DECLARE a cursor in one function, and * OPEN/FETCH/CLOSE it in another functions. This is very useful for e.g. * event-driver programming, but may also lead to dangerous programming. * The limitation when this is allowed and doesn't cause problems have to * be documented, like the allocated variables must not be realloc()'ed. * * We have to change the variables to our own struct and just store the * pointer instead of the variable. Do it only for local variables, not * for globals. */ char *result = ""; int insert; for (insert = 1; insert >= 0; insert--) { struct arguments *list; struct arguments *ptr; struct arguments *newlist = NULL; struct variable *newvar, *newind; list = (insert ? cur->argsinsert : cur->argsresult); for (ptr = list; ptr != NULL; ptr = ptr->next) { char var_text[20]; char *original_var; bool skip_set_var = false; bool var_ptr = false; /* change variable name to "ECPGget_var()" */ original_var = ptr->variable->name; snprintf(var_text, sizeof(var_text), "%d))", ecpg_internal_var); /* Don't emit ECPGset_var() calls for global variables */ if (ptr->variable->brace_level == 0) { newvar = ptr->variable; skip_set_var = true; } else if ((ptr->variable->type->type == ECPGt_char_variable) && (strncmp(ptr->variable->name, "ECPGprepared_statement", strlen("ECPGprepared_statement")) == 0)) { newvar = ptr->variable; skip_set_var = true; } else if ((ptr->variable->type->type != ECPGt_varchar && ptr->variable->type->type != ECPGt_char && ptr->variable->type->type != ECPGt_unsigned_char && ptr->variable->type->type != ECPGt_string && ptr->variable->type->type != ECPGt_bytea) && atoi(ptr->variable->type->size) > 1) { newvar = new_variable(cat_str(4, "(", ecpg_type_name(ptr->variable->type->u.element->type), " *)(ECPGget_var(", var_text), ECPGmake_array_type(ECPGmake_simple_type(ptr->variable->type->u.element->type, "1", ptr->variable->type->u.element->counter), ptr->variable->type->size), 0); } else if ((ptr->variable->type->type == ECPGt_varchar || ptr->variable->type->type == ECPGt_char || ptr->variable->type->type == ECPGt_unsigned_char || ptr->variable->type->type == ECPGt_string || ptr->variable->type->type == ECPGt_bytea) && atoi(ptr->variable->type->size) > 1) { newvar = new_variable(cat_str(4, "(", ecpg_type_name(ptr->variable->type->type), " *)(ECPGget_var(", var_text), ECPGmake_simple_type(ptr->variable->type->type, ptr->variable->type->size, ptr->variable->type->counter), 0); if (ptr->variable->type->type == ECPGt_varchar || ptr->variable->type->type == ECPGt_bytea) var_ptr = true; } else if (ptr->variable->type->type == ECPGt_struct || ptr->variable->type->type == ECPGt_union) { newvar = new_variable(cat_str(5, "(*(", ptr->variable->type->type_name, " *)(ECPGget_var(", var_text, ")"), ECPGmake_struct_type(ptr->variable->type->u.members, ptr->variable->type->type, ptr->variable->type->type_name, ptr->variable->type->struct_sizeof), 0); var_ptr = true; } else if (ptr->variable->type->type == ECPGt_array) { if (ptr->variable->type->u.element->type == ECPGt_struct || ptr->variable->type->u.element->type == ECPGt_union) { newvar = new_variable(cat_str(5, "(*(", ptr->variable->type->u.element->type_name, " *)(ECPGget_var(", var_text, ")"), ECPGmake_struct_type(ptr->variable->type->u.element->u.members, ptr->variable->type->u.element->type, ptr->variable->type->u.element->type_name, ptr->variable->type->u.element->struct_sizeof), 0); } else { newvar = new_variable(cat_str(4, "(", ecpg_type_name(ptr->variable->type->u.element->type), " *)(ECPGget_var(", var_text), ECPGmake_array_type(ECPGmake_simple_type(ptr->variable->type->u.element->type, ptr->variable->type->u.element->size, ptr->variable->type->u.element->counter), ptr->variable->type->size), 0); var_ptr = true; } } else { newvar = new_variable(cat_str(4, "*(", ecpg_type_name(ptr->variable->type->type), " *)(ECPGget_var(", var_text), ECPGmake_simple_type(ptr->variable->type->type, ptr->variable->type->size, ptr->variable->type->counter), 0); var_ptr = true; } /* * create call to "ECPGset_var(, , . * )" */ if (!skip_set_var) { snprintf(var_text, sizeof(var_text), "%d, %s", ecpg_internal_var++, var_ptr ? "&(" : "("); result = cat_str(5, result, "ECPGset_var(", var_text, original_var, "), __LINE__);\n"); } /* * now the indicator if there is one and it's not a global * variable */ if ((ptr->indicator->type->type == ECPGt_NO_INDICATOR) || (ptr->indicator->brace_level == 0)) { newind = ptr->indicator; } else { /* change variable name to "ECPGget_var()" */ original_var = ptr->indicator->name; snprintf(var_text, sizeof(var_text), "%d))", ecpg_internal_var); var_ptr = false; if (ptr->indicator->type->type == ECPGt_struct || ptr->indicator->type->type == ECPGt_union) { newind = new_variable(cat_str(5, "(*(", ptr->indicator->type->type_name, " *)(ECPGget_var(", var_text, ")"), ECPGmake_struct_type(ptr->indicator->type->u.members, ptr->indicator->type->type, ptr->indicator->type->type_name, ptr->indicator->type->struct_sizeof), 0); var_ptr = true; } else if (ptr->indicator->type->type == ECPGt_array) { if (ptr->indicator->type->u.element->type == ECPGt_struct || ptr->indicator->type->u.element->type == ECPGt_union) { newind = new_variable(cat_str(5, "(*(", ptr->indicator->type->u.element->type_name, " *)(ECPGget_var(", var_text, ")"), ECPGmake_struct_type(ptr->indicator->type->u.element->u.members, ptr->indicator->type->u.element->type, ptr->indicator->type->u.element->type_name, ptr->indicator->type->u.element->struct_sizeof), 0); } else { newind = new_variable(cat_str(4, "(", ecpg_type_name(ptr->indicator->type->u.element->type), " *)(ECPGget_var(", var_text), ECPGmake_array_type(ECPGmake_simple_type(ptr->indicator->type->u.element->type, ptr->indicator->type->u.element->size, ptr->indicator->type->u.element->counter), ptr->indicator->type->size), 0); var_ptr = true; } } else if (atoi(ptr->indicator->type->size) > 1) { newind = new_variable(cat_str(4, "(", ecpg_type_name(ptr->indicator->type->type), " *)(ECPGget_var(", var_text), ECPGmake_simple_type(ptr->indicator->type->type, ptr->indicator->type->size, ptr->variable->type->counter), 0); } else { newind = new_variable(cat_str(4, "*(", ecpg_type_name(ptr->indicator->type->type), " *)(ECPGget_var(", var_text), ECPGmake_simple_type(ptr->indicator->type->type, ptr->indicator->type->size, ptr->variable->type->counter), 0); var_ptr = true; } /* * create call to "ECPGset_var(, . )" */ snprintf(var_text, sizeof(var_text), "%d, %s", ecpg_internal_var++, var_ptr ? "&(" : "("); result = cat_str(5, result, "ECPGset_var(", var_text, original_var, "), __LINE__);\n"); } add_variable_to_tail(&newlist, newvar, newind); } if (insert) cur->argsinsert_oos = newlist; else cur->argsresult_oos = newlist; } return result; } /* This tests whether the cursor was declared and opened in the same function. */ #define SAMEFUNC(cur) \ ((cur->function == NULL) || \ (cur->function != NULL && current_function != NULL && \ strcmp(cur->function, current_function) == 0)) static struct cursor * add_additional_variables(const char *name, bool insert) { struct cursor *ptr; struct arguments *p; int (*strcmp_fn) (const char *, const char *) = ((name[0] == ':' || name[0] == '"') ? strcmp : pg_strcasecmp); for (ptr = cur; ptr != NULL; ptr = ptr->next) { if (strcmp_fn(ptr->name, name) == 0) break; } if (ptr == NULL) { mmerror(PARSE_ERROR, ET_ERROR, "cursor \"%s\" does not exist", name); return NULL; } if (insert) { /* * add all those input variables that were given earlier * * note that we have to append here but have to keep the existing * order */ for (p = (SAMEFUNC(ptr) ? ptr->argsinsert : ptr->argsinsert_oos); p; p = p->next) add_variable_to_tail(&argsinsert, p->variable, p->indicator); } /* add all those output variables that were given earlier */ for (p = (SAMEFUNC(ptr) ? ptr->argsresult : ptr->argsresult_oos); p; p = p->next) add_variable_to_tail(&argsresult, p->variable, p->indicator); return ptr; } static void add_typedef(const char *name, const char *dimension, const char *length, enum ECPGttype type_enum, const char *type_dimension, const char *type_index, int initializer, int array) { /* add entry to list */ struct typedefs *ptr, *this; if ((type_enum == ECPGt_struct || type_enum == ECPGt_union) && initializer == 1) mmerror(PARSE_ERROR, ET_ERROR, "initializer not allowed in type definition"); else if (INFORMIX_MODE && strcmp(name, "string") == 0) mmerror(PARSE_ERROR, ET_ERROR, "type name \"string\" is reserved in Informix mode"); else { for (ptr = types; ptr != NULL; ptr = ptr->next) { if (strcmp(name, ptr->name) == 0) /* re-definition is a bug */ mmerror(PARSE_ERROR, ET_ERROR, "type \"%s\" is already defined", name); } adjust_array(type_enum, &dimension, &length, type_dimension, type_index, array, true); this = (struct typedefs *) mm_alloc(sizeof(struct typedefs)); /* initial definition */ this->next = types; this->name = mm_strdup(name); this->brace_level = braces_open; this->type = (struct this_type *) mm_alloc(sizeof(struct this_type)); this->type->type_storage = NULL; this->type->type_enum = type_enum; this->type->type_str = mm_strdup(name); this->type->type_dimension = mm_strdup(dimension); /* dimension of array */ this->type->type_index = mm_strdup(length); /* length of string */ this->type->type_sizeof = ECPGstruct_sizeof ? mm_strdup(ECPGstruct_sizeof) : NULL; this->struct_member_list = (type_enum == ECPGt_struct || type_enum == ECPGt_union) ? ECPGstruct_member_dup(struct_member_list[struct_level]) : NULL; if (type_enum != ECPGt_varchar && type_enum != ECPGt_bytea && type_enum != ECPGt_char && type_enum != ECPGt_unsigned_char && type_enum != ECPGt_string && atoi(this->type->type_index) >= 0) mmerror(PARSE_ERROR, ET_ERROR, "multidimensional arrays for simple data types are not supported"); types = this; } } /* * check an SQL identifier is declared or not. * If it is already declared, the global variable * connection will be changed to the related connection. */ static bool check_declared_list(const char *name) { struct declared_list *ptr = NULL; for (ptr = g_declared_list; ptr != NULL; ptr = ptr->next) { if (!ptr->connection) continue; if (strcmp(name, ptr->name) == 0) { if (connection && strcmp(ptr->connection, connection) != 0) mmerror(PARSE_ERROR, ET_WARNING, "connection %s is overwritten with %s by DECLARE statement %s", connection, ptr->connection, name); update_connection(ptr->connection); return true; } } return false; } /* * If newconn isn't NULL, update the global "connection" variable to that; * otherwise do nothing. */ static void update_connection(const char *newconn) { if (newconn) { free(connection); connection = mm_strdup(newconn); } } %} %expect 0 %name-prefix="base_yy" %locations %union { double dval; char *str; int ival; struct when action; struct index index; int tagname; struct this_type type; enum ECPGttype type_enum; enum ECPGdtype dtype_enum; struct fetch_desc descriptor; struct su_symbol struct_union; struct prep prep; struct exec exec; struct describe describe; }