postgresql/src/interfaces/ecpg/preproc/ecpg.trailer

1889 lines
55 KiB
Plaintext

/* src/interfaces/ecpg/preproc/ecpg.trailer */
statements: /*EMPTY*/
| statements statement
;
statement: ecpgstart at stmt ';' { connection = NULL; }
| ecpgstart stmt ';'
| ecpgstart ECPGVarDeclaration
{
fprintf(base_yyout, "%s", $2);
free($2);
output_line_number();
}
| ECPGDeclaration
| c_thing { fprintf(base_yyout, "%s", $1); free($1); }
| CPP_LINE { fprintf(base_yyout, "%s", $1); free($1); }
| '{' { braces_open++; fputs("{", base_yyout); }
| '}'
{
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");
$$ = cat_str(7, mm_strdup("create"), $2, mm_strdup("table"), $4, mm_strdup("as"), $7, $8);
}
| 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");
$$ = cat_str(7, mm_strdup("create"), $2, mm_strdup("table if not exists"), $7, mm_strdup("as"), $10, $11);
}
;
at: AT connection_object
{
connection = $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, mm_strdup(","), $5, mm_strdup(","), $4); }
| SQL_CONNECT TO DEFAULT
{ $$ = mm_strdup("NULL, NULL, NULL, \"DEFAULT\""); }
/* also allow ORACLE syntax */
| SQL_CONNECT ora_user
{ $$ = cat_str(3, mm_strdup("NULL,"), $2, mm_strdup(", NULL")); }
| DATABASE connection_target
{ $$ = cat2_str($2, mm_strdup(", 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(mm_strdup("\""), make3_str($1, $2, $3), mm_strdup("\""));
}
| db_prefix ':' server opt_port '/' opt_database_name opt_options
{
/* new style: <tcp|unix>:postgresql://server[:port][/dbname] */
if (strncmp($1, "unix:postgresql", strlen("unix:postgresql")) != 0 && strncmp($1, "tcp:postgresql", strlen("tcp:postgresql")) != 0)
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(mm_strdup("\""), $1, mm_strdup(":")), $3, make3_str(make3_str($4, mm_strdup("/"), $6), $7, mm_strdup("\"")));
}
| char_variable
{
$$ = $1;
}
| ecpg_sconst
{
/* We can only process double quoted strings not single quotes ones,
* so we change the quotes.
* Note, that the rule for ecpg_sconst adds these single quotes. */
$1[0] = '\"';
$1[strlen($1)-1] = '\"';
$$ = $1;
}
;
opt_database_name: database_name { $$ = $1; }
| /*EMPTY*/ { $$ = 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, mm_strdup(":"), $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 { $$ = $1; }
| /*EMPTY*/ { $$ = EMPTY; }
;
server_name: ColId { $$ = $1; }
| ColId '.' server_name { $$ = make3_str($1, mm_strdup("."), $3); }
| IP { $$ = make_name(); }
;
opt_port: ':' Iconst { $$ = make2_str(mm_strdup(":"), $2); }
| /*EMPTY*/ { $$ = EMPTY; }
;
opt_connection_name: AS connection_object { $$ = $2; }
| /*EMPTY*/ { $$ = mm_strdup("NULL"); }
;
opt_user: USER ora_user { $$ = $2; }
| /*EMPTY*/ { $$ = mm_strdup("NULL, NULL"); }
;
ora_user: user_name
{ $$ = cat2_str($1, mm_strdup(", NULL")); }
| user_name '/' user_name
{ $$ = cat_str(3, $1, mm_strdup(","), $3); }
| user_name SQL_IDENTIFIED BY user_name
{ $$ = cat_str(3, $1, mm_strdup(","), $4); }
| user_name USING user_name
{ $$ = cat_str(3, $1, mm_strdup(","), $3); }
;
user_name: RoleId
{
if ($1[0] == '\"')
$$ = $1;
else
$$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
}
| ecpg_sconst
{
if ($1[0] == '\"')
$$ = $1;
else
$$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
}
| 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(mm_strdup(argsinsert->variable->name), mm_strdup(".arr"));
else
$$ = mm_strdup(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, mm_strdup(".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(mm_strdup("?"), $2);
}
| /*EMPTY*/ { $$ = 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);
$$ = cat_str(3, make2_str($1, $2), $3, $4);
}
;
opt_opt_value: /*EMPTY*/
{ $$ = EMPTY; }
| '=' Iconst
{ $$ = make2_str(mm_strdup("="), $2); }
| '=' ecpg_ident
{ $$ = make2_str(mm_strdup("="), $2); }
| '=' civar
{ $$ = make2_str(mm_strdup("="), $2); }
;
prepared_name: name
{
if ($1[0] == '\"' && $1[strlen($1)-1] == '\"') /* already quoted? */
$$ = $1;
else /* not quoted => convert to lowercase */
{
size_t i;
for (i = 0; i< strlen($1); i++)
$1[i] = tolower((unsigned char) $1[i]);
$$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
}
}
| char_variable { $$ = $1; }
;
/*
* 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;
char *cursor_marker = $2[0] == ':' ? mm_strdup("$0") : mm_strdup($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));
const char *con = connection ? connection : "NULL";
char *comment;
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 = $2;
this->function = (current_function ? mm_strdup(current_function) : NULL);
this->connection = connection;
this->command = cat_str(6, mm_strdup("declare"), cursor_marker, $3, mm_strdup("cursor"), $5, mm_strdup("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, mm_strdup("/*"), mm_strdup(this->command), mm_strdup("*/"));
$$ = 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 { $$ = $1; }
| var_declaration { $$ = $1; }
;
precision: NumericOnly { $$ = $1; };
opt_scale: ',' NumericOnly { $$ = $2; }
| /* EMPTY */ { $$ = EMPTY; }
;
ecpg_interval: opt_interval { $$ = $1; }
| YEAR_P TO MINUTE_P { $$ = mm_strdup("year to minute"); }
| YEAR_P TO SECOND_P { $$ = mm_strdup("year to second"); }
| DAY_P TO DAY_P { $$ = mm_strdup("day to day"); }
| MONTH_P TO MONTH_P { $$ = mm_strdup("month to month"); }
;
/*
* 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);
free($3);
output_line_number();
}
;
sql_startdeclare: ecpgstart BEGIN_P DECLARE SQL_SECTION ';' {};
sql_enddeclare: ecpgstart END_P DECLARE SQL_SECTION ';' {};
var_type_declarations: /*EMPTY*/ { $$ = EMPTY; }
| vt_declarations { $$ = $1; }
;
vt_declarations: single_vt_declaration { $$ = $1; }
| CPP_LINE { $$ = $1; }
| vt_declarations single_vt_declaration { $$ = cat2_str($1, $2); }
| vt_declarations CPP_LINE { $$ = cat2_str($1, $2); }
;
variable_declarations: var_declaration { $$ = $1; }
| variable_declarations var_declaration { $$ = cat2_str($1, $2); }
;
type_declaration: S_TYPEDEF
{
/* reset this variable so we see if there was */
/* an initializer specified */
initializer = 0;
}
var_type opt_pointer ECPGColLabelCommon 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();
$$ = mm_strdup("");
};
var_declaration: storage_declaration
var_type
{
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, mm_strdup(";\n"));
}
| var_type
{
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, mm_strdup(";\n"));
}
| struct_union_type_with_symbol ';'
{
$$ = cat2_str($1, mm_strdup(";"));
}
;
opt_bit_field: ':' Iconst { $$ =cat2_str(mm_strdup(":"), $2); }
| /* EMPTY */ { $$ = EMPTY; }
;
storage_declaration: storage_clause storage_modifier
{$$ = cat2_str ($1, $2); }
| storage_clause {$$ = $1; }
| storage_modifier {$$ = $1; }
;
storage_clause : S_EXTERN { $$ = mm_strdup("extern"); }
| S_STATIC { $$ = mm_strdup("static"); }
| S_REGISTER { $$ = mm_strdup("register"); }
| S_AUTO { $$ = mm_strdup("auto"); }
;
storage_modifier : S_CONST { $$ = mm_strdup("const"); }
| S_VOLATILE { $$ = mm_strdup("volatile"); }
;
var_type: simple_type
{
$$.type_enum = $1;
$$.type_str = mm_strdup(ecpg_type_name($1));
$$.type_dimension = mm_strdup("-1");
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
| struct_union_type
{
$$.type_str = $1;
$$.type_dimension = mm_strdup("-1");
$$.type_index = mm_strdup("-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 = $1;
$$.type_enum = ECPGt_int;
$$.type_dimension = mm_strdup("-1");
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
| ECPGColLabelCommon '(' precision opt_scale ')'
{
if (strcmp($1, "numeric") == 0)
{
$$.type_enum = ECPGt_numeric;
$$.type_str = mm_strdup("numeric");
}
else if (strcmp($1, "decimal") == 0)
{
$$.type_enum = ECPGt_decimal;
$$.type_str = mm_strdup("decimal");
}
else
{
mmerror(PARSE_ERROR, ET_ERROR, "only data types numeric and decimal have precision/scale argument");
$$.type_enum = ECPGt_numeric;
$$.type_str = mm_strdup("numeric");
}
$$.type_dimension = mm_strdup("-1");
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
| ECPGColLabelCommon ecpg_interval
{
if (strlen($2) != 0 && strcmp ($1, "datetime") != 0 && strcmp ($1, "interval") != 0)
mmerror (PARSE_ERROR, ET_ERROR, "interval specification not allowed here");
/*
* Check for type names that the SQL grammar treats as
* unreserved keywords
*/
if (strcmp($1, "varchar") == 0)
{
$$.type_enum = ECPGt_varchar;
$$.type_str = EMPTY; /*mm_strdup("varchar");*/
$$.type_dimension = mm_strdup("-1");
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
else if (strcmp($1, "bytea") == 0)
{
$$.type_enum = ECPGt_bytea;
$$.type_str = EMPTY;
$$.type_dimension = mm_strdup("-1");
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
else if (strcmp($1, "float") == 0)
{
$$.type_enum = ECPGt_float;
$$.type_str = mm_strdup("float");
$$.type_dimension = mm_strdup("-1");
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
else if (strcmp($1, "double") == 0)
{
$$.type_enum = ECPGt_double;
$$.type_str = mm_strdup("double");
$$.type_dimension = mm_strdup("-1");
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
else if (strcmp($1, "numeric") == 0)
{
$$.type_enum = ECPGt_numeric;
$$.type_str = mm_strdup("numeric");
$$.type_dimension = mm_strdup("-1");
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
else if (strcmp($1, "decimal") == 0)
{
$$.type_enum = ECPGt_decimal;
$$.type_str = mm_strdup("decimal");
$$.type_dimension = mm_strdup("-1");
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
else if (strcmp($1, "date") == 0)
{
$$.type_enum = ECPGt_date;
$$.type_str = mm_strdup("date");
$$.type_dimension = mm_strdup("-1");
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
else if (strcmp($1, "timestamp") == 0)
{
$$.type_enum = ECPGt_timestamp;
$$.type_str = mm_strdup("timestamp");
$$.type_dimension = mm_strdup("-1");
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
else if (strcmp($1, "interval") == 0)
{
$$.type_enum = ECPGt_interval;
$$.type_str = mm_strdup("interval");
$$.type_dimension = mm_strdup("-1");
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
else if (strcmp($1, "datetime") == 0)
{
$$.type_enum = ECPGt_timestamp;
$$.type_str = mm_strdup("timestamp");
$$.type_dimension = mm_strdup("-1");
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
else if ((strcmp($1, "string") == 0) && INFORMIX_MODE)
{
$$.type_enum = ECPGt_string;
$$.type_str = mm_strdup("char");
$$.type_dimension = mm_strdup("-1");
$$.type_index = mm_strdup("-1");
$$.type_sizeof = NULL;
}
else
{
/* this is for typedef'ed types */
struct typedefs *this = get_typedef($1);
$$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? EMPTY : 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, mm_strdup("sizeof("), mm_strdup(this->name), mm_strdup(")"));
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);
$$.type_str = mm_strdup(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;
struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
free(name);
}
else
{
$$.type_str = name;
$$.type_enum = ECPGt_long;
$$.type_dimension = mm_strdup("-1");
$$.type_index = mm_strdup("-1");
$$.type_sizeof = mm_strdup("");
struct_member_list[struct_level] = NULL;
}
}
;
enum_type: ENUM_P symbol enum_definition
{ $$ = cat_str(3, mm_strdup("enum"), $2, $3); }
| ENUM_P enum_definition
{ $$ = cat2_str(mm_strdup("enum"), $2); }
| ENUM_P symbol
{ $$ = cat2_str(mm_strdup("enum"), $2); }
;
enum_definition: '{' c_list '}'
{ $$ = cat_str(3, mm_strdup("{"), $2, mm_strdup("}")); };
struct_union_type_with_symbol: s_struct_union_symbol
{
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 (strncmp($1.su, "struct", sizeof("struct")-1) == 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_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;
this->struct_member_list = struct_member_list[struct_level];
types = this;
$$ = cat_str(4, su_type.type_str, mm_strdup("{"), $4, mm_strdup("}"));
}
;
struct_union_type: struct_union_type_with_symbol { $$ = $1; }
| s_struct_union
{
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, mm_strdup("{"), $4, mm_strdup("}"));
}
;
s_struct_union_symbol: SQL_STRUCT symbol
{
$$.su = mm_strdup("struct");
$$.symbol = $2;
ECPGstruct_sizeof = cat_str(3, mm_strdup("sizeof("), cat2_str(mm_strdup($$.su), mm_strdup($$.symbol)), mm_strdup(")"));
}
| UNION symbol
{
$$.su = mm_strdup("union");
$$.symbol = $2;
}
;
s_struct_union: SQL_STRUCT
{
ECPGstruct_sizeof = mm_strdup(""); /* This must not be NULL to distinguish from simple types. */
$$ = mm_strdup("struct");
}
| UNION
{
$$ = mm_strdup("union");
}
;
simple_type: unsigned_type { $$=$1; }
| 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
{ $$ = $1; }
| variable_list ',' variable
{
if (actual_type[struct_level].type_enum == ECPGt_varchar || actual_type[struct_level].type_enum == ECPGt_bytea)
$$ = cat_str(3, $1, mm_strdup(";"), $3);
else
$$ = cat_str(3, $1, mm_strdup(","), $3);
}
;
variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initializer
{
struct ECPGtype * type;
char *dimension = $3.index1; /* dimension of array */
char *length = $3.index2; /* length of string */
char *dim_str;
char *vcn;
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, mm_strdup($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=mm_strdup("");
else
dim_str=cat_str(3, mm_strdup("["), mm_strdup(dimension), mm_strdup("]"));
/* 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 */
vcn = (char *) mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
sprintf(vcn, "%d", *varlen_type_counter);
if (strcmp(dimension, "0") == 0)
$$ = cat_str(7, make2_str(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } *"), mm_strdup($2), $4, $5);
else
$$ = cat_str(8, make2_str(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } "), mm_strdup($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 <var>[] = "string" */
{
/* if we have an initializer but no string size set, let's use the initializer's length */
free(length);
length = mm_alloc(i+sizeof("sizeof()"));
sprintf(length, "sizeof(%s)", $5+2);
}
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, mm_strdup($2), $3.str, $4, $5);
break;
default:
if (atoi(dimension) < 0)
type = ECPGmake_simple_type(actual_type[struct_level].type_enum, mm_strdup("1"), 0);
else
type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, mm_strdup("1"), 0), dimension);
$$ = cat_str(5, $1, mm_strdup($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]));
free($2);
}
;
opt_initializer: /*EMPTY*/
{ $$ = EMPTY; }
| '=' c_term
{
initializer = 1;
$$ = cat2_str(mm_strdup("="), $2);
}
;
opt_pointer: /*EMPTY*/ { $$ = EMPTY; }
| '*' { $$ = mm_strdup("*"); }
| '*' '*' { $$ = mm_strdup("**"); }
;
/*
* 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, mm_strdup("/* declare statement"), $3, mm_strdup("*/"));
}
;
/*
* the exec sql disconnect statement: disconnect from the given database
*/
ECPGDisconnect: SQL_DISCONNECT dis_name { $$ = $2; }
;
dis_name: connection_object { $$ = $1; }
| CURRENT_P { $$ = mm_strdup("\"CURRENT\""); }
| ALL { $$ = mm_strdup("\"ALL\""); }
| /* EMPTY */ { $$ = mm_strdup("\"CURRENT\""); }
;
connection_object: database_name { $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\"")); }
| DEFAULT { $$ = mm_strdup("\"DEFAULT\""); }
| char_variable { $$ = $1; }
;
execstring: char_variable
{ $$ = $1; }
| CSTRING
{ $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\"")); }
;
/*
* the exec sql free command to deallocate a previously
* prepared statement
*/
ECPGFree: SQL_FREE cursor_name { $$ = $2; }
| SQL_FREE ALL { $$ = mm_strdup("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*/ { $$ = EMPTY; }
| ecpg_using { $$ = $1; }
;
ecpg_using: USING using_list { $$ = EMPTY; }
| using_descriptor { $$ = $1; }
;
using_descriptor: USING SQL_P SQL_DESCRIPTOR quoted_ident_stringvar
{
add_variable_to_head(&argsinsert, descriptor_variable($4,0), &no_indicator);
$$ = EMPTY;
}
| USING SQL_DESCRIPTOR name
{
add_variable_to_head(&argsinsert, sqlda_variable($3), &no_indicator);
$$ = EMPTY;
}
;
into_descriptor: INTO SQL_P SQL_DESCRIPTOR quoted_ident_stringvar
{
add_variable_to_head(&argsresult, descriptor_variable($4,1), &no_indicator);
$$ = EMPTY;
}
| INTO SQL_DESCRIPTOR name
{
add_variable_to_head(&argsresult, sqlda_variable($3), &no_indicator);
$$ = EMPTY;
}
;
into_sqlda: INTO name
{
add_variable_to_head(&argsresult, sqlda_variable($2), &no_indicator);
$$ = EMPTY;
}
;
using_list: UsingValue | UsingValue ',' using_list;
UsingValue: UsingConst
{
char *length = mm_alloc(32);
sprintf(length, "%zu", strlen($1));
add_variable_to_head(&argsinsert, new_variable($1, ECPGmake_simple_type(ECPGt_const, length, 0), 0), &no_indicator);
}
| civar { $$ = EMPTY; }
| civarind { $$ = EMPTY; }
;
UsingConst: Iconst { $$ = $1; }
| '+' Iconst { $$ = cat_str(2, mm_strdup("+"), $2); }
| '-' Iconst { $$ = cat_str(2, mm_strdup("-"), $2); }
| ecpg_fconst { $$ = $1; }
| '+' ecpg_fconst { $$ = cat_str(2, mm_strdup("+"), $2); }
| '-' ecpg_fconst { $$ = cat_str(2, mm_strdup("-"), $2); }
| ecpg_sconst { $$ = $1; }
| ecpg_bconst { $$ = $1; }
| ecpg_xconst { $$ = $1; }
;
/*
* We accept DESCRIBE [OUTPUT] but do nothing with DESCRIBE INPUT so far.
*/
ECPGDescribe: SQL_DESCRIBE INPUT_P prepared_name using_descriptor
{
const char *con = connection ? connection : "NULL";
mmerror(PARSE_ERROR, ET_WARNING, "using unsupported DESCRIBE statement");
$$ = (char *) mm_alloc(sizeof("1, , ") + strlen(con) + strlen($3));
sprintf($$, "1, %s, %s", con, $3);
}
| SQL_DESCRIBE opt_output prepared_name using_descriptor
{
const char *con = connection ? connection : "NULL";
struct variable *var;
var = argsinsert->variable;
remove_variable_from_list(&argsinsert, var);
add_variable_to_head(&argsresult, var, &no_indicator);
$$ = (char *) mm_alloc(sizeof("0, , ") + strlen(con) + strlen($3));
sprintf($$, "0, %s, %s", con, $3);
}
| SQL_DESCRIBE opt_output prepared_name into_descriptor
{
const char *con = connection ? connection : "NULL";
$$ = (char *) mm_alloc(sizeof("0, , ") + strlen(con) + strlen($3));
sprintf($$, "0, %s, %s", con, $3);
}
| SQL_DESCRIBE INPUT_P prepared_name into_sqlda
{
const char *con = connection ? connection : "NULL";
mmerror(PARSE_ERROR, ET_WARNING, "using unsupported DESCRIBE statement");
$$ = (char *) mm_alloc(sizeof("1, , ") + strlen(con) + strlen($3));
sprintf($$, "1, %s, %s", con, $3);
}
| SQL_DESCRIBE opt_output prepared_name into_sqlda
{
const char *con = connection ? connection : "NULL";
$$ = (char *) mm_alloc(sizeof("0, , ") + strlen(con) + strlen($3));
sprintf($$, "0, %s, %s", con, $3);
}
;
opt_output: SQL_OUTPUT { $$ = mm_strdup("output"); }
| /* EMPTY */ { $$ = EMPTY; }
;
/*
* dynamic SQL: descriptor based access
* originally written by Christof Petig <christof.petig@wtal.de>
* and Peter Eisentraut <peter.eisentraut@credativ.de>
*/
/*
* allocate a descriptor
*/
ECPGAllocateDescr: SQL_ALLOCATE SQL_DESCRIPTOR quoted_ident_stringvar
{
add_descriptor($3,connection);
$$ = $3;
}
;
/*
* deallocate a descriptor
*/
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 = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
sprintf(length, "%zu", strlen($1));
new_variable($1, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
$$ = $1;
}
| cvariable
{
$$ = $1;
}
;
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 = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
sprintf(length, "%zu", strlen($1));
new_variable($1, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
$$ = $1;
}
| IntConstVar
{
$$ = $1;
}
| '-' ecpg_fconst
{
char *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
char *var = cat2_str(mm_strdup("-"), $2);
sprintf(length, "%zu", strlen(var));
new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
$$ = var;
}
| '-' Iconst
{
char *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
char *var = cat2_str(mm_strdup("-"), $2);
sprintf(length, "%zu", strlen(var));
new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
$$ = var;
}
| ecpg_sconst
{
char *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
char *var = $1 + 1;
var[strlen(var) - 1] = '\0';
sprintf(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 { $$ = mm_strdup("on"); }
| OFF { $$ = mm_strdup("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;
}
ECPGColLabelCommon 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, mm_strdup("/* exec sql type"), mm_strdup($3), mm_strdup("is"), mm_strdup($5.type_str), mm_strdup($6.str), $7, mm_strdup("*/"));
else
$$ = cat_str(6, mm_strdup("typedef "), mm_strdup($5.type_str), *$7?mm_strdup("*"):mm_strdup(""), mm_strdup($3), mm_strdup($6.str), mm_strdup(";"));
}
;
opt_reference: SQL_REFERENCE { $$ = mm_strdup("reference"); }
| /*EMPTY*/ { $$ = 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);
char *dimension = $6.index1;
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, mm_strdup("1"), 0);
else
type = ECPGmake_array_type(ECPGmake_simple_type($5.type_enum, mm_strdup("1"), 0), dimension);
break;
}
ECPGfree_type(p->type);
p->type = type;
}
$$ = cat_str(7, mm_strdup("/* exec sql var"), mm_strdup($3), mm_strdup("is"), mm_strdup($5.type_str), mm_strdup($6.str), $7, mm_strdup("*/"));
}
;
/*
* 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 = $<action>3.code;
when_error.command = $<action>3.command;
$$ = cat_str(3, mm_strdup("/* exec sql whenever sqlerror "), $3.str, mm_strdup("; */"));
}
| SQL_WHENEVER NOT SQL_FOUND action
{
when_nf.code = $<action>4.code;
when_nf.command = $<action>4.command;
$$ = cat_str(3, mm_strdup("/* exec sql whenever not found "), $4.str, mm_strdup("; */"));
}
| SQL_WHENEVER SQL_SQLWARNING action
{
when_warn.code = $<action>3.code;
when_warn.command = $<action>3.command;
$$ = cat_str(3, mm_strdup("/* exec sql whenever sql_warning "), $3.str, mm_strdup("; */"));
}
;
action : CONTINUE_P
{
$<action>$.code = W_NOTHING;
$<action>$.command = NULL;
$<action>$.str = mm_strdup("continue");
}
| SQL_SQLPRINT
{
$<action>$.code = W_SQLPRINT;
$<action>$.command = NULL;
$<action>$.str = mm_strdup("sqlprint");
}
| SQL_STOP
{
$<action>$.code = W_STOP;
$<action>$.command = NULL;
$<action>$.str = mm_strdup("stop");
}
| SQL_GOTO name
{
$<action>$.code = W_GOTO;
$<action>$.command = mm_strdup($2);
$<action>$.str = cat2_str(mm_strdup("goto "), $2);
}
| SQL_GO TO name
{
$<action>$.code = W_GOTO;
$<action>$.command = mm_strdup($3);
$<action>$.str = cat2_str(mm_strdup("goto "), $3);
}
| DO name '(' c_args ')'
{
$<action>$.code = W_DO;
$<action>$.command = cat_str(4, $2, mm_strdup("("), $4, mm_strdup(")"));
$<action>$.str = cat2_str(mm_strdup("do"), mm_strdup($<action>$.command));
}
| DO SQL_BREAK
{
$<action>$.code = W_BREAK;
$<action>$.command = NULL;
$<action>$.str = mm_strdup("break");
}
| DO CONTINUE_P
{
$<action>$.code = W_CONTINUE;
$<action>$.command = NULL;
$<action>$.str = mm_strdup("continue");
}
| CALL name '(' c_args ')'
{
$<action>$.code = W_DO;
$<action>$.command = cat_str(4, $2, mm_strdup("("), $4, mm_strdup(")"));
$<action>$.str = cat2_str(mm_strdup("call"), mm_strdup($<action>$.command));
}
| CALL name
{
$<action>$.code = W_DO;
$<action>$.command = cat2_str($2, mm_strdup("()"));
$<action>$.str = cat2_str(mm_strdup("call"), mm_strdup($<action>$.command));
}
;
/* some other stuff for ecpg */
/* additional unreserved keywords */
ECPGKeywords: ECPGKeywords_vanames { $$ = $1; }
| ECPGKeywords_rest { $$ = $1; }
;
ECPGKeywords_vanames: SQL_BREAK { $$ = mm_strdup("break"); }
| SQL_CARDINALITY { $$ = mm_strdup("cardinality"); }
| SQL_COUNT { $$ = mm_strdup("count"); }
| SQL_DATETIME_INTERVAL_CODE { $$ = mm_strdup("datetime_interval_code"); }
| SQL_DATETIME_INTERVAL_PRECISION { $$ = mm_strdup("datetime_interval_precision"); }
| SQL_FOUND { $$ = mm_strdup("found"); }
| SQL_GO { $$ = mm_strdup("go"); }
| SQL_GOTO { $$ = mm_strdup("goto"); }
| SQL_IDENTIFIED { $$ = mm_strdup("identified"); }
| SQL_INDICATOR { $$ = mm_strdup("indicator"); }
| SQL_KEY_MEMBER { $$ = mm_strdup("key_member"); }
| SQL_LENGTH { $$ = mm_strdup("length"); }
| SQL_NULLABLE { $$ = mm_strdup("nullable"); }
| SQL_OCTET_LENGTH { $$ = mm_strdup("octet_length"); }
| SQL_RETURNED_LENGTH { $$ = mm_strdup("returned_length"); }
| SQL_RETURNED_OCTET_LENGTH { $$ = mm_strdup("returned_octet_length"); }
| SQL_SCALE { $$ = mm_strdup("scale"); }
| SQL_SECTION { $$ = mm_strdup("section"); }
| SQL_SQLERROR { $$ = mm_strdup("sqlerror"); }
| SQL_SQLPRINT { $$ = mm_strdup("sqlprint"); }
| SQL_SQLWARNING { $$ = mm_strdup("sqlwarning"); }
| SQL_STOP { $$ = mm_strdup("stop"); }
;
ECPGKeywords_rest: SQL_CONNECT { $$ = mm_strdup("connect"); }
| SQL_DESCRIBE { $$ = mm_strdup("describe"); }
| SQL_DISCONNECT { $$ = mm_strdup("disconnect"); }
| SQL_OPEN { $$ = mm_strdup("open"); }
| SQL_VAR { $$ = mm_strdup("var"); }
| SQL_WHENEVER { $$ = mm_strdup("whenever"); }
;
/* additional keywords that can be SQL type names (but not ECPGColLabels) */
ECPGTypeName: SQL_BOOL { $$ = mm_strdup("bool"); }
| SQL_LONG { $$ = mm_strdup("long"); }
| SQL_OUTPUT { $$ = mm_strdup("output"); }
| SQL_SHORT { $$ = mm_strdup("short"); }
| SQL_STRUCT { $$ = mm_strdup("struct"); }
| SQL_SIGNED { $$ = mm_strdup("signed"); }
| SQL_UNSIGNED { $$ = mm_strdup("unsigned"); }
;
symbol: ColLabel { $$ = $1; }
;
ECPGColId: ecpg_ident { $$ = $1; }
| unreserved_keyword { $$ = $1; }
| col_name_keyword { $$ = $1; }
| ECPGunreserved_interval { $$ = $1; }
| ECPGKeywords { $$ = $1; }
| ECPGCKeywords { $$ = $1; }
| CHAR_P { $$ = mm_strdup("char"); }
| VALUES { $$ = mm_strdup("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 { $$ = $1; }
| all_unreserved_keyword { $$ = $1; }
| col_name_keyword { $$ = $1; }
| ECPGKeywords { $$ = $1; }
| ECPGCKeywords { $$ = $1; }
| CHAR_P { $$ = mm_strdup("char"); }
| VALUES { $$ = mm_strdup("values"); }
;
/* Type/function identifier --- names that can be type or function names.
*/
type_function_name: ecpg_ident { $$ = $1; }
| all_unreserved_keyword { $$ = $1; }
| type_func_name_keyword { $$ = $1; }
| ECPGKeywords { $$ = $1; }
| ECPGCKeywords { $$ = $1; }
| ECPGTypeName { $$ = $1; }
;
/* Column label --- allowed labels in "AS" clauses.
* This presently includes *all* Postgres keywords.
*/
ColLabel: ECPGColLabel { $$ = $1; }
| ECPGTypeName { $$ = $1; }
| CHAR_P { $$ = mm_strdup("char"); }
| CURRENT_P { $$ = mm_strdup("current"); }
| INPUT_P { $$ = mm_strdup("input"); }
| INT_P { $$ = mm_strdup("int"); }
| TO { $$ = mm_strdup("to"); }
| UNION { $$ = mm_strdup("union"); }
| VALUES { $$ = mm_strdup("values"); }
| ECPGCKeywords { $$ = $1; }
| ECPGunreserved_interval { $$ = $1; }
;
ECPGColLabel: ECPGColLabelCommon { $$ = $1; }
| unreserved_keyword { $$ = $1; }
| reserved_keyword { $$ = $1; }
| ECPGKeywords_rest { $$ = $1; }
| CONNECTION { $$ = mm_strdup("connection"); }
;
ECPGColLabelCommon: ecpg_ident { $$ = $1; }
| col_name_keyword { $$ = $1; }
| type_func_name_keyword { $$ = $1; }
| ECPGKeywords_vanames { $$ = $1; }
;
ECPGCKeywords: S_AUTO { $$ = mm_strdup("auto"); }
| S_CONST { $$ = mm_strdup("const"); }
| S_EXTERN { $$ = mm_strdup("extern"); }
| S_REGISTER { $$ = mm_strdup("register"); }
| S_STATIC { $$ = mm_strdup("static"); }
| S_TYPEDEF { $$ = mm_strdup("typedef"); }
| S_VOLATILE { $$ = mm_strdup("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 { $$ = $1; }
| ECPGunreserved_interval { $$ = $1; }
| CONNECTION { $$ = mm_strdup("connection"); }
;
ECPGunreserved_interval: DAY_P { $$ = mm_strdup("day"); }
| HOUR_P { $$ = mm_strdup("hour"); }
| MINUTE_P { $$ = mm_strdup("minute"); }
| MONTH_P { $$ = mm_strdup("month"); }
| SECOND_P { $$ = mm_strdup("second"); }
| YEAR_P { $$ = mm_strdup("year"); }
;
into_list : coutputvariable | into_list ',' coutputvariable
;
ecpgstart: SQL_START {
reset_variables();
pacounter = 1;
}
;
c_args: /*EMPTY*/ { $$ = EMPTY; }
| c_list { $$ = $1; }
;
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);
$$ = $1;
}
;
civar: cvariable
{
add_variable_to_head(&argsinsert, find_variable($1), &no_indicator);
$$ = create_questionmarks($1, false);
}
;
indicator: cvariable { check_indicator((find_variable($1))->type); $$ = $1; }
| 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 */
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;
}
}
$$ = $1;
}
;
ecpg_param: PARAM { $$ = make_name(); } ;
ecpg_bconst: BCONST { $$ = make_name(); } ;
ecpg_fconst: FCONST { $$ = make_name(); } ;
ecpg_sconst: SCONST { $$ = $1; } ;
ecpg_xconst: XCONST { $$ = make_name(); } ;
ecpg_ident: IDENT { $$ = $1; }
| CSTRING { $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\"")); }
;
quoted_ident_stringvar: name
{ $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\"")); }
| char_variable
{ $$ = make3_str(mm_strdup("("), $1, mm_strdup(")")); }
;
/*
* C stuff
*/
c_stuff_item: c_anything { $$ = $1; }
| '(' ')' { $$ = mm_strdup("()"); }
| '(' c_stuff ')'
{ $$ = cat_str(3, mm_strdup("("), $2, mm_strdup(")")); }
;
c_stuff: c_stuff_item { $$ = $1; }
| c_stuff c_stuff_item
{ $$ = cat2_str($1, $2); }
;
c_list: c_term { $$ = $1; }
| c_list ',' c_term { $$ = cat_str(3, $1, mm_strdup(","), $3); }
;
c_term: c_stuff { $$ = $1; }
| '{' c_list '}' { $$ = cat_str(3, mm_strdup("{"), $2, mm_strdup("}")); }
;
c_thing: c_anything { $$ = $1; }
| '(' { $$ = mm_strdup("("); }
| ')' { $$ = mm_strdup(")"); }
| ',' { $$ = mm_strdup(","); }
| ';' { $$ = mm_strdup(";"); }
;
c_anything: ecpg_ident { $$ = $1; }
| Iconst { $$ = $1; }
| ecpg_fconst { $$ = $1; }
| ecpg_sconst { $$ = $1; }
| '*' { $$ = mm_strdup("*"); }
| '+' { $$ = mm_strdup("+"); }
| '-' { $$ = mm_strdup("-"); }
| '/' { $$ = mm_strdup("/"); }
| '%' { $$ = mm_strdup("%"); }
| NULL_P { $$ = mm_strdup("NULL"); }
| S_ADD { $$ = mm_strdup("+="); }
| S_AND { $$ = mm_strdup("&&"); }
| S_ANYTHING { $$ = make_name(); }
| S_AUTO { $$ = mm_strdup("auto"); }
| S_CONST { $$ = mm_strdup("const"); }
| S_DEC { $$ = mm_strdup("--"); }
| S_DIV { $$ = mm_strdup("/="); }
| S_DOTPOINT { $$ = mm_strdup(".*"); }
| S_EQUAL { $$ = mm_strdup("=="); }
| S_EXTERN { $$ = mm_strdup("extern"); }
| S_INC { $$ = mm_strdup("++"); }
| S_LSHIFT { $$ = mm_strdup("<<"); }
| S_MEMBER { $$ = mm_strdup("->"); }
| S_MEMPOINT { $$ = mm_strdup("->*"); }
| S_MOD { $$ = mm_strdup("%="); }
| S_MUL { $$ = mm_strdup("*="); }
| S_NEQUAL { $$ = mm_strdup("!="); }
| S_OR { $$ = mm_strdup("||"); }
| S_REGISTER { $$ = mm_strdup("register"); }
| S_RSHIFT { $$ = mm_strdup(">>"); }
| S_STATIC { $$ = mm_strdup("static"); }
| S_SUB { $$ = mm_strdup("-="); }
| S_TYPEDEF { $$ = mm_strdup("typedef"); }
| S_VOLATILE { $$ = mm_strdup("volatile"); }
| SQL_BOOL { $$ = mm_strdup("bool"); }
| ENUM_P { $$ = mm_strdup("enum"); }
| HOUR_P { $$ = mm_strdup("hour"); }
| INT_P { $$ = mm_strdup("int"); }
| SQL_LONG { $$ = mm_strdup("long"); }
| MINUTE_P { $$ = mm_strdup("minute"); }
| MONTH_P { $$ = mm_strdup("month"); }
| SECOND_P { $$ = mm_strdup("second"); }
| SQL_SHORT { $$ = mm_strdup("short"); }
| SQL_SIGNED { $$ = mm_strdup("signed"); }
| SQL_STRUCT { $$ = mm_strdup("struct"); }
| SQL_UNSIGNED { $$ = mm_strdup("unsigned"); }
| YEAR_P { $$ = mm_strdup("year"); }
| CHAR_P { $$ = mm_strdup("char"); }
| FLOAT_P { $$ = mm_strdup("float"); }
| TO { $$ = mm_strdup("to"); }
| UNION { $$ = mm_strdup("union"); }
| VARCHAR { $$ = mm_strdup("varchar"); }
| '[' { $$ = mm_strdup("["); }
| ']' { $$ = mm_strdup("]"); }
| '=' { $$ = mm_strdup("="); }
| ':' { $$ = mm_strdup(":"); }
;
DeallocateStmt: DEALLOCATE prepared_name { $$ = $2; }
| DEALLOCATE PREPARE prepared_name { $$ = $3; }
| DEALLOCATE ALL { $$ = mm_strdup("all"); }
| DEALLOCATE PREPARE ALL { $$ = mm_strdup("all"); }
;
Iresult: Iconst { $$ = $1; }
| '(' Iresult ')' { $$ = cat_str(3, mm_strdup("("), $2, mm_strdup(")")); }
| Iresult '+' Iresult { $$ = cat_str(3, $1, mm_strdup("+"), $3); }
| Iresult '-' Iresult { $$ = cat_str(3, $1, mm_strdup("-"), $3); }
| Iresult '*' Iresult { $$ = cat_str(3, $1, mm_strdup("*"), $3); }
| Iresult '/' Iresult { $$ = cat_str(3, $1, mm_strdup("/"), $3); }
| Iresult '%' Iresult { $$ = cat_str(3, $1, mm_strdup("%"), $3); }
| ecpg_sconst { $$ = $1; }
| ColId { $$ = $1; }
| 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, mm_strdup("("), $3.type_str, mm_strdup(")"));
}
;
execute_rest: /* EMPTY */ { $$ = EMPTY; }
| ecpg_using opt_ecpg_into { $$ = EMPTY; }
| ecpg_into ecpg_using { $$ = EMPTY; }
| ecpg_into { $$ = EMPTY; }
;
ecpg_into: INTO into_list { $$ = EMPTY; }
| into_descriptor { $$ = $1; }
;
opt_ecpg_into: /* EMPTY */ { $$ = EMPTY; }
| ecpg_into { $$ = $1; }
;
ecpg_fetch_into: ecpg_into { $$ = $1; }
| using_descriptor
{
struct variable *var;
var = argsinsert->variable;
remove_variable_from_list(&argsinsert, var);
add_variable_to_head(&argsresult, var, &no_indicator);
$$ = $1;
}
;
opt_ecpg_fetch_into: /* EMPTY */ { $$ = EMPTY; }
| ecpg_fetch_into { $$ = $1; }
;
%%
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. */
}