mirror of
https://github.com/postgres/postgres.git
synced 2025-06-04 12:42:24 +03:00
Back-patch commit 92fb64983 into the pre-9.6 branches. Without this, ecpg fails to build with the latest version of flex. It's not unreasonable that people would want to compile our old branches with recent tools. Per report from Дилян Палаузов. Discussion: https://postgr.es/m/d845c1af-e18d-6651-178f-9f08cdf37e10@aegee.org
577 lines
17 KiB
Plaintext
577 lines
17 KiB
Plaintext
/* src/interfaces/ecpg/preproc/ecpg.header */
|
|
|
|
/* Copyright comment */
|
|
%{
|
|
#include "postgres_fe.h"
|
|
|
|
#include "extern.h"
|
|
#include "ecpg_config.h"
|
|
#include <unistd.h>
|
|
|
|
/* Location tracking support --- simpler than bison's default */
|
|
#define YYLLOC_DEFAULT(Current, Rhs, N) \
|
|
do { \
|
|
if (N) \
|
|
(Current) = (Rhs)[1]; \
|
|
else \
|
|
(Current) = (Rhs)[0]; \
|
|
} while (0)
|
|
|
|
/*
|
|
* 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 char pacounter_buffer[sizeof(int) * CHAR_BIT * 10 / 3]; /* a rough guess at the size we need */
|
|
static struct this_type actual_type[STRUCT_DEPTH];
|
|
static char *actual_startline[STRUCT_DEPTH];
|
|
static int varchar_counter = 1;
|
|
|
|
/* temporarily store struct members while creating the data structure */
|
|
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};
|
|
|
|
struct ECPGtype ecpg_query = {ECPGt_char_variable, NULL, NULL, NULL, {NULL}, 0};
|
|
|
|
/*
|
|
* Handle parsing errors and warnings
|
|
*/
|
|
void
|
|
mmerror(int error_code, enum errortype type, const char *error, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
/* internationalize the error message string */
|
|
error = _(error);
|
|
|
|
fprintf(stderr, "%s:%d: ", input_filename, base_yylineno);
|
|
|
|
switch(type)
|
|
{
|
|
case ET_WARNING:
|
|
fprintf(stderr, _("WARNING: "));
|
|
break;
|
|
case ET_ERROR:
|
|
case ET_FATAL:
|
|
fprintf(stderr, _("ERROR: "));
|
|
break;
|
|
}
|
|
|
|
va_start(ap, error);
|
|
vfprintf(stderr, error, ap);
|
|
va_end(ap);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
switch(type)
|
|
{
|
|
case ET_WARNING:
|
|
break;
|
|
case ET_ERROR:
|
|
ret_value = error_code;
|
|
break;
|
|
case ET_FATAL:
|
|
if (base_yyin)
|
|
fclose(base_yyin);
|
|
if (base_yyout)
|
|
fclose(base_yyout);
|
|
|
|
if (strcmp(output_filename, "-") != 0 && unlink(output_filename) != 0)
|
|
fprintf(stderr, _("could not remove output file \"%s\"\n"), output_filename);
|
|
exit(error_code);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* string concatenation
|
|
*/
|
|
|
|
static char *
|
|
cat2_str(char *str1, char *str2)
|
|
{
|
|
char * res_str = (char *)mm_alloc(strlen(str1) + strlen(str2) + 2);
|
|
|
|
strcpy(res_str, str1);
|
|
if (strlen(str1) != 0 && strlen(str2) != 0)
|
|
strcat(res_str, " ");
|
|
strcat(res_str, str2);
|
|
free(str1);
|
|
free(str2);
|
|
return(res_str);
|
|
}
|
|
|
|
static char *
|
|
cat_str(int count, ...)
|
|
{
|
|
va_list args;
|
|
int i;
|
|
char *res_str;
|
|
|
|
va_start(args, count);
|
|
|
|
res_str = va_arg(args, char *);
|
|
|
|
/* now add all other strings */
|
|
for (i = 1; i < count; i++)
|
|
res_str = cat2_str(res_str, va_arg(args, char *));
|
|
|
|
va_end(args);
|
|
|
|
return(res_str);
|
|
}
|
|
|
|
static char *
|
|
make2_str(char *str1, char *str2)
|
|
{
|
|
char * res_str = (char *)mm_alloc(strlen(str1) + strlen(str2) + 1);
|
|
|
|
strcpy(res_str, str1);
|
|
strcat(res_str, str2);
|
|
free(str1);
|
|
free(str2);
|
|
return(res_str);
|
|
}
|
|
|
|
static char *
|
|
make3_str(char *str1, char *str2, char *str3)
|
|
{
|
|
char * res_str = (char *)mm_alloc(strlen(str1) + strlen(str2) +strlen(str3) + 1);
|
|
|
|
strcpy(res_str, str1);
|
|
strcat(res_str, str2);
|
|
strcat(res_str, str3);
|
|
free(str1);
|
|
free(str2);
|
|
free(str3);
|
|
return(res_str);
|
|
}
|
|
|
|
/* and the rest */
|
|
static char *
|
|
make_name(void)
|
|
{
|
|
return mm_strdup(base_yytext);
|
|
}
|
|
|
|
static char *
|
|
create_questionmarks(char *name, bool array)
|
|
{
|
|
struct variable *p = find_variable(name);
|
|
int count;
|
|
char *result = EMPTY;
|
|
|
|
/* 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 essantially 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 --)
|
|
{
|
|
sprintf(pacounter_buffer, "$%d", pacounter++);
|
|
result = cat_str(3, result, mm_strdup(pacounter_buffer), mm_strdup(" , "));
|
|
}
|
|
|
|
/* removed 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 = EMPTY;
|
|
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;
|
|
sprintf(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)
|
|
&& atoi(ptr->variable->type->size) > 1)
|
|
{
|
|
newvar = new_variable(cat_str(4, mm_strdup("("),
|
|
mm_strdup(ecpg_type_name(ptr->variable->type->u.element->type)),
|
|
mm_strdup(" *)(ECPGget_var("),
|
|
mm_strdup(var_text)),
|
|
ECPGmake_array_type(ECPGmake_simple_type(ptr->variable->type->u.element->type,
|
|
mm_strdup("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)
|
|
&& atoi(ptr->variable->type->size) > 1)
|
|
{
|
|
newvar = new_variable(cat_str(4, mm_strdup("("),
|
|
mm_strdup(ecpg_type_name(ptr->variable->type->type)),
|
|
mm_strdup(" *)(ECPGget_var("),
|
|
mm_strdup(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)
|
|
var_ptr = true;
|
|
}
|
|
else if (ptr->variable->type->type == ECPGt_struct
|
|
|| ptr->variable->type->type == ECPGt_union)
|
|
{
|
|
newvar = new_variable(cat_str(5, mm_strdup("(*("),
|
|
mm_strdup(ptr->variable->type->type_name),
|
|
mm_strdup(" *)(ECPGget_var("),
|
|
mm_strdup(var_text),
|
|
mm_strdup(")")),
|
|
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, mm_strdup("(*("),
|
|
mm_strdup(ptr->variable->type->u.element->type_name),
|
|
mm_strdup(" *)(ECPGget_var("),
|
|
mm_strdup(var_text),
|
|
mm_strdup(")")),
|
|
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, mm_strdup("("),
|
|
mm_strdup(ecpg_type_name(ptr->variable->type->type)),
|
|
mm_strdup(" *)(ECPGget_var("),
|
|
mm_strdup(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, mm_strdup("*("),
|
|
mm_strdup(ecpg_type_name(ptr->variable->type->type)),
|
|
mm_strdup(" *)(ECPGget_var("),
|
|
mm_strdup(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)
|
|
{
|
|
sprintf(var_text, "%d, %s", ecpg_internal_var++, var_ptr ? "&(" : "(");
|
|
result = cat_str(5, result, mm_strdup("ECPGset_var("),
|
|
mm_strdup(var_text), mm_strdup(original_var),
|
|
mm_strdup("), __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;
|
|
sprintf(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, mm_strdup("(*("),
|
|
mm_strdup(ptr->indicator->type->type_name),
|
|
mm_strdup(" *)(ECPGget_var("),
|
|
mm_strdup(var_text),
|
|
mm_strdup(")")),
|
|
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, mm_strdup("(*("),
|
|
mm_strdup(ptr->indicator->type->u.element->type_name),
|
|
mm_strdup(" *)(ECPGget_var("),
|
|
mm_strdup(var_text),
|
|
mm_strdup(")")),
|
|
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, mm_strdup("("),
|
|
mm_strdup(ecpg_type_name(ptr->indicator->type->u.element->type)),
|
|
mm_strdup(" *)(ECPGget_var("), mm_strdup(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, mm_strdup("("),
|
|
mm_strdup(ecpg_type_name(ptr->indicator->type->type)),
|
|
mm_strdup(" *)(ECPGget_var("),
|
|
mm_strdup(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, mm_strdup("*("),
|
|
mm_strdup(ecpg_type_name(ptr->indicator->type->type)),
|
|
mm_strdup(" *)(ECPGget_var("),
|
|
mm_strdup(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>)" */
|
|
sprintf(var_text, "%d, %s", ecpg_internal_var++, var_ptr ? "&(" : "(");
|
|
result = cat_str(5, result, mm_strdup("ECPGset_var("),
|
|
mm_strdup(var_text), mm_strdup(original_var),
|
|
mm_strdup("), __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 && strcmp(cur->function, current_function) == 0))
|
|
|
|
static struct cursor *
|
|
add_additional_variables(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(char *name, char *dimension, char *length, enum ECPGttype type_enum,
|
|
char *type_dimension, 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 = name;
|
|
this->brace_level = braces_open;
|
|
this->type = (struct this_type *) mm_alloc(sizeof(struct this_type));
|
|
this->type->type_enum = type_enum;
|
|
this->type->type_str = mm_strdup(name);
|
|
this->type->type_dimension = dimension; /* dimension of array */
|
|
this->type->type_index = length; /* length of string */
|
|
this->type->type_sizeof = ECPGstruct_sizeof;
|
|
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_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;
|
|
}
|
|
}
|
|
%}
|
|
|
|
%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;
|
|
}
|