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

1889 lines
55 KiB
Plaintext
Raw Normal View History

2010-09-20 22:08:53 +02:00
/* 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)
2009-01-26 11:19:44 +01:00
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)
2009-01-23 13:43:32 +01:00
mmerror(PARSE_ERROR, ET_ERROR, "Unix-domain sockets only work on \"localhost\" but not on \"%s\"", $3 + strlen("//"));
2014-05-21 14:00:39 +02:00
$$ = 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)
2009-01-23 13:43:32 +01:00
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)
2009-01-23 13:43:32 +01:00
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:
2009-01-23 13:43:32 +01:00
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
{
2009-01-26 11:19:44 +01:00
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 */
2009-01-26 11:19:44 +01:00
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)
2009-01-26 11:19:44 +01:00
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";
2009-01-23 13:43:32 +01:00
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";
2009-01-23 13:43:32 +01:00
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(); } ;
Reduce size of backend scanner's tables. Previously, the core scanner's yy_transition[] array had 37045 elements. Since that number is larger than INT16_MAX, Flex generated the array to contain 32-bit integers. By reimplementing some of the bulkier scanner rules, this patch reduces the array to 20495 elements. The much smaller total length, combined with the consequent use of 16-bit integers for the array elements reduces the binary size by over 200kB. This was accomplished in two ways: 1. Consolidate handling of quote continuations into a new start condition, rather than duplicating that logic for five different string types. 2. Treat Unicode strings and identifiers followed by a UESCAPE sequence as three separate tokens, rather than one. The logic to de-escape Unicode strings is moved to the filter code in parser.c, which already had the ability to provide special processing for token sequences. While we could have implemented the conversion in the grammar, that approach was rejected for performance and maintainability reasons. Performance in microbenchmarks of raw parsing seems equal or slightly faster in most cases, and it's reasonable to expect that in real-world usage (with more competition for the CPU cache) there will be a larger win. The exception is UESCAPE sequences; lexing those is about 10% slower, primarily because the scanner now has to be called three times rather than one. This seems acceptable since that feature is very rarely used. The psql and epcg lexers are likewise modified, primarily because we want to keep them all in sync. Since those lexers don't use the space-hogging -CF option, the space savings is much less, but it's still good for perhaps 10kB apiece. While at it, merge the ecpg lexer's handling of C-style comments used in SQL and in C. Those have different rules regarding nested comments, but since we already have the ability to keep track of the previous start condition, we can use that to handle both cases within a single start condition. This matches the core scanner more closely. John Naylor Discussion: https://postgr.es/m/CACPNZCvaoa3EgVWm5yZhcSTX6RAtaLgniCPcBVOCwm8h3xpWkw@mail.gmail.com
2020-01-13 21:04:31 +01:00
ecpg_sconst: SCONST { $$ = $1; } ;
ecpg_xconst: XCONST { $$ = make_name(); } ;
Reduce size of backend scanner's tables. Previously, the core scanner's yy_transition[] array had 37045 elements. Since that number is larger than INT16_MAX, Flex generated the array to contain 32-bit integers. By reimplementing some of the bulkier scanner rules, this patch reduces the array to 20495 elements. The much smaller total length, combined with the consequent use of 16-bit integers for the array elements reduces the binary size by over 200kB. This was accomplished in two ways: 1. Consolidate handling of quote continuations into a new start condition, rather than duplicating that logic for five different string types. 2. Treat Unicode strings and identifiers followed by a UESCAPE sequence as three separate tokens, rather than one. The logic to de-escape Unicode strings is moved to the filter code in parser.c, which already had the ability to provide special processing for token sequences. While we could have implemented the conversion in the grammar, that approach was rejected for performance and maintainability reasons. Performance in microbenchmarks of raw parsing seems equal or slightly faster in most cases, and it's reasonable to expect that in real-world usage (with more competition for the CPU cache) there will be a larger win. The exception is UESCAPE sequences; lexing those is about 10% slower, primarily because the scanner now has to be called three times rather than one. This seems acceptable since that feature is very rarely used. The psql and epcg lexers are likewise modified, primarily because we want to keep them all in sync. Since those lexers don't use the space-hogging -CF option, the space savings is much less, but it's still good for perhaps 10kB apiece. While at it, merge the ecpg lexer's handling of C-style comments used in SQL and in C. Those have different rules regarding nested comments, but since we already have the ability to keep track of the previous start condition, we can use that to handle both cases within a single start condition. This matches the core scanner more closely. John Naylor Discussion: https://postgr.es/m/CACPNZCvaoa3EgVWm5yZhcSTX6RAtaLgniCPcBVOCwm8h3xpWkw@mail.gmail.com
2020-01-13 21:04:31 +01:00
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. */
}