You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
postgres/src/interfaces/ecpg/preproc/ecpg.header

603 lines
18 KiB

/* src/interfaces/ecpg/preproc/ecpg.header */
/* Copyright comment */
%{
#include "postgres_fe.h"
#include "preproc_extern.h"
#include "preproc.h"
#include "ecpg_config.h"
#include <unistd.h>
/* silence -Wmissing-variable-declarations */
extern int base_yychar;
extern int base_yynerrs;
/*
* The %name-prefix option below will make bison call base_yylex, but we
* really want it to call filtered_base_yylex (see parser.c).
*/
#define base_yylex filtered_base_yylex
/*
* This is only here so the string gets into the POT. Bison uses it
* internally.
*/
#define bison_gettext_dummy gettext_noop("syntax error")
/*
* Variables containing simple states.
*/
int struct_level = 0;
int braces_open; /* brace level counter */
char *current_function;
int ecpg_internal_var = 0;
char *connection = NULL;
char *input_filename = NULL;
static int FoundInto = 0;
static int initializer = 0;
static int pacounter = 1;
static struct this_type actual_type[STRUCT_DEPTH];
static char *actual_startline[STRUCT_DEPTH];
static int varchar_counter = 1;
static int bytea_counter = 1;
/*
* We temporarily store struct members here while parsing struct declarations.
* The struct_member_list (at a given nesting depth) is constructed while
* scanning the fields within "struct { .... }", but we can't remove it upon
* seeing the right brace. It's kept around and copied into the variables
* or typedefs that follow, in order to handle cases like
* "struct foo { ... } foovar1, foovar2;". We recycle the storage only
* upon closing the current nesting level or starting the next struct
* declaration within the same nesting level.
* For cases like "struct foo foovar1, foovar2;", we copy the saved struct
* field list for the typedef or struct tag into the struct_member_list
* global variable, and then copy it again to the newly-declared variables.
*/
struct ECPGstruct_member *struct_member_list[STRUCT_DEPTH] = {NULL};
/* also store struct type so we can do a sizeof() later */
static char *ECPGstruct_sizeof = NULL;
/* for forward declarations we have to store some data as well */
static char *forward_name = NULL;
struct ECPGtype ecpg_no_indicator = {ECPGt_NO_INDICATOR, NULL, NULL, NULL, {NULL}, 0};
struct variable no_indicator = {"no_indicator", &ecpg_no_indicator, 0, NULL};
static struct ECPGtype ecpg_query = {ECPGt_char_variable, NULL, NULL, NULL, {NULL}, 0};
static bool check_declared_list(const char *name);
static void update_connection(const char *newconn);
/*
* "Location tracking" support. We commandeer Bison's location tracking
* mechanism to manage the output string for productions that ordinarily would
* return a <str> result. This allows the majority of those productions to
* have default semantic actions, reducing the size of the parser, and also
* greatly reducing its compilation time on some versions of clang.
*
* To do this, we make YYLTYPE be a pointer to a malloc'd string, and then
* merge the location strings of the input tokens in the default YYLLOC
* computation. Productions that are okay with the standard merge need not
* do anything more; otherwise, they can override it by assigning to @$.
*/
#define YYLLOC_DEFAULT(Current, Rhs, N) yylloc_default(&(Current), Rhs, N)
static void
yylloc_default(YYLTYPE *target, YYLTYPE *rhs, int N)
{
if (N > 1)
{
/* Concatenate non-empty inputs with one space between them */
char *result,
*ptr;
size_t needed = 0;
for (int i = 1; i <= N; i++)
{
size_t thislen = strlen(rhs[i]);
if (needed > 0 && thislen > 0)
needed++;
needed += thislen;
}
result = (char *) loc_alloc(needed + 1);
ptr = result;
for (int i = 1; i <= N; i++)
{
size_t thislen = strlen(rhs[i]);
if (ptr > result && thislen > 0)
*ptr++ = ' ';
memcpy(ptr, rhs[i], thislen);
ptr += thislen;
}
*ptr = '\0';
*target = result;
}
else if (N == 1)
{
/* Just re-use the single input */
*target = rhs[1];
}
else
{
/* No need to allocate any space */
*target = "";
}
}
/* and the rest */
static char *
create_questionmarks(const char *name, bool array)
{
struct variable *p = find_variable(name);
int count;
char *result = "";
/*
* In case we have a struct, we have to print as many "?" as there are
* attributes in the struct
*
* An array is only allowed together with an element argument
*
* This is essentially only used for inserts, but using a struct as input
* parameter is an error anywhere else so we don't have to worry here.
*/
if (p->type->type == ECPGt_struct || (array && p->type->type == ECPGt_array && p->type->u.element->type == ECPGt_struct))
{
struct ECPGstruct_member *m;
if (p->type->type == ECPGt_struct)
m = p->type->u.members;
else
m = p->type->u.element->u.members;
for (count = 0; m != NULL; m = m->next, count++);
}
else
count = 1;
for (; count > 0; count--)
{
char buf[32];
snprintf(buf, sizeof(buf), "$%d", pacounter++);
result = cat_str(3, result, buf, " , ");
}
/* remove the trailing " ," */
result[strlen(result) - 3] = '\0';
return result;
}
static char *
adjust_outofscope_cursor_vars(struct cursor *cur)
{
/*
* Informix accepts DECLARE with variables that are out of scope when OPEN
* is called. For instance you can DECLARE a cursor in one function, and
* OPEN/FETCH/CLOSE it in another functions. This is very useful for e.g.
* event-driver programming, but may also lead to dangerous programming.
* The limitation when this is allowed and doesn't cause problems have to
* be documented, like the allocated variables must not be realloc()'ed.
*
* We have to change the variables to our own struct and just store the
* pointer instead of the variable. Do it only for local variables, not
* for globals.
*/
char *result = "";
int insert;
for (insert = 1; insert >= 0; insert--)
{
struct arguments *list;
struct arguments *ptr;
struct arguments *newlist = NULL;
struct variable *newvar,
*newind;
list = (insert ? cur->argsinsert : cur->argsresult);
for (ptr = list; ptr != NULL; ptr = ptr->next)
{
char var_text[20];
char *original_var;
bool skip_set_var = false;
bool var_ptr = false;
/* change variable name to "ECPGget_var(<counter>)" */
original_var = ptr->variable->name;
snprintf(var_text, sizeof(var_text), "%d))", ecpg_internal_var);
/* Don't emit ECPGset_var() calls for global variables */
if (ptr->variable->brace_level == 0)
{
newvar = ptr->variable;
skip_set_var = true;
}
else if ((ptr->variable->type->type == ECPGt_char_variable)
&& (strncmp(ptr->variable->name, "ECPGprepared_statement", strlen("ECPGprepared_statement")) == 0))
{
newvar = ptr->variable;
skip_set_var = true;
}
else if ((ptr->variable->type->type != ECPGt_varchar
&& ptr->variable->type->type != ECPGt_char
&& ptr->variable->type->type != ECPGt_unsigned_char
&& ptr->variable->type->type != ECPGt_string
&& ptr->variable->type->type != ECPGt_bytea)
&& atoi(ptr->variable->type->size) > 1)
{
newvar = new_variable(cat_str(4, "(",
ecpg_type_name(ptr->variable->type->u.element->type),
" *)(ECPGget_var(",
var_text),
ECPGmake_array_type(ECPGmake_simple_type(ptr->variable->type->u.element->type,
"1",
ptr->variable->type->u.element->counter),
ptr->variable->type->size),
0);
}
else if ((ptr->variable->type->type == ECPGt_varchar
|| ptr->variable->type->type == ECPGt_char
|| ptr->variable->type->type == ECPGt_unsigned_char
|| ptr->variable->type->type == ECPGt_string
|| ptr->variable->type->type == ECPGt_bytea)
&& atoi(ptr->variable->type->size) > 1)
{
newvar = new_variable(cat_str(4, "(",
ecpg_type_name(ptr->variable->type->type),
" *)(ECPGget_var(",
var_text),
ECPGmake_simple_type(ptr->variable->type->type,
ptr->variable->type->size,
ptr->variable->type->counter),
0);
if (ptr->variable->type->type == ECPGt_varchar ||
ptr->variable->type->type == ECPGt_bytea)
var_ptr = true;
}
else if (ptr->variable->type->type == ECPGt_struct
|| ptr->variable->type->type == ECPGt_union)
{
newvar = new_variable(cat_str(5, "(*(",
ptr->variable->type->type_name,
" *)(ECPGget_var(",
var_text,
")"),
ECPGmake_struct_type(ptr->variable->type->u.members,
ptr->variable->type->type,
ptr->variable->type->type_name,
ptr->variable->type->struct_sizeof),
0);
var_ptr = true;
}
else if (ptr->variable->type->type == ECPGt_array)
{
if (ptr->variable->type->u.element->type == ECPGt_struct
|| ptr->variable->type->u.element->type == ECPGt_union)
{
newvar = new_variable(cat_str(5, "(*(",
ptr->variable->type->u.element->type_name,
" *)(ECPGget_var(",
var_text,
")"),
ECPGmake_struct_type(ptr->variable->type->u.element->u.members,
ptr->variable->type->u.element->type,
ptr->variable->type->u.element->type_name,
ptr->variable->type->u.element->struct_sizeof),
0);
}
else
{
newvar = new_variable(cat_str(4, "(",
ecpg_type_name(ptr->variable->type->u.element->type),
" *)(ECPGget_var(",
var_text),
ECPGmake_array_type(ECPGmake_simple_type(ptr->variable->type->u.element->type,
ptr->variable->type->u.element->size,
ptr->variable->type->u.element->counter),
ptr->variable->type->size),
0);
var_ptr = true;
}
}
else
{
newvar = new_variable(cat_str(4, "*(",
ecpg_type_name(ptr->variable->type->type),
" *)(ECPGget_var(",
var_text),
ECPGmake_simple_type(ptr->variable->type->type,
ptr->variable->type->size,
ptr->variable->type->counter),
0);
var_ptr = true;
}
/*
* create call to "ECPGset_var(<counter>, <connection>, <pointer>.
* <line number>)"
*/
if (!skip_set_var)
{
snprintf(var_text, sizeof(var_text), "%d, %s",
ecpg_internal_var++, var_ptr ? "&(" : "(");
result = cat_str(5, result, "ECPGset_var(",
var_text, original_var,
"), __LINE__);\n");
}
/*
* now the indicator if there is one and it's not a global
* variable
*/
if ((ptr->indicator->type->type == ECPGt_NO_INDICATOR) || (ptr->indicator->brace_level == 0))
{
newind = ptr->indicator;
}
else
{
/* change variable name to "ECPGget_var(<counter>)" */
original_var = ptr->indicator->name;
snprintf(var_text, sizeof(var_text), "%d))", ecpg_internal_var);
var_ptr = false;
if (ptr->indicator->type->type == ECPGt_struct
|| ptr->indicator->type->type == ECPGt_union)
{
newind = new_variable(cat_str(5, "(*(",
ptr->indicator->type->type_name,
" *)(ECPGget_var(",
var_text,
")"),
ECPGmake_struct_type(ptr->indicator->type->u.members,
ptr->indicator->type->type,
ptr->indicator->type->type_name,
ptr->indicator->type->struct_sizeof),
0);
var_ptr = true;
}
else if (ptr->indicator->type->type == ECPGt_array)
{
if (ptr->indicator->type->u.element->type == ECPGt_struct
|| ptr->indicator->type->u.element->type == ECPGt_union)
{
newind = new_variable(cat_str(5, "(*(",
ptr->indicator->type->u.element->type_name,
" *)(ECPGget_var(",
var_text,
")"),
ECPGmake_struct_type(ptr->indicator->type->u.element->u.members,
ptr->indicator->type->u.element->type,
ptr->indicator->type->u.element->type_name,
ptr->indicator->type->u.element->struct_sizeof),
0);
}
else
{
newind = new_variable(cat_str(4, "(",
ecpg_type_name(ptr->indicator->type->u.element->type),
" *)(ECPGget_var(",
var_text),
ECPGmake_array_type(ECPGmake_simple_type(ptr->indicator->type->u.element->type,
ptr->indicator->type->u.element->size,
ptr->indicator->type->u.element->counter),
ptr->indicator->type->size),
0);
var_ptr = true;
}
}
else if (atoi(ptr->indicator->type->size) > 1)
{
newind = new_variable(cat_str(4, "(",
ecpg_type_name(ptr->indicator->type->type),
" *)(ECPGget_var(",
var_text),
ECPGmake_simple_type(ptr->indicator->type->type,
ptr->indicator->type->size,
ptr->variable->type->counter),
0);
}
else
{
newind = new_variable(cat_str(4, "*(",
ecpg_type_name(ptr->indicator->type->type),
" *)(ECPGget_var(",
var_text),
ECPGmake_simple_type(ptr->indicator->type->type,
ptr->indicator->type->size,
ptr->variable->type->counter),
0);
var_ptr = true;
}
/*
* create call to "ECPGset_var(<counter>, <pointer>. <line
* number>)"
*/
snprintf(var_text, sizeof(var_text), "%d, %s",
ecpg_internal_var++, var_ptr ? "&(" : "(");
result = cat_str(5, result, "ECPGset_var(",
var_text, original_var,
"), __LINE__);\n");
}
add_variable_to_tail(&newlist, newvar, newind);
}
if (insert)
cur->argsinsert_oos = newlist;
else
cur->argsresult_oos = newlist;
}
return result;
}
/* This tests whether the cursor was declared and opened in the same function. */
#define SAMEFUNC(cur) \
((cur->function == NULL) || \
(cur->function != NULL && current_function != NULL && \
strcmp(cur->function, current_function) == 0))
static struct cursor *
add_additional_variables(const char *name, bool insert)
{
struct cursor *ptr;
struct arguments *p;
int (*strcmp_fn) (const char *, const char *) = ((name[0] == ':' || name[0] == '"') ? strcmp : pg_strcasecmp);
for (ptr = cur; ptr != NULL; ptr = ptr->next)
{
if (strcmp_fn(ptr->name, name) == 0)
break;
}
if (ptr == NULL)
{
mmerror(PARSE_ERROR, ET_ERROR, "cursor \"%s\" does not exist", name);
return NULL;
}
if (insert)
{
/*
* add all those input variables that were given earlier
*
* note that we have to append here but have to keep the existing
* order
*/
for (p = (SAMEFUNC(ptr) ? ptr->argsinsert : ptr->argsinsert_oos); p; p = p->next)
add_variable_to_tail(&argsinsert, p->variable, p->indicator);
}
/* add all those output variables that were given earlier */
for (p = (SAMEFUNC(ptr) ? ptr->argsresult : ptr->argsresult_oos); p; p = p->next)
add_variable_to_tail(&argsresult, p->variable, p->indicator);
return ptr;
}
static void
add_typedef(const char *name, const char *dimension, const char *length,
enum ECPGttype type_enum,
const char *type_dimension, const char *type_index,
int initializer, int array)
{
/* add entry to list */
struct typedefs *ptr,
*this;
if ((type_enum == ECPGt_struct ||
type_enum == ECPGt_union) &&
initializer == 1)
mmerror(PARSE_ERROR, ET_ERROR, "initializer not allowed in type definition");
else if (INFORMIX_MODE && strcmp(name, "string") == 0)
mmerror(PARSE_ERROR, ET_ERROR, "type name \"string\" is reserved in Informix mode");
else
{
for (ptr = types; ptr != NULL; ptr = ptr->next)
{
if (strcmp(name, ptr->name) == 0)
/* re-definition is a bug */
mmerror(PARSE_ERROR, ET_ERROR, "type \"%s\" is already defined", name);
}
adjust_array(type_enum, &dimension, &length,
type_dimension, type_index, array, true);
this = (struct typedefs *) mm_alloc(sizeof(struct typedefs));
/* initial definition */
this->next = types;
this->name = mm_strdup(name);
this->brace_level = braces_open;
this->type = (struct this_type *) mm_alloc(sizeof(struct this_type));
this->type->type_storage = NULL;
this->type->type_enum = type_enum;
this->type->type_str = mm_strdup(name);
this->type->type_dimension = mm_strdup(dimension); /* dimension of array */
this->type->type_index = mm_strdup(length); /* length of string */
this->type->type_sizeof = ECPGstruct_sizeof ? mm_strdup(ECPGstruct_sizeof) : NULL;
this->struct_member_list = (type_enum == ECPGt_struct || type_enum == ECPGt_union) ?
ECPGstruct_member_dup(struct_member_list[struct_level]) : NULL;
if (type_enum != ECPGt_varchar &&
type_enum != ECPGt_bytea &&
type_enum != ECPGt_char &&
type_enum != ECPGt_unsigned_char &&
type_enum != ECPGt_string &&
atoi(this->type->type_index) >= 0)
mmerror(PARSE_ERROR, ET_ERROR, "multidimensional arrays for simple data types are not supported");
types = this;
}
}
/*
* check an SQL identifier is declared or not.
* If it is already declared, the global variable
* connection will be changed to the related connection.
*/
static bool
check_declared_list(const char *name)
{
struct declared_list *ptr = NULL;
for (ptr = g_declared_list; ptr != NULL; ptr = ptr->next)
{
if (!ptr->connection)
continue;
if (strcmp(name, ptr->name) == 0)
{
if (connection && strcmp(ptr->connection, connection) != 0)
mmerror(PARSE_ERROR, ET_WARNING, "connection %s is overwritten with %s by DECLARE statement %s", connection, ptr->connection, name);
update_connection(ptr->connection);
return true;
}
}
return false;
}
/*
* If newconn isn't NULL, update the global "connection" variable to that;
* otherwise do nothing.
*/
static void
update_connection(const char *newconn)
{
if (newconn)
{
free(connection);
connection = mm_strdup(newconn);
}
}
%}
%expect 0
%name-prefix="base_yy"
%locations
%union {
double dval;
char *str;
int ival;
struct when action;
struct index index;
int tagname;
struct this_type type;
enum ECPGttype type_enum;
enum ECPGdtype dtype_enum;
struct fetch_desc descriptor;
struct su_symbol struct_union;
struct prep prep;
struct exec exec;
struct describe describe;
}