From 64453763304e32775f6fdc891cd4b5834fc6902d Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 12 Aug 2024 15:00:40 -0400
Subject: [PATCH v3 6/7] Improve ecpg preprocessor's memory management.

Invent a notion of "local" storage that will automatically be
reclaimed at the end of each statement.  Use this for location
strings as well as other visibly short-lived data within the parser.

Also, make cat_str and make_str return local storage and not free
their inputs, which allows dispensing with a whole lot of retail
mm_strdup calls.  We do have to add some new ones in places where
a local-lifetime string needs to be added to a longer-lived data
structure, but on balance there are a lot less mm_strdup calls than
before.  I've not attempted to do any performance testing, but this
should result in substantially less malloc/free traffic than there
was in the old implementation.

In hopes of flushing out places where changes were necessary,
I changed YYLTYPE from "char *" to "const char *", which forced
const-ification of various function arguments that probably
should've been like that all along.
---
 src/interfaces/ecpg/preproc/descriptor.c     |  32 +-
 src/interfaces/ecpg/preproc/ecpg.addons      | 144 +++--
 src/interfaces/ecpg/preproc/ecpg.header      | 191 ++++---
 src/interfaces/ecpg/preproc/ecpg.trailer     | 548 ++++++++++---------
 src/interfaces/ecpg/preproc/output.c         |   5 +-
 src/interfaces/ecpg/preproc/parser.c         |   6 +-
 src/interfaces/ecpg/preproc/preproc_extern.h |  30 +-
 src/interfaces/ecpg/preproc/type.c           |   8 +-
 src/interfaces/ecpg/preproc/type.h           |  30 +-
 src/interfaces/ecpg/preproc/util.c           |  87 +++
 src/interfaces/ecpg/preproc/variable.c       |  31 +-
 src/tools/pgindent/typedefs.list             |   1 +
 12 files changed, 596 insertions(+), 517 deletions(-)

diff --git a/src/interfaces/ecpg/preproc/descriptor.c b/src/interfaces/ecpg/preproc/descriptor.c
index f4b1878289..9b87d07d09 100644
--- a/src/interfaces/ecpg/preproc/descriptor.c
+++ b/src/interfaces/ecpg/preproc/descriptor.c
@@ -18,13 +18,12 @@
 static struct assignment *assignments;
 
 void
-push_assignment(char *var, enum ECPGdtype value)
+push_assignment(const char *var, enum ECPGdtype value)
 {
 	struct assignment *new = (struct assignment *) mm_alloc(sizeof(struct assignment));
 
 	new->next = assignments;
-	new->variable = mm_alloc(strlen(var) + 1);
-	strcpy(new->variable, var);
+	new->variable = mm_strdup(var);
 	new->value = value;
 	assignments = new;
 }
@@ -73,7 +72,7 @@ ECPGnumeric_lvalue(char *name)
 static struct descriptor *descriptors;
 
 void
-add_descriptor(char *name, char *connection)
+add_descriptor(const char *name, const char *connection)
 {
 	struct descriptor *new;
 
@@ -83,20 +82,16 @@ add_descriptor(char *name, char *connection)
 	new = (struct descriptor *) mm_alloc(sizeof(struct descriptor));
 
 	new->next = descriptors;
-	new->name = mm_alloc(strlen(name) + 1);
-	strcpy(new->name, name);
+	new->name = mm_strdup(name);
 	if (connection)
-	{
-		new->connection = mm_alloc(strlen(connection) + 1);
-		strcpy(new->connection, connection);
-	}
+		new->connection = mm_strdup(connection);
 	else
-		new->connection = connection;
+		new->connection = NULL;
 	descriptors = new;
 }
 
 void
-drop_descriptor(char *name, char *connection)
+drop_descriptor(const char *name, const char *connection)
 {
 	struct descriptor *i;
 	struct descriptor **lastptr = &descriptors;
@@ -126,9 +121,8 @@ drop_descriptor(char *name, char *connection)
 		mmerror(PARSE_ERROR, ET_WARNING, "descriptor %s bound to the default connection does not exist", name);
 }
 
-struct descriptor
-		   *
-lookup_descriptor(char *name, char *connection)
+struct descriptor *
+lookup_descriptor(const char *name, const char *connection)
 {
 	struct descriptor *i;
 
@@ -159,7 +153,7 @@ lookup_descriptor(char *name, char *connection)
 }
 
 void
-output_get_descr_header(char *desc_name)
+output_get_descr_header(const char *desc_name)
 {
 	struct assignment *results;
 
@@ -178,7 +172,7 @@ output_get_descr_header(char *desc_name)
 }
 
 void
-output_get_descr(char *desc_name, char *index)
+output_get_descr(const char *desc_name, const char *index)
 {
 	struct assignment *results;
 
@@ -211,7 +205,7 @@ output_get_descr(char *desc_name, char *index)
 }
 
 void
-output_set_descr_header(char *desc_name)
+output_set_descr_header(const char *desc_name)
 {
 	struct assignment *results;
 
@@ -272,7 +266,7 @@ descriptor_item_name(enum ECPGdtype itemcode)
 }
 
 void
-output_set_descr(char *desc_name, char *index)
+output_set_descr(const char *desc_name, const char *index)
 {
 	struct assignment *results;
 
diff --git a/src/interfaces/ecpg/preproc/ecpg.addons b/src/interfaces/ecpg/preproc/ecpg.addons
index 24ee54554e..0120757312 100644
--- a/src/interfaces/ecpg/preproc/ecpg.addons
+++ b/src/interfaces/ecpg/preproc/ecpg.addons
@@ -45,18 +45,16 @@ ECPG: stmtExecuteStmt block
 			else
 			{
 				/* case of ecpg_ident or CSTRING */
-				char	   *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
-				char	   *str = mm_strdup($1.name + 1);
+				char	   *length = loc_alloc(32);
+				char	   *str;
 
-				/*
-				 * It must be cut off double quotation because new_variable()
-				 * double-quotes.
-				 */
+				/* Remove double quotes from name */
+				str = loc_strdup($1.name + 1);
 				str[strlen(str) - 1] = '\0';
-				sprintf(length, "%zu", strlen(str));
+				snprintf(length, 32, "%zu", strlen(str));
 				add_variable_to_tail(&argsinsert, new_variable(str, ECPGmake_simple_type(ECPGt_const, length, 0), 0), &no_indicator);
 			}
-			output_statement(cat_str(3, mm_strdup("execute"), mm_strdup("$0"), $1.type), 0, ECPGst_exec_with_exprlist);
+			output_statement(cat_str(3, "execute", "$0", $1.type), 0, ECPGst_exec_with_exprlist);
 		}
 	}
 ECPG: stmtPrepareStmt block
@@ -66,7 +64,7 @@ ECPG: stmtPrepareStmt block
 			output_prepare_statement($1.name, $1.stmt);
 		else if (strlen($1.type) == 0)
 		{
-			char	   *stmt = cat_str(3, mm_strdup("\""), $1.stmt, mm_strdup("\""));
+			char	   *stmt = cat_str(3, "\"", $1.stmt, "\"");
 
 			output_prepare_statement($1.name, stmt);
 		}
@@ -77,18 +75,16 @@ ECPG: stmtPrepareStmt block
 				add_variable_to_tail(&argsinsert, find_variable($1.name), &no_indicator);
 			else
 			{
-				char	   *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
-				char	   *str = mm_strdup($1.name + 1);
+				char	   *length = loc_alloc(32);
+				char	   *str;
 
-				/*
-				 * It must be cut off double quotation because new_variable()
-				 * double-quotes.
-				 */
+				/* Remove double quotes from name */
+				str = loc_strdup($1.name + 1);
 				str[strlen(str) - 1] = '\0';
-				sprintf(length, "%zu", strlen(str));
+				snprintf(length, 32, "%zu", strlen(str));
 				add_variable_to_tail(&argsinsert, new_variable(str, ECPGmake_simple_type(ECPGt_const, length, 0), 0), &no_indicator);
 			}
-			output_statement(cat_str(5, mm_strdup("prepare"), mm_strdup("$0"), $1.type, mm_strdup("as"), $1.stmt), 0, ECPGst_prepare);
+			output_statement(cat_str(5, "prepare", "$0", $1.type, "as", $1.stmt), 0, ECPGst_prepare);
 		}
 	}
 ECPG: stmtTransactionStmt block
@@ -142,8 +138,6 @@ ECPG: stmtViewStmt rule
 		fputs("ECPGt_EORT);", base_yyout);
 		fprintf(base_yyout, "}");
 		output_line_number();
-
-		free($1.stmt_name);
 	}
 	| ECPGDisconnect
 	{
@@ -175,8 +169,6 @@ ECPG: stmtViewStmt rule
 	{
 		lookup_descriptor($1.name, connection);
 		output_get_descr($1.name, $1.str);
-		free($1.name);
-		free($1.str);
 	}
 	| ECPGGetDescriptorHeader
 	{
@@ -190,7 +182,7 @@ ECPG: stmtViewStmt rule
 		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);
+			output_statement(ptr->command, 0, ECPGst_normal);
 			ptr->opened = true;
 		}
 	}
