/* 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;
}
