/* src/interfaces/ecpg/preproc/ecpg.addons */
ECPG: stmtClosePortalStmt block
	{
		if (INFORMIX_MODE)
		{
			if (pg_strcasecmp($1+strlen("close "), "database") == 0)
			{
				if (connection)
					mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in CLOSE DATABASE statement");

				fprintf(yyout, "{ ECPGdisconnect(__LINE__, \"CURRENT\");");
				whenever_action(2);
				free($1);
				break;
			}
		}

		output_statement($1, 0, ECPGst_normal);
	}
ECPG: stmtDeallocateStmt block
	{
		if (connection)
			mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in DEALLOCATE statement");

		output_deallocate_prepare_statement($1);
	}
ECPG: stmtDeclareCursorStmt block
	{ output_simple_statement($1); }
ECPG: stmtDiscardStmt block
ECPG: stmtFetchStmt block
	{ output_statement($1, 1, ECPGst_normal); }
ECPG: stmtDeleteStmt block
ECPG: stmtInsertStmt block
ECPG: stmtSelectStmt block
ECPG: stmtUpdateStmt block
	{ output_statement($1, 1, ECPGst_prepnormal); }
ECPG: stmtExecuteStmt block
	{ output_statement($1, 1, ECPGst_execute); }
ECPG: stmtPrepareStmt block
	{
		if ($1.type == NULL || strlen($1.type) == 0)
			output_prepare_statement($1.name, $1.stmt);
		else
			output_statement(cat_str(5, mm_strdup("prepare"), $1.name, $1.type, mm_strdup("as"), $1.stmt), 0, ECPGst_normal);
	}