@@ -211,8 +203,6 @@ ECPG: stmtViewStmt rule
 	{
 		lookup_descriptor($1.name, connection);
 		output_set_descr($1.name, $1.str);
-		free($1.name);
-		free($1.str);
 	}
 	| ECPGSetDescriptorHeader
 	{
@@ -243,9 +233,9 @@ ECPG: stmtViewStmt rule
 	}
 ECPG: where_or_current_clauseWHERECURRENT_POFcursor_name block
 	{
-		char	   *cursor_marker = @4[0] == ':' ? mm_strdup("$0") : @4;
+		const char *cursor_marker = @4[0] == ':' ? "$0" : @4;
 
-		@$ = cat_str(2, mm_strdup("where current of"), cursor_marker);
+		@$ = cat_str(2, "where current of", cursor_marker);
 	}
 ECPG: CopyStmtCOPYopt_binaryqualified_nameopt_column_listcopy_fromopt_programcopy_file_namecopy_delimiteropt_withcopy_optionswhere_clause addon
 		if (strcmp(@6, "from") == 0 &&
@@ -253,21 +243,21 @@ ECPG: CopyStmtCOPYopt_binaryqualified_nameopt_column_listcopy_fromopt_programcop
 			mmerror(PARSE_ERROR, ET_WARNING, "COPY FROM STDIN is not implemented");
 ECPG: var_valueNumericOnly addon
 		if (@1[0] == '$')
-			@$ = mm_strdup("$0");
+			@$ = "$0";
 ECPG: fetch_argscursor_name addon
 		struct cursor *ptr = add_additional_variables(@1, false);
 
 		if (ptr->connection)
 			connection = mm_strdup(ptr->connection);
 		if (@1[0] == ':')
-			@$ = mm_strdup("$0");
+			@$ = "$0";
 ECPG: fetch_argsfrom_incursor_name addon
 		struct cursor *ptr = add_additional_variables(@2, false);
 
 		if (ptr->connection)
 			connection = mm_strdup(ptr->connection);
 		if (@2[0] == ':')
-			@$ = cat2_str(mm_strdup(@1), mm_strdup("$0"));
+			@$ = cat2_str(@1, "$0");
 ECPG: fetch_argsNEXTopt_from_incursor_name addon
 ECPG: fetch_argsPRIORopt_from_incursor_name addon
 ECPG: fetch_argsFIRST_Popt_from_incursor_name addon
@@ -278,7 +268,7 @@ ECPG: fetch_argsALLopt_from_incursor_name addon
 		if (ptr->connection)
 			connection = mm_strdup(ptr->connection);
 		if (@3[0] == ':')
-			@$ = cat_str(3, mm_strdup(@1), mm_strdup(@2), mm_strdup("$0"));
+			@$ = cat_str(3, @1, @2, "$0");
 ECPG: fetch_argsSignedIconstopt_from_incursor_name addon
 		struct cursor *ptr = add_additional_variables(@3, false);
 		bool	replace = false;
@@ -287,16 +277,16 @@ ECPG: fetch_argsSignedIconstopt_from_incursor_name addon
 			connection = mm_strdup(ptr->connection);
 		if (@3[0] == ':')
 		{
-			@3 = mm_strdup("$0");
+			@3 = "$0";
 			replace = true;
 		}
 		if (@1[0] == '$')
 		{
-			@1 = mm_strdup("$0");
+			@1 = "$0";
 			replace = true;
 		}
 		if (replace)
-			@$ = cat_str(3, mm_strdup(@1), mm_strdup(@2), mm_strdup(@3));
+			@$ = cat_str(3, @1, @2, @3);
 ECPG: fetch_argsFORWARDALLopt_from_incursor_name addon
 ECPG: fetch_argsBACKWARDALLopt_from_incursor_name addon
 		struct cursor *ptr = add_additional_variables(@4, false);
@@ -304,7 +294,7 @@ ECPG: fetch_argsBACKWARDALLopt_from_incursor_name addon
 		if (ptr->connection)
 			connection = mm_strdup(ptr->connection);
 		if (@4[0] == ':')
-			@$ = cat_str(4, mm_strdup(@1), mm_strdup(@2), mm_strdup(@3), mm_strdup("$0"));
+			@$ = cat_str(4, @1, @2, @3, "$0");
 ECPG: fetch_argsABSOLUTE_PSignedIconstopt_from_incursor_name addon
 ECPG: fetch_argsRELATIVE_PSignedIconstopt_from_incursor_name addon
 ECPG: fetch_argsFORWARDSignedIconstopt_from_incursor_name addon
@@ -316,20 +306,20 @@ ECPG: fetch_argsBACKWARDSignedIconstopt_from_incursor_name addon
 			connection = mm_strdup(ptr->connection);
 		if (@4[0] == ':')
 		{
-			@4 = mm_strdup("$0");
+			@4 = "$0";
 			replace = true;
 		}
 		if (@2[0] == '$')
 		{
-			@2 = mm_strdup("$0");
+			@2 = "$0";
 			replace = true;
 		}
 		if (replace)
-			@$ = cat_str(4, mm_strdup(@1), mm_strdup(@2), mm_strdup(@3), mm_strdup(@4));
+			@$ = cat_str(4, @1, @2, @3, @4);
 ECPG: cursor_namename block
 	| char_civar
 	{
-		char	   *curname = mm_alloc(strlen(@1) + 2);
+		char	   *curname = loc_alloc(strlen(@1) + 2);
 
 		sprintf(curname, ":%s", @1);
 		@$ = curname;
@@ -367,7 +357,7 @@ ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectSt
 	{
 		struct cursor *ptr,
 				   *this;
-		char	   *cursor_marker = @2[0] == ':' ? mm_strdup("$0") : mm_strdup(@2);
+		const char *cursor_marker = @2[0] == ':' ? "$0" : @2;
 		char	   *comment,
 				   *c1,
 				   *c2;
@@ -394,7 +384,7 @@ ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectSt
 		this->function = (current_function ? mm_strdup(current_function) : NULL);
 		this->connection = connection ? mm_strdup(connection) : NULL;
 		this->opened = false;
-		this->command = cat_str(7, mm_strdup("declare"), cursor_marker, @3, mm_strdup("cursor"), @5, mm_strdup("for"), @7);
+		this->command = mm_strdup(cat_str(7, "declare", cursor_marker, @3, "cursor", @5, "for", @7));
 		this->argsinsert = argsinsert;
 		this->argsinsert_oos = NULL;
 		this->argsresult = argsresult;
@@ -402,20 +392,20 @@ ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectSt
 		argsinsert = argsresult = NULL;
 		cur = this;
 
-		c1 = mm_strdup(this->command);
-		if ((c2 = strstr(c1, "*/")) != NULL)
+		c1 = loc_strdup(this->command);
+		while ((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("*/"));
+		comment = cat_str(3, "/*", c1, "*/");
 
 		@$ = cat2_str(adjust_outofscope_cursor_vars(this), comment);
 	}
 ECPG: ClosePortalStmtCLOSEcursor_name block
 	{
-		char	   *cursor_marker = @2[0] == ':' ? mm_strdup("$0") : @2;
+		const char *cursor_marker = @2[0] == ':' ? "$0" : @2;
 		struct cursor *ptr = NULL;
 
 		for (ptr = cur; ptr != NULL; ptr = ptr->next)
@@ -427,23 +417,23 @@ ECPG: ClosePortalStmtCLOSEcursor_name block
 				break;
 			}
 		}
-		@$ = cat2_str(mm_strdup("close"), cursor_marker);
+		@$ = cat2_str("close", cursor_marker);
 	}
 ECPG: opt_hold block
 	{
 		if (compat == ECPG_COMPAT_INFORMIX_SE && autocommit)
-			@$ = mm_strdup("with hold");
+			@$ = "with hold";
 		else
-			@$ = EMPTY;
+			@$ = "";
 	}
 ECPG: into_clauseINTOOptTempTableName block
 	{
 		FoundInto = 1;
-		@$ = cat2_str(mm_strdup("into"), @2);
+		@$ = cat2_str("into", @2);
 	}
 	| ecpg_into
 	{
-		@$ = EMPTY;
+		@$ = "";
 	}
 ECPG: TypenameSimpleTypenameopt_array_bounds block
 	{
@@ -451,37 +441,33 @@ ECPG: TypenameSimpleTypenameopt_array_bounds block
 	}
 ECPG: TypenameSETOFSimpleTypenameopt_array_bounds block
 	{
-		@$ = cat_str(3, mm_strdup("setof"), @2, $3.str);
+		@$ = cat_str(3, "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");
+			$$.index1 = "0";
 		else if (strcmp($1.index2, "-1") == 0)
-			$$.index2 = mm_strdup("0");
-		$$.str = cat_str(2, $1.str, mm_strdup("[]"));
+			$$.index2 = "0";
+		$$.str = cat_str(2, $1.str, "[]");
 	}
 	| opt_array_bounds '[' Iresult ']'
 	{
 		$$.index1 = $1.index1;
 		$$.index2 = $1.index2;
 		if (strcmp($1.index1, "-1") == 0)
-			$$.index1 = mm_strdup(@3);
+			$$.index1 = @3;
 		else if (strcmp($1.index2, "-1") == 0)
-			$$.index2 = mm_strdup(@3);
-		$$.str = cat_str(4, $1.str, mm_strdup("["), @3, mm_strdup("]"));
+			$$.index2 = @3;
+		$$.str = cat_str(4, $1.str, "[", @3, "]");
 	}
 ECPG: opt_array_bounds block
 	{
-		$$.index1 = mm_strdup("-1");
-		$$.index2 = mm_strdup("-1");
-		$$.str = EMPTY;
-	}
-ECPG: IconstICONST block
-	{
-		@$ = make_name();
+		$$.index1 = "-1";
+		$$.index2 = "-1";
+		$$.str = "";
 	}
 ECPG: AexprConstNULL_P rule
 	| civar
@@ -494,83 +480,83 @@ ECPG: FetchStmtMOVEfetch_args rule
 	| FETCH fetch_args ecpg_fetch_into
 	| FETCH FORWARD cursor_name opt_ecpg_fetch_into
 	{
-		char	   *cursor_marker = @3[0] == ':' ? mm_strdup("$0") : @3;
+		const char *cursor_marker = @3[0] == ':' ? "$0" : @3;
 		struct cursor *ptr = add_additional_variables(@3, false);
 
 		if (ptr->connection)
 			connection = mm_strdup(ptr->connection);
 
-		@$ = cat_str(2, mm_strdup("fetch forward"), cursor_marker);
+		@$ = cat_str(2, "fetch forward", cursor_marker);
 	}
 	| FETCH FORWARD from_in cursor_name opt_ecpg_fetch_into
 	{
-		char	   *cursor_marker = @4[0] == ':' ? mm_strdup("$0") : @4;
+		const char *cursor_marker = @4[0] == ':' ? "$0" : @4;
 		struct cursor *ptr = add_additional_variables(@4, false);
 
 		if (ptr->connection)
 			connection = mm_strdup(ptr->connection);
 
-		@$ = cat_str(2, mm_strdup("fetch forward from"), cursor_marker);
+		@$ = cat_str(2, "fetch forward from", cursor_marker);
 	}
 	| FETCH BACKWARD cursor_name opt_ecpg_fetch_into
 	{
-		char	   *cursor_marker = @3[0] == ':' ? mm_strdup("$0") : @3;
+		const char *cursor_marker = @3[0] == ':' ? "$0" : @3;
 		struct cursor *ptr = add_additional_variables(@3, false);
 
 		if (ptr->connection)
 			connection = mm_strdup(ptr->connection);
 
-		@$ = cat_str(2, mm_strdup("fetch backward"), cursor_marker);
+		@$ = cat_str(2, "fetch backward", cursor_marker);
 	}
 	| FETCH BACKWARD from_in cursor_name opt_ecpg_fetch_into
 	{
-		char	   *cursor_marker = @4[0] == ':' ? mm_strdup("$0") : @4;
+		const char *cursor_marker = @4[0] == ':' ? "$0" : @4;
 		struct cursor *ptr = add_additional_variables(@4, false);
 
 		if (ptr->connection)
 			connection = mm_strdup(ptr->connection);
 
-		@$ = cat_str(2, mm_strdup("fetch backward from"), cursor_marker);
+		@$ = cat_str(2, "fetch backward from", cursor_marker);
 	}
 	| MOVE FORWARD cursor_name
 	{
-		char	   *cursor_marker = @3[0] == ':' ? mm_strdup("$0") : @3;
+		const char *cursor_marker = @3[0] == ':' ? "$0" : @3;
 		struct cursor *ptr = add_additional_variables(@3, false);
 
 		if (ptr->connection)
 			connection = mm_strdup(ptr->connection);
 
-		@$ = cat_str(2, mm_strdup("move forward"), cursor_marker);
+		@$ = cat_str(2, "move forward", cursor_marker);
 	}
 	| MOVE FORWARD from_in cursor_name
 	{
-		char	   *cursor_marker = @4[0] == ':' ? mm_strdup("$0") : @4;
+		const char *cursor_marker = @4[0] == ':' ? "$0" : @4;
 		struct cursor *ptr = add_additional_variables(@4, false);
 
 		if (ptr->connection)
 			connection = mm_strdup(ptr->connection);
 
-		@$ = cat_str(2, mm_strdup("move forward from"), cursor_marker);
+		@$ = cat_str(2, "move forward from", cursor_marker);
 	}
 	| MOVE BACKWARD cursor_name
 	{
-		char	   *cursor_marker = @3[0] == ':' ? mm_strdup("$0") : @3;
+		const char *cursor_marker = @3[0] == ':' ? "$0" : @3;
 		struct cursor *ptr = add_additional_variables(@3, false);
 
 		if (ptr->connection)
 			connection = mm_strdup(ptr->connection);
 
-		@$ = cat_str(2, mm_strdup("move backward"), cursor_marker);
+		@$ = cat_str(2, "move backward", cursor_marker);
 	}
 	| MOVE BACKWARD from_in cursor_name
 	{
-		char	   *cursor_marker = @4[0] == ':' ? mm_strdup("$0") : @4;
+		const char *cursor_marker = @4[0] == ':' ? "$0" : @4;
 		struct cursor *ptr = add_additional_variables(@4, false);
 
 		if (ptr->connection)
 			connection = mm_strdup(ptr->connection);
 
-		@$ = cat_str(2, mm_strdup("move backward from"), cursor_marker);
+		@$ = cat_str(2, "move backward from", cursor_marker);
 	}
 ECPG: limit_clauseLIMITselect_limit_value','select_offset_value block
 	{
diff --git a/src/interfaces/ecpg/preproc/ecpg.header b/src/interfaces/ecpg/preproc/ecpg.header
index 76f60e71e4..a330874662 100644
--- a/src/interfaces/ecpg/preproc/ecpg.header
+++ b/src/interfaces/ecpg/preproc/ecpg.header
@@ -39,8 +39,6 @@ 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;
@@ -64,23 +62,24 @@ static bool check_declared_list(const char *name);
 
 
 /*
- * string concatenation
+ * String concatenation support routines.  These return "local" (transient)
+ * storage.  cat2_str and cat_str insert spaces between nonempty inputs;
+ * make2_str and make3_str do not.
  */
 
 static char *
-cat2_str(char *str1, char *str2)
+cat2_str(const char *str1, const char *str2)
 {
-	char	   *res_str = (char *) mm_alloc(strlen(str1) + strlen(str2) + 2);
+	char	   *res_str = (char *) loc_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;
 }
 
+/* Concatenate N inputs */
 static char *
 cat_str(int count,...)
 {
@@ -102,28 +101,23 @@ cat_str(int count,...)
 }
 
 static char *
-make2_str(char *str1, char *str2)
+make2_str(const char *str1, const char *str2)
 {
-	char	   *res_str = (char *) mm_alloc(strlen(str1) + strlen(str2) + 1);
+	char	   *res_str = (char *) loc_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)
+make3_str(const char *str1, const char *str2, const char *str3)
 {
-	char	   *res_str = (char *) mm_alloc(strlen(str1) + strlen(str2) + strlen(str3) + 1);
+	char	   *res_str = (char *) loc_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;
 }
 
@@ -159,7 +153,7 @@ yylloc_default(YYLTYPE *target, YYLTYPE *rhs, int N)
 				needed++;
 			needed += thislen;
 		}
-		result = (char *) mm_alloc(needed + 1);
+		result = (char *) loc_alloc(needed + 1);
 		ptr = result;
 		for (int i = 1; i <= N; i++)
 		{
@@ -179,22 +173,19 @@ yylloc_default(YYLTYPE *target, YYLTYPE *rhs, int N)
 		*target = rhs[1];
 	}
 	else
-		*target = EMPTY;
+	{
+		/* No need to allocate any space */
+		*target = "";
+	}
 }
 
 /* and the rest */
 static char *
