mirror of
https://github.com/postgres/postgres.git
synced 2025-05-12 16:21:30 +03:00
This reverts commit bd7c95f0c1a38becffceb3ea7234d57167f6d4bf, along with assorted follow-on fixes. There are some questions about the definition and implementation of that statement, and we don't have time to resolve them before v13 release. Rather than ship the feature and then have backwards-compatibility concerns constraining any redesign, let's remove it for now and try again later. Discussion: https://postgr.es/m/TY2PR01MB2443EC8286995378AEB7D9F8F5B10@TY2PR01MB2443.jpnprd01.prod.outlook.com
1951 lines
56 KiB
Plaintext
1951 lines
56 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
|
|
{
|
|
#ifdef HAVE_LONG_LONG_INT
|
|
$$ = ECPGt_unsigned_long_long;
|
|
#else
|
|
$$ = ECPGt_unsigned_long;
|
|
#endif
|
|
}
|
|
| SQL_UNSIGNED SQL_LONG SQL_LONG INT_P
|
|
{
|
|
#ifdef HAVE_LONG_LONG_INT
|
|
$$ = ECPGt_unsigned_long_long;
|
|
#else
|
|
$$ = ECPGt_unsigned_long;
|
|
#endif
|
|
}
|
|
| 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
|
|
{
|
|
#ifdef HAVE_LONG_LONG_INT
|
|
$$ = ECPGt_long_long;
|
|
#else
|
|
$$ = ECPGt_long;
|
|
#endif
|
|
}
|
|
| SQL_LONG SQL_LONG INT_P
|
|
{
|
|
#ifdef HAVE_LONG_LONG_INT
|
|
$$ = ECPGt_long_long;
|
|
#else
|
|
$$ = ECPGt_long;
|
|
#endif
|
|
}
|
|
| 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, "%d", (int) 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, "%d", (int) 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, "%d", (int) 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, "%d", (int) 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, "%d", (int) 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, "%d", (int) 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
|
|
{
|
|
/* could have been input as '' or $$ */
|
|
$$ = (char *)mm_alloc(strlen($1) + 3);
|
|
$$[0]='\'';
|
|
strcpy($$+1, $1);
|
|
$$[strlen($1)+1]='\'';
|
|
$$[strlen($1)+2]='\0';
|
|
free($1);
|
|
}
|
|
| ECONST
|
|
{
|
|
$$ = (char *)mm_alloc(strlen($1) + 4);
|
|
$$[0]='E';
|
|
$$[1]='\'';
|
|
strcpy($$+2, $1);
|
|
$$[strlen($1)+2]='\'';
|
|
$$[strlen($1)+3]='\0';
|
|
free($1);
|
|
}
|
|
| NCONST
|
|
{
|
|
$$ = (char *)mm_alloc(strlen($1) + 4);
|
|
$$[0]='N';
|
|
$$[1]='\'';
|
|
strcpy($$+2, $1);
|
|
$$[strlen($1)+2]='\'';
|
|
$$[strlen($1)+3]='\0';
|
|
free($1);
|
|
}
|
|
| UCONST { $$ = $1; }
|
|
| DOLCONST { $$ = $1; }
|
|
;
|
|
|
|
ecpg_xconst: XCONST { $$ = make_name(); } ;
|
|
|
|
ecpg_ident: IDENT { $$ = make_name(); }
|
|
| CSTRING { $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\"")); }
|
|
| UIDENT { $$ = $1; }
|
|
;
|
|
|
|
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. */
|
|
}
|