ECPG: stmtTransactionStmt block
	{
		fprintf(yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", $1);
		whenever_action(2);
		free($1);
	}
ECPG: stmtViewStmt rule
	| ECPGAllocateDescr
	{
		fprintf(yyout,"ECPGallocate_desc(__LINE__, %s);",$1);
		whenever_action(0);
		free($1);
	}
	| ECPGConnect
	{
		if (connection)
			mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in CONNECT statement");

		fprintf(yyout, "{ ECPGconnect(__LINE__, %d, %s, %d); ", compat, $1, autocommit);
		reset_variables();
		whenever_action(2);
		free($1);
	}
	| ECPGCursorStmt
	{
		output_simple_statement($1);
	}
	| ECPGDeallocateDescr
	{
		if (connection)
			mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in DEALLOCATE statement");
		fprintf(yyout,"ECPGdeallocate_desc(__LINE__, %s);",$1);
		whenever_action(0);
		free($1);
	}
	| ECPGDeclare
	{
		output_simple_statement($1);
	}
	| ECPGDescribe
	{
		fprintf(yyout, "{ ECPGdescribe(__LINE__, %d, %s,", compat, $1);
		dump_variables(argsresult, 1);
		fputs("ECPGt_EORT);", yyout);
		fprintf(yyout, "}");
		output_line_number();

		free($1);
	}
	| ECPGDisconnect
	{
		if (connection)
			mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in DISCONNECT statement");

		fprintf(yyout, "{ ECPGdisconnect(__LINE__, %s);",
				$1 ? $1 : "\"CURRENT\"");
		whenever_action(2);
		free($1);
	}
	| ECPGExecuteImmediateStmt	{ output_statement($1, 0, ECPGst_exec_immediate); }
	| ECPGFree
	{
		const char *con = connection ? connection : "NULL";

		if (!strcmp($1, "all"))
			fprintf(yyout, "{ ECPGdeallocate_all(__LINE__, %d, %s);", compat, con);
		else if ($1[0] == ':')
			fprintf(yyout, "{ ECPGdeallocate(__LINE__, %d, %s, %s);", compat, con, $1+1);
		else
			fprintf(yyout, "{ ECPGdeallocate(__LINE__, %d, %s, \"%s\");", compat, con, $1);

		whenever_action(2);
		free($1);
	}
	| ECPGGetDescriptor
	{
		lookup_descriptor($1.name, connection);
		output_get_descr($1.name, $1.str);
		free($1.name);
		free($1.str);
	}
	| ECPGGetDescriptorHeader
	{
		lookup_descriptor($1, connection);
		output_get_descr_header($1);
		free($1);
	}
	| ECPGOpen
	{
		struct cursor *ptr;

		if ((ptr = add_additional_variables($1, true)) != NULL)
		{
			connection = ptr->connection ? mm_strdup(ptr->connection) : NULL;
			output_statement(mm_strdup(ptr->command), 0, ECPGst_normal);
			ptr->opened = true;
		}
	}
	| ECPGSetAutocommit
	{
		fprintf(yyout, "{ ECPGsetcommit(__LINE__, \"%s\", %s);", $1, connection ? connection : "NULL");
		whenever_action(2);
		free($1);
	}
	| ECPGSetConnection
	{
		if (connection)
			mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in SET CONNECTION statement");

		fprintf(yyout, "{ ECPGsetconn(__LINE__, %s);", $1);
		whenever_action(2);
		free($1);
	}
	| ECPGSetDescriptor
	{
		lookup_descriptor($1.name, connection);
		output_set_descr($1.name, $1.str);
		free($1.name);
		free($1.str);
	}
	| ECPGSetDescriptorHeader
	{
		lookup_descriptor($1, connection);
		output_set_descr_header($1);
		free($1);
	}
	| ECPGTypedef
	{
		if (connection)
			mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in TYPE statement");

		fprintf(yyout, "%s", $1);
		free($1);
		output_line_number();
	}
	| ECPGVar
	{
		if (connection)
			mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in VAR statement");

		output_simple_statement($1);
	}
	| ECPGWhenever
	{
		if (connection)
			mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in WHENEVER statement");

		output_simple_statement($1);
	}
ECPG: where_or_current_clauseWHERECURRENT_POFcursor_name block
	{
		char *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
		$$ = cat_str(2,mm_strdup("where current of"), cursor_marker);
	}
ECPG: CopyStmtCOPYopt_binaryqualified_nameopt_column_listopt_oidscopy_fromcopy_file_namecopy_delimiteropt_withcopy_options addon
			if (strcmp($6, "to") == 0 && strcmp($7, "stdin") == 0)
				mmerror(PARSE_ERROR, ET_ERROR, "COPY TO STDIN is not possible");
			else if (strcmp($6, "from") == 0 && strcmp($7, "stdout") == 0)
				mmerror(PARSE_ERROR, ET_ERROR, "COPY FROM STDOUT is not possible");
			else if (strcmp($6, "from") == 0 && strcmp($7, "stdin") == 0)
				mmerror(PARSE_ERROR, ET_WARNING, "COPY FROM STDIN is not implemented");
ECPG: CopyStmtCOPYselect_with_parensTOcopy_file_nameopt_withcopy_options addon
			if (strcmp($4, "stdin") == 0)
				mmerror(PARSE_ERROR, ET_ERROR, "COPY TO STDIN is not possible");
ECPG: var_valueNumericOnly addon
		if ($1[0] == '$')
		{
			free($1);
			$1 = mm_strdup("$0");
		}
ECPG: fetch_argscursor_name addon
		add_additional_variables($1, false);
		if ($1[0] == ':')
		{
			free($1);
			$1 = mm_strdup("$0");
		}
ECPG: fetch_argsfrom_incursor_name addon
		add_additional_variables($2, false);
		if ($2[0] == ':')
		{
			free($2);
			$2 = mm_strdup("$0");
		}
ECPG: fetch_argsNEXTopt_from_incursor_name addon
ECPG: fetch_argsPRIORopt_from_incursor_name addon
ECPG: fetch_argsFIRST_Popt_from_incursor_name addon
ECPG: fetch_argsLAST_Popt_from_incursor_name addon
ECPG: fetch_argsALLopt_from_incursor_name addon
		add_additional_variables($3, false);
		if ($3[0] == ':')
		{
			free($3);
			$3 = mm_strdup("$0");
		}
ECPG: fetch_argsSignedIconstopt_from_incursor_name addon
		add_additional_variables($3, false);
		if ($3[0] == ':')
		{
			free($3);
			$3 = mm_strdup("$0");
		}
		if ($1[0] == '$')
		{
			free($1);
			$1 = mm_strdup("$0");
		}
ECPG: fetch_argsFORWARDALLopt_from_incursor_name addon
ECPG: fetch_argsBACKWARDALLopt_from_incursor_name addon
		add_additional_variables($4, false);
		if ($4[0] == ':')
		{
			free($4);
			$4 = mm_strdup("$0");
		}
ECPG: fetch_argsABSOLUTE_PSignedIconstopt_from_incursor_name addon
ECPG: fetch_argsRELATIVE_PSignedIconstopt_from_incursor_name addon
ECPG: fetch_argsFORWARDSignedIconstopt_from_incursor_name addon
ECPG: fetch_argsBACKWARDSignedIconstopt_from_incursor_name addon
		add_additional_variables($4, false);
		if ($4[0] == ':')
		{
			free($4);
			$4 = mm_strdup("$0");
		}
		if ($2[0] == '$')
		{
			free($2);
			$2 = mm_strdup("$0");
		}
ECPG: cursor_namename rule
	| char_civar
		{
			char *curname = mm_alloc(strlen($1) + 2);
			sprintf(curname, ":%s", $1);
			free($1);
			$1 = curname;
			$$ = $1;
		}
ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block
	{
		$$.name = $2;
		$$.type = $3;
		$$.stmt = cat_str(3, mm_strdup("\""), $5, mm_strdup("\""));
	}
	| PREPARE prepared_name FROM execstring
	{
		$$.name = $2;
		$$.type = NULL;
		$$.stmt = $4;
	}
ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block
	{ $$ = $2; }
ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectStmt block
	{
		struct cursor *ptr, *this;
		char *cursor_marker = $2[0] == ':' ? mm_strdup("$0") : mm_strdup($2);
		char *comment, *c1, *c2;
		int (* strcmp_fn)(const char *, const char *) = ($2[0] == ':' ? strcmp : pg_strcasecmp);

		for (ptr = cur; ptr != NULL; ptr = ptr->next)
		{
			if (strcmp_fn($2, ptr->name) == 0)
			{
				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));

		this->next = cur;
		this->name = $2;
		this->function = (current_function ? mm_strdup(current_function) : NULL);
		this->connection = connection;
		this->opened = false;
		this->command =  cat_str(7, mm_strdup("declare"), cursor_marker, $3, mm_strdup("cursor"), $5, mm_strdup("for"), $7);
		this->argsinsert = argsinsert;
		this->argsinsert_oos = NULL;
		this->argsresult = argsresult;
		this->argsresult_oos = NULL;
		argsinsert = argsresult = NULL;
		cur = this;

		c1 = mm_strdup(this->command);
		if ((c2 = strstr(c1, "*/")) != NULL)
		{
			/* We put this text into a comment, so we better remove [*][/]. */
			c2[0] = '.';
			c2[1] = '.';
		}
		comment = cat_str(3, mm_strdup("/*"), c1, mm_strdup("*/"));

		if ((braces_open > 0) && INFORMIX_MODE) /* we're in a function */
			$$ = cat_str(3, adjust_outofscope_cursor_vars(this),
				mm_strdup("ECPG_informix_reset_sqlca();"),
				comment);
		else
			$$ = cat2_str(adjust_outofscope_cursor_vars(this), comment);
	}
ECPG: ClosePortalStmtCLOSEcursor_name block
	{
		char *cursor_marker = $2[0] == ':' ? mm_strdup("$0") : $2;
		$$ = cat2_str(mm_strdup("close"), cursor_marker);
	}
ECPG: opt_hold block
	{
		if (compat == ECPG_COMPAT_INFORMIX_SE && autocommit)
			$$ = mm_strdup("with hold");
		else
			$$ = EMPTY;
	}
ECPG: into_clauseINTOOptTempTableName block
					{
						FoundInto = 1;
						$$= cat2_str(mm_strdup("into"), $2);
					}
	| ecpg_into                     { $$ = EMPTY; }
ECPG: table_refselect_with_parens addon
		mmerror(PARSE_ERROR, ET_ERROR, "subquery in FROM must have an alias");
ECPG: TypenameSimpleTypenameopt_array_bounds block
	{	$$ = cat2_str($1, $2.str); }
ECPG: TypenameSETOFSimpleTypenameopt_array_bounds block
	{	$$ = cat_str(3, mm_strdup("setof"), $2, $3.str); }
ECPG: opt_array_boundsopt_array_bounds'['']' block
	{
		$$.index1 = $1.index1;
		$$.index2 = $1.index2;
		if (strcmp($$.index1, "-1") == 0)
			$$.index1 = mm_strdup("0");
		else if (strcmp($1.index2, "-1") == 0)
			$$.index2 = mm_strdup("0");
		$$.str = cat_str(2, $1.str, mm_strdup("[]"));
	}
	| opt_array_bounds '[' Iresult ']'
	{
		$$.index1 = $1.index1;
		$$.index2 = $1.index2;
		if (strcmp($1.index1, "-1") == 0)
			$$.index1 = strdup($3);
		else if (strcmp($1.index2, "-1") == 0)
			$$.index2 = strdup($3);
		$$.str = cat_str(4, $1.str, mm_strdup("["), $3, mm_strdup("]"));
	}
ECPG: opt_array_bounds
	{
		$$.index1 = mm_strdup("-1");
		$$.index2 = mm_strdup("-1");
		$$.str= EMPTY;
	}
ECPG: IconstICONST block
	{ $$ = make_name(); }
ECPG: AexprConstNULL_P rule
	| civar			{ $$ = $1; }
	| civarind		{ $$ = $1; }
ECPG: ColIdcol_name_keyword rule
	| ECPGKeywords                  { $$ = $1; }
	| ECPGCKeywords                 { $$ = $1; }
	| CHAR_P                        { $$ = mm_strdup("char"); }
	| VALUES                        { $$ = mm_strdup("values"); }
ECPG: type_function_nametype_func_name_keyword rule
	| ECPGKeywords                          { $$ = $1; }
	| ECPGTypeName                          { $$ = $1; }
	| ECPGCKeywords                         { $$ = $1; }
ECPG: VariableShowStmtSHOWALL block
	{
		mmerror(PARSE_ERROR, ET_ERROR, "SHOW ALL is not implemented");
		$$ = EMPTY;
	}
ECPG: FetchStmtMOVEfetch_args rule
	| FETCH fetch_args ecpg_fetch_into
	{
		$$ = cat2_str(mm_strdup("fetch"), $2);
	}
	| FETCH FORWARD cursor_name opt_ecpg_fetch_into
	{
		char *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
		add_additional_variables($3, false);
		$$ = cat_str(2, mm_strdup("fetch forward"), cursor_marker);
	}
	| FETCH FORWARD from_in cursor_name opt_ecpg_fetch_into
	{
		char *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
		add_additional_variables($4, false);
		$$ = cat_str(2, mm_strdup("fetch forward from"), cursor_marker);
	}
	| FETCH BACKWARD cursor_name opt_ecpg_fetch_into
	{
		char *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
		add_additional_variables($3, false);
		$$ = cat_str(2, mm_strdup("fetch backward"), cursor_marker);
	}
	| FETCH BACKWARD from_in cursor_name opt_ecpg_fetch_into
	{
		char *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
		add_additional_variables($4, false);
		$$ = cat_str(2, mm_strdup("fetch backward from"), cursor_marker);
	}
	| MOVE FORWARD cursor_name
	{
		char *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
		add_additional_variables($3, false);
		$$ = cat_str(2, mm_strdup("move forward"), cursor_marker);
	}
	| MOVE FORWARD from_in cursor_name
	{
		char *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
		add_additional_variables($4, false);
		$$ = cat_str(2, mm_strdup("move forward from"), cursor_marker);
	}
	| MOVE BACKWARD cursor_name
	{
		char *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
		add_additional_variables($3, false);
		$$ = cat_str(2, mm_strdup("move backward"), cursor_marker);
	}
	| MOVE BACKWARD from_in cursor_name
	{
		char *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
		add_additional_variables($4, false);
		$$ = cat_str(2, mm_strdup("move backward from"), cursor_marker);
	}
ECPG: limit_clauseLIMITselect_limit_value','select_offset_value block
        {
                mmerror(PARSE_ERROR, ET_WARNING, "no longer supported LIMIT #,# syntax passed to server");
                $$ = cat_str(4, mm_strdup("limit"), $2, mm_strdup(","), $4);
        }
ECPG: SignedIconstIconst rule
	| civar	{ $$ = $1; }