-make_name(void)
-{
-	return mm_strdup(base_yytext);
-}
-
-static char *
-create_questionmarks(char *name, bool array)
+create_questionmarks(const char *name, bool array)
 {
 	struct variable *p = find_variable(name);
 	int			count;
-	char	   *result = EMPTY;
+	char	   *result = "";
 
 	/*
 	 * In case we have a struct, we have to print as many "?" as there are
@@ -222,12 +213,13 @@ create_questionmarks(char *name, bool array)
 
 	for (; count > 0; count--)
 	{
-		sprintf(pacounter_buffer, "$%d", pacounter++);
-		result = cat_str(3, result, mm_strdup(pacounter_buffer), mm_strdup(" , "));
-	}
+		char	buf[32];
 
-	/* removed the trailing " ," */
+		snprintf(buf, sizeof(buf), "$%d", pacounter++);
+		result = cat_str(3, result, buf, " , ");
+	}
 
+	/* remove the trailing " ," */
 	result[strlen(result) - 3] = '\0';
 	return result;
 }
@@ -247,8 +239,7 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
 	 * pointer instead of the variable. Do it only for local variables, not
 	 * for globals.
 	 */
-
-	char	   *result = EMPTY;
+	char	   *result = "";
 	int			insert;
 
 	for (insert = 1; insert >= 0; insert--)
@@ -270,7 +261,7 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
 
 			/* change variable name to "ECPGget_var(<counter>)" */
 			original_var = ptr->variable->name;
-			sprintf(var_text, "%d))", ecpg_internal_var);
+			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)
@@ -291,12 +282,12 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
 					  && ptr->variable->type->type != ECPGt_bytea)
 					 && 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)),
+				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,
-																			   mm_strdup("1"),
+																			   "1",
 																			   ptr->variable->type->u.element->counter),
 														  ptr->variable->type->size),
 									  0);
@@ -308,10 +299,10 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
 					  || ptr->variable->type->type == ECPGt_bytea)
 					 && 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)),
+				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),
@@ -323,11 +314,11 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
 			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(")")),
+				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,
@@ -340,11 +331,11 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
 				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(")")),
+					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,
@@ -353,10 +344,10 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
 				}
 				else
 				{
-					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)),
+					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),
@@ -367,10 +358,10 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
 			}
 			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)),
+				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),
@@ -384,10 +375,11 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
 			 */
 			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"));
+				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");
 			}
 
 			/*
@@ -402,17 +394,17 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
 			{
 				/* change variable name to "ECPGget_var(<counter>)" */
 				original_var = ptr->indicator->name;
-				sprintf(var_text, "%d))", ecpg_internal_var);
+				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, mm_strdup("(*("),
-												  mm_strdup(ptr->indicator->type->type_name),
-												  mm_strdup(" *)(ECPGget_var("),
-												  mm_strdup(var_text),
-												  mm_strdup(")")),
+					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,
@@ -425,11 +417,11 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
 					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(")")),
+						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,
@@ -438,9 +430,10 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
 					}
 					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)),
+						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),
@@ -451,10 +444,10 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
 				}
 				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)),
+					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),
@@ -462,10 +455,10 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
 				}
 				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)),
+					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),
@@ -477,10 +470,11 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
 				 * 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"));
+				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);
@@ -501,7 +495,7 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
 	 (cur->function != NULL && strcmp(cur->function, current_function) == 0))
 
 static struct cursor *
-add_additional_variables(char *name, bool insert)
+add_additional_variables(const char *name, bool insert)
 {
 	struct cursor *ptr;
 	struct arguments *p;
@@ -539,8 +533,10 @@ add_additional_variables(char *name, bool insert)
 }
 
 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_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,
@@ -560,19 +556,20 @@ add_typedef(char *name, char *dimension, char *length, enum ECPGttype type_enum,
 				/* 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);
+		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->name = mm_strdup(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_dimension = mm_strdup(dimension); /* dimension of array */
+		this->type->type_index = mm_strdup(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;
diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer
index e6475e170d..392b5032bf 100644
--- a/src/interfaces/ecpg/preproc/ecpg.trailer
+++ b/src/interfaces/ecpg/preproc/ecpg.trailer
@@ -2,6 +2,12 @@
 
 statements: /* EMPTY */
 	| statements statement
+	{
+		/* Reclaim local storage used while processing statement */
+		reclaim_local_storage();
+		/* Clean up now-dangling location pointer */
+		@$ = "";
+	}
 	;
 
 statement: ecpgstart at toplevel_stmt ';'
@@ -68,7 +74,7 @@ CreateAsStmt: CREATE OptTemp TABLE create_as_target AS
 
 at: AT connection_object
 	{
-		connection = @2;
+		connection = mm_strdup(@2);
 
 		/*
 		 * Do we have a variable as connection target?  Remove the variable
@@ -84,20 +90,20 @@ at: AT connection_object
  */
 ECPGConnect: SQL_CONNECT TO connection_target opt_connection_name opt_user
 	{
-		@$ = cat_str(5, @3, mm_strdup(","), @5, mm_strdup(","), @4);
+		@$ = cat_str(5, @3, ",", @5, ",", @4);
 	}
 	| SQL_CONNECT TO DEFAULT
 	{
-		@$ = mm_strdup("NULL, NULL, NULL, \"DEFAULT\"");
+		@$ = "NULL, NULL, NULL, \"DEFAULT\"";
 	}
 	/* also allow ORACLE syntax */
 	| SQL_CONNECT ora_user
 	{
-		@$ = cat_str(3, mm_strdup("NULL,"), @2, mm_strdup(", NULL"));
+		@$ = cat_str(3, "NULL,", @2, ", NULL");
 	}
 	| DATABASE connection_target
 	{
-		@$ = cat2_str(@2, mm_strdup(", NULL, NULL, NULL"));
+		@$ = cat2_str(@2, ", NULL, NULL, NULL");
 	}
 	;
 
@@ -111,7 +117,7 @@ connection_target: opt_database_name opt_server opt_port
 		if (@1[0] == '\"')
 			@$ = @1;
 		else
-			@$ = make3_str(mm_strdup("\""), make3_str(@1, @2, @3), mm_strdup("\""));
+			@$ = make3_str("\"", make3_str(@1, @2, @3), "\"");
 	}
 	| db_prefix ':' server opt_port '/' opt_database_name opt_options
 	{
@@ -127,19 +133,21 @@ connection_target: opt_database_name opt_server opt_port
 			strncmp(@3 + strlen("//"), "127.0.0.1", strlen("127.0.0.1")) != 0)
 			mmerror(PARSE_ERROR, ET_ERROR, "Unix-domain sockets only work on \"localhost\" but not on \"%s\"", @3 + strlen("//"));
 
-		@$ = make3_str(make3_str(mm_strdup("\""), @1, mm_strdup(":")), @3, make3_str(make3_str(@4, mm_strdup("/"), @6), @7, mm_strdup("\"")));
+		@$ = make3_str(make3_str("\"", @1, ":"), @3, make3_str(make3_str(@4, "/", @6), @7, "\""));
 	}
 	| char_variable
 	| ecpg_sconst
 	{
 		/*
-		 * We can only process double quoted strings not single quotes ones,
-		 * so we change the quotes. Note, that the rule for ecpg_sconst adds
+		 * We can only process double quoted strings not single quoted ones,
+		 * so we change the quotes. Note that the rule for ecpg_sconst adds
 		 * these single quotes.
 		 */
-		@1[0] = '\"';
-		@1[strlen(@1) - 1] = '\"';
-		@$ = @1;
+		char   *str = loc_strdup(@1);
+
+		str[0] = '\"';
+		str[strlen(str) - 1] = '\"';
+		@$ = str;
 	}
 	;
 
@@ -155,7 +163,7 @@ db_prefix: ecpg_ident cvariable
 		if (strcmp(@1, "tcp") != 0 && strcmp(@1, "unix") != 0)
 			mmerror(PARSE_ERROR, ET_ERROR, "invalid connection type: %s", @1);
 
-		@$ = make3_str(@1, mm_strdup(":"), @2);
+		@$ = make3_str(@1, ":", @2);
 	}
 	;
 
@@ -175,14 +183,11 @@ opt_server: server
 server_name: ColId
 	| ColId '.' server_name
 	| IP
-	{
-		@$ = make_name();
-	}
 	;
 
 opt_port: ':' Iconst
 	{
-		@$ = make2_str(mm_strdup(":"), @2);
+		@$ = make2_str(":", @2);
 	}
 	| /* EMPTY */
 	;
@@ -193,7 +198,7 @@ opt_connection_name: AS connection_object
 	}
 	| /* EMPTY */
 	{
-		@$ = mm_strdup("NULL");
+		@$ = "NULL";
 	}
 	;
 
@@ -203,25 +208,25 @@ opt_user: USER ora_user
 	}
 	| /* EMPTY */
 	{
-		@$ = mm_strdup("NULL, NULL");
+		@$ = "NULL, NULL";
 	}
 	;
 
 ora_user: user_name
 	{
-		@$ = cat2_str(@1, mm_strdup(", NULL"));
+		@$ = cat2_str(@1, ", NULL");
 	}
 	| user_name '/' user_name
 	{
-		@$ = cat_str(3, @1, mm_strdup(","), @3);
+		@$ = cat_str(3, @1, ",", @3);
 	}
 	| user_name SQL_IDENTIFIED BY user_name
 	{
-		@$ = cat_str(3, @1, mm_strdup(","), @4);
+		@$ = cat_str(3, @1, ",", @4);
 	}
 	| user_name USING user_name
 	{
-		@$ = cat_str(3, @1, mm_strdup(","), @3);
+		@$ = cat_str(3, @1, ",", @3);
 	}
 	;
 
@@ -230,14 +235,14 @@ user_name: RoleId
 		if (@1[0] == '\"')
 			@$ = @1;
 		else
-			@$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
+			@$ = make3_str("\"", @1, "\"");
 	}
 	| ecpg_sconst
 	{
 		if (@1[0] == '\"')
 			@$ = @1;
 		else
-			@$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
+			@$ = make3_str("\"", @1, "\"");
 	}
 	| civar
 	{
@@ -249,9 +254,9 @@ user_name: RoleId
 
 		/* handle varchars */
 		if (type == ECPGt_varchar)
-			@$ = make2_str(mm_strdup(argsinsert->variable->name), mm_strdup(".arr"));
+			@$ = make2_str(argsinsert->variable->name, ".arr");
 		else
-			@$ = mm_strdup(argsinsert->variable->name);
+			@$ = argsinsert->variable->name;
 	}
 	;
 
@@ -278,7 +283,7 @@ char_variable: cvariable
 					@$ = @1;
 					break;
 				case ECPGt_varchar:
-					@$ = make2_str(@1, mm_strdup(".arr"));
+					@$ = make2_str(@1, ".arr");
 					break;
 				default:
 					mmerror(PARSE_ERROR, ET_ERROR, "invalid data type");
@@ -297,7 +302,7 @@ opt_options: Op connect_options
 		if (strcmp(@1, "?") != 0)
 			mmerror(PARSE_ERROR, ET_ERROR, "unrecognized token \"%s\"", @1);
 
-		@$ = make2_str(mm_strdup("?"), @2);
+		@$ = make2_str("?", @2);
 	}
 	| /* EMPTY */
 	;
