+ variable_declarations sql_enddeclare
+ {
+ fprintf(yyout, "%s/* exec sql end declare section */", $3);
+ free($3);
+ output_line_number();
+ }
+
+sql_startdeclare : ecpgstart BEGIN_TRANS DECLARE SQL_SECTION ';' {}
+
+sql_enddeclare: ecpgstart END_TRANS DECLARE SQL_SECTION ';' {}
+
+variable_declarations: /* empty */ { $$ = EMPTY; }
+ | declarations { $$ = $1; }
+
+declarations: declaration { $$ = $1; }
+ | declarations declaration { $$ = cat2_str($1, $2); }
+
+declaration: storage_clause storage_modifier
+ {
+ actual_storage[struct_level] = cat2_str(mm_strdup($1), mm_strdup($2));
+ actual_startline[struct_level] = hashline_number();
+ }
+ type
+ {
+ actual_type[struct_level].type_enum = $4.type_enum;
+ actual_type[struct_level].type_dimension = $4.type_dimension;
+ actual_type[struct_level].type_index = $4.type_index;
+
+ /* we do not need the string "varchar" for output */
+ /* so replace it with an empty string */
+ if ($4.type_enum == ECPGt_varchar)
+ {
+ free($4.type_str);
+ $4.type_str=EMPTY;
+ }
+ }
+ variable_list ';'
+ {
+ $$ = cat_str(6, actual_startline[struct_level], $1, $2, $4.type_str, $6, make_str(";\n"));
+ }
+
+storage_clause : S_EXTERN { $$ = make_str("extern"); }
+ | S_STATIC { $$ = make_str("static"); }
+ | S_REGISTER { $$ = make_str("register"); }
+ | S_AUTO { $$ = make_str("auto"); }
+ | /* empty */ { $$ = EMPTY; }
+
+storage_modifier : S_CONST { $$ = make_str("const"); }
+ | S_VOLATILE { $$ = make_str("volatile"); }
+ | /* empty */ { $$ = EMPTY; }
+
+type: simple_type
+ {
+ $$.type_enum = $1;
+ $$.type_str = mm_strdup(ECPGtype_name($1));
+ $$.type_dimension = -1;
+ $$.type_index = -1;
+ }
+ | varchar_type
+ {
+ $$.type_enum = ECPGt_varchar;
+ $$.type_str = make_str("varchar");;
+ $$.type_dimension = -1;
+ $$.type_index = -1;
+ }
+ | struct_type
+ {
+ $$.type_enum = ECPGt_struct;
+ $$.type_str = $1;
+ $$.type_dimension = -1;
+ $$.type_index = -1;
+ }
+ | union_type
+ {
+ $$.type_enum = ECPGt_union;
+ $$.type_str = $1;
+ $$.type_dimension = -1;
+ $$.type_index = -1;
+ }
+ | enum_type
+ {
+ $$.type_str = $1;
+ $$.type_enum = ECPGt_int;
+ $$.type_dimension = -1;
+ $$.type_index = -1;
+ }
+ | symbol
+ {
+ /* this is for typedef'ed types */
+ struct typedefs *this = get_typedef($1);
+
+ $$.type_str = (this->type->type_enum == ECPGt_varchar) ? EMPTY : mm_strdup(this->name);
+ $$.type_enum = this->type->type_enum;
+ $$.type_dimension = this->type->type_dimension;
+ $$.type_index = this->type->type_index;
+ struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
+ }
+
+enum_type: SQL_ENUM opt_symbol enum_definition
+ {
+ $$ = cat_str(3, make_str("enum"), $2, $3);
+ }
+ | SQL_ENUM symbol
+ {
+ $$ = cat2_str(make_str("enum"), $2);
+ }
+
+enum_definition: '{' c_list '}' { $$ = cat_str(3, make_str("{"), $2, make_str("}")); }
+
+struct_type: s_struct '{' variable_declarations '}'
+ {
+ ECPGfree_struct_member(struct_member_list[struct_level]);
+ free(actual_storage[struct_level--]);
+ $$ = cat_str(4, $1, make_str("{"), $3, make_str("}"));
+ }
+
+union_type: s_union '{' variable_declarations '}'
+ {
+ ECPGfree_struct_member(struct_member_list[struct_level]);
+ free(actual_storage[struct_level--]);
+ $$ = cat_str(4, $1, make_str("{"), $3, make_str("}"));
+ }
+
+s_struct: SQL_STRUCT opt_symbol
+ {
+ struct_member_list[struct_level++] = NULL;
+ if (struct_level >= STRUCT_DEPTH)
+ mmerror(ET_ERROR, "Too many levels in nested structure definition");
+
+ /* reset this variable so we see if there was */
+ /* an initializer specified */
+ initializer = 0;
+
+ $$ = cat2_str(make_str("struct"), $2);
+ }
+
+s_union: UNION opt_symbol
+ {
+ struct_member_list[struct_level++] = NULL;
+ if (struct_level >= STRUCT_DEPTH)
+ mmerror(ET_ERROR, "Too many levels in nested structure definition");
+
+ /* reset this variable so we see if there was */
+ /* an initializer specified */
+ initializer = 0;
+
+ $$ = cat2_str(make_str("union"), $2);
+ }
+
+opt_symbol: /* empty */ { $$ = EMPTY; }
+ | symbol { $$ = $1; }
+
+simple_type: unsigned_type { $$=$1; }
+ | opt_signed signed_type { $$=$2; }
+ ;
+
+unsigned_type: SQL_UNSIGNED SQL_SHORT { $$ = ECPGt_unsigned_short; }
+ | SQL_UNSIGNED SQL_SHORT SQL_INT { $$ = ECPGt_unsigned_short; }
+ | SQL_UNSIGNED { $$ = ECPGt_unsigned_int; }
+ | SQL_UNSIGNED SQL_INT { $$ = ECPGt_unsigned_int; }
+ | SQL_UNSIGNED SQL_LONG { $$ = ECPGt_unsigned_long; }
+ | SQL_UNSIGNED SQL_LONG SQL_INT { $$ = ECPGt_unsigned_long; }
+ | SQL_UNSIGNED CHAR { $$ = ECPGt_unsigned_char; }
+ ;
+
+signed_type: SQL_SHORT { $$ = ECPGt_short; }
+ | SQL_SHORT SQL_INT { $$ = ECPGt_short; }
+ | SQL_INT { $$ = ECPGt_int; }
+ | SQL_LONG { $$ = ECPGt_long; }
+ | SQL_LONG SQL_INT { $$ = ECPGt_long; }
+ | SQL_BOOL { $$ = ECPGt_bool; };
+ | FLOAT { $$ = ECPGt_float; }
+ | DOUBLE { $$ = ECPGt_double; }
+ | CHAR { $$ = ECPGt_char; }
+ ;
+
+opt_signed: SQL_SIGNED
+ | /* EMPTY */
+ ;
+
+varchar_type: VARCHAR { $$ = ECPGt_varchar; }
+
+variable_list: variable
+ {
+ $$ = $1;
+ }
+ | variable_list ',' variable
+ {
+ $$ = cat_str(3, $1, make_str(","), $3);
+ }
+
+variable: opt_pointer symbol opt_array_bounds opt_initializer
+ {
+ struct ECPGtype * type;
+ int dimension = $3.index1; /* dimension of array */
+ int length = $3.index2; /* lenght of string */
+ char dim[14L], ascii_len[12];
+
+ adjust_array(actual_type[struct_level].type_enum, &dimension, &length, actual_type[struct_level].type_dimension, actual_type[struct_level].type_index, strlen($1));
+
+ switch (actual_type[struct_level].type_enum)
+ {
+ case ECPGt_struct:
+ case ECPGt_union:
+ if (dimension < 0)
+ type = ECPGmake_struct_type(struct_member_list[struct_level], actual_type[struct_level].type_enum);
+ else
+ type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level], actual_type[struct_level].type_enum), dimension);
+
+ $$ = cat_str(4, $1, mm_strdup($2), $3.str, $4);
+ break;
+ case ECPGt_varchar:
+ if (dimension == -1)
+ type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length);
+ else
+ type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length), dimension);
+
+ switch(dimension)
+ {
+ case 0:
+ case -1:
+ case 1:
+ *dim = '\0';
+ break;
+ default:
+ sprintf(dim, "[%d]", dimension);
+ break;
+ }
+ sprintf(ascii_len, "%d", length);
+
+ if (length == 0)
+ mmerror(ET_ERROR, "pointer to varchar are not implemented");
+
+ if (dimension == 0)
+ $$ = cat_str(7, mm_strdup(actual_storage[struct_level]), make2_str(make_str(" struct varchar_"), mm_strdup($2)), make_str(" { int len; char arr["), mm_strdup(ascii_len), make_str("]; } *"), mm_strdup($2), $4);
+ else
+ $$ = cat_str(8, mm_strdup(actual_storage[struct_level]), make2_str(make_str(" struct varchar_"), mm_strdup($2)), make_str(" { int len; char arr["), mm_strdup(ascii_len), make_str("]; } "), mm_strdup($2), mm_strdup(dim), $4);
+
+ break;
+ case ECPGt_char:
+ case ECPGt_unsigned_char:
+ if (dimension == -1)
+ type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length);
+ else
+ type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length), dimension);
+
+ $$ = cat_str(4, $1, mm_strdup($2), $3.str, $4);
+ break;
+ default:
+ if (dimension < 0)
+ type = ECPGmake_simple_type(actual_type[struct_level].type_enum, 1);
+ else
+ type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, 1), dimension);
+
+ $$ = cat_str(4, $1, mm_strdup($2), $3.str, $4);
+ break;
+ }
+
+ if (struct_level == 0)
+ new_variable($2, type);
+ else
+ ECPGmake_struct_member($2, type, &(struct_member_list[struct_level - 1]));
+
+ free($2);
+ }
+
+opt_initializer: /* empty */ { $$ = EMPTY; }
+ | '=' c_term {
+ initializer = 1;
+ $$ = cat2_str(make_str("="), $2);
+ }
+
+opt_pointer: /* empty */ { $$ = EMPTY; }
+ | '*' { $$ = make_str("*"); }
+
+/*
+ * As long as the prepare statement is not supported by the backend, we will
+ * try to simulate it here so we get dynamic SQL
+ */
+ECPGDeclare: DECLARE STATEMENT ident
+ {
+ /* this is only supported for compatibility */
+ $$ = cat_str(3, make_str("/* declare statement"), $3, make_str("*/"));
+ }
+/*
+ * the exec sql disconnect statement: disconnect from the given database
+ */
+ECPGDisconnect: SQL_DISCONNECT dis_name { $$ = $2; }
+
+dis_name: connection_object { $$ = $1; }
+ | CURRENT { $$ = make_str("CURRENT"); }
+ | ALL { $$ = make_str("ALL"); }
+ | /* empty */ { $$ = make_str("CURRENT"); }
+
+connection_object: connection_target { $$ = $1; }
+ | DEFAULT { $$ = make_str("DEFAULT"); }
+
+/*
+ * execute a given string as sql command
+ */
+ECPGExecute : EXECUTE IMMEDIATE execstring
+ {
+ struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable));
+
+ thisquery->type = &ecpg_query;
+ thisquery->brace_level = 0;
+ thisquery->next = NULL;
+ thisquery->name = $3;
+
+ add_variable(&argsinsert, thisquery, &no_indicator);
+
+ $$ = make_str("?");
+ }
+ | EXECUTE ident
+ {
+ struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable));
+
+ thisquery->type = &ecpg_query;
+ thisquery->brace_level = 0;
+ thisquery->next = NULL;
+ thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(\"\")") + strlen($2));
+ sprintf(thisquery->name, "ECPGprepared_statement(\"%s\")", $2);
+
+ add_variable(&argsinsert, thisquery, &no_indicator);
+ } ecpg_using
+ {
+ $$ = make_str("?");
+ }
+
+execstring: char_variable |
+ CSTRING { $$ = make3_str(make_str("\""), $1, make_str("\"")); };
+
+/*
+ * the exec sql free command to deallocate a previously
+ * prepared statement
+ */
+ECPGFree: SQL_FREE ident { $$ = $2; }
+
+/*
+ * open is an open cursor, at the moment this has to be removed
+ */
+ECPGOpen: SQL_OPEN name ecpg_using {
+ $$ = $2;
+};
+
+ecpg_using: /* empty */ { $$ = EMPTY; }
+ | USING variablelist {
+ /* mmerror ("open cursor with variables not implemented yet"); */
+ $$ = EMPTY;
+ }
+
+variablelist: cinputvariable | cinputvariable ',' variablelist
+
+/*
+ * As long as the prepare statement is not supported by the backend, we will
+ * try to simulate it here so we get dynamic SQL
+ */
+ECPGPrepare: SQL_PREPARE ident FROM execstring
+ {
+ $$ = cat2_str(make3_str(make_str("\""), $2, make_str("\",")), $4);
+ }
+
+/*
+ * dynamic SQL: descriptor based access
+ * written by Christof Petig <christof.petig@wtal.de>
+ */
+
+/*
+ * deallocate a descriptor
+ */
+ECPGDeallocateDescr: SQL_DEALLOCATE SQL_DESCRIPTOR ident
+{ drop_descriptor($3,connection);
+ $$ = $3;
+}
+
+/*
+ * allocate a descriptor
+ */
+ECPGAllocateDescr: SQL_ALLOCATE SQL_DESCRIPTOR ident
+{ add_descriptor($3,connection);
+ $$ = $3;
+}
+
+/*
+ * read from descriptor
+ */
+
+ECPGGetDescHeaderItem: cvariable '=' ident {
+ push_assignment($1,$3);
+}
+
+ECPGGetDescItem: cvariable '=' ident {
+ push_assignment($1,$3);
+}
+ | cvariable '=' TYPE_P {
+ push_assignment($1,"type");
+}
+ | cvariable '=' PRECISION {
+ push_assignment($1,"precision");
+}
+ | cvariable '=' SQL_INDICATOR {
+ push_assignment($1,"indicator");
+}
+
+ECPGGetDescHeaderItems: ECPGGetDescHeaderItem
+ | ECPGGetDescHeaderItems ',' ECPGGetDescHeaderItem;
+
+ECPGGetDescItems: ECPGGetDescItem
+ | ECPGGetDescItems ',' ECPGGetDescItem;
+
+ECPGGetDescriptorHeader: SQL_GET SQL_DESCRIPTOR ident ECPGGetDescHeaderItems
+{ $$ = $3; }
+
+ECPGGetDescriptor: SQL_GET SQL_DESCRIPTOR ident SQL_VALUE cvariable ECPGGetDescItems
+{ $$ = $3; descriptor_index=$5; }
+ | SQL_GET SQL_DESCRIPTOR ident SQL_VALUE Iconst ECPGGetDescItems
+{ $$ = $3; descriptor_index=$5; }
+
+/*
+ * fetch [ in | from ] <portalname> into sql descriptor <name>
+ */
+
+FetchDescriptorStmt: FETCH from_in name INTO SQL_SQL SQL_DESCRIPTOR ident
+ {
+ $$ = cat_str(3, make_str("fetch"), $2, $3);
+ descriptor_name=$7;
+ }
+ | FETCH name INTO SQL_SQL SQL_DESCRIPTOR ident
+ {
+ $$ = cat2_str(make_str("fetch"), $2);
+ descriptor_name=$6;
+ }
+ ;
+
+/*
+ * for compatibility with ORACLE we will also allow the keyword RELEASE
+ * after a transaction statement to disconnect from the database.
+ */
+
+ECPGRelease: TransactionStmt SQL_RELEASE
+ {
+ if (strcmp($1, "begin") == 0)
+ mmerror(ET_ERROR, "RELEASE does not make sense when beginning a transaction");
+
+ fprintf(yyout, "ECPGtrans(__LINE__, %s, \"%s\");",
+ connection ? connection : "NULL", $1);
+ whenever_action(0);
+ fprintf(yyout, "ECPGdisconnect(__LINE__, \"\");");
+ whenever_action(0);
+ free($1);
+ }
+
+/*
+ * set/reset the automatic transaction mode, this needs a differnet handling
+ * as the other set commands
+ */
+ECPGSetAutocommit: SET SQL_AUTOCOMMIT to_equal on_off
+ {
+ $$ = $4;
+ }
+
+on_off: ON { $$ = make_str("on"); }
+ | SQL_OFF { $$ = make_str("off"); }
+
+to_equal: TO | '=';