mirror of
https://github.com/postgres/postgres.git
synced 2025-07-17 06:41:09 +03:00
Backpatch to all supported versions, where applicable, to make backpatching of future fixes go more smoothly. Josh Soref Discussion: https://www.postgresql.org/message-id/CACZqfqCf+5qRztLPgmmosr-B0Ye4srWzzw_mo4c_8_B_mtjmJQ@mail.gmail.com
594 lines
17 KiB
Plaintext
594 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};
|
|
|
|
static struct ECPGtype ecpg_query = {ECPGt_char_variable, NULL, NULL, NULL, {NULL}, 0};
|
|
|
|
static void vmmerror(int error_code, enum errortype type, const char *error, va_list ap) pg_attribute_printf(3, 0);
|
|
|
|
/*
|
|
* Handle parsing errors and warnings
|
|
*/
|
|
static void
|
|
vmmerror(int error_code, enum errortype type, const char *error, va_list ap)
|
|
{
|
|
/* localize 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:
|
|
fprintf(stderr, _("ERROR: "));
|
|
break;
|
|
}
|
|
|
|
vfprintf(stderr, error, ap);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
switch(type)
|
|
{
|
|
case ET_WARNING:
|
|
break;
|
|
case ET_ERROR:
|
|
ret_value = error_code;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
mmerror(int error_code, enum errortype type, const char *error, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, error);
|
|
vmmerror(error_code, type, error, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
void
|
|
mmfatal(int error_code, const char *error, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, error);
|
|
vmmerror(error_code, ET_ERROR, error, ap);
|
|
va_end(ap);
|
|
|
|
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 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 --)
|
|
{
|
|
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;
|
|
}
|