@@ -321,30 +326,34 @@ connect_options: ColId opt_opt_value
 opt_opt_value: /* EMPTY */
 	| '=' Iconst
 	{
-		@$ = make2_str(mm_strdup("="), @2);
+		@$ = make2_str("=", @2);
 	}
 	| '=' ecpg_ident
 	{
-		@$ = make2_str(mm_strdup("="), @2);
+		@$ = make2_str("=", @2);
 	}
 	| '=' civar
 	{
-		@$ = make2_str(mm_strdup("="), @2);
+		@$ = make2_str("=", @2);
 	}
 	;
 
 prepared_name: name
 	{
-		if (@1[0] == '\"' && @1[strlen(@1) - 1] == '\"')	/* already quoted? */
+		size_t		slen = strlen(@1);
+
+		if (@1[0] == '\"' && @1[slen - 1] == '\"')	/* already quoted? */
 			@$ = @1;
 		else					/* not quoted => convert to lowercase */
 		{
-			size_t		i;
-
-			for (i = 0; i < strlen(@1); i++)
-				@1[i] = tolower((unsigned char) @1[i]);
-
-			@$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
+			char	   *str = loc_alloc(slen + 3);
+
+			str[0] = '\"';
+			for (size_t i = 0; i < slen; i++)
+				str[i + 1] = tolower((unsigned char) @1[i]);
+			str[slen + 1] = '\"';
+			str[slen + 2] = '\0';
+			@$ = str;
 		}
 	}
 	| char_variable
@@ -355,7 +364,7 @@ prepared_name: name
  */
 ECPGDeclareStmt: DECLARE prepared_name STATEMENT
 	{
-		struct declared_list *ptr = NULL;
+		struct declared_list *ptr;
 
 		/* Check whether the declared name has been defined or not */
 		for (ptr = g_declared_list; ptr != NULL; ptr = ptr->next)
@@ -368,12 +377,11 @@ ECPGDeclareStmt: DECLARE prepared_name STATEMENT
 		}
 
 		/* Add a new declared name into the g_declared_list */
-		ptr = NULL;
 		ptr = (struct declared_list *) mm_alloc(sizeof(struct declared_list));
 		if (ptr)
 		{
 			/* initial definition */
-			ptr->name = @2;
+			ptr->name = mm_strdup(@2);
 			if (connection)
 				ptr->connection = mm_strdup(connection);
 			else
@@ -383,7 +391,7 @@ ECPGDeclareStmt: DECLARE prepared_name STATEMENT
 			g_declared_list = ptr;
 		}
 
-		@$ = cat_str(3, mm_strdup("/* declare "), mm_strdup(@2), mm_strdup(" as an SQL identifier */"));
+		@$ = cat_str(3, "/* declare ", @2, " as an SQL identifier */");
 	}
 	;
 
@@ -395,7 +403,7 @@ ECPGCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared_
 	{
 		struct cursor *ptr,
 				   *this;
-		char	   *cursor_marker = @2[0] == ':' ? mm_strdup("$0") : mm_strdup(@2);
+		const char *cursor_marker = @2[0] == ':' ? "$0" : @2;
 		int			(*strcmp_fn) (const char *, const char *) = ((@2[0] == ':' || @2[0] == '"') ? strcmp : pg_strcasecmp);
 		struct variable *thisquery = (struct variable *) mm_alloc(sizeof(struct variable));
 		char	   *comment;
@@ -422,10 +430,10 @@ ECPGCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared_
 
 		/* initial definition */
 		this->next = cur;
-		this->name = @2;
+		this->name = mm_strdup(@2);
 		this->function = (current_function ? mm_strdup(current_function) : NULL);
 		this->connection = connection ? mm_strdup(connection) : NULL;
-		this->command = cat_str(6, mm_strdup("declare"), cursor_marker, @3, mm_strdup("cursor"), @5, mm_strdup("for $1"));
+		this->command = mm_strdup(cat_str(6, "declare", cursor_marker, @3, "cursor", @5, "for $1"));
 		this->argsresult = NULL;
 		this->argsresult_oos = NULL;
 
@@ -448,7 +456,7 @@ ECPGCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared_
 
 		cur = this;
 
-		comment = cat_str(3, mm_strdup("/*"), mm_strdup(this->command), mm_strdup("*/"));
+		comment = cat_str(3, "/*", this->command, "*/");
 
 		@$ = cat_str(2, adjust_outofscope_cursor_vars(this),
 					 comment);
@@ -541,45 +549,44 @@ type_declaration: S_TYPEDEF
 
 		fprintf(base_yyout, "typedef %s %s %s %s;\n", $3.type_str, *@4 ? "*" : "", @5, $6.str);
 		output_line_number();
-		@$ = EMPTY;
+		@$ = "";
 	}
 	;
 
 var_declaration:
 	storage_declaration var_type
 	{
-		actual_type[struct_level].type_storage = @1;
+		actual_type[struct_level].type_storage = mm_strdup(@1);
 		actual_type[struct_level].type_enum = $2.type_enum;
-		actual_type[struct_level].type_str = $2.type_str;
-		actual_type[struct_level].type_dimension = $2.type_dimension;
-		actual_type[struct_level].type_index = $2.type_index;
-		actual_type[struct_level].type_sizeof = $2.type_sizeof;
+		actual_type[struct_level].type_str = mm_strdup($2.type_str);
+		actual_type[struct_level].type_dimension = mm_strdup($2.type_dimension);
+		actual_type[struct_level].type_index = mm_strdup($2.type_index);
+		actual_type[struct_level].type_sizeof =
+			$2.type_sizeof ? mm_strdup($2.type_sizeof) : NULL;
 
 		actual_startline[struct_level] = hashline_number();
 	}
 	variable_list ';'
 	{
-		@$ = cat_str(5, actual_startline[struct_level], @1, $2.type_str, @4, mm_strdup(";\n"));
+		@$ = cat_str(5, actual_startline[struct_level], @1, $2.type_str, @4, ";\n");
 	}
 	| var_type
 	{
-		actual_type[struct_level].type_storage = EMPTY;
+		actual_type[struct_level].type_storage = mm_strdup("");
 		actual_type[struct_level].type_enum = $1.type_enum;
-		actual_type[struct_level].type_str = $1.type_str;
-		actual_type[struct_level].type_dimension = $1.type_dimension;
-		actual_type[struct_level].type_index = $1.type_index;
-		actual_type[struct_level].type_sizeof = $1.type_sizeof;
+		actual_type[struct_level].type_str = mm_strdup($1.type_str);
+		actual_type[struct_level].type_dimension = mm_strdup($1.type_dimension);
+		actual_type[struct_level].type_index = mm_strdup($1.type_index);
+		actual_type[struct_level].type_sizeof =
+			$1.type_sizeof ? mm_strdup($1.type_sizeof) : NULL;
 
 		actual_startline[struct_level] = hashline_number();
 	}
 	variable_list ';'
 	{
-		@$ = cat_str(4, actual_startline[struct_level], $1.type_str, @3, mm_strdup(";\n"));
+		@$ = cat_str(4, actual_startline[struct_level], $1.type_str, @3, ";\n");
 	}
 	| struct_union_type_with_symbol ';'
-	{
-		@$ = cat2_str(@1, mm_strdup(";"));
-	}
 	;
 
 opt_bit_field: ':' Iconst
@@ -604,16 +611,16 @@ storage_modifier: S_CONST
 var_type: simple_type
 	{
 		$$.type_enum = $1;
-		$$.type_str = mm_strdup(ecpg_type_name($1));
-		$$.type_dimension = mm_strdup("-1");
-		$$.type_index = mm_strdup("-1");
+		$$.type_str = loc_strdup(ecpg_type_name($1));
+		$$.type_dimension = "-1";
+		$$.type_index = "-1";
 		$$.type_sizeof = NULL;
 	}
 	| struct_union_type
 	{
-		$$.type_str = @1;
-		$$.type_dimension = mm_strdup("-1");
-		$$.type_index = mm_strdup("-1");
+		$$.type_str = loc_strdup(@1);
+		$$.type_dimension = "-1";
+		$$.type_index = "-1";
 
 		if (strncmp(@1, "struct", sizeof("struct") - 1) == 0)
 		{
@@ -628,26 +635,26 @@ var_type: simple_type
 	}
 	| enum_type
 	{
-		$$.type_str = @1;
+		$$.type_str = loc_strdup(@1);
 		$$.type_enum = ECPGt_int;
-		$$.type_dimension = mm_strdup("-1");
-		$$.type_index = mm_strdup("-1");
+		$$.type_dimension = "-1";
+		$$.type_index = "-1";
 		$$.type_sizeof = NULL;
 	}
 	| NUMERIC '(' precision opt_scale ')'
 	{
 		$$.type_enum = ECPGt_numeric;
-		$$.type_str = mm_strdup("numeric");
-		$$.type_dimension = mm_strdup("-1");
-		$$.type_index = mm_strdup("-1");
+		$$.type_str = "numeric";
+		$$.type_dimension = "-1";
+		$$.type_index = "-1";
 		$$.type_sizeof = NULL;
 	}
 	| DECIMAL_P '(' precision opt_scale ')'
 	{
 		$$.type_enum = ECPGt_decimal;
-		$$.type_str = mm_strdup("decimal");
-		$$.type_dimension = mm_strdup("-1");
-		$$.type_index = mm_strdup("-1");
+		$$.type_str = "decimal";
+		$$.type_dimension = "-1";
+		$$.type_index = "-1";
 		$$.type_sizeof = NULL;
 	}
 	| IDENT '(' precision opt_scale ')'
@@ -660,63 +667,63 @@ var_type: simple_type
 		if (strcmp(@1, "numeric") == 0)
 		{
 			$$.type_enum = ECPGt_numeric;
-			$$.type_str = mm_strdup("numeric");
+			$$.type_str = "numeric";
 		}
 		else if (strcmp(@1, "decimal") == 0)
 		{
 			$$.type_enum = ECPGt_decimal;
-			$$.type_str = mm_strdup("decimal");
+			$$.type_str = "decimal";
 		}
 		else
 		{
 			mmerror(PARSE_ERROR, ET_ERROR, "only data types numeric and decimal have precision/scale argument");
 			$$.type_enum = ECPGt_numeric;
-			$$.type_str = mm_strdup("numeric");
+			$$.type_str = "numeric";
 		}
 
-		$$.type_dimension = mm_strdup("-1");
-		$$.type_index = mm_strdup("-1");
+		$$.type_dimension = "-1";
+		$$.type_index = "-1";
 		$$.type_sizeof = NULL;
 	}
 	| VARCHAR
 	{
 		$$.type_enum = ECPGt_varchar;
-		$$.type_str = EMPTY;	/* mm_strdup("varchar"); */
-		$$.type_dimension = mm_strdup("-1");
-		$$.type_index = mm_strdup("-1");
+		$$.type_str = "";	/* "varchar"; */
+		$$.type_dimension = "-1";
+		$$.type_index = "-1";
 		$$.type_sizeof = NULL;
 	}
 	| FLOAT_P
 	{
 		/* Note: DOUBLE is handled in simple_type */
 		$$.type_enum = ECPGt_float;
-		$$.type_str = mm_strdup("float");
-		$$.type_dimension = mm_strdup("-1");
-		$$.type_index = mm_strdup("-1");
+		$$.type_str = "float";
+		$$.type_dimension = "-1";
+		$$.type_index = "-1";
 		$$.type_sizeof = NULL;
 	}
 	| NUMERIC
 	{
 		$$.type_enum = ECPGt_numeric;
-		$$.type_str = mm_strdup("numeric");
-		$$.type_dimension = mm_strdup("-1");
-		$$.type_index = mm_strdup("-1");
+		$$.type_str = "numeric";
+		$$.type_dimension = "-1";
+		$$.type_index = "-1";
 		$$.type_sizeof = NULL;
 	}
 	| DECIMAL_P
 	{
 		$$.type_enum = ECPGt_decimal;
-		$$.type_str = mm_strdup("decimal");
-		$$.type_dimension = mm_strdup("-1");
-		$$.type_index = mm_strdup("-1");
+		$$.type_str = "decimal";
+		$$.type_dimension = "-1";
+		$$.type_index = "-1";
 		$$.type_sizeof = NULL;
 	}
 	| TIMESTAMP
 	{
 		$$.type_enum = ECPGt_timestamp;
-		$$.type_str = mm_strdup("timestamp");
-		$$.type_dimension = mm_strdup("-1");
-		$$.type_index = mm_strdup("-1");
+		$$.type_str = "timestamp";
+		$$.type_dimension = "-1";
+		$$.type_index = "-1";
 		$$.type_sizeof = NULL;
 	}
 	| STRING_P
