| /* src/interfaces/ecpg/preproc/ecpg.header */ |
| |
| /* Copyright comment */ |
| %{ |
| #include "postgres_fe.h" |
| |
| #include "preproc_extern.h" |
| #include "ecpg_config.h" |
| #include <unistd.h> |
| |
| /* Location tracking support --- simpler than bison's default */ |
| #define YYLLOC_DEFAULT(Current, Rhs, N) \ |
| do { \ |
| if (N) \ |
| (Current) = (Rhs)[1]; \ |
| else \ |
| (Current) = (Rhs)[0]; \ |
| } while (0) |
| |
| /* |
| * 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 char pacounter_buffer[sizeof(int) * CHAR_BIT * 10 / 3]; /* a rough guess at the size we need */ |
| static struct this_type actual_type[STRUCT_DEPTH]; |
| static char *actual_startline[STRUCT_DEPTH]; |
| static int varchar_counter = 1; |
| static int bytea_counter = 1; |
| |
| /* temporarily store struct members while creating the data structure */ |
| 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 void vmmerror(int error_code, enum errortype type, const char *error, va_list ap) pg_attribute_printf(3, 0); |
| |
| static bool check_declared_list(const char*); |
| |
| /* |
| * Handle parsing errors and warnings |
| */ |
| static void |
| vmmerror(int error_code, enum errortype type, const char *error, va_list ap) |
| { |
| /* localize the error message string */ |
| error = _(error); |
| |
| fprintf(stderr, "%s:%d: ", input_filename, base_yylineno); |
| |
| switch(type) |
| { |
| case ET_WARNING: |
| fprintf(stderr, _("WARNING: ")); |
| break; |
| case ET_ERROR: |
| fprintf(stderr, _("ERROR: ")); |
| break; |
| } |
| |
| vfprintf(stderr, error, ap); |
| |
| fprintf(stderr, "\n"); |
| |
| switch(type) |
| { |
| case ET_WARNING: |
| break; |
| case ET_ERROR: |
| ret_value = error_code; |
| break; |
| } |
| } |
| |
| void |
| mmerror(int error_code, enum errortype type, const char *error, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, error); |
| vmmerror(error_code, type, error, ap); |
| va_end(ap); |
| } |
| |
| void |
| mmfatal(int error_code, const char *error, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, error); |
| vmmerror(error_code, ET_ERROR, error, ap); |
| va_end(ap); |
| |
| if (base_yyin) |
| fclose(base_yyin); |
| if (base_yyout) |
| fclose(base_yyout); |
| |
| if (strcmp(output_filename, "-") != 0 && unlink(output_filename) != 0) |
| fprintf(stderr, _("could not remove output file \"%s\"\n"), output_filename); |
| exit(error_code); |
| } |
| |
| /* |
| * string concatenation |
| */ |
| |
| static char * |
| cat2_str(char *str1, char *str2) |
| { |
| char * res_str = (char *)mm_alloc(strlen(str1) + strlen(str2) + 2); |
| |
| strcpy(res_str, str1); |
| if (strlen(str1) != 0 && strlen(str2) != 0) |
| strcat(res_str, " "); |
| strcat(res_str, str2); |
| free(str1); |
| free(str2); |
| return res_str; |
| } |
| |
| static char * |
| cat_str(int count, ...) |
| { |
| va_list args; |
| int i; |
| char *res_str; |
| |
| va_start(args, count); |
| |
| res_str = va_arg(args, char *); |
| |
| /* now add all other strings */ |
| for (i = 1; i < count; i++) |
| res_str = cat2_str(res_str, va_arg(args, char *)); |
| |
| va_end(args); |
| |
| return res_str; |
| } |
| |
| static char * |
| make2_str(char *str1, char *str2) |
| { |
| char * res_str = (char *)mm_alloc(strlen(str1) + strlen(str2) + 1); |
| |
| strcpy(res_str, str1); |
| strcat(res_str, str2); |
| free(str1); |
| free(str2); |
| return res_str; |
| } |
| |
| static char * |
| make3_str(char *str1, char *str2, char *str3) |
| { |
| char * res_str = (char *)mm_alloc(strlen(str1) + strlen(str2) +strlen(str3) + 1); |
| |
| strcpy(res_str, str1); |
| strcat(res_str, str2); |
| strcat(res_str, str3); |
| free(str1); |
| free(str2); |
| free(str3); |
| return res_str; |
| } |
| |
| /* and the rest */ |
| static char * |
| make_name(void) |
| { |
| return mm_strdup(base_yytext); |
| } |
| |
| static char * |
| create_questionmarks(char *name, bool array) |
| { |
| struct variable *p = find_variable(name); |
| int count; |
| char *result = EMPTY; |
| |
| /* 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 --) |
| { |
| sprintf(pacounter_buffer, "$%d", pacounter++); |
| result = cat_str(3, result, mm_strdup(pacounter_buffer), mm_strdup(" , ")); |
| } |
| |
| /* removed 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 = EMPTY; |
| 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(<counter>)" */ |
| original_var = ptr->variable->name; |
| sprintf(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, mm_strdup("("), |
| mm_strdup(ecpg_type_name(ptr->variable->type->u.element->type)), |
| mm_strdup(" *)(ECPGget_var("), |
| mm_strdup(var_text)), |
| ECPGmake_array_type(ECPGmake_simple_type(ptr->variable->type->u.element->type, |
| mm_strdup("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, mm_strdup("("), |
| mm_strdup(ecpg_type_name(ptr->variable->type->type)), |
| mm_strdup(" *)(ECPGget_var("), |
| mm_strdup(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, mm_strdup("(*("), |
| mm_strdup(ptr->variable->type->type_name), |
| mm_strdup(" *)(ECPGget_var("), |
| mm_strdup(var_text), |
| mm_strdup(")")), |
| 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, mm_strdup("(*("), |
| mm_strdup(ptr->variable->type->u.element->type_name), |
| mm_strdup(" *)(ECPGget_var("), |
| mm_strdup(var_text), |
| mm_strdup(")")), |
| 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, mm_strdup("("), |
| mm_strdup(ecpg_type_name(ptr->variable->type->u.element->type)), |
| mm_strdup(" *)(ECPGget_var("), |
| mm_strdup(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, mm_strdup("*("), |
| mm_strdup(ecpg_type_name(ptr->variable->type->type)), |
| mm_strdup(" *)(ECPGget_var("), |
| mm_strdup(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(<counter>, <connection>, <pointer>. <line number>)" */ |
| if (!skip_set_var) |
| { |
| sprintf(var_text, "%d, %s", ecpg_internal_var++, var_ptr ? "&(" : "("); |
| result = cat_str(5, result, mm_strdup("ECPGset_var("), |
| mm_strdup(var_text), mm_strdup(original_var), |
| mm_strdup("), __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(<counter>)" */ |
| original_var = ptr->indicator->name; |
| sprintf(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, mm_strdup("(*("), |
| mm_strdup(ptr->indicator->type->type_name), |
| mm_strdup(" *)(ECPGget_var("), |
| mm_strdup(var_text), |
| mm_strdup(")")), |
| 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, mm_strdup("(*("), |
| mm_strdup(ptr->indicator->type->u.element->type_name), |
| mm_strdup(" *)(ECPGget_var("), |
| mm_strdup(var_text), |
| mm_strdup(")")), |
| 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, mm_strdup("("), |
| mm_strdup(ecpg_type_name(ptr->indicator->type->u.element->type)), |
| mm_strdup(" *)(ECPGget_var("), mm_strdup(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, mm_strdup("("), |
| mm_strdup(ecpg_type_name(ptr->indicator->type->type)), |
| mm_strdup(" *)(ECPGget_var("), |
| mm_strdup(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, mm_strdup("*("), |
| mm_strdup(ecpg_type_name(ptr->indicator->type->type)), |
| mm_strdup(" *)(ECPGget_var("), |
| mm_strdup(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(<counter>, <pointer>. <line number>)" */ |
| sprintf(var_text, "%d, %s", ecpg_internal_var++, var_ptr ? "&(" : "("); |
| result = cat_str(5, result, mm_strdup("ECPGset_var("), |
| mm_strdup(var_text), mm_strdup(original_var), |
| mm_strdup("), __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 && strcmp(cur->function, current_function) == 0)) |
| |
| static struct cursor * |
| add_additional_variables(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(char *name, char *dimension, char *length, enum ECPGttype type_enum, |
| char *type_dimension, 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 = name; |
| this->brace_level = braces_open; |
| this->type = (struct this_type *) mm_alloc(sizeof(struct this_type)); |
| this->type->type_enum = type_enum; |
| this->type->type_str = mm_strdup(name); |
| this->type->type_dimension = dimension; /* dimension of array */ |
| this->type->type_index = length; /* length of string */ |
| this->type->type_sizeof = ECPGstruct_sizeof; |
| 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); |
| connection = mm_strdup(ptr -> connection); |
| return true; |
| } |
| } |
| return false; |
| } |
| %} |
| |
| %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; |
| } |