/* src/interfaces/ecpg/preproc/ecpg.trailer */ statements: /* EMPTY */ | statements statement { /* Reclaim local storage used while processing statement */ reclaim_local_storage(); /* Clean up now-dangling location pointer */ @$ = ""; } ; statement: ecpgstart at toplevel_stmt ';' { if (connection) free(connection); connection = NULL; } | ecpgstart toplevel_stmt ';' { if (connection) free(connection); connection = NULL; } | ecpgstart ECPGVarDeclaration { fprintf(base_yyout, "%s", @$); output_line_number(); } | ECPGDeclaration | c_thing { fprintf(base_yyout, "%s", @$); } | CPP_LINE { fprintf(base_yyout, "%s", @$); } | '{' { braces_open++; fputs("{", base_yyout); } | '}' { if (braces_open > 0) { remove_typedefs(braces_open); remove_variables(braces_open); if (--braces_open == 0) { free(current_function); current_function = NULL; } } fputs("}", base_yyout); } ; CreateAsStmt: CREATE OptTemp TABLE create_as_target AS { FoundInto = 0; } SelectStmt opt_with_data { if (FoundInto == 1) mmerror(PARSE_ERROR, ET_ERROR, "CREATE TABLE AS cannot specify INTO"); } | CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS { FoundInto = 0; } SelectStmt opt_with_data { if (FoundInto == 1) mmerror(PARSE_ERROR, ET_ERROR, "CREATE TABLE AS cannot specify INTO"); } ; at: AT connection_object { if (connection) free(connection); connection = mm_strdup(@2); /* * Do we have a variable as connection target? Remove the variable * from the variable list or else it will be used twice. */ if (argsinsert != NULL) argsinsert = NULL; } ; /* * the exec sql connect statement: connect to the given database */ ECPGConnect: SQL_CONNECT TO connection_target opt_connection_name opt_user { @$ = cat_str(5, @3, ",", @5, ",", @4); } | SQL_CONNECT TO DEFAULT { @$ = "NULL, NULL, NULL, \"DEFAULT\""; } /* also allow ORACLE syntax */ | SQL_CONNECT ora_user { @$ = cat_str(3, "NULL,", @2, ", NULL"); } | DATABASE connection_target { @$ = cat2_str(@2, ", NULL, NULL, NULL"); } ; connection_target: opt_database_name opt_server opt_port { /* old style: dbname[@server][:port] */ if (strlen(@2) > 0 && *(@2) != '@') mmerror(PARSE_ERROR, ET_ERROR, "expected \"@\", found \"%s\"", @2); /* C strings need to be handled differently */ if (@1[0] == '\"') @$ = @1; else @$ = make3_str("\"", make3_str(@1, @2, @3), "\""); } | db_prefix ':' server opt_port '/' opt_database_name opt_options { /* new style: :postgresql://server[:port][/dbname] */ if (strncmp(@1, "unix:postgresql", strlen("unix:postgresql")) != 0 && strncmp(@1, "tcp:postgresql", strlen("tcp:postgresql")) != 0) mmerror(PARSE_ERROR, ET_ERROR, "only protocols \"tcp\" and \"unix\" and database type \"postgresql\" are supported"); if (strncmp(@3, "//", strlen("//")) != 0) 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) mmerror(PARSE_ERROR, ET_ERROR, "Unix-domain sockets only work on \"localhost\" but not on \"%s\"", @3 + strlen("//")); @$ = make3_str(make3_str("\"", @1, ":"), @3, make3_str(make3_str(@4, "/", @6), @7, "\"")); } | char_variable | ecpg_sconst { /* * We can only process double quoted strings not single quoted ones, * so we change the quotes. Note that the rule for ecpg_sconst adds * these single quotes. */ char *str = loc_strdup(@1); str[0] = '\"'; str[strlen(str) - 1] = '\"'; @$ = str; } ; opt_database_name: name | /* EMPTY */ ; db_prefix: ecpg_ident cvariable { if (strcmp(@2, "postgresql") != 0 && strcmp(@2, "postgres") != 0) mmerror(PARSE_ERROR, ET_ERROR, "expected \"postgresql\", found \"%s\"", @2); if (strcmp(@1, "tcp") != 0 && strcmp(@1, "unix") != 0) mmerror(PARSE_ERROR, ET_ERROR, "invalid connection type: %s", @1); @$ = make3_str(@1, ":", @2); } ; server: Op server_name { if (strcmp(@1, "@") != 0 && strcmp(@1, "//") != 0) mmerror(PARSE_ERROR, ET_ERROR, "expected \"@\" or \"://\", found \"%s\"", @1); @$ = make2_str(@1, @2); } ; opt_server: server | /* EMPTY */ ; server_name: ColId | ColId '.' server_name | IP ; opt_port: ':' Iconst { @$ = make2_str(":", @2); } | /* EMPTY */ ; opt_connection_name: AS connection_object { @$ = @2; } | /* EMPTY */ { @$ = "NULL"; } ; opt_user: USER ora_user { @$ = @2; } | /* EMPTY */ { @$ = "NULL, NULL"; } ; ora_user: user_name { @$ = cat2_str(@1, ", NULL"); } | user_name '/' user_name { @$ = cat_str(3, @1, ",", @3); } | user_name SQL_IDENTIFIED BY user_name { @$ = cat_str(3, @1, ",", @4); } | user_name USING user_name { @$ = cat_str(3, @1, ",", @3); } ; user_name: RoleId { if (@1[0] == '\"') @$ = @1; else @$ = make3_str("\"", @1, "\""); } | ecpg_sconst { if (@1[0] == '\"') @$ = @1; else @$ = make3_str("\"", @1, "\""); } | civar { enum ECPGttype type = argsinsert->variable->type->type; /* if array see what's inside */ if (type == ECPGt_array) type = argsinsert->variable->type->u.element->type; /* handle varchars */ if (type == ECPGt_varchar) @$ = make2_str(argsinsert->variable->name, ".arr"); else @$ = argsinsert->variable->name; } ; char_variable: cvariable { /* check if we have a string variable */ struct variable *p = find_variable(@1); enum ECPGttype type = p->type->type; /* If we have just one character this is not a string */ if (atol(p->type->size) == 1) mmerror(PARSE_ERROR, ET_ERROR, "invalid data type"); else { /* if array see what's inside */ if (type == ECPGt_array) type = p->type->u.element->type; switch (type) { case ECPGt_char: case ECPGt_unsigned_char: case ECPGt_string: @$ = @1; break; case ECPGt_varchar: @$ = make2_str(@1, ".arr"); break; default: mmerror(PARSE_ERROR, ET_ERROR, "invalid data type"); @$ = @1; break; } } } ; opt_options: Op connect_options { if (strlen(@1) == 0) mmerror(PARSE_ERROR, ET_ERROR, "incomplete statement"); if (strcmp(@1, "?") != 0) mmerror(PARSE_ERROR, ET_ERROR, "unrecognized token \"%s\"", @1); @$ = make2_str("?", @2); } | /* EMPTY */ ; connect_options: ColId opt_opt_value { @$ = make2_str(@1, @2); } | ColId opt_opt_value Op connect_options { if (strlen(@3) == 0) mmerror(PARSE_ERROR, ET_ERROR, "incomplete statement"); if (strcmp(@3, "&") != 0) mmerror(PARSE_ERROR, ET_ERROR, "unrecognized token \"%s\"", @3); @$ = make3_str(make2_str(@1, @2), @3, @4); } ; opt_opt_value: /* EMPTY */ | '=' Iconst { @$ = make2_str("=", @2); } | '=' ecpg_ident { @$ = make2_str("=", @2); } | '=' civar { @$ = make2_str("=", @2); } ; prepared_name: name { size_t slen = strlen(@1); if (@1[0] == '\"' && @1[slen - 1] == '\"') /* already quoted? */ @$ = @1; else /* not quoted => convert to lowercase */ { char *str = loc_alloc(slen + 3); str[0] = '\"'; for (size_t i = 0; i < slen; i++) str[i + 1] = tolower((unsigned char) @1[i]); str[slen + 1] = '\"'; str[slen + 2] = '\0'; @$ = str; } } | char_variable ; /* * Declare Statement */ ECPGDeclareStmt: DECLARE prepared_name STATEMENT { struct declared_list *ptr; /* Check whether the declared name has been defined or not */ for (ptr = g_declared_list; ptr != NULL; ptr = ptr->next) { if (strcmp(@2, ptr->name) == 0) { /* re-definition is not allowed */ mmerror(PARSE_ERROR, ET_ERROR, "name \"%s\" is already declared", ptr->name); } } /* Add a new declared name into the g_declared_list */ ptr = (struct declared_list *) mm_alloc(sizeof(struct declared_list)); if (ptr) { /* initial definition */ ptr->name = mm_strdup(@2); if (connection) ptr->connection = mm_strdup(connection); else ptr->connection = NULL; ptr->next = g_declared_list; g_declared_list = ptr; } @$ = cat_str(3, "/* declare ", @2, " as an SQL identifier */"); } ; /* * Declare a prepared cursor. The syntax is different from the standard * declare statement, so we create a new rule. */ ECPGCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared_name { struct cursor *ptr, *this; const char *cursor_marker = @2[0] == ':' ? "$0" : @2; int (*strcmp_fn) (const char *, const char *) = ((@2[0] == ':' || @2[0] == '"') ? strcmp : pg_strcasecmp); struct variable *thisquery = (struct variable *) mm_alloc(sizeof(struct variable)); char *comment; char *con; if (INFORMIX_MODE && pg_strcasecmp(@2, "database") == 0) mmfatal(PARSE_ERROR, "\"database\" cannot be used as cursor name in INFORMIX mode"); check_declared_list(@7); con = connection ? connection : "NULL"; for (ptr = cur; ptr != NULL; ptr = ptr->next) { if (strcmp_fn(@2, ptr->name) == 0) { /* re-definition is a bug */ if (@2[0] == ':') mmerror(PARSE_ERROR, ET_ERROR, "using variable \"%s\" in different declare statements is not supported", @2 + 1); else mmerror(PARSE_ERROR, ET_ERROR, "cursor \"%s\" is already defined", @2); } } this = (struct cursor *) mm_alloc(sizeof(struct cursor)); /* initial definition */ this->next = cur; this->name = mm_strdup(@2); this->function = (current_function ? mm_strdup(current_function) : NULL); this->connection = connection ? mm_strdup(connection) : NULL; this->opened = false; this->command = mm_strdup(cat_str(6, "declare", cursor_marker, @3, "cursor", @5, "for $1")); this->argsresult = NULL; this->argsresult_oos = NULL; thisquery->type = &ecpg_query; thisquery->brace_level = 0; thisquery->next = NULL; thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(, , __LINE__)") + strlen(con) + strlen(@7)); sprintf(thisquery->name, "ECPGprepared_statement(%s, %s, __LINE__)", con, @7); this->argsinsert = NULL; this->argsinsert_oos = NULL; if (@2[0] == ':') { struct variable *var = find_variable(@2 + 1); remove_variable_from_list(&argsinsert, var); add_variable_to_head(&(this->argsinsert), var, &no_indicator); } add_variable_to_head(&(this->argsinsert), thisquery, &no_indicator); cur = this; comment = cat_str(3, "/*", this->command, "*/"); @$ = cat_str(2, adjust_outofscope_cursor_vars(this), comment); } ; ECPGExecuteImmediateStmt: EXECUTE IMMEDIATE execstring { /* * execute immediate means prepare the statement and immediately * execute it */ @$ = @3; } ; /* * variable declaration outside exec sql declare block */ ECPGVarDeclaration: single_vt_declaration; single_vt_declaration: type_declaration | var_declaration ; precision: NumericOnly ; opt_scale: ',' NumericOnly { @$ = @2; } | /* EMPTY */ ; ecpg_interval: opt_interval | YEAR_P TO MINUTE_P | YEAR_P TO SECOND_P | DAY_P TO DAY_P | MONTH_P TO MONTH_P ; /* * variable declaration inside exec sql declare block */ ECPGDeclaration: sql_startdeclare { fputs("/* exec sql begin declare section */", base_yyout); } var_type_declarations sql_enddeclare { fprintf(base_yyout, "%s/* exec sql end declare section */", @3); output_line_number(); } ; sql_startdeclare: ecpgstart BEGIN_P DECLARE SQL_SECTION ';' { } ; sql_enddeclare: ecpgstart END_P DECLARE SQL_SECTION ';' { } ; var_type_declarations: /* EMPTY */ | vt_declarations ; vt_declarations: single_vt_declaration | CPP_LINE | vt_declarations single_vt_declaration | vt_declarations CPP_LINE ; variable_declarations: var_declaration | variable_declarations var_declaration ; type_declaration: S_TYPEDEF { /* reset this variable so we see if there was */ /* an initializer specified */ initializer = 0; } var_type opt_pointer ECPGColLabel opt_array_bounds ';' { add_typedef(@5, $6.index1, $6.index2, $3.type_enum, $3.type_dimension, $3.type_index, initializer, *@4 ? 1 : 0); fprintf(base_yyout, "typedef %s %s %s %s;\n", $3.type_str, *@4 ? "*" : "", @5, $6.str); output_line_number(); @$ = ""; } ; var_declaration: storage_declaration var_type { actual_type[struct_level].type_storage = loc_strdup(@1); actual_type[struct_level].type_enum = $2.type_enum; actual_type[struct_level].type_str = $2.type_str; actual_type[struct_level].type_dimension = $2.type_dimension; actual_type[struct_level].type_index = $2.type_index; actual_type[struct_level].type_sizeof = $2.type_sizeof; actual_startline[struct_level] = hashline_number(); } variable_list ';' { @$ = cat_str(5, actual_startline[struct_level], @1, $2.type_str, @4, ";\n"); } | var_type { actual_type[struct_level].type_storage = loc_strdup(""); actual_type[struct_level].type_enum = $1.type_enum; actual_type[struct_level].type_str = $1.type_str; actual_type[struct_level].type_dimension = $1.type_dimension; actual_type[struct_level].type_index = $1.type_index; actual_type[struct_level].type_sizeof = $1.type_sizeof; actual_startline[struct_level] = hashline_number(); } variable_list ';' { @$ = cat_str(4, actual_startline[struct_level], $1.type_str, @3, ";\n"); } | struct_union_type_with_symbol ';' ; opt_bit_field: ':' Iconst | /* EMPTY */ ; storage_declaration: storage_clause storage_modifier | storage_clause | storage_modifier ; storage_clause: S_EXTERN | S_STATIC | S_REGISTER | S_AUTO ; storage_modifier: S_CONST | S_VOLATILE ; var_type: simple_type { $$.type_enum = $1; $$.type_str = loc_strdup(ecpg_type_name($1)); $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } | struct_union_type { $$.type_str = loc_strdup(@1); $$.type_dimension = "-1"; $$.type_index = "-1"; if (strncmp(@1, "struct", sizeof("struct") - 1) == 0) { $$.type_enum = ECPGt_struct; $$.type_sizeof = ECPGstruct_sizeof; } else { $$.type_enum = ECPGt_union; $$.type_sizeof = NULL; } } | enum_type { $$.type_str = loc_strdup(@1); $$.type_enum = ECPGt_int; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } | NUMERIC '(' precision opt_scale ')' { $$.type_enum = ECPGt_numeric; $$.type_str = "numeric"; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } | DECIMAL_P '(' precision opt_scale ')' { $$.type_enum = ECPGt_decimal; $$.type_str = "decimal"; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } | IDENT '(' precision opt_scale ')' { /* * In C parsing mode, NUMERIC and DECIMAL are not keywords, so they * will show up here as a plain identifier, and we need this duplicate * code to recognize them. */ if (strcmp(@1, "numeric") == 0) { $$.type_enum = ECPGt_numeric; $$.type_str = "numeric"; } else if (strcmp(@1, "decimal") == 0) { $$.type_enum = ECPGt_decimal; $$.type_str = "decimal"; } else { mmerror(PARSE_ERROR, ET_ERROR, "only data types numeric and decimal have precision/scale argument"); $$.type_enum = ECPGt_numeric; $$.type_str = "numeric"; } $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } | VARCHAR { $$.type_enum = ECPGt_varchar; $$.type_str = ""; /* "varchar"; */ $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } | FLOAT_P { /* Note: DOUBLE is handled in simple_type */ $$.type_enum = ECPGt_float; $$.type_str = "float"; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } | NUMERIC { $$.type_enum = ECPGt_numeric; $$.type_str = "numeric"; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } | DECIMAL_P { $$.type_enum = ECPGt_decimal; $$.type_str = "decimal"; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } | TIMESTAMP { $$.type_enum = ECPGt_timestamp; $$.type_str = "timestamp"; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } | STRING_P { if (INFORMIX_MODE) { /* In Informix mode, "string" is automatically a typedef */ $$.type_enum = ECPGt_string; $$.type_str = "char"; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } else { /* Otherwise, legal only if user typedef'ed it */ struct typedefs *this = get_typedef("string", false); $$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? mm_strdup("") : mm_strdup(this->name); $$.type_enum = this->type->type_enum; $$.type_dimension = this->type->type_dimension; $$.type_index = this->type->type_index; if (this->type->type_sizeof && strlen(this->type->type_sizeof) != 0) $$.type_sizeof = this->type->type_sizeof; else $$.type_sizeof = cat_str(3, "sizeof(", this->name, ")"); ECPGfree_struct_member(struct_member_list[struct_level]); struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list); } } | INTERVAL ecpg_interval { $$.type_enum = ECPGt_interval; $$.type_str = "interval"; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } | IDENT ecpg_interval { /* * In C parsing mode, the above SQL type names are not keywords, so * they will show up here as a plain identifier, and we need this * duplicate code to recognize them. * * Note that we also handle the type names bytea, date, and datetime * here, but not above because those are not currently SQL keywords. * If they ever become so, they must gain duplicate productions above. */ if (strlen(@2) != 0 && strcmp(@1, "datetime") != 0 && strcmp(@1, "interval") != 0) mmerror(PARSE_ERROR, ET_ERROR, "interval specification not allowed here"); if (strcmp(@1, "varchar") == 0) { $$.type_enum = ECPGt_varchar; $$.type_str = ""; /* "varchar"; */ $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } else if (strcmp(@1, "bytea") == 0) { $$.type_enum = ECPGt_bytea; $$.type_str = ""; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } else if (strcmp(@1, "float") == 0) { $$.type_enum = ECPGt_float; $$.type_str = "float"; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } else if (strcmp(@1, "double") == 0) { $$.type_enum = ECPGt_double; $$.type_str = "double"; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } else if (strcmp(@1, "numeric") == 0) { $$.type_enum = ECPGt_numeric; $$.type_str = "numeric"; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } else if (strcmp(@1, "decimal") == 0) { $$.type_enum = ECPGt_decimal; $$.type_str = "decimal"; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } else if (strcmp(@1, "date") == 0) { $$.type_enum = ECPGt_date; $$.type_str = "date"; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } else if (strcmp(@1, "timestamp") == 0) { $$.type_enum = ECPGt_timestamp; $$.type_str = "timestamp"; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } else if (strcmp(@1, "interval") == 0) { $$.type_enum = ECPGt_interval; $$.type_str = "interval"; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } else if (strcmp(@1, "datetime") == 0) { $$.type_enum = ECPGt_timestamp; $$.type_str = "timestamp"; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } else if ((strcmp(@1, "string") == 0) && INFORMIX_MODE) { $$.type_enum = ECPGt_string; $$.type_str = "char"; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = NULL; } else { /* Otherwise, it must be a user-defined typedef name */ struct typedefs *this = get_typedef(@1, false); $$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? "" : this->name; $$.type_enum = this->type->type_enum; $$.type_dimension = this->type->type_dimension; $$.type_index = this->type->type_index; if (this->type->type_sizeof && strlen(this->type->type_sizeof) != 0) $$.type_sizeof = this->type->type_sizeof; else $$.type_sizeof = cat_str(3, "sizeof(", this->name, ")"); ECPGfree_struct_member(struct_member_list[struct_level]); struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list); } } | s_struct_union_symbol { /* this is for named structs/unions */ char *name; struct typedefs *this; bool forward = (forward_name != NULL && strcmp($1.symbol, forward_name) == 0 && strcmp($1.su, "struct") == 0); name = cat2_str($1.su, $1.symbol); /* Do we have a forward definition? */ if (!forward) { /* No */ this = get_typedef(name, false); $$.type_str = this->name; $$.type_enum = this->type->type_enum; $$.type_dimension = this->type->type_dimension; $$.type_index = this->type->type_index; $$.type_sizeof = this->type->type_sizeof; ECPGfree_struct_member(struct_member_list[struct_level]); struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list); } else { $$.type_str = name; $$.type_enum = ECPGt_long; $$.type_dimension = "-1"; $$.type_index = "-1"; $$.type_sizeof = ""; ECPGfree_struct_member(struct_member_list[struct_level]); struct_member_list[struct_level] = NULL; } } ; enum_type: ENUM_P symbol enum_definition | ENUM_P enum_definition | ENUM_P symbol ; enum_definition: '{' c_list '}' ; struct_union_type_with_symbol: s_struct_union_symbol { ECPGfree_struct_member(struct_member_list[struct_level]); struct_member_list[struct_level++] = NULL; if (struct_level >= STRUCT_DEPTH) mmerror(PARSE_ERROR, ET_ERROR, "too many levels in nested structure/union definition"); forward_name = mm_strdup($1.symbol); } '{' variable_declarations '}' { struct typedefs *ptr, *this; struct this_type su_type; ECPGfree_struct_member(struct_member_list[struct_level]); struct_member_list[struct_level] = NULL; struct_level--; if (strcmp($1.su, "struct") == 0) su_type.type_enum = ECPGt_struct; else su_type.type_enum = ECPGt_union; su_type.type_str = cat2_str($1.su, $1.symbol); free(forward_name); forward_name = NULL; /* * This is essentially a typedef but needs the keyword struct/union as * well. So we create the typedef for each struct definition with * symbol */ for (ptr = types; ptr != NULL; ptr = ptr->next) { if (strcmp(su_type.type_str, ptr->name) == 0) /* re-definition is a bug */ mmerror(PARSE_ERROR, ET_ERROR, "type \"%s\" is already defined", su_type.type_str); } this = (struct typedefs *) mm_alloc(sizeof(struct typedefs)); /* initial definition */ this->next = types; this->name = mm_strdup(su_type.type_str); 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 = su_type.type_enum; this->type->type_str = mm_strdup(su_type.type_str); this->type->type_dimension = mm_strdup("-1"); /* dimension of array */ this->type->type_index = mm_strdup("-1"); /* length of string */ this->type->type_sizeof = ECPGstruct_sizeof ? mm_strdup(ECPGstruct_sizeof) : NULL; this->struct_member_list = ECPGstruct_member_dup(struct_member_list[struct_level]); types = this; @$ = cat_str(4, su_type.type_str, "{", @4, "}"); } ; struct_union_type: struct_union_type_with_symbol | s_struct_union { ECPGfree_struct_member(struct_member_list[struct_level]); struct_member_list[struct_level++] = NULL; if (struct_level >= STRUCT_DEPTH) mmerror(PARSE_ERROR, ET_ERROR, "too many levels in nested structure/union definition"); } '{' variable_declarations '}' { ECPGfree_struct_member(struct_member_list[struct_level]); struct_member_list[struct_level] = NULL; struct_level--; @$ = cat_str(4, @1, "{", @4, "}"); } ; s_struct_union_symbol: SQL_STRUCT symbol { $$.su = "struct"; $$.symbol = @2; free(ECPGstruct_sizeof); ECPGstruct_sizeof = mm_strdup(cat_str(3, "sizeof(", cat2_str($$.su, $$.symbol), ")")); } | UNION symbol { $$.su = "union"; $$.symbol = @2; } ; s_struct_union: SQL_STRUCT { free(ECPGstruct_sizeof); ECPGstruct_sizeof = mm_strdup(""); /* This must not be NULL to * distinguish from simple types. */ @$ = "struct"; } | UNION { @$ = "union"; } ; simple_type: unsigned_type | opt_signed signed_type { $$ = $2; } ; unsigned_type: SQL_UNSIGNED SQL_SHORT { $$ = ECPGt_unsigned_short; } | SQL_UNSIGNED SQL_SHORT INT_P { $$ = ECPGt_unsigned_short; } | SQL_UNSIGNED { $$ = ECPGt_unsigned_int; } | SQL_UNSIGNED INT_P { $$ = ECPGt_unsigned_int; } | SQL_UNSIGNED SQL_LONG { $$ = ECPGt_unsigned_long; } | SQL_UNSIGNED SQL_LONG INT_P { $$ = ECPGt_unsigned_long; } | SQL_UNSIGNED SQL_LONG SQL_LONG { $$ = ECPGt_unsigned_long_long; } | SQL_UNSIGNED SQL_LONG SQL_LONG INT_P { $$ = ECPGt_unsigned_long_long; } | SQL_UNSIGNED CHAR_P { $$ = ECPGt_unsigned_char; } ; signed_type: SQL_SHORT { $$ = ECPGt_short; } | SQL_SHORT INT_P { $$ = ECPGt_short; } | INT_P { $$ = ECPGt_int; } | SQL_LONG { $$ = ECPGt_long; } | SQL_LONG INT_P { $$ = ECPGt_long; } | SQL_LONG SQL_LONG { $$ = ECPGt_long_long; } | SQL_LONG SQL_LONG INT_P { $$ = ECPGt_long_long; } | SQL_BOOL { $$ = ECPGt_bool; } | CHAR_P { $$ = ECPGt_char; } | DOUBLE_P { $$ = ECPGt_double; } ; opt_signed: SQL_SIGNED | /* EMPTY */ ; variable_list: variable | variable_list ',' variable { if (actual_type[struct_level].type_enum == ECPGt_varchar || actual_type[struct_level].type_enum == ECPGt_bytea) @$ = cat_str(4, @1, ";", actual_type[struct_level].type_storage, @3); else @$ = cat_str(3, @1, ",", @3); } ; variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initializer { struct ECPGtype *type; const char *dimension = $3.index1; /* dimension of array */ const char *length = $3.index2; /* length of string */ char *dim_str; char vcn[32]; int *varlen_type_counter; char *struct_name; adjust_array(actual_type[struct_level].type_enum, &dimension, &length, actual_type[struct_level].type_dimension, actual_type[struct_level].type_index, strlen(@1), false); switch (actual_type[struct_level].type_enum) { case ECPGt_struct: case ECPGt_union: if (atoi(dimension) < 0) type = ECPGmake_struct_type(struct_member_list[struct_level], actual_type[struct_level].type_enum, actual_type[struct_level].type_str, actual_type[struct_level].type_sizeof); else type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level], actual_type[struct_level].type_enum, actual_type[struct_level].type_str, actual_type[struct_level].type_sizeof), dimension); @$ = cat_str(5, @1, @2, $3.str, @4, @5); break; case ECPGt_varchar: case ECPGt_bytea: if (actual_type[struct_level].type_enum == ECPGt_varchar) { varlen_type_counter = &varchar_counter; struct_name = " struct varchar_"; } else { varlen_type_counter = &bytea_counter; struct_name = " struct bytea_"; } if (atoi(dimension) < 0) type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length, *varlen_type_counter); else type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length, *varlen_type_counter), dimension); if (strcmp(dimension, "0") == 0 || abs(atoi(dimension)) == 1) dim_str = ""; else dim_str = cat_str(3, "[", dimension, "]"); /* * cannot check for atoi <= 0 because a defined constant will * yield 0 here as well */ if (atoi(length) < 0 || strcmp(length, "0") == 0) mmerror(PARSE_ERROR, ET_ERROR, "pointers to varchar are not implemented"); /* * make sure varchar struct name is unique by adding a unique * counter to its definition */ snprintf(vcn, sizeof(vcn), "%d", *varlen_type_counter); if (strcmp(dimension, "0") == 0) @$ = cat_str(7, make2_str(struct_name, vcn), " { int len; char arr[", length, "]; } *", @2, @4, @5); else @$ = cat_str(8, make2_str(struct_name, vcn), " { int len; char arr[", length, "]; } ", @2, dim_str, @4, @5); (*varlen_type_counter)++; break; case ECPGt_char: case ECPGt_unsigned_char: case ECPGt_string: if (atoi(dimension) == -1) { int i = strlen(@5); if (atoi(length) == -1 && i > 0) /* char [] = * "string" */ { /* * if we have an initializer but no string size set, * let's use the initializer's length */ char *buf = loc_alloc(32); snprintf(buf, 32, "sizeof(%s)", @5 + 2); length = buf; } type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length, 0); } else type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length, 0), dimension); @$ = cat_str(5, @1, @2, $3.str, @4, @5); break; default: if (atoi(dimension) < 0) type = ECPGmake_simple_type(actual_type[struct_level].type_enum, "1", 0); else type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, "1", 0), dimension); @$ = cat_str(5, @1, @2, $3.str, @4, @5); break; } if (struct_level == 0) new_variable(@2, type, braces_open); else ECPGmake_struct_member(@2, type, &(struct_member_list[struct_level - 1])); } ; opt_initializer: /* EMPTY */ | '=' c_term { initializer = 1; } ; opt_pointer: /* EMPTY */ | '*' | '*' '*' { @$ = "**"; } ; /* * We try to simulate the correct DECLARE syntax here so we get dynamic SQL */ ECPGDeclare: DECLARE STATEMENT ecpg_ident { /* this is only supported for compatibility */ @$ = cat_str(3, "/* declare statement", @3, "*/"); } ; /* * the exec sql disconnect statement: disconnect from the given database */ ECPGDisconnect: SQL_DISCONNECT dis_name { @$ = @2; } ; dis_name: connection_object | CURRENT_P { @$ = "\"CURRENT\""; } | ALL { @$ = "\"ALL\""; } | /* EMPTY */ { @$ = "\"CURRENT\""; } ; connection_object: name { @$ = make3_str("\"", @1, "\""); } | DEFAULT { @$ = "\"DEFAULT\""; } | char_variable ; execstring: char_variable | CSTRING { @$ = make3_str("\"", @1, "\""); } ; /* * the exec sql free command to deallocate a previously * prepared statement */ ECPGFree: SQL_FREE cursor_name { @$ = @2; } | SQL_FREE ALL { @$ = "all"; } ; /* * open is an open cursor, at the moment this has to be removed */ ECPGOpen: SQL_OPEN cursor_name opt_ecpg_using { if (@2[0] == ':') remove_variable_from_list(&argsinsert, find_variable(@2 + 1)); @$ = @2; } ; opt_ecpg_using: /* EMPTY */ | ecpg_using ; ecpg_using: USING using_list { @$ = ""; } | using_descriptor ; using_descriptor: USING SQL_P SQL_DESCRIPTOR quoted_ident_stringvar { add_variable_to_head(&argsinsert, descriptor_variable(@4, 0), &no_indicator); @$ = ""; } | USING SQL_DESCRIPTOR name { add_variable_to_head(&argsinsert, sqlda_variable(@3), &no_indicator); @$ = ""; } ; into_descriptor: INTO SQL_P SQL_DESCRIPTOR quoted_ident_stringvar { add_variable_to_head(&argsresult, descriptor_variable(@4, 1), &no_indicator); @$ = ""; } | INTO SQL_DESCRIPTOR name { add_variable_to_head(&argsresult, sqlda_variable(@3), &no_indicator); @$ = ""; } ; into_sqlda: INTO name { add_variable_to_head(&argsresult, sqlda_variable(@2), &no_indicator); @$ = ""; } ; using_list: UsingValue | UsingValue ',' using_list ; UsingValue: UsingConst { char length[32]; snprintf(length, sizeof(length), "%zu", strlen(@1)); add_variable_to_head(&argsinsert, new_variable(@1, ECPGmake_simple_type(ECPGt_const, length, 0), 0), &no_indicator); } | civar { @$ = ""; } | civarind { @$ = ""; } ; UsingConst: Iconst | '+' Iconst | '-' Iconst | ecpg_fconst | '+' ecpg_fconst | '-' ecpg_fconst | ecpg_sconst | ecpg_bconst | ecpg_xconst ; /* * We accept DESCRIBE [OUTPUT] but do nothing with DESCRIBE INPUT so far. */ ECPGDescribe: SQL_DESCRIBE INPUT_P prepared_name using_descriptor { $$.input = 1; $$.stmt_name = @3; } | SQL_DESCRIBE opt_output prepared_name using_descriptor { struct variable *var; var = argsinsert->variable; remove_variable_from_list(&argsinsert, var); add_variable_to_head(&argsresult, var, &no_indicator); $$.input = 0; $$.stmt_name = @3; } | SQL_DESCRIBE opt_output prepared_name into_descriptor { $$.input = 0; $$.stmt_name = @3; } | SQL_DESCRIBE INPUT_P prepared_name into_sqlda { $$.input = 1; $$.stmt_name = @3; } | SQL_DESCRIBE opt_output prepared_name into_sqlda { $$.input = 0; $$.stmt_name = @3; } ; opt_output: SQL_OUTPUT | /* EMPTY */ ; /* * dynamic SQL: descriptor based access * originally written by Christof Petig * and Peter Eisentraut */ /* * allocate a descriptor */ ECPGAllocateDescr: SQL_ALLOCATE SQL_DESCRIPTOR quoted_ident_stringvar { add_descriptor(@3, connection); @$ = @3; } ; /* * deallocate a descriptor */ ECPGDeallocateDescr: DEALLOCATE SQL_DESCRIPTOR quoted_ident_stringvar { drop_descriptor(@3, connection); @$ = @3; } ; /* * manipulate a descriptor header */ ECPGGetDescriptorHeader: SQL_GET SQL_DESCRIPTOR quoted_ident_stringvar ECPGGetDescHeaderItems { @$ = @3; } ; ECPGGetDescHeaderItems: ECPGGetDescHeaderItem | ECPGGetDescHeaderItems ',' ECPGGetDescHeaderItem ; ECPGGetDescHeaderItem: cvariable '=' desc_header_item { push_assignment(@1, $3); } ; ECPGSetDescriptorHeader: SET SQL_DESCRIPTOR quoted_ident_stringvar ECPGSetDescHeaderItems { @$ = @3; } ; ECPGSetDescHeaderItems: ECPGSetDescHeaderItem | ECPGSetDescHeaderItems ',' ECPGSetDescHeaderItem ; ECPGSetDescHeaderItem: desc_header_item '=' IntConstVar { push_assignment(@3, $1); } ; IntConstVar: Iconst { char length[32]; snprintf(length, sizeof(length), "%zu", strlen(@1)); new_variable(@1, ECPGmake_simple_type(ECPGt_const, length, 0), 0); } | cvariable ; desc_header_item: SQL_COUNT { $$ = ECPGd_count; } ; /* * manipulate a descriptor */ ECPGGetDescriptor: SQL_GET SQL_DESCRIPTOR quoted_ident_stringvar VALUE_P IntConstVar ECPGGetDescItems { $$.str = @5; $$.name = @3; } ; ECPGGetDescItems: ECPGGetDescItem | ECPGGetDescItems ',' ECPGGetDescItem ; ECPGGetDescItem: cvariable '=' descriptor_item { push_assignment(@1, $3); } ; ECPGSetDescriptor: SET SQL_DESCRIPTOR quoted_ident_stringvar VALUE_P IntConstVar ECPGSetDescItems { $$.str = @5; $$.name = @3; } ; ECPGSetDescItems: ECPGSetDescItem | ECPGSetDescItems ',' ECPGSetDescItem ; ECPGSetDescItem: descriptor_item '=' AllConstVar { push_assignment(@3, $1); } ; AllConstVar: ecpg_fconst { char length[32]; snprintf(length, sizeof(length), "%zu", strlen(@1)); new_variable(@1, ECPGmake_simple_type(ECPGt_const, length, 0), 0); } | IntConstVar | '-' ecpg_fconst { char length[32]; char *var = cat2_str("-", @2); snprintf(length, sizeof(length), "%zu", strlen(var)); new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0); @$ = var; } | '-' Iconst { char length[32]; char *var = cat2_str("-", @2); snprintf(length, sizeof(length), "%zu", strlen(var)); new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0); @$ = var; } | ecpg_sconst { char length[32]; char *var; /* Strip single quotes from ecpg_sconst */ var = loc_strdup(@1 + 1); var[strlen(var) - 1] = '\0'; snprintf(length, sizeof(length), "%zu", strlen(var)); new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0); @$ = var; } ; descriptor_item: SQL_CARDINALITY { $$ = ECPGd_cardinality; } | DATA_P { $$ = 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; } | NAME_P { $$ = 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; } ; /* * set/reset the automatic transaction mode, this needs a different handling * as the other set commands */ ECPGSetAutocommit: SET SQL_AUTOCOMMIT '=' on_off { @$ = @4; } | SET SQL_AUTOCOMMIT TO on_off { @$ = @4; } ; on_off: ON | OFF ; /* * set the actual connection, this needs a different handling as the other * set commands */ ECPGSetConnection: SET CONNECTION TO connection_object { @$ = @4; } | SET CONNECTION '=' connection_object { @$ = @4; } | SET CONNECTION connection_object { @$ = @3; } ; /* * define a new type for embedded SQL */ ECPGTypedef: TYPE_P { /* reset this variable so we see if there was */ /* an initializer specified */ initializer = 0; } ECPGColLabel IS var_type opt_array_bounds opt_reference { add_typedef(@3, $6.index1, $6.index2, $5.type_enum, $5.type_dimension, $5.type_index, initializer, *@7 ? 1 : 0); if (auto_create_c == false) @$ = cat_str(7, "/* exec sql type", @3, "is", $5.type_str, $6.str, @7, "*/"); else @$ = cat_str(6, "typedef ", $5.type_str, *@7 ? "*" : "", @3, $6.str, ";"); } ; opt_reference: SQL_REFERENCE | /* EMPTY */ ; /* * define the type of one variable for embedded SQL */ ECPGVar: SQL_VAR { /* reset this variable so we see if there was */ /* an initializer specified */ initializer = 0; } ColLabel IS var_type opt_array_bounds opt_reference { struct variable *p = find_variable(@3); const char *dimension = $6.index1; const char *length = $6.index2; struct ECPGtype *type; if (($5.type_enum == ECPGt_struct || $5.type_enum == ECPGt_union) && initializer == 1) mmerror(PARSE_ERROR, ET_ERROR, "initializer not allowed in EXEC SQL VAR command"); else { adjust_array($5.type_enum, &dimension, &length, $5.type_dimension, $5.type_index, *@7 ? 1 : 0, false); switch ($5.type_enum) { case ECPGt_struct: case ECPGt_union: if (atoi(dimension) < 0) type = ECPGmake_struct_type(struct_member_list[struct_level], $5.type_enum, $5.type_str, $5.type_sizeof); else type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level], $5.type_enum, $5.type_str, $5.type_sizeof), dimension); break; case ECPGt_varchar: case ECPGt_bytea: if (atoi(dimension) == -1) type = ECPGmake_simple_type($5.type_enum, length, 0); else type = ECPGmake_array_type(ECPGmake_simple_type($5.type_enum, length, 0), dimension); break; case ECPGt_char: case ECPGt_unsigned_char: case ECPGt_string: if (atoi(dimension) == -1) type = ECPGmake_simple_type($5.type_enum, length, 0); else type = ECPGmake_array_type(ECPGmake_simple_type($5.type_enum, length, 0), dimension); break; default: if (atoi(length) >= 0) mmerror(PARSE_ERROR, ET_ERROR, "multidimensional arrays for simple data types are not supported"); if (atoi(dimension) < 0) type = ECPGmake_simple_type($5.type_enum, "1", 0); else type = ECPGmake_array_type(ECPGmake_simple_type($5.type_enum, "1", 0), dimension); break; } ECPGfree_type(p->type); p->type = type; } @$ = cat_str(7, "/* exec sql var", @3, "is", $5.type_str, $6.str, @7, "*/"); } ; /* * whenever statement: decide what to do in case of error/no data found * according to SQL standards we lack: SQLSTATE, CONSTRAINT and SQLEXCEPTION */ ECPGWhenever: SQL_WHENEVER SQL_SQLERROR action { when_error.code = $3.code; free(when_error.command); when_error.command = $3.command ? mm_strdup($3.command) : NULL; @$ = cat_str(3, "/* exec sql whenever sqlerror ", $3.str, "; */"); } | SQL_WHENEVER NOT SQL_FOUND action { when_nf.code = $4.code; free(when_nf.command); when_nf.command = $4.command ? mm_strdup($4.command) : NULL; @$ = cat_str(3, "/* exec sql whenever not found ", $4.str, "; */"); } | SQL_WHENEVER SQL_SQLWARNING action { when_warn.code = $3.code; free(when_warn.command); when_warn.command = $3.command ? mm_strdup($3.command) : NULL; @$ = cat_str(3, "/* exec sql whenever sql_warning ", $3.str, "; */"); } ; action: CONTINUE_P { $$.code = W_NOTHING; $$.command = NULL; $$.str = "continue"; } | SQL_SQLPRINT { $$.code = W_SQLPRINT; $$.command = NULL; $$.str = "sqlprint"; } | SQL_STOP { $$.code = W_STOP; $$.command = NULL; $$.str = "stop"; } | SQL_GOTO name { $$.code = W_GOTO; $$.command = loc_strdup(@2); $$.str = cat2_str("goto ", @2); } | SQL_GO TO name { $$.code = W_GOTO; $$.command = loc_strdup(@3); $$.str = cat2_str("goto ", @3); } | DO name '(' c_args ')' { $$.code = W_DO; $$.command = cat_str(4, @2, "(", @4, ")"); $$.str = cat2_str("do", $$.command); } | DO SQL_BREAK { $$.code = W_BREAK; $$.command = NULL; $$.str = "break"; } | DO CONTINUE_P { $$.code = W_CONTINUE; $$.command = NULL; $$.str = "continue"; } | CALL name '(' c_args ')' { $$.code = W_DO; $$.command = cat_str(4, @2, "(", @4, ")"); $$.str = cat2_str("call", $$.command); } | CALL name { $$.code = W_DO; $$.command = cat2_str(@2, "()"); $$.str = cat2_str("call", $$.command); } ; /* some other stuff for ecpg */ /* additional unreserved keywords */ ECPGKeywords: ECPGKeywords_vanames | ECPGKeywords_rest ; ECPGKeywords_vanames: SQL_BREAK | SQL_CARDINALITY | SQL_COUNT | SQL_DATETIME_INTERVAL_CODE | SQL_DATETIME_INTERVAL_PRECISION | SQL_FOUND | SQL_GO | SQL_GOTO | SQL_IDENTIFIED | SQL_INDICATOR | SQL_KEY_MEMBER | SQL_LENGTH | SQL_NULLABLE | SQL_OCTET_LENGTH | SQL_RETURNED_LENGTH | SQL_RETURNED_OCTET_LENGTH | SQL_SCALE | SQL_SECTION | SQL_SQLERROR | SQL_SQLPRINT | SQL_SQLWARNING | SQL_STOP ; ECPGKeywords_rest: SQL_CONNECT | SQL_DESCRIBE | SQL_DISCONNECT | SQL_OPEN | SQL_VAR | SQL_WHENEVER ; /* additional keywords that can be SQL type names (but not ECPGColLabels) */ ECPGTypeName: SQL_BOOL | SQL_LONG | SQL_OUTPUT | SQL_SHORT | SQL_STRUCT | SQL_SIGNED | SQL_UNSIGNED ; symbol: ColLabel ; ECPGColId: ecpg_ident | unreserved_keyword | col_name_keyword | ECPGunreserved_interval | ECPGKeywords | ECPGCKeywords | CHAR_P | VALUES ; /* * Name classification hierarchy. * * These productions should match those in the core grammar, except that * we use all_unreserved_keyword instead of unreserved_keyword, and * where possible include ECPG keywords as well as core keywords. */ /* Column identifier --- names that can be column, table, etc names. */ ColId: ecpg_ident | all_unreserved_keyword | col_name_keyword | ECPGKeywords | ECPGCKeywords | CHAR_P | VALUES ; /* Type/function identifier --- names that can be type or function names. */ type_function_name: ecpg_ident | all_unreserved_keyword | type_func_name_keyword | ECPGKeywords | ECPGCKeywords | ECPGTypeName ; /* Column label --- allowed labels in "AS" clauses. * This presently includes *all* Postgres keywords. */ ColLabel: ECPGColLabel | ECPGTypeName | CHAR_P | CURRENT_P | INPUT_P | INT_P | TO | UNION | VALUES | ECPGCKeywords | ECPGunreserved_interval ; ECPGColLabel: ecpg_ident | unreserved_keyword | col_name_keyword | type_func_name_keyword | reserved_keyword | ECPGKeywords_vanames | ECPGKeywords_rest | CONNECTION ; ECPGCKeywords: S_AUTO | S_CONST | S_EXTERN | S_REGISTER | S_STATIC | S_TYPEDEF | S_VOLATILE ; /* "Unreserved" keywords --- available for use as any kind of name. */ /* * The following symbols must be excluded from ECPGColLabel and directly * included into ColLabel to enable C variables to get names from ECPGColLabel: * DAY_P, HOUR_P, MINUTE_P, MONTH_P, SECOND_P, YEAR_P. * * We also have to exclude CONNECTION, CURRENT, and INPUT for various reasons. * CONNECTION can be added back in all_unreserved_keyword, but CURRENT and * INPUT are reserved for ecpg purposes. * * The mentioned exclusions are done by $replace_line settings in parse.pl. */ all_unreserved_keyword: unreserved_keyword | ECPGunreserved_interval | CONNECTION ; ECPGunreserved_interval: DAY_P | HOUR_P | MINUTE_P | MONTH_P | SECOND_P | YEAR_P ; into_list: coutputvariable | into_list ',' coutputvariable ; ecpgstart: SQL_START { reset_variables(); pacounter = 1; @$ = ""; } ; c_args: /* EMPTY */ | c_list ; coutputvariable: cvariable indicator { add_variable_to_head(&argsresult, find_variable(@1), find_variable(@2)); } | cvariable { add_variable_to_head(&argsresult, find_variable(@1), &no_indicator); } ; civarind: cvariable indicator { if (find_variable(@2)->type->type == ECPGt_array) mmerror(PARSE_ERROR, ET_ERROR, "arrays of indicators are not allowed on input"); add_variable_to_head(&argsinsert, find_variable(@1), find_variable(@2)); @$ = create_questionmarks(@1, false); } ; char_civar: char_variable { char *ptr = strstr(@1, ".arr"); if (ptr) /* varchar, we need the struct name here, not * the struct element */ *ptr = '\0'; add_variable_to_head(&argsinsert, find_variable(@1), &no_indicator); } ; civar: cvariable { add_variable_to_head(&argsinsert, find_variable(@1), &no_indicator); @$ = create_questionmarks(@1, false); } ; indicator: cvariable { check_indicator((find_variable(@1))->type); } | SQL_INDICATOR cvariable { check_indicator((find_variable(@2))->type); @$ = @2; } | SQL_INDICATOR name { check_indicator((find_variable(@2))->type); @$ = @2; } ; cvariable: CVARIABLE { /* * As long as multidimensional arrays are not implemented we have to * check for those here */ const char *ptr = @1; int brace_open = 0, brace = false; for (; *ptr; ptr++) { switch (*ptr) { case '[': if (brace) mmfatal(PARSE_ERROR, "multidimensional arrays for simple data types are not supported"); brace_open++; break; case ']': brace_open--; if (brace_open == 0) brace = true; break; case '\t': case ' ': break; default: if (brace_open == 0) brace = false; break; } } } ; ecpg_param: PARAM ; ecpg_bconst: BCONST ; ecpg_fconst: FCONST ; ecpg_sconst: SCONST ; ecpg_xconst: XCONST ; ecpg_ident: IDENT | CSTRING { @$ = make3_str("\"", @1, "\""); } ; quoted_ident_stringvar: name { @$ = make3_str("\"", @1, "\""); } | char_variable { @$ = make3_str("(", @1, ")"); } ; /* * C stuff */ c_stuff_item: c_anything | '(' ')' { @$ = "()"; } | '(' c_stuff ')' ; c_stuff: c_stuff_item | c_stuff c_stuff_item ; c_list: c_term | c_list ',' c_term ; c_term: c_stuff | '{' c_list '}' ; c_thing: c_anything | '(' | ')' | ',' | ';' ; /* * Note: NULL_P is treated specially to force it to be output in upper case, * since it's likely meant as a reference to the standard C macro NULL. */ c_anything: ecpg_ident | Iconst | ecpg_fconst | ecpg_sconst | '*' | '+' | '-' | '/' | '%' | NULL_P { @$ = "NULL"; } | S_ADD | S_AND | S_ANYTHING | S_AUTO | S_CONST | S_DEC | S_DIV | S_DOTPOINT | S_EQUAL | S_EXTERN | S_INC | S_LSHIFT | S_MEMBER | S_MEMPOINT | S_MOD | S_MUL | S_NEQUAL | S_OR | S_REGISTER | S_RSHIFT | S_STATIC | S_SUB | S_TYPEDEF | S_VOLATILE | SQL_BOOL | ENUM_P | HOUR_P | INT_P | SQL_LONG | MINUTE_P | MONTH_P | SECOND_P | SQL_SHORT | SQL_SIGNED | SQL_STRUCT | SQL_UNSIGNED | YEAR_P | CHAR_P | FLOAT_P | TO | UNION | VARCHAR | '[' | ']' | '=' | ':' ; DeallocateStmt: DEALLOCATE prepared_name { check_declared_list(@2); @$ = @2; } | DEALLOCATE PREPARE prepared_name { check_declared_list(@3); @$ = @3; } | DEALLOCATE ALL { @$ = "all"; } | DEALLOCATE PREPARE ALL { @$ = "all"; } ; Iresult: Iconst | '(' Iresult ')' | Iresult '+' Iresult | Iresult '-' Iresult | Iresult '*' Iresult | Iresult '/' Iresult | Iresult '%' Iresult | ecpg_sconst | ColId | ColId '(' var_type ')' { if (pg_strcasecmp(@1, "sizeof") != 0) mmerror(PARSE_ERROR, ET_ERROR, "operator not allowed in variable definition"); else @$ = cat_str(4, @1, "(", $3.type_str, ")"); } ; execute_rest: /* EMPTY */ | ecpg_using opt_ecpg_into | ecpg_into ecpg_using | ecpg_into ; ecpg_into: INTO into_list { /* always suppress this from the constructed string */ @$ = ""; } | into_descriptor ; opt_ecpg_into: /* EMPTY */ | ecpg_into ; ecpg_fetch_into: ecpg_into | using_descriptor { struct variable *var; var = argsinsert->variable; remove_variable_from_list(&argsinsert, var); add_variable_to_head(&argsresult, var, &no_indicator); } ; opt_ecpg_fetch_into: /* EMPTY */ | ecpg_fetch_into ; %% void base_yyerror(const char *error) { /* translator: %s is typically the translation of "syntax error" */ mmerror(PARSE_ERROR, ET_ERROR, "%s at or near \"%s\"", _(error), token_start ? token_start : base_yytext); } void parser_init(void) { /* * This function is empty. It only exists for compatibility with the * backend parser right now. */ }