@@ -725,9 +732,9 @@ var_type: simple_type
 		{
 			/* In Informix mode, "string" is automatically a typedef */
 			$$.type_enum = ECPGt_string;
-			$$.type_str = mm_strdup("char");
-			$$.type_dimension = mm_strdup("-1");
-			$$.type_index = mm_strdup("-1");
+			$$.type_str = "char";
+			$$.type_dimension = "-1";
+			$$.type_index = "-1";
 			$$.type_sizeof = NULL;
 		}
 		else
@@ -735,14 +742,14 @@ var_type: simple_type
 			/* Otherwise, legal only if user typedef'ed it */
 			struct typedefs *this = get_typedef("string", false);
 
-			$$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? EMPTY : mm_strdup(this->name);
+			$$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? mm_strdup("") : mm_strdup(this->name);
 			$$.type_enum = this->type->type_enum;
 			$$.type_dimension = this->type->type_dimension;
 			$$.type_index = this->type->type_index;
 			if (this->type->type_sizeof && strlen(this->type->type_sizeof) != 0)
 				$$.type_sizeof = this->type->type_sizeof;
 			else
-				$$.type_sizeof = cat_str(3, mm_strdup("sizeof("), mm_strdup(this->name), mm_strdup(")"));
+				$$.type_sizeof = cat_str(3, "sizeof(", this->name, ")");
 
 			struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
 		}
@@ -750,9 +757,9 @@ var_type: simple_type
 	| INTERVAL ecpg_interval
 	{
 		$$.type_enum = ECPGt_interval;
-		$$.type_str = mm_strdup("interval");
-		$$.type_dimension = mm_strdup("-1");
-		$$.type_index = mm_strdup("-1");
+		$$.type_str = "interval";
+		$$.type_dimension = "-1";
+		$$.type_index = "-1";
 		$$.type_sizeof = NULL;
 	}
 	| IDENT ecpg_interval
@@ -772,89 +779,89 @@ var_type: simple_type
 		if (strcmp(@1, "varchar") == 0)
 		{
 			$$.type_enum = ECPGt_varchar;
-			$$.type_str = EMPTY;	/* mm_strdup("varchar"); */
-			$$.type_dimension = mm_strdup("-1");
-			$$.type_index = mm_strdup("-1");
+			$$.type_str = "";	/* "varchar"; */
+			$$.type_dimension = "-1";
+			$$.type_index = "-1";
 			$$.type_sizeof = NULL;
 		}
 		else if (strcmp(@1, "bytea") == 0)
 		{
 			$$.type_enum = ECPGt_bytea;
-			$$.type_str = EMPTY;
-			$$.type_dimension = mm_strdup("-1");
-			$$.type_index = mm_strdup("-1");
+			$$.type_str = "";
+			$$.type_dimension = "-1";
+			$$.type_index = "-1";
 			$$.type_sizeof = NULL;
 		}
 		else if (strcmp(@1, "float") == 0)
 		{
 			$$.type_enum = ECPGt_float;
-			$$.type_str = mm_strdup("float");
-			$$.type_dimension = mm_strdup("-1");
-			$$.type_index = mm_strdup("-1");
+			$$.type_str = "float";
+			$$.type_dimension = "-1";
+			$$.type_index = "-1";
 			$$.type_sizeof = NULL;
 		}
 		else if (strcmp(@1, "double") == 0)
 		{
 			$$.type_enum = ECPGt_double;
-			$$.type_str = mm_strdup("double");
-			$$.type_dimension = mm_strdup("-1");
-			$$.type_index = mm_strdup("-1");
+			$$.type_str = "double";
+			$$.type_dimension = "-1";
+			$$.type_index = "-1";
 			$$.type_sizeof = NULL;
 		}
 		else if (strcmp(@1, "numeric") == 0)
 		{
 			$$.type_enum = ECPGt_numeric;
-			$$.type_str = mm_strdup("numeric");
-			$$.type_dimension = mm_strdup("-1");
-			$$.type_index = mm_strdup("-1");
+			$$.type_str = "numeric";
+			$$.type_dimension = "-1";
+			$$.type_index = "-1";
 			$$.type_sizeof = NULL;
 		}
 		else if (strcmp(@1, "decimal") == 0)
 		{
 			$$.type_enum = ECPGt_decimal;
-			$$.type_str = mm_strdup("decimal");
-			$$.type_dimension = mm_strdup("-1");
-			$$.type_index = mm_strdup("-1");
+			$$.type_str = "decimal";
+			$$.type_dimension = "-1";
+			$$.type_index = "-1";
 			$$.type_sizeof = NULL;
 		}
 		else if (strcmp(@1, "date") == 0)
 		{
 			$$.type_enum = ECPGt_date;
-			$$.type_str = mm_strdup("date");
-			$$.type_dimension = mm_strdup("-1");
-			$$.type_index = mm_strdup("-1");
+			$$.type_str = "date";
+			$$.type_dimension = "-1";
+			$$.type_index = "-1";
 			$$.type_sizeof = NULL;
 		}
 		else if (strcmp(@1, "timestamp") == 0)
 		{
 			$$.type_enum = ECPGt_timestamp;
-			$$.type_str = mm_strdup("timestamp");
-			$$.type_dimension = mm_strdup("-1");
-			$$.type_index = mm_strdup("-1");
+			$$.type_str = "timestamp";
+			$$.type_dimension = "-1";
+			$$.type_index = "-1";
 			$$.type_sizeof = NULL;
 		}
 		else if (strcmp(@1, "interval") == 0)
 		{
 			$$.type_enum = ECPGt_interval;
-			$$.type_str = mm_strdup("interval");
-			$$.type_dimension = mm_strdup("-1");
-			$$.type_index = mm_strdup("-1");
+			$$.type_str = "interval";
+			$$.type_dimension = "-1";
+			$$.type_index = "-1";
 			$$.type_sizeof = NULL;
 		}
 		else if (strcmp(@1, "datetime") == 0)
 		{
 			$$.type_enum = ECPGt_timestamp;
-			$$.type_str = mm_strdup("timestamp");
-			$$.type_dimension = mm_strdup("-1");
-			$$.type_index = mm_strdup("-1");
+			$$.type_str = "timestamp";
+			$$.type_dimension = "-1";
+			$$.type_index = "-1";
 			$$.type_sizeof = NULL;
 		}
 		else if ((strcmp(@1, "string") == 0) && INFORMIX_MODE)
 		{
 			$$.type_enum = ECPGt_string;
-			$$.type_str = mm_strdup("char");
-			$$.type_dimension = mm_strdup("-1");
-			$$.type_index = mm_strdup("-1");
+			$$.type_str = "char";
+			$$.type_dimension = "-1";
+			$$.type_index = "-1";
 			$$.type_sizeof = NULL;
 		}
 		else
@@ -862,14 +869,14 @@ var_type: simple_type
 			/* Otherwise, it must be a user-defined typedef name */
 			struct typedefs *this = get_typedef(@1, false);
 
-			$$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? EMPTY : mm_strdup(this->name);
+			$$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? mm_strdup("") : mm_strdup(this->name);
 			$$.type_enum = this->type->type_enum;
 			$$.type_dimension = this->type->type_dimension;
 			$$.type_index = this->type->type_index;
 			if (this->type->type_sizeof && strlen(this->type->type_sizeof) != 0)
 				$$.type_sizeof = this->type->type_sizeof;
 			else
-				$$.type_sizeof = cat_str(3, mm_strdup("sizeof("), mm_strdup(this->name), mm_strdup(")"));
+				$$.type_sizeof = cat_str(3, "sizeof(", this->name, ")");
 
 			struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
 		}
@@ -888,21 +895,20 @@ var_type: simple_type
 			/* No */
 
 			this = get_typedef(name, false);
-			$$.type_str = mm_strdup(this->name);
+			$$.type_str = this->name;
 			$$.type_enum = this->type->type_enum;
 			$$.type_dimension = this->type->type_dimension;
 			$$.type_index = this->type->type_index;
 			$$.type_sizeof = this->type->type_sizeof;
 			struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
-			free(name);
 		}
 		else
 		{
 			$$.type_str = name;
 			$$.type_enum = ECPGt_long;
-			$$.type_dimension = mm_strdup("-1");
-			$$.type_index = mm_strdup("-1");
-			$$.type_sizeof = mm_strdup("");
+			$$.type_dimension = "-1";
+			$$.type_index = "-1";
+			$$.type_sizeof = "";
 			struct_member_list[struct_level] = NULL;
 		}
 	}
@@ -932,7 +938,7 @@ struct_union_type_with_symbol: s_struct_union_symbol
 		ECPGfree_struct_member(struct_member_list[struct_level]);
 		struct_member_list[struct_level] = NULL;
 		struct_level--;
-		if (strncmp($1.su, "struct", sizeof("struct") - 1) == 0)
+		if (strcmp($1.su, "struct") == 0)
 			su_type.type_enum = ECPGt_struct;
 		else
 			su_type.type_enum = ECPGt_union;
@@ -967,7 +973,7 @@ struct_union_type_with_symbol: s_struct_union_symbol
 		this->struct_member_list = struct_member_list[struct_level];
 
 		types = this;
-		@$ = cat_str(4, su_type.type_str, mm_strdup("{"), @4, mm_strdup("}"));
+		@$ = cat_str(4, su_type.type_str, "{", @4, "}");
 	}
 	;
 
@@ -983,19 +989,21 @@ struct_union_type: struct_union_type_with_symbol
 		ECPGfree_struct_member(struct_member_list[struct_level]);
 		struct_member_list[struct_level] = NULL;
 		struct_level--;
-		@$ = cat_str(4, @1, mm_strdup("{"), @4, mm_strdup("}"));
+		@$ = cat_str(4, @1, "{", @4, "}");
 	}
 	;
 
 s_struct_union_symbol: SQL_STRUCT symbol
 	{
-		$$.su = mm_strdup("struct");
+		$$.su = "struct";
 		$$.symbol = @2;
-		ECPGstruct_sizeof = cat_str(3, mm_strdup("sizeof("), cat2_str(mm_strdup($$.su), mm_strdup($$.symbol)), mm_strdup(")"));
+		ECPGstruct_sizeof = mm_strdup(cat_str(3, "sizeof(",
+											  cat2_str($$.su, $$.symbol),
+											  ")"));
 	}
 	| UNION symbol
 	{
-		$$.su = mm_strdup("union");
+		$$.su = "union";
 		$$.symbol = @2;
 	}
 	;
@@ -1004,11 +1012,11 @@ s_struct_union: SQL_STRUCT
 	{
 		ECPGstruct_sizeof = mm_strdup("");	/* This must not be NULL to
 											 * distinguish from simple types. */
-		@$ = mm_strdup("struct");
+		@$ = "struct";
 	}
 	| UNION
 	{
-		@$ = mm_strdup("union");
+		@$ = "union";
 	}
 	;
 
@@ -1047,23 +1055,27 @@ variable_list: variable
 	| variable_list ',' variable
 	{
 		if (actual_type[struct_level].type_enum == ECPGt_varchar || actual_type[struct_level].type_enum == ECPGt_bytea)
-			@$ = cat_str(4, @1, mm_strdup(";"), mm_strdup(actual_type[struct_level].type_storage), @3);
+			@$ = cat_str(4, @1, ";", actual_type[struct_level].type_storage, @3);
 		else
-			@$ = cat_str(3, @1, mm_strdup(","), @3);
+			@$ = cat_str(3, @1, ",", @3);
 	}
 	;
 
 variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initializer
 	{
 		struct ECPGtype *type;
-		char	   *dimension = $3.index1;	/* dimension of array */
-		char	   *length = $3.index2; /* length of string */
+		const char *dimension = $3.index1;	/* dimension of array */
+		const char *length = $3.index2; /* length of string */
 		char	   *dim_str;
 		char	   *vcn;
 		int		   *varlen_type_counter;
 		char	   *struct_name;
 
-		adjust_array(actual_type[struct_level].type_enum, &dimension, &length, actual_type[struct_level].type_dimension, actual_type[struct_level].type_index, strlen(@1), false);
+		adjust_array(actual_type[struct_level].type_enum,
+					 &dimension, &length,
+					 actual_type[struct_level].type_dimension,
+					 actual_type[struct_level].type_index,
+					 strlen(@1), false);
 		switch (actual_type[struct_level].type_enum)
 		{
 			case ECPGt_struct:
@@ -1073,7 +1085,7 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
 				else
 					type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level], actual_type[struct_level].type_enum, actual_type[struct_level].type_str, actual_type[struct_level].type_sizeof), dimension);
 
-				@$ = cat_str(5, @1, mm_strdup(@2), $3.str, @4, @5);
+				@$ = cat_str(5, @1, @2, $3.str, @4, @5);
 				break;
 
 			case ECPGt_varchar:
@@ -1094,9 +1106,9 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
 					type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length, *varlen_type_counter), dimension);
 
 				if (strcmp(dimension, "0") == 0 || abs(atoi(dimension)) == 1)
-					dim_str = mm_strdup("");
+					dim_str = "";
 				else
-					dim_str = cat_str(3, mm_strdup("["), mm_strdup(dimension), mm_strdup("]"));
+					dim_str = cat_str(3, "[", dimension, "]");
 
 				/*
 				 * cannot check for atoi <= 0 because a defined constant will
@@ -1109,12 +1121,12 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
 				 * make sure varchar struct name is unique by adding a unique
 				 * counter to its definition
 				 */
-				vcn = (char *) mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
-				sprintf(vcn, "%d", *varlen_type_counter);
+				vcn = (char *) loc_alloc(32);
+				snprintf(vcn, 32, "%d", *varlen_type_counter);
 				if (strcmp(dimension, "0") == 0)
-					@$ = cat_str(7, make2_str(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } *"), mm_strdup(@2), @4, @5);
+					@$ = cat_str(7, make2_str(struct_name, vcn), " { int len; char arr[", length, "]; } *", @2, @4, @5);
 				else
-					@$ = cat_str(8, make2_str(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["), mm_strdup(length), mm_strdup("]; } "), mm_strdup(@2), dim_str, @4, @5);
+					@$ = cat_str(8, make2_str(struct_name, vcn), " { int len; char arr[", length, "]; } ", @2, dim_str, @4, @5);
 				(*varlen_type_counter)++;
 				break;
 
@@ -1132,25 +1144,26 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
 						 * if we have an initializer but no string size set,
 						 * let's use the initializer's length
 						 */
-						free(length);
-						length = mm_alloc(i + sizeof("sizeof()"));
-						sprintf(length, "sizeof(%s)", @5 + 2);
+						char   *buf = loc_alloc(32);
+
+						snprintf(buf, 32, "sizeof(%s)", @5 + 2);
+						length = buf;
 					}
 					type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length, 0);
 				}
 				else
 					type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length, 0), dimension);
 
-				@$ = cat_str(5, @1, mm_strdup(@2), $3.str, @4, @5);
+				@$ = cat_str(5, @1, @2, $3.str, @4, @5);
 				break;
 
 			default:
 				if (atoi(dimension) < 0)
-					type = ECPGmake_simple_type(actual_type[struct_level].type_enum, mm_strdup("1"), 0);
+					type = ECPGmake_simple_type(actual_type[struct_level].type_enum, "1", 0);
 				else
-					type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, mm_strdup("1"), 0), dimension);
+					type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, "1", 0), dimension);
 
-				@$ = cat_str(5, @1, mm_strdup(@2), $3.str, @4, @5);
+				@$ = cat_str(5, @1, @2, $3.str, @4, @5);
 				break;
 		}
 
@@ -1172,7 +1185,7 @@ opt_pointer: /* EMPTY */
 	| '*'
 	| '*' '*'
 	{
-		@$ = mm_strdup("**");
+		@$ = "**";
 	}
 	;
 
@@ -1182,7 +1195,7 @@ opt_pointer: /* EMPTY */
 ECPGDeclare: DECLARE STATEMENT ecpg_ident
 	{
 		/* this is only supported for compatibility */
-		@$ = cat_str(3, mm_strdup("/* declare statement"), @3, mm_strdup("*/"));
+		@$ = cat_str(3, "/* declare statement", @3, "*/");
 	}
 	;
 /*
@@ -1197,25 +1210,25 @@ ECPGDisconnect: SQL_DISCONNECT dis_name
 dis_name: connection_object
 	| CURRENT_P
 	{
-		@$ = mm_strdup("\"CURRENT\"");
+		@$ = "\"CURRENT\"";
 	}
 	| ALL
 	{
-		@$ = mm_strdup("\"ALL\"");
+		@$ = "\"ALL\"";
 	}
 	| /* EMPTY */
 	{
-		@$ = mm_strdup("\"CURRENT\"");
+		@$ = "\"CURRENT\"";
 	}
 	;
 
 connection_object: name
 	{
-		@$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
+		@$ = make3_str("\"", @1, "\"");
 	}
 	| DEFAULT
 	{
-		@$ = mm_strdup("\"DEFAULT\"");
+		@$ = "\"DEFAULT\"";
 	}
 	| char_variable
 	;
@@ -1223,7 +1236,7 @@ connection_object: name
 execstring: char_variable
 	| CSTRING
 	{
-		@$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
+		@$ = make3_str("\"", @1, "\"");
 	}
 	;
 
@@ -1237,7 +1250,7 @@ ECPGFree: SQL_FREE cursor_name
 	}
 	| SQL_FREE ALL
 	{
-		@$ = mm_strdup("all");
+		@$ = "all";
 	}
 	;
 
@@ -1258,7 +1271,7 @@ opt_ecpg_using: /* EMPTY */
 
 ecpg_using: USING using_list
 	{
-		@$ = EMPTY;
+		@$ = "";
 	}
 	| using_descriptor
 	;
@@ -1266,31 +1279,31 @@ ecpg_using: USING using_list
 using_descriptor: USING SQL_P SQL_DESCRIPTOR quoted_ident_stringvar
 	{
 		add_variable_to_head(&argsinsert, descriptor_variable(@4, 0), &no_indicator);
-		@$ = EMPTY;
+		@$ = "";
 	}
 	| USING SQL_DESCRIPTOR name
 	{
 		add_variable_to_head(&argsinsert, sqlda_variable(@3), &no_indicator);
-		@$ = EMPTY;
+		@$ = "";
 	}
 	;
 
 into_descriptor: INTO SQL_P SQL_DESCRIPTOR quoted_ident_stringvar
 	{
 		add_variable_to_head(&argsresult, descriptor_variable(@4, 1), &no_indicator);
-		@$ = EMPTY;
+		@$ = "";
 	}
 	| INTO SQL_DESCRIPTOR name
 	{
 		add_variable_to_head(&argsresult, sqlda_variable(@3), &no_indicator);
-		@$ = EMPTY;
+		@$ = "";
 	}
 	;
 
 into_sqlda: INTO name
 	{
 		add_variable_to_head(&argsresult, sqlda_variable(@2), &no_indicator);
-		@$ = EMPTY;
+		@$ = "";
 	}
 	;
 
@@ -1299,18 +1312,18 @@ using_list: UsingValue | UsingValue ',' using_list
 
 UsingValue: UsingConst
 	{
-		char	   *length = mm_alloc(32);
+		char	   *length = loc_alloc(32);
 
-		sprintf(length, "%zu", strlen(@1));
+		snprintf(length, 32, "%zu", strlen(@1));
 		add_variable_to_head(&argsinsert, new_variable(@1, ECPGmake_simple_type(ECPGt_const, length, 0), 0), &no_indicator);
 	}
 	| civar
 	{
-		@$ = EMPTY;
+		@$ = "";
 	}
 	| civarind
 	{
-		@$ = EMPTY;
+		@$ = "";
 	}
 	;
 
@@ -1430,9 +1443,9 @@ ECPGSetDescHeaderItem: desc_header_item '=' IntConstVar
 
 IntConstVar: Iconst
 	{
-		char	   *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
+		char	   *length = loc_alloc(32);
 
-		sprintf(length, "%zu", strlen(@1));
+		snprintf(length, 32, "%zu", strlen(@1));
 		new_variable(@1, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
 	}
 	| cvariable
@@ -1484,37 +1497,39 @@ ECPGSetDescItem: descriptor_item '=' AllConstVar
 
 AllConstVar: ecpg_fconst
 	{
-		char	   *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
+		char	   *length = loc_alloc(32);
 
-		sprintf(length, "%zu", strlen(@1));
+		snprintf(length, 32, "%zu", strlen(@1));
 		new_variable(@1, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
 	}
 	| IntConstVar
 	| '-' ecpg_fconst
 	{
-		char	   *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
-		char	   *var = cat2_str(mm_strdup("-"), @2);
+		char	   *length = loc_alloc(32);
+		char	   *var = cat2_str("-", @2);
 
-		sprintf(length, "%zu", strlen(var));
+		snprintf(length, 32, "%zu", strlen(var));
 		new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
 		@$ = var;
 	}
 	| '-' Iconst
 	{
-		char	   *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
-		char	   *var = cat2_str(mm_strdup("-"), @2);
+		char	   *length = loc_alloc(32);
+		char	   *var = cat2_str("-", @2);
 
-		sprintf(length, "%zu", strlen(var));
+		snprintf(length, 32, "%zu", strlen(var));
 		new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
 		@$ = var;
 	}
 	| ecpg_sconst
 	{
-		char	   *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
-		char	   *var = @1 + 1;
+		char	   *length = loc_alloc(32);
+		char	   *var;
 
+		/* Strip single quotes from ecpg_sconst */
+		var = loc_strdup(@1 + 1);
 		var[strlen(var) - 1] = '\0';
-		sprintf(length, "%zu", strlen(var));
+		snprintf(length, 32, "%zu", strlen(var));
 		new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
 		@$ = var;
 	}
@@ -1587,9 +1602,9 @@ ECPGTypedef: TYPE_P
 		add_typedef(@3, $6.index1, $6.index2, $5.type_enum, $5.type_dimension, $5.type_index, initializer, *@7 ? 1 : 0);
 
 		if (auto_create_c == false)
-			@$ = cat_str(7, mm_strdup("/* exec sql type"), mm_strdup(@3), mm_strdup("is"), mm_strdup($5.type_str), mm_strdup($6.str), @7, mm_strdup("*/"));
+			@$ = cat_str(7, "/* exec sql type", @3, "is", $5.type_str, $6.str, @7, "*/");
 		else
-			@$ = cat_str(6, mm_strdup("typedef "), mm_strdup($5.type_str), *@7 ? mm_strdup("*") : mm_strdup(""), mm_strdup(@3), mm_strdup($6.str), mm_strdup(";"));
+			@$ = cat_str(6, "typedef ", $5.type_str, *@7 ? "*" : "", @3, $6.str, ";");
 	}
 	;
 
@@ -1609,8 +1624,8 @@ ECPGVar: SQL_VAR
 	ColLabel	IS var_type opt_array_bounds opt_reference
 	{
 		struct variable *p = find_variable(@3);
-		char	   *dimension = $6.index1;
-		char	   *length = $6.index2;
+		const char *dimension = $6.index1;
+		const char *length = $6.index2;
 		struct ECPGtype *type;
 
 		if			(($5.type_enum == ECPGt_struct ||
@@ -1619,7 +1634,8 @@ ECPGVar: SQL_VAR
 			mmerror(PARSE_ERROR, ET_ERROR, "initializer not allowed in EXEC SQL VAR command");
 		else
 		{
-			adjust_array($5.type_enum, &dimension, &length, $5.type_dimension, $5.type_index, *@7 ? 1 : 0, false);
+			adjust_array($5.type_enum, &dimension, &length,
+						 $5.type_dimension, $5.type_index, *@7 ? 1 : 0, false);
 
 			switch ($5.type_enum)
 			{
@@ -1653,9 +1669,9 @@ ECPGVar: SQL_VAR
 						mmerror(PARSE_ERROR, ET_ERROR, "multidimensional arrays for simple data types are not supported");
 
 					if (atoi(dimension) < 0)
-						type = ECPGmake_simple_type($5.type_enum, mm_strdup("1"), 0);
+						type = ECPGmake_simple_type($5.type_enum, "1", 0);
 					else
-						type = ECPGmake_array_type(ECPGmake_simple_type($5.type_enum, mm_strdup("1"), 0), dimension);
+						type = ECPGmake_array_type(ECPGmake_simple_type($5.type_enum, "1", 0), dimension);
 					break;
 			}
 
@@ -1663,7 +1679,7 @@ ECPGVar: SQL_VAR
 			p->type = type;
 		}
 
-					@$ = cat_str(7, mm_strdup("/* exec sql var"), mm_strdup(@3), mm_strdup("is"), mm_strdup($5.type_str), mm_strdup($6.str), @7, mm_strdup("*/"));
+					@$ = cat_str(7, "/* exec sql var", @3, "is", $5.type_str, $6.str, @7, "*/");
 	}
 	;
 
@@ -1673,83 +1689,83 @@ ECPGVar: SQL_VAR
  */
 ECPGWhenever: SQL_WHENEVER SQL_SQLERROR action
 	{
-		when_error.code = $<action>3.code;
-		when_error.command = $<action>3.command;
-		@$ = cat_str(3, mm_strdup("/* exec sql whenever sqlerror "), $3.str, mm_strdup("; */"));
+		when_error.code = $3.code;
+		when_error.command = $3.command ? mm_strdup($3.command) : NULL;
+		@$ = cat_str(3, "/* exec sql whenever sqlerror ", $3.str, "; */");
 	}
 	| SQL_WHENEVER NOT SQL_FOUND action
 	{
-		when_nf.code = $<action>4.code;
-		when_nf.command = $<action>4.command;
-		@$ = cat_str(3, mm_strdup("/* exec sql whenever not found "), $4.str, mm_strdup("; */"));
+		when_nf.code = $4.code;
+		when_nf.command = $4.command ? mm_strdup($4.command) : NULL;
+		@$ = cat_str(3, "/* exec sql whenever not found ", $4.str, "; */");
 	}
 	| SQL_WHENEVER SQL_SQLWARNING action
 	{
-		when_warn.code = $<action>3.code;
-		when_warn.command = $<action>3.command;
-		@$ = cat_str(3, mm_strdup("/* exec sql whenever sql_warning "), $3.str, mm_strdup("; */"));
+		when_warn.code = $3.code;
+		when_warn.command = $3.command ? mm_strdup($3.command) : NULL;
+		@$ = cat_str(3, "/* exec sql whenever sql_warning ", $3.str, "; */");
 	}
 	;
 
 action: CONTINUE_P
 	{
-		$<action>$.code = W_NOTHING;
-		$<action>$.command = NULL;
-		$<action>$.str = mm_strdup("continue");
+		$$.code = W_NOTHING;
+		$$.command = NULL;
+		$$.str = "continue";
 	}
 	| SQL_SQLPRINT
 	{
-		$<action>$.code = W_SQLPRINT;
-		$<action>$.command = NULL;
-		$<action>$.str = mm_strdup("sqlprint");
+		$$.code = W_SQLPRINT;
+		$$.command = NULL;
+		$$.str = "sqlprint";
 	}
 	| SQL_STOP
 	{
-		$<action>$.code = W_STOP;
-		$<action>$.command = NULL;
-		$<action>$.str = mm_strdup("stop");
+		$$.code = W_STOP;
+		$$.command = NULL;
+		$$.str = "stop";
 	}
 	| SQL_GOTO name
 	{
-		$<action>$.code = W_GOTO;
-		$<action>$.command = mm_strdup(@2);
-		$<action>$.str = cat2_str(mm_strdup("goto "), @2);
+		$$.code = W_GOTO;
+		$$.command = loc_strdup(@2);
+		$$.str = cat2_str("goto ", @2);
 	}
 	| SQL_GO TO name
 	{
-		$<action>$.code = W_GOTO;
-		$<action>$.command = mm_strdup(@3);
-		$<action>$.str = cat2_str(mm_strdup("goto "), @3);
+		$$.code = W_GOTO;
+		$$.command = loc_strdup(@3);
+		$$.str = cat2_str("goto ", @3);
 	}
 	| DO name '(' c_args ')'
 	{
-		$<action>$.code = W_DO;
-		$<action>$.command = cat_str(4, @2, mm_strdup("("), @4, mm_strdup(")"));
-		$<action>$.str = cat2_str(mm_strdup("do"), mm_strdup($<action>$.command));
+		$$.code = W_DO;
+		$$.command = cat_str(4, @2, "(", @4, ")");
+		$$.str = cat2_str("do", $$.command);
 	}
 	| DO SQL_BREAK
 	{
-		$<action>$.code = W_BREAK;
-		$<action>$.command = NULL;
-		$<action>$.str = mm_strdup("break");
+		$$.code = W_BREAK;
+		$$.command = NULL;
+		$$.str = "break";
 	}
 	| DO CONTINUE_P
 	{
-		$<action>$.code = W_CONTINUE;
-		$<action>$.command = NULL;
-		$<action>$.str = mm_strdup("continue");
+		$$.code = W_CONTINUE;
+		$$.command = NULL;
+		$$.str = "continue";
 	}
 	| CALL name '(' c_args ')'
 	{
-		$<action>$.code = W_DO;
-		$<action>$.command = cat_str(4, @2, mm_strdup("("), @4, mm_strdup(")"));
-		$<action>$.str = cat2_str(mm_strdup("call"), mm_strdup($<action>$.command));
+		$$.code = W_DO;
+		$$.command = cat_str(4, @2, "(", @4, ")");
+		$$.str = cat2_str("call", $$.command);
 	}
 	| CALL name
 	{
-		$<action>$.code = W_DO;
-		$<action>$.command = cat2_str(@2, mm_strdup("()"));
-		$<action>$.str = cat2_str(mm_strdup("call"), mm_strdup($<action>$.command));
+		$$.code = W_DO;
+		$$.command = cat2_str(@2, "()");
+		$$.str = cat2_str("call", $$.command);
 	}
 	;
 
@@ -1913,7 +1929,7 @@ ecpgstart: SQL_START
 	{
 		reset_variables();
 		pacounter = 1;
-		@$ = EMPTY;
+		@$ = "";
 	}
 	;
 
@@ -1982,7 +1998,7 @@ cvariable: CVARIABLE
 		 * As long as multidimensional arrays are not implemented we have to
 		 * check for those here
 		 */
-		char	   *ptr = @1;
+		const char *ptr = @1;
 		int			brace_open = 0,
 					brace = false;
 
@@ -2013,18 +2029,12 @@ cvariable: CVARIABLE
 	;
 
 ecpg_param: PARAM
-	{
-		@$ = make_name();
-	}
 	;
 
 ecpg_bconst: BCONST
 	;
 
 ecpg_fconst: FCONST
-	{
-		@$ = make_name();
-	}
 	;
 
 ecpg_sconst: SCONST
@@ -2036,17 +2046,17 @@ ecpg_xconst: XCONST
 ecpg_ident: IDENT
 	| CSTRING
 	{
-		@$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
+		@$ = make3_str("\"", @1, "\"");
 	}
 	;
 
 quoted_ident_stringvar: name
 	{
-		@$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
+		@$ = make3_str("\"", @1, "\"");
 	}
 	| char_variable
 	{
-		@$ = make3_str(mm_strdup("("), @1, mm_strdup(")"));
+		@$ = make3_str("(", @1, ")");
 	}
 	;
 
@@ -2057,7 +2067,7 @@ quoted_ident_stringvar: name
 c_stuff_item: c_anything
 	| '(' ')'
 	{
-		@$ = mm_strdup("()");
+		@$ = "()";
 	}
 	| '(' c_stuff ')'
 	;
@@ -2094,7 +2104,7 @@ c_anything: ecpg_ident
 	| '-'
 	| '/'
 	| '%'
-	| NULL_P						{ @$ = mm_strdup("NULL"); }
+	| NULL_P						{ @$ = "NULL"; }
 	| S_ADD
 	| S_AND
 	| S_ANYTHING
@@ -2155,11 +2165,11 @@ DeallocateStmt: DEALLOCATE prepared_name
 	}
 	| DEALLOCATE ALL
 	{
-		@$ = mm_strdup("all");
+		@$ = "all";
 	}
 	| DEALLOCATE PREPARE ALL
 	{
-		@$ = mm_strdup("all");
+		@$ = "all";
 	}
 	;
 
@@ -2177,7 +2187,7 @@ Iresult: Iconst
 		if (pg_strcasecmp(@1, "sizeof") != 0)
 			mmerror(PARSE_ERROR, ET_ERROR, "operator not allowed in variable definition");
 		else
-			@$ = cat_str(4, @1, mm_strdup("("), $3.type_str, mm_strdup(")"));
+			@$ = cat_str(4, @1, "(", $3.type_str, ")");
 	}
 	;
 
@@ -2190,7 +2200,7 @@ execute_rest: /* EMPTY */
 ecpg_into: INTO into_list
 	{
 		/* always suppress this from the constructed string */
-		@$ = EMPTY;
+		@$ = "";
 	}
 	| into_descriptor
 	;
diff --git a/src/interfaces/ecpg/preproc/output.c b/src/interfaces/ecpg/preproc/output.c
index 8d2b6e7cb8..a18904f88b 100644
--- a/src/interfaces/ecpg/preproc/output.c
+++ b/src/interfaces/ecpg/preproc/output.c
@@ -12,7 +12,6 @@ output_line_number(void)
 	char	   *line = hashline_number();
 
 	fprintf(base_yyout, "%s", line);
-	free(line);
 }
 
 void
@@ -100,7 +99,7 @@ hashline_number(void)
 		)
 	{
 		/* "* 2" here is for escaping '\' and '"' below */
-		char	   *line = mm_alloc(strlen("\n#line %d \"%s\"\n") + sizeof(int) * CHAR_BIT * 10 / 3 + strlen(input_filename) * 2);
+		char	   *line = loc_alloc(strlen("\n#line %d \"%s\"\n") + sizeof(int) * CHAR_BIT * 10 / 3 + strlen(input_filename) * 2);
 		char	   *src,
 				   *dest;
 
@@ -119,7 +118,7 @@ hashline_number(void)
 		return line;
 	}
 
-	return EMPTY;
+	return "";
 }
 
 static char *ecpg_statement_type_name[] = {
diff --git a/src/interfaces/ecpg/preproc/parser.c b/src/interfaces/ecpg/preproc/parser.c
index 8807c22cb6..78eeb78466 100644
--- a/src/interfaces/ecpg/preproc/parser.c
+++ b/src/interfaces/ecpg/preproc/parser.c
@@ -204,7 +204,7 @@ filtered_base_yylex(void)
 
 				/* Combine 3 tokens into 1 */
 				base_yylval.str = psprintf("%s UESCAPE %s", base_yylval.str, escstr);
-				base_yylloc = mm_strdup(base_yylval.str);
+				base_yylloc = loc_strdup(base_yylval.str);
 
 				/* Clear have_lookahead, thereby consuming all three tokens */
 				have_lookahead = false;
@@ -254,11 +254,11 @@ base_yylex_location(void)
 		case UIDENT:
 		case IP:
 			/* Duplicate the <str> value */
-			base_yylloc = mm_strdup(base_yylval.str);
+			base_yylloc = loc_strdup(base_yylval.str);
 			break;
 		default:
 			/* Else just use the input, i.e., yytext */
-			base_yylloc = mm_strdup(base_yytext);
+			base_yylloc = loc_strdup(base_yytext);
 			/* Apply an ASCII-only downcasing */
 			for (unsigned char *ptr = (unsigned char *) base_yylloc; *ptr; ptr++)
 			{
diff --git a/src/interfaces/ecpg/preproc/preproc_extern.h b/src/interfaces/ecpg/preproc/preproc_extern.h
index da93967462..2e41870ea8 100644
--- a/src/interfaces/ecpg/preproc/preproc_extern.h
+++ b/src/interfaces/ecpg/preproc/preproc_extern.h
@@ -13,12 +13,11 @@
 /* defines */
 
 #define STRUCT_DEPTH 128
-#define EMPTY mm_strdup("")
 
 /*
  * "Location tracking" support --- see ecpg.header for more comments.
  */
-typedef char *YYLTYPE;
+typedef const char *YYLTYPE;
 
 #define YYLTYPE_IS_DECLARED 1
 
@@ -82,18 +81,21 @@ extern int	base_yylex(void);
 extern void base_yyerror(const char *error);
 extern void *mm_alloc(size_t size);
 extern char *mm_strdup(const char *string);
+extern void *loc_alloc(size_t size);
+extern char *loc_strdup(const char *string);
+extern void reclaim_local_storage(void);
 extern void mmerror(int error_code, enum errortype type, const char *error,...) pg_attribute_printf(3, 4);
 extern void mmfatal(int error_code, const char *error,...) pg_attribute_printf(2, 3) pg_attribute_noreturn();
-extern void output_get_descr_header(char *desc_name);
-extern void output_get_descr(char *desc_name, char *index);
-extern void output_set_descr_header(char *desc_name);
-extern void output_set_descr(char *desc_name, char *index);
-extern void push_assignment(char *var, enum ECPGdtype value);
-extern struct variable *find_variable(char *name);
+extern void output_get_descr_header(const char *desc_name);
+extern void output_get_descr(const char *desc_name, const char *index);
+extern void output_set_descr_header(const char *desc_name);
+extern void output_set_descr(const char *desc_name, const char *index);
+extern void push_assignment(const char *var, enum ECPGdtype value);
+extern struct variable *find_variable(const char *name);
 extern void whenever_action(int mode);
-extern void add_descriptor(char *name, char *connection);
-extern void drop_descriptor(char *name, char *connection);
-extern struct descriptor *lookup_descriptor(char *name, char *connection);
+extern void add_descriptor(const char *name, const char *connection);
+extern void drop_descriptor(const char *name, const char *connection);
+extern struct descriptor *lookup_descriptor(const char *name, const char *connection);
 extern struct variable *descriptor_variable(const char *name, int input);
 extern struct variable *sqlda_variable(const char *name);
 extern void add_variable_to_head(struct arguments **list,
@@ -105,9 +107,9 @@ extern void add_variable_to_tail(struct arguments **list,
 extern void remove_variable_from_list(struct arguments **list, struct variable *var);
 extern void dump_variables(struct arguments *list, int mode);
 extern struct typedefs *get_typedef(const char *name, bool noerror);
-extern void adjust_array(enum ECPGttype type_enum, char **dimension,
-						 char **length, char *type_dimension,
-						 char *type_index, int pointer_len,
+extern void adjust_array(enum ECPGttype type_enum, const char **dimension,
+						 const char **length, const char *type_dimension,
+						 const char *type_index, int pointer_len,
 						 bool type_definition);
 extern void reset_variables(void);
 extern void check_indicator(struct ECPGtype *var);
diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c
index 5610a8dc76..7f52521dbf 100644
--- a/src/interfaces/ecpg/preproc/type.c
+++ b/src/interfaces/ecpg/preproc/type.c
@@ -69,13 +69,13 @@ ECPGmake_struct_member(const char *name, struct ECPGtype *type, struct ECPGstruc
 }
 
 struct ECPGtype *
-ECPGmake_simple_type(enum ECPGttype type, char *size, int counter)
+ECPGmake_simple_type(enum ECPGttype type, const char *size, int counter)
 {
 	struct ECPGtype *ne = (struct ECPGtype *) mm_alloc(sizeof(struct ECPGtype));
 
 	ne->type = type;
 	ne->type_name = NULL;
-	ne->size = size;
+	ne->size = mm_strdup(size);
 	ne->u.element = NULL;
 	ne->struct_sizeof = NULL;
 	ne->counter = counter;		/* only needed for varchar and bytea */
@@ -84,7 +84,7 @@ ECPGmake_simple_type(enum ECPGttype type, char *size, int counter)
 }
 
 struct ECPGtype *
-ECPGmake_array_type(struct ECPGtype *type, char *size)
+ECPGmake_array_type(struct ECPGtype *type, const char *size)
 {
 	struct ECPGtype *ne = ECPGmake_simple_type(ECPGt_array, size, 0);
 
@@ -96,7 +96,7 @@ ECPGmake_array_type(struct ECPGtype *type, char *size)
 struct ECPGtype *
 ECPGmake_struct_type(struct ECPGstruct_member *rm, enum ECPGttype type, char *type_name, char *struct_sizeof)
 {
-	struct ECPGtype *ne = ECPGmake_simple_type(type, mm_strdup("1"), 0);
+	struct ECPGtype *ne = ECPGmake_simple_type(type, "1", 0);
 
 	ne->type_name = mm_strdup(type_name);
 	ne->u.members = ECPGstruct_member_dup(rm);
diff --git a/src/interfaces/ecpg/preproc/type.h b/src/interfaces/ecpg/preproc/type.h
index ce2124361f..90126551d1 100644
--- a/src/interfaces/ecpg/preproc/type.h
+++ b/src/interfaces/ecpg/preproc/type.h
@@ -35,8 +35,8 @@ struct ECPGtype
 /* Everything is malloced. */
 void		ECPGmake_struct_member(const char *name, struct ECPGtype *type,
 								   struct ECPGstruct_member **start);
-struct ECPGtype *ECPGmake_simple_type(enum ECPGttype type, char *size, int counter);
-struct ECPGtype *ECPGmake_array_type(struct ECPGtype *type, char *size);
+struct ECPGtype *ECPGmake_simple_type(enum ECPGttype type, const char *size, int counter);
+struct ECPGtype *ECPGmake_array_type(struct ECPGtype *type, const char *size);
 struct ECPGtype *ECPGmake_struct_type(struct ECPGstruct_member *rm,
 									  enum ECPGttype type, char *type_name,
 									  char *struct_sizeof);
@@ -93,28 +93,28 @@ struct when
 
 struct index
 {
-	char	   *index1;
-	char	   *index2;
-	char	   *str;
+	const char *index1;
+	const char *index2;
+	const char *str;
 };
 
 struct su_symbol
 {
-	char	   *su;
-	char	   *symbol;
+	const char *su;
+	const char *symbol;
 };
 
 struct prep
 {
-	char	   *name;
-	char	   *stmt;
-	char	   *type;
+	const char *name;
+	const char *stmt;
+	const char *type;
 };
 
 struct exec
 {
-	char	   *name;
-	char	   *type;
+	const char *name;
+	const char *type;
 };
 
 struct this_type
@@ -221,14 +221,14 @@ enum errortype
 
 struct fetch_desc
 {
-	char	   *str;
-	char	   *name;
+	const char *str;
+	const char *name;
 };
 
 struct describe
 {
 	int			input;
-	char	   *stmt_name;
+	const char *stmt_name;
 };
 
 #endif							/* _ECPG_PREPROC_TYPE_H */
diff --git a/src/interfaces/ecpg/preproc/util.c b/src/interfaces/ecpg/preproc/util.c
index b80802ca9f..e2613cb499 100644
--- a/src/interfaces/ecpg/preproc/util.c
+++ b/src/interfaces/ecpg/preproc/util.c
@@ -103,3 +103,90 @@ mm_strdup(const char *string)
 
 	return new;
 }
+
+/*
+ * "Local" (or "location"?) memory management support
+ *
+ * These functions manage memory that is only needed for a short time
+ * (processing of one input statement) within the ecpg grammar.
+ * Data allocated with these is not meant to be freed separately;
+ * rather it's freed by calling reclaim_local_storage() at the end
+ * of each statement cycle.
+ */
+
+typedef struct loc_chunk
+{
+	struct loc_chunk *next;		/* list link */
+	unsigned int chunk_used;	/* index of first unused byte in data[] */
+	unsigned int chunk_avail;	/* # bytes still available in data[] */
+	char		data[FLEXIBLE_ARRAY_MEMBER];	/* actual storage */
+} loc_chunk;
+
+#define LOC_CHUNK_OVERHEAD	MAXALIGN(offsetof(loc_chunk, data))
+#define LOC_CHUNK_MIN_SIZE	8192
+
+/* Head of list of loc_chunks */
+static loc_chunk *loc_chunks = NULL;
+
+/*
+ * Allocate local space of the requested size.
+ *
+ * Exits on OOM.
+ */
+void *
+loc_alloc(size_t size)
+{
+	void	   *result;
+	loc_chunk  *cur_chunk = loc_chunks;
+
+	/* Ensure all allocations are adequately aligned */
+	size = MAXALIGN(size);
+
+	/* Need a new chunk? */
+	if (cur_chunk == NULL || size > cur_chunk->chunk_avail)
+	{
+		size_t		chunk_size = Max(size, LOC_CHUNK_MIN_SIZE);
+
+		cur_chunk = mm_alloc(chunk_size + LOC_CHUNK_OVERHEAD);
+		/* Depending on alignment rules, we could waste a bit here */
+		cur_chunk->chunk_used = LOC_CHUNK_OVERHEAD - offsetof(loc_chunk, data);
+		cur_chunk->chunk_avail = chunk_size;
+		/* New chunk becomes the head of the list */
+		cur_chunk->next = loc_chunks;
+		loc_chunks = cur_chunk;
+	}
+
+	result = cur_chunk->data + cur_chunk->chunk_used;
+	cur_chunk->chunk_used += size;
+	cur_chunk->chunk_avail -= size;
+	return result;
+}
+
+/*
+ * Copy given string into local storage
+ */
+char *
+loc_strdup(const char *string)
+{
+	char	   *result = loc_alloc(strlen(string) + 1);
+
+	strcpy(result, string);
+	return result;
+}
+
+/*
+ * Reclaim local storage when appropriate
+ */
+void
+reclaim_local_storage(void)
+{
+	loc_chunk  *cur_chunk,
+			   *next_chunk;
+
+	for (cur_chunk = loc_chunks; cur_chunk; cur_chunk = next_chunk)
+	{
+		next_chunk = cur_chunk->next;
+		free(cur_chunk);
+	}
+	loc_chunks = NULL;
+}
diff --git a/src/interfaces/ecpg/preproc/variable.c b/src/interfaces/ecpg/preproc/variable.c
index b23ed5edf4..6b87d5ff3d 100644
--- a/src/interfaces/ecpg/preproc/variable.c
+++ b/src/interfaces/ecpg/preproc/variable.c
@@ -22,7 +22,7 @@ new_variable(const char *name, struct ECPGtype *type, int brace_level)
 }
 
 static struct variable *
-find_struct_member(char *name, char *str, struct ECPGstruct_member *members, int brace_level)
+find_struct_member(const char *name, char *str, struct ECPGstruct_member *members, int brace_level)
 {
 	char	   *next = strpbrk(++str, ".-["),
 			   *end,
@@ -123,7 +123,7 @@ find_struct_member(char *name, char *str, struct ECPGstruct_member *members, int
 }
 
 static struct variable *
-find_struct(char *name, char *next, char *end)
+find_struct(const char *name, char *next, char *end)
 {
 	struct variable *p;
 	char		c = *next;
@@ -174,7 +174,7 @@ find_struct(char *name, char *next, char *end)
 }
 
 static struct variable *
-find_simple(char *name)
+find_simple(const char *name)
 {
 	struct variable *p;
 
@@ -190,7 +190,7 @@ find_simple(char *name)
 /* Note that this function will end the program in case of an unknown */
 /* variable */
 struct variable *
-find_variable(char *name)
+find_variable(const char *name)
 {
 	char	   *next,
 			   *end;
@@ -513,7 +513,10 @@ get_typedef(const char *name, bool noerror)
 }
 
 void
-adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *type_dimension, char *type_index, int pointer_len, bool type_definition)
+adjust_array(enum ECPGttype type_enum,
+			 const char **dimension, const char **length,
+			 const char *type_dimension, const char *type_index,
+			 int pointer_len, bool type_definition)
 {
 	if (atoi(type_index) >= 0)
 	{
@@ -556,7 +559,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
 			if (pointer_len)
 			{
 				*length = *dimension;
-				*dimension = mm_strdup("0");
+				*dimension = "0";
 			}
 
 			if (atoi(*length) >= 0)
@@ -567,13 +570,13 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
 		case ECPGt_bytea:
 			/* pointer has to get dimension 0 */
 			if (pointer_len)
-				*dimension = mm_strdup("0");
+				*dimension = "0";
 
 			/* one index is the string length */
 			if (atoi(*length) < 0)
 			{
 				*length = *dimension;
-				*dimension = mm_strdup("-1");
+				*dimension = "-1";
 			}
 
 			break;
@@ -583,13 +586,13 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
 			/* char ** */
 			if (pointer_len == 2)
 			{
-				*length = *dimension = mm_strdup("0");
+				*length = *dimension = "0";
 				break;
 			}
 
 			/* pointer has to get length 0 */
 			if (pointer_len == 1)
-				*length = mm_strdup("0");
+				*length = "0";
 
 			/* one index is the string length */
 			if (atoi(*length) < 0)
@@ -604,13 +607,13 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
 					 * do not change this for typedefs since it will be
 					 * changed later on when the variable is defined
 					 */
-					*length = mm_strdup("1");
+					*length = "1";
 				else if (strcmp(*dimension, "0") == 0)
-					*length = mm_strdup("-1");
+					*length = "-1";
 				else
 					*length = *dimension;
 
-				*dimension = mm_strdup("-1");
+				*dimension = "-1";
 			}
 			break;
 		default:
@@ -618,7 +621,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
 			if (pointer_len)
 			{
 				*length = *dimension;
-				*dimension = mm_strdup("0");
+				*dimension = "0";
 			}
 
 			if (atoi(*length) >= 0)
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 547d14b3e7..12121036af 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3594,6 +3594,7 @@ libpq_source
 line_t
 lineno_t
 list_sort_comparator
+loc_chunk
 local_relopt
 local_relopts
 local_source
-- 
2.43.5

