ECPG cleanup and fix for clang compile-time problem

Поиск
Список
Период
Сортировка
От Tom Lane
Тема ECPG cleanup and fix for clang compile-time problem
Дата
Msg-id 2011420.1713493114@sss.pgh.pa.us
обсуждение исходный текст
Ответы Re: ECPG cleanup and fix for clang compile-time problem  (Andres Freund <andres@anarazel.de>)
Список pgsql-hackers
Here is a patch series that aims to clean up ecpg's preprocessor
code a little and fix the compile time problems we're seeing with
late-model clang [1].  I guess whether it's a cleanup is in the eye of
the beholder, but it definitely succeeds at fixing compile time: for
me, the time needed to compile preproc.o with clang 16 drops from
104 seconds to less than 1 second.  It might be a little faster at
processing input too, though that wasn't the primary goal.

The reason that clang is having a problem seems to be the large number
of virtually-duplicate semantic actions in the generated preproc.y.
So I looked for a way to allow most productions to use the default
semantic action rather than having to write anything.  The core idea
of this patch is to stop returning <str> results from grammar
nonterminals and instead pass the strings back as Bison location data,
which we can do by redefining YYLTYPE as "char *".  Since ecpg isn't
using Bison's location logic for error reports, and seems unlikely to
do so in future, this doesn't cost us anything.  Then we can implement
a one-size-fits-most token concatenation rule in YYLLOC_DEFAULT, and
only the various handmade rules that don't want to just concatenate
their inputs need to do something different.  (Within those handmade
rules, the main notational change needed is to write "@N" not "$N"
for the string value of the N'th input token, and "@@" not "$@"
for the output string value.)  Aside from not giving clang
indigestion, this makes the compiled parser a little smaller since
there are fewer semantic actions that need code space.

As Andres remarked in the other thread, the parse.pl script that
constructs preproc.y is undocumented and unreadable, so I spent
a good deal of time reverse-engineering and cleaning that up
before I went to work on the actual problem.  Four of the six
patches in this series are in the way of cleanup and adding
documentation, with no significant behavioral changes.

The patch series comprises:

0001: pgindent the code in pgc.l and preproc.y's precursor files.
Yeah, this was my latent OCD rearing its head, but I hate looking
at or working on messy code.  It did actually pay some dividends
later on, by making it easier to make bulk edits.

0002: improve the external documentation and error checking of
parse.pl.  This was basically to convince myself that I knew
what it was supposed to do before I started changing it.
The error checks did find some errors, too: in particular,
it turns out there are two unused entries in ecpg.addons.

(This implies that check_rules.pl is completely worthless and should
be nuked: it adds build cycles and maintenance effort while failing
to reliably accomplish its one job of detecting dead rules, because
what it is testing is not the same thing that parse.pl actually does.
I've not included that removal in this patch series, though.)

0003: clean up and simplify parse.pl, and write some internal
documentation for it.  The effort of understanding it exposed that
there was a pretty fair amount of dead or at least redundant code,
so I got rid of that.  This patch changes the output preproc.y
file only to the extent of removing some blank lines that didn't
seem very useful to preserve.

0004: this is where something useful happens, specifically where
we change <str>-returning productions to return void and instead
pass back the desired output string as location data.  In most
cases the productions now need no explicit semantic action at all,
allowing substantial simplification in parse.pl.

0005: more cleanup.  I didn't want to add more memory-management
code to preproc/type.c, where mm_alloc and mm_strdup have lived
for no explicable reason.  I pulled those and a couple of other
functions out to a new file util.c, so as to have a better home
for new utility code.

0006: the big problem with 0004 is that it can't use the trick
of freeing input substrings as soon as it's created the merged
string, as cat_str and friends have historically done.  That's
because YYLLOC_DEFAULT runs before the rule's semantic action
if any, so that if the action does need to look at the input
strings, they'd already be freed.  So 0004 is leaking memory
rather badly.  Fix that by creating a notion of "local" memory
that will be reclaimed at end of statement, analogously to
short-lived memory contexts in the backend.  All the string
concatenation work happens in short-lived storage and we don't
worry about getting rid of intermediate values immediately.
By making cat_str and friends work similarly, we can get rid
of quite a lot of explicit mm_strdup calls, although we do have
to add some at places where we're building long-lived data
structures.  This should greatly reduce the malloc/free traffic
too, at the cost of eating somewhat more space intra-statement.

In my view 0006 is about the scariest part of this, as it's
hard to be sure that there are no use-after-free problems
wherein a pointer to a short-lived string survives past end
of statement.  It gets through the ecpg regression tests
under valgrind successfully, but I don't have much faith
in the thoroughness of the code coverage of those tests.
(If our code coverage tools worked on bison/flex stuff,
maybe this'd be less scary ... but they don't.)

I'll park this in the July commitfest.

            regards, tom lane

[1] https://www.postgresql.org/message-id/flat/CAGECzQQg4qmGbqqLbK9yyReWd1g%3Dd7T07_gua%2BRKXsdsW9BG-Q%40mail.gmail.com

From d2dfafa70f4b3dbc30c906223c05cb1a95c4cb81 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Wed, 17 Apr 2024 15:27:33 -0400
Subject: [PATCH v1 1/6] Clean up indentation and whitespace inconsistencies in
 ecpg.

ecpg's lexer and parser files aren't normally processed by
pgindent, and unsurprisingly there's a lot of code in there
that doesn't really match project style.  I spent some time
running pgindent over the fragments of these files that are
C code, and this is the result.  This is in the same spirit
as commit 30ed71e42, though apparently Peter used a different
method for that one, since it didn't find these problems.
---
 src/interfaces/ecpg/preproc/ecpg.addons  |  231 +-
 src/interfaces/ecpg/preproc/ecpg.header  |  180 +-
 src/interfaces/ecpg/preproc/ecpg.trailer | 3566 ++++++++++++----------
 src/interfaces/ecpg/preproc/pgc.l        |  644 ++--
 4 files changed, 2609 insertions(+), 2012 deletions(-)

diff --git a/src/interfaces/ecpg/preproc/ecpg.addons b/src/interfaces/ecpg/preproc/ecpg.addons
index e94da2a3f8..e7dce4e404 100644
--- a/src/interfaces/ecpg/preproc/ecpg.addons
+++ b/src/interfaces/ecpg/preproc/ecpg.addons
@@ -3,7 +3,7 @@ ECPG: stmtClosePortalStmt block
     {
         if (INFORMIX_MODE)
         {
-            if (pg_strcasecmp($1+strlen("close "), "database") == 0)
+            if (pg_strcasecmp($1 + strlen("close "), "database") == 0)
             {
                 if (connection)
                     mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in CLOSE DATABASE statement");
@@ -22,7 +22,9 @@ ECPG: stmtDeallocateStmt block
         output_deallocate_prepare_statement($1);
     }
 ECPG: stmtDeclareCursorStmt block
-    { output_simple_statement($1, (strncmp($1, "ECPGset_var", strlen("ECPGset_var")) == 0) ? 4 : 0); }
+    {
+        output_simple_statement($1, (strncmp($1, "ECPGset_var", strlen("ECPGset_var")) == 0) ? 4 : 0);
+    }
 ECPG: stmtDiscardStmt block
 ECPG: stmtFetchStmt block
     { output_statement($1, 1, ECPGst_normal); }
@@ -44,10 +46,13 @@ 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 = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
+                char       *str = mm_strdup($1.name + 1);

-                /* It must be cut off double quotation because new_variable() double-quotes. */
+                /*
+                 * It must be cut off double quotation because new_variable()
+                 * double-quotes.
+                 */
                 str[strlen(str) - 1] = '\0';
                 sprintf(length, "%zu", strlen(str));
                 add_variable_to_tail(&argsinsert, new_variable(str, ECPGmake_simple_type(ECPGt_const, length, 0), 0),
&no_indicator);
@@ -62,7 +67,8 @@ 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, mm_strdup("\""), $1.stmt, mm_strdup("\""));
+
             output_prepare_statement($1.name, stmt);
         }
         else
@@ -72,10 +78,13 @@ 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 = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
+                char       *str = mm_strdup($1.name + 1);

-                /* It must be cut off double quotation because new_variable() double-quotes. */
+                /*
+                 * It must be cut off double quotation because new_variable()
+                 * double-quotes.
+                 */
                 str[strlen(str) - 1] = '\0';
                 sprintf(length, "%zu", strlen(str));
                 add_variable_to_tail(&argsinsert, new_variable(str, ECPGmake_simple_type(ECPGt_const, length, 0), 0),
&no_indicator);
@@ -98,7 +107,7 @@ ECPG: toplevel_stmtTransactionStmtLegacy block
 ECPG: stmtViewStmt rule
     | ECPGAllocateDescr
     {
-        fprintf(base_yyout,"ECPGallocate_desc(__LINE__, %s);",$1);
+        fprintf(base_yyout, "ECPGallocate_desc(__LINE__, %s);", $1);
         whenever_action(0);
         free($1);
     }
@@ -118,11 +127,11 @@ ECPG: stmtViewStmt rule
     }
     | ECPGCursorStmt
     {
-         output_simple_statement($1, (strncmp($1, "ECPGset_var", strlen("ECPGset_var")) == 0) ? 4 : 0);
+        output_simple_statement($1, (strncmp($1, "ECPGset_var", strlen("ECPGset_var")) == 0) ? 4 : 0);
     }
     | ECPGDeallocateDescr
     {
-        fprintf(base_yyout,"ECPGdeallocate_desc(__LINE__, %s);",$1);
+        fprintf(base_yyout, "ECPGdeallocate_desc(__LINE__, %s);", $1);
         whenever_action(0);
         free($1);
     }
@@ -152,7 +161,10 @@ ECPG: stmtViewStmt rule
         whenever_action(2);
         free($1);
     }
-    | ECPGExecuteImmediateStmt    { output_statement($1, 0, ECPGst_exec_immediate); }
+    | ECPGExecuteImmediateStmt
+    {
+        output_statement($1, 0, ECPGst_exec_immediate);
+    }
     | ECPGFree
     {
         const char *con = connection ? connection : "NULL";
@@ -160,7 +172,7 @@ ECPG: stmtViewStmt rule
         if (strcmp($1, "all") == 0)
             fprintf(base_yyout, "{ ECPGdeallocate_all(__LINE__, %d, %s);", compat, con);
         else if ($1[0] == ':')
-            fprintf(base_yyout, "{ ECPGdeallocate(__LINE__, %d, %s, %s);", compat, con, $1+1);
+            fprintf(base_yyout, "{ ECPGdeallocate(__LINE__, %d, %s, %s);", compat, con, $1 + 1);
         else
             fprintf(base_yyout, "{ ECPGdeallocate(__LINE__, %d, %s, \"%s\");", compat, con, $1);

@@ -244,13 +256,14 @@ ECPG: stmtViewStmt rule
     }
 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);
+        char       *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
+
+        $$ = cat_str(2, mm_strdup("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 &&
-               (strcmp($7, "stdin") == 0 || strcmp($7, "stdout") == 0))
-                mmerror(PARSE_ERROR, ET_WARNING, "COPY FROM STDIN is not implemented");
+        if (strcmp($6, "from") == 0 &&
+            (strcmp($7, "stdin") == 0 || strcmp($7, "stdout") == 0))
+            mmerror(PARSE_ERROR, ET_WARNING, "COPY FROM STDIN is not implemented");
 ECPG: var_valueNumericOnly addon
         if ($1[0] == '$')
         {
@@ -259,9 +272,9 @@ ECPG: var_valueNumericOnly addon
         }
 ECPG: fetch_argscursor_name addon
         struct cursor *ptr = add_additional_variables($1, false);
-        if (ptr -> connection)
-            connection = mm_strdup(ptr -> connection);

+        if (ptr->connection)
+            connection = mm_strdup(ptr->connection);
         if ($1[0] == ':')
         {
             free($1);
@@ -269,9 +282,9 @@ ECPG: fetch_argscursor_name addon
         }
 ECPG: fetch_argsfrom_incursor_name addon
         struct cursor *ptr = add_additional_variables($2, false);
-        if (ptr -> connection)
-            connection = mm_strdup(ptr -> connection);

+        if (ptr->connection)
+            connection = mm_strdup(ptr->connection);
         if ($2[0] == ':')
         {
             free($2);
@@ -283,9 +296,9 @@ ECPG: fetch_argsFIRST_Popt_from_incursor_name addon
 ECPG: fetch_argsLAST_Popt_from_incursor_name addon
 ECPG: fetch_argsALLopt_from_incursor_name addon
         struct cursor *ptr = add_additional_variables($3, false);
-        if (ptr -> connection)
-            connection = mm_strdup(ptr -> connection);

+        if (ptr->connection)
+            connection = mm_strdup(ptr->connection);
         if ($3[0] == ':')
         {
             free($3);
@@ -293,9 +306,9 @@ ECPG: fetch_argsALLopt_from_incursor_name addon
         }
 ECPG: fetch_argsSignedIconstopt_from_incursor_name addon
         struct cursor *ptr = add_additional_variables($3, false);
-        if (ptr -> connection)
-            connection = mm_strdup(ptr -> connection);

+        if (ptr->connection)
+            connection = mm_strdup(ptr->connection);
         if ($3[0] == ':')
         {
             free($3);
@@ -309,9 +322,9 @@ ECPG: fetch_argsSignedIconstopt_from_incursor_name addon
 ECPG: fetch_argsFORWARDALLopt_from_incursor_name addon
 ECPG: fetch_argsBACKWARDALLopt_from_incursor_name addon
         struct cursor *ptr = add_additional_variables($4, false);
-        if (ptr -> connection)
-            connection = mm_strdup(ptr -> connection);

+        if (ptr->connection)
+            connection = mm_strdup(ptr->connection);
         if ($4[0] == ':')
         {
             free($4);
@@ -322,9 +335,9 @@ ECPG: fetch_argsRELATIVE_PSignedIconstopt_from_incursor_name addon
 ECPG: fetch_argsFORWARDSignedIconstopt_from_incursor_name addon
 ECPG: fetch_argsBACKWARDSignedIconstopt_from_incursor_name addon
         struct cursor *ptr = add_additional_variables($4, false);
-        if (ptr -> connection)
-            connection = mm_strdup(ptr -> connection);

+        if (ptr->connection)
+            connection = mm_strdup(ptr->connection);
         if ($4[0] == ':')
         {
             free($4);
@@ -337,13 +350,14 @@ ECPG: fetch_argsBACKWARDSignedIconstopt_from_incursor_name addon
         }
 ECPG: cursor_namename rule
     | char_civar
-        {
-            char *curname = mm_alloc(strlen($1) + 2);
-            sprintf(curname, ":%s", $1);
-            free($1);
-            $1 = curname;
-            $$ = $1;
-        }
+    {
+        char       *curname = mm_alloc(strlen($1) + 2);
+
+        sprintf(curname, ":%s", $1);
+        free($1);
+        $1 = curname;
+        $$ = $1;
+    }
 ECPG: ExplainableStmtExecuteStmt block
     {
         $$ = $1.name;
@@ -367,28 +381,31 @@ ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block
     }
 ECPG: ExecuteStmtCREATEOptTempTABLEcreate_as_targetASEXECUTEprepared_nameexecute_param_clauseopt_with_dataexecute_rest
block
     {
-        $$.name = cat_str(8,mm_strdup("create"),$2,mm_strdup("table"),$4,mm_strdup("as execute"),$7,$8,$9);
+        $$.name = cat_str(8, mm_strdup("create"), $2, mm_strdup("table"), $4, mm_strdup("as execute"), $7, $8, $9);
     }
 ECPG:
ExecuteStmtCREATEOptTempTABLEIF_PNOTEXISTScreate_as_targetASEXECUTEprepared_nameexecute_param_clauseopt_with_dataexecute_rest
block
     {
-        $$.name = cat_str(8,mm_strdup("create"),$2,mm_strdup("table if not exists"),$7,mm_strdup("as
execute"),$10,$11,$12);
+        $$.name = cat_str(8, mm_strdup("create"), $2, mm_strdup("table if not exists"), $7, mm_strdup("as execute"),
$10,$11, $12); 
     }
 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] == ':' || $2[0] == '"') ? strcmp : pg_strcasecmp);
+        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] == ':' || $2[0] == '"') ? strcmp :
pg_strcasecmp);

-                if (INFORMIX_MODE && pg_strcasecmp($2, "database") == 0)
-                        mmfatal(PARSE_ERROR, "\"database\" cannot be used as cursor name in INFORMIX mode");
+        if (INFORMIX_MODE && pg_strcasecmp($2, "database") == 0)
+            mmfatal(PARSE_ERROR, "\"database\" cannot be used as cursor name in INFORMIX mode");

         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); 
+                    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);
             }
@@ -401,7 +418,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 = 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;
@@ -422,15 +439,15 @@ ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectSt
     }
 ECPG: ClosePortalStmtCLOSEcursor_name block
     {
-        char *cursor_marker = $2[0] == ':' ? mm_strdup("$0") : $2;
+        char       *cursor_marker = $2[0] == ':' ? mm_strdup("$0") : $2;
         struct cursor *ptr = NULL;
-        for (ptr = cur; ptr != NULL; ptr = ptr -> next)
+
+        for (ptr = cur; ptr != NULL; ptr = ptr->next)
         {
-            if (strcmp($2, ptr -> name) == 0)
+            if (strcmp($2, ptr->name) == 0)
             {
-                if (ptr -> connection)
-                    connection = mm_strdup(ptr -> connection);
-
+                if (ptr->connection)
+                    connection = mm_strdup(ptr->connection);
                 break;
             }
         }
@@ -444,15 +461,22 @@ ECPG: opt_hold block
             $$ = EMPTY;
     }
 ECPG: into_clauseINTOOptTempTableName block
-                    {
-                        FoundInto = 1;
-                        $$= cat2_str(mm_strdup("into"), $2);
-                    }
-    | ecpg_into { $$ = EMPTY; }
+    {
+        FoundInto = 1;
+        $$ = cat2_str(mm_strdup("into"), $2);
+    }
+    | ecpg_into
+    {
+        $$ = EMPTY;
+    }
 ECPG: TypenameSimpleTypenameopt_array_bounds block
-    {    $$ = cat2_str($1, $2.str); }
+    {
+        $$ = cat2_str($1, $2.str);
+    }
 ECPG: TypenameSETOFSimpleTypenameopt_array_bounds block
-    {    $$ = cat_str(3, mm_strdup("setof"), $2, $3.str); }
+    {
+        $$ = cat_str(3, mm_strdup("setof"), $2, $3.str);
+    }
 ECPG: opt_array_boundsopt_array_bounds'['']' block
     {
         $$.index1 = $1.index1;
@@ -477,22 +501,24 @@ ECPG: opt_array_bounds
     {
         $$.index1 = mm_strdup("-1");
         $$.index2 = mm_strdup("-1");
-        $$.str= EMPTY;
+        $$.str = EMPTY;
     }
 ECPG: IconstICONST block
-    { $$ = make_name(); }
+    {
+        $$ = make_name();
+    }
 ECPG: AexprConstNULL_P rule
-    | civar            { $$ = $1; }
-    | civarind        { $$ = $1; }
+    | civar                            { $$ = $1; }
+    | civarind                        { $$ = $1; }
 ECPG: ColIdcol_name_keyword rule
-    | ECPGKeywords                  { $$ = $1; }
-    | ECPGCKeywords                 { $$ = $1; }
-    | CHAR_P                        { $$ = mm_strdup("char"); }
-    | VALUES                        { $$ = mm_strdup("values"); }
+    | 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; }
+    | ECPGKeywords                    { $$ = $1; }
+    | ECPGTypeName                    { $$ = $1; }
+    | ECPGCKeywords                    { $$ = $1; }
 ECPG: VariableShowStmtSHOWALL block
     {
         mmerror(PARSE_ERROR, ET_ERROR, "SHOW ALL is not implemented");
@@ -505,73 +531,81 @@ ECPG: FetchStmtMOVEfetch_args rule
     }
     | FETCH FORWARD cursor_name opt_ecpg_fetch_into
     {
-        char *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
+        char       *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
         struct cursor *ptr = add_additional_variables($3, false);
-        if (ptr -> connection)
-            connection = mm_strdup(ptr -> connection);
+
+        if (ptr->connection)
+            connection = mm_strdup(ptr->connection);

         $$ = 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;
+        char       *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
         struct cursor *ptr = add_additional_variables($4, false);
-            if (ptr -> connection)
-                connection = mm_strdup(ptr -> connection);
+
+        if (ptr->connection)
+            connection = mm_strdup(ptr->connection);

         $$ = 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;
+        char       *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
         struct cursor *ptr = add_additional_variables($3, false);
-        if (ptr -> connection)
-            connection = mm_strdup(ptr -> connection);
+
+        if (ptr->connection)
+            connection = mm_strdup(ptr->connection);

         $$ = 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;
+        char       *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
         struct cursor *ptr = add_additional_variables($4, false);
-        if (ptr -> connection)
-            connection = mm_strdup(ptr -> connection);
+
+        if (ptr->connection)
+            connection = mm_strdup(ptr->connection);

         $$ = cat_str(2, mm_strdup("fetch backward from"), cursor_marker);
     }
     | MOVE FORWARD cursor_name
     {
-        char *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
+        char       *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
         struct cursor *ptr = add_additional_variables($3, false);
-        if (ptr -> connection)
-            connection = mm_strdup(ptr -> connection);
+
+        if (ptr->connection)
+            connection = mm_strdup(ptr->connection);

         $$ = cat_str(2, mm_strdup("move forward"), cursor_marker);
     }
     | MOVE FORWARD from_in cursor_name
     {
-        char *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
+        char       *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
         struct cursor *ptr = add_additional_variables($4, false);
-        if (ptr -> connection)
-            connection = mm_strdup(ptr -> connection);
+
+        if (ptr->connection)
+            connection = mm_strdup(ptr->connection);

         $$ = cat_str(2, mm_strdup("move forward from"), cursor_marker);
     }
     | MOVE BACKWARD cursor_name
     {
-        char *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
+        char       *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
         struct cursor *ptr = add_additional_variables($3, false);
-        if (ptr -> connection)
-            connection = mm_strdup(ptr -> connection);
+
+        if (ptr->connection)
+            connection = mm_strdup(ptr->connection);

         $$ = cat_str(2, mm_strdup("move backward"), cursor_marker);
     }
     | MOVE BACKWARD from_in cursor_name
     {
-        char *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
+        char       *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
         struct cursor *ptr = add_additional_variables($4, false);
-        if (ptr -> connection)
-            connection = mm_strdup(ptr -> connection);
+
+        if (ptr->connection)
+            connection = mm_strdup(ptr->connection);

         $$ = cat_str(2, mm_strdup("move backward from"), cursor_marker);
     }
@@ -581,4 +615,7 @@ ECPG: limit_clauseLIMITselect_limit_value','select_offset_value block
         $$ = cat_str(4, mm_strdup("limit"), $2, mm_strdup(","), $4);
     }
 ECPG: SignedIconstIconst rule
-    | civar    { $$ = $1; }
+    | civar
+    {
+        $$ = $1;
+    }
diff --git a/src/interfaces/ecpg/preproc/ecpg.header b/src/interfaces/ecpg/preproc/ecpg.header
index 5950289425..3ed39b5c77 100644
--- a/src/interfaces/ecpg/preproc/ecpg.header
+++ b/src/interfaces/ecpg/preproc/ecpg.header
@@ -32,24 +32,25 @@
 /*
  * Variables containing simple states.
  */
-int struct_level = 0;
-int braces_open; /* brace level counter */
-char *current_function;
-int ecpg_internal_var = 0;
-char    *connection = NULL;
-char    *input_filename = NULL;
+int            struct_level = 0;
+int            braces_open;        /* brace level counter */
+char       *current_function;
+int            ecpg_internal_var = 0;
+char       *connection = NULL;
+char       *input_filename = NULL;

 static int    FoundInto = 0;
 static int    initializer = 0;
 static int    pacounter = 1;
-static char    pacounter_buffer[sizeof(int) * CHAR_BIT * 10 / 3]; /* a rough guess at the size we need */
+static 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;
 static int    bytea_counter = 1;

 /* temporarily store struct members while creating the data structure */
-struct ECPGstruct_member *struct_member_list[STRUCT_DEPTH] = { NULL };
+struct ECPGstruct_member *struct_member_list[STRUCT_DEPTH] = {NULL};

 /* also store struct type so we can do a sizeof() later */
 static char *ECPGstruct_sizeof = NULL;
@@ -77,7 +78,7 @@ vmmerror(int error_code, enum errortype type, const char *error, va_list ap)

     fprintf(stderr, "%s:%d: ", input_filename, base_yylineno);

-    switch(type)
+    switch (type)
     {
         case ET_WARNING:
             fprintf(stderr, _("WARNING: "));
@@ -91,7 +92,7 @@ vmmerror(int error_code, enum errortype type, const char *error, va_list ap)

     fprintf(stderr, "\n");

-    switch(type)
+    switch (type)
     {
         case ET_WARNING:
             break;
@@ -102,7 +103,7 @@ vmmerror(int error_code, enum errortype type, const char *error, va_list ap)
 }

 void
-mmerror(int error_code, enum errortype type, const char *error, ...)
+mmerror(int error_code, enum errortype type, const char *error,...)
 {
     va_list        ap;

@@ -112,7 +113,7 @@ mmerror(int error_code, enum errortype type, const char *error, ...)
 }

 void
-mmfatal(int error_code, const char *error, ...)
+mmfatal(int error_code, const char *error,...)
 {
     va_list        ap;

@@ -137,7 +138,7 @@ mmfatal(int error_code, const char *error, ...)
 static char *
 cat2_str(char *str1, char *str2)
 {
-    char * res_str    = (char *)mm_alloc(strlen(str1) + strlen(str2) + 2);
+    char       *res_str = (char *) mm_alloc(strlen(str1) + strlen(str2) + 2);

     strcpy(res_str, str1);
     if (strlen(str1) != 0 && strlen(str2) != 0)
@@ -149,11 +150,11 @@ cat2_str(char *str1, char *str2)
 }

 static char *
-cat_str(int count, ...)
+cat_str(int count,...)
 {
     va_list        args;
     int            i;
-    char        *res_str;
+    char       *res_str;

     va_start(args, count);

@@ -171,7 +172,7 @@ cat_str(int count, ...)
 static char *
 make2_str(char *str1, char *str2)
 {
-    char * res_str    = (char *)mm_alloc(strlen(str1) + strlen(str2) + 1);
+    char       *res_str = (char *) mm_alloc(strlen(str1) + strlen(str2) + 1);

     strcpy(res_str, str1);
     strcat(res_str, str2);
@@ -183,7 +184,7 @@ make2_str(char *str1, char *str2)
 static char *
 make3_str(char *str1, char *str2, char *str3)
 {
-    char * res_str    = (char *)mm_alloc(strlen(str1) + strlen(str2) +strlen(str3) + 1);
+    char       *res_str = (char *) mm_alloc(strlen(str1) + strlen(str2) + strlen(str3) + 1);

     strcpy(res_str, str1);
     strcat(res_str, str2);
@@ -205,13 +206,18 @@ static char *
 create_questionmarks(char *name, bool array)
 {
     struct variable *p = find_variable(name);
-    int count;
-    char *result = EMPTY;
+    int            count;
+    char       *result = EMPTY;

-    /* In case we have a struct, we have to print as many "?" as there are attributes in the struct
+    /*
+     * In case we have a struct, we have to print as many "?" as there are
+     * attributes in the struct
+     *
      * An array is only allowed together with an element argument
-     * This is essentially only used for inserts, but using a struct as input parameter is an error anywhere else
-     * so we don't have to worry here. */
+     *
+     * This is essentially only used for inserts, but using a struct as input
+     * parameter is an error anywhere else so we don't have to worry here.
+     */

     if (p->type->type == ECPGt_struct || (array && p->type->type == ECPGt_array && p->type->u.element->type ==
ECPGt_struct))
     {
@@ -222,12 +228,12 @@ create_questionmarks(char *name, bool array)
         else
             m = p->type->u.element->u.members;

-        for (count = 0; m != NULL; m=m->next, count++);
+        for (count = 0; m != NULL; m = m->next, count++);
     }
     else
         count = 1;

-    for (; count > 0; count --)
+    for (; count > 0; count--)
     {
         sprintf(pacounter_buffer, "$%d", pacounter++);
         result = cat_str(3, result, mm_strdup(pacounter_buffer), mm_strdup(" , "));
@@ -235,42 +241,45 @@ create_questionmarks(char *name, bool array)

     /* removed the trailing " ," */

-    result[strlen(result)-3] = '\0';
+    result[strlen(result) - 3] = '\0';
     return result;
 }

 static char *
 adjust_outofscope_cursor_vars(struct cursor *cur)
 {
-    /* Informix accepts DECLARE with variables that are out of scope when OPEN is called.
-     * For instance you can DECLARE a cursor in one function, and OPEN/FETCH/CLOSE
-     * it in another functions. This is very useful for e.g. event-driver programming,
-     * but may also lead to dangerous programming. The limitation when this is allowed
-     * and doesn't cause problems have to be documented, like the allocated variables
-     * must not be realloc()'ed.
+    /*
+     * Informix accepts DECLARE with variables that are out of scope when OPEN
+     * is called. For instance you can DECLARE a cursor in one function, and
+     * OPEN/FETCH/CLOSE it in another functions. This is very useful for e.g.
+     * event-driver programming, but may also lead to dangerous programming.
+     * The limitation when this is allowed and doesn't cause problems have to
+     * be documented, like the allocated variables must not be realloc()'ed.
      *
-     * We have to change the variables to our own struct and just store the pointer
-     * instead of the variable. Do it only for local variables, not for globals.
+     * We have to change the variables to our own struct and just store the
+     * pointer instead of the variable. Do it only for local variables, not
+     * for globals.
      */

-    char *result = EMPTY;
-    int insert;
+    char       *result = EMPTY;
+    int            insert;

     for (insert = 1; insert >= 0; insert--)
     {
         struct arguments *list;
         struct arguments *ptr;
         struct arguments *newlist = NULL;
-        struct variable *newvar, *newind;
+        struct variable *newvar,
+                   *newind;

         list = (insert ? cur->argsinsert : cur->argsresult);

         for (ptr = list; ptr != NULL; ptr = ptr->next)
         {
-            char var_text[20];
-            char *original_var;
-            bool skip_set_var = false;
-            bool var_ptr = false;
+            char        var_text[20];
+            char       *original_var;
+            bool        skip_set_var = false;
+            bool        var_ptr = false;

             /* change variable name to "ECPGget_var(<counter>)" */
             original_var = ptr->variable->name;
@@ -345,10 +354,10 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
                     || 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(")")),
+                                                  mm_strdup(ptr->variable->type->u.element->type_name),
+                                                  mm_strdup(" *)(ECPGget_var("),
+                                                  mm_strdup(var_text),
+                                                  mm_strdup(")")),
                                           ECPGmake_struct_type(ptr->variable->type->u.element->u.members,
                                                                ptr->variable->type->u.element->type,
                                                                ptr->variable->type->u.element->type_name,
@@ -382,7 +391,10 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
                 var_ptr = true;
             }

-            /* create call to "ECPGset_var(<counter>, <connection>, <pointer>. <line number>)" */
+            /*
+             * create call to "ECPGset_var(<counter>, <connection>, <pointer>.
+             * <line number>)"
+             */
             if (!skip_set_var)
             {
                 sprintf(var_text, "%d, %s", ecpg_internal_var++, var_ptr ? "&(" : "(");
@@ -391,7 +403,10 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
                                  mm_strdup("), __LINE__);\n"));
             }

-            /* now the indicator if there is one and it's not a global variable */
+            /*
+             * now the indicator if there is one and it's not a global
+             * variable
+             */
             if ((ptr->indicator->type->type == ECPGt_NO_INDICATOR) || (ptr->indicator->brace_level == 0))
             {
                 newind = ptr->indicator;
@@ -407,10 +422,10 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
                     || 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(")")),
+                                                  mm_strdup(ptr->indicator->type->type_name),
+                                                  mm_strdup(" *)(ECPGget_var("),
+                                                  mm_strdup(var_text),
+                                                  mm_strdup(")")),
                                           ECPGmake_struct_type(ptr->indicator->type->u.members,
                                                                ptr->indicator->type->type,
                                                                ptr->indicator->type->type_name,
@@ -424,10 +439,10 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
                         || 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(")")),
+                                                      mm_strdup(ptr->indicator->type->u.element->type_name),
+                                                      mm_strdup(" *)(ECPGget_var("),
+                                                      mm_strdup(var_text),
+                                                      mm_strdup(")")),
                                               ECPGmake_struct_type(ptr->indicator->type->u.element->u.members,
                                                                    ptr->indicator->type->u.element->type,
                                                                    ptr->indicator->type->u.element->type_name,
@@ -471,7 +486,10 @@ adjust_outofscope_cursor_vars(struct cursor *cur)
                     var_ptr = true;
                 }

-                /* create call to "ECPGset_var(<counter>, <pointer>. <line number>)" */
+                /*
+                 * 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),
@@ -500,9 +518,9 @@ add_additional_variables(char *name, bool insert)
 {
     struct cursor *ptr;
     struct arguments *p;
-    int (* strcmp_fn)(const char *, const char *) = ((name[0] == ':' || name[0] == '"') ? strcmp : pg_strcasecmp);
+    int            (*strcmp_fn) (const char *, const char *) = ((name[0] == ':' || name[0] == '"') ? strcmp :
pg_strcasecmp);

-    for (ptr = cur; ptr != NULL; ptr=ptr->next)
+    for (ptr = cur; ptr != NULL; ptr = ptr->next)
     {
         if (strcmp_fn(ptr->name, name) == 0)
             break;
@@ -516,8 +534,12 @@ add_additional_variables(char *name, bool insert)

     if (insert)
     {
-        /* add all those input variables that were given earlier
-         * note that we have to append here but have to keep the existing order */
+        /*
+         * add all those input variables that were given earlier
+         *
+         * note that we have to append here but have to keep the existing
+         * order
+         */
         for (p = (SAMEFUNC(ptr) ? ptr->argsinsert : ptr->argsinsert_oos); p; p = p->next)
             add_variable_to_tail(&argsinsert, p->variable, p->indicator);
     }
@@ -534,7 +556,8 @@ add_typedef(char *name, char *dimension, char *length, enum ECPGttype type_enum,
             char *type_dimension, char *type_index, int initializer, int array)
 {
     /* add entry to list */
-    struct typedefs *ptr, *this;
+    struct typedefs *ptr,
+               *this;

     if ((type_enum == ECPGt_struct ||
          type_enum == ECPGt_union) &&
@@ -565,7 +588,7 @@ add_typedef(char *name, char *dimension, char *length, enum ECPGttype type_enum,
         this->type->type_index = length;    /* length of string */
         this->type->type_sizeof = ECPGstruct_sizeof;
         this->struct_member_list = (type_enum == ECPGt_struct || type_enum == ECPGt_union) ?
-        ECPGstruct_member_dup(struct_member_list[struct_level]) : NULL;
+            ECPGstruct_member_dup(struct_member_list[struct_level]) : NULL;

         if (type_enum != ECPGt_varchar &&
             type_enum != ECPGt_bytea &&
@@ -588,15 +611,16 @@ static bool
 check_declared_list(const char *name)
 {
     struct declared_list *ptr = NULL;
-    for (ptr = g_declared_list; ptr != NULL; ptr = ptr -> next)
+
+    for (ptr = g_declared_list; ptr != NULL; ptr = ptr->next)
     {
         if (!ptr->connection)
             continue;
-        if (strcmp(name, ptr -> name) == 0)
+        if (strcmp(name, ptr->name) == 0)
         {
             if (connection && strcmp(ptr->connection, connection) != 0)
                 mmerror(PARSE_ERROR, ET_WARNING, "connection %s is overwritten with %s by DECLARE statement %s",
connection,ptr->connection, name); 
-            connection = mm_strdup(ptr -> connection);
+            connection = mm_strdup(ptr->connection);
             return true;
         }
     }
@@ -609,18 +633,18 @@ check_declared_list(const char *name)
 %locations

 %union {
-    double    dval;
-    char    *str;
-    int        ival;
-    struct    when        action;
-    struct    index        index;
-    int        tagname;
-    struct    this_type    type;
-    enum    ECPGttype    type_enum;
-    enum    ECPGdtype    dtype_enum;
-    struct    fetch_desc    descriptor;
-    struct  su_symbol    struct_union;
-    struct    prep        prep;
-    struct    exec        exec;
-    struct describe        describe;
+    double        dval;
+    char       *str;
+    int            ival;
+    struct when action;
+    struct index index;
+    int            tagname;
+    struct this_type type;
+    enum ECPGttype type_enum;
+    enum ECPGdtype dtype_enum;
+    struct fetch_desc descriptor;
+    struct su_symbol struct_union;
+    struct prep prep;
+    struct exec exec;
+    struct describe describe;
 }
diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer
index b2aa44f36d..b6233e5e53 100644
--- a/src/interfaces/ecpg/preproc/ecpg.trailer
+++ b/src/interfaces/ecpg/preproc/ecpg.trailer
@@ -1,465 +1,610 @@
 /* src/interfaces/ecpg/preproc/ecpg.trailer */

-statements: /*EMPTY*/
-                | statements statement
-        ;
+statements: /* EMPTY */
+    | statements statement
+    ;

 statement: ecpgstart at toplevel_stmt ';'
-                {
-                    if (connection)
-                        free(connection);
-                    connection = NULL;
-                }
-                | ecpgstart toplevel_stmt ';'
-                {
-                    if (connection)
-                        free(connection);
-                    connection = NULL;
-                }
-                | ecpgstart ECPGVarDeclaration
-                {
-                    fprintf(base_yyout, "%s", $2);
-                    free($2);
-                    output_line_number();
-                }
-                | ECPGDeclaration
-                | c_thing               { fprintf(base_yyout, "%s", $1); free($1); }
-                | CPP_LINE              { fprintf(base_yyout, "%s", $1); free($1); }
-                | '{'                   { braces_open++; fputs("{", base_yyout); }
-                | '}'
+    {
+        if (connection)
+            free(connection);
+        connection = NULL;
+    }
+    | ecpgstart toplevel_stmt ';'
+    {
+        if (connection)
+            free(connection);
+        connection = NULL;
+    }
+    | ecpgstart ECPGVarDeclaration
+    {
+        fprintf(base_yyout, "%s", $2);
+        free($2);
+        output_line_number();
+    }
+    | ECPGDeclaration
+    | c_thing
+    {
+        fprintf(base_yyout, "%s", $1);
+        free($1);
+    }
+    | CPP_LINE
+    {
+        fprintf(base_yyout, "%s", $1);
+        free($1);
+    }
+    | '{'
+    {
+        braces_open++;
+        fputs("{", base_yyout);
+    }
+    | '}'
+    {
+        remove_typedefs(braces_open);
+        remove_variables(braces_open--);
+        if (braces_open == 0)
         {
-            remove_typedefs(braces_open);
-            remove_variables(braces_open--);
-            if (braces_open == 0)
-            {
-                free(current_function);
-                current_function = NULL;
-            }
-            fputs("}", base_yyout);
+            free(current_function);
+            current_function = NULL;
         }
-        ;
+        fputs("}", base_yyout);
+    }
+    ;

-CreateAsStmt: CREATE OptTemp TABLE create_as_target AS {FoundInto = 0;} SelectStmt opt_with_data
-        {
-            if (FoundInto == 1)
-                mmerror(PARSE_ERROR, ET_ERROR, "CREATE TABLE AS cannot specify INTO");
+CreateAsStmt: CREATE OptTemp TABLE create_as_target AS
+    {
+        FoundInto = 0;
+    } SelectStmt opt_with_data
+    {
+        if (FoundInto == 1)
+            mmerror(PARSE_ERROR, ET_ERROR, "CREATE TABLE AS cannot specify INTO");

-            $$ = cat_str(7, mm_strdup("create"), $2, mm_strdup("table"), $4, mm_strdup("as"), $7, $8);
-        }
-        |  CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS {FoundInto = 0;} SelectStmt opt_with_data
-        {
-            if (FoundInto == 1)
-                mmerror(PARSE_ERROR, ET_ERROR, "CREATE TABLE AS cannot specify INTO");
+        $$ = cat_str(7, mm_strdup("create"), $2, mm_strdup("table"), $4, mm_strdup("as"), $7, $8);
+    }
+    | CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS
+    {
+        FoundInto = 0;
+    } SelectStmt opt_with_data
+    {
+        if (FoundInto == 1)
+            mmerror(PARSE_ERROR, ET_ERROR, "CREATE TABLE AS cannot specify INTO");

-            $$ = cat_str(7, mm_strdup("create"), $2, mm_strdup("table if not exists"), $7, mm_strdup("as"), $10, $11);
-        }
-        ;
+        $$ = cat_str(7, mm_strdup("create"), $2, mm_strdup("table if not exists"), $7, mm_strdup("as"), $10, $11);
+    }
+    ;

 at: AT connection_object
-        {
-            connection = $2;
-            /*
-             * Do we have a variable as connection target?  Remove the variable
-             * from the variable list or else it will be used twice.
-             */
-            if (argsinsert != NULL)
-                argsinsert = NULL;
-        }
-        ;
+    {
+        connection = $2;
+
+        /*
+         * Do we have a variable as connection target?  Remove the variable
+         * from the variable list or else it will be used twice.
+         */
+        if (argsinsert != NULL)
+            argsinsert = NULL;
+    }
+    ;

 /*
  * the exec sql connect statement: connect to the given database
  */
 ECPGConnect: SQL_CONNECT TO connection_target opt_connection_name opt_user
-            { $$ = cat_str(5, $3, mm_strdup(","), $5, mm_strdup(","), $4); }
-        | SQL_CONNECT TO DEFAULT
-            { $$ = mm_strdup("NULL, NULL, NULL, \"DEFAULT\""); }
-          /* also allow ORACLE syntax */
-        | SQL_CONNECT ora_user
-            { $$ = cat_str(3, mm_strdup("NULL,"), $2, mm_strdup(", NULL")); }
-        | DATABASE connection_target
-            { $$ = cat2_str($2, mm_strdup(", NULL, NULL, NULL")); }
-        ;
+    {
+        $$ = cat_str(5, $3, mm_strdup(","), $5, mm_strdup(","), $4);
+    }
+    | SQL_CONNECT TO DEFAULT
+    {
+        $$ = mm_strdup("NULL, NULL, NULL, \"DEFAULT\"");
+    }
+    /* also allow ORACLE syntax */
+    | SQL_CONNECT ora_user
+    {
+        $$ = cat_str(3, mm_strdup("NULL,"), $2, mm_strdup(", NULL"));
+    }
+    | DATABASE connection_target
+    {
+        $$ = cat2_str($2, mm_strdup(", NULL, NULL, NULL"));
+    }
+    ;

 connection_target: opt_database_name opt_server opt_port
-        {
-            /* old style: dbname[@server][:port] */
-            if (strlen($2) > 0 && *($2) != '@')
-                mmerror(PARSE_ERROR, ET_ERROR, "expected \"@\", found \"%s\"", $2);
+    {
+        /* old style: dbname[@server][:port] */
+        if (strlen($2) > 0 && *($2) != '@')
+            mmerror(PARSE_ERROR, ET_ERROR, "expected \"@\", found \"%s\"", $2);

-            /* C strings need to be handled differently */
-            if ($1[0] == '\"')
-                $$ = $1;
-            else
-                $$ = make3_str(mm_strdup("\""), make3_str($1, $2, $3), mm_strdup("\""));
-        }
-        |  db_prefix ':' server opt_port '/' opt_database_name opt_options
-        {
-            /* new style: <tcp|unix>:postgresql://server[:port][/dbname] */
-            if (strncmp($1, "unix:postgresql", strlen("unix:postgresql")) != 0 && strncmp($1, "tcp:postgresql",
strlen("tcp:postgresql"))!= 0) 
-                mmerror(PARSE_ERROR, ET_ERROR, "only protocols \"tcp\" and \"unix\" and database type \"postgresql\"
aresupported"); 
+        /* C strings need to be handled differently */
+        if ($1[0] == '\"')
+            $$ = $1;
+        else
+            $$ = make3_str(mm_strdup("\""), make3_str($1, $2, $3), mm_strdup("\""));
+    }
+    | db_prefix ':' server opt_port '/' opt_database_name opt_options
+    {
+        /* new style: <tcp|unix>:postgresql://server[:port][/dbname] */
+        if (strncmp($1, "unix:postgresql", strlen("unix:postgresql")) != 0 && strncmp($1, "tcp:postgresql",
strlen("tcp:postgresql"))!= 0) 
+            mmerror(PARSE_ERROR, ET_ERROR, "only protocols \"tcp\" and \"unix\" and database type \"postgresql\" are
supported");

-            if (strncmp($3, "//", strlen("//")) != 0)
-                mmerror(PARSE_ERROR, ET_ERROR, "expected \"://\", found \"%s\"", $3);
+        if (strncmp($3, "//", strlen("//")) != 0)
+            mmerror(PARSE_ERROR, ET_ERROR, "expected \"://\", found \"%s\"", $3);

-            if (strncmp($1, "unix", strlen("unix")) == 0 &&
-                strncmp($3 + strlen("//"), "localhost", strlen("localhost")) != 0 &&
-                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("//")); 
+        if (strncmp($1, "unix", strlen("unix")) == 0 &&
+            strncmp($3 + strlen("//"), "localhost", strlen("localhost")) != 0 &&
+            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("\""))); 
-        }
-        | char_variable
-        {
-            $$ = $1;
-        }
-        | 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 these single quotes. */
-            $1[0] = '\"';
-            $1[strlen($1)-1] = '\"';
-            $$ = $1;
-        }
-        ;
+        $$ = make3_str(make3_str(mm_strdup("\""), $1, mm_strdup(":")), $3, make3_str(make3_str($4, mm_strdup("/"),
$6),$7, mm_strdup("\""))); 
+    }
+    | char_variable
+    {
+        $$ = $1;
+    }
+    | 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
+         * these single quotes.
+         */
+        $1[0] = '\"';
+        $1[strlen($1) - 1] = '\"';
+        $$ = $1;
+    }
+    ;

-opt_database_name: name                { $$ = $1; }
-        | /*EMPTY*/            { $$ = EMPTY; }
-        ;
+opt_database_name: name
+    {
+        $$ = $1;
+    }
+    | /* EMPTY */
+    {
+        $$ = EMPTY;
+    }
+    ;

 db_prefix: ecpg_ident cvariable
-        {
-            if (strcmp($2, "postgresql") != 0 && strcmp($2, "postgres") != 0)
-                mmerror(PARSE_ERROR, ET_ERROR, "expected \"postgresql\", found \"%s\"", $2);
+    {
+        if (strcmp($2, "postgresql") != 0 && strcmp($2, "postgres") != 0)
+            mmerror(PARSE_ERROR, ET_ERROR, "expected \"postgresql\", found \"%s\"", $2);

-            if (strcmp($1, "tcp") != 0 && strcmp($1, "unix") != 0)
-                mmerror(PARSE_ERROR, ET_ERROR, "invalid connection type: %s", $1);
+        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, mm_strdup(":"), $2);
+    }
+    ;

 server: Op server_name
-        {
-            if (strcmp($1, "@") != 0 && strcmp($1, "//") != 0)
-                mmerror(PARSE_ERROR, ET_ERROR, "expected \"@\" or \"://\", found \"%s\"", $1);
+    {
+        if (strcmp($1, "@") != 0 && strcmp($1, "//") != 0)
+            mmerror(PARSE_ERROR, ET_ERROR, "expected \"@\" or \"://\", found \"%s\"", $1);

-            $$ = make2_str($1, $2);
-        }
-        ;
+        $$ = make2_str($1, $2);
+    }
+    ;

-opt_server: server            { $$ = $1; }
-        | /*EMPTY*/            { $$ = EMPTY; }
-        ;
+opt_server: server
+    {
+        $$ = $1;
+    }
+    | /* EMPTY */
+    {
+        $$ = EMPTY;
+    }
+    ;

-server_name: ColId                    { $$ = $1; }
-        | ColId '.' server_name        { $$ = make3_str($1, mm_strdup("."), $3); }
-        | IP                        { $$ = make_name(); }
-        ;
+server_name: ColId
+    {
+        $$ = $1;
+    }
+    | ColId '.' server_name
+    {
+        $$ = make3_str($1, mm_strdup("."), $3);
+    }
+    | IP
+    {
+        $$ = make_name();
+    }
+    ;

-opt_port: ':' Iconst        { $$ = make2_str(mm_strdup(":"), $2); }
-        | /*EMPTY*/    { $$ = EMPTY; }
-        ;
+opt_port: ':' Iconst
+    {
+        $$ = make2_str(mm_strdup(":"), $2);
+    }
+    | /* EMPTY */
+    {
+        $$ = EMPTY;
+    }
+    ;

-opt_connection_name: AS connection_object    { $$ = $2; }
-        | /*EMPTY*/            { $$ = mm_strdup("NULL"); }
-        ;
+opt_connection_name: AS connection_object
+    {
+        $$ = $2;
+    }
+    | /* EMPTY */
+    {
+        $$ = mm_strdup("NULL");
+    }
+    ;

-opt_user: USER ora_user        { $$ = $2; }
-        | /*EMPTY*/            { $$ = mm_strdup("NULL, NULL"); }
-        ;
+opt_user: USER ora_user
+    {
+        $$ = $2;
+    }
+    | /* EMPTY */
+    {
+        $$ = mm_strdup("NULL, NULL");
+    }
+    ;

 ora_user: user_name
-            { $$ = cat2_str($1, mm_strdup(", NULL")); }
-        | user_name '/' user_name
-            { $$ = cat_str(3, $1, mm_strdup(","), $3); }
-        | user_name SQL_IDENTIFIED BY user_name
-            { $$ = cat_str(3, $1, mm_strdup(","), $4); }
-        | user_name USING user_name
-            { $$ = cat_str(3, $1, mm_strdup(","), $3); }
-        ;
+    {
+        $$ = cat2_str($1, mm_strdup(", NULL"));
+    }
+    | user_name '/' user_name
+    {
+        $$ = cat_str(3, $1, mm_strdup(","), $3);
+    }
+    | user_name SQL_IDENTIFIED BY user_name
+    {
+        $$ = cat_str(3, $1, mm_strdup(","), $4);
+    }
+    | user_name USING user_name
+    {
+        $$ = cat_str(3, $1, mm_strdup(","), $3);
+    }
+    ;

 user_name: RoleId
-        {
-            if ($1[0] == '\"')
-                $$ = $1;
-            else
-                $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
-        }
-        | ecpg_sconst
-        {
-            if ($1[0] == '\"')
-                $$ = $1;
-            else
-                $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
-        }
-        | civar
-        {
-            enum ECPGttype type = argsinsert->variable->type->type;
+    {
+        if ($1[0] == '\"')
+            $$ = $1;
+        else
+            $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+    }
+    | ecpg_sconst
+    {
+        if ($1[0] == '\"')
+            $$ = $1;
+        else
+            $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+    }
+    | civar
+    {
+        enum ECPGttype type = argsinsert->variable->type->type;

-            /* if array see what's inside */
-            if (type == ECPGt_array)
-                type = argsinsert->variable->type->u.element->type;
+        /* if array see what's inside */
+        if (type == ECPGt_array)
+            type = argsinsert->variable->type->u.element->type;

-            /* handle varchars */
-            if (type == ECPGt_varchar)
-                $$ = make2_str(mm_strdup(argsinsert->variable->name), mm_strdup(".arr"));
-            else
-                $$ = mm_strdup(argsinsert->variable->name);
-        }
-        ;
+        /* handle varchars */
+        if (type == ECPGt_varchar)
+            $$ = make2_str(mm_strdup(argsinsert->variable->name), mm_strdup(".arr"));
+        else
+            $$ = mm_strdup(argsinsert->variable->name);
+    }
+    ;

 char_variable: cvariable
+    {
+        /* check if we have a string variable */
+        struct variable *p = find_variable($1);
+        enum ECPGttype type = p->type->type;
+
+        /* If we have just one character this is not a string */
+        if (atol(p->type->size) == 1)
+            mmerror(PARSE_ERROR, ET_ERROR, "invalid data type");
+        else
         {
-            /* check if we have a string variable */
-            struct variable *p = find_variable($1);
-            enum ECPGttype type = p->type->type;
+            /* if array see what's inside */
+            if (type == ECPGt_array)
+                type = p->type->u.element->type;

-            /* If we have just one character this is not a string */
-            if (atol(p->type->size) == 1)
-                    mmerror(PARSE_ERROR, ET_ERROR, "invalid data type");
-            else
+            switch (type)
             {
-                /* if array see what's inside */
-                if (type == ECPGt_array)
-                    type = p->type->u.element->type;
-
-                switch (type)
-                {
-                    case ECPGt_char:
-                    case ECPGt_unsigned_char:
-                    case ECPGt_string:
-                        $$ = $1;
-                        break;
-                    case ECPGt_varchar:
-                        $$ = make2_str($1, mm_strdup(".arr"));
-                        break;
-                    default:
-                        mmerror(PARSE_ERROR, ET_ERROR, "invalid data type");
-                        $$ = $1;
-                        break;
-                }
+                case ECPGt_char:
+                case ECPGt_unsigned_char:
+                case ECPGt_string:
+                    $$ = $1;
+                    break;
+                case ECPGt_varchar:
+                    $$ = make2_str($1, mm_strdup(".arr"));
+                    break;
+                default:
+                    mmerror(PARSE_ERROR, ET_ERROR, "invalid data type");
+                    $$ = $1;
+                    break;
             }
         }
-        ;
+    }
+    ;

 opt_options: Op connect_options
-        {
-            if (strlen($1) == 0)
-                mmerror(PARSE_ERROR, ET_ERROR, "incomplete statement");
+    {
+        if (strlen($1) == 0)
+            mmerror(PARSE_ERROR, ET_ERROR, "incomplete statement");

-            if (strcmp($1, "?") != 0)
-                mmerror(PARSE_ERROR, ET_ERROR, "unrecognized token \"%s\"", $1);
+        if (strcmp($1, "?") != 0)
+            mmerror(PARSE_ERROR, ET_ERROR, "unrecognized token \"%s\"", $1);

-            $$ = make2_str(mm_strdup("?"), $2);
-        }
-        | /*EMPTY*/    { $$ = EMPTY; }
-        ;
+        $$ = make2_str(mm_strdup("?"), $2);
+    }
+    | /* EMPTY */
+    {
+        $$ = EMPTY;
+    }
+    ;

-connect_options:  ColId opt_opt_value
-            {
-                $$ = make2_str($1, $2);
-            }
-        | ColId opt_opt_value Op connect_options
-            {
-                if (strlen($3) == 0)
-                    mmerror(PARSE_ERROR, ET_ERROR, "incomplete statement");
+connect_options: ColId opt_opt_value
+    {
+        $$ = make2_str($1, $2);
+    }
+    | ColId opt_opt_value Op connect_options
+    {
+        if (strlen($3) == 0)
+            mmerror(PARSE_ERROR, ET_ERROR, "incomplete statement");

-                if (strcmp($3, "&") != 0)
-                    mmerror(PARSE_ERROR, ET_ERROR, "unrecognized token \"%s\"", $3);
+        if (strcmp($3, "&") != 0)
+            mmerror(PARSE_ERROR, ET_ERROR, "unrecognized token \"%s\"", $3);

-                $$ = cat_str(3, make2_str($1, $2), $3, $4);
-            }
-        ;
-
-opt_opt_value: /*EMPTY*/
-            { $$ = EMPTY; }
-        | '=' Iconst
-            { $$ = make2_str(mm_strdup("="), $2); }
-        | '=' ecpg_ident
-            { $$ = make2_str(mm_strdup("="), $2); }
-        | '=' civar
-            { $$ = make2_str(mm_strdup("="), $2); }
-        ;
+        $$ = cat_str(3, make2_str($1, $2), $3, $4);
+    }
+    ;
+
+opt_opt_value: /* EMPTY */
+    {
+        $$ = EMPTY;
+    }
+    | '=' Iconst
+    {
+        $$ = make2_str(mm_strdup("="), $2);
+    }
+    | '=' ecpg_ident
+    {
+        $$ = make2_str(mm_strdup("="), $2);
+    }
+    | '=' civar
+    {
+        $$ = make2_str(mm_strdup("="), $2);
+    }
+    ;

 prepared_name: name
+    {
+        if ($1[0] == '\"' && $1[strlen($1) - 1] == '\"')    /* already quoted? */
+            $$ = $1;
+        else                    /* not quoted => convert to lowercase */
         {
-            if ($1[0] == '\"' && $1[strlen($1)-1] == '\"') /* already quoted? */
-                $$ = $1;
-            else /* not quoted => convert to lowercase */
-            {
-                size_t i;
+            size_t        i;

-                for (i = 0; i< strlen($1); i++)
-                    $1[i] = tolower((unsigned char) $1[i]);
+            for (i = 0; i < strlen($1); i++)
+                $1[i] = tolower((unsigned char) $1[i]);

-                $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
-            }
+            $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
         }
-        | char_variable { $$ = $1; }
-        ;
+    }
+    | char_variable
+    {
+        $$ = $1;
+    }
+    ;

 /*
  * Declare Statement
  */
 ECPGDeclareStmt: DECLARE prepared_name STATEMENT
+    {
+        struct declared_list *ptr = NULL;
+
+        /* Check whether the declared name has been defined or not */
+        for (ptr = g_declared_list; ptr != NULL; ptr = ptr->next)
         {
-            struct declared_list *ptr = NULL;
-            /* Check whether the declared name has been defined or not */
-            for (ptr = g_declared_list; ptr != NULL; ptr = ptr->next)
+            if (strcmp($2, ptr->name) == 0)
             {
-                if (strcmp($2, ptr->name) == 0)
-                {
-                    /* re-definition is not allowed */
-                    mmerror(PARSE_ERROR, ET_ERROR, "name \"%s\" is already declared", ptr->name);
-                }
+                /* re-definition is not allowed */
+                mmerror(PARSE_ERROR, ET_ERROR, "name \"%s\" is already declared", ptr->name);
             }
+        }

-            /* 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;
-                if (connection)
-                    ptr -> connection = mm_strdup(connection);
-                else
-                    ptr -> connection = NULL;
-
-                ptr -> next = g_declared_list;
-                g_declared_list = ptr;
-            }
+        /* 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;
+            if (connection)
+                ptr->connection = mm_strdup(connection);
+            else
+                ptr->connection = NULL;

-            $$ = cat_str(3 , mm_strdup("/* declare "), mm_strdup($2), mm_strdup(" as an SQL identifier */"));
+            ptr->next = g_declared_list;
+            g_declared_list = ptr;
         }
-;
+
+        $$ = cat_str(3, mm_strdup("/* declare "), mm_strdup($2), mm_strdup(" as an SQL identifier */"));
+    }
+    ;

 /*
  * Declare a prepared cursor. The syntax is different from the standard
  * declare statement, so we create a new rule.
  */
-ECPGCursorStmt:  DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared_name
-        {
-            struct cursor *ptr, *this;
-            char *cursor_marker = $2[0] == ':' ? mm_strdup("$0") : mm_strdup($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;
-            char *con;
-
-            if (INFORMIX_MODE && pg_strcasecmp($2, "database") == 0)
-                                mmfatal(PARSE_ERROR, "\"database\" cannot be used as cursor name in INFORMIX mode");
-
-                        check_declared_list($7);
-            con = connection ? connection : "NULL";
-            for (ptr = cur; ptr != NULL; ptr = ptr->next)
+ECPGCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared_name
+    {
+        struct cursor *ptr,
+                   *this;
+        char       *cursor_marker = $2[0] == ':' ? mm_strdup("$0") : mm_strdup($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;
+        char       *con;
+
+        if (INFORMIX_MODE && pg_strcasecmp($2, "database") == 0)
+            mmfatal(PARSE_ERROR, "\"database\" cannot be used as cursor name in INFORMIX mode");
+
+        check_declared_list($7);
+        con = connection ? connection : "NULL";
+        for (ptr = cur; ptr != NULL; ptr = ptr->next)
+        {
+            if (strcmp_fn($2, ptr->name) == 0)
             {
-                if (strcmp_fn($2, ptr->name) == 0)
-                {
-                    /* re-definition is a bug */
-                    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);
-                }
+                /* re-definition is a bug */
+                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 = (struct cursor *) mm_alloc(sizeof(struct cursor));

-            /* initial definition */
-            this->next = cur;
-            this->name = $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->argsresult = NULL;
-            this->argsresult_oos = NULL;
-
-            thisquery->type = &ecpg_query;
-            thisquery->brace_level = 0;
-            thisquery->next = NULL;
-            thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(, , __LINE__)") + strlen(con) +
strlen($7));
-            sprintf(thisquery->name, "ECPGprepared_statement(%s, %s, __LINE__)", con, $7);
-
-            this->argsinsert = NULL;
-            this->argsinsert_oos = NULL;
-            if ($2[0] == ':')
-            {
-                struct variable *var = find_variable($2 + 1);
-                remove_variable_from_list(&argsinsert, var);
-                add_variable_to_head(&(this->argsinsert), var, &no_indicator);
-            }
-            add_variable_to_head(&(this->argsinsert), thisquery, &no_indicator);
+        /* initial definition */
+        this->next = cur;
+        this->name = $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->argsresult = NULL;
+        this->argsresult_oos = NULL;

-            cur = this;
+        thisquery->type = &ecpg_query;
+        thisquery->brace_level = 0;
+        thisquery->next = NULL;
+        thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(, , __LINE__)") + strlen(con) +
strlen($7));
+        sprintf(thisquery->name, "ECPGprepared_statement(%s, %s, __LINE__)", con, $7);

-            comment = cat_str(3, mm_strdup("/*"), mm_strdup(this->command), mm_strdup("*/"));
+        this->argsinsert = NULL;
+        this->argsinsert_oos = NULL;
+        if ($2[0] == ':')
+        {
+            struct variable *var = find_variable($2 + 1);

-            $$ = cat_str(2, adjust_outofscope_cursor_vars(this),
-                    comment);
+            remove_variable_from_list(&argsinsert, var);
+            add_variable_to_head(&(this->argsinsert), var, &no_indicator);
         }
-        ;
+        add_variable_to_head(&(this->argsinsert), thisquery, &no_indicator);
+
+        cur = this;
+
+        comment = cat_str(3, mm_strdup("/*"), mm_strdup(this->command), mm_strdup("*/"));
+
+        $$ = cat_str(2, adjust_outofscope_cursor_vars(this),
+                     comment);
+    }
+    ;

 ECPGExecuteImmediateStmt: EXECUTE IMMEDIATE execstring
-            {
-              /* execute immediate means prepare the statement and
-               * immediately execute it */
-              $$ = $3;
-            };
+    {
+        /*
+         * execute immediate means prepare the statement and immediately
+         * execute it
+         */
+        $$ = $3;
+    }
+    ;
+
 /*
  * variable declaration outside exec sql declare block
  */
 ECPGVarDeclaration: single_vt_declaration;

-single_vt_declaration: type_declaration        { $$ = $1; }
-        | var_declaration        { $$ = $1; }
-        ;
-
-precision:    NumericOnly    { $$ = $1; };
-
-opt_scale:    ',' NumericOnly    { $$ = $2; }
-        | /* EMPTY */    { $$ = EMPTY; }
-        ;
+single_vt_declaration: type_declaration
+    {
+        $$ = $1;
+    }
+    | var_declaration
+    {
+        $$ = $1;
+    }
+    ;

-ecpg_interval:    opt_interval    { $$ = $1; }
-        | YEAR_P TO MINUTE_P    { $$ = mm_strdup("year to minute"); }
-        | YEAR_P TO SECOND_P    { $$ = mm_strdup("year to second"); }
-        | DAY_P TO DAY_P        { $$ = mm_strdup("day to day"); }
-        | MONTH_P TO MONTH_P    { $$ = mm_strdup("month to month"); }
-        ;
+precision: NumericOnly
+    {
+        $$ = $1;
+    }
+    ;
+
+opt_scale: ',' NumericOnly
+    {
+        $$ = $2;
+    }
+    | /* EMPTY */
+    {
+        $$ = EMPTY;
+    }
+    ;
+
+ecpg_interval: opt_interval            { $$ = $1; }
+    | YEAR_P TO MINUTE_P            { $$ = mm_strdup("year to minute"); }
+    | YEAR_P TO SECOND_P            { $$ = mm_strdup("year to second"); }
+    | DAY_P TO DAY_P                { $$ = mm_strdup("day to day"); }
+    | MONTH_P TO MONTH_P            { $$ = mm_strdup("month to month");    }
+    ;

 /*
  * variable declaration inside exec sql declare block
  */
 ECPGDeclaration: sql_startdeclare
-        { fputs("/* exec sql begin declare section */", base_yyout); }
-        var_type_declarations sql_enddeclare
-        {
-            fprintf(base_yyout, "%s/* exec sql end declare section */", $3);
-            free($3);
-            output_line_number();
-        }
-        ;
+    {
+        fputs("/* exec sql begin declare section */", base_yyout);
+    }
+    var_type_declarations sql_enddeclare
+    {
+        fprintf(base_yyout, "%s/* exec sql end declare section */", $3);
+        free($3);
+        output_line_number();
+    }
+    ;

-sql_startdeclare: ecpgstart BEGIN_P DECLARE SQL_SECTION ';' {};
+sql_startdeclare: ecpgstart BEGIN_P DECLARE SQL_SECTION ';'
+    {
+    }
+    ;

-sql_enddeclare: ecpgstart END_P DECLARE SQL_SECTION ';' {};
+sql_enddeclare: ecpgstart END_P DECLARE SQL_SECTION ';'
+    {
+    }
+    ;

-var_type_declarations:    /*EMPTY*/            { $$ = EMPTY; }
-        | vt_declarations            { $$ = $1; }
-        ;
+var_type_declarations: /* EMPTY */
+    {
+        $$ = EMPTY;
+    }
+    | vt_declarations
+    {
+        $$ = $1;
+    }
+    ;

-vt_declarations:  single_vt_declaration            { $$ = $1; }
-        | CPP_LINE                { $$ = $1; }
-        | vt_declarations single_vt_declaration    { $$ = cat2_str($1, $2); }
-        | vt_declarations CPP_LINE        { $$ = cat2_str($1, $2); }
-        ;
+vt_declarations: single_vt_declaration
+    {
+        $$ = $1;
+    }
+    | CPP_LINE
+    {
+        $$ = $1;
+    }
+    | vt_declarations single_vt_declaration
+    {
+        $$ = cat2_str($1, $2);
+    }
+    | vt_declarations CPP_LINE
+    {
+        $$ = cat2_str($1, $2);
+    }
+    ;

-variable_declarations:    var_declaration    { $$ = $1; }
-        | variable_declarations var_declaration    { $$ = cat2_str($1, $2); }
-        ;
+variable_declarations: var_declaration
+    {
+        $$ = $1;
+    }
+    | variable_declarations var_declaration
+    {
+        $$ = cat2_str($1, $2);
+    }
+    ;

 type_declaration: S_TYPEDEF
     {
@@ -467,750 +612,885 @@ type_declaration: S_TYPEDEF
         /* an initializer specified */
         initializer = 0;
     }
-    var_type opt_pointer ECPGColLabel opt_array_bounds ';'
+    var_type    opt_pointer ECPGColLabel opt_array_bounds ';'
     {
         add_typedef($5, $6.index1, $6.index2, $3.type_enum, $3.type_dimension, $3.type_index, initializer, *$4 ? 1 :
0);

         fprintf(base_yyout, "typedef %s %s %s %s;\n", $3.type_str, *$4 ? "*" : "", $5, $6.str);
         output_line_number();
         $$ = mm_strdup("");
-    };
+    }
+    ;

 var_declaration:
-        storage_declaration var_type
+    storage_declaration var_type
+    {
+        actual_type[struct_level].type_storage = $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_startline[struct_level] = hashline_number();
+    }
+    variable_list ';'
+    {
+        $$ = cat_str(5, actual_startline[struct_level], $1, $2.type_str, $4, mm_strdup(";\n"));
+    }
+    | var_type
+    {
+        actual_type[struct_level].type_storage = EMPTY;
+        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_startline[struct_level] = hashline_number();
+    }
+    variable_list ';'
+    {
+        $$ = cat_str(4, actual_startline[struct_level], $1.type_str, $3, mm_strdup(";\n"));
+    }
+    | struct_union_type_with_symbol ';'
+    {
+        $$ = cat2_str($1, mm_strdup(";"));
+    }
+    ;
+
+opt_bit_field: ':' Iconst
+    {
+        $$ = cat2_str(mm_strdup(":"), $2);
+    }
+    | /* EMPTY */
+    {
+        $$ = EMPTY;
+    }
+    ;
+
+storage_declaration: storage_clause storage_modifier
+    {
+        $$ = cat2_str($1, $2);
+    }
+    | storage_clause
+    {
+        $$ = $1;
+    }
+    | storage_modifier
+    {
+        $$ = $1;
+    }
+    ;
+
+storage_clause: S_EXTERN                { $$ = mm_strdup("extern"); }
+    | S_STATIC                            { $$ = mm_strdup("static"); }
+    | S_REGISTER                        { $$ = mm_strdup("register"); }
+    | S_AUTO                            { $$ = mm_strdup("auto"); }
+    ;
+
+storage_modifier: S_CONST                { $$ = mm_strdup("const"); }
+    | S_VOLATILE                        { $$ = mm_strdup("volatile"); }
+    ;
+
+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_sizeof = NULL;
+    }
+    | struct_union_type
+    {
+        $$.type_str = $1;
+        $$.type_dimension = mm_strdup("-1");
+        $$.type_index = mm_strdup("-1");
+
+        if (strncmp($1, "struct", sizeof("struct") - 1) == 0)
         {
-            actual_type[struct_level].type_storage = $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_startline[struct_level] = hashline_number();
+            $$.type_enum = ECPGt_struct;
+            $$.type_sizeof = ECPGstruct_sizeof;
         }
-        variable_list ';'
+        else
         {
-            $$ = cat_str(5, actual_startline[struct_level], $1, $2.type_str, $4, mm_strdup(";\n"));
+            $$.type_enum = ECPGt_union;
+            $$.type_sizeof = NULL;
         }
-        | var_type
+    }
+    | enum_type
+    {
+        $$.type_str = $1;
+        $$.type_enum = ECPGt_int;
+        $$.type_dimension = mm_strdup("-1");
+        $$.type_index = mm_strdup("-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_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_sizeof = NULL;
+    }
+    | IDENT '(' precision opt_scale ')'
+    {
+        /*
+         * In C parsing mode, NUMERIC and DECIMAL are not keywords, so they
+         * will show up here as a plain identifier, and we need this duplicate
+         * code to recognize them.
+         */
+        if (strcmp($1, "numeric") == 0)
         {
-            actual_type[struct_level].type_storage = EMPTY;
-            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_startline[struct_level] = hashline_number();
+            $$.type_enum = ECPGt_numeric;
+            $$.type_str = mm_strdup("numeric");
         }
-        variable_list ';'
+        else if (strcmp($1, "decimal") == 0)
         {
-            $$ = cat_str(4, actual_startline[struct_level], $1.type_str, $3, mm_strdup(";\n"));
+            $$.type_enum = ECPGt_decimal;
+            $$.type_str = mm_strdup("decimal");
         }
-        | struct_union_type_with_symbol ';'
+        else
         {
-            $$ = cat2_str($1, mm_strdup(";"));
+            mmerror(PARSE_ERROR, ET_ERROR, "only data types numeric and decimal have precision/scale argument");
+            $$.type_enum = ECPGt_numeric;
+            $$.type_str = mm_strdup("numeric");
         }
-        ;
-
-opt_bit_field:    ':' Iconst    { $$ =cat2_str(mm_strdup(":"), $2); }
-        | /* EMPTY */    { $$ = EMPTY; }
-        ;

-storage_declaration: storage_clause storage_modifier
-            {$$ = cat2_str ($1, $2); }
-        | storage_clause        {$$ = $1; }
-        | storage_modifier        {$$ = $1; }
-        ;
-
-storage_clause : S_EXTERN    { $$ = mm_strdup("extern"); }
-        | S_STATIC            { $$ = mm_strdup("static"); }
-        | S_REGISTER        { $$ = mm_strdup("register"); }
-        | S_AUTO            { $$ = mm_strdup("auto"); }
-        ;
-
-storage_modifier : S_CONST    { $$ = mm_strdup("const"); }
-        | S_VOLATILE        { $$ = mm_strdup("volatile"); }
-        ;
-
-var_type:    simple_type
+        $$.type_dimension = mm_strdup("-1");
+        $$.type_index = mm_strdup("-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_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_sizeof = NULL;
+    }
+    | NUMERIC
+    {
+        $$.type_enum = ECPGt_numeric;
+        $$.type_str = mm_strdup("numeric");
+        $$.type_dimension = mm_strdup("-1");
+        $$.type_index = mm_strdup("-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_sizeof = NULL;
+    }
+    | TIMESTAMP
+    {
+        $$.type_enum = ECPGt_timestamp;
+        $$.type_str = mm_strdup("timestamp");
+        $$.type_dimension = mm_strdup("-1");
+        $$.type_index = mm_strdup("-1");
+        $$.type_sizeof = NULL;
+    }
+    | STRING_P
+    {
+        if (INFORMIX_MODE)
         {
-            $$.type_enum = $1;
-            $$.type_str = mm_strdup(ecpg_type_name($1));
+            /* 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_sizeof = NULL;
         }
-        | struct_union_type
+        else
         {
-            $$.type_str = $1;
-            $$.type_dimension = mm_strdup("-1");
-            $$.type_index = mm_strdup("-1");
+            /* Otherwise, legal only if user typedef'ed it */
+            struct typedefs *this = get_typedef("string", false);

-            if (strncmp($1, "struct", sizeof("struct")-1) == 0)
-            {
-                $$.type_enum = ECPGt_struct;
-                $$.type_sizeof = ECPGstruct_sizeof;
-            }
+            $$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? EMPTY :
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_enum = ECPGt_union;
-                $$.type_sizeof = NULL;
-            }
+                $$.type_sizeof = cat_str(3, mm_strdup("sizeof("), mm_strdup(this->name), mm_strdup(")"));
+
+            struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
         }
-        | enum_type
+    }
+    | INTERVAL ecpg_interval
+    {
+        $$.type_enum = ECPGt_interval;
+        $$.type_str = mm_strdup("interval");
+        $$.type_dimension = mm_strdup("-1");
+        $$.type_index = mm_strdup("-1");
+        $$.type_sizeof = NULL;
+    }
+    | IDENT ecpg_interval
+    {
+        /*
+         * In C parsing mode, the above SQL type names are not keywords, so
+         * they will show up here as a plain identifier, and we need this
+         * duplicate code to recognize them.
+         *
+         * Note that we also handle the type names bytea, date, and datetime
+         * here, but not above because those are not currently SQL keywords.
+         * If they ever become so, they must gain duplicate productions above.
+         */
+        if (strlen($2) != 0 && strcmp($1, "datetime") != 0 && strcmp($1, "interval") != 0)
+            mmerror(PARSE_ERROR, ET_ERROR, "interval specification not allowed here");
+
+        if (strcmp($1, "varchar") == 0)
         {
-            $$.type_str = $1;
-            $$.type_enum = ECPGt_int;
+            $$.type_enum = ECPGt_varchar;
+            $$.type_str = EMPTY;    /* mm_strdup("varchar"); */
             $$.type_dimension = mm_strdup("-1");
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        | NUMERIC '(' precision opt_scale ')'
+        else if (strcmp($1, "bytea") == 0)
         {
-            $$.type_enum = ECPGt_numeric;
-            $$.type_str = mm_strdup("numeric");
+            $$.type_enum = ECPGt_bytea;
+            $$.type_str = EMPTY;
             $$.type_dimension = mm_strdup("-1");
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        | DECIMAL_P '(' precision opt_scale ')'
+        else if (strcmp($1, "float") == 0)
         {
-            $$.type_enum = ECPGt_decimal;
-            $$.type_str = mm_strdup("decimal");
+            $$.type_enum = ECPGt_float;
+            $$.type_str = mm_strdup("float");
             $$.type_dimension = mm_strdup("-1");
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        | IDENT '(' precision opt_scale ')'
+        else if (strcmp($1, "double") == 0)
         {
-            /*
-             * In C parsing mode, NUMERIC and DECIMAL are not keywords, so
-             * they will show up here as a plain identifier, and we need
-             * this duplicate code to recognize them.
-             */
-            if (strcmp($1, "numeric") == 0)
-            {
-                $$.type_enum = ECPGt_numeric;
-                $$.type_str = mm_strdup("numeric");
-            }
-            else if (strcmp($1, "decimal") == 0)
-            {
-                $$.type_enum = ECPGt_decimal;
-                $$.type_str = mm_strdup("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_enum = ECPGt_double;
+            $$.type_str = mm_strdup("double");
             $$.type_dimension = mm_strdup("-1");
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        | VARCHAR
+        else if (strcmp($1, "numeric") == 0)
         {
-            $$.type_enum = ECPGt_varchar;
-            $$.type_str = EMPTY; /*mm_strdup("varchar");*/
+            $$.type_enum = ECPGt_numeric;
+            $$.type_str = mm_strdup("numeric");
             $$.type_dimension = mm_strdup("-1");
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        | FLOAT_P
+        else if (strcmp($1, "decimal") == 0)
         {
-            /* Note: DOUBLE is handled in simple_type */
-            $$.type_enum = ECPGt_float;
-            $$.type_str = mm_strdup("float");
+            $$.type_enum = ECPGt_decimal;
+            $$.type_str = mm_strdup("decimal");
             $$.type_dimension = mm_strdup("-1");
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        | NUMERIC
+        else if (strcmp($1, "date") == 0)
         {
-            $$.type_enum = ECPGt_numeric;
-            $$.type_str = mm_strdup("numeric");
+            $$.type_enum = ECPGt_date;
+            $$.type_str = mm_strdup("date");
             $$.type_dimension = mm_strdup("-1");
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        | DECIMAL_P
+        else if (strcmp($1, "timestamp") == 0)
         {
-            $$.type_enum = ECPGt_decimal;
-            $$.type_str = mm_strdup("decimal");
+            $$.type_enum = ECPGt_timestamp;
+            $$.type_str = mm_strdup("timestamp");
             $$.type_dimension = mm_strdup("-1");
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        | TIMESTAMP
+        else if (strcmp($1, "interval") == 0)
         {
-            $$.type_enum = ECPGt_timestamp;
-            $$.type_str = mm_strdup("timestamp");
+            $$.type_enum = ECPGt_interval;
+            $$.type_str = mm_strdup("interval");
             $$.type_dimension = mm_strdup("-1");
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        | STRING_P
-        {
-            if (INFORMIX_MODE)
-            {
-                /* 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_sizeof = NULL;
-            }
-            else
-            {
-                /* 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_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(")"));
-
-                struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
-            }
-        }
-        | INTERVAL ecpg_interval
+        else if (strcmp($1, "datetime") == 0)
         {
-            $$.type_enum = ECPGt_interval;
-            $$.type_str = mm_strdup("interval");
+            $$.type_enum = ECPGt_timestamp;
+            $$.type_str = mm_strdup("timestamp");
             $$.type_dimension = mm_strdup("-1");
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        | IDENT ecpg_interval
+        else if ((strcmp($1, "string") == 0) && INFORMIX_MODE)
         {
-            /*
-             * In C parsing mode, the above SQL type names are not keywords,
-             * so they will show up here as a plain identifier, and we need
-             * this duplicate code to recognize them.
-             *
-             * Note that we also handle the type names bytea, date, and
-             * datetime here, but not above because those are not currently
-             * SQL keywords.  If they ever become so, they must gain duplicate
-             * productions above.
-             */
-            if (strlen($2) != 0 && strcmp ($1, "datetime") != 0 && strcmp ($1, "interval") != 0)
-                mmerror (PARSE_ERROR, ET_ERROR, "interval specification not allowed here");
-
-            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_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_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_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_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_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_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_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_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_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_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_sizeof = NULL;
-            }
-            else
-            {
-                /* 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_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(")"));
-
-                struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
-            }
+            $$.type_enum = ECPGt_string;
+            $$.type_str = mm_strdup("char");
+            $$.type_dimension = mm_strdup("-1");
+            $$.type_index = mm_strdup("-1");
+            $$.type_sizeof = NULL;
         }
-        | s_struct_union_symbol
+        else
         {
-            /* this is for named structs/unions */
-            char *name;
-            struct typedefs *this;
-            bool forward = (forward_name != NULL && strcmp($1.symbol, forward_name) == 0 && strcmp($1.su, "struct") ==
0);
-
-            name = cat2_str($1.su, $1.symbol);
-            /* Do we have a forward definition? */
-            if (!forward)
-            {
-                /* No */
+            /* Otherwise, it must be a user-defined typedef name */
+            struct typedefs *this = get_typedef($1, false);

-                this = get_typedef(name, false);
-                $$.type_str = mm_strdup(this->name);
-                $$.type_enum = this->type->type_enum;
-                $$.type_dimension = this->type->type_dimension;
-                $$.type_index = this->type->type_index;
+            $$.type_str = (this->type->type_enum == ECPGt_varchar || this->type->type_enum == ECPGt_bytea) ? EMPTY :
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;
-                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("");
-                struct_member_list[struct_level] = NULL;
-            }
+                $$.type_sizeof = cat_str(3, mm_strdup("sizeof("), mm_strdup(this->name), mm_strdup(")"));
+
+            struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
+        }
+    }
+    | s_struct_union_symbol
+    {
+        /* this is for named structs/unions */
+        char       *name;
+        struct typedefs *this;
+        bool        forward = (forward_name != NULL && strcmp($1.symbol, forward_name) == 0 && strcmp($1.su, "struct")
==0); 
+
+        name = cat2_str($1.su, $1.symbol);
+        /* Do we have a forward definition? */
+        if (!forward)
+        {
+            /* No */
+
+            this = get_typedef(name, false);
+            $$.type_str = mm_strdup(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("");
+            struct_member_list[struct_level] = NULL;
         }
-        ;
+    }
+    ;

 enum_type: ENUM_P symbol enum_definition
-            { $$ = cat_str(3, mm_strdup("enum"), $2, $3); }
-        | ENUM_P enum_definition
-            { $$ = cat2_str(mm_strdup("enum"), $2); }
-        | ENUM_P symbol
-            { $$ = cat2_str(mm_strdup("enum"), $2); }
-        ;
+    {
+        $$ = cat_str(3, mm_strdup("enum"), $2, $3);
+    }
+    | ENUM_P enum_definition
+    {
+        $$ = cat2_str(mm_strdup("enum"), $2);
+    }
+    | ENUM_P symbol
+    {
+        $$ = cat2_str(mm_strdup("enum"), $2);
+    }
+    ;

 enum_definition: '{' c_list '}'
-            { $$ = cat_str(3, mm_strdup("{"), $2, mm_strdup("}")); };
+    {
+        $$ = cat_str(3, mm_strdup("{"), $2, mm_strdup("}"));
+    }
+    ;

 struct_union_type_with_symbol: s_struct_union_symbol
-        {
-            struct_member_list[struct_level++] = NULL;
-            if (struct_level >= STRUCT_DEPTH)
-                 mmerror(PARSE_ERROR, ET_ERROR, "too many levels in nested structure/union definition");
-            forward_name = mm_strdup($1.symbol);
-        }
-        '{' variable_declarations '}'
-        {
-            struct typedefs *ptr, *this;
-            struct this_type su_type;
-
-            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)
-                su_type.type_enum = ECPGt_struct;
-            else
-                su_type.type_enum = ECPGt_union;
-            su_type.type_str = cat2_str($1.su, $1.symbol);
-            free(forward_name);
-            forward_name = NULL;
-
-            /* This is essentially a typedef but needs the keyword struct/union as well.
-             * So we create the typedef for each struct definition with symbol */
-            for (ptr = types; ptr != NULL; ptr = ptr->next)
-            {
-                    if (strcmp(su_type.type_str, ptr->name) == 0)
-                            /* re-definition is a bug */
-                            mmerror(PARSE_ERROR, ET_ERROR, "type \"%s\" is already defined", su_type.type_str);
-            }
-
-            this = (struct typedefs *) mm_alloc(sizeof(struct typedefs));
-
-            /* initial definition */
-            this->next = types;
-            this->name = mm_strdup(su_type.type_str);
-            this->brace_level = braces_open;
-            this->type = (struct this_type *) mm_alloc(sizeof(struct this_type));
-            this->type->type_enum = su_type.type_enum;
-            this->type->type_str = mm_strdup(su_type.type_str);
-            this->type->type_dimension = mm_strdup("-1"); /* dimension of array */
-            this->type->type_index = mm_strdup("-1");    /* length of string */
-            this->type->type_sizeof = ECPGstruct_sizeof;
-            this->struct_member_list = struct_member_list[struct_level];
-
-            types = this;
-            $$ = cat_str(4, su_type.type_str, mm_strdup("{"), $4, mm_strdup("}"));
-        }
-        ;
+    {
+        struct_member_list[struct_level++] = NULL;
+        if (struct_level >= STRUCT_DEPTH)
+            mmerror(PARSE_ERROR, ET_ERROR, "too many levels in nested structure/union definition");
+        forward_name = mm_strdup($1.symbol);
+    }
+    '{' variable_declarations '}'
+    {
+        struct typedefs *ptr,
+                   *this;
+        struct this_type su_type;
+
+        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)
+            su_type.type_enum = ECPGt_struct;
+        else
+            su_type.type_enum = ECPGt_union;
+        su_type.type_str = cat2_str($1.su, $1.symbol);
+        free(forward_name);
+        forward_name = NULL;
+
+        /*
+         * This is essentially a typedef but needs the keyword struct/union as
+         * well. So we create the typedef for each struct definition with
+         * symbol
+         */
+        for (ptr = types; ptr != NULL; ptr = ptr->next)
+        {
+            if (strcmp(su_type.type_str, ptr->name) == 0)
+                /* re-definition is a bug */
+                mmerror(PARSE_ERROR, ET_ERROR, "type \"%s\" is already defined", su_type.type_str);
+        }
+
+        this = (struct typedefs *) mm_alloc(sizeof(struct typedefs));
+
+        /* initial definition */
+        this->next = types;
+        this->name = mm_strdup(su_type.type_str);
+        this->brace_level = braces_open;
+        this->type = (struct this_type *) mm_alloc(sizeof(struct this_type));
+        this->type->type_enum = su_type.type_enum;
+        this->type->type_str = mm_strdup(su_type.type_str);
+        this->type->type_dimension = mm_strdup("-1");    /* dimension of array */
+        this->type->type_index = mm_strdup("-1");    /* length of string */
+        this->type->type_sizeof = ECPGstruct_sizeof;
+        this->struct_member_list = struct_member_list[struct_level];
+
+        types = this;
+        $$ = cat_str(4, su_type.type_str, mm_strdup("{"), $4, mm_strdup("}"));
+    }
+    ;

-struct_union_type: struct_union_type_with_symbol    { $$ = $1; }
-        | s_struct_union
-        {
-            struct_member_list[struct_level++] = NULL;
-            if (struct_level >= STRUCT_DEPTH)
-                 mmerror(PARSE_ERROR, ET_ERROR, "too many levels in nested structure/union definition");
-        }
-        '{' variable_declarations '}'
-        {
-            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("}"));
-        }
-        ;
+struct_union_type: struct_union_type_with_symbol
+    {
+        $$ = $1;
+    }
+    | s_struct_union
+    {
+        struct_member_list[struct_level++] = NULL;
+        if (struct_level >= STRUCT_DEPTH)
+            mmerror(PARSE_ERROR, ET_ERROR, "too many levels in nested structure/union definition");
+    }
+    '{' variable_declarations '}'
+    {
+        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("}"));
+    }
+    ;

 s_struct_union_symbol: SQL_STRUCT symbol
-        {
-            $$.su = mm_strdup("struct");
-            $$.symbol = $2;
-            ECPGstruct_sizeof = cat_str(3, mm_strdup("sizeof("), cat2_str(mm_strdup($$.su), mm_strdup($$.symbol)),
mm_strdup(")"));
-        }
-        | UNION symbol
-        {
-            $$.su = mm_strdup("union");
-            $$.symbol = $2;
-        }
-        ;
+    {
+        $$.su = mm_strdup("struct");
+        $$.symbol = $2;
+        ECPGstruct_sizeof = cat_str(3, mm_strdup("sizeof("), cat2_str(mm_strdup($$.su), mm_strdup($$.symbol)),
mm_strdup(")"));
+    }
+    | UNION symbol
+    {
+        $$.su = mm_strdup("union");
+        $$.symbol = $2;
+    }
+    ;

 s_struct_union: SQL_STRUCT
-        {
-            ECPGstruct_sizeof = mm_strdup(""); /* This must not be NULL to distinguish from simple types. */
-            $$ = mm_strdup("struct");
-        }
-        | UNION
-        {
-            $$ = mm_strdup("union");
-        }
-        ;
-
-simple_type: unsigned_type                    { $$=$1; }
-        |    opt_signed signed_type            { $$=$2; }
-        ;
-
-unsigned_type: SQL_UNSIGNED SQL_SHORT        { $$ = ECPGt_unsigned_short; }
-        | SQL_UNSIGNED SQL_SHORT INT_P    { $$ = ECPGt_unsigned_short; }
-        | SQL_UNSIGNED                        { $$ = ECPGt_unsigned_int; }
-        | SQL_UNSIGNED INT_P                { $$ = ECPGt_unsigned_int; }
-        | SQL_UNSIGNED SQL_LONG                { $$ = ECPGt_unsigned_long; }
-        | SQL_UNSIGNED SQL_LONG INT_P        { $$ = ECPGt_unsigned_long; }
-        | SQL_UNSIGNED SQL_LONG SQL_LONG    { $$ = ECPGt_unsigned_long_long; }
-        | SQL_UNSIGNED SQL_LONG SQL_LONG INT_P { $$ = ECPGt_unsigned_long_long; }
-        | SQL_UNSIGNED CHAR_P            { $$ = ECPGt_unsigned_char; }
-        ;
-
-signed_type: SQL_SHORT                { $$ = ECPGt_short; }
-        | SQL_SHORT INT_P            { $$ = ECPGt_short; }
-        | INT_P                        { $$ = ECPGt_int; }
-        | SQL_LONG                    { $$ = ECPGt_long; }
-        | SQL_LONG INT_P            { $$ = ECPGt_long; }
-        | SQL_LONG SQL_LONG            { $$ = ECPGt_long_long; }
-        | SQL_LONG SQL_LONG INT_P    { $$ = ECPGt_long_long; }
-        | SQL_BOOL                    { $$ = ECPGt_bool; }
-        | CHAR_P                    { $$ = ECPGt_char; }
-        | DOUBLE_P                    { $$ = ECPGt_double; }
-        ;
+    {
+        ECPGstruct_sizeof = mm_strdup("");    /* This must not be NULL to
+                                             * distinguish from simple types. */
+        $$ = mm_strdup("struct");
+    }
+    | UNION
+    {
+        $$ = mm_strdup("union");
+    }
+    ;
+
+simple_type: unsigned_type                { $$ = $1; }
+    | opt_signed signed_type            { $$ = $2; }
+    ;
+
+unsigned_type: SQL_UNSIGNED SQL_SHORT    { $$ = ECPGt_unsigned_short; }
+    | SQL_UNSIGNED SQL_SHORT INT_P        { $$ = ECPGt_unsigned_short; }
+    | SQL_UNSIGNED                        { $$ = ECPGt_unsigned_int; }
+    | SQL_UNSIGNED INT_P                { $$ = ECPGt_unsigned_int; }
+    | SQL_UNSIGNED SQL_LONG                { $$ = ECPGt_unsigned_long; }
+    | SQL_UNSIGNED SQL_LONG INT_P        { $$ = ECPGt_unsigned_long; }
+    | SQL_UNSIGNED SQL_LONG SQL_LONG    { $$ = ECPGt_unsigned_long_long; }
+    | SQL_UNSIGNED SQL_LONG SQL_LONG INT_P    { $$ = ECPGt_unsigned_long_long; }
+    | SQL_UNSIGNED CHAR_P                { $$ = ECPGt_unsigned_char; }
+    ;
+
+signed_type: SQL_SHORT                    { $$ = ECPGt_short; }
+    | SQL_SHORT INT_P                    { $$ = ECPGt_short; }
+    | INT_P                                { $$ = ECPGt_int; }
+    | SQL_LONG                            { $$ = ECPGt_long; }
+    | SQL_LONG INT_P                    { $$ = ECPGt_long; }
+    | SQL_LONG SQL_LONG                    { $$ = ECPGt_long_long; }
+    | SQL_LONG SQL_LONG INT_P            { $$ = ECPGt_long_long; }
+    | SQL_BOOL                            { $$ = ECPGt_bool; }
+    | CHAR_P                            { $$ = ECPGt_char; }
+    | DOUBLE_P                            { $$ = ECPGt_double; }
+    ;

 opt_signed: SQL_SIGNED
-        |    /* EMPTY */
-        ;
+    | /* EMPTY */
+    ;

 variable_list: variable
-            { $$ = $1; }
-        | 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);
-            else
-                $$ = cat_str(3, $1, mm_strdup(","), $3);
-        }
-        ;
+    {
+        $$ = $1;
+    }
+    | 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);
+        else
+            $$ = cat_str(3, $1, mm_strdup(","), $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 */
-            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); 
-            switch (actual_type[struct_level].type_enum)
-            {
-                case ECPGt_struct:
-                case ECPGt_union:
-                    if (atoi(dimension) < 0)
-                        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); 
-                    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);
+    {
+        struct ECPGtype *type;
+        char       *dimension = $3.index1;    /* dimension of array */
+        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); 
+        switch (actual_type[struct_level].type_enum)
+        {
+            case ECPGt_struct:
+            case ECPGt_union:
+                if (atoi(dimension) < 0)
+                    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); 
+                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);
-                    break;
+                $$ = cat_str(5, $1, mm_strdup($2), $3.str, $4, $5);
+                break;

-                case ECPGt_varchar:
-                case ECPGt_bytea:
-                    if (actual_type[struct_level].type_enum == ECPGt_varchar)
-                    {
-                        varlen_type_counter = &varchar_counter;
-                        struct_name = " struct varchar_";
-                    }
-                    else
-                    {
-                        varlen_type_counter = &bytea_counter;
-                        struct_name = " struct bytea_";
-                    }
-                    if (atoi(dimension) < 0)
-                        type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length,
*varlen_type_counter);
-                    else
-                        type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length,
*varlen_type_counter),dimension); 
+            case ECPGt_varchar:
+            case ECPGt_bytea:
+                if (actual_type[struct_level].type_enum == ECPGt_varchar)
+                {
+                    varlen_type_counter = &varchar_counter;
+                    struct_name = " struct varchar_";
+                }
+                else
+                {
+                    varlen_type_counter = &bytea_counter;
+                    struct_name = " struct bytea_";
+                }
+                if (atoi(dimension) < 0)
+                    type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length, *varlen_type_counter);
+                else
+                    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("");
-                    else
-                            dim_str=cat_str(3, mm_strdup("["), mm_strdup(dimension), mm_strdup("]"));
-                    /* cannot check for atoi <= 0 because a defined constant will yield 0 here as well */
-                    if (atoi(length) < 0 || strcmp(length, "0") == 0)
-                        mmerror(PARSE_ERROR, ET_ERROR, "pointers to varchar are not implemented");
-
-                    /* 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);
-                    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); 
-                    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); 
-                    (*varlen_type_counter)++;
-                    break;
+                if (strcmp(dimension, "0") == 0 || abs(atoi(dimension)) == 1)
+                    dim_str = mm_strdup("");
+                else
+                    dim_str = cat_str(3, mm_strdup("["), mm_strdup(dimension), mm_strdup("]"));
+
+                /*
+                 * cannot check for atoi <= 0 because a defined constant will
+                 * yield 0 here as well
+                 */
+                if (atoi(length) < 0 || strcmp(length, "0") == 0)
+                    mmerror(PARSE_ERROR, ET_ERROR, "pointers to varchar are not implemented");
+
+                /*
+                 * 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);
+                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); 
+                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); 
+                (*varlen_type_counter)++;
+                break;
+
+            case ECPGt_char:
+            case ECPGt_unsigned_char:
+            case ECPGt_string:
+                if (atoi(dimension) == -1)
+                {
+                    int            i = strlen($5);

-                case ECPGt_char:
-                case ECPGt_unsigned_char:
-                case ECPGt_string:
-                    if (atoi(dimension) == -1)
+                    if (atoi(length) == -1 && i > 0)    /* char <var>[] =
+                                                         * "string" */
                     {
-                        int i = strlen($5);
-
-                        if (atoi(length) == -1 && i > 0) /* char <var>[] = "string" */
-                        {
-                            /* 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);
-                        }
-                        type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length, 0);
+                        /*
+                         * 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);
                     }
-                    else
-                        type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length,
0),dimension); 
+                    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);
-                    break;
+                $$ = cat_str(5, $1, mm_strdup($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);
-                    else
-                        type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum,
mm_strdup("1"),0), dimension); 
+            default:
+                if (atoi(dimension) < 0)
+                    type = ECPGmake_simple_type(actual_type[struct_level].type_enum, mm_strdup("1"), 0);
+                else
+                    type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum,
mm_strdup("1"),0), dimension); 

-                    $$ = cat_str(5, $1, mm_strdup($2), $3.str, $4, $5);
-                    break;
-            }
+                $$ = cat_str(5, $1, mm_strdup($2), $3.str, $4, $5);
+                break;
+        }

-            if (struct_level == 0)
-                new_variable($2, type, braces_open);
-            else
-                ECPGmake_struct_member($2, type, &(struct_member_list[struct_level - 1]));
+        if (struct_level == 0)
+            new_variable($2, type, braces_open);
+        else
+            ECPGmake_struct_member($2, type, &(struct_member_list[struct_level - 1]));

-            free($2);
-        }
-        ;
+        free($2);
+    }
+    ;

-opt_initializer: /*EMPTY*/
-            { $$ = EMPTY; }
-        | '=' c_term
-        {
-            initializer = 1;
-            $$ = cat2_str(mm_strdup("="), $2);
-        }
-        ;
+opt_initializer: /* EMPTY */
+    {
+        $$ = EMPTY;
+    }
+    | '=' c_term
+    {
+        initializer = 1;
+        $$ = cat2_str(mm_strdup("="), $2);
+    }
+    ;

-opt_pointer: /*EMPTY*/                { $$ = EMPTY; }
-        | '*'                        { $$ = mm_strdup("*"); }
-        | '*' '*'                    { $$ = mm_strdup("**"); }
-        ;
+opt_pointer: /* EMPTY */
+    {
+        $$ = EMPTY;
+    }
+    | '*'
+    {
+        $$ = mm_strdup("*");
+    }
+    | '*' '*'
+    {
+        $$ = mm_strdup("**");
+    }
+    ;

 /*
  * We try to simulate the correct DECLARE syntax here so we get dynamic SQL
  */
 ECPGDeclare: DECLARE STATEMENT ecpg_ident
-        {
-            /* this is only supported for compatibility */
-            $$ = cat_str(3, mm_strdup("/* declare statement"), $3, mm_strdup("*/"));
-        }
-        ;
+    {
+        /* this is only supported for compatibility */
+        $$ = cat_str(3, mm_strdup("/* declare statement"), $3, mm_strdup("*/"));
+    }
+    ;
 /*
  * the exec sql disconnect statement: disconnect from the given database
  */
-ECPGDisconnect: SQL_DISCONNECT dis_name { $$ = $2; }
-        ;
+ECPGDisconnect: SQL_DISCONNECT dis_name
+    {
+        $$ = $2;
+    }
+    ;

-dis_name: connection_object            { $$ = $1; }
-        | CURRENT_P            { $$ = mm_strdup("\"CURRENT\""); }
-        | ALL                { $$ = mm_strdup("\"ALL\""); }
-        | /* EMPTY */            { $$ = mm_strdup("\"CURRENT\""); }
-        ;
+dis_name: connection_object
+    {
+        $$ = $1;
+    }
+    | CURRENT_P
+    {
+        $$ = mm_strdup("\"CURRENT\"");
+    }
+    | ALL
+    {
+        $$ = mm_strdup("\"ALL\"");
+    }
+    | /* EMPTY */
+    {
+        $$ = mm_strdup("\"CURRENT\"");
+    }
+    ;

-connection_object: name                { $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\"")); }
-        | DEFAULT            { $$ = mm_strdup("\"DEFAULT\""); }
-        | char_variable            { $$ = $1; }
-        ;
+connection_object: name
+    {
+        $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+    }
+    | DEFAULT
+    {
+        $$ = mm_strdup("\"DEFAULT\"");
+    }
+    | char_variable
+    {
+        $$ = $1;
+    }
+    ;

 execstring: char_variable
-            { $$ = $1; }
-        |    CSTRING
-            { $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\"")); }
-        ;
+    {
+        $$ = $1;
+    }
+    | CSTRING
+    {
+        $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+    }
+    ;

 /*
  * the exec sql free command to deallocate a previously
  * prepared statement
  */
-ECPGFree:    SQL_FREE cursor_name    { $$ = $2; }
-        | SQL_FREE ALL    { $$ = mm_strdup("all"); }
-        ;
+ECPGFree: SQL_FREE cursor_name
+    {
+        $$ = $2;
+    }
+    | SQL_FREE ALL
+    {
+        $$ = mm_strdup("all");
+    }
+    ;

 /*
  * open is an open cursor, at the moment this has to be removed
  */
 ECPGOpen: SQL_OPEN cursor_name opt_ecpg_using
-        {
-            if ($2[0] == ':')
-                remove_variable_from_list(&argsinsert, find_variable($2 + 1));
-            $$ = $2;
-        }
-        ;
+    {
+        if ($2[0] == ':')
+            remove_variable_from_list(&argsinsert, find_variable($2 + 1));
+        $$ = $2;
+    }
+    ;

-opt_ecpg_using: /*EMPTY*/    { $$ = EMPTY; }
-        | ecpg_using        { $$ = $1; }
-        ;
+opt_ecpg_using: /* EMPTY */
+    {
+        $$ = EMPTY;
+    }
+    | ecpg_using
+    {
+        $$ = $1;
+    }
+    ;

-ecpg_using:    USING using_list    { $$ = EMPTY; }
-        | using_descriptor        { $$ = $1; }
-        ;
+ecpg_using: USING using_list
+    {
+        $$ = EMPTY;
+    }
+    | using_descriptor
+    {
+        $$ = $1;
+    }
+    ;

 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;
-        }
-        ;
+    {
+        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;
-        }
-        ;
+    {
+        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;
-        }
-        ;
+    {
+        add_variable_to_head(&argsresult, sqlda_variable($2), &no_indicator);
+        $$ = EMPTY;
+    }
+    ;

-using_list: UsingValue | UsingValue ',' using_list;
+using_list: UsingValue | UsingValue ',' using_list
+    ;

 UsingValue: UsingConst
-        {
-            char *length = mm_alloc(32);
+    {
+        char       *length = mm_alloc(32);

-            sprintf(length, "%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; }
-        ;
-
-UsingConst: Iconst            { $$ = $1; }
-        | '+' Iconst        { $$ = cat_str(2, mm_strdup("+"), $2); }
-        | '-' Iconst        { $$ = cat_str(2, mm_strdup("-"), $2); }
-        | ecpg_fconst        { $$ = $1; }
-        | '+' ecpg_fconst    { $$ = cat_str(2, mm_strdup("+"), $2); }
-        | '-' ecpg_fconst    { $$ = cat_str(2, mm_strdup("-"), $2); }
-        | ecpg_sconst        { $$ = $1; }
-        | ecpg_bconst        { $$ = $1; }
-        | ecpg_xconst        { $$ = $1; }
-        ;
+        sprintf(length, "%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;
+    }
+    ;
+
+UsingConst: Iconst
+    {
+        $$ = $1;
+    }
+    | '+' Iconst
+    {
+        $$ = cat_str(2, mm_strdup("+"), $2);
+    }
+    | '-' Iconst
+    {
+        $$ = cat_str(2, mm_strdup("-"), $2);
+    }
+    | ecpg_fconst
+    {
+        $$ = $1;
+    }
+    | '+' ecpg_fconst
+    {
+        $$ = cat_str(2, mm_strdup("+"), $2);
+    }
+    | '-' ecpg_fconst
+    {
+        $$ = cat_str(2, mm_strdup("-"), $2);
+    }
+    | ecpg_sconst
+    {
+        $$ = $1;
+    }
+    | ecpg_bconst
+    {
+        $$ = $1;
+    }
+    | ecpg_xconst
+    {
+        $$ = $1;
+    }
+    ;

 /*
  * We accept DESCRIBE [OUTPUT] but do nothing with DESCRIBE INPUT so far.
@@ -1223,6 +1503,7 @@ ECPGDescribe: SQL_DESCRIBE INPUT_P prepared_name using_descriptor
     | SQL_DESCRIBE opt_output prepared_name using_descriptor
     {
         struct variable *var;
+
         var = argsinsert->variable;
         remove_variable_from_list(&argsinsert, var);
         add_variable_to_head(&argsresult, var, &no_indicator);
@@ -1247,8 +1528,14 @@ ECPGDescribe: SQL_DESCRIBE INPUT_P prepared_name using_descriptor
     }
     ;

-opt_output:    SQL_OUTPUT    { $$ = mm_strdup("output"); }
-    |    /* EMPTY */    { $$ = EMPTY; }
+opt_output: SQL_OUTPUT
+    {
+        $$ = mm_strdup("output");
+    }
+    | /* EMPTY */
+    {
+        $$ = EMPTY;
+    }
     ;

 /*
@@ -1261,425 +1548,465 @@ opt_output:    SQL_OUTPUT    { $$ = mm_strdup("output"); }
  * allocate a descriptor
  */
 ECPGAllocateDescr: SQL_ALLOCATE SQL_DESCRIPTOR quoted_ident_stringvar
-        {
-            add_descriptor($3,connection);
-            $$ = $3;
-        }
-        ;
+    {
+        add_descriptor($3, connection);
+        $$ = $3;
+    }
+    ;


 /*
  * deallocate a descriptor
  */
-ECPGDeallocateDescr:    DEALLOCATE SQL_DESCRIPTOR quoted_ident_stringvar
-        {
-            drop_descriptor($3,connection);
-            $$ = $3;
-        }
-        ;
+ECPGDeallocateDescr: DEALLOCATE SQL_DESCRIPTOR quoted_ident_stringvar
+    {
+        drop_descriptor($3, connection);
+        $$ = $3;
+    }
+    ;

 /*
  * manipulate a descriptor header
  */

 ECPGGetDescriptorHeader: SQL_GET SQL_DESCRIPTOR quoted_ident_stringvar ECPGGetDescHeaderItems
-            {  $$ = $3; }
-        ;
+    {
+        $$ = $3;
+    }
+    ;

 ECPGGetDescHeaderItems: ECPGGetDescHeaderItem
-        | ECPGGetDescHeaderItems ',' ECPGGetDescHeaderItem
-        ;
+    | ECPGGetDescHeaderItems ',' ECPGGetDescHeaderItem
+    ;

 ECPGGetDescHeaderItem: cvariable '=' desc_header_item
-            { push_assignment($1, $3); }
-        ;
-
+    {
+        push_assignment($1, $3);
+    }
+    ;

 ECPGSetDescriptorHeader: SET SQL_DESCRIPTOR quoted_ident_stringvar ECPGSetDescHeaderItems
-            { $$ = $3; }
-        ;
+    {
+        $$ = $3;
+    }
+    ;

 ECPGSetDescHeaderItems: ECPGSetDescHeaderItem
-        | ECPGSetDescHeaderItems ',' ECPGSetDescHeaderItem
-        ;
+    | ECPGSetDescHeaderItems ',' ECPGSetDescHeaderItem
+    ;

 ECPGSetDescHeaderItem: desc_header_item '=' IntConstVar
-        {
-            push_assignment($3, $1);
-        }
-        ;
+    {
+        push_assignment($3, $1);
+    }
+    ;

 IntConstVar: Iconst
-        {
-            char *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
+    {
+        char       *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);

-            sprintf(length, "%zu", strlen($1));
-            new_variable($1, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
-            $$ = $1;
-        }
-        | cvariable
-        {
-            $$ = $1;
-        }
-        ;
+        sprintf(length, "%zu", strlen($1));
+        new_variable($1, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
+        $$ = $1;
+    }
+    | cvariable
+    {
+        $$ = $1;
+    }
+    ;

-desc_header_item:    SQL_COUNT            { $$ = ECPGd_count; }
-        ;
+desc_header_item: SQL_COUNT
+    {
+        $$ = ECPGd_count;
+    }
+    ;

 /*
  * manipulate a descriptor
  */

-ECPGGetDescriptor:    SQL_GET SQL_DESCRIPTOR quoted_ident_stringvar VALUE_P IntConstVar ECPGGetDescItems
-            {  $$.str = $5; $$.name = $3; }
-        ;
+ECPGGetDescriptor: SQL_GET SQL_DESCRIPTOR quoted_ident_stringvar VALUE_P IntConstVar ECPGGetDescItems
+    {
+        $$.str = $5;
+        $$.name = $3;
+    }
+    ;

 ECPGGetDescItems: ECPGGetDescItem
-        | ECPGGetDescItems ',' ECPGGetDescItem
-        ;
-
-ECPGGetDescItem: cvariable '=' descriptor_item    { push_assignment($1, $3); };
+    | ECPGGetDescItems ',' ECPGGetDescItem
+    ;

+ECPGGetDescItem: cvariable '=' descriptor_item
+    {
+        push_assignment($1, $3);
+    }
+    ;

-ECPGSetDescriptor:    SET SQL_DESCRIPTOR quoted_ident_stringvar VALUE_P IntConstVar ECPGSetDescItems
-            {  $$.str = $5; $$.name = $3; }
-        ;
+ECPGSetDescriptor: SET SQL_DESCRIPTOR quoted_ident_stringvar VALUE_P IntConstVar ECPGSetDescItems
+    {
+        $$.str = $5;
+        $$.name = $3;
+    }
+    ;

 ECPGSetDescItems: ECPGSetDescItem
-        | ECPGSetDescItems ',' ECPGSetDescItem
-        ;
+    | ECPGSetDescItems ',' ECPGSetDescItem
+    ;

 ECPGSetDescItem: descriptor_item '=' AllConstVar
-        {
-            push_assignment($3, $1);
-        }
-        ;
+    {
+        push_assignment($3, $1);
+    }
+    ;

 AllConstVar: ecpg_fconst
-        {
-            char *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
-
-            sprintf(length, "%zu", strlen($1));
-            new_variable($1, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
-            $$ = $1;
-        }
-
-        | IntConstVar
-        {
-            $$ = $1;
-        }
-
-        | '-' ecpg_fconst
-        {
-            char *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
-            char *var = cat2_str(mm_strdup("-"), $2);
+    {
+        char       *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);

-            sprintf(length, "%zu", strlen(var));
-            new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
-            $$ = var;
-        }
+        sprintf(length, "%zu", strlen($1));
+        new_variable($1, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
+        $$ = $1;
+    }
+    | IntConstVar
+    {
+        $$ = $1;
+    }
+    | '-' ecpg_fconst
+    {
+        char       *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
+        char       *var = cat2_str(mm_strdup("-"), $2);

-        | '-' Iconst
-        {
-            char *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
-            char *var = cat2_str(mm_strdup("-"), $2);
+        sprintf(length, "%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);

-            sprintf(length, "%zu", strlen(var));
-            new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
-            $$ = var;
-        }
+        sprintf(length, "%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;

-        | ecpg_sconst
-        {
-            char *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
-            char *var = $1 + 1;
+        var[strlen(var) - 1] = '\0';
+        sprintf(length, "%zu", strlen(var));
+        new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
+        $$ = var;
+    }
+    ;

-            var[strlen(var) - 1] = '\0';
-            sprintf(length, "%zu", strlen(var));
-            new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
-            $$ = var;
-        }
-        ;
-
-descriptor_item:    SQL_CARDINALITY            { $$ = ECPGd_cardinality; }
-        | DATA_P                { $$ = ECPGd_data; }
-        | SQL_DATETIME_INTERVAL_CODE        { $$ = ECPGd_di_code; }
-        | SQL_DATETIME_INTERVAL_PRECISION    { $$ = ECPGd_di_precision; }
-        | SQL_INDICATOR                { $$ = ECPGd_indicator; }
-        | SQL_KEY_MEMBER            { $$ = ECPGd_key_member; }
-        | SQL_LENGTH                { $$ = ECPGd_length; }
-        | NAME_P                { $$ = ECPGd_name; }
-        | SQL_NULLABLE                { $$ = ECPGd_nullable; }
-        | SQL_OCTET_LENGTH            { $$ = ECPGd_octet; }
-        | PRECISION                { $$ = ECPGd_precision; }
-        | SQL_RETURNED_LENGTH            { $$ = ECPGd_length; }
-        | SQL_RETURNED_OCTET_LENGTH        { $$ = ECPGd_ret_octet; }
-        | SQL_SCALE                { $$ = ECPGd_scale; }
-        | TYPE_P                { $$ = ECPGd_type; }
-        ;
+descriptor_item: SQL_CARDINALITY        { $$ = ECPGd_cardinality; }
+    | DATA_P                            { $$ = ECPGd_data; }
+    | SQL_DATETIME_INTERVAL_CODE        { $$ = ECPGd_di_code; }
+    | SQL_DATETIME_INTERVAL_PRECISION    { $$ = ECPGd_di_precision; }
+    | SQL_INDICATOR                        { $$ = ECPGd_indicator; }
+    | SQL_KEY_MEMBER                    { $$ = ECPGd_key_member; }
+    | SQL_LENGTH                        { $$ = ECPGd_length; }
+    | NAME_P                            { $$ = ECPGd_name; }
+    | SQL_NULLABLE                        { $$ = ECPGd_nullable; }
+    | SQL_OCTET_LENGTH                    { $$ = ECPGd_octet; }
+    | PRECISION                            { $$ = ECPGd_precision; }
+    | SQL_RETURNED_LENGTH                { $$ = ECPGd_length; }
+    | SQL_RETURNED_OCTET_LENGTH            { $$ = ECPGd_ret_octet; }
+    | SQL_SCALE                            { $$ = ECPGd_scale; }
+    | TYPE_P                            { $$ = ECPGd_type; }
+    ;

 /*
  * set/reset the automatic transaction mode, this needs a different handling
  * as the other set commands
  */
-ECPGSetAutocommit:    SET SQL_AUTOCOMMIT '=' on_off    { $$ = $4; }
-        |  SET SQL_AUTOCOMMIT TO on_off   { $$ = $4; }
-        ;
+ECPGSetAutocommit: SET SQL_AUTOCOMMIT '=' on_off
+    {
+        $$ = $4;
+    }
+    | SET SQL_AUTOCOMMIT TO on_off
+    {
+        $$ = $4;
+    }
+    ;

-on_off: ON                { $$ = mm_strdup("on"); }
-        | OFF            { $$ = mm_strdup("off"); }
-        ;
+on_off: ON
+    {
+        $$ = mm_strdup("on");
+    }
+    | OFF
+    {
+        $$ = mm_strdup("off");
+    }
+    ;

 /*
  * set the actual connection, this needs a different handling as the other
  * set commands
  */
-ECPGSetConnection:    SET CONNECTION TO connection_object { $$ = $4; }
-        | SET CONNECTION '=' connection_object { $$ = $4; }
-        | SET CONNECTION  connection_object { $$ = $3; }
-        ;
+ECPGSetConnection: SET CONNECTION TO connection_object
+    {
+        $$ = $4;
+    }
+    | SET CONNECTION '=' connection_object
+    {
+        $$ = $4;
+    }
+    | SET CONNECTION connection_object
+    {
+        $$ = $3;
+    }
+    ;

 /*
  * define a new type for embedded SQL
  */
 ECPGTypedef: TYPE_P
-        {
-            /* reset this variable so we see if there was */
-            /* an initializer specified */
-            initializer = 0;
-        }
-        ECPGColLabel IS var_type opt_array_bounds opt_reference
-        {
-            add_typedef($3, $6.index1, $6.index2, $5.type_enum, $5.type_dimension, $5.type_index, initializer, *$7 ? 1
:0); 
+    {
+        /* reset this variable so we see if there was */
+        /* an initializer specified */
+        initializer = 0;
+    }
+    ECPGColLabel IS var_type opt_array_bounds opt_reference
+    {
+        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("*/")); 
-            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(";")); 
-        }
-        ;
+        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("*/")); 
+        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(";")); 
+    }
+    ;

-opt_reference: SQL_REFERENCE        { $$ = mm_strdup("reference"); }
-        | /*EMPTY*/                    { $$ = EMPTY; }
-        ;
+opt_reference: SQL_REFERENCE
+    {
+        $$ = mm_strdup("reference");
+    }
+    | /* EMPTY */
+    {
+        $$ = EMPTY;
+    }
+    ;

 /*
  * define the type of one variable for embedded SQL
  */
 ECPGVar: SQL_VAR
+    {
+        /* reset this variable so we see if there was */
+        /* an initializer specified */
+        initializer = 0;
+    }
+    ColLabel    IS var_type opt_array_bounds opt_reference
+    {
+        struct variable *p = find_variable($3);
+        char       *dimension = $6.index1;
+        char       *length = $6.index2;
+        struct ECPGtype *type;
+
+        if            (($5.type_enum == ECPGt_struct ||
+                      $5.type_enum == ECPGt_union) &&
+                     initializer == 1)
+            mmerror(PARSE_ERROR, ET_ERROR, "initializer not allowed in EXEC SQL VAR command");
+        else
         {
-            /* reset this variable so we see if there was */
-            /* an initializer specified */
-            initializer = 0;
-        }
-        ColLabel IS var_type opt_array_bounds opt_reference
-        {
-            struct variable *p = find_variable($3);
-            char *dimension = $6.index1;
-            char *length = $6.index2;
-            struct ECPGtype * type;
-
-            if (($5.type_enum == ECPGt_struct ||
-                 $5.type_enum == ECPGt_union) &&
-                initializer == 1)
-                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)
-                {
+            switch ($5.type_enum)
+            {
                     case ECPGt_struct:
                     case ECPGt_union:
-                        if (atoi(dimension) < 0)
-                            type = ECPGmake_struct_type(struct_member_list[struct_level], $5.type_enum, $5.type_str,
$5.type_sizeof);
-                        else
-                            type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level],
$5.type_enum,$5.type_str, $5.type_sizeof), dimension); 
-                        break;
+                    if (atoi(dimension) < 0)
+                        type = ECPGmake_struct_type(struct_member_list[struct_level], $5.type_enum, $5.type_str,
$5.type_sizeof);
+                    else
+                        type = ECPGmake_array_type(ECPGmake_struct_type(struct_member_list[struct_level],
$5.type_enum,$5.type_str, $5.type_sizeof), dimension); 
+                    break;

                     case ECPGt_varchar:
                     case ECPGt_bytea:
-                        if (atoi(dimension) == -1)
-                            type = ECPGmake_simple_type($5.type_enum, length, 0);
-                        else
-                            type = ECPGmake_array_type(ECPGmake_simple_type($5.type_enum, length, 0), dimension);
-                        break;
+                    if (atoi(dimension) == -1)
+                        type = ECPGmake_simple_type($5.type_enum, length, 0);
+                    else
+                        type = ECPGmake_array_type(ECPGmake_simple_type($5.type_enum, length, 0), dimension);
+                    break;

                     case ECPGt_char:
                     case ECPGt_unsigned_char:
                     case ECPGt_string:
-                        if (atoi(dimension) == -1)
-                            type = ECPGmake_simple_type($5.type_enum, length, 0);
-                        else
-                            type = ECPGmake_array_type(ECPGmake_simple_type($5.type_enum, length, 0), dimension);
-                        break;
+                    if (atoi(dimension) == -1)
+                        type = ECPGmake_simple_type($5.type_enum, length, 0);
+                    else
+                        type = ECPGmake_array_type(ECPGmake_simple_type($5.type_enum, length, 0), dimension);
+                    break;

                     default:
-                        if (atoi(length) >= 0)
-                            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);
-                        else
-                            type = ECPGmake_array_type(ECPGmake_simple_type($5.type_enum, mm_strdup("1"), 0),
dimension);
-                        break;
-                }
+                    if (atoi(length) >= 0)
+                        mmerror(PARSE_ERROR, ET_ERROR, "multidimensional arrays for simple data types are not
supported");

-                ECPGfree_type(p->type);
-                p->type = type;
+                    if (atoi(dimension) < 0)
+                        type = ECPGmake_simple_type($5.type_enum, mm_strdup("1"), 0);
+                    else
+                        type = ECPGmake_array_type(ECPGmake_simple_type($5.type_enum, mm_strdup("1"), 0), dimension);
+                    break;
             }

-            $$ = 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("*/")); 
+            ECPGfree_type(p->type);
+            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("*/")); 
+    }
+    ;

 /*
  * whenever statement: decide what to do in case of error/no data found
  * according to SQL standards we lack: SQLSTATE, CONSTRAINT and SQLEXCEPTION
  */
 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("; */"));
-        }
-        | 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("; */"));
-        }
-        | 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_error.code = $<action>3.code;
+        when_error.command = $<action>3.command;
+        $$ = cat_str(3, mm_strdup("/* exec sql whenever sqlerror "), $3.str, mm_strdup("; */"));
+    }
+    | 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("; */"));
+    }
+    | 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("; */"));
+    }
+    ;

-action : CONTINUE_P
-        {
-            $<action>$.code = W_NOTHING;
-            $<action>$.command = NULL;
-            $<action>$.str = mm_strdup("continue");
-        }
-        | SQL_SQLPRINT
-        {
-            $<action>$.code = W_SQLPRINT;
-            $<action>$.command = NULL;
-            $<action>$.str = mm_strdup("sqlprint");
-        }
-        | SQL_STOP
-        {
-            $<action>$.code = W_STOP;
-            $<action>$.command = NULL;
-            $<action>$.str = mm_strdup("stop");
-        }
-        | SQL_GOTO name
-        {
-            $<action>$.code = W_GOTO;
-            $<action>$.command = mm_strdup($2);
-            $<action>$.str = cat2_str(mm_strdup("goto "), $2);
-        }
-        | SQL_GO TO name
-        {
-            $<action>$.code = W_GOTO;
-            $<action>$.command = mm_strdup($3);
-            $<action>$.str = cat2_str(mm_strdup("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));
-        }
-        | DO SQL_BREAK
-        {
-            $<action>$.code = W_BREAK;
-            $<action>$.command = NULL;
-            $<action>$.str = mm_strdup("break");
-        }
-        | DO CONTINUE_P
-        {
-            $<action>$.code = W_CONTINUE;
-            $<action>$.command = NULL;
-            $<action>$.str = mm_strdup("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));
-        }
-        | CALL name
-        {
-            $<action>$.code = W_DO;
-            $<action>$.command = cat2_str($2, mm_strdup("()"));
-            $<action>$.str = cat2_str(mm_strdup("call"), mm_strdup($<action>$.command));
-        }
-        ;
+action: CONTINUE_P
+    {
+        $<action>$.code = W_NOTHING;
+        $<action>$.command = NULL;
+        $<action>$.str = mm_strdup("continue");
+    }
+    | SQL_SQLPRINT
+    {
+        $<action>$.code = W_SQLPRINT;
+        $<action>$.command = NULL;
+        $<action>$.str = mm_strdup("sqlprint");
+    }
+    | SQL_STOP
+    {
+        $<action>$.code = W_STOP;
+        $<action>$.command = NULL;
+        $<action>$.str = mm_strdup("stop");
+    }
+    | SQL_GOTO name
+    {
+        $<action>$.code = W_GOTO;
+        $<action>$.command = mm_strdup($2);
+        $<action>$.str = cat2_str(mm_strdup("goto "), $2);
+    }
+    | SQL_GO TO name
+    {
+        $<action>$.code = W_GOTO;
+        $<action>$.command = mm_strdup($3);
+        $<action>$.str = cat2_str(mm_strdup("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));
+    }
+    | DO SQL_BREAK
+    {
+        $<action>$.code = W_BREAK;
+        $<action>$.command = NULL;
+        $<action>$.str = mm_strdup("break");
+    }
+    | DO CONTINUE_P
+    {
+        $<action>$.code = W_CONTINUE;
+        $<action>$.command = NULL;
+        $<action>$.str = mm_strdup("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));
+    }
+    | CALL name
+    {
+        $<action>$.code = W_DO;
+        $<action>$.command = cat2_str($2, mm_strdup("()"));
+        $<action>$.str = cat2_str(mm_strdup("call"), mm_strdup($<action>$.command));
+    }
+    ;

 /* some other stuff for ecpg */

 /* additional unreserved keywords */
-ECPGKeywords: ECPGKeywords_vanames    { $$ = $1; }
-        | ECPGKeywords_rest    { $$ = $1; }
-        ;
-
-ECPGKeywords_vanames:  SQL_BREAK        { $$ = mm_strdup("break"); }
-        | SQL_CARDINALITY                { $$ = mm_strdup("cardinality"); }
-        | SQL_COUNT                        { $$ = mm_strdup("count"); }
-        | SQL_DATETIME_INTERVAL_CODE    { $$ = mm_strdup("datetime_interval_code"); }
-        | SQL_DATETIME_INTERVAL_PRECISION    { $$ = mm_strdup("datetime_interval_precision"); }
-        | SQL_FOUND                        { $$ = mm_strdup("found"); }
-        | SQL_GO                        { $$ = mm_strdup("go"); }
-        | SQL_GOTO                        { $$ = mm_strdup("goto"); }
-        | SQL_IDENTIFIED                { $$ = mm_strdup("identified"); }
-        | SQL_INDICATOR                { $$ = mm_strdup("indicator"); }
-        | SQL_KEY_MEMBER            { $$ = mm_strdup("key_member"); }
-        | SQL_LENGTH                { $$ = mm_strdup("length"); }
-        | SQL_NULLABLE                { $$ = mm_strdup("nullable"); }
-        | SQL_OCTET_LENGTH            { $$ = mm_strdup("octet_length"); }
-        | SQL_RETURNED_LENGTH        { $$ = mm_strdup("returned_length"); }
-        | SQL_RETURNED_OCTET_LENGTH    { $$ = mm_strdup("returned_octet_length"); }
-        | SQL_SCALE                    { $$ = mm_strdup("scale"); }
-        | SQL_SECTION                { $$ = mm_strdup("section"); }
-        | SQL_SQLERROR                { $$ = mm_strdup("sqlerror"); }
-        | SQL_SQLPRINT                { $$ = mm_strdup("sqlprint"); }
-        | SQL_SQLWARNING            { $$ = mm_strdup("sqlwarning"); }
-        | SQL_STOP                    { $$ = mm_strdup("stop"); }
-        ;
-
-ECPGKeywords_rest:  SQL_CONNECT        { $$ = mm_strdup("connect"); }
-        | SQL_DESCRIBE                { $$ = mm_strdup("describe"); }
-        | SQL_DISCONNECT            { $$ = mm_strdup("disconnect"); }
-        | SQL_OPEN                    { $$ = mm_strdup("open"); }
-        | SQL_VAR                    { $$ = mm_strdup("var"); }
-        | SQL_WHENEVER                { $$ = mm_strdup("whenever"); }
-        ;
+ECPGKeywords: ECPGKeywords_vanames        { $$ = $1; }
+    | ECPGKeywords_rest                    { $$ = $1; }
+    ;
+
+ECPGKeywords_vanames: SQL_BREAK            { $$ = mm_strdup("break"); }
+    | SQL_CARDINALITY                    { $$ = mm_strdup("cardinality"); }
+    | SQL_COUNT                            { $$ = mm_strdup("count"); }
+    | SQL_DATETIME_INTERVAL_CODE        { $$ = mm_strdup("datetime_interval_code"); }
+    | SQL_DATETIME_INTERVAL_PRECISION    { $$ = mm_strdup("datetime_interval_precision"); }
+    | SQL_FOUND                            { $$ = mm_strdup("found"); }
+    | SQL_GO                            { $$ = mm_strdup("go"); }
+    | SQL_GOTO                            { $$ = mm_strdup("goto"); }
+    | SQL_IDENTIFIED                    { $$ = mm_strdup("identified"); }
+    | SQL_INDICATOR                        { $$ = mm_strdup("indicator"); }
+    | SQL_KEY_MEMBER                    { $$ = mm_strdup("key_member"); }
+    | SQL_LENGTH                        { $$ = mm_strdup("length"); }
+    | SQL_NULLABLE                        { $$ = mm_strdup("nullable"); }
+    | SQL_OCTET_LENGTH                    { $$ = mm_strdup("octet_length"); }
+    | SQL_RETURNED_LENGTH                { $$ = mm_strdup("returned_length"); }
+    | SQL_RETURNED_OCTET_LENGTH            { $$ = mm_strdup("returned_octet_length"); }
+    | SQL_SCALE                            { $$ = mm_strdup("scale"); }
+    | SQL_SECTION                        { $$ = mm_strdup("section"); }
+    | SQL_SQLERROR                        { $$ = mm_strdup("sqlerror"); }
+    | SQL_SQLPRINT                        { $$ = mm_strdup("sqlprint"); }
+    | SQL_SQLWARNING                    { $$ = mm_strdup("sqlwarning"); }
+    | SQL_STOP                            { $$ = mm_strdup("stop"); }
+    ;
+
+ECPGKeywords_rest: SQL_CONNECT            { $$ = mm_strdup("connect"); }
+    | SQL_DESCRIBE                        { $$ = mm_strdup("describe"); }
+    | SQL_DISCONNECT                    { $$ = mm_strdup("disconnect"); }
+    | SQL_OPEN                            { $$ = mm_strdup("open"); }
+    | SQL_VAR                            { $$ = mm_strdup("var"); }
+    | SQL_WHENEVER                        { $$ = mm_strdup("whenever"); }
+    ;

 /* additional keywords that can be SQL type names (but not ECPGColLabels) */
-ECPGTypeName:  SQL_BOOL                { $$ = mm_strdup("bool"); }
-        | SQL_LONG                    { $$ = mm_strdup("long"); }
-        | SQL_OUTPUT                { $$ = mm_strdup("output"); }
-        | SQL_SHORT                    { $$ = mm_strdup("short"); }
-        | SQL_STRUCT                { $$ = mm_strdup("struct"); }
-        | SQL_SIGNED                { $$ = mm_strdup("signed"); }
-        | SQL_UNSIGNED                { $$ = mm_strdup("unsigned"); }
-        ;
-
-symbol: ColLabel                    { $$ = $1; }
-        ;
-
-ECPGColId: ecpg_ident                { $$ = $1; }
-        | unreserved_keyword        { $$ = $1; }
-        | col_name_keyword            { $$ = $1; }
-        | ECPGunreserved_interval    { $$ = $1; }
-        | ECPGKeywords                { $$ = $1; }
-        | ECPGCKeywords                { $$ = $1; }
-        | CHAR_P                    { $$ = mm_strdup("char"); }
-        | VALUES                    { $$ = mm_strdup("values"); }
-        ;
+ECPGTypeName: SQL_BOOL                    { $$ = mm_strdup("bool"); }
+    | SQL_LONG                            { $$ = mm_strdup("long"); }
+    | SQL_OUTPUT                        { $$ = mm_strdup("output"); }
+    | SQL_SHORT                            { $$ = mm_strdup("short"); }
+    | SQL_STRUCT                        { $$ = mm_strdup("struct"); }
+    | SQL_SIGNED                        { $$ = mm_strdup("signed"); }
+    | SQL_UNSIGNED                        { $$ = mm_strdup("unsigned"); }
+    ;
+
+symbol: ColLabel                        { $$ = $1; }
+    ;
+
+ECPGColId: ecpg_ident                    { $$ = $1; }
+    | unreserved_keyword                { $$ = $1; }
+    | col_name_keyword                    { $$ = $1; }
+    | ECPGunreserved_interval            { $$ = $1; }
+    | ECPGKeywords                        { $$ = $1; }
+    | ECPGCKeywords                        { $$ = $1; }
+    | CHAR_P                            { $$ = mm_strdup("char"); }
+    | VALUES                            { $$ = mm_strdup("values"); }
+    ;

 /*
  * Name classification hierarchy.
@@ -1691,59 +2018,59 @@ ECPGColId: ecpg_ident                { $$ = $1; }

 /* Column identifier --- names that can be column, table, etc names.
  */
-ColId:    ecpg_ident                    { $$ = $1; }
-        | all_unreserved_keyword    { $$ = $1; }
-        | col_name_keyword            { $$ = $1; }
-        | ECPGKeywords                { $$ = $1; }
-        | ECPGCKeywords                { $$ = $1; }
-        | CHAR_P                    { $$ = mm_strdup("char"); }
-        | VALUES                    { $$ = mm_strdup("values"); }
-        ;
+ColId: ecpg_ident                        { $$ = $1; }
+    | all_unreserved_keyword            { $$ = $1; }
+    | col_name_keyword                    { $$ = $1; }
+    | ECPGKeywords                        { $$ = $1; }
+    | ECPGCKeywords                        { $$ = $1; }
+    | CHAR_P                            { $$ = mm_strdup("char"); }
+    | VALUES                            { $$ = mm_strdup("values"); }
+    ;

 /* Type/function identifier --- names that can be type or function names.
  */
-type_function_name:    ecpg_ident        { $$ = $1; }
-        | all_unreserved_keyword    { $$ = $1; }
-        | type_func_name_keyword    { $$ = $1; }
-        | ECPGKeywords                { $$ = $1; }
-        | ECPGCKeywords                { $$ = $1; }
-        | ECPGTypeName                { $$ = $1; }
-        ;
+type_function_name: ecpg_ident            { $$ = $1; }
+    | all_unreserved_keyword            { $$ = $1; }
+    | type_func_name_keyword            { $$ = $1; }
+    | ECPGKeywords                        { $$ = $1; }
+    | ECPGCKeywords                        { $$ = $1; }
+    | ECPGTypeName                        { $$ = $1; }
+    ;

 /* Column label --- allowed labels in "AS" clauses.
  * This presently includes *all* Postgres keywords.
  */
-ColLabel:  ECPGColLabel                { $$ = $1; }
-        | ECPGTypeName                { $$ = $1; }
-        | CHAR_P                    { $$ = mm_strdup("char"); }
-        | CURRENT_P                    { $$ = mm_strdup("current"); }
-        | INPUT_P                    { $$ = mm_strdup("input"); }
-        | INT_P                        { $$ = mm_strdup("int"); }
-        | TO                        { $$ = mm_strdup("to"); }
-        | UNION                        { $$ = mm_strdup("union"); }
-        | VALUES                    { $$ = mm_strdup("values"); }
-        | ECPGCKeywords                { $$ = $1; }
-        | ECPGunreserved_interval    { $$ = $1; }
-        ;
-
-ECPGColLabel:  ecpg_ident            { $$ = $1; }
-        | unreserved_keyword        { $$ = $1; }
-        | col_name_keyword            { $$ = $1; }
-        | type_func_name_keyword    { $$ = $1; }
-        | reserved_keyword            { $$ = $1; }
-        | ECPGKeywords_vanames        { $$ = $1; }
-        | ECPGKeywords_rest            { $$ = $1; }
-        | CONNECTION                { $$ = mm_strdup("connection"); }
-        ;
-
-ECPGCKeywords: S_AUTO                { $$ = mm_strdup("auto"); }
-        | S_CONST                    { $$ = mm_strdup("const"); }
-        | S_EXTERN                    { $$ = mm_strdup("extern"); }
-        | S_REGISTER                { $$ = mm_strdup("register"); }
-        | S_STATIC                    { $$ = mm_strdup("static"); }
-        | S_TYPEDEF                    { $$ = mm_strdup("typedef"); }
-        | S_VOLATILE                { $$ = mm_strdup("volatile"); }
-        ;
+ColLabel: ECPGColLabel                    { $$ = $1; }
+    | ECPGTypeName                        { $$ = $1; }
+    | CHAR_P                            { $$ = mm_strdup("char"); }
+    | CURRENT_P                            { $$ = mm_strdup("current"); }
+    | INPUT_P                            { $$ = mm_strdup("input"); }
+    | INT_P                                { $$ = mm_strdup("int"); }
+    | TO                                { $$ = mm_strdup("to"); }
+    | UNION                                { $$ = mm_strdup("union"); }
+    | VALUES                            { $$ = mm_strdup("values"); }
+    | ECPGCKeywords                        { $$ = $1; }
+    | ECPGunreserved_interval            { $$ = $1; }
+    ;
+
+ECPGColLabel: ecpg_ident                { $$ = $1; }
+    | unreserved_keyword                { $$ = $1; }
+    | col_name_keyword                    { $$ = $1; }
+    | type_func_name_keyword            { $$ = $1; }
+    | reserved_keyword                    { $$ = $1; }
+    | ECPGKeywords_vanames                { $$ = $1; }
+    | ECPGKeywords_rest                    { $$ = $1; }
+    | CONNECTION                        { $$ = mm_strdup("connection"); }
+    ;
+
+ECPGCKeywords: S_AUTO                    { $$ = mm_strdup("auto"); }
+    | S_CONST                            { $$ = mm_strdup("const"); }
+    | S_EXTERN                            { $$ = mm_strdup("extern"); }
+    | S_REGISTER                        { $$ = mm_strdup("register"); }
+    | S_STATIC                            { $$ = mm_strdup("static"); }
+    | S_TYPEDEF                            { $$ = mm_strdup("typedef"); }
+    | S_VOLATILE                        { $$ = mm_strdup("volatile"); }
+    ;

 /* "Unreserved" keywords --- available for use as any kind of name.
  */
@@ -1760,250 +2087,402 @@ ECPGCKeywords: S_AUTO                { $$ = mm_strdup("auto"); }
  * The mentioned exclusions are done by $replace_line settings in parse.pl.
  */
 all_unreserved_keyword: unreserved_keyword    { $$ = $1; }
-        | ECPGunreserved_interval            { $$ = $1; }
-        | CONNECTION                        { $$ = mm_strdup("connection"); }
-        ;
-
-ECPGunreserved_interval: DAY_P                { $$ = mm_strdup("day"); }
-        | HOUR_P                            { $$ = mm_strdup("hour"); }
-        | MINUTE_P                            { $$ = mm_strdup("minute"); }
-        | MONTH_P                            { $$ = mm_strdup("month"); }
-        | SECOND_P                            { $$ = mm_strdup("second"); }
-        | YEAR_P                            { $$ = mm_strdup("year"); }
-        ;
+    | ECPGunreserved_interval            { $$ = $1; }
+    | CONNECTION                        { $$ = mm_strdup("connection"); }
+    ;

+ECPGunreserved_interval: DAY_P            { $$ = mm_strdup("day"); }
+    | HOUR_P                            { $$ = mm_strdup("hour"); }
+    | MINUTE_P                            { $$ = mm_strdup("minute"); }
+    | MONTH_P                            { $$ = mm_strdup("month"); }
+    | SECOND_P                            { $$ = mm_strdup("second"); }
+    | YEAR_P                            { $$ = mm_strdup("year"); }
+    ;

-into_list : coutputvariable | into_list ',' coutputvariable
-        ;
+into_list: coutputvariable | into_list ',' coutputvariable
+    ;

-ecpgstart: SQL_START    {
-                reset_variables();
-                pacounter = 1;
-            }
-        ;
+ecpgstart: SQL_START
+    {
+        reset_variables();
+        pacounter = 1;
+    }
+    ;

-c_args: /*EMPTY*/        { $$ = EMPTY; }
-        | c_list        { $$ = $1; }
-        ;
+c_args: /* EMPTY */
+    {
+        $$ = EMPTY;
+    }
+    | c_list
+    {
+        $$ = $1;
+    }
+    ;

 coutputvariable: cvariable indicator
-            { add_variable_to_head(&argsresult, find_variable($1), find_variable($2)); }
-        | cvariable
-            { add_variable_to_head(&argsresult, find_variable($1), &no_indicator); }
-        ;
+    {
+        add_variable_to_head(&argsresult, find_variable($1), find_variable($2));
+    }
+    | cvariable
+    {
+        add_variable_to_head(&argsresult, find_variable($1), &no_indicator);
+    }
+    ;


 civarind: cvariable indicator
-        {
-            if (find_variable($2)->type->type == ECPGt_array)
-                mmerror(PARSE_ERROR, ET_ERROR, "arrays of indicators are not allowed on input");
+    {
+        if (find_variable($2)->type->type == ECPGt_array)
+            mmerror(PARSE_ERROR, ET_ERROR, "arrays of indicators are not allowed on input");

-            add_variable_to_head(&argsinsert, find_variable($1), find_variable($2));
-            $$ = create_questionmarks($1, false);
-        }
-        ;
+        add_variable_to_head(&argsinsert, find_variable($1), find_variable($2));
+        $$ = create_questionmarks($1, false);
+    }
+    ;

 char_civar: char_variable
-        {
-            char *ptr = strstr($1, ".arr");
+    {
+        char       *ptr = strstr($1, ".arr");

-            if (ptr) /* varchar, we need the struct name here, not the struct element */
-                *ptr = '\0';
-            add_variable_to_head(&argsinsert, find_variable($1), &no_indicator);
-            $$ = $1;
-        }
-        ;
+        if (ptr)                /* varchar, we need the struct name here, not
+                                 * the struct element */
+            *ptr = '\0';
+        add_variable_to_head(&argsinsert, find_variable($1), &no_indicator);
+        $$ = $1;
+    }
+    ;

 civar: cvariable
-        {
-            add_variable_to_head(&argsinsert, find_variable($1), &no_indicator);
-            $$ = create_questionmarks($1, false);
-        }
-        ;
+    {
+        add_variable_to_head(&argsinsert, find_variable($1), &no_indicator);
+        $$ = create_questionmarks($1, false);
+    }
+    ;

-indicator: cvariable                { check_indicator((find_variable($1))->type); $$ = $1; }
-        | SQL_INDICATOR cvariable    { check_indicator((find_variable($2))->type); $$ = $2; }
-        | SQL_INDICATOR name        { check_indicator((find_variable($2))->type); $$ = $2; }
-        ;
+indicator: cvariable
+    {
+        check_indicator((find_variable($1))->type);
+        $$ = $1;
+    }
+    | SQL_INDICATOR cvariable
+    {
+        check_indicator((find_variable($2))->type);
+        $$ = $2;
+    }
+    | SQL_INDICATOR name
+    {
+        check_indicator((find_variable($2))->type);
+        $$ = $2;
+    }
+    ;

-cvariable:    CVARIABLE
-        {
-            /* As long as multidimensional arrays are not implemented we have to check for those here */
-            char *ptr = $1;
-            int brace_open=0, brace = false;
+cvariable: CVARIABLE
+    {
+        /*
+         * As long as multidimensional arrays are not implemented we have to
+         * check for those here
+         */
+        char       *ptr = $1;
+        int            brace_open = 0,
+                    brace = false;

-            for (; *ptr; ptr++)
+        for (; *ptr; ptr++)
+        {
+            switch (*ptr)
             {
-                switch (*ptr)
-                {
-                    case '[':
-                            if (brace)
-                                mmfatal(PARSE_ERROR, "multidimensional arrays for simple data types are not
supported");
-                            brace_open++;
-                            break;
-                    case ']':
-                            brace_open--;
-                            if (brace_open == 0)
-                                brace = true;
-                            break;
-                    case '\t':
-                    case ' ':
-                            break;
-                    default:
-                            if (brace_open == 0)
-                                brace = false;
-                            break;
-                }
+                case '[':
+                    if (brace)
+                        mmfatal(PARSE_ERROR, "multidimensional arrays for simple data types are not supported");
+                    brace_open++;
+                    break;
+                case ']':
+                    brace_open--;
+                    if (brace_open == 0)
+                        brace = true;
+                    break;
+                case '\t':
+                case ' ':
+                    break;
+                default:
+                    if (brace_open == 0)
+                        brace = false;
+                    break;
             }
-            $$ = $1;
         }
-        ;
+        $$ = $1;
+    }
+    ;

-ecpg_param:    PARAM        { $$ = make_name(); } ;
+ecpg_param: PARAM
+    {
+        $$ = make_name();
+    }
+    ;

-ecpg_bconst:    BCONST        { $$ = $1; } ;
+ecpg_bconst: BCONST
+    {
+        $$ = $1;
+    }
+    ;

-ecpg_fconst:    FCONST        { $$ = make_name(); } ;
+ecpg_fconst: FCONST
+    {
+        $$ = make_name();
+    }
+    ;

-ecpg_sconst:    SCONST        { $$ = $1; } ;
+ecpg_sconst: SCONST
+    {
+        $$ = $1;
+    }
+    ;

-ecpg_xconst:    XCONST        { $$ = $1; } ;
+ecpg_xconst: XCONST
+    {
+        $$ = $1;
+    }
+    ;

-ecpg_ident:    IDENT        { $$ = $1; }
-        | CSTRING    { $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\"")); }
-        ;
+ecpg_ident: IDENT
+    {
+        $$ = $1;
+    }
+    | CSTRING
+    {
+        $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+    }
+    ;

 quoted_ident_stringvar: name
-            { $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\"")); }
-        | char_variable
-            { $$ = make3_str(mm_strdup("("), $1, mm_strdup(")")); }
-        ;
+    {
+        $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+    }
+    | char_variable
+    {
+        $$ = make3_str(mm_strdup("("), $1, mm_strdup(")"));
+    }
+    ;

 /*
  * C stuff
  */

-c_stuff_item: c_anything            { $$ = $1; }
-        | '(' ')'            { $$ = mm_strdup("()"); }
-        | '(' c_stuff ')'
-            { $$ = cat_str(3, mm_strdup("("), $2, mm_strdup(")")); }
-        ;
-
-c_stuff: c_stuff_item            { $$ = $1; }
-        | c_stuff c_stuff_item
-            { $$ = cat2_str($1, $2); }
-        ;
-
-c_list: c_term                { $$ = $1; }
-        | c_list ',' c_term    { $$ = cat_str(3, $1, mm_strdup(","), $3); }
-        ;
-
-c_term:  c_stuff            { $$ = $1; }
-        | '{' c_list '}'    { $$ = cat_str(3, mm_strdup("{"), $2, mm_strdup("}")); }
-        ;
-
-c_thing:    c_anything        { $$ = $1; }
-        |    '('        { $$ = mm_strdup("("); }
-        |    ')'        { $$ = mm_strdup(")"); }
-        |    ','        { $$ = mm_strdup(","); }
-        |    ';'        { $$ = mm_strdup(";"); }
-        ;
-
-c_anything:  ecpg_ident                { $$ = $1; }
-        | Iconst            { $$ = $1; }
-        | ecpg_fconst            { $$ = $1; }
-        | ecpg_sconst            { $$ = $1; }
-        | '*'                { $$ = mm_strdup("*"); }
-        | '+'                { $$ = mm_strdup("+"); }
-        | '-'                { $$ = mm_strdup("-"); }
-        | '/'                { $$ = mm_strdup("/"); }
-        | '%'                { $$ = mm_strdup("%"); }
-        | NULL_P            { $$ = mm_strdup("NULL"); }
-        | S_ADD                { $$ = mm_strdup("+="); }
-        | S_AND                { $$ = mm_strdup("&&"); }
-        | S_ANYTHING            { $$ = make_name(); }
-        | S_AUTO            { $$ = mm_strdup("auto"); }
-        | S_CONST            { $$ = mm_strdup("const"); }
-        | S_DEC                { $$ = mm_strdup("--"); }
-        | S_DIV                { $$ = mm_strdup("/="); }
-        | S_DOTPOINT            { $$ = mm_strdup(".*"); }
-        | S_EQUAL            { $$ = mm_strdup("=="); }
-        | S_EXTERN            { $$ = mm_strdup("extern"); }
-        | S_INC                { $$ = mm_strdup("++"); }
-        | S_LSHIFT            { $$ = mm_strdup("<<"); }
-        | S_MEMBER            { $$ = mm_strdup("->"); }
-        | S_MEMPOINT            { $$ = mm_strdup("->*"); }
-        | S_MOD                { $$ = mm_strdup("%="); }
-        | S_MUL                { $$ = mm_strdup("*="); }
-        | S_NEQUAL            { $$ = mm_strdup("!="); }
-        | S_OR                { $$ = mm_strdup("||"); }
-        | S_REGISTER            { $$ = mm_strdup("register"); }
-        | S_RSHIFT            { $$ = mm_strdup(">>"); }
-        | S_STATIC            { $$ = mm_strdup("static"); }
-        | S_SUB                { $$ = mm_strdup("-="); }
-        | S_TYPEDEF            { $$ = mm_strdup("typedef"); }
-        | S_VOLATILE            { $$ = mm_strdup("volatile"); }
-        | SQL_BOOL            { $$ = mm_strdup("bool"); }
-        | ENUM_P            { $$ = mm_strdup("enum"); }
-        | HOUR_P            { $$ = mm_strdup("hour"); }
-        | INT_P                { $$ = mm_strdup("int"); }
-        | SQL_LONG            { $$ = mm_strdup("long"); }
-        | MINUTE_P            { $$ = mm_strdup("minute"); }
-        | MONTH_P            { $$ = mm_strdup("month"); }
-        | SECOND_P            { $$ = mm_strdup("second"); }
-        | SQL_SHORT            { $$ = mm_strdup("short"); }
-        | SQL_SIGNED            { $$ = mm_strdup("signed"); }
-        | SQL_STRUCT            { $$ = mm_strdup("struct"); }
-        | SQL_UNSIGNED            { $$ = mm_strdup("unsigned"); }
-        | YEAR_P            { $$ = mm_strdup("year"); }
-        | CHAR_P            { $$ = mm_strdup("char"); }
-        | FLOAT_P            { $$ = mm_strdup("float"); }
-        | TO                { $$ = mm_strdup("to"); }
-        | UNION                { $$ = mm_strdup("union"); }
-        | VARCHAR            { $$ = mm_strdup("varchar"); }
-        | '['                { $$ = mm_strdup("["); }
-        | ']'                { $$ = mm_strdup("]"); }
-        | '='                { $$ = mm_strdup("="); }
-        | ':'                { $$ = mm_strdup(":"); }
-        ;
-
-DeallocateStmt: DEALLOCATE prepared_name    { check_declared_list($2); $$ = $2; }
-        | DEALLOCATE PREPARE prepared_name    { check_declared_list($3); $$ = $3; }
-        | DEALLOCATE ALL                    { $$ = mm_strdup("all"); }
-        | DEALLOCATE PREPARE ALL            { $$ = mm_strdup("all"); }
-        ;
-
-Iresult: Iconst                        { $$ = $1; }
-        | '(' Iresult ')'            { $$ = cat_str(3, mm_strdup("("), $2, mm_strdup(")")); }
-        | Iresult '+' Iresult        { $$ = cat_str(3, $1, mm_strdup("+"), $3); }
-        | Iresult '-' Iresult        { $$ = cat_str(3, $1, mm_strdup("-"), $3); }
-        | Iresult '*' Iresult        { $$ = cat_str(3, $1, mm_strdup("*"), $3); }
-        | Iresult '/' Iresult        { $$ = cat_str(3, $1, mm_strdup("/"), $3); }
-        | Iresult '%' Iresult        { $$ = cat_str(3, $1, mm_strdup("%"), $3); }
-        | ecpg_sconst                { $$ = $1; }
-        | ColId                        { $$ = $1; }
-        | ColId '(' var_type ')'    { 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(")"));
-                        }
-        ;
-
-execute_rest: /* EMPTY */    { $$ = EMPTY; }
-    | ecpg_using opt_ecpg_into  { $$ = EMPTY; }
-    | ecpg_into ecpg_using  { $$ = EMPTY; }
-    | ecpg_into                { $$ = EMPTY; }
-    ;
-
-ecpg_into: INTO into_list    { $$ = EMPTY; }
-    | into_descriptor        { $$ = $1; }
-    ;
-
-opt_ecpg_into:    /* EMPTY */    { $$ = EMPTY; }
-    | ecpg_into        { $$ = $1; }
-    ;
-
-ecpg_fetch_into: ecpg_into    { $$ = $1; }
+c_stuff_item: c_anything
+    {
+        $$ = $1;
+    }
+    | '(' ')'
+    {
+        $$ = mm_strdup("()");
+    }
+    | '(' c_stuff ')'
+    {
+        $$ = cat_str(3, mm_strdup("("), $2, mm_strdup(")"));
+    }
+    ;
+
+c_stuff: c_stuff_item
+    {
+        $$ = $1;
+    }
+    | c_stuff c_stuff_item
+    {
+        $$ = cat2_str($1, $2);
+    }
+    ;
+
+c_list: c_term
+    {
+        $$ = $1;
+    }
+    | c_list ',' c_term
+    {
+        $$ = cat_str(3, $1, mm_strdup(","), $3);
+    }
+    ;
+
+c_term: c_stuff
+    {
+        $$ = $1;
+    }
+    | '{' c_list '}'
+    {
+        $$ = cat_str(3, mm_strdup("{"), $2, mm_strdup("}"));
+    }
+    ;
+
+c_thing: c_anything                    { $$ = $1; }
+    | '('                            { $$ = mm_strdup("("); }
+    | ')'                            { $$ = mm_strdup(")"); }
+    | ','                            { $$ = mm_strdup(","); }
+    | ';'                            { $$ = mm_strdup(";"); }
+    ;
+
+c_anything: ecpg_ident                { $$ = $1; }
+    | Iconst                        { $$ = $1; }
+    | ecpg_fconst                    { $$ = $1; }
+    | ecpg_sconst                    { $$ = $1; }
+    | '*'                            { $$ = mm_strdup("*"); }
+    | '+'                            { $$ = mm_strdup("+"); }
+    | '-'                            { $$ = mm_strdup("-"); }
+    | '/'                            { $$ = mm_strdup("/"); }
+    | '%'                            { $$ = mm_strdup("%"); }
+    | NULL_P                        { $$ = mm_strdup("NULL"); }
+    | S_ADD                            { $$ = mm_strdup("+="); }
+    | S_AND                            { $$ = mm_strdup("&&"); }
+    | S_ANYTHING                    { $$ = make_name(); }
+    | S_AUTO                        { $$ = mm_strdup("auto"); }
+    | S_CONST                        { $$ = mm_strdup("const"); }
+    | S_DEC                            { $$ = mm_strdup("--"); }
+    | S_DIV                            { $$ = mm_strdup("/="); }
+    | S_DOTPOINT                    { $$ = mm_strdup(".*"); }
+    | S_EQUAL                        { $$ = mm_strdup("=="); }
+    | S_EXTERN                        { $$ = mm_strdup("extern"); }
+    | S_INC                            { $$ = mm_strdup("++"); }
+    | S_LSHIFT                        { $$ = mm_strdup("<<"); }
+    | S_MEMBER                        { $$ = mm_strdup("->"); }
+    | S_MEMPOINT                    { $$ = mm_strdup("->*"); }
+    | S_MOD                            { $$ = mm_strdup("%="); }
+    | S_MUL                            { $$ = mm_strdup("*="); }
+    | S_NEQUAL                        { $$ = mm_strdup("!="); }
+    | S_OR                            { $$ = mm_strdup("||"); }
+    | S_REGISTER                    { $$ = mm_strdup("register"); }
+    | S_RSHIFT                        { $$ = mm_strdup(">>"); }
+    | S_STATIC                        { $$ = mm_strdup("static"); }
+    | S_SUB                            { $$ = mm_strdup("-="); }
+    | S_TYPEDEF                        { $$ = mm_strdup("typedef"); }
+    | S_VOLATILE                    { $$ = mm_strdup("volatile"); }
+    | SQL_BOOL                        { $$ = mm_strdup("bool"); }
+    | ENUM_P                        { $$ = mm_strdup("enum"); }
+    | HOUR_P                        { $$ = mm_strdup("hour"); }
+    | INT_P                            { $$ = mm_strdup("int"); }
+    | SQL_LONG                        { $$ = mm_strdup("long"); }
+    | MINUTE_P                        { $$ = mm_strdup("minute"); }
+    | MONTH_P                        { $$ = mm_strdup("month"); }
+    | SECOND_P                        { $$ = mm_strdup("second"); }
+    | SQL_SHORT                        { $$ = mm_strdup("short"); }
+    | SQL_SIGNED                    { $$ = mm_strdup("signed"); }
+    | SQL_STRUCT                    { $$ = mm_strdup("struct"); }
+    | SQL_UNSIGNED                    { $$ = mm_strdup("unsigned"); }
+    | YEAR_P                        { $$ = mm_strdup("year"); }
+    | CHAR_P                        { $$ = mm_strdup("char"); }
+    | FLOAT_P                        { $$ = mm_strdup("float"); }
+    | TO                            { $$ = mm_strdup("to"); }
+    | UNION                            { $$ = mm_strdup("union"); }
+    | VARCHAR                        { $$ = mm_strdup("varchar"); }
+    | '['                            { $$ = mm_strdup("["); }
+    | ']'                            { $$ = mm_strdup("]"); }
+    | '='                            { $$ = mm_strdup("="); }
+    | ':'                            { $$ = mm_strdup(":"); }
+    ;
+
+DeallocateStmt: DEALLOCATE prepared_name
+    {
+        check_declared_list($2);
+        $$ = $2;
+    }
+    | DEALLOCATE PREPARE prepared_name
+    {
+        check_declared_list($3);
+        $$ = $3;
+    }
+    | DEALLOCATE ALL
+    {
+        $$ = mm_strdup("all");
+    }
+    | DEALLOCATE PREPARE ALL
+    {
+        $$ = mm_strdup("all");
+    }
+    ;
+
+Iresult: Iconst
+    {
+        $$ = $1;
+    }
+    | '(' Iresult ')'
+    {
+        $$ = cat_str(3, mm_strdup("("), $2, mm_strdup(")"));
+    }
+    | Iresult '+' Iresult
+    {
+        $$ = cat_str(3, $1, mm_strdup("+"), $3);
+    }
+    | Iresult '-' Iresult
+    {
+        $$ = cat_str(3, $1, mm_strdup("-"), $3);
+    }
+    | Iresult '*' Iresult
+    {
+        $$ = cat_str(3, $1, mm_strdup("*"), $3);
+    }
+    | Iresult '/' Iresult
+    {
+        $$ = cat_str(3, $1, mm_strdup("/"), $3);
+    }
+    | Iresult '%' Iresult
+    {
+        $$ = cat_str(3, $1, mm_strdup("%"), $3);
+    }
+    | ecpg_sconst
+    {
+        $$ = $1;
+    }
+    | ColId
+    {
+        $$ = $1;
+    }
+    | ColId '(' var_type ')'
+    {
+        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(")"));
+    }
+    ;
+
+execute_rest: /* EMPTY */
+    {
+        $$ = EMPTY;
+    }
+    | ecpg_using opt_ecpg_into
+    {
+        $$ = EMPTY;
+    }
+    | ecpg_into ecpg_using
+    {
+        $$ = EMPTY;
+    }
+    | ecpg_into
+    {
+        $$ = EMPTY;
+    }
+    ;
+
+ecpg_into: INTO into_list
+    {
+        $$ = EMPTY;
+    }
+    | into_descriptor
+    {
+        $$ = $1;
+    }
+    ;
+
+opt_ecpg_into: /* EMPTY */
+    {
+        $$ = EMPTY;
+    }
+    | ecpg_into
+    {
+        $$ = $1;
+    }
+    ;
+
+ecpg_fetch_into: ecpg_into
+    {
+        $$ = $1;
+    }
     | using_descriptor
     {
         struct variable *var;
@@ -2015,20 +2494,31 @@ ecpg_fetch_into: ecpg_into    { $$ = $1; }
     }
     ;

-opt_ecpg_fetch_into:    /* EMPTY */    { $$ = EMPTY; }
-    | ecpg_fetch_into        { $$ = $1; }
+opt_ecpg_fetch_into: /* EMPTY */
+    {
+        $$ = EMPTY;
+    }
+    | ecpg_fetch_into
+    {
+        $$ = $1;
+    }
     ;

 %%

-void base_yyerror(const char *error)
+void
+base_yyerror(const char *error)
 {
     /* translator: %s is typically the translation of "syntax error" */
     mmerror(PARSE_ERROR, ET_ERROR, "%s at or near \"%s\"",
             _(error), token_start ? token_start : base_yytext);
 }

-void parser_init(void)
+void
+parser_init(void)
 {
- /* This function is empty. It only exists for compatibility with the backend parser right now. */
+    /*
+     * This function is empty. It only exists for compatibility with the
+     * backend parser right now.
+     */
 }
diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l
index bcfbd0978b..c2bd01330a 100644
--- a/src/interfaces/ecpg/preproc/pgc.l
+++ b/src/interfaces/ecpg/preproc/pgc.l
@@ -35,8 +35,8 @@

 extern YYSTYPE base_yylval;

-static int        xcdepth = 0;    /* depth of nesting in slash-star comments */
-static char       *dolqstart = NULL;    /* current $foo$ quote start string */
+static int    xcdepth = 0;        /* depth of nesting in slash-star comments */
+static char *dolqstart = NULL;    /* current $foo$ quote start string */

 /*
  * literalbuf is used to accumulate literal values when multiple rules
@@ -44,15 +44,15 @@ static char       *dolqstart = NULL;    /* current $foo$ quote start string */
  * to empty, addlit to add text.  Note that the buffer is permanently
  * malloc'd to the largest size needed so far in the current run.
  */
-static char       *literalbuf = NULL;        /* expandable buffer */
-static int        literallen;                /* actual current length */
-static int        literalalloc;            /* current allocated buffer size */
+static char *literalbuf = NULL; /* expandable buffer */
+static int    literallen;            /* actual current length */
+static int    literalalloc;        /* current allocated buffer size */

 /* Used for detecting global state together with braces_open */
-static int        parenths_open;
+static int    parenths_open;

 /* Used to tell parse_include() whether the command was #include or #include_next */
-static bool        include_next;
+static bool include_next;

 #define startlit()    (literalbuf[0] = '\0', literallen = 0)
 static void addlit(char *ytext, int yleng);
@@ -63,11 +63,11 @@ static bool ecpg_isspace(char ch);
 static bool isdefine(void);
 static bool isinformixdefine(void);

-char *token_start;
+char       *token_start;

 /* vars to keep track of start conditions when scanning literals */
-static int state_before_str_start;
-static int state_before_str_stop;
+static int    state_before_str_start;
+static int    state_before_str_stop;

 /*
  * State for handling include files and macro expansion.  We use a new
@@ -78,10 +78,10 @@ static int state_before_str_stop;
  */
 static struct _yy_buffer
 {
-    YY_BUFFER_STATE        buffer;
-    long                lineno;
-    char               *filename;
-    struct _yy_buffer  *next;
+    YY_BUFFER_STATE buffer;
+    long        lineno;
+    char       *filename;
+    struct _yy_buffer *next;
 } *yy_buffer = NULL;

 /*
@@ -540,7 +540,9 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
 <xb>{xbinside}    {
                     addlit(yytext, yyleng);
                 }
-<xb><<EOF>>        { mmfatal(PARSE_ERROR, "unterminated bit string literal"); }
+<xb><<EOF>>        {
+                    mmfatal(PARSE_ERROR, "unterminated bit string literal");
+                }

 <SQL>{xhstart}    {
                     token_start = yytext;
@@ -548,7 +550,9 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                     BEGIN(xh);
                     startlit();
                 }
-<xh><<EOF>>        { mmfatal(PARSE_ERROR, "unterminated hexadecimal string literal"); }
+<xh><<EOF>>        {
+                    mmfatal(PARSE_ERROR, "unterminated hexadecimal string literal");
+                }

 <C>{xqstart}    {
                     token_start = yytext;
@@ -559,9 +563,7 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+

 <SQL>{
 {xnstart}        {
-                    /* National character.
-                     * Transfer it as-is to the backend.
-                     */
+                    /* National character. Transfer it as-is to the backend. */
                     token_start = yytext;
                     state_before_str_start = YYSTATE;
                     BEGIN(xn);
@@ -650,29 +652,37 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                     }
                 }

-<xq,xe,xn,xus>{xqdouble}    { addlit(yytext, yyleng); }
-<xqc>{xqcquote}                { addlit(yytext, yyleng); }
-<xq,xqc,xn,xus>{xqinside}    { addlit(yytext, yyleng); }
-<xe>{xeinside}  {
+<xq,xe,xn,xus>{xqdouble} {
+                    addlit(yytext, yyleng);
+                }
+<xqc>{xqcquote}    {
+                    addlit(yytext, yyleng);
+                }
+<xq,xqc,xn,xus>{xqinside} {
                     addlit(yytext, yyleng);
                 }
-<xe>{xeunicode} {
+<xe>{xeinside}    {
                     addlit(yytext, yyleng);
                 }
-<xe>{xeescape}  {
+<xe>{xeunicode}    {
                     addlit(yytext, yyleng);
                 }
-<xe>{xeoctesc}  {
+<xe>{xeescape}    {
                     addlit(yytext, yyleng);
                 }
-<xe>{xehexesc}  {
+<xe>{xeoctesc}    {
+                    addlit(yytext, yyleng);
+                }
+<xe>{xehexesc}    {
                     addlit(yytext, yyleng);
                 }
 <xe>.            {
                     /* This is only needed for \ just before EOF */
                     addlitchar(yytext[0]);
                 }
-<xq,xqc,xe,xn,xus><<EOF>>    { mmfatal(PARSE_ERROR, "unterminated quoted string"); }
+<xq,xqc,xe,xn,xus><<EOF>>    {
+                    mmfatal(PARSE_ERROR, "unterminated quoted string");
+                }

 <SQL>{
 {dolqdelim}        {
@@ -692,7 +702,7 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                 }
 } /* <SQL> */

-<xdolq>{dolqdelim} {
+<xdolq>{dolqdelim}    {
                     if (strcmp(yytext, dolqstart) == 0)
                     {
                         addlit(yytext, yyleng);
@@ -723,7 +733,9 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                     /* single quote or dollar sign */
                     addlitchar(yytext[0]);
                 }
-<xdolq><<EOF>>    { mmfatal(PARSE_ERROR, "unterminated dollar-quoted string"); }
+<xdolq><<EOF>>    {
+                    mmfatal(PARSE_ERROR, "unterminated dollar-quoted string");
+                }

 <SQL>{
 {xdstart}        {
@@ -742,6 +754,7 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                     BEGIN(state_before_str_start);
                     if (literallen == 0)
                         mmerror(PARSE_ERROR, ET_ERROR, "zero-length delimited identifier");
+
                     /*
                      * The server will truncate the identifier here.  We do
                      * not, as (1) it does not change the result; (2) we don't
@@ -762,26 +775,34 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                     BEGIN(state_before_str_start);
                     if (literallen == 0)
                         mmerror(PARSE_ERROR, ET_ERROR, "zero-length delimited identifier");
-                    /* The backend will truncate the identifier here. We do not as it does not change the result. */
+
+                    /*
+                     * The backend will truncate the identifier here. We do
+                     * not as it does not change the result.
+                     */
                     base_yylval.str = psprintf("U&\"%s\"", literalbuf);
                     return UIDENT;
                 }
-<xd,xui>{xddouble}    {
+<xd,xui>{xddouble} {
                     addlit(yytext, yyleng);
                 }
-<xd,xui>{xdinside}    {
+<xd,xui>{xdinside} {
                     addlit(yytext, yyleng);
                 }
-<xd,xui><<EOF>>    { mmfatal(PARSE_ERROR, "unterminated quoted identifier"); }
+<xd,xui><<EOF>>    {
+                    mmfatal(PARSE_ERROR, "unterminated quoted identifier");
+                }
 <C>{xdstart}    {
                     state_before_str_start = YYSTATE;
                     BEGIN(xdc);
                     startlit();
                 }
-<xdc>{xdcinside}    {
+<xdc>{xdcinside} {
                     addlit(yytext, yyleng);
                 }
-<xdc><<EOF>>    { mmfatal(PARSE_ERROR, "unterminated quoted string"); }
+<xdc><<EOF>>    {
+                    mmfatal(PARSE_ERROR, "unterminated quoted string");
+                }

 <SQL>{
 {typecast}        {
@@ -818,21 +839,20 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                     return NOT_EQUALS;
                 }

-{informix_special}    {
-              /* are we simulating Informix? */
-                if (INFORMIX_MODE)
-                {
-                    unput(':');
-                }
-                else
-                    return yytext[0];
+{informix_special} {
+                    /* are we simulating Informix? */
+                    if (INFORMIX_MODE)
+                    {
+                        unput(':');
+                    }
+                    else
+                        return yytext[0];
                 }

 {self}            {
                     /*
-                     * We may find a ';' inside a structure
-                     * definition in a TYPE or VAR statement.
-                     * This is not an EOL marker.
+                     * We may find a ';' inside a structure definition in a
+                     * TYPE or VAR statement. This is not an EOL marker.
                      */
                     if (yytext[0] == ';' && struct_level == 0)
                         BEGIN(C);
@@ -877,7 +897,8 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+

                         for (ic = nchars - 2; ic >= 0; ic--)
                         {
-                            char c = yytext[ic];
+                            char        c = yytext[ic];
+
                             if (c == '~' || c == '!' || c == '@' ||
                                 c == '#' || c == '^' || c == '&' ||
                                 c == '|' || c == '`' || c == '?' ||
@@ -890,11 +911,12 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                              * didn't find a qualifying character, so remove
                              * all trailing [+-]
                              */
-                            do {
+                            do
+                            {
                                 nchars--;
                             } while (nchars > 1 &&
-                                 (yytext[nchars - 1] == '+' ||
-                                  yytext[nchars - 1] == '-'));
+                                     (yytext[nchars - 1] == '+' ||
+                                      yytext[nchars - 1] == '-'));
                         }
                     }

@@ -902,6 +924,7 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                     {
                         /* Strip the unwanted chars from the token */
                         yyless(nchars);
+
                         /*
                          * If what we have left is only one char, and it's
                          * one of the characters matching "self", then
@@ -911,6 +934,7 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                         if (nchars == 1 &&
                             strchr(",()[].;:+-*/%^<>=", yytext[0]))
                             return yytext[0];
+
                         /*
                          * Likewise, if what we have left is two chars, and
                          * those match the tokens ">=", "<=", "=>", "<>" or
@@ -937,7 +961,7 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                 }

 {param}            {
-                    base_yylval.ival = atol(yytext+1);
+                    base_yylval.ival = atol(yytext + 1);
                     return PARAM;
                 }
 {param_junk}    {
@@ -992,16 +1016,16 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
      * Note that some trailing junk is valid in C (such as 100LL), so we
      * contain this to SQL mode.
      */
-{decinteger_junk}    {
+{decinteger_junk} {
                     mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
                 }
-{hexinteger_junk}    {
+{hexinteger_junk} {
                     mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
                 }
-{octinteger_junk}    {
+{octinteger_junk} {
                     mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
                 }
-{bininteger_junk}    {
+{bininteger_junk} {
                     mmfatal(PARSE_ERROR, "trailing junk after numeric literal");
                 }
 {numeric_junk}    {
@@ -1012,7 +1036,7 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                 }

 :{identifier}((("->"|\.){identifier})|(\[{array}\]))*    {
-                    base_yylval.str = mm_strdup(yytext+1);
+                    base_yylval.str = mm_strdup(yytext + 1);
                     return CVARIABLE;
                 }

@@ -1020,7 +1044,7 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                     /* First check to see if it's a define symbol to expand */
                     if (!isdefine())
                     {
-                        int        kwvalue;
+                        int            kwvalue;

                         /*
                          * User-defined typedefs override SQL keywords, but
@@ -1046,8 +1070,8 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                          *
                          * The backend will attempt to truncate and case-fold
                          * the identifier, but I see no good reason for ecpg
-                         * to do so; that's just another way that ecpg could get
-                         * out of step with the backend.
+                         * to do so; that's just another way that ecpg could
+                         * get out of step with the backend.
                          */
                         base_yylval.str = mm_strdup(yytext);
                         return IDENT;
@@ -1063,75 +1087,82 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
      * Begin ECPG-specific rules
      */

-<C>{exec_sql}        { BEGIN(SQL); return SQL_START; }
+<C>{exec_sql}    {
+                    BEGIN(SQL);
+                    return SQL_START;
+                }
 <C>{informix_special}    {
-                        /* are we simulating Informix? */
-                        if (INFORMIX_MODE)
-                        {
-                            BEGIN(SQL);
-                            return SQL_START;
-                        }
-                        else
-                            return S_ANYTHING;
-                     }
-<C>{ccomment}        { ECHO; }
-<C>{cppinclude}        {
-                        if (system_includes)
-                        {
-                            include_next = false;
-                            BEGIN(incl);
-                        }
-                        else
-                        {
-                            base_yylval.str = mm_strdup(yytext);
-                            return CPP_LINE;
-                        }
+                    /* are we simulating Informix? */
+                    if (INFORMIX_MODE)
+                    {
+                        BEGIN(SQL);
+                        return SQL_START;
                     }
-<C>{cppinclude_next}        {
-                        if (system_includes)
-                        {
-                            include_next = true;
-                            BEGIN(incl);
-                        }
-                        else
-                        {
-                            base_yylval.str = mm_strdup(yytext);
-                            return CPP_LINE;
-                        }
+                    else
+                        return S_ANYTHING;
+                }
+<C>{ccomment}    {
+                    ECHO;
+                }
+<C>{cppinclude}    {
+                    if (system_includes)
+                    {
+                        include_next = false;
+                        BEGIN(incl);
                     }
-<C,SQL>{cppline}    {
+                    else
+                    {
                         base_yylval.str = mm_strdup(yytext);
                         return CPP_LINE;
                     }
-<C>{identifier}        {
-                        /*
-                         * Try to detect a function name:
-                         * look for identifiers at the global scope
-                         * keep the last identifier before the first '(' and '{'
-                         */
-                        if (braces_open == 0 && parenths_open == 0)
-                        {
-                            if (current_function)
-                                free(current_function);
-                            current_function = mm_strdup(yytext);
-                        }
-                        /* Informix uses SQL defines only in SQL space */
-                        /* however, some defines have to be taken care of for compatibility */
-                        if ((!INFORMIX_MODE || !isinformixdefine()) && !isdefine())
-                        {
-                            int        kwvalue;
+                }
+<C>{cppinclude_next} {
+                    if (system_includes)
+                    {
+                        include_next = true;
+                        BEGIN(incl);
+                    }
+                    else
+                    {
+                        base_yylval.str = mm_strdup(yytext);
+                        return CPP_LINE;
+                    }
+                }
+<C,SQL>{cppline} {
+                    base_yylval.str = mm_strdup(yytext);
+                    return CPP_LINE;
+                }
+<C>{identifier}    {
+                    /*
+                     * Try to detect a function name:
+                     * look for identifiers at the global scope
+                     * keep the last identifier before the first '(' and '{'
+                     */
+                    if (braces_open == 0 && parenths_open == 0)
+                    {
+                        if (current_function)
+                            free(current_function);
+                        current_function = mm_strdup(yytext);
+                    }
+                    /* Informix uses SQL defines only in SQL space */
+                    /* however, some defines have to be taken care of for compatibility */
+                    if ((!INFORMIX_MODE || !isinformixdefine()) && !isdefine())
+                    {
+                        int            kwvalue;

-                            kwvalue = ScanCKeywordLookup(yytext);
-                            if (kwvalue >= 0)
-                                return kwvalue;
-                            else
-                            {
-                                base_yylval.str = mm_strdup(yytext);
-                                return IDENT;
-                            }
+                        kwvalue = ScanCKeywordLookup(yytext);
+                        if (kwvalue >= 0)
+                            return kwvalue;
+                        else
+                        {
+                            base_yylval.str = mm_strdup(yytext);
+                            return IDENT;
                         }
                     }
-<C>{xcstop}            { mmerror(PARSE_ERROR, ET_ERROR, "nested /* ... */ comments"); }
+                }
+<C>{xcstop}        {
+                    mmerror(PARSE_ERROR, ET_ERROR, "nested /* ... */ comments");
+                }
 <C>":"                { return ':'; }
 <C>";"                { return ';'; }
 <C>","                { return ','; }
@@ -1167,44 +1198,46 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
 <C>{other}            { return S_ANYTHING; }
 <C>{exec_sql}{define}{space}*    { BEGIN(def_ident); }
 <C>{informix_special}{define}{space}*    {
-                        /* are we simulating Informix? */
-                        if (INFORMIX_MODE)
-                        {
-                            BEGIN(def_ident);
-                        }
-                        else
-                        {
-                            yyless(1);
-                            return S_ANYTHING;
-                        }
+                    /* are we simulating Informix? */
+                    if (INFORMIX_MODE)
+                    {
+                        BEGIN(def_ident);
+                    }
+                    else
+                    {
+                        yyless(1);
+                        return S_ANYTHING;
                     }
-<C>{exec_sql}{undef}{space}*        { BEGIN(undef); }
+                }
+<C>{exec_sql}{undef}{space}* {
+                    BEGIN(undef);
+                }
 <C>{informix_special}{undef}{space}* {
-                        /* are we simulating Informix? */
-                        if (INFORMIX_MODE)
-                        {
-                            BEGIN(undef);
-                        }
-                        else
-                        {
-                            yyless(1);
-                            return S_ANYTHING;
-                        }
+                    /* are we simulating Informix? */
+                    if (INFORMIX_MODE)
+                    {
+                        BEGIN(undef);
                     }
+                    else
+                    {
+                        yyless(1);
+                        return S_ANYTHING;
+                    }
+                }
 <undef>{identifier}{space}*";" {
-                    struct _defines *ptr, *ptr2 = NULL;
-                    int i;
+                    struct _defines *ptr,
+                               *ptr2 = NULL;
+                    int            i;

                     /*
-                     *    Skip the ";" and trailing whitespace. Note that yytext
-                     *    contains at least one non-space character plus the ";"
+                     * Skip the ";" and trailing whitespace. Note that yytext
+                     * contains at least one non-space character plus the ";"
                      */
-                    for (i = strlen(yytext)-2;
+                    for (i = strlen(yytext) - 2;
                          i > 0 && ecpg_isspace(yytext[i]);
                          i--)
                         ;
-                    yytext[i+1] = '\0';
-
+                    yytext[i + 1] = '\0';

                     /* Find and unset any matching define; should be only 1 */
                     for (ptr = defines; ptr; ptr2 = ptr, ptr = ptr->next)
@@ -1230,88 +1263,90 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                     BEGIN(C);
                 }
 <undef>{other}|\n {
-                        mmfatal(PARSE_ERROR, "missing identifier in EXEC SQL UNDEF command");
-                        yyterminate();
+                    mmfatal(PARSE_ERROR, "missing identifier in EXEC SQL UNDEF command");
+                    yyterminate();
+                }
+<C>{exec_sql}{include}{space}* {
+                    BEGIN(incl);
                 }
-<C>{exec_sql}{include}{space}*    { BEGIN(incl); }
 <C>{informix_special}{include}{space}* {
-                      /* are we simulating Informix? */
-                      if (INFORMIX_MODE)
-                      {
-                          BEGIN(incl);
-                      }
-                      else
-                      {
-                          yyless(1);
-                          return S_ANYTHING;
-                      }
+                    /* are we simulating Informix? */
+                    if (INFORMIX_MODE)
+                    {
+                        BEGIN(incl);
                     }
-<C,xskip>{exec_sql}{ifdef}{space}* {
-                      if (preproc_tos >= MAX_NESTED_IF-1)
-                          mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
-                      preproc_tos++;
-                      stacked_if_value[preproc_tos].active = false;
-                      stacked_if_value[preproc_tos].saw_active = false;
-                      stacked_if_value[preproc_tos].else_branch = false;
-                      ifcond = true;
-                      BEGIN(xcond);
+                    else
+                    {
+                        yyless(1);
+                        return S_ANYTHING;
                     }
+                }
+<C,xskip>{exec_sql}{ifdef}{space}* {
+                    if (preproc_tos >= MAX_NESTED_IF - 1)
+                        mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
+                    preproc_tos++;
+                    stacked_if_value[preproc_tos].active = false;
+                    stacked_if_value[preproc_tos].saw_active = false;
+                    stacked_if_value[preproc_tos].else_branch = false;
+                    ifcond = true;
+                    BEGIN(xcond);
+                }
 <C,xskip>{informix_special}{ifdef}{space}* {
-                      /* are we simulating Informix? */
-                      if (INFORMIX_MODE)
-                      {
-                          if (preproc_tos >= MAX_NESTED_IF-1)
-                              mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
-                          preproc_tos++;
-                          stacked_if_value[preproc_tos].active = false;
-                          stacked_if_value[preproc_tos].saw_active = false;
-                          stacked_if_value[preproc_tos].else_branch = false;
-                          ifcond = true;
-                          BEGIN(xcond);
-                      }
-                      else
-                      {
-                          yyless(1);
-                          return S_ANYTHING;
-                      }
+                    /* are we simulating Informix? */
+                    if (INFORMIX_MODE)
+                    {
+                        if (preproc_tos >= MAX_NESTED_IF - 1)
+                            mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
+                        preproc_tos++;
+                        stacked_if_value[preproc_tos].active = false;
+                        stacked_if_value[preproc_tos].saw_active = false;
+                        stacked_if_value[preproc_tos].else_branch = false;
+                        ifcond = true;
+                        BEGIN(xcond);
                     }
-<C,xskip>{exec_sql}{ifndef}{space}* {
-                      if (preproc_tos >= MAX_NESTED_IF-1)
-                          mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
-                      preproc_tos++;
-                      stacked_if_value[preproc_tos].active = false;
-                      stacked_if_value[preproc_tos].saw_active = false;
-                      stacked_if_value[preproc_tos].else_branch = false;
-                      ifcond = false;
-                      BEGIN(xcond);
+                    else
+                    {
+                        yyless(1);
+                        return S_ANYTHING;
                     }
+                }
+<C,xskip>{exec_sql}{ifndef}{space}* {
+                    if (preproc_tos >= MAX_NESTED_IF - 1)
+                        mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
+                    preproc_tos++;
+                    stacked_if_value[preproc_tos].active = false;
+                    stacked_if_value[preproc_tos].saw_active = false;
+                    stacked_if_value[preproc_tos].else_branch = false;
+                    ifcond = false;
+                    BEGIN(xcond);
+                }
 <C,xskip>{informix_special}{ifndef}{space}* {
-                      /* are we simulating Informix? */
-                      if (INFORMIX_MODE)
-                      {
-                          if (preproc_tos >= MAX_NESTED_IF-1)
-                              mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
-                          preproc_tos++;
-                          stacked_if_value[preproc_tos].active = false;
-                          stacked_if_value[preproc_tos].saw_active = false;
-                          stacked_if_value[preproc_tos].else_branch = false;
-                          ifcond = false;
-                          BEGIN(xcond);
-                      }
-                      else
-                      {
-                          yyless(1);
-                          return S_ANYTHING;
-                      }
-                    }
-<C,xskip>{exec_sql}{elif}{space}*    {
-                        if (preproc_tos == 0)
-                            mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\"");
-                        if (stacked_if_value[preproc_tos].else_branch)
-                            mmfatal(PARSE_ERROR, "missing \"EXEC SQL ENDIF;\"");
-                        ifcond = true;
+                    /* are we simulating Informix? */
+                    if (INFORMIX_MODE)
+                    {
+                        if (preproc_tos >= MAX_NESTED_IF - 1)
+                            mmfatal(PARSE_ERROR, "too many nested EXEC SQL IFDEF conditions");
+                        preproc_tos++;
+                        stacked_if_value[preproc_tos].active = false;
+                        stacked_if_value[preproc_tos].saw_active = false;
+                        stacked_if_value[preproc_tos].else_branch = false;
+                        ifcond = false;
                         BEGIN(xcond);
                     }
+                    else
+                    {
+                        yyless(1);
+                        return S_ANYTHING;
+                    }
+                }
+<C,xskip>{exec_sql}{elif}{space}* {
+                    if (preproc_tos == 0)
+                        mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\"");
+                    if (stacked_if_value[preproc_tos].else_branch)
+                        mmfatal(PARSE_ERROR, "missing \"EXEC SQL ENDIF;\"");
+                    ifcond = true;
+                    BEGIN(xcond);
+                }
 <C,xskip>{informix_special}{elif}{space}* {
                     /* are we simulating Informix? */
                     if (INFORMIX_MODE)
@@ -1330,7 +1365,8 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                     }
                 }

-<C,xskip>{exec_sql}{else}{space}*";" {    /* only exec sql endif pops the stack, so take care of duplicated 'else' */
+<C,xskip>{exec_sql}{else}{space}*";" {
+    /* only exec sql endif pops the stack, so take care of duplicated 'else' */
                     if (preproc_tos == 0)
                         mmfatal(PARSE_ERROR, "missing matching \"EXEC SQL IFDEF\" / \"EXEC SQL IFNDEF\"");
                     else if (stacked_if_value[preproc_tos].else_branch)
@@ -1339,7 +1375,7 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                     {
                         stacked_if_value[preproc_tos].else_branch = true;
                         stacked_if_value[preproc_tos].active =
-                            (stacked_if_value[preproc_tos-1].active &&
+                            (stacked_if_value[preproc_tos - 1].active &&
                              !stacked_if_value[preproc_tos].saw_active);
                         stacked_if_value[preproc_tos].saw_active = true;

@@ -1349,7 +1385,7 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                             BEGIN(xskip);
                     }
                 }
-<C,xskip>{informix_special}{else}{space}*";"    {
+<C,xskip>{informix_special}{else}{space}*";" {
                     /* are we simulating Informix? */
                     if (INFORMIX_MODE)
                     {
@@ -1361,7 +1397,7 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                         {
                             stacked_if_value[preproc_tos].else_branch = true;
                             stacked_if_value[preproc_tos].active =
-                                (stacked_if_value[preproc_tos-1].active &&
+                                (stacked_if_value[preproc_tos - 1].active &&
                                  !stacked_if_value[preproc_tos].saw_active);
                             stacked_if_value[preproc_tos].saw_active = true;

@@ -1384,11 +1420,11 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                         preproc_tos--;

                     if (stacked_if_value[preproc_tos].active)
-                       BEGIN(C);
+                        BEGIN(C);
                     else
-                       BEGIN(xskip);
+                        BEGIN(xskip);
                 }
-<C,xskip>{informix_special}{endif}{space}*";"    {
+<C,xskip>{informix_special}{endif}{space}*";" {
                     /* are we simulating Informix? */
                     if (INFORMIX_MODE)
                     {
@@ -1409,23 +1445,24 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                     }
                 }

-<xskip>{other}        { /* ignore */ }
+<xskip>{other}    { /* ignore */ }

 <xcond>{identifier}{space}*";" {
                     {
                         struct _defines *defptr;
                         unsigned int i;
-                        bool this_active;
+                        bool        this_active;

                         /*
-                         *    Skip the ";" and trailing whitespace. Note that yytext
-                         *    contains at least one non-space character plus the ";"
+                         * Skip the ";" and trailing whitespace. Note that
+                         * yytext contains at least one non-space character
+                         * plus the ";"
                          */
-                        for (i = strlen(yytext)-2;
+                        for (i = strlen(yytext) - 2;
                              i > 0 && ecpg_isspace(yytext[i]);
                              i--)
-                            ;
-                        yytext[i+1] = '\0';
+                            /* skip */ ;
+                        yytext[i + 1] = '\0';

                         /* Does a definition exist? */
                         for (defptr = defines; defptr; defptr = defptr->next)
@@ -1441,7 +1478,7 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+

                         this_active = (defptr ? ifcond : !ifcond);
                         stacked_if_value[preproc_tos].active =
-                            (stacked_if_value[preproc_tos-1].active &&
+                            (stacked_if_value[preproc_tos - 1].active &&
                              !stacked_if_value[preproc_tos].saw_active &&
                              this_active);
                         stacked_if_value[preproc_tos].saw_active |= this_active;
@@ -1453,59 +1490,59 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                         BEGIN(xskip);
                 }

-<xcond>{other}|\n    {
-                mmfatal(PARSE_ERROR, "missing identifier in EXEC SQL IFDEF command");
-                yyterminate();
-            }
+<xcond>{other}|\n {
+                    mmfatal(PARSE_ERROR, "missing identifier in EXEC SQL IFDEF command");
+                    yyterminate();
+                }
 <def_ident>{identifier} {
-                newdefsymbol = mm_strdup(yytext);
-                BEGIN(def);
-                startlit();
-            }
-<def_ident>{other}|\n    {
-                mmfatal(PARSE_ERROR, "missing identifier in EXEC SQL DEFINE command");
-                yyterminate();
-            }
-<def>{space}*";"    {
-                        struct _defines *ptr;
+                    newdefsymbol = mm_strdup(yytext);
+                    BEGIN(def);
+                    startlit();
+                }
+<def_ident>{other}|\n {
+                    mmfatal(PARSE_ERROR, "missing identifier in EXEC SQL DEFINE command");
+                    yyterminate();
+                }
+<def>{space}*";" {
+                    struct _defines *ptr;

-                        /* Does it already exist? */
-                        for (ptr = defines; ptr != NULL; ptr = ptr->next)
-                        {
-                            if (strcmp(newdefsymbol, ptr->name) == 0)
-                            {
-                                free(ptr->value);
-                                ptr->value = mm_strdup(literalbuf);
-                                /* Don't leak newdefsymbol */
-                                free(newdefsymbol);
-                                break;
-                            }
-                        }
-                        if (ptr == NULL)
+                    /* Does it already exist? */
+                    for (ptr = defines; ptr != NULL; ptr = ptr->next)
+                    {
+                        if (strcmp(newdefsymbol, ptr->name) == 0)
                         {
-                            /* Not present, make a new entry */
-                            ptr = (struct _defines *) mm_alloc(sizeof(struct _defines));
-
-                            ptr->name = newdefsymbol;
+                            free(ptr->value);
                             ptr->value = mm_strdup(literalbuf);
-                            ptr->cmdvalue = NULL;
-                            ptr->used = NULL;
-                            ptr->next = defines;
-                            defines = ptr;
+                            /* Don't leak newdefsymbol */
+                            free(newdefsymbol);
+                            break;
                         }
-
-                        BEGIN(C);
                     }
-<def>[^;]            { addlit(yytext, yyleng); }
-<incl>\<[^\>]+\>{space}*";"?        {    parse_include(); }
+                    if (ptr == NULL)
+                    {
+                        /* Not present, make a new entry */
+                        ptr = (struct _defines *) mm_alloc(sizeof(struct _defines));
+
+                        ptr->name = newdefsymbol;
+                        ptr->value = mm_strdup(literalbuf);
+                        ptr->cmdvalue = NULL;
+                        ptr->used = NULL;
+                        ptr->next = defines;
+                        defines = ptr;
+                    }
+
+                    BEGIN(C);
+                }
+<def>[^;]        { addlit(yytext, yyleng); }
+<incl>\<[^\>]+\>{space}*";"?    {    parse_include(); }
 <incl>{dquote}{xdinside}{dquote}{space}*";"?    {    parse_include(); }
-<incl>[^;\<\>\"]+";"        { parse_include(); }
-<incl>{other}|\n        {
+<incl>[^;\<\>\"]+";"    { parse_include(); }
+<incl>{other}|\n {
                     mmfatal(PARSE_ERROR, "syntax error in EXEC SQL INCLUDE command");
                     yyterminate();
                 }

-<<EOF>>                {
+<<EOF>>            {
                     if (yy_buffer == NULL)
                     {
                         /* No more input */
@@ -1520,7 +1557,7 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                     {
                         /* Revert to previous input source */
                         struct _yy_buffer *yb = yy_buffer;
-                        int i;
+                        int            i;
                         struct _defines *ptr;

                         /* Check to see if we are exiting a macro value */
@@ -1552,11 +1589,12 @@ cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+

                         if (i != 0)
                             output_line_number();
-
                     }
                 }

-<INITIAL>{other}|\n    { mmfatal(PARSE_ERROR, "internal error: unreachable state; please report this to <%s>",
PACKAGE_BUGREPORT);} 
+<INITIAL>{other}|\n {
+                    mmfatal(PARSE_ERROR, "internal error: unreachable state; please report this to <%s>",
PACKAGE_BUGREPORT);
+                }

 %%

@@ -1592,15 +1630,15 @@ static void
 addlit(char *ytext, int yleng)
 {
     /* enlarge buffer if needed */
-    if ((literallen+yleng) >= literalalloc)
+    if ((literallen + yleng) >= literalalloc)
     {
         do
             literalalloc *= 2;
-        while ((literallen+yleng) >= literalalloc);
+        while ((literallen + yleng) >= literalalloc);
         literalbuf = (char *) realloc(literalbuf, literalalloc);
     }
     /* append new data, add trailing null */
-    memcpy(literalbuf+literallen, ytext, yleng);
+    memcpy(literalbuf + literallen, ytext, yleng);
     literallen += yleng;
     literalbuf[literallen] = '\0';
 }
@@ -1609,7 +1647,7 @@ static void
 addlitchar(unsigned char ychar)
 {
     /* enlarge buffer if needed */
-    if ((literallen+1) >= literalalloc)
+    if ((literallen + 1) >= literalalloc)
     {
         literalalloc *= 2;
         literalbuf = (char *) realloc(literalbuf, literalalloc);
@@ -1648,12 +1686,12 @@ parse_include(void)
     /* got the include file name */
     struct _yy_buffer *yb;
     struct _include_path *ip;
-    char inc_file[MAXPGPATH];
+    char        inc_file[MAXPGPATH];
     unsigned int i;

     yb = mm_alloc(sizeof(struct _yy_buffer));

-    yb->buffer =    YY_CURRENT_BUFFER;
+    yb->buffer = YY_CURRENT_BUFFER;
     yb->lineno = yylineno;
     yb->filename = input_filename;
     yb->next = yy_buffer;
@@ -1661,10 +1699,10 @@ parse_include(void)
     yy_buffer = yb;

     /*
-     * skip the ";" if there is one and trailing whitespace. Note that
-     * yytext contains at least one non-space character plus the ";"
+     * skip the ";" if there is one and trailing whitespace. Note that yytext
+     * contains at least one non-space character plus the ";"
      */
-    for (i = strlen(yytext)-2;
+    for (i = strlen(yytext) - 2;
          i > 0 && ecpg_isspace(yytext[i]);
          i--)
         ;
@@ -1672,17 +1710,21 @@ parse_include(void)
     if (yytext[i] == ';')
         i--;

-    yytext[i+1] = '\0';
+    yytext[i + 1] = '\0';

     yyin = NULL;

     /* If file name is enclosed in '"' remove these and look only in '.' */
-    /* Informix does look into all include paths though, except filename starts with '/' */
+
+    /*
+     * Informix does look into all include paths though, except filename
+     * starts with '/'
+     */
     if (yytext[0] == '"' && yytext[i] == '"' &&
         ((compat != ECPG_COMPAT_INFORMIX && compat != ECPG_COMPAT_INFORMIX_SE) || yytext[1] == '/'))
     {
         yytext[i] = '\0';
-        memmove(yytext, yytext+1, strlen(yytext));
+        memmove(yytext, yytext + 1, strlen(yytext));

         strlcpy(inc_file, yytext, sizeof(inc_file));
         yyin = fopen(inc_file, "r");
@@ -1701,7 +1743,7 @@ parse_include(void)
         if ((yytext[0] == '"' && yytext[i] == '"') || (yytext[0] == '<' && yytext[i] == '>'))
         {
             yytext[i] = '\0';
-            memmove(yytext, yytext+1, strlen(yytext));
+            memmove(yytext, yytext + 1, strlen(yytext));
         }

         for (ip = include_paths; yyin == NULL && ip != NULL; ip = ip->next)
@@ -1711,7 +1753,7 @@ parse_include(void)
                 fprintf(stderr, _("Error: include path \"%s/%s\" is too long on line %d, skipping\n"), ip->path,
yytext,yylineno); 
                 continue;
             }
-            snprintf (inc_file, sizeof(inc_file), "%s/%s", ip->path, yytext);
+            snprintf(inc_file, sizeof(inc_file), "%s/%s", ip->path, yytext);
             yyin = fopen(inc_file, "r");
             if (!yyin)
             {
@@ -1721,10 +1763,14 @@ parse_include(void)
                     yyin = fopen(inc_file, "r");
                 }
             }
-            /* if the command was "include_next" we have to disregard the first hit */
+
+            /*
+             * if the command was "include_next" we have to disregard the
+             * first hit
+             */
             if (yyin && include_next)
             {
-                fclose (yyin);
+                fclose(yyin);
                 yyin = NULL;
                 include_next = false;
             }
@@ -1734,7 +1780,7 @@ parse_include(void)
         mmfatal(NO_INCLUDE_FILE, "could not open include file \"%s\" on line %d", yytext, yylineno);

     input_filename = mm_strdup(inc_file);
-    yy_switch_to_buffer(yy_create_buffer(yyin,YY_BUF_SIZE));
+    yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
     yylineno = 1;
     output_line_number();

@@ -1779,7 +1825,7 @@ isdefine(void)

             yb = mm_alloc(sizeof(struct _yy_buffer));

-            yb->buffer =  YY_CURRENT_BUFFER;
+            yb->buffer = YY_CURRENT_BUFFER;
             yb->lineno = yylineno;
             yb->filename = mm_strdup(input_filename);
             yb->next = yy_buffer;
@@ -1823,7 +1869,7 @@ isinformixdefine(void)

         yb = mm_alloc(sizeof(struct _yy_buffer));

-        yb->buffer =  YY_CURRENT_BUFFER;
+        yb->buffer = YY_CURRENT_BUFFER;
         yb->lineno = yylineno;
         yb->filename = mm_strdup(input_filename);
         yb->next = yy_buffer;
--
2.39.3

From ca5046e4990e75d971802c88e1bc5c02f646af84 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Wed, 17 Apr 2024 15:39:15 -0400
Subject: [PATCH v1 2/6] Clean up documentation of parse.pl, and add more input
 checking.

README.parser is the user's manual, such as it is, for parse.pl.
It's rather poorly written if you ask me; so try to improve it.
(More could be written here, but this at least covers the same
info in a more organized fashion.)

Also, the single solitary line of usage info in parse.pl itself
was a lie.  Replace.

Add some error checks that the ecpg.addons entries meet the syntax
rules set forth in README.parser.  One of them didn't, but
accidentally worked anyway because the logic in include_addon is
such that 'block' is the default behavior.

Also add a cross-check that each ecpg.addons entry is matched exactly
once in the backend grammar.  This exposed that there are two dead
entries there --- they are dead because the %replace_types table in
parse.pl causes their nonterminals to be ignored altogether.
Removing them doesn't change the generated preproc.y file.

(This implies that check_rules.pl is completely worthless and should
be nuked: it adds build cycles and maintenance effort while failing
to reliably accomplish its one job of detecting dead rules.  I've
not done that here, though.)
---
 src/interfaces/ecpg/preproc/README.parser | 119 ++++++++++++++--------
 src/interfaces/ecpg/preproc/ecpg.addons   |  11 +-
 src/interfaces/ecpg/preproc/parse.pl      |  53 ++++++++--
 3 files changed, 120 insertions(+), 63 deletions(-)

diff --git a/src/interfaces/ecpg/preproc/README.parser b/src/interfaces/ecpg/preproc/README.parser
index ddc3061d48..7f7b0d5381 100644
--- a/src/interfaces/ecpg/preproc/README.parser
+++ b/src/interfaces/ecpg/preproc/README.parser
@@ -1,42 +1,77 @@
-ECPG modifies and extends the core grammar in a way that
-1) every token in ECPG is <str> type. New tokens are
-   defined in ecpg.tokens, types are defined in ecpg.type
-2) most tokens from the core grammar are simply converted
-   to literals concatenated together to form the SQL string
-   passed to the server, this is done by parse.pl.
-3) some rules need side-effects, actions are either added
-   or completely overridden (compared to the basic token
-   concatenation) for them, these are defined in ecpg.addons,
-   the rules for ecpg.addons are explained below.
-4) new grammar rules are needed for ECPG metacommands.
-   These are in ecpg.trailer.
-5) ecpg.header contains common functions, etc. used by
-   actions for grammar rules.
-
-In "ecpg.addons", every modified rule follows this pattern:
-       ECPG: dumpedtokens postfix
-where "dumpedtokens" is simply tokens from core gram.y's
-rules concatenated together. e.g. if gram.y has this:
-       ruleA: tokenA tokenB tokenC {...}
-then "dumpedtokens" is "ruleAtokenAtokenBtokenC".
-"postfix" above can be:
-a) "block" - the automatic rule created by parse.pl is completely
-    overridden, the code block has to be written completely as
-    it were in a plain bison grammar
-b) "rule" - the automatic rule is extended on, so new syntaxes
-    are accepted for "ruleA". E.g.:
-      ECPG: ruleAtokenAtokenBtokenC rule
-          | tokenD tokenE { action_code; }
-          ...
-    It will be substituted with:
-      ruleA: <original syntax forms and actions up to and including
-                    "tokenA tokenB tokenC">
-             | tokenD tokenE { action_code; }
-             ...
-c) "addon" - the automatic action for the rule (SQL syntax constructed
-    from the tokens concatenated together) is prepended with a new
-    action code part. This code part is written as is's already inside
-    the { ... }
-
-Multiple "addon" or "block" lines may appear together with the
-new code block if the code block is common for those rules.
+ECPG's grammar (preproc.y) is built by parse.pl from the
+backend's grammar (gram.y) plus various add-on rules.
+Some notes:
+
+1) Most input matching core grammar productions is simply converted
+   to strings and concatenated together to form the SQL string
+   passed to the server.  parse.pl can automatically build the
+   grammar actions needed to do this.
+2) Some grammar rules need special actions that are added to or
+   completely override the default token-concatenation behavior.
+   This is controlled by ecpg.addons as explained below.
+3) Additional grammar rules are needed for ECPG's own commands.
+   These are in ecpg.trailer, as is the "epilogue" part of preproc.y.
+4) ecpg.header contains the "prologue" part of preproc.y, including
+   support functions, Bison options, etc.
+5) Additional terminals added by ECPG must be defined in ecpg.tokens.
+   Additional nonterminals added by ECPG must be defined in ecpg.type.
+
+ecpg.header, ecpg.tokens, ecpg.type, and ecpg.trailer are just
+copied verbatim into preproc.y at appropriate points.
+
+ecpg.addons contains entries that begin with a line like
+       ECPG: concattokens ruletype
+and typically have one or more following lines that are the code
+for a grammar action.  Any line not starting with ECPG: is taken
+to be part of the code block for the preceding ECPG: line.
+
+"concattokens" identifies which gram.y production this entry affects.
+It is simply the target nonterminal and the tokens from the gram.y rule
+concatenated together.  For example, to modify the action for a gram.y
+rule like this:
+      target: tokenA tokenB tokenC {...}
+"concattokens" would be "targettokenAtokenBtokenC".  If we want to
+modify a non-first alternative for a nonterminal, we still write the
+nonterminal.  For example, "concattokens" should be "targettokenDtokenE"
+to affect the second alternative in:
+      target: tokenA tokenB tokenC {...}
+              | tokenD tokenE {...}
+
+"ruletype" is one of:
+
+a) "block" - the automatic action that parse.pl would create is
+    completely overridden.  Instead the entry's code block is emitted.
+    The code block must include the braces ({}) needed for a Bison action.
+
+b) "addon" - the entry's code block is inserted into the generated
+    action, ahead of the automatic token-concatenation code.
+    In this case the code block need not contain braces, since
+    it will be inserted within braces.
+
+c) "rule" - the automatic action is emitted, but then the entry's
+    code block is added verbatim afterwards.  This typically is
+    used to add new alternatives to a nonterminal of the core grammar.
+    For example, given the entry:
+      ECPG: targettokenAtokenBtokenC rule
+          | tokenD tokenE { custom_action; }
+    what will be emitted is
+      target: tokenA tokenB tokenC { automatic_action; }
+          | tokenD tokenE { custom_action; }
+
+Multiple ECPG: entries can share the same code block, if the
+same action is needed for all.  When an ECPG: line is immediately
+followed by another one, it is not assigned an empty code block;
+rather the next nonempty code block is assumed to apply to all
+preceding ECPG: entries.
+
+In addition to the modifications specified by ecpg.addons,
+parse.pl contains some tables that list backend grammar
+productions to be ignored or modified.
+
+Nonterminals that construct strings (as described above) should be
+given <str> type, which is parse.pl's default assumption for
+nonterminals found in gram.y.  That can be overridden at need by
+making an entry in parse.pl's %replace_types table.  %replace_types
+can also be used to suppress output of a nonterminal's rules
+altogether (in which case ecpg.trailer had better provide replacement
+rules, since the nonterminal will still be referred to elsewhere).
diff --git a/src/interfaces/ecpg/preproc/ecpg.addons b/src/interfaces/ecpg/preproc/ecpg.addons
index e7dce4e404..6a1893553b 100644
--- a/src/interfaces/ecpg/preproc/ecpg.addons
+++ b/src/interfaces/ecpg/preproc/ecpg.addons
@@ -497,7 +497,7 @@ ECPG: opt_array_boundsopt_array_bounds'['']' block
             $$.index2 = mm_strdup($3);
         $$.str = cat_str(4, $1.str, mm_strdup("["), $3, mm_strdup("]"));
     }
-ECPG: opt_array_bounds
+ECPG: opt_array_bounds block
     {
         $$.index1 = mm_strdup("-1");
         $$.index2 = mm_strdup("-1");
@@ -510,15 +510,6 @@ ECPG: IconstICONST block
 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");
diff --git a/src/interfaces/ecpg/preproc/parse.pl b/src/interfaces/ecpg/preproc/parse.pl
index fe8d3e5178..edb6630e09 100644
--- a/src/interfaces/ecpg/preproc/parse.pl
+++ b/src/interfaces/ecpg/preproc/parse.pl
@@ -1,7 +1,13 @@
 #!/usr/bin/perl
 # src/interfaces/ecpg/preproc/parse.pl
 # parser generator for ecpg version 2
-# call with backend parser as stdin
+#
+# See README.parser for some explanation of what this does.
+#
+# Command-line options:
+#   --srcdir: where to find ecpg-provided input files (default ".")
+#   --parser: the backend gram.y file to read (required, no default)
+#   --output: where to write preproc.y (required, no default)
 #
 # Copyright (c) 2007-2024, PostgreSQL Global Development Group
 #
@@ -148,6 +154,14 @@ dump_buffer('trailer');

 close($parserfh);

+# Cross-check that we don't have dead or ambiguous addon rules.
+foreach (keys %addons)
+{
+    die "addon rule $_ was never used\n" if $addons{$_}{used} == 0;
+    die "addon rule $_ was matched multiple times\n" if $addons{$_}{used} > 1;
+}
+
+
 sub main
 {
   line: while (<$parserfh>)
@@ -487,7 +501,10 @@ sub include_addon
     my $rec = $addons{$block};
     return 0 unless $rec;

-    my $rectype = (defined $rec->{type}) ? $rec->{type} : '';
+    # Track usage for later cross-check
+    $rec->{used}++;
+
+    my $rectype = $rec->{type};
     if ($rectype eq 'rule')
     {
         dump_fields($stmt_mode, $fields, ' { ');
@@ -668,10 +685,10 @@ sub dump_line
 }

 =top
-    load addons into cache
+    load ecpg.addons into %addons hash.  The result is something like
     %addons = {
-        stmtClosePortalStmt => { 'type' => 'block', 'lines' => [ "{", "if (INFORMIX_MODE)" ..., "}" ] },
-        stmtViewStmt => { 'type' => 'rule', 'lines' => [ "| ECPGAllocateDescr", ... ] }
+        stmtClosePortalStmt => { 'type' => 'block', 'lines' => [ "{", "if (INFORMIX_MODE)" ..., "}" ], 'used' => 0 },
+        stmtViewStmt => { 'type' => 'rule', 'lines' => [ "| ECPGAllocateDescr", ... ], 'used' => 0 }
     }

 =cut
@@ -681,17 +698,24 @@ sub preload_addons
     my $filename = $srcdir . "/ecpg.addons";
     open(my $fh, '<', $filename) or die;

-    # there may be multiple lines starting ECPG: and then multiple lines of code.
-    # the code need to be add to all prior ECPG records.
-    my (@needsRules, @code, $record);
+    # There may be multiple ECPG: lines and then multiple lines of code.
+    # Each block of code needs to be added to all prior ECPG records.
+    my (@needsRules, @code);

     # there may be comments before the first ECPG line, skip them
     my $skip = 1;
     while (<$fh>)
     {
-        if (/^ECPG:\s(\S+)\s?(\w+)?/)
+        if (/^ECPG:\s+(\S+)\s+(\w+)\s*$/)
         {
+            # Found an ECPG: line, so we're done skipping the header
             $skip = 0;
+            # Validate record type and target
+            die "invalid record type $2 in addon rule for $1\n"
+              unless ($2 eq 'block' or $2 eq 'addon' or $2 eq 'rule');
+            die "duplicate addon rule for $1\n" if (exists $addons{$1});
+            # If we had some preceding code lines, attach them to all
+            # as-yet-unfinished records.
             if (@code)
             {
                 for my $x (@needsRules)
@@ -701,20 +725,27 @@ sub preload_addons
                 @code = ();
                 @needsRules = ();
             }
-            $record = {};
+            my $record = {};
             $record->{type} = $2;
             $record->{lines} = [];
-            if (exists $addons{$1}) { die "Ga! there are dups!\n"; }
+            $record->{used} = 0;
             $addons{$1} = $record;
             push(@needsRules, $record);
         }
+        elsif (/^ECPG:/)
+        {
+            # Complain if preceding regex failed to match
+            die "incorrect syntax in ECPG line: $_\n";
+        }
         else
         {
+            # Non-ECPG line: add to @code unless we're still skipping
             next if $skip;
             push(@code, $_);
         }
     }
     close($fh);
+    # Deal with final code block
     if (@code)
     {
         for my $x (@needsRules)
--
2.39.3

From 174b99115b28b1e194bd4913303b1141d97da222 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Wed, 17 Apr 2024 15:43:55 -0400
Subject: [PATCH v1 3/6] Major cleanup, simplification, and documentation of
 parse.pl.

Remove a lot of cruft, clean up and document what's left.
This produces the same output as before, except for fewer
blank lines.  (It's not like we're making any attempt to
match the layout of gram.y, so I removed the one bit of
logic that seemed to have that in mind.)
---
 src/interfaces/ecpg/preproc/parse.pl | 486 ++++++++++++++++-----------
 1 file changed, 292 insertions(+), 194 deletions(-)

diff --git a/src/interfaces/ecpg/preproc/parse.pl b/src/interfaces/ecpg/preproc/parse.pl
index edb6630e09..7e53401dd9 100644
--- a/src/interfaces/ecpg/preproc/parse.pl
+++ b/src/interfaces/ecpg/preproc/parse.pl
@@ -31,27 +31,11 @@ GetOptions(
     'output=s' => \$outfile,
     'parser=s' => \$parser,) or die "wrong arguments";

-# open parser / output file early, to raise errors early
-open(my $parserfh, '<', $parser) or die "could not open parser file $parser";
-open(my $outfh, '>', $outfile) or die "could not open output file $outfile";
-
-my $copymode = 0;
-my $brace_indent = 0;
-my $yaccmode = 0;
-my $in_rule = 0;
-my $header_included = 0;
-my $has_feature_not_supported = 0;
-my $has_if_command = 0;
-my $tokenmode = 0;
-
-my (%buff, $infield, $comment, %tokens, %addons);
-my ($stmt_mode, @fields);
-my $line = '';
-my $non_term_id;

+# These hash tables define additional transformations to apply to
+# grammar rules.

-# some token have to be replaced by other symbols
-# either in the rule
+# Substitutions to apply to tokens whenever they are seen in a rule.
 my %replace_token = (
     'BCONST' => 'ecpg_bconst',
     'FCONST' => 'ecpg_fconst',
@@ -60,7 +44,9 @@ my %replace_token = (
     'IDENT' => 'ecpg_ident',
     'PARAM' => 'ecpg_param',);

-# or in the block
+# Substitutions to apply to terminal token names to reconstruct the
+# literal form of the token.  (There is also a hard-wired substitution
+# rule that strips trailing '_P'.)
 my %replace_string = (
     'FORMAT_LA' => 'format',
     'NOT_LA' => 'not',
@@ -75,14 +61,16 @@ my %replace_string = (
     'GREATER_EQUALS' => '>=',
     'NOT_EQUALS' => '<>',);

-# specific replace_types for specific non-terminals - never include the ':'
-# ECPG-only replace_types are defined in ecpg-replace_types
+# This hash can provide a result type to override '<str>' for nonterminals
+# that need that, or it can specify 'ignore' to cause us to skip the rule
+# for that nonterminal.  (In that case, ecpg.trailer had better provide
+# a substitute rule.)
 my %replace_types = (
     'PrepareStmt' => '<prep>',
     'ExecuteStmt' => '<exec>',
     'opt_array_bounds' => '<index>',

-    # "ignore" means: do not create type and rules for this non-term-id
+    # "ignore" means: do not create type and rules for this nonterminal
     'parse_toplevel' => 'ignore',
     'stmtmulti' => 'ignore',
     'CreateAsStmt' => 'ignore',
@@ -97,9 +85,12 @@ my %replace_types = (
     'plassign_target' => 'ignore',
     'plassign_equals' => 'ignore',);

-# these replace_line commands excise certain keywords from the core keyword
-# lists.  Be sure to account for these in ColLabel and related productions.
+# This hash provides an "ignore" option or substitute expansion for any
+# rule or rule alternative.  The hash key is the same "concattokens" tag
+# used for lookup in ecpg.addons.
 my %replace_line = (
+    # These entries excise certain keywords from the core keyword lists.
+    # Be sure to account for these in ColLabel and related productions.
     'unreserved_keywordCONNECTION' => 'ignore',
     'unreserved_keywordCURRENT_P' => 'ignore',
     'unreserved_keywordDAY_P' => 'ignore',
@@ -137,10 +128,77 @@ my %replace_line = (
       'PREPARE prepared_name prep_type_clause AS PreparableStmt',
     'var_nameColId' => 'ECPGColId');

+
+# Declare assorted state variables.
+
+# yaccmode counts the '%%' separator lines we have seen, so that we can
+# distinguish prologue, rules, and epilogue sections of gram.y.
+my $yaccmode = 0;
+# in /* ... */ comment?
+my $comment = 0;
+# in { ... } braced text?
+my $brace_indent = 0;
+# within a rule (production)?
+my $in_rule = 0;
+# count of alternatives processed within the current rule.
+my $alt_count = 0;
+# copymode = 1 when we want to emit the current rule to preproc.y.
+# If it's 0, we have decided to ignore the current rule, and should
+# skip all output until we get to the ending semicolon.
+my $copymode = 0;
+# tokenmode = 1 indicates we are processing %token and following declarations.
+my $tokenmode = 0;
+# stmt_mode = 1 indicates that we are processing the 'stmt:' rule.
+my $stmt_mode = 0;
+# Hacky state for emitting feature-not-supported warnings.
+my $has_feature_not_supported = 0;
+my $has_if_command = 0;
+
+# %addons holds the rules loaded from ecpg.addons.
+my %addons;
+
+# %buff holds various named "buffers", which are just strings that accumulate
+# the output destined for different sections of the preproc.y file.  This
+# allows us to process the input in one pass even though the resulting output
+# needs to appear in various places.  See dump_buffer calls below for the
+# set of buffer names and the order in which they'll be dumped.
+my %buff;
+
+# %tokens contains an entry for every name we have discovered to be a token.
+my %tokens;
+
+# $non_term_id is the name of the nonterminal that is the target of the
+# current rule.
+my $non_term_id;
+
+# $line holds the reconstructed rule text (that is, RHS token list) that
+# we plan to emit for the current rule.
+my $line = '';
+
+# @fields holds the items to be emitted in the token-concatenation action
+# for the current rule (assuming we emit one).  "$N" refers to the N'th
+# input token of the rule; anything else is a string to emit literally.
+# (We assume no such string can need to start with '$'.)
+my @fields;
+
+
+# Open parser / output file early, to raise errors early.
+open(my $parserfh, '<', $parser) or die "could not open parser file $parser";
+open(my $outfh, '>', $outfile) or die "could not open output file $outfile";
+
+# Read the various ecpg-supplied input files.
+# ecpg.addons is loaded into the %addons hash, while the other files
+# are just copied into buffers for verbatim output later.
 preload_addons();
+include_file('header', 'ecpg.header');
+include_file('tokens', 'ecpg.tokens');
+include_file('ecpgtype', 'ecpg.type');
+include_file('trailer', 'ecpg.trailer');

+# Read gram.y, and do the bulk of the processing.
 main();

+# Emit data from the various buffers we filled.
 dump_buffer('header');
 dump_buffer('tokens');
 dump_buffer('types');
@@ -149,7 +207,6 @@ dump_buffer('orig_tokens');
 print $outfh '%%', "\n";
 print $outfh 'prog: statements;', "\n";
 dump_buffer('rules');
-include_file('trailer', 'ecpg.trailer');
 dump_buffer('trailer');

 close($parserfh);
@@ -162,83 +219,67 @@ foreach (keys %addons)
 }


+# Read the backend grammar.
 sub main
 {
   line: while (<$parserfh>)
     {
         chomp;

-        # comment out the line below to make the result file match (blank line wise)
-        # the prior version.
-        #next if ($_ eq '');
-
-        # Dump the action for a rule -
-        # stmt_mode indicates if we are processing the 'stmt:'
-        # rule (mode==0 means normal,  mode==1 means stmt:)
-        # flds are the fields to use. These may start with a '$' - in
-        # which case they are the result of a previous non-terminal
-        #
-        # if they don't start with a '$' then they are token name
-        #
-        # len is the number of fields in flds...
-        # leadin is the padding to apply at the beginning (just use for formatting)
-
         if (/^%%/)
         {
-            $tokenmode = 2;
-            $copymode = 1;
+            # New file section, so advance yaccmode.
             $yaccmode++;
-            $infield = 0;
+            # We are no longer examining %token and related commands.
+            $tokenmode = 0;
+            # Shouldn't be anything else on the line.
+            next line;
         }

+        # Hacky check for rules that throw FEATURE_NOT_SUPPORTED
+        # (do this before $_ has a chance to get clobbered)
         if ($yaccmode == 1)
         {
-            # Check for rules that throw FEATURE_NOT_SUPPORTED
             $has_feature_not_supported = 1 if /ERRCODE_FEATURE_NOT_SUPPORTED/;
             $has_if_command = 1 if /^\s*if/;
         }

+        # We track %prec per-line, not per-rule, which is not quite right
+        # but there are no counterexamples in gram.y at present.
         my $prec = 0;

-        # Make sure any braces are split
+        # Make sure any braces are split into separate fields
         s/{/ { /g;
         s/}/ } /g;

-        # Any comments are split
+        # Likewise for comment start/end markers
         s|\/\*| /* |g;
         s|\*\/| */ |g;

         # Now split the line into individual fields
         my @arr = split(' ');

+        # Ignore empty lines
         if (!@arr)
         {
-            # empty line: in tokenmode 1, emit an empty line, else ignore
-            if ($tokenmode == 1)
-            {
-                add_to_buffer('orig_tokens', '');
-            }
             next line;
         }

-        if ($arr[0] eq '%token' && $tokenmode == 0)
+        # Once we have seen %token in the prologue, we assume all that follows
+        # up to the '%%' separator is %token and associativity declarations.
+        # Collect and process that as necessary.
+        if ($arr[0] eq '%token' && $yaccmode == 0)
         {
             $tokenmode = 1;
-            include_file('tokens', 'ecpg.tokens');
-        }
-        elsif ($arr[0] eq '%type' && $header_included == 0)
-        {
-            include_file('header', 'ecpg.header');
-            include_file('ecpgtype', 'ecpg.type');
-            $header_included = 1;
         }

         if ($tokenmode == 1)
         {
+            # Collect everything of interest on this line into $str.
             my $str = '';
-            my $prior = '';
             for my $a (@arr)
             {
+                # Skip comments.
                 if ($a eq '/*')
                 {
                     $comment++;
@@ -253,40 +294,50 @@ sub main
                 {
                     next;
                 }
+
+                # If it's "<something>", it's a type in a %token declaration,
+                # which we can just drop.
                 if (substr($a, 0, 1) eq '<')
                 {
                     next;
-
-                    # its a type
                 }
+
+                # Remember that this is a token.  This will also make entries
+                # for "%token" and the associativity keywords such as "%left",
+                # which should be harmless so it's not worth the trouble to
+                # avoid it.  If a token appears both in %token and in an
+                # associativity declaration, we'll redundantly re-set its
+                # entry, which is also OK.
                 $tokens{$a} = 1;

+                # Accumulate the line in $str.
                 $str = $str . ' ' . $a;
-                if ($a eq 'IDENT' && $prior eq '%nonassoc')
-                {

-                    # add more tokens to the list
+                # HACK: insert our own %nonassoc line after IDENT.
+                # XXX: this seems pretty wrong, IDENT is not last on its line!
+                if ($a eq 'IDENT' && $arr[0] eq '%nonassoc')
+                {
                     $str = $str . "\n%nonassoc CSTRING";
                 }
-                $prior = $a;
             }
+            # Save the lightly-processed line in orig_tokens.
             add_to_buffer('orig_tokens', $str);
             next line;
         }

-        # Don't worry about anything if we're not in the right section of gram.y
+        # The rest is only appropriate if we're in the rules section of gram.y
         if ($yaccmode != 1)
         {
             next line;
         }

-
-        # Go through each field in turn
+        # Go through each word of the rule in turn
         for (
             my $fieldIndexer = 0;
             $fieldIndexer < scalar(@arr);
             $fieldIndexer++)
         {
+            # Detect and ignore comments and braced action text
             if ($arr[$fieldIndexer] eq '*/' && $comment)
             {
                 $comment = 0;
@@ -298,15 +349,10 @@ sub main
             }
             elsif ($arr[$fieldIndexer] eq '/*')
             {
-
-                # start of a multiline comment
+                # start of a possibly-multiline comment
                 $comment = 1;
                 next;
             }
-            elsif ($arr[$fieldIndexer] eq '//')
-            {
-                next line;
-            }
             elsif ($arr[$fieldIndexer] eq '}')
             {
                 $brace_indent--;
@@ -317,29 +363,35 @@ sub main
                 $brace_indent++;
                 next;
             }
-
             if ($brace_indent > 0)
             {
                 next;
             }
+
+            # OK, it's not a comment or part of an action.
+            # Check for ';' ending the current rule, or '|' ending the
+            # current alternative.
             if ($arr[$fieldIndexer] eq ';')
             {
                 if ($copymode)
                 {
-                    if ($infield)
-                    {
-                        dump_line($stmt_mode, \@fields);
-                    }
+                    # Print the accumulated rule.
+                    emit_rule(\@fields);
                     add_to_buffer('rules', ";\n\n");
                 }
                 else
                 {
+                    # End of an ignored rule; revert to copymode = 1.
                     $copymode = 1;
                 }
+
+                # Reset for the next rule.
                 @fields = ();
-                $infield = 0;
                 $line = '';
                 $in_rule = 0;
+                $alt_count = 0;
+                $has_feature_not_supported = 0;
+                $has_if_command = 0;
                 next;
             }

@@ -347,56 +399,68 @@ sub main
             {
                 if ($copymode)
                 {
-                    if ($infield)
-                    {
-                        $infield = $infield + dump_line($stmt_mode, \@fields);
-                    }
-                    if ($infield > 1)
-                    {
-                        $line = '| ';
-                    }
+                    # Print the accumulated alternative.
+                    # Increment $alt_count for each non-ignored alternative.
+                    $alt_count += emit_rule(\@fields);
                 }
+
+                # Reset for the next alternative.
                 @fields = ();
+                # Start the next line with '|' if we've printed at least one
+                # alternative.
+                if ($alt_count > 1)
+                {
+                    $line = '| ';
+                }
+                else
+                {
+                    $line = '';
+                }
+                $has_feature_not_supported = 0;
+                $has_if_command = 0;
                 next;
             }

+            # Apply replace_token substitution if we have one.
             if (exists $replace_token{ $arr[$fieldIndexer] })
             {
                 $arr[$fieldIndexer] = $replace_token{ $arr[$fieldIndexer] };
             }

-            # Are we looking at a declaration of a non-terminal ?
-            if (($arr[$fieldIndexer] =~ /[A-Za-z0-9]+:/)
+            # Are we looking at a declaration of a non-terminal?
+            # We detect that by seeing ':' on the end of the token or
+            # as the next token.
+            if (($arr[$fieldIndexer] =~ /[A-Za-z0-9]+:$/)
                 || (   $fieldIndexer + 1 < scalar(@arr)
                     && $arr[ $fieldIndexer + 1 ] eq ':'))
             {
+                # Extract the non-terminal, sans : if any
                 $non_term_id = $arr[$fieldIndexer];
                 $non_term_id =~ tr/://d;

+                # Consume the ':' if it's separate
+                if (!($arr[$fieldIndexer] =~ /[A-Za-z0-9]+:$/))
+                {
+                    $fieldIndexer++;
+                }
+
+                # Check for %replace_types override of nonterminal's type
                 if (not defined $replace_types{$non_term_id})
                 {
+                    # By default, the type is <str>
                     $replace_types{$non_term_id} = '<str>';
-                    $copymode = 1;
                 }
                 elsif ($replace_types{$non_term_id} eq 'ignore')
                 {
+                    # We'll ignore this nonterminal and rule altogether.
                     $copymode = 0;
-                    $line = '';
                     next line;
                 }
-                $line = $line . ' ' . $arr[$fieldIndexer];

-                # Do we have the : attached already ?
-                # If yes, we'll have already printed the ':'
-                if (!($arr[$fieldIndexer] =~ '[A-Za-z0-9]+:'))
-                {
+                # OK, we want this rule.
+                $copymode = 1;

-                    # Consume the ':' which is next...
-                    $line = $line . ':';
-                    $fieldIndexer++;
-                }
-
-                # Special mode?
+                # Set special mode for the "stmt:" rule.
                 if ($non_term_id eq 'stmt')
                 {
                     $stmt_mode = 1;
@@ -405,69 +469,73 @@ sub main
                 {
                     $stmt_mode = 0;
                 }
+
+                # Emit appropriate %type declaration for this nonterminal.
                 my $tstr =
                     '%type '
                   . $replace_types{$non_term_id} . ' '
                   . $non_term_id;
                 add_to_buffer('types', $tstr);

-                if ($copymode)
-                {
-                    add_to_buffer('rules', $line);
-                }
+                # Emit the target part of the rule.
+                # Note: the leading space is just to match
+                # the old, rather weird output logic.
+                $tstr = ' ' . $non_term_id . ':';
+                add_to_buffer('rules', $tstr);
+
+                # Prepare for reading the fields (tokens) of the rule.
                 $line = '';
                 @fields = ();
-                $infield = 1;
                 die "unterminated rule at grammar line $.\n"
                   if $in_rule;
                 $in_rule = 1;
+                $alt_count = 1;
                 next;
             }
             elsif ($copymode)
             {
+                # Not a nonterminal declaration, so just add it to $line.
                 $line = $line . ' ' . $arr[$fieldIndexer];
             }
+
+            # %prec and whatever follows it should get added to $line,
+            # but not to @fields.
             if ($arr[$fieldIndexer] eq '%prec')
             {
                 $prec = 1;
                 next;
             }

+            # Emit transformed version of token to @fields if appropriate.
             if (   $copymode
                 && !$prec
                 && !$comment
-                && $fieldIndexer < scalar(@arr)
-                && length($arr[$fieldIndexer])
-                && $infield)
+                && $in_rule)
             {
-                if ($arr[$fieldIndexer] ne 'Op'
-                    && ((   defined $tokens{ $arr[$fieldIndexer] }
-                            && $tokens{ $arr[$fieldIndexer] } > 0)
-                        || $arr[$fieldIndexer] =~ /'.+'/)
-                    || $stmt_mode == 1)
+                my $S = $arr[$fieldIndexer];
+
+                # If it's a known terminal token (other than Op) or a literal
+                # character, we need to emit the equivalent string, which'll
+                # later get wrapped into a C string literal, perhaps after
+                # merging with adjacent strings.
+                if ($S ne 'Op'
+                    && (defined $tokens{$S}
+                        || $S =~ /^'.+'$/))
                 {
-                    my $S;
-                    if (exists $replace_string{ $arr[$fieldIndexer] })
-                    {
-                        $S = $replace_string{ $arr[$fieldIndexer] };
-                    }
-                    else
-                    {
-                        $S = $arr[$fieldIndexer];
-                    }
-                    $S =~ s/_P//g;
+                    # Apply replace_string substitution if any.
+                    $S = $replace_string{$S} if (exists $replace_string{$S});
+                    # Automatically strip _P if present.
+                    $S =~ s/_P$//;
+                    # And get rid of quotes if it's a literal character.
                     $S =~ tr/'//d;
-                    if ($stmt_mode == 1)
-                    {
-                        push(@fields, $S);
-                    }
-                    else
-                    {
-                        push(@fields, lc($S));
-                    }
+                    # Finally, downcase and push into @fields.
+                    push(@fields, lc($S));
                 }
                 else
                 {
+                    # Otherwise, push a $N reference to this input token.
+                    # (We assume this cannot be confused with anything the
+                    # above code would produce.)
                     push(@fields, '$' . (scalar(@fields) + 1));
                 }
             }
@@ -495,94 +563,108 @@ sub include_file
     return;
 }

-sub include_addon
+# Emit the semantic action for the current rule.
+# This function mainly accounts for any modifications specified
+# by an ecpg.addons entry.
+sub emit_rule_action
 {
-    my ($buffer, $block, $fields, $stmt_mode) = @_;
-    my $rec = $addons{$block};
-    return 0 unless $rec;
+    my ($tag, $fields) = @_;

-    # Track usage for later cross-check
+    # See if we have an addons entry; if not, just emit default action
+    my $rec = $addons{$tag};
+    if (!$rec)
+    {
+        emit_default_action($fields, 0);
+        return;
+    }
+
+    # Track addons entry usage for later cross-check
     $rec->{used}++;

     my $rectype = $rec->{type};
     if ($rectype eq 'rule')
     {
-        dump_fields($stmt_mode, $fields, ' { ');
+        # Emit default action and then the code block.
+        emit_default_action($fields, 0);
     }
     elsif ($rectype eq 'addon')
     {
+        # Emit the code block wrapped in the same braces as the default action.
         add_to_buffer('rules', ' { ');
     }

-    #add_to_buffer( $stream, $_ );
-    #We have an array to add to the buffer, we'll add it ourself instead of
-    #calling add_to_buffer, which does not know about arrays
-
-    push(@{ $buff{$buffer} }, @{ $rec->{lines} });
+    # Emit the addons entry's code block.
+    # We have an array to add to the buffer, we'll add it directly instead of
+    # calling add_to_buffer, which does not know about arrays.
+    push(@{ $buff{'rules'} }, @{ $rec->{lines} });

     if ($rectype eq 'addon')
     {
-        dump_fields($stmt_mode, $fields, '');
+        emit_default_action($fields, 1);
     }
-
-
-    # if we added something (ie there are lines in our array), return 1
-    return 1 if (scalar(@{ $rec->{lines} }) > 0);
-    return 0;
+    return;
 }

-
-# include_addon does this same thing, but does not call this
-# sub... so if you change this, you need to fix include_addon too
+# Add the given line to the specified buffer.
 #   Pass:  buffer_name, string_to_append
+# Note we add a newline automatically.
 sub add_to_buffer
 {
     push(@{ $buff{ $_[0] } }, "$_[1]\n");
     return;
 }

+# Dump the specified buffer to the output file.
 sub dump_buffer
 {
     my ($buffer) = @_;
+    # Label the output for debugging purposes.
     print $outfh '/* ', $buffer, ' */', "\n";
     my $ref = $buff{$buffer};
     print $outfh @$ref;
     return;
 }

-sub dump_fields
+# Emit the default action (usually token concatenation) for the current rule.
+#   Pass: fields array, brace_printed boolean
+# brace_printed should be true if caller already printed action's open brace.
+sub emit_default_action
 {
-    my ($mode, $flds, $ln) = @_;
+    my ($flds, $brace_printed) = @_;
     my $len = scalar(@$flds);

-    if ($mode == 0)
+    if ($stmt_mode == 0)
     {
-
-        #Normal
-        add_to_buffer('rules', $ln);
+        # Normal rule
         if ($has_feature_not_supported and not $has_if_command)
         {
             # The backend unconditionally reports
             # FEATURE_NOT_SUPPORTED in this rule, so let's emit
             # a warning on the ecpg side.
+            if (!$brace_printed)
+            {
+                add_to_buffer('rules', ' { ');
+                $brace_printed = 1;
+            }
             add_to_buffer('rules',
                 'mmerror(PARSE_ERROR, ET_WARNING, "unsupported feature will be passed to server");'
             );
         }
-        $has_feature_not_supported = 0;
-        $has_if_command = 0;

         if ($len == 0)
         {
-
-            # We have no fields ?
+            # Empty rule
+            if (!$brace_printed)
+            {
+                add_to_buffer('rules', ' { ');
+                $brace_printed = 1;
+            }
             add_to_buffer('rules', ' $$=EMPTY; }');
         }
         else
         {
-
-            # Go through each field and try to 'aggregate' the tokens
-            # into a single 'mm_strdup' where possible
+            # Go through each field and aggregate consecutive literal tokens
+            # into a single 'mm_strdup' call.
             my @flds_new;
             my $str;
             for (my $z = 0; $z < $len; $z++)
@@ -600,8 +682,10 @@ sub dump_fields
                     if ($z >= $len - 1
                         || substr($flds->[ $z + 1 ], 0, 1) eq '$')
                     {
-
-                        # We're at the end...
+                        # Can't combine any more literals; push to @flds_new.
+                        # This code would need work if any literals contain
+                        # backslash or double quote, but right now that never
+                        # happens.
                         push(@flds_new, "mm_strdup(\"$str\")");
                         last;
                     }
@@ -614,49 +698,62 @@ sub dump_fields
             $len = scalar(@flds_new);
             if ($len == 1)
             {
-
-                # Straight assignment
+                # Single field can be handled by straight assignment
+                if (!$brace_printed)
+                {
+                    add_to_buffer('rules', ' { ');
+                    $brace_printed = 1;
+                }
                 $str = ' $$ = ' . $flds_new[0] . ';';
                 add_to_buffer('rules', $str);
             }
             else
             {
-
-                # Need to concatenate the results to form
-                # our final string
+                # Need to concatenate the results to form our final string
+                if (!$brace_printed)
+                {
+                    add_to_buffer('rules', ' { ');
+                    $brace_printed = 1;
+                }
                 $str =
                   ' $$ = cat_str(' . $len . ',' . join(',', @flds_new) . ');';
                 add_to_buffer('rules', $str);
             }
-            add_to_buffer('rules', '}');
+            add_to_buffer('rules', '}') if ($brace_printed);
         }
     }
     else
     {
-
-        # we're in the stmt: rule
+        # We're in the "stmt:" rule, where we need to output special actions.
+        # This code assumes that no ecpg.addons entry applies.
         if ($len)
         {
-
-            # or just the statement ...
+            # Any regular kind of statement calls output_statement
             add_to_buffer('rules',
                 ' { output_statement($1, 0, ECPGst_normal); }');
         }
         else
         {
+            # The empty production for stmt: do nothing
             add_to_buffer('rules', ' { $$ = NULL; }');
         }
     }
     return;
 }

-
-sub dump_line
+# Print the accumulated rule text (in $line) and the appropriate action.
+# Ordinarily return 1.  However, if the rule matches an "ignore"
+# entry in %replace_line, then do nothing and return 0.
+sub emit_rule
 {
-    my ($stmt_mode, $fields) = @_;
-    my $block = $non_term_id . $line;
-    $block =~ tr/ |//d;
-    my $rep = $replace_line{$block};
+    my ($fields) = @_;
+
+    # compute tag to be used as lookup key in %replace_line and %addons
+    my $tag = $non_term_id . $line;
+    $tag =~ tr/ |//d;
+
+    # apply replace_line substitution if any
+    my $rep = $replace_line{$tag};
     if ($rep)
     {
         if ($rep eq 'ignore')
@@ -664,6 +761,7 @@ sub dump_line
             return 0;
         }

+        # non-ignore entries replace the line, but we'd better keep any '|'
         if (index($line, '|') != -1)
         {
             $line = '| ' . $rep;
@@ -672,15 +770,15 @@ sub dump_line
         {
             $line = $rep;
         }
-        $block = $non_term_id . $line;
-        $block =~ tr/ |//d;
+
+        # recompute tag for use in emit_rule_action
+        $tag = $non_term_id . $line;
+        $tag =~ tr/ |//d;
     }
+
+    # Emit $line, then print the appropriate action.
     add_to_buffer('rules', $line);
-    my $i = include_addon('rules', $block, $fields, $stmt_mode);
-    if ($i == 0)
-    {
-        dump_fields($stmt_mode, $fields, ' { ');
-    }
+    emit_rule_action($tag, $fields);
     return 1;
 }

--
2.39.3

From 526a2212bf1bf9f0dfa16a17456e8a1778cac086 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 18 Apr 2024 12:33:57 -0400
Subject: [PATCH v1 4/6] Re-implement ecpg preprocessor's string management.

Most productions in the preprocessor grammar construct strings
representing SQL or C statements or fragments thereof.  Instead
of returning these as <str> results of the productions, return
them as "location" values, taking advantage of Bison's flexibility
about what a location is.  We aren't really giving up anything
thereby, since ecpg's error reports have always just given line
numbers, and that's tracked separately.  The advantage of this
is that a single instance of the YYLLOC_DEFAULT macro can
perform all the work needed by the vast majority of productions,
including all the ones made automatically by parse.pl.  This
avoids having large numbers of effectively-identical productions,
which tickles an optimization inefficiency in recent versions of
clang.  (This patch reduces the compilation time for preproc.o
by more than 100-fold with clang 16.)  The compiled parser is
noticeably smaller as well.

A disadvantage of this approach is that YYLLOC_DEFAULT is applied
before running the production's semantic action (if any).  This
means it cannot use the method favored by cat_str() of free'ing
all the input strings; if the action needs to look at the input
strings, it'd be looking at dangling storage.  As this stands,
therefore, it leaks memory like a sieve.  This is already a big
patch though, and fixing the memory management seems like a
separable problem, so let's leave that for the next step.
(This does remove some free() calls that I'd have had to touch
anyway, in the expectation that the next step will manage
memory reclamation quite differently.)
---
 src/interfaces/ecpg/preproc/README.parser    |   32 +-
 src/interfaces/ecpg/preproc/ecpg.addons      |  294 ++---
 src/interfaces/ecpg/preproc/ecpg.header      |   63 +-
 src/interfaces/ecpg/preproc/ecpg.trailer     | 1148 +++++++-----------
 src/interfaces/ecpg/preproc/ecpg.type        |  127 --
 src/interfaces/ecpg/preproc/output.c         |   16 +-
 src/interfaces/ecpg/preproc/parse.pl         |  215 +---
 src/interfaces/ecpg/preproc/parser.c         |   58 +-
 src/interfaces/ecpg/preproc/preproc_extern.h |   15 +-
 9 files changed, 752 insertions(+), 1216 deletions(-)

diff --git a/src/interfaces/ecpg/preproc/README.parser b/src/interfaces/ecpg/preproc/README.parser
index 7f7b0d5381..412465c79b 100644
--- a/src/interfaces/ecpg/preproc/README.parser
+++ b/src/interfaces/ecpg/preproc/README.parser
@@ -4,8 +4,8 @@ Some notes:

 1) Most input matching core grammar productions is simply converted
    to strings and concatenated together to form the SQL string
-   passed to the server.  parse.pl can automatically build the
-   grammar actions needed to do this.
+   passed to the server.  This is handled mostly automatically,
+   as described below.
 2) Some grammar rules need special actions that are added to or
    completely override the default token-concatenation behavior.
    This is controlled by ecpg.addons as explained below.
@@ -14,11 +14,31 @@ Some notes:
 4) ecpg.header contains the "prologue" part of preproc.y, including
    support functions, Bison options, etc.
 5) Additional terminals added by ECPG must be defined in ecpg.tokens.
-   Additional nonterminals added by ECPG must be defined in ecpg.type.
+   Additional nonterminals added by ECPG must be defined in ecpg.type,
+   but only if they have non-void result type, which most don't.

 ecpg.header, ecpg.tokens, ecpg.type, and ecpg.trailer are just
 copied verbatim into preproc.y at appropriate points.

+
+In the original implementation of ecpg, the strings constructed
+by grammar rules were returned as the Bison result of each rule.
+This led to a large number of effectively-identical rule actions,
+which caused compilation-time problems with some versions of clang.
+Now, rules that need to return a string are declared as having
+void type (which in Bison means leaving out any %type declaration
+for them).  Instead, we abuse Bison's "location tracking" mechanism
+to carry the string results, which allows a single YYLLOC_DEFAULT
+call to handle the standard token-concatenation behavior for the
+vast majority of the rules.  Rules that don't need to do anything
+else can omit a semantic action altogether.  Rules that need to
+construct an output string specially can do so, but they should
+assign it to "@$" rather than the usual "$$"; also, to reference
+the string value of the N'th input token, write "@N" not "$N".
+(But rules that return something other than a simple string
+continue to use the normal Bison notations.)
+
+
 ecpg.addons contains entries that begin with a line like
        ECPG: concattokens ruletype
 and typically have one or more following lines that are the code
@@ -69,9 +89,9 @@ parse.pl contains some tables that list backend grammar
 productions to be ignored or modified.

 Nonterminals that construct strings (as described above) should be
-given <str> type, which is parse.pl's default assumption for
-nonterminals found in gram.y.  That can be overridden at need by
-making an entry in parse.pl's %replace_types table.  %replace_types
+given void type, which is parse.pl's default assumption for
+nonterminals found in gram.y.  If the result should be of some other
+type, make an entry in parse.pl's %replace_types table.  %replace_types
 can also be used to suppress output of a nonterminal's rules
 altogether (in which case ecpg.trailer had better provide replacement
 rules, since the nonterminal will still be referred to elsewhere).
diff --git a/src/interfaces/ecpg/preproc/ecpg.addons b/src/interfaces/ecpg/preproc/ecpg.addons
index 6a1893553b..24ee54554e 100644
--- a/src/interfaces/ecpg/preproc/ecpg.addons
+++ b/src/interfaces/ecpg/preproc/ecpg.addons
@@ -3,36 +3,35 @@ ECPG: stmtClosePortalStmt block
     {
         if (INFORMIX_MODE)
         {
-            if (pg_strcasecmp($1 + strlen("close "), "database") == 0)
+            if (pg_strcasecmp(@1 + strlen("close "), "database") == 0)
             {
                 if (connection)
                     mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in CLOSE DATABASE statement");

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

-        output_statement($1, 0, ECPGst_normal);
+        output_statement(@1, 0, ECPGst_normal);
     }
 ECPG: stmtDeallocateStmt block
     {
-        output_deallocate_prepare_statement($1);
+        output_deallocate_prepare_statement(@1);
     }
 ECPG: stmtDeclareCursorStmt block
     {
-        output_simple_statement($1, (strncmp($1, "ECPGset_var", strlen("ECPGset_var")) == 0) ? 4 : 0);
+        output_simple_statement(@1, (strncmp(@1, "ECPGset_var", strlen("ECPGset_var")) == 0) ? 4 : 0);
     }
 ECPG: stmtDiscardStmt block
 ECPG: stmtFetchStmt block
-    { output_statement($1, 1, ECPGst_normal); }
+    { output_statement(@1, 1, ECPGst_normal); }
 ECPG: stmtDeleteStmt block
 ECPG: stmtInsertStmt block
 ECPG: stmtSelectStmt block
 ECPG: stmtUpdateStmt block
-    { output_statement($1, 1, ECPGst_prepnormal); }
+    { output_statement(@1, 1, ECPGst_prepnormal); }
 ECPG: stmtExecuteStmt block
     {
         check_declared_list($1.name);
@@ -94,50 +93,45 @@ ECPG: stmtPrepareStmt block
     }
 ECPG: stmtTransactionStmt block
     {
-        fprintf(base_yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", $1);
+        fprintf(base_yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", @1);
         whenever_action(2);
-        free($1);
     }
 ECPG: toplevel_stmtTransactionStmtLegacy block
     {
-        fprintf(base_yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", $1);
+        fprintf(base_yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", @1);
         whenever_action(2);
-        free($1);
     }
 ECPG: stmtViewStmt rule
     | ECPGAllocateDescr
     {
-        fprintf(base_yyout, "ECPGallocate_desc(__LINE__, %s);", $1);
+        fprintf(base_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(base_yyout, "{ ECPGconnect(__LINE__, %d, %s, %d); ", compat, $1, autocommit);
+        fprintf(base_yyout, "{ ECPGconnect(__LINE__, %d, %s, %d); ", compat, @1, autocommit);
         reset_variables();
         whenever_action(2);
-        free($1);
     }
     | ECPGDeclareStmt
     {
-        output_simple_statement($1, 0);
+        output_simple_statement(@1, 0);
     }
     | ECPGCursorStmt
     {
-        output_simple_statement($1, (strncmp($1, "ECPGset_var", strlen("ECPGset_var")) == 0) ? 4 : 0);
+        output_simple_statement(@1, (strncmp(@1, "ECPGset_var", strlen("ECPGset_var")) == 0) ? 4 : 0);
     }
     | ECPGDeallocateDescr
     {
-        fprintf(base_yyout, "ECPGdeallocate_desc(__LINE__, %s);", $1);
+        fprintf(base_yyout, "ECPGdeallocate_desc(__LINE__, %s);", @1);
         whenever_action(0);
-        free($1);
     }
     | ECPGDeclare
     {
-        output_simple_statement($1, 0);
+        output_simple_statement(@1, 0);
     }
     | ECPGDescribe
     {
@@ -157,27 +151,25 @@ ECPG: stmtViewStmt rule
             mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in DISCONNECT statement");

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

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

         whenever_action(2);
-        free($1);
     }
     | ECPGGetDescriptor
     {
@@ -188,15 +180,14 @@ ECPG: stmtViewStmt rule
     }
     | ECPGGetDescriptorHeader
     {
-        lookup_descriptor($1, connection);
-        output_get_descr_header($1);
-        free($1);
+        lookup_descriptor(@1, connection);
+        output_get_descr_header(@1);
     }
     | ECPGOpen
     {
         struct cursor *ptr;

-        if ((ptr = add_additional_variables($1, true)) != NULL)
+        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);
@@ -205,18 +196,16 @@ ECPG: stmtViewStmt rule
     }
     | ECPGSetAutocommit
     {
-        fprintf(base_yyout, "{ ECPGsetcommit(__LINE__, \"%s\", %s);", $1, connection ? connection : "NULL");
+        fprintf(base_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(base_yyout, "{ ECPGsetconn(__LINE__, %s);", $1);
+        fprintf(base_yyout, "{ ECPGsetconn(__LINE__, %s);", @1);
         whenever_action(2);
-        free($1);
     }
     | ECPGSetDescriptor
     {
@@ -227,17 +216,15 @@ ECPG: stmtViewStmt rule
     }
     | ECPGSetDescriptorHeader
     {
-        lookup_descriptor($1, connection);
-        output_set_descr_header($1);
-        free($1);
+        lookup_descriptor(@1, connection);
+        output_set_descr_header(@1);
     }
     | ECPGTypedef
     {
         if (connection)
             mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in TYPE statement");

-        fprintf(base_yyout, "%s", $1);
-        free($1);
+        fprintf(base_yyout, "%s", @1);
         output_line_number();
     }
     | ECPGVar
@@ -245,180 +232,169 @@ ECPG: stmtViewStmt rule
         if (connection)
             mmerror(PARSE_ERROR, ET_ERROR, "AT option not allowed in VAR statement");

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

-        output_simple_statement($1, 0);
+        output_simple_statement(@1, 0);
     }
 ECPG: where_or_current_clauseWHERECURRENT_POFcursor_name block
     {
-        char       *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
+        char       *cursor_marker = @4[0] == ':' ? mm_strdup("$0") : @4;

-        $$ = cat_str(2, mm_strdup("where current of"), cursor_marker);
+        @$ = cat_str(2, mm_strdup("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 &&
-            (strcmp($7, "stdin") == 0 || strcmp($7, "stdout") == 0))
+        if (strcmp(@6, "from") == 0 &&
+            (strcmp(@7, "stdin") == 0 || strcmp(@7, "stdout") == 0))
             mmerror(PARSE_ERROR, ET_WARNING, "COPY FROM STDIN is not implemented");
 ECPG: var_valueNumericOnly addon
-        if ($1[0] == '$')
-        {
-            free($1);
-            $1 = mm_strdup("$0");
-        }
+        if (@1[0] == '$')
+            @$ = mm_strdup("$0");
 ECPG: fetch_argscursor_name addon
-        struct cursor *ptr = add_additional_variables($1, false);
+        struct cursor *ptr = add_additional_variables(@1, false);

         if (ptr->connection)
             connection = mm_strdup(ptr->connection);
-        if ($1[0] == ':')
-        {
-            free($1);
-            $1 = mm_strdup("$0");
-        }
+        if (@1[0] == ':')
+            @$ = mm_strdup("$0");
 ECPG: fetch_argsfrom_incursor_name addon
-        struct cursor *ptr = add_additional_variables($2, false);
+        struct cursor *ptr = add_additional_variables(@2, false);

         if (ptr->connection)
             connection = mm_strdup(ptr->connection);
-        if ($2[0] == ':')
-        {
-            free($2);
-            $2 = mm_strdup("$0");
-        }
+        if (@2[0] == ':')
+            @$ = cat2_str(mm_strdup(@1), 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
-        struct cursor *ptr = add_additional_variables($3, false);
+        struct cursor *ptr = add_additional_variables(@3, false);

         if (ptr->connection)
             connection = mm_strdup(ptr->connection);
-        if ($3[0] == ':')
-        {
-            free($3);
-            $3 = mm_strdup("$0");
-        }
+        if (@3[0] == ':')
+            @$ = cat_str(3, mm_strdup(@1), mm_strdup(@2), mm_strdup("$0"));
 ECPG: fetch_argsSignedIconstopt_from_incursor_name addon
-        struct cursor *ptr = add_additional_variables($3, false);
+        struct cursor *ptr = add_additional_variables(@3, false);
+        bool    replace = false;

         if (ptr->connection)
             connection = mm_strdup(ptr->connection);
-        if ($3[0] == ':')
+        if (@3[0] == ':')
         {
-            free($3);
-            $3 = mm_strdup("$0");
+            @3 = mm_strdup("$0");
+            replace = true;
         }
-        if ($1[0] == '$')
+        if (@1[0] == '$')
         {
-            free($1);
-            $1 = mm_strdup("$0");
+            @1 = mm_strdup("$0");
+            replace = true;
         }
+        if (replace)
+            @$ = cat_str(3, mm_strdup(@1), mm_strdup(@2), mm_strdup(@3));
 ECPG: fetch_argsFORWARDALLopt_from_incursor_name addon
 ECPG: fetch_argsBACKWARDALLopt_from_incursor_name addon
-        struct cursor *ptr = add_additional_variables($4, false);
+        struct cursor *ptr = add_additional_variables(@4, false);

         if (ptr->connection)
             connection = mm_strdup(ptr->connection);
-        if ($4[0] == ':')
-        {
-            free($4);
-            $4 = mm_strdup("$0");
-        }
+        if (@4[0] == ':')
+            @$ = cat_str(4, mm_strdup(@1), mm_strdup(@2), mm_strdup(@3), 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
-        struct cursor *ptr = add_additional_variables($4, false);
+        struct cursor *ptr = add_additional_variables(@4, false);
+        bool    replace = false;

         if (ptr->connection)
             connection = mm_strdup(ptr->connection);
-        if ($4[0] == ':')
+        if (@4[0] == ':')
         {
-            free($4);
-            $4 = mm_strdup("$0");
+            @4 = mm_strdup("$0");
+            replace = true;
         }
-        if ($2[0] == '$')
+        if (@2[0] == '$')
         {
-            free($2);
-            $2 = mm_strdup("$0");
+            @2 = mm_strdup("$0");
+            replace = true;
         }
-ECPG: cursor_namename rule
+        if (replace)
+            @$ = cat_str(4, mm_strdup(@1), mm_strdup(@2), mm_strdup(@3), mm_strdup(@4));
+ECPG: cursor_namename block
     | char_civar
     {
-        char       *curname = mm_alloc(strlen($1) + 2);
+        char       *curname = mm_alloc(strlen(@1) + 2);

-        sprintf(curname, ":%s", $1);
-        free($1);
-        $1 = curname;
-        $$ = $1;
+        sprintf(curname, ":%s", @1);
+        @$ = curname;
     }
 ECPG: ExplainableStmtExecuteStmt block
     {
-        $$ = $1.name;
+        @$ = $1.name;
     }
 ECPG: PrepareStmtPREPAREprepared_nameprep_type_clauseASPreparableStmt block
     {
-        $$.name = $2;
-        $$.type = $3;
-        $$.stmt = $5;
+        $$.name = @2;
+        $$.type = @3;
+        $$.stmt = @5;
     }
     | PREPARE prepared_name FROM execstring
     {
-        $$.name = $2;
+        $$.name = @2;
         $$.type = NULL;
-        $$.stmt = $4;
+        $$.stmt = @4;
     }
 ECPG: ExecuteStmtEXECUTEprepared_nameexecute_param_clauseexecute_rest block
     {
-        $$.name = $2;
-        $$.type = $3;
+        $$.name = @2;
+        $$.type = @3;
     }
 ECPG: ExecuteStmtCREATEOptTempTABLEcreate_as_targetASEXECUTEprepared_nameexecute_param_clauseopt_with_dataexecute_rest
block
     {
-        $$.name = cat_str(8, mm_strdup("create"), $2, mm_strdup("table"), $4, mm_strdup("as execute"), $7, $8, $9);
+        $$.name = @$;
     }
 ECPG:
ExecuteStmtCREATEOptTempTABLEIF_PNOTEXISTScreate_as_targetASEXECUTEprepared_nameexecute_param_clauseopt_with_dataexecute_rest
block
     {
-        $$.name = cat_str(8, mm_strdup("create"), $2, mm_strdup("table if not exists"), $7, mm_strdup("as execute"),
$10,$11, $12); 
+        $$.name = @$;
     }
 ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectStmt block
     {
         struct cursor *ptr,
                    *this;
-        char       *cursor_marker = $2[0] == ':' ? mm_strdup("$0") : mm_strdup($2);
+        char       *cursor_marker = @2[0] == ':' ? mm_strdup("$0") : mm_strdup(@2);
         char       *comment,
                    *c1,
                    *c2;
-        int            (*strcmp_fn) (const char *, const char *) = (($2[0] == ':' || $2[0] == '"') ? strcmp :
pg_strcasecmp);
+        int            (*strcmp_fn) (const char *, const char *) = ((@2[0] == ':' || @2[0] == '"') ? strcmp :
pg_strcasecmp);

-        if (INFORMIX_MODE && pg_strcasecmp($2, "database") == 0)
+        if (INFORMIX_MODE && pg_strcasecmp(@2, "database") == 0)
             mmfatal(PARSE_ERROR, "\"database\" cannot be used as cursor name in INFORMIX mode");

         for (ptr = cur; ptr != NULL; ptr = ptr->next)
         {
-            if (strcmp_fn($2, ptr->name) == 0)
+            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); 
+                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);
+                    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->name = mm_strdup(@2);
         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 = 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;
@@ -435,47 +411,47 @@ ECPG: DeclareCursorStmtDECLAREcursor_namecursor_optionsCURSORopt_holdFORSelectSt
         }
         comment = cat_str(3, mm_strdup("/*"), c1, mm_strdup("*/"));

-        $$ = cat2_str(adjust_outofscope_cursor_vars(this), comment);
+        @$ = cat2_str(adjust_outofscope_cursor_vars(this), comment);
     }
 ECPG: ClosePortalStmtCLOSEcursor_name block
     {
-        char       *cursor_marker = $2[0] == ':' ? mm_strdup("$0") : $2;
+        char       *cursor_marker = @2[0] == ':' ? mm_strdup("$0") : @2;
         struct cursor *ptr = NULL;

         for (ptr = cur; ptr != NULL; ptr = ptr->next)
         {
-            if (strcmp($2, ptr->name) == 0)
+            if (strcmp(@2, ptr->name) == 0)
             {
                 if (ptr->connection)
                     connection = mm_strdup(ptr->connection);
                 break;
             }
         }
-        $$ = cat2_str(mm_strdup("close"), cursor_marker);
+        @$ = cat2_str(mm_strdup("close"), cursor_marker);
     }
 ECPG: opt_hold block
     {
         if (compat == ECPG_COMPAT_INFORMIX_SE && autocommit)
-            $$ = mm_strdup("with hold");
+            @$ = mm_strdup("with hold");
         else
-            $$ = EMPTY;
+            @$ = EMPTY;
     }
 ECPG: into_clauseINTOOptTempTableName block
     {
         FoundInto = 1;
-        $$ = cat2_str(mm_strdup("into"), $2);
+        @$ = cat2_str(mm_strdup("into"), @2);
     }
     | ecpg_into
     {
-        $$ = EMPTY;
+        @$ = EMPTY;
     }
 ECPG: TypenameSimpleTypenameopt_array_bounds block
     {
-        $$ = cat2_str($1, $2.str);
+        @$ = cat2_str(@1, $2.str);
     }
 ECPG: TypenameSETOFSimpleTypenameopt_array_bounds block
     {
-        $$ = cat_str(3, mm_strdup("setof"), $2, $3.str);
+        @$ = cat_str(3, mm_strdup("setof"), @2, $3.str);
     }
 ECPG: opt_array_boundsopt_array_bounds'['']' block
     {
@@ -492,10 +468,10 @@ ECPG: opt_array_boundsopt_array_bounds'['']' block
         $$.index1 = $1.index1;
         $$.index2 = $1.index2;
         if (strcmp($1.index1, "-1") == 0)
-            $$.index1 = mm_strdup($3);
+            $$.index1 = mm_strdup(@3);
         else if (strcmp($1.index2, "-1") == 0)
-            $$.index2 = mm_strdup($3);
-        $$.str = cat_str(4, $1.str, mm_strdup("["), $3, mm_strdup("]"));
+            $$.index2 = mm_strdup(@3);
+        $$.str = cat_str(4, $1.str, mm_strdup("["), @3, mm_strdup("]"));
     }
 ECPG: opt_array_bounds block
     {
@@ -505,108 +481,100 @@ ECPG: opt_array_bounds block
     }
 ECPG: IconstICONST block
     {
-        $$ = make_name();
+        @$ = make_name();
     }
 ECPG: AexprConstNULL_P rule
-    | civar                            { $$ = $1; }
-    | civarind                        { $$ = $1; }
+    | civar
+    | civarind
 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;
-        struct cursor *ptr = add_additional_variables($3, false);
+        char       *cursor_marker = @3[0] == ':' ? mm_strdup("$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, 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;
-        struct cursor *ptr = add_additional_variables($4, false);
+        char       *cursor_marker = @4[0] == ':' ? mm_strdup("$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, mm_strdup("fetch forward from"), cursor_marker);
     }
     | FETCH BACKWARD cursor_name opt_ecpg_fetch_into
     {
-        char       *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
-        struct cursor *ptr = add_additional_variables($3, false);
+        char       *cursor_marker = @3[0] == ':' ? mm_strdup("$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, 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;
-        struct cursor *ptr = add_additional_variables($4, false);
+        char       *cursor_marker = @4[0] == ':' ? mm_strdup("$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, mm_strdup("fetch backward from"), cursor_marker);
     }
     | MOVE FORWARD cursor_name
     {
-        char       *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
-        struct cursor *ptr = add_additional_variables($3, false);
+        char       *cursor_marker = @3[0] == ':' ? mm_strdup("$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, mm_strdup("move forward"), cursor_marker);
     }
     | MOVE FORWARD from_in cursor_name
     {
-        char       *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
-        struct cursor *ptr = add_additional_variables($4, false);
+        char       *cursor_marker = @4[0] == ':' ? mm_strdup("$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, mm_strdup("move forward from"), cursor_marker);
     }
     | MOVE BACKWARD cursor_name
     {
-        char       *cursor_marker = $3[0] == ':' ? mm_strdup("$0") : $3;
-        struct cursor *ptr = add_additional_variables($3, false);
+        char       *cursor_marker = @3[0] == ':' ? mm_strdup("$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, mm_strdup("move backward"), cursor_marker);
     }
     | MOVE BACKWARD from_in cursor_name
     {
-        char       *cursor_marker = $4[0] == ':' ? mm_strdup("$0") : $4;
-        struct cursor *ptr = add_additional_variables($4, false);
+        char       *cursor_marker = @4[0] == ':' ? mm_strdup("$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, 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;
-    }
diff --git a/src/interfaces/ecpg/preproc/ecpg.header b/src/interfaces/ecpg/preproc/ecpg.header
index 3ed39b5c77..46023a0106 100644
--- a/src/interfaces/ecpg/preproc/ecpg.header
+++ b/src/interfaces/ecpg/preproc/ecpg.header
@@ -8,14 +8,6 @@
 #include "ecpg_config.h"
 #include <unistd.h>

-/* Location tracking support --- simpler than bison's default */
-#define YYLLOC_DEFAULT(Current, Rhs, N) \
-    do { \
-        if (N)                        \
-            (Current) = (Rhs)[1];    \
-        else                        \
-            (Current) = (Rhs)[0];    \
-    } while (0)

 /*
  * The %name-prefix option below will make bison call base_yylex, but we
@@ -195,6 +187,61 @@ make3_str(char *str1, char *str2, char *str3)
     return res_str;
 }

+/*
+ * "Location tracking" support.  We commandeer Bison's location tracking
+ * mechanism to manage the output string for productions that ordinarily would
+ * return a <str> result.  This allows the majority of those productions to
+ * have default semantic actions, reducing the size of the parser, and also
+ * greatly reducing its compilation time on some versions of clang.
+ *
+ * To do this, we make YYLTYPE be a pointer to a malloc'd string, and then
+ * merge the location strings of the input tokens in the default YYLLOC
+ * computation.  Productions that are okay with the standard merge need not
+ * do anything more; otherwise, they can override it by assigning to @$.
+ */
+#define YYLLOC_DEFAULT(Current, Rhs, N) yylloc_default(&(Current), Rhs, N)
+
+static void
+yylloc_default(YYLTYPE *target, YYLTYPE *rhs, int N)
+{
+    if (N > 1)
+    {
+        /* Concatenate non-empty inputs with one space between them */
+        char       *result,
+                   *ptr;
+        size_t        needed = 0;
+
+        for (int i = 1; i <= N; i++)
+        {
+            size_t        thislen = strlen(rhs[i]);
+
+            if (needed > 0 && thislen > 0)
+                needed++;
+            needed += thislen;
+        }
+        result = (char *) mm_alloc(needed + 1);
+        ptr = result;
+        for (int i = 1; i <= N; i++)
+        {
+            size_t        thislen = strlen(rhs[i]);
+
+            if (ptr > result && thislen > 0)
+                *ptr++ = ' ';
+            memcpy(ptr, rhs[i], thislen);
+            ptr += thislen;
+        }
+        *ptr = '\0';
+        *target = result;
+    }
+    else if (N == 1)
+    {
+        /* Just re-use the single input */
+        *target = rhs[1];
+    }
+    else
+        *target = EMPTY;
+}
+
 /* and the rest */
 static char *
 make_name(void)
diff --git a/src/interfaces/ecpg/preproc/ecpg.trailer b/src/interfaces/ecpg/preproc/ecpg.trailer
index b6233e5e53..e6475e170d 100644
--- a/src/interfaces/ecpg/preproc/ecpg.trailer
+++ b/src/interfaces/ecpg/preproc/ecpg.trailer
@@ -18,20 +18,17 @@ statement: ecpgstart at toplevel_stmt ';'
     }
     | ecpgstart ECPGVarDeclaration
     {
-        fprintf(base_yyout, "%s", $2);
-        free($2);
+        fprintf(base_yyout, "%s", @$);
         output_line_number();
     }
     | ECPGDeclaration
     | c_thing
     {
-        fprintf(base_yyout, "%s", $1);
-        free($1);
+        fprintf(base_yyout, "%s", @$);
     }
     | CPP_LINE
     {
-        fprintf(base_yyout, "%s", $1);
-        free($1);
+        fprintf(base_yyout, "%s", @$);
     }
     | '{'
     {
@@ -58,8 +55,6 @@ CreateAsStmt: CREATE OptTemp TABLE create_as_target AS
     {
         if (FoundInto == 1)
             mmerror(PARSE_ERROR, ET_ERROR, "CREATE TABLE AS cannot specify INTO");
-
-        $$ = cat_str(7, mm_strdup("create"), $2, mm_strdup("table"), $4, mm_strdup("as"), $7, $8);
     }
     | CREATE OptTemp TABLE IF_P NOT EXISTS create_as_target AS
     {
@@ -68,14 +63,12 @@ CreateAsStmt: CREATE OptTemp TABLE create_as_target AS
     {
         if (FoundInto == 1)
             mmerror(PARSE_ERROR, ET_ERROR, "CREATE TABLE AS cannot specify INTO");
-
-        $$ = cat_str(7, mm_strdup("create"), $2, mm_strdup("table if not exists"), $7, mm_strdup("as"), $10, $11);
     }
     ;

 at: AT connection_object
     {
-        connection = $2;
+        connection = @2;

         /*
          * Do we have a variable as connection target?  Remove the variable
@@ -91,55 +84,52 @@ 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, mm_strdup(","), @5, mm_strdup(","), @4);
     }
     | SQL_CONNECT TO DEFAULT
     {
-        $$ = mm_strdup("NULL, NULL, NULL, \"DEFAULT\"");
+        @$ = mm_strdup("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, mm_strdup("NULL,"), @2, mm_strdup(", NULL"));
     }
     | DATABASE connection_target
     {
-        $$ = cat2_str($2, mm_strdup(", NULL, NULL, NULL"));
+        @$ = cat2_str(@2, mm_strdup(", NULL, NULL, NULL"));
     }
     ;

 connection_target: opt_database_name opt_server opt_port
     {
         /* old style: dbname[@server][:port] */
-        if (strlen($2) > 0 && *($2) != '@')
-            mmerror(PARSE_ERROR, ET_ERROR, "expected \"@\", found \"%s\"", $2);
+        if (strlen(@2) > 0 && *(@2) != '@')
+            mmerror(PARSE_ERROR, ET_ERROR, "expected \"@\", found \"%s\"", @2);

         /* C strings need to be handled differently */
-        if ($1[0] == '\"')
-            $$ = $1;
+        if (@1[0] == '\"')
+            @$ = @1;
         else
-            $$ = make3_str(mm_strdup("\""), make3_str($1, $2, $3), mm_strdup("\""));
+            @$ = make3_str(mm_strdup("\""), make3_str(@1, @2, @3), mm_strdup("\""));
     }
     | db_prefix ':' server opt_port '/' opt_database_name opt_options
     {
         /* new style: <tcp|unix>:postgresql://server[:port][/dbname] */
-        if (strncmp($1, "unix:postgresql", strlen("unix:postgresql")) != 0 && strncmp($1, "tcp:postgresql",
strlen("tcp:postgresql"))!= 0) 
+        if (strncmp(@1, "unix:postgresql", strlen("unix:postgresql")) != 0 && strncmp(@1, "tcp:postgresql",
strlen("tcp:postgresql"))!= 0) 
             mmerror(PARSE_ERROR, ET_ERROR, "only protocols \"tcp\" and \"unix\" and database type \"postgresql\" are
supported");

-        if (strncmp($3, "//", strlen("//")) != 0)
-            mmerror(PARSE_ERROR, ET_ERROR, "expected \"://\", found \"%s\"", $3);
+        if (strncmp(@3, "//", strlen("//")) != 0)
+            mmerror(PARSE_ERROR, ET_ERROR, "expected \"://\", found \"%s\"", @3);

-        if (strncmp($1, "unix", strlen("unix")) == 0 &&
-            strncmp($3 + strlen("//"), "localhost", strlen("localhost")) != 0 &&
-            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("//"));
+        if (strncmp(@1, "unix", strlen("unix")) == 0 &&
+            strncmp(@3 + strlen("//"), "localhost", strlen("localhost")) != 0 &&
+            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(mm_strdup("\""), @1, mm_strdup(":")), @3, make3_str(make3_str(@4, mm_strdup("/"),
@6),@7, mm_strdup("\""))); 
     }
     | char_variable
-    {
-        $$ = $1;
-    }
     | ecpg_sconst
     {
         /*
@@ -147,128 +137,107 @@ connection_target: opt_database_name opt_server opt_port
          * so we change the quotes. Note, that the rule for ecpg_sconst adds
          * these single quotes.
          */
-        $1[0] = '\"';
-        $1[strlen($1) - 1] = '\"';
-        $$ = $1;
+        @1[0] = '\"';
+        @1[strlen(@1) - 1] = '\"';
+        @$ = @1;
     }
     ;

 opt_database_name: name
-    {
-        $$ = $1;
-    }
     | /* EMPTY */
-    {
-        $$ = EMPTY;
-    }
     ;

 db_prefix: ecpg_ident cvariable
     {
-        if (strcmp($2, "postgresql") != 0 && strcmp($2, "postgres") != 0)
-            mmerror(PARSE_ERROR, ET_ERROR, "expected \"postgresql\", found \"%s\"", $2);
+        if (strcmp(@2, "postgresql") != 0 && strcmp(@2, "postgres") != 0)
+            mmerror(PARSE_ERROR, ET_ERROR, "expected \"postgresql\", found \"%s\"", @2);

-        if (strcmp($1, "tcp") != 0 && strcmp($1, "unix") != 0)
-            mmerror(PARSE_ERROR, ET_ERROR, "invalid connection type: %s", $1);
+        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, mm_strdup(":"), @2);
     }
     ;

 server: Op server_name
     {
-        if (strcmp($1, "@") != 0 && strcmp($1, "//") != 0)
-            mmerror(PARSE_ERROR, ET_ERROR, "expected \"@\" or \"://\", found \"%s\"", $1);
+        if (strcmp(@1, "@") != 0 && strcmp(@1, "//") != 0)
+            mmerror(PARSE_ERROR, ET_ERROR, "expected \"@\" or \"://\", found \"%s\"", @1);

-        $$ = make2_str($1, $2);
+        @$ = make2_str(@1, @2);
     }
     ;

 opt_server: server
-    {
-        $$ = $1;
-    }
     | /* EMPTY */
-    {
-        $$ = EMPTY;
-    }
     ;

 server_name: ColId
-    {
-        $$ = $1;
-    }
     | ColId '.' server_name
-    {
-        $$ = make3_str($1, mm_strdup("."), $3);
-    }
     | IP
     {
-        $$ = make_name();
+        @$ = make_name();
     }
     ;

 opt_port: ':' Iconst
     {
-        $$ = make2_str(mm_strdup(":"), $2);
+        @$ = make2_str(mm_strdup(":"), @2);
     }
     | /* EMPTY */
-    {
-        $$ = EMPTY;
-    }
     ;

 opt_connection_name: AS connection_object
     {
-        $$ = $2;
+        @$ = @2;
     }
     | /* EMPTY */
     {
-        $$ = mm_strdup("NULL");
+        @$ = mm_strdup("NULL");
     }
     ;

 opt_user: USER ora_user
     {
-        $$ = $2;
+        @$ = @2;
     }
     | /* EMPTY */
     {
-        $$ = mm_strdup("NULL, NULL");
+        @$ = mm_strdup("NULL, NULL");
     }
     ;

 ora_user: user_name
     {
-        $$ = cat2_str($1, mm_strdup(", NULL"));
+        @$ = cat2_str(@1, mm_strdup(", NULL"));
     }
     | user_name '/' user_name
     {
-        $$ = cat_str(3, $1, mm_strdup(","), $3);
+        @$ = cat_str(3, @1, mm_strdup(","), @3);
     }
     | user_name SQL_IDENTIFIED BY user_name
     {
-        $$ = cat_str(3, $1, mm_strdup(","), $4);
+        @$ = cat_str(3, @1, mm_strdup(","), @4);
     }
     | user_name USING user_name
     {
-        $$ = cat_str(3, $1, mm_strdup(","), $3);
+        @$ = cat_str(3, @1, mm_strdup(","), @3);
     }
     ;

 user_name: RoleId
     {
-        if ($1[0] == '\"')
-            $$ = $1;
+        if (@1[0] == '\"')
+            @$ = @1;
         else
-            $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+            @$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
     }
     | ecpg_sconst
     {
-        if ($1[0] == '\"')
-            $$ = $1;
+        if (@1[0] == '\"')
+            @$ = @1;
         else
-            $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+            @$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
     }
     | civar
     {
@@ -280,16 +249,16 @@ user_name: RoleId

         /* handle varchars */
         if (type == ECPGt_varchar)
-            $$ = make2_str(mm_strdup(argsinsert->variable->name), mm_strdup(".arr"));
+            @$ = make2_str(mm_strdup(argsinsert->variable->name), mm_strdup(".arr"));
         else
-            $$ = mm_strdup(argsinsert->variable->name);
+            @$ = mm_strdup(argsinsert->variable->name);
     }
     ;

 char_variable: cvariable
     {
         /* check if we have a string variable */
-        struct variable *p = find_variable($1);
+        struct variable *p = find_variable(@1);
         enum ECPGttype type = p->type->type;

         /* If we have just one character this is not a string */
@@ -306,14 +275,14 @@ char_variable: cvariable
                 case ECPGt_char:
                 case ECPGt_unsigned_char:
                 case ECPGt_string:
-                    $$ = $1;
+                    @$ = @1;
                     break;
                 case ECPGt_varchar:
-                    $$ = make2_str($1, mm_strdup(".arr"));
+                    @$ = make2_str(@1, mm_strdup(".arr"));
                     break;
                 default:
                     mmerror(PARSE_ERROR, ET_ERROR, "invalid data type");
-                    $$ = $1;
+                    @$ = @1;
                     break;
             }
         }
@@ -322,72 +291,63 @@ char_variable: cvariable

 opt_options: Op connect_options
     {
-        if (strlen($1) == 0)
+        if (strlen(@1) == 0)
             mmerror(PARSE_ERROR, ET_ERROR, "incomplete statement");

-        if (strcmp($1, "?") != 0)
-            mmerror(PARSE_ERROR, ET_ERROR, "unrecognized token \"%s\"", $1);
+        if (strcmp(@1, "?") != 0)
+            mmerror(PARSE_ERROR, ET_ERROR, "unrecognized token \"%s\"", @1);

-        $$ = make2_str(mm_strdup("?"), $2);
+        @$ = make2_str(mm_strdup("?"), @2);
     }
     | /* EMPTY */
-    {
-        $$ = EMPTY;
-    }
     ;

 connect_options: ColId opt_opt_value
     {
-        $$ = make2_str($1, $2);
+        @$ = make2_str(@1, @2);
     }
     | ColId opt_opt_value Op connect_options
     {
-        if (strlen($3) == 0)
+        if (strlen(@3) == 0)
             mmerror(PARSE_ERROR, ET_ERROR, "incomplete statement");

-        if (strcmp($3, "&") != 0)
-            mmerror(PARSE_ERROR, ET_ERROR, "unrecognized token \"%s\"", $3);
+        if (strcmp(@3, "&") != 0)
+            mmerror(PARSE_ERROR, ET_ERROR, "unrecognized token \"%s\"", @3);

-        $$ = cat_str(3, make2_str($1, $2), $3, $4);
+        @$ = cat_str(3, make2_str(@1, @2), @3, @4);
     }
     ;

 opt_opt_value: /* EMPTY */
-    {
-        $$ = EMPTY;
-    }
     | '=' Iconst
     {
-        $$ = make2_str(mm_strdup("="), $2);
+        @$ = make2_str(mm_strdup("="), @2);
     }
     | '=' ecpg_ident
     {
-        $$ = make2_str(mm_strdup("="), $2);
+        @$ = make2_str(mm_strdup("="), @2);
     }
     | '=' civar
     {
-        $$ = make2_str(mm_strdup("="), $2);
+        @$ = make2_str(mm_strdup("="), @2);
     }
     ;

 prepared_name: name
     {
-        if ($1[0] == '\"' && $1[strlen($1) - 1] == '\"')    /* already quoted? */
-            $$ = $1;
+        if (@1[0] == '\"' && @1[strlen(@1) - 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]);
+            for (i = 0; i < strlen(@1); i++)
+                @1[i] = tolower((unsigned char) @1[i]);

-            $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+            @$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
         }
     }
     | char_variable
-    {
-        $$ = $1;
-    }
     ;

 /*
@@ -400,7 +360,7 @@ ECPGDeclareStmt: DECLARE prepared_name STATEMENT
         /* Check whether the declared name has been defined or not */
         for (ptr = g_declared_list; ptr != NULL; ptr = ptr->next)
         {
-            if (strcmp($2, ptr->name) == 0)
+            if (strcmp(@2, ptr->name) == 0)
             {
                 /* re-definition is not allowed */
                 mmerror(PARSE_ERROR, ET_ERROR, "name \"%s\" is already declared", ptr->name);
@@ -413,7 +373,7 @@ ECPGDeclareStmt: DECLARE prepared_name STATEMENT
         if (ptr)
         {
             /* initial definition */
-            ptr->name = $2;
+            ptr->name = @2;
             if (connection)
                 ptr->connection = mm_strdup(connection);
             else
@@ -423,7 +383,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, mm_strdup("/* declare "), mm_strdup(@2), mm_strdup(" as an SQL identifier */"));
     }
     ;

@@ -435,26 +395,26 @@ 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);
-        int            (*strcmp_fn) (const char *, const char *) = (($2[0] == ':' || $2[0] == '"') ? strcmp :
pg_strcasecmp);
+        char       *cursor_marker = @2[0] == ':' ? mm_strdup("$0") : mm_strdup(@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;
         char       *con;

-        if (INFORMIX_MODE && pg_strcasecmp($2, "database") == 0)
+        if (INFORMIX_MODE && pg_strcasecmp(@2, "database") == 0)
             mmfatal(PARSE_ERROR, "\"database\" cannot be used as cursor name in INFORMIX mode");

-        check_declared_list($7);
+        check_declared_list(@7);
         con = connection ? connection : "NULL";
         for (ptr = cur; ptr != NULL; ptr = ptr->next)
         {
-            if (strcmp_fn($2, ptr->name) == 0)
+            if (strcmp_fn(@2, ptr->name) == 0)
             {
                 /* re-definition is a bug */
-                if ($2[0] == ':')
-                    mmerror(PARSE_ERROR, ET_ERROR, "using variable \"%s\" in different declare statements is not
supported",$2 + 1); 
+                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);
+                    mmerror(PARSE_ERROR, ET_ERROR, "cursor \"%s\" is already defined", @2);
             }
         }

@@ -462,24 +422,24 @@ ECPGCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared_

         /* initial definition */
         this->next = cur;
-        this->name = $2;
+        this->name = @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 = cat_str(6, mm_strdup("declare"), cursor_marker, @3, mm_strdup("cursor"), @5, mm_strdup("for
$1"));
         this->argsresult = NULL;
         this->argsresult_oos = NULL;

         thisquery->type = &ecpg_query;
         thisquery->brace_level = 0;
         thisquery->next = NULL;
-        thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(, , __LINE__)") + strlen(con) +
strlen($7));
-        sprintf(thisquery->name, "ECPGprepared_statement(%s, %s, __LINE__)", con, $7);
+        thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(, , __LINE__)") + strlen(con) +
strlen(@7));
+        sprintf(thisquery->name, "ECPGprepared_statement(%s, %s, __LINE__)", con, @7);

         this->argsinsert = NULL;
         this->argsinsert_oos = NULL;
-        if ($2[0] == ':')
+        if (@2[0] == ':')
         {
-            struct variable *var = find_variable($2 + 1);
+            struct variable *var = find_variable(@2 + 1);

             remove_variable_from_list(&argsinsert, var);
             add_variable_to_head(&(this->argsinsert), var, &no_indicator);
@@ -490,7 +450,7 @@ ECPGCursorStmt: DECLARE cursor_name cursor_options CURSOR opt_hold FOR prepared_

         comment = cat_str(3, mm_strdup("/*"), mm_strdup(this->command), mm_strdup("*/"));

-        $$ = cat_str(2, adjust_outofscope_cursor_vars(this),
+        @$ = cat_str(2, adjust_outofscope_cursor_vars(this),
                      comment);
     }
     ;
@@ -501,7 +461,7 @@ ECPGExecuteImmediateStmt: EXECUTE IMMEDIATE execstring
          * execute immediate means prepare the statement and immediately
          * execute it
          */
-        $$ = $3;
+        @$ = @3;
     }
     ;

@@ -511,36 +471,24 @@ ECPGExecuteImmediateStmt: EXECUTE IMMEDIATE execstring
 ECPGVarDeclaration: single_vt_declaration;

 single_vt_declaration: type_declaration
-    {
-        $$ = $1;
-    }
     | var_declaration
-    {
-        $$ = $1;
-    }
     ;

 precision: NumericOnly
-    {
-        $$ = $1;
-    }
     ;

 opt_scale: ',' NumericOnly
     {
-        $$ = $2;
+        @$ = @2;
     }
     | /* EMPTY */
-    {
-        $$ = EMPTY;
-    }
     ;

-ecpg_interval: opt_interval            { $$ = $1; }
-    | YEAR_P TO MINUTE_P            { $$ = mm_strdup("year to minute"); }
-    | YEAR_P TO SECOND_P            { $$ = mm_strdup("year to second"); }
-    | DAY_P TO DAY_P                { $$ = mm_strdup("day to day"); }
-    | MONTH_P TO MONTH_P            { $$ = mm_strdup("month to month");    }
+ecpg_interval: opt_interval
+    | YEAR_P TO MINUTE_P
+    | YEAR_P TO SECOND_P
+    | DAY_P TO DAY_P
+    | MONTH_P TO MONTH_P
     ;

 /*
@@ -552,8 +500,7 @@ ECPGDeclaration: sql_startdeclare
     }
     var_type_declarations sql_enddeclare
     {
-        fprintf(base_yyout, "%s/* exec sql end declare section */", $3);
-        free($3);
+        fprintf(base_yyout, "%s/* exec sql end declare section */", @3);
         output_line_number();
     }
     ;
@@ -569,41 +516,17 @@ sql_enddeclare: ecpgstart END_P DECLARE SQL_SECTION ';'
     ;

 var_type_declarations: /* EMPTY */
-    {
-        $$ = EMPTY;
-    }
     | vt_declarations
-    {
-        $$ = $1;
-    }
     ;

 vt_declarations: single_vt_declaration
-    {
-        $$ = $1;
-    }
     | CPP_LINE
-    {
-        $$ = $1;
-    }
     | vt_declarations single_vt_declaration
-    {
-        $$ = cat2_str($1, $2);
-    }
     | vt_declarations CPP_LINE
-    {
-        $$ = cat2_str($1, $2);
-    }
     ;

 variable_declarations: var_declaration
-    {
-        $$ = $1;
-    }
     | variable_declarations var_declaration
-    {
-        $$ = cat2_str($1, $2);
-    }
     ;

 type_declaration: S_TYPEDEF
@@ -614,18 +537,18 @@ type_declaration: S_TYPEDEF
     }
     var_type    opt_pointer ECPGColLabel opt_array_bounds ';'
     {
-        add_typedef($5, $6.index1, $6.index2, $3.type_enum, $3.type_dimension, $3.type_index, initializer, *$4 ? 1 :
0);
+        add_typedef(@5, $6.index1, $6.index2, $3.type_enum, $3.type_dimension, $3.type_index, initializer, *@4 ? 1 :
0);

-        fprintf(base_yyout, "typedef %s %s %s %s;\n", $3.type_str, *$4 ? "*" : "", $5, $6.str);
+        fprintf(base_yyout, "typedef %s %s %s %s;\n", $3.type_str, *@4 ? "*" : "", @5, $6.str);
         output_line_number();
-        $$ = mm_strdup("");
+        @$ = EMPTY;
     }
     ;

 var_declaration:
     storage_declaration var_type
     {
-        actual_type[struct_level].type_storage = $1;
+        actual_type[struct_level].type_storage = @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;
@@ -636,7 +559,7 @@ var_declaration:
     }
     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, mm_strdup(";\n"));
     }
     | var_type
     {
@@ -651,46 +574,31 @@ var_declaration:
     }
     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, mm_strdup(";\n"));
     }
     | struct_union_type_with_symbol ';'
     {
-        $$ = cat2_str($1, mm_strdup(";"));
+        @$ = cat2_str(@1, mm_strdup(";"));
     }
     ;

 opt_bit_field: ':' Iconst
-    {
-        $$ = cat2_str(mm_strdup(":"), $2);
-    }
     | /* EMPTY */
-    {
-        $$ = EMPTY;
-    }
     ;

 storage_declaration: storage_clause storage_modifier
-    {
-        $$ = cat2_str($1, $2);
-    }
     | storage_clause
-    {
-        $$ = $1;
-    }
     | storage_modifier
-    {
-        $$ = $1;
-    }
     ;

-storage_clause: S_EXTERN                { $$ = mm_strdup("extern"); }
-    | S_STATIC                            { $$ = mm_strdup("static"); }
-    | S_REGISTER                        { $$ = mm_strdup("register"); }
-    | S_AUTO                            { $$ = mm_strdup("auto"); }
+storage_clause: S_EXTERN
+    | S_STATIC
+    | S_REGISTER
+    | S_AUTO
     ;

-storage_modifier: S_CONST                { $$ = mm_strdup("const"); }
-    | S_VOLATILE                        { $$ = mm_strdup("volatile"); }
+storage_modifier: S_CONST
+    | S_VOLATILE
     ;

 var_type: simple_type
@@ -703,11 +611,11 @@ var_type: simple_type
     }
     | struct_union_type
     {
-        $$.type_str = $1;
+        $$.type_str = @1;
         $$.type_dimension = mm_strdup("-1");
         $$.type_index = mm_strdup("-1");

-        if (strncmp($1, "struct", sizeof("struct") - 1) == 0)
+        if (strncmp(@1, "struct", sizeof("struct") - 1) == 0)
         {
             $$.type_enum = ECPGt_struct;
             $$.type_sizeof = ECPGstruct_sizeof;
@@ -720,7 +628,7 @@ var_type: simple_type
     }
     | enum_type
     {
-        $$.type_str = $1;
+        $$.type_str = @1;
         $$.type_enum = ECPGt_int;
         $$.type_dimension = mm_strdup("-1");
         $$.type_index = mm_strdup("-1");
@@ -749,12 +657,12 @@ var_type: simple_type
          * will show up here as a plain identifier, and we need this duplicate
          * code to recognize them.
          */
-        if (strcmp($1, "numeric") == 0)
+        if (strcmp(@1, "numeric") == 0)
         {
             $$.type_enum = ECPGt_numeric;
             $$.type_str = mm_strdup("numeric");
         }
-        else if (strcmp($1, "decimal") == 0)
+        else if (strcmp(@1, "decimal") == 0)
         {
             $$.type_enum = ECPGt_decimal;
             $$.type_str = mm_strdup("decimal");
@@ -858,10 +766,10 @@ var_type: simple_type
          * here, but not above because those are not currently SQL keywords.
          * If they ever become so, they must gain duplicate productions above.
          */
-        if (strlen($2) != 0 && strcmp($1, "datetime") != 0 && strcmp($1, "interval") != 0)
+        if (strlen(@2) != 0 && strcmp(@1, "datetime") != 0 && strcmp(@1, "interval") != 0)
             mmerror(PARSE_ERROR, ET_ERROR, "interval specification not allowed here");

-        if (strcmp($1, "varchar") == 0)
+        if (strcmp(@1, "varchar") == 0)
         {
             $$.type_enum = ECPGt_varchar;
             $$.type_str = EMPTY;    /* mm_strdup("varchar"); */
@@ -869,7 +777,7 @@ var_type: simple_type
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        else if (strcmp($1, "bytea") == 0)
+        else if (strcmp(@1, "bytea") == 0)
         {
             $$.type_enum = ECPGt_bytea;
             $$.type_str = EMPTY;
@@ -877,7 +785,7 @@ var_type: simple_type
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        else if (strcmp($1, "float") == 0)
+        else if (strcmp(@1, "float") == 0)
         {
             $$.type_enum = ECPGt_float;
             $$.type_str = mm_strdup("float");
@@ -885,7 +793,7 @@ var_type: simple_type
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        else if (strcmp($1, "double") == 0)
+        else if (strcmp(@1, "double") == 0)
         {
             $$.type_enum = ECPGt_double;
             $$.type_str = mm_strdup("double");
@@ -893,7 +801,7 @@ var_type: simple_type
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        else if (strcmp($1, "numeric") == 0)
+        else if (strcmp(@1, "numeric") == 0)
         {
             $$.type_enum = ECPGt_numeric;
             $$.type_str = mm_strdup("numeric");
@@ -901,7 +809,7 @@ var_type: simple_type
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        else if (strcmp($1, "decimal") == 0)
+        else if (strcmp(@1, "decimal") == 0)
         {
             $$.type_enum = ECPGt_decimal;
             $$.type_str = mm_strdup("decimal");
@@ -909,7 +817,7 @@ var_type: simple_type
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        else if (strcmp($1, "date") == 0)
+        else if (strcmp(@1, "date") == 0)
         {
             $$.type_enum = ECPGt_date;
             $$.type_str = mm_strdup("date");
@@ -917,7 +825,7 @@ var_type: simple_type
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        else if (strcmp($1, "timestamp") == 0)
+        else if (strcmp(@1, "timestamp") == 0)
         {
             $$.type_enum = ECPGt_timestamp;
             $$.type_str = mm_strdup("timestamp");
@@ -925,7 +833,7 @@ var_type: simple_type
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        else if (strcmp($1, "interval") == 0)
+        else if (strcmp(@1, "interval") == 0)
         {
             $$.type_enum = ECPGt_interval;
             $$.type_str = mm_strdup("interval");
@@ -933,7 +841,7 @@ var_type: simple_type
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        else if (strcmp($1, "datetime") == 0)
+        else if (strcmp(@1, "datetime") == 0)
         {
             $$.type_enum = ECPGt_timestamp;
             $$.type_str = mm_strdup("timestamp");
@@ -941,7 +849,7 @@ var_type: simple_type
             $$.type_index = mm_strdup("-1");
             $$.type_sizeof = NULL;
         }
-        else if ((strcmp($1, "string") == 0) && INFORMIX_MODE)
+        else if ((strcmp(@1, "string") == 0) && INFORMIX_MODE)
         {
             $$.type_enum = ECPGt_string;
             $$.type_str = mm_strdup("char");
@@ -952,7 +860,7 @@ var_type: simple_type
         else
         {
             /* Otherwise, it must be a user-defined typedef name */
-            struct typedefs *this = get_typedef($1, false);
+            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_enum = this->type->type_enum;
@@ -1001,23 +909,11 @@ var_type: simple_type
     ;

 enum_type: ENUM_P symbol enum_definition
-    {
-        $$ = cat_str(3, mm_strdup("enum"), $2, $3);
-    }
     | ENUM_P enum_definition
-    {
-        $$ = cat2_str(mm_strdup("enum"), $2);
-    }
     | ENUM_P symbol
-    {
-        $$ = cat2_str(mm_strdup("enum"), $2);
-    }
     ;

 enum_definition: '{' c_list '}'
-    {
-        $$ = cat_str(3, mm_strdup("{"), $2, mm_strdup("}"));
-    }
     ;

 struct_union_type_with_symbol: s_struct_union_symbol
@@ -1071,14 +967,11 @@ 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, mm_strdup("{"), @4, mm_strdup("}"));
     }
     ;

 struct_union_type: struct_union_type_with_symbol
-    {
-        $$ = $1;
-    }
     | s_struct_union
     {
         struct_member_list[struct_level++] = NULL;
@@ -1090,20 +983,20 @@ 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, mm_strdup("{"), @4, mm_strdup("}"));
     }
     ;

 s_struct_union_symbol: SQL_STRUCT symbol
     {
         $$.su = mm_strdup("struct");
-        $$.symbol = $2;
+        $$.symbol = @2;
         ECPGstruct_sizeof = cat_str(3, mm_strdup("sizeof("), cat2_str(mm_strdup($$.su), mm_strdup($$.symbol)),
mm_strdup(")"));
     }
     | UNION symbol
     {
         $$.su = mm_strdup("union");
-        $$.symbol = $2;
+        $$.symbol = @2;
     }
     ;

@@ -1111,15 +1004,15 @@ s_struct_union: SQL_STRUCT
     {
         ECPGstruct_sizeof = mm_strdup("");    /* This must not be NULL to
                                              * distinguish from simple types. */
-        $$ = mm_strdup("struct");
+        @$ = mm_strdup("struct");
     }
     | UNION
     {
-        $$ = mm_strdup("union");
+        @$ = mm_strdup("union");
     }
     ;

-simple_type: unsigned_type                { $$ = $1; }
+simple_type: unsigned_type
     | opt_signed signed_type            { $$ = $2; }
     ;

@@ -1151,15 +1044,12 @@ opt_signed: SQL_SIGNED
     ;

 variable_list: variable
-    {
-        $$ = $1;
-    }
     | 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, mm_strdup(";"), mm_strdup(actual_type[struct_level].type_storage), @3);
         else
-            $$ = cat_str(3, $1, mm_strdup(","), $3);
+            @$ = cat_str(3, @1, mm_strdup(","), @3);
     }
     ;

@@ -1173,7 +1063,7 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
         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:
@@ -1183,7 +1073,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, mm_strdup(@2), $3.str, @4, @5);
                 break;

             case ECPGt_varchar:
@@ -1222,9 +1112,9 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
                 vcn = (char *) mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
                 sprintf(vcn, "%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(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["),
mm_strdup(length),mm_strdup("]; } *"), mm_strdup(@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(mm_strdup(struct_name), vcn), mm_strdup(" { int len; char arr["),
mm_strdup(length),mm_strdup("]; } "), mm_strdup(@2), dim_str, @4, @5); 
                 (*varlen_type_counter)++;
                 break;

@@ -1233,7 +1123,7 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
             case ECPGt_string:
                 if (atoi(dimension) == -1)
                 {
-                    int            i = strlen($5);
+                    int            i = strlen(@5);

                     if (atoi(length) == -1 && i > 0)    /* char <var>[] =
                                                          * "string" */
@@ -1244,14 +1134,14 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
                          */
                         free(length);
                         length = mm_alloc(i + sizeof("sizeof()"));
-                        sprintf(length, "sizeof(%s)", $5 + 2);
+                        sprintf(length, "sizeof(%s)", @5 + 2);
                     }
                     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, mm_strdup(@2), $3.str, @4, @5);
                 break;

             default:
@@ -1260,41 +1150,29 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_bit_field opt_initialize
                 else
                     type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum,
mm_strdup("1"),0), dimension); 

-                $$ = cat_str(5, $1, mm_strdup($2), $3.str, $4, $5);
+                @$ = cat_str(5, @1, mm_strdup(@2), $3.str, @4, @5);
                 break;
         }

         if (struct_level == 0)
-            new_variable($2, type, braces_open);
+            new_variable(@2, type, braces_open);
         else
-            ECPGmake_struct_member($2, type, &(struct_member_list[struct_level - 1]));
-
-        free($2);
+            ECPGmake_struct_member(@2, type, &(struct_member_list[struct_level - 1]));
     }
     ;

 opt_initializer: /* EMPTY */
-    {
-        $$ = EMPTY;
-    }
     | '=' c_term
     {
         initializer = 1;
-        $$ = cat2_str(mm_strdup("="), $2);
     }
     ;

 opt_pointer: /* EMPTY */
-    {
-        $$ = EMPTY;
-    }
     | '*'
-    {
-        $$ = mm_strdup("*");
-    }
     | '*' '*'
     {
-        $$ = mm_strdup("**");
+        @$ = mm_strdup("**");
     }
     ;

@@ -1304,7 +1182,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, mm_strdup("/* declare statement"), @3, mm_strdup("*/"));
     }
     ;
 /*
@@ -1312,49 +1190,40 @@ ECPGDeclare: DECLARE STATEMENT ecpg_ident
  */
 ECPGDisconnect: SQL_DISCONNECT dis_name
     {
-        $$ = $2;
+        @$ = @2;
     }
     ;

 dis_name: connection_object
-    {
-        $$ = $1;
-    }
     | CURRENT_P
     {
-        $$ = mm_strdup("\"CURRENT\"");
+        @$ = mm_strdup("\"CURRENT\"");
     }
     | ALL
     {
-        $$ = mm_strdup("\"ALL\"");
+        @$ = mm_strdup("\"ALL\"");
     }
     | /* EMPTY */
     {
-        $$ = mm_strdup("\"CURRENT\"");
+        @$ = mm_strdup("\"CURRENT\"");
     }
     ;

 connection_object: name
     {
-        $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+        @$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
     }
     | DEFAULT
     {
-        $$ = mm_strdup("\"DEFAULT\"");
+        @$ = mm_strdup("\"DEFAULT\"");
     }
     | char_variable
-    {
-        $$ = $1;
-    }
     ;

 execstring: char_variable
-    {
-        $$ = $1;
-    }
     | CSTRING
     {
-        $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+        @$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
     }
     ;

@@ -1364,11 +1233,11 @@ execstring: char_variable
  */
 ECPGFree: SQL_FREE cursor_name
     {
-        $$ = $2;
+        @$ = @2;
     }
     | SQL_FREE ALL
     {
-        $$ = mm_strdup("all");
+        @$ = mm_strdup("all");
     }
     ;

@@ -1377,60 +1246,51 @@ ECPGFree: SQL_FREE cursor_name
  */
 ECPGOpen: SQL_OPEN cursor_name opt_ecpg_using
     {
-        if ($2[0] == ':')
-            remove_variable_from_list(&argsinsert, find_variable($2 + 1));
-        $$ = $2;
+        if (@2[0] == ':')
+            remove_variable_from_list(&argsinsert, find_variable(@2 + 1));
+        @$ = @2;
     }
     ;

 opt_ecpg_using: /* EMPTY */
-    {
-        $$ = EMPTY;
-    }
     | ecpg_using
-    {
-        $$ = $1;
-    }
     ;

 ecpg_using: USING using_list
     {
-        $$ = EMPTY;
+        @$ = EMPTY;
     }
     | using_descriptor
-    {
-        $$ = $1;
-    }
     ;

 using_descriptor: USING SQL_P SQL_DESCRIPTOR quoted_ident_stringvar
     {
-        add_variable_to_head(&argsinsert, descriptor_variable($4, 0), &no_indicator);
-        $$ = EMPTY;
+        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;
+        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;
+        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;
+        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;
+        add_variable_to_head(&argsresult, sqlda_variable(@2), &no_indicator);
+        @$ = EMPTY;
     }
     ;

@@ -1441,55 +1301,28 @@ UsingValue: UsingConst
     {
         char       *length = mm_alloc(32);

-        sprintf(length, "%zu", strlen($1));
-        add_variable_to_head(&argsinsert, new_variable($1, ECPGmake_simple_type(ECPGt_const, length, 0), 0),
&no_indicator);
+        sprintf(length, "%zu", strlen(@1));
+        add_variable_to_head(&argsinsert, new_variable(@1, ECPGmake_simple_type(ECPGt_const, length, 0), 0),
&no_indicator);
     }
     | civar
     {
-        $$ = EMPTY;
+        @$ = EMPTY;
     }
     | civarind
     {
-        $$ = EMPTY;
+        @$ = EMPTY;
     }
     ;

 UsingConst: Iconst
-    {
-        $$ = $1;
-    }
     | '+' Iconst
-    {
-        $$ = cat_str(2, mm_strdup("+"), $2);
-    }
     | '-' Iconst
-    {
-        $$ = cat_str(2, mm_strdup("-"), $2);
-    }
     | ecpg_fconst
-    {
-        $$ = $1;
-    }
     | '+' ecpg_fconst
-    {
-        $$ = cat_str(2, mm_strdup("+"), $2);
-    }
     | '-' ecpg_fconst
-    {
-        $$ = cat_str(2, mm_strdup("-"), $2);
-    }
     | ecpg_sconst
-    {
-        $$ = $1;
-    }
     | ecpg_bconst
-    {
-        $$ = $1;
-    }
     | ecpg_xconst
-    {
-        $$ = $1;
-    }
     ;

 /*
@@ -1498,7 +1331,7 @@ UsingConst: Iconst
 ECPGDescribe: SQL_DESCRIBE INPUT_P prepared_name using_descriptor
     {
         $$.input = 1;
-        $$.stmt_name = $3;
+        $$.stmt_name = @3;
     }
     | SQL_DESCRIBE opt_output prepared_name using_descriptor
     {
@@ -1509,33 +1342,27 @@ ECPGDescribe: SQL_DESCRIBE INPUT_P prepared_name using_descriptor
         add_variable_to_head(&argsresult, var, &no_indicator);

         $$.input = 0;
-        $$.stmt_name = $3;
+        $$.stmt_name = @3;
     }
     | SQL_DESCRIBE opt_output prepared_name into_descriptor
     {
         $$.input = 0;
-        $$.stmt_name = $3;
+        $$.stmt_name = @3;
     }
     | SQL_DESCRIBE INPUT_P prepared_name into_sqlda
     {
         $$.input = 1;
-        $$.stmt_name = $3;
+        $$.stmt_name = @3;
     }
     | SQL_DESCRIBE opt_output prepared_name into_sqlda
     {
         $$.input = 0;
-        $$.stmt_name = $3;
+        $$.stmt_name = @3;
     }
     ;

 opt_output: SQL_OUTPUT
-    {
-        $$ = mm_strdup("output");
-    }
     | /* EMPTY */
-    {
-        $$ = EMPTY;
-    }
     ;

 /*
@@ -1549,8 +1376,8 @@ opt_output: SQL_OUTPUT
  */
 ECPGAllocateDescr: SQL_ALLOCATE SQL_DESCRIPTOR quoted_ident_stringvar
     {
-        add_descriptor($3, connection);
-        $$ = $3;
+        add_descriptor(@3, connection);
+        @$ = @3;
     }
     ;

@@ -1560,8 +1387,8 @@ ECPGAllocateDescr: SQL_ALLOCATE SQL_DESCRIPTOR quoted_ident_stringvar
  */
 ECPGDeallocateDescr: DEALLOCATE SQL_DESCRIPTOR quoted_ident_stringvar
     {
-        drop_descriptor($3, connection);
-        $$ = $3;
+        drop_descriptor(@3, connection);
+        @$ = @3;
     }
     ;

@@ -1571,7 +1398,7 @@ ECPGDeallocateDescr: DEALLOCATE SQL_DESCRIPTOR quoted_ident_stringvar

 ECPGGetDescriptorHeader: SQL_GET SQL_DESCRIPTOR quoted_ident_stringvar ECPGGetDescHeaderItems
     {
-        $$ = $3;
+        @$ = @3;
     }
     ;

@@ -1581,13 +1408,13 @@ ECPGGetDescHeaderItems: ECPGGetDescHeaderItem

 ECPGGetDescHeaderItem: cvariable '=' desc_header_item
     {
-        push_assignment($1, $3);
+        push_assignment(@1, $3);
     }
     ;

 ECPGSetDescriptorHeader: SET SQL_DESCRIPTOR quoted_ident_stringvar ECPGSetDescHeaderItems
     {
-        $$ = $3;
+        @$ = @3;
     }
     ;

@@ -1597,7 +1424,7 @@ ECPGSetDescHeaderItems: ECPGSetDescHeaderItem

 ECPGSetDescHeaderItem: desc_header_item '=' IntConstVar
     {
-        push_assignment($3, $1);
+        push_assignment(@3, $1);
     }
     ;

@@ -1605,14 +1432,10 @@ IntConstVar: Iconst
     {
         char       *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);

-        sprintf(length, "%zu", strlen($1));
-        new_variable($1, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
-        $$ = $1;
+        sprintf(length, "%zu", strlen(@1));
+        new_variable(@1, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
     }
     | cvariable
-    {
-        $$ = $1;
-    }
     ;

 desc_header_item: SQL_COUNT
@@ -1627,8 +1450,8 @@ desc_header_item: SQL_COUNT

 ECPGGetDescriptor: SQL_GET SQL_DESCRIPTOR quoted_ident_stringvar VALUE_P IntConstVar ECPGGetDescItems
     {
-        $$.str = $5;
-        $$.name = $3;
+        $$.str = @5;
+        $$.name = @3;
     }
     ;

@@ -1638,14 +1461,14 @@ ECPGGetDescItems: ECPGGetDescItem

 ECPGGetDescItem: cvariable '=' descriptor_item
     {
-        push_assignment($1, $3);
+        push_assignment(@1, $3);
     }
     ;

 ECPGSetDescriptor: SET SQL_DESCRIPTOR quoted_ident_stringvar VALUE_P IntConstVar ECPGSetDescItems
     {
-        $$.str = $5;
-        $$.name = $3;
+        $$.str = @5;
+        $$.name = @3;
     }
     ;

@@ -1655,7 +1478,7 @@ ECPGSetDescItems: ECPGSetDescItem

 ECPGSetDescItem: descriptor_item '=' AllConstVar
     {
-        push_assignment($3, $1);
+        push_assignment(@3, $1);
     }
     ;

@@ -1663,41 +1486,37 @@ AllConstVar: ecpg_fconst
     {
         char       *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);

-        sprintf(length, "%zu", strlen($1));
-        new_variable($1, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
-        $$ = $1;
+        sprintf(length, "%zu", strlen(@1));
+        new_variable(@1, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
     }
     | IntConstVar
-    {
-        $$ = $1;
-    }
     | '-' ecpg_fconst
     {
         char       *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
-        char       *var = cat2_str(mm_strdup("-"), $2);
+        char       *var = cat2_str(mm_strdup("-"), @2);

         sprintf(length, "%zu", strlen(var));
         new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
-        $$ = var;
+        @$ = var;
     }
     | '-' Iconst
     {
         char       *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
-        char       *var = cat2_str(mm_strdup("-"), $2);
+        char       *var = cat2_str(mm_strdup("-"), @2);

         sprintf(length, "%zu", strlen(var));
         new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
-        $$ = var;
+        @$ = var;
     }
     | ecpg_sconst
     {
         char       *length = mm_alloc(sizeof(int) * CHAR_BIT * 10 / 3);
-        char       *var = $1 + 1;
+        char       *var = @1 + 1;

         var[strlen(var) - 1] = '\0';
         sprintf(length, "%zu", strlen(var));
         new_variable(var, ECPGmake_simple_type(ECPGt_const, length, 0), 0);
-        $$ = var;
+        @$ = var;
     }
     ;

@@ -1724,22 +1543,16 @@ descriptor_item: SQL_CARDINALITY        { $$ = ECPGd_cardinality; }
  */
 ECPGSetAutocommit: SET SQL_AUTOCOMMIT '=' on_off
     {
-        $$ = $4;
+        @$ = @4;
     }
     | SET SQL_AUTOCOMMIT TO on_off
     {
-        $$ = $4;
+        @$ = @4;
     }
     ;

 on_off: ON
-    {
-        $$ = mm_strdup("on");
-    }
     | OFF
-    {
-        $$ = mm_strdup("off");
-    }
     ;

 /*
@@ -1748,15 +1561,15 @@ on_off: ON
  */
 ECPGSetConnection: SET CONNECTION TO connection_object
     {
-        $$ = $4;
+        @$ = @4;
     }
     | SET CONNECTION '=' connection_object
     {
-        $$ = $4;
+        @$ = @4;
     }
     | SET CONNECTION connection_object
     {
-        $$ = $3;
+        @$ = @3;
     }
     ;

@@ -1771,23 +1584,17 @@ ECPGTypedef: TYPE_P
     }
     ECPGColLabel IS var_type opt_array_bounds opt_reference
     {
-        add_typedef($3, $6.index1, $6.index2, $5.type_enum, $5.type_dimension, $5.type_index, initializer, *$7 ? 1 :
0);
+        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, mm_strdup("/* exec sql type"), mm_strdup(@3), mm_strdup("is"), mm_strdup($5.type_str),
mm_strdup($6.str),@7, mm_strdup("*/")); 
         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, mm_strdup("typedef "), mm_strdup($5.type_str), *@7 ? mm_strdup("*") : mm_strdup(""),
mm_strdup(@3),mm_strdup($6.str), mm_strdup(";")); 
     }
     ;

 opt_reference: SQL_REFERENCE
-    {
-        $$ = mm_strdup("reference");
-    }
     | /* EMPTY */
-    {
-        $$ = EMPTY;
-    }
     ;

 /*
@@ -1801,7 +1608,7 @@ ECPGVar: SQL_VAR
     }
     ColLabel    IS var_type opt_array_bounds opt_reference
     {
-        struct variable *p = find_variable($3);
+        struct variable *p = find_variable(@3);
         char       *dimension = $6.index1;
         char       *length = $6.index2;
         struct ECPGtype *type;
@@ -1812,7 +1619,7 @@ 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)
             {
@@ -1856,7 +1663,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, mm_strdup("/* exec sql var"), mm_strdup(@3), mm_strdup("is"),
mm_strdup($5.type_str),mm_strdup($6.str), @7, mm_strdup("*/")); 
     }
     ;

@@ -1868,19 +1675,19 @@ 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("; */"));
+        @$ = cat_str(3, mm_strdup("/* exec sql whenever sqlerror "), $3.str, mm_strdup("; */"));
     }
     | 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("; */"));
+        @$ = cat_str(3, mm_strdup("/* exec sql whenever not found "), $4.str, mm_strdup("; */"));
     }
     | 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("; */"));
+        @$ = cat_str(3, mm_strdup("/* exec sql whenever sql_warning "), $3.str, mm_strdup("; */"));
     }
     ;

@@ -1905,19 +1712,19 @@ action: CONTINUE_P
     | SQL_GOTO name
     {
         $<action>$.code = W_GOTO;
-        $<action>$.command = mm_strdup($2);
-        $<action>$.str = cat2_str(mm_strdup("goto "), $2);
+        $<action>$.command = mm_strdup(@2);
+        $<action>$.str = cat2_str(mm_strdup("goto "), @2);
     }
     | SQL_GO TO name
     {
         $<action>$.code = W_GOTO;
-        $<action>$.command = mm_strdup($3);
-        $<action>$.str = cat2_str(mm_strdup("goto "), $3);
+        $<action>$.command = mm_strdup(@3);
+        $<action>$.str = cat2_str(mm_strdup("goto "), @3);
     }
     | DO name '(' c_args ')'
     {
         $<action>$.code = W_DO;
-        $<action>$.command = cat_str(4, $2, mm_strdup("("), $4, mm_strdup(")"));
+        $<action>$.command = cat_str(4, @2, mm_strdup("("), @4, mm_strdup(")"));
         $<action>$.str = cat2_str(mm_strdup("do"), mm_strdup($<action>$.command));
     }
     | DO SQL_BREAK
@@ -1935,13 +1742,13 @@ action: CONTINUE_P
     | CALL name '(' c_args ')'
     {
         $<action>$.code = W_DO;
-        $<action>$.command = cat_str(4, $2, mm_strdup("("), $4, mm_strdup(")"));
+        $<action>$.command = cat_str(4, @2, mm_strdup("("), @4, mm_strdup(")"));
         $<action>$.str = cat2_str(mm_strdup("call"), mm_strdup($<action>$.command));
     }
     | CALL name
     {
         $<action>$.code = W_DO;
-        $<action>$.command = cat2_str($2, mm_strdup("()"));
+        $<action>$.command = cat2_str(@2, mm_strdup("()"));
         $<action>$.str = cat2_str(mm_strdup("call"), mm_strdup($<action>$.command));
     }
     ;
@@ -1949,63 +1756,63 @@ action: CONTINUE_P
 /* some other stuff for ecpg */

 /* additional unreserved keywords */
-ECPGKeywords: ECPGKeywords_vanames        { $$ = $1; }
-    | ECPGKeywords_rest                    { $$ = $1; }
-    ;
-
-ECPGKeywords_vanames: SQL_BREAK            { $$ = mm_strdup("break"); }
-    | SQL_CARDINALITY                    { $$ = mm_strdup("cardinality"); }
-    | SQL_COUNT                            { $$ = mm_strdup("count"); }
-    | SQL_DATETIME_INTERVAL_CODE        { $$ = mm_strdup("datetime_interval_code"); }
-    | SQL_DATETIME_INTERVAL_PRECISION    { $$ = mm_strdup("datetime_interval_precision"); }
-    | SQL_FOUND                            { $$ = mm_strdup("found"); }
-    | SQL_GO                            { $$ = mm_strdup("go"); }
-    | SQL_GOTO                            { $$ = mm_strdup("goto"); }
-    | SQL_IDENTIFIED                    { $$ = mm_strdup("identified"); }
-    | SQL_INDICATOR                        { $$ = mm_strdup("indicator"); }
-    | SQL_KEY_MEMBER                    { $$ = mm_strdup("key_member"); }
-    | SQL_LENGTH                        { $$ = mm_strdup("length"); }
-    | SQL_NULLABLE                        { $$ = mm_strdup("nullable"); }
-    | SQL_OCTET_LENGTH                    { $$ = mm_strdup("octet_length"); }
-    | SQL_RETURNED_LENGTH                { $$ = mm_strdup("returned_length"); }
-    | SQL_RETURNED_OCTET_LENGTH            { $$ = mm_strdup("returned_octet_length"); }
-    | SQL_SCALE                            { $$ = mm_strdup("scale"); }
-    | SQL_SECTION                        { $$ = mm_strdup("section"); }
-    | SQL_SQLERROR                        { $$ = mm_strdup("sqlerror"); }
-    | SQL_SQLPRINT                        { $$ = mm_strdup("sqlprint"); }
-    | SQL_SQLWARNING                    { $$ = mm_strdup("sqlwarning"); }
-    | SQL_STOP                            { $$ = mm_strdup("stop"); }
-    ;
-
-ECPGKeywords_rest: SQL_CONNECT            { $$ = mm_strdup("connect"); }
-    | SQL_DESCRIBE                        { $$ = mm_strdup("describe"); }
-    | SQL_DISCONNECT                    { $$ = mm_strdup("disconnect"); }
-    | SQL_OPEN                            { $$ = mm_strdup("open"); }
-    | SQL_VAR                            { $$ = mm_strdup("var"); }
-    | SQL_WHENEVER                        { $$ = mm_strdup("whenever"); }
+ECPGKeywords: ECPGKeywords_vanames
+    | ECPGKeywords_rest
+    ;
+
+ECPGKeywords_vanames: SQL_BREAK
+    | SQL_CARDINALITY
+    | SQL_COUNT
+    | SQL_DATETIME_INTERVAL_CODE
+    | SQL_DATETIME_INTERVAL_PRECISION
+    | SQL_FOUND
+    | SQL_GO
+    | SQL_GOTO
+    | SQL_IDENTIFIED
+    | SQL_INDICATOR
+    | SQL_KEY_MEMBER
+    | SQL_LENGTH
+    | SQL_NULLABLE
+    | SQL_OCTET_LENGTH
+    | SQL_RETURNED_LENGTH
+    | SQL_RETURNED_OCTET_LENGTH
+    | SQL_SCALE
+    | SQL_SECTION
+    | SQL_SQLERROR
+    | SQL_SQLPRINT
+    | SQL_SQLWARNING
+    | SQL_STOP
+    ;
+
+ECPGKeywords_rest: SQL_CONNECT
+    | SQL_DESCRIBE
+    | SQL_DISCONNECT
+    | SQL_OPEN
+    | SQL_VAR
+    | SQL_WHENEVER
     ;

 /* additional keywords that can be SQL type names (but not ECPGColLabels) */
-ECPGTypeName: SQL_BOOL                    { $$ = mm_strdup("bool"); }
-    | SQL_LONG                            { $$ = mm_strdup("long"); }
-    | SQL_OUTPUT                        { $$ = mm_strdup("output"); }
-    | SQL_SHORT                            { $$ = mm_strdup("short"); }
-    | SQL_STRUCT                        { $$ = mm_strdup("struct"); }
-    | SQL_SIGNED                        { $$ = mm_strdup("signed"); }
-    | SQL_UNSIGNED                        { $$ = mm_strdup("unsigned"); }
+ECPGTypeName: SQL_BOOL
+    | SQL_LONG
+    | SQL_OUTPUT
+    | SQL_SHORT
+    | SQL_STRUCT
+    | SQL_SIGNED
+    | SQL_UNSIGNED
     ;

-symbol: ColLabel                        { $$ = $1; }
+symbol: ColLabel
     ;

-ECPGColId: ecpg_ident                    { $$ = $1; }
-    | unreserved_keyword                { $$ = $1; }
-    | col_name_keyword                    { $$ = $1; }
-    | ECPGunreserved_interval            { $$ = $1; }
-    | ECPGKeywords                        { $$ = $1; }
-    | ECPGCKeywords                        { $$ = $1; }
-    | CHAR_P                            { $$ = mm_strdup("char"); }
-    | VALUES                            { $$ = mm_strdup("values"); }
+ECPGColId: ecpg_ident
+    | unreserved_keyword
+    | col_name_keyword
+    | ECPGunreserved_interval
+    | ECPGKeywords
+    | ECPGCKeywords
+    | CHAR_P
+    | VALUES
     ;

 /*
@@ -2018,58 +1825,58 @@ ECPGColId: ecpg_ident                    { $$ = $1; }

 /* Column identifier --- names that can be column, table, etc names.
  */
-ColId: ecpg_ident                        { $$ = $1; }
-    | all_unreserved_keyword            { $$ = $1; }
-    | col_name_keyword                    { $$ = $1; }
-    | ECPGKeywords                        { $$ = $1; }
-    | ECPGCKeywords                        { $$ = $1; }
-    | CHAR_P                            { $$ = mm_strdup("char"); }
-    | VALUES                            { $$ = mm_strdup("values"); }
+ColId: ecpg_ident
+    | all_unreserved_keyword
+    | col_name_keyword
+    | ECPGKeywords
+    | ECPGCKeywords
+    | CHAR_P
+    | VALUES
     ;

 /* Type/function identifier --- names that can be type or function names.
  */
-type_function_name: ecpg_ident            { $$ = $1; }
-    | all_unreserved_keyword            { $$ = $1; }
-    | type_func_name_keyword            { $$ = $1; }
-    | ECPGKeywords                        { $$ = $1; }
-    | ECPGCKeywords                        { $$ = $1; }
-    | ECPGTypeName                        { $$ = $1; }
+type_function_name: ecpg_ident
+    | all_unreserved_keyword
+    | type_func_name_keyword
+    | ECPGKeywords
+    | ECPGCKeywords
+    | ECPGTypeName
     ;

 /* Column label --- allowed labels in "AS" clauses.
  * This presently includes *all* Postgres keywords.
  */
-ColLabel: ECPGColLabel                    { $$ = $1; }
-    | ECPGTypeName                        { $$ = $1; }
-    | CHAR_P                            { $$ = mm_strdup("char"); }
-    | CURRENT_P                            { $$ = mm_strdup("current"); }
-    | INPUT_P                            { $$ = mm_strdup("input"); }
-    | INT_P                                { $$ = mm_strdup("int"); }
-    | TO                                { $$ = mm_strdup("to"); }
-    | UNION                                { $$ = mm_strdup("union"); }
-    | VALUES                            { $$ = mm_strdup("values"); }
-    | ECPGCKeywords                        { $$ = $1; }
-    | ECPGunreserved_interval            { $$ = $1; }
-    ;
-
-ECPGColLabel: ecpg_ident                { $$ = $1; }
-    | unreserved_keyword                { $$ = $1; }
-    | col_name_keyword                    { $$ = $1; }
-    | type_func_name_keyword            { $$ = $1; }
-    | reserved_keyword                    { $$ = $1; }
-    | ECPGKeywords_vanames                { $$ = $1; }
-    | ECPGKeywords_rest                    { $$ = $1; }
-    | CONNECTION                        { $$ = mm_strdup("connection"); }
-    ;
-
-ECPGCKeywords: S_AUTO                    { $$ = mm_strdup("auto"); }
-    | S_CONST                            { $$ = mm_strdup("const"); }
-    | S_EXTERN                            { $$ = mm_strdup("extern"); }
-    | S_REGISTER                        { $$ = mm_strdup("register"); }
-    | S_STATIC                            { $$ = mm_strdup("static"); }
-    | S_TYPEDEF                            { $$ = mm_strdup("typedef"); }
-    | S_VOLATILE                        { $$ = mm_strdup("volatile"); }
+ColLabel: ECPGColLabel
+    | ECPGTypeName
+    | CHAR_P
+    | CURRENT_P
+    | INPUT_P
+    | INT_P
+    | TO
+    | UNION
+    | VALUES
+    | ECPGCKeywords
+    | ECPGunreserved_interval
+    ;
+
+ECPGColLabel: ecpg_ident
+    | unreserved_keyword
+    | col_name_keyword
+    | type_func_name_keyword
+    | reserved_keyword
+    | ECPGKeywords_vanames
+    | ECPGKeywords_rest
+    | CONNECTION
+    ;
+
+ECPGCKeywords: S_AUTO
+    | S_CONST
+    | S_EXTERN
+    | S_REGISTER
+    | S_STATIC
+    | S_TYPEDEF
+    | S_VOLATILE
     ;

 /* "Unreserved" keywords --- available for use as any kind of name.
@@ -2086,17 +1893,17 @@ ECPGCKeywords: S_AUTO                    { $$ = mm_strdup("auto"); }
  *
  * The mentioned exclusions are done by $replace_line settings in parse.pl.
  */
-all_unreserved_keyword: unreserved_keyword    { $$ = $1; }
-    | ECPGunreserved_interval            { $$ = $1; }
-    | CONNECTION                        { $$ = mm_strdup("connection"); }
+all_unreserved_keyword: unreserved_keyword
+    | ECPGunreserved_interval
+    | CONNECTION
     ;

-ECPGunreserved_interval: DAY_P            { $$ = mm_strdup("day"); }
-    | HOUR_P                            { $$ = mm_strdup("hour"); }
-    | MINUTE_P                            { $$ = mm_strdup("minute"); }
-    | MONTH_P                            { $$ = mm_strdup("month"); }
-    | SECOND_P                            { $$ = mm_strdup("second"); }
-    | YEAR_P                            { $$ = mm_strdup("year"); }
+ECPGunreserved_interval: DAY_P
+    | HOUR_P
+    | MINUTE_P
+    | MONTH_P
+    | SECOND_P
+    | YEAR_P
     ;

 into_list: coutputvariable | into_list ',' coutputvariable
@@ -2106,73 +1913,66 @@ ecpgstart: SQL_START
     {
         reset_variables();
         pacounter = 1;
+        @$ = EMPTY;
     }
     ;

 c_args: /* EMPTY */
-    {
-        $$ = EMPTY;
-    }
     | c_list
-    {
-        $$ = $1;
-    }
     ;

 coutputvariable: cvariable indicator
     {
-        add_variable_to_head(&argsresult, find_variable($1), find_variable($2));
+        add_variable_to_head(&argsresult, find_variable(@1), find_variable(@2));
     }
     | cvariable
     {
-        add_variable_to_head(&argsresult, find_variable($1), &no_indicator);
+        add_variable_to_head(&argsresult, find_variable(@1), &no_indicator);
     }
     ;


 civarind: cvariable indicator
     {
-        if (find_variable($2)->type->type == ECPGt_array)
+        if (find_variable(@2)->type->type == ECPGt_array)
             mmerror(PARSE_ERROR, ET_ERROR, "arrays of indicators are not allowed on input");

-        add_variable_to_head(&argsinsert, find_variable($1), find_variable($2));
-        $$ = create_questionmarks($1, false);
+        add_variable_to_head(&argsinsert, find_variable(@1), find_variable(@2));
+        @$ = create_questionmarks(@1, false);
     }
     ;

 char_civar: char_variable
     {
-        char       *ptr = strstr($1, ".arr");
+        char       *ptr = strstr(@1, ".arr");

         if (ptr)                /* varchar, we need the struct name here, not
                                  * the struct element */
             *ptr = '\0';
-        add_variable_to_head(&argsinsert, find_variable($1), &no_indicator);
-        $$ = $1;
+        add_variable_to_head(&argsinsert, find_variable(@1), &no_indicator);
     }
     ;

 civar: cvariable
     {
-        add_variable_to_head(&argsinsert, find_variable($1), &no_indicator);
-        $$ = create_questionmarks($1, false);
+        add_variable_to_head(&argsinsert, find_variable(@1), &no_indicator);
+        @$ = create_questionmarks(@1, false);
     }
     ;

 indicator: cvariable
     {
-        check_indicator((find_variable($1))->type);
-        $$ = $1;
+        check_indicator((find_variable(@1))->type);
     }
     | SQL_INDICATOR cvariable
     {
-        check_indicator((find_variable($2))->type);
-        $$ = $2;
+        check_indicator((find_variable(@2))->type);
+        @$ = @2;
     }
     | SQL_INDICATOR name
     {
-        check_indicator((find_variable($2))->type);
-        $$ = $2;
+        check_indicator((find_variable(@2))->type);
+        @$ = @2;
     }
     ;

@@ -2182,7 +1982,7 @@ cvariable: CVARIABLE
          * As long as multidimensional arrays are not implemented we have to
          * check for those here
          */
-        char       *ptr = $1;
+        char       *ptr = @1;
         int            brace_open = 0,
                     brace = false;

@@ -2209,57 +2009,44 @@ cvariable: CVARIABLE
                     break;
             }
         }
-        $$ = $1;
     }
     ;

 ecpg_param: PARAM
     {
-        $$ = make_name();
+        @$ = make_name();
     }
     ;

 ecpg_bconst: BCONST
-    {
-        $$ = $1;
-    }
     ;

 ecpg_fconst: FCONST
     {
-        $$ = make_name();
+        @$ = make_name();
     }
     ;

 ecpg_sconst: SCONST
-    {
-        $$ = $1;
-    }
     ;

 ecpg_xconst: XCONST
-    {
-        $$ = $1;
-    }
     ;

 ecpg_ident: IDENT
-    {
-        $$ = $1;
-    }
     | CSTRING
     {
-        $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+        @$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
     }
     ;

 quoted_ident_stringvar: name
     {
-        $$ = make3_str(mm_strdup("\""), $1, mm_strdup("\""));
+        @$ = make3_str(mm_strdup("\""), @1, mm_strdup("\""));
     }
     | char_variable
     {
-        $$ = make3_str(mm_strdup("("), $1, mm_strdup(")"));
+        @$ = make3_str(mm_strdup("("), @1, mm_strdup(")"));
     }
     ;

@@ -2268,221 +2055,151 @@ quoted_ident_stringvar: name
  */

 c_stuff_item: c_anything
-    {
-        $$ = $1;
-    }
     | '(' ')'
     {
-        $$ = mm_strdup("()");
+        @$ = mm_strdup("()");
     }
     | '(' c_stuff ')'
-    {
-        $$ = cat_str(3, mm_strdup("("), $2, mm_strdup(")"));
-    }
     ;

 c_stuff: c_stuff_item
-    {
-        $$ = $1;
-    }
     | c_stuff c_stuff_item
-    {
-        $$ = cat2_str($1, $2);
-    }
     ;

 c_list: c_term
-    {
-        $$ = $1;
-    }
     | c_list ',' c_term
-    {
-        $$ = cat_str(3, $1, mm_strdup(","), $3);
-    }
     ;

 c_term: c_stuff
-    {
-        $$ = $1;
-    }
     | '{' c_list '}'
-    {
-        $$ = cat_str(3, mm_strdup("{"), $2, mm_strdup("}"));
-    }
-    ;
-
-c_thing: c_anything                    { $$ = $1; }
-    | '('                            { $$ = mm_strdup("("); }
-    | ')'                            { $$ = mm_strdup(")"); }
-    | ','                            { $$ = mm_strdup(","); }
-    | ';'                            { $$ = mm_strdup(";"); }
-    ;
-
-c_anything: ecpg_ident                { $$ = $1; }
-    | Iconst                        { $$ = $1; }
-    | ecpg_fconst                    { $$ = $1; }
-    | ecpg_sconst                    { $$ = $1; }
-    | '*'                            { $$ = mm_strdup("*"); }
-    | '+'                            { $$ = mm_strdup("+"); }
-    | '-'                            { $$ = mm_strdup("-"); }
-    | '/'                            { $$ = mm_strdup("/"); }
-    | '%'                            { $$ = mm_strdup("%"); }
-    | NULL_P                        { $$ = mm_strdup("NULL"); }
-    | S_ADD                            { $$ = mm_strdup("+="); }
-    | S_AND                            { $$ = mm_strdup("&&"); }
-    | S_ANYTHING                    { $$ = make_name(); }
-    | S_AUTO                        { $$ = mm_strdup("auto"); }
-    | S_CONST                        { $$ = mm_strdup("const"); }
-    | S_DEC                            { $$ = mm_strdup("--"); }
-    | S_DIV                            { $$ = mm_strdup("/="); }
-    | S_DOTPOINT                    { $$ = mm_strdup(".*"); }
-    | S_EQUAL                        { $$ = mm_strdup("=="); }
-    | S_EXTERN                        { $$ = mm_strdup("extern"); }
-    | S_INC                            { $$ = mm_strdup("++"); }
-    | S_LSHIFT                        { $$ = mm_strdup("<<"); }
-    | S_MEMBER                        { $$ = mm_strdup("->"); }
-    | S_MEMPOINT                    { $$ = mm_strdup("->*"); }
-    | S_MOD                            { $$ = mm_strdup("%="); }
-    | S_MUL                            { $$ = mm_strdup("*="); }
-    | S_NEQUAL                        { $$ = mm_strdup("!="); }
-    | S_OR                            { $$ = mm_strdup("||"); }
-    | S_REGISTER                    { $$ = mm_strdup("register"); }
-    | S_RSHIFT                        { $$ = mm_strdup(">>"); }
-    | S_STATIC                        { $$ = mm_strdup("static"); }
-    | S_SUB                            { $$ = mm_strdup("-="); }
-    | S_TYPEDEF                        { $$ = mm_strdup("typedef"); }
-    | S_VOLATILE                    { $$ = mm_strdup("volatile"); }
-    | SQL_BOOL                        { $$ = mm_strdup("bool"); }
-    | ENUM_P                        { $$ = mm_strdup("enum"); }
-    | HOUR_P                        { $$ = mm_strdup("hour"); }
-    | INT_P                            { $$ = mm_strdup("int"); }
-    | SQL_LONG                        { $$ = mm_strdup("long"); }
-    | MINUTE_P                        { $$ = mm_strdup("minute"); }
-    | MONTH_P                        { $$ = mm_strdup("month"); }
-    | SECOND_P                        { $$ = mm_strdup("second"); }
-    | SQL_SHORT                        { $$ = mm_strdup("short"); }
-    | SQL_SIGNED                    { $$ = mm_strdup("signed"); }
-    | SQL_STRUCT                    { $$ = mm_strdup("struct"); }
-    | SQL_UNSIGNED                    { $$ = mm_strdup("unsigned"); }
-    | YEAR_P                        { $$ = mm_strdup("year"); }
-    | CHAR_P                        { $$ = mm_strdup("char"); }
-    | FLOAT_P                        { $$ = mm_strdup("float"); }
-    | TO                            { $$ = mm_strdup("to"); }
-    | UNION                            { $$ = mm_strdup("union"); }
-    | VARCHAR                        { $$ = mm_strdup("varchar"); }
-    | '['                            { $$ = mm_strdup("["); }
-    | ']'                            { $$ = mm_strdup("]"); }
-    | '='                            { $$ = mm_strdup("="); }
-    | ':'                            { $$ = mm_strdup(":"); }
+    ;
+
+c_thing: c_anything
+    | '('
+    | ')'
+    | ','
+    | ';'
+    ;
+
+/*
+ * Note: NULL_P is treated specially to force it to be output in upper case,
+ * since it's likely meant as a reference to the standard C macro NULL.
+ */
+c_anything: ecpg_ident
+    | Iconst
+    | ecpg_fconst
+    | ecpg_sconst
+    | '*'
+    | '+'
+    | '-'
+    | '/'
+    | '%'
+    | NULL_P                        { @$ = mm_strdup("NULL"); }
+    | S_ADD
+    | S_AND
+    | S_ANYTHING
+    | S_AUTO
+    | S_CONST
+    | S_DEC
+    | S_DIV
+    | S_DOTPOINT
+    | S_EQUAL
+    | S_EXTERN
+    | S_INC
+    | S_LSHIFT
+    | S_MEMBER
+    | S_MEMPOINT
+    | S_MOD
+    | S_MUL
+    | S_NEQUAL
+    | S_OR
+    | S_REGISTER
+    | S_RSHIFT
+    | S_STATIC
+    | S_SUB
+    | S_TYPEDEF
+    | S_VOLATILE
+    | SQL_BOOL
+    | ENUM_P
+    | HOUR_P
+    | INT_P
+    | SQL_LONG
+    | MINUTE_P
+    | MONTH_P
+    | SECOND_P
+    | SQL_SHORT
+    | SQL_SIGNED
+    | SQL_STRUCT
+    | SQL_UNSIGNED
+    | YEAR_P
+    | CHAR_P
+    | FLOAT_P
+    | TO
+    | UNION
+    | VARCHAR
+    | '['
+    | ']'
+    | '='
+    | ':'
     ;

 DeallocateStmt: DEALLOCATE prepared_name
     {
-        check_declared_list($2);
-        $$ = $2;
+        check_declared_list(@2);
+        @$ = @2;
     }
     | DEALLOCATE PREPARE prepared_name
     {
-        check_declared_list($3);
-        $$ = $3;
+        check_declared_list(@3);
+        @$ = @3;
     }
     | DEALLOCATE ALL
     {
-        $$ = mm_strdup("all");
+        @$ = mm_strdup("all");
     }
     | DEALLOCATE PREPARE ALL
     {
-        $$ = mm_strdup("all");
+        @$ = mm_strdup("all");
     }
     ;

 Iresult: Iconst
-    {
-        $$ = $1;
-    }
     | '(' Iresult ')'
-    {
-        $$ = cat_str(3, mm_strdup("("), $2, mm_strdup(")"));
-    }
     | Iresult '+' Iresult
-    {
-        $$ = cat_str(3, $1, mm_strdup("+"), $3);
-    }
     | Iresult '-' Iresult
-    {
-        $$ = cat_str(3, $1, mm_strdup("-"), $3);
-    }
     | Iresult '*' Iresult
-    {
-        $$ = cat_str(3, $1, mm_strdup("*"), $3);
-    }
     | Iresult '/' Iresult
-    {
-        $$ = cat_str(3, $1, mm_strdup("/"), $3);
-    }
     | Iresult '%' Iresult
-    {
-        $$ = cat_str(3, $1, mm_strdup("%"), $3);
-    }
     | ecpg_sconst
-    {
-        $$ = $1;
-    }
     | ColId
-    {
-        $$ = $1;
-    }
     | ColId '(' var_type ')'
     {
-        if (pg_strcasecmp($1, "sizeof") != 0)
+        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, mm_strdup("("), $3.type_str, mm_strdup(")"));
     }
     ;

 execute_rest: /* EMPTY */
-    {
-        $$ = EMPTY;
-    }
     | ecpg_using opt_ecpg_into
-    {
-        $$ = EMPTY;
-    }
     | ecpg_into ecpg_using
-    {
-        $$ = EMPTY;
-    }
     | ecpg_into
-    {
-        $$ = EMPTY;
-    }
     ;

 ecpg_into: INTO into_list
     {
-        $$ = EMPTY;
+        /* always suppress this from the constructed string */
+        @$ = EMPTY;
     }
     | into_descriptor
-    {
-        $$ = $1;
-    }
     ;

 opt_ecpg_into: /* EMPTY */
-    {
-        $$ = EMPTY;
-    }
     | ecpg_into
-    {
-        $$ = $1;
-    }
     ;

 ecpg_fetch_into: ecpg_into
-    {
-        $$ = $1;
-    }
     | using_descriptor
     {
         struct variable *var;
@@ -2490,18 +2207,11 @@ ecpg_fetch_into: ecpg_into
         var = argsinsert->variable;
         remove_variable_from_list(&argsinsert, var);
         add_variable_to_head(&argsresult, var, &no_indicator);
-        $$ = $1;
     }
     ;

 opt_ecpg_fetch_into: /* EMPTY */
-    {
-        $$ = EMPTY;
-    }
     | ecpg_fetch_into
-    {
-        $$ = $1;
-    }
     ;

 %%
diff --git a/src/interfaces/ecpg/preproc/ecpg.type b/src/interfaces/ecpg/preproc/ecpg.type
index 4fe80a5a4b..2929f358ff 100644
--- a/src/interfaces/ecpg/preproc/ecpg.type
+++ b/src/interfaces/ecpg/preproc/ecpg.type
@@ -1,131 +1,4 @@
 /* src/interfaces/ecpg/preproc/ecpg.type */
-%type <str> ECPGAllocateDescr
-%type <str> ECPGCKeywords
-%type <str> ECPGColId
-%type <str> ECPGColLabel
-%type <str> ECPGConnect
-%type <str> ECPGCursorStmt
-%type <str> ECPGDeallocateDescr
-%type <str> ECPGDeclaration
-%type <str> ECPGDeclare
-%type <str> ECPGDeclareStmt
-%type <str> ECPGDisconnect
-%type <str> ECPGExecuteImmediateStmt
-%type <str> ECPGFree
-%type <str> ECPGGetDescHeaderItem
-%type <str> ECPGGetDescItem
-%type <str> ECPGGetDescriptorHeader
-%type <str> ECPGKeywords
-%type <str> ECPGKeywords_rest
-%type <str> ECPGKeywords_vanames
-%type <str> ECPGOpen
-%type <str> ECPGSetAutocommit
-%type <str> ECPGSetConnection
-%type <str> ECPGSetDescHeaderItem
-%type <str> ECPGSetDescItem
-%type <str> ECPGSetDescriptorHeader
-%type <str> ECPGTypeName
-%type <str> ECPGTypedef
-%type <str> ECPGVar
-%type <str> ECPGVarDeclaration
-%type <str> ECPGWhenever
-%type <str> ECPGunreserved_interval
-%type <str> UsingConst
-%type <str> UsingValue
-%type <str> all_unreserved_keyword
-%type <str> c_anything
-%type <str> c_args
-%type <str> c_list
-%type <str> c_stuff
-%type <str> c_stuff_item
-%type <str> c_term
-%type <str> c_thing
-%type <str> char_variable
-%type <str> char_civar
-%type <str> civar
-%type <str> civarind
-%type <str> ColId
-%type <str> ColLabel
-%type <str> connect_options
-%type <str> connection_object
-%type <str> connection_target
-%type <str> coutputvariable
-%type <str> cvariable
-%type <str> db_prefix
-%type <str> CreateAsStmt
-%type <str> DeallocateStmt
-%type <str> dis_name
-%type <str> ecpg_bconst
-%type <str> ecpg_fconst
-%type <str> ecpg_ident
-%type <str> ecpg_interval
-%type <str> ecpg_into
-%type <str> ecpg_fetch_into
-%type <str> ecpg_param
-%type <str> ecpg_sconst
-%type <str> ecpg_using
-%type <str> ecpg_xconst
-%type <str> enum_definition
-%type <str> enum_type
-%type <str> execstring
-%type <str> execute_rest
-%type <str> indicator
-%type <str> into_descriptor
-%type <str> into_sqlda
-%type <str> Iresult
-%type <str> on_off
-%type <str> opt_bit_field
-%type <str> opt_connection_name
-%type <str> opt_database_name
-%type <str> opt_ecpg_into
-%type <str> opt_ecpg_fetch_into
-%type <str> opt_ecpg_using
-%type <str> opt_initializer
-%type <str> opt_options
-%type <str> opt_output
-%type <str> opt_pointer
-%type <str> opt_port
-%type <str> opt_reference
-%type <str> opt_scale
-%type <str> opt_server
-%type <str> opt_user
-%type <str> opt_opt_value
-%type <str> ora_user
-%type <str> precision
-%type <str> prepared_name
-%type <str> quoted_ident_stringvar
-%type <str> s_struct_union
-%type <str> server
-%type <str> server_name
-%type <str> single_vt_declaration
-%type <str> storage_clause
-%type <str> storage_declaration
-%type <str> storage_modifier
-%type <str> struct_union_type
-%type <str> struct_union_type_with_symbol
-%type <str> symbol
-%type <str> type_declaration
-%type <str> type_function_name
-%type <str> user_name
-%type <str> using_descriptor
-%type <str> var_declaration
-%type <str> var_type_declarations
-%type <str> variable
-%type <str> variable_declarations
-%type <str> variable_list
-%type <str> vt_declarations
-
-%type <str> Op
-%type <str> IntConstVar
-%type <str> AllConstVar
-%type <str> CSTRING
-%type <str> CPP_LINE
-%type <str> CVARIABLE
-%type <str> BCONST
-%type <str> SCONST
-%type <str> XCONST
-%type <str> IDENT
-
 %type  <struct_union> s_struct_union_symbol

 %type  <descriptor> ECPGGetDescriptor
diff --git a/src/interfaces/ecpg/preproc/output.c b/src/interfaces/ecpg/preproc/output.c
index 6c0b8a27b1..8d2b6e7cb8 100644
--- a/src/interfaces/ecpg/preproc/output.c
+++ b/src/interfaces/ecpg/preproc/output.c
@@ -4,7 +4,7 @@

 #include "preproc_extern.h"

-static void output_escaped_str(char *str, bool quoted);
+static void output_escaped_str(const char *str, bool quoted);

 void
 output_line_number(void)
@@ -16,13 +16,12 @@ output_line_number(void)
 }

 void
-output_simple_statement(char *stmt, int whenever_mode)
+output_simple_statement(const char *stmt, int whenever_mode)
 {
     output_escaped_str(stmt, false);
     if (whenever_mode)
         whenever_action(whenever_mode);
     output_line_number();
-    free(stmt);
 }


@@ -133,7 +132,7 @@ static char *ecpg_statement_type_name[] = {
 };

 void
-output_statement(char *stmt, int whenever_mode, enum ECPG_statement_type st)
+output_statement(const char *stmt, int whenever_mode, enum ECPG_statement_type st)
 {
     fprintf(base_yyout, "{ ECPGdo(__LINE__, %d, %d, %s, %d, ", compat, force_indicator, connection ? connection :
"NULL",questionmarks); 

@@ -163,11 +162,10 @@ output_statement(char *stmt, int whenever_mode, enum ECPG_statement_type st)
     reset_variables();

     whenever_action(whenever_mode | 2);
-    free(stmt);
 }

 void
-output_prepare_statement(char *name, char *stmt)
+output_prepare_statement(const char *name, const char *stmt)
 {
     fprintf(base_yyout, "{ ECPGprepare(__LINE__, %s, %d, ", connection ? connection : "NULL", questionmarks);
     output_escaped_str(name, true);
@@ -175,11 +173,10 @@ output_prepare_statement(char *name, char *stmt)
     output_escaped_str(stmt, true);
     fputs(");", base_yyout);
     whenever_action(2);
-    free(name);
 }

 void
-output_deallocate_prepare_statement(char *name)
+output_deallocate_prepare_statement(const char *name)
 {
     const char *con = connection ? connection : "NULL";

@@ -193,11 +190,10 @@ output_deallocate_prepare_statement(char *name)
         fprintf(base_yyout, "{ ECPGdeallocate_all(__LINE__, %d, %s);", compat, con);

     whenever_action(2);
-    free(name);
 }

 static void
-output_escaped_str(char *str, bool quoted)
+output_escaped_str(const char *str, bool quoted)
 {
     int            i = 0;
     int            len = strlen(str);
diff --git a/src/interfaces/ecpg/preproc/parse.pl b/src/interfaces/ecpg/preproc/parse.pl
index 7e53401dd9..15bdef7dca 100644
--- a/src/interfaces/ecpg/preproc/parse.pl
+++ b/src/interfaces/ecpg/preproc/parse.pl
@@ -44,27 +44,10 @@ my %replace_token = (
     'IDENT' => 'ecpg_ident',
     'PARAM' => 'ecpg_param',);

-# Substitutions to apply to terminal token names to reconstruct the
-# literal form of the token.  (There is also a hard-wired substitution
-# rule that strips trailing '_P'.)
-my %replace_string = (
-    'FORMAT_LA' => 'format',
-    'NOT_LA' => 'not',
-    'NULLS_LA' => 'nulls',
-    'WITH_LA' => 'with',
-    'WITHOUT_LA' => 'without',
-    'TYPECAST' => '::',
-    'DOT_DOT' => '..',
-    'COLON_EQUALS' => ':=',
-    'EQUALS_GREATER' => '=>',
-    'LESS_EQUALS' => '<=',
-    'GREATER_EQUALS' => '>=',
-    'NOT_EQUALS' => '<>',);
-
-# This hash can provide a result type to override '<str>' for nonterminals
+# This hash can provide a result type to override "void" for nonterminals
 # that need that, or it can specify 'ignore' to cause us to skip the rule
-# for that nonterminal.  (In that case, ecpg.trailer had better provide
-# a substitute rule.)
+# for that nonterminal.  (In either case, ecpg.trailer had better provide
+# a substitute rule, since the default won't do.)
 my %replace_types = (
     'PrepareStmt' => '<prep>',
     'ExecuteStmt' => '<exec>',
@@ -175,11 +158,8 @@ my $non_term_id;
 # we plan to emit for the current rule.
 my $line = '';

-# @fields holds the items to be emitted in the token-concatenation action
-# for the current rule (assuming we emit one).  "$N" refers to the N'th
-# input token of the rule; anything else is a string to emit literally.
-# (We assume no such string can need to start with '$'.)
-my @fields;
+# count of tokens included in $line.
+my $line_count = 0;


 # Open parser / output file early, to raise errors early.
@@ -244,10 +224,6 @@ sub main
             $has_if_command = 1 if /^\s*if/;
         }

-        # We track %prec per-line, not per-rule, which is not quite right
-        # but there are no counterexamples in gram.y at present.
-        my $prec = 0;
-
         # Make sure any braces are split into separate fields
         s/{/ { /g;
         s/}/ } /g;
@@ -296,7 +272,7 @@ sub main
                 }

                 # If it's "<something>", it's a type in a %token declaration,
-                # which we can just drop.
+                # which we should just drop so that the tokens have void type.
                 if (substr($a, 0, 1) eq '<')
                 {
                     next;
@@ -376,7 +352,7 @@ sub main
                 if ($copymode)
                 {
                     # Print the accumulated rule.
-                    emit_rule(\@fields);
+                    emit_rule();
                     add_to_buffer('rules', ";\n\n");
                 }
                 else
@@ -386,8 +362,8 @@ sub main
                 }

                 # Reset for the next rule.
-                @fields = ();
                 $line = '';
+                $line_count = 0;
                 $in_rule = 0;
                 $alt_count = 0;
                 $has_feature_not_supported = 0;
@@ -401,11 +377,10 @@ sub main
                 {
                     # Print the accumulated alternative.
                     # Increment $alt_count for each non-ignored alternative.
-                    $alt_count += emit_rule(\@fields);
+                    $alt_count += emit_rule();
                 }

                 # Reset for the next alternative.
-                @fields = ();
                 # Start the next line with '|' if we've printed at least one
                 # alternative.
                 if ($alt_count > 1)
@@ -416,6 +391,7 @@ sub main
                 {
                     $line = '';
                 }
+                $line_count = 0;
                 $has_feature_not_supported = 0;
                 $has_if_command = 0;
                 next;
@@ -444,13 +420,9 @@ sub main
                     $fieldIndexer++;
                 }

-                # Check for %replace_types override of nonterminal's type
-                if (not defined $replace_types{$non_term_id})
-                {
-                    # By default, the type is <str>
-                    $replace_types{$non_term_id} = '<str>';
-                }
-                elsif ($replace_types{$non_term_id} eq 'ignore')
+                # Check for %replace_types entry indicating to ignore it.
+                if (defined $replace_types{$non_term_id}
+                    && $replace_types{$non_term_id} eq 'ignore')
                 {
                     # We'll ignore this nonterminal and rule altogether.
                     $copymode = 0;
@@ -470,22 +442,26 @@ sub main
                     $stmt_mode = 0;
                 }

-                # Emit appropriate %type declaration for this nonterminal.
-                my $tstr =
-                    '%type '
-                  . $replace_types{$non_term_id} . ' '
-                  . $non_term_id;
-                add_to_buffer('types', $tstr);
+                # Emit appropriate %type declaration for this nonterminal,
+                # if it has a type; otherwise omit that.
+                if (defined $replace_types{$non_term_id})
+                {
+                    my $tstr =
+                        '%type '
+                      . $replace_types{$non_term_id} . ' '
+                      . $non_term_id;
+                    add_to_buffer('types', $tstr);
+                }

                 # Emit the target part of the rule.
                 # Note: the leading space is just to match
                 # the old, rather weird output logic.
-                $tstr = ' ' . $non_term_id . ':';
+                my $tstr = ' ' . $non_term_id . ':';
                 add_to_buffer('rules', $tstr);

-                # Prepare for reading the fields (tokens) of the rule.
+                # Prepare for reading the tokens of the rule.
                 $line = '';
-                @fields = ();
+                $line_count = 0;
                 die "unterminated rule at grammar line $.\n"
                   if $in_rule;
                 $in_rule = 1;
@@ -496,48 +472,7 @@ sub main
             {
                 # Not a nonterminal declaration, so just add it to $line.
                 $line = $line . ' ' . $arr[$fieldIndexer];
-            }
-
-            # %prec and whatever follows it should get added to $line,
-            # but not to @fields.
-            if ($arr[$fieldIndexer] eq '%prec')
-            {
-                $prec = 1;
-                next;
-            }
-
-            # Emit transformed version of token to @fields if appropriate.
-            if (   $copymode
-                && !$prec
-                && !$comment
-                && $in_rule)
-            {
-                my $S = $arr[$fieldIndexer];
-
-                # If it's a known terminal token (other than Op) or a literal
-                # character, we need to emit the equivalent string, which'll
-                # later get wrapped into a C string literal, perhaps after
-                # merging with adjacent strings.
-                if ($S ne 'Op'
-                    && (defined $tokens{$S}
-                        || $S =~ /^'.+'$/))
-                {
-                    # Apply replace_string substitution if any.
-                    $S = $replace_string{$S} if (exists $replace_string{$S});
-                    # Automatically strip _P if present.
-                    $S =~ s/_P$//;
-                    # And get rid of quotes if it's a literal character.
-                    $S =~ tr/'//d;
-                    # Finally, downcase and push into @fields.
-                    push(@fields, lc($S));
-                }
-                else
-                {
-                    # Otherwise, push a $N reference to this input token.
-                    # (We assume this cannot be confused with anything the
-                    # above code would produce.)
-                    push(@fields, '$' . (scalar(@fields) + 1));
-                }
+                $line_count++;
             }
         }
     }
@@ -568,13 +503,13 @@ sub include_file
 # by an ecpg.addons entry.
 sub emit_rule_action
 {
-    my ($tag, $fields) = @_;
+    my ($tag) = @_;

     # See if we have an addons entry; if not, just emit default action
     my $rec = $addons{$tag};
     if (!$rec)
     {
-        emit_default_action($fields, 0);
+        emit_default_action(0);
         return;
     }

@@ -585,7 +520,7 @@ sub emit_rule_action
     if ($rectype eq 'rule')
     {
         # Emit default action and then the code block.
-        emit_default_action($fields, 0);
+        emit_default_action(0);
     }
     elsif ($rectype eq 'addon')
     {
@@ -600,7 +535,7 @@ sub emit_rule_action

     if ($rectype eq 'addon')
     {
-        emit_default_action($fields, 1);
+        emit_default_action(1);
     }
     return;
 }
@@ -626,12 +561,11 @@ sub dump_buffer
 }

 # Emit the default action (usually token concatenation) for the current rule.
-#   Pass: fields array, brace_printed boolean
+#   Pass: brace_printed boolean
 # brace_printed should be true if caller already printed action's open brace.
 sub emit_default_action
 {
-    my ($flds, $brace_printed) = @_;
-    my $len = scalar(@$flds);
+    my ($brace_printed) = @_;

     if ($stmt_mode == 0)
     {
@@ -651,91 +585,21 @@ sub emit_default_action
             );
         }

-        if ($len == 0)
-        {
-            # Empty rule
-            if (!$brace_printed)
-            {
-                add_to_buffer('rules', ' { ');
-                $brace_printed = 1;
-            }
-            add_to_buffer('rules', ' $$=EMPTY; }');
-        }
-        else
-        {
-            # Go through each field and aggregate consecutive literal tokens
-            # into a single 'mm_strdup' call.
-            my @flds_new;
-            my $str;
-            for (my $z = 0; $z < $len; $z++)
-            {
-                if (substr($flds->[$z], 0, 1) eq '$')
-                {
-                    push(@flds_new, $flds->[$z]);
-                    next;
-                }
-
-                $str = $flds->[$z];
-
-                while (1)
-                {
-                    if ($z >= $len - 1
-                        || substr($flds->[ $z + 1 ], 0, 1) eq '$')
-                    {
-                        # Can't combine any more literals; push to @flds_new.
-                        # This code would need work if any literals contain
-                        # backslash or double quote, but right now that never
-                        # happens.
-                        push(@flds_new, "mm_strdup(\"$str\")");
-                        last;
-                    }
-                    $z++;
-                    $str = $str . ' ' . $flds->[$z];
-                }
-            }
-
-            # So - how many fields did we end up with ?
-            $len = scalar(@flds_new);
-            if ($len == 1)
-            {
-                # Single field can be handled by straight assignment
-                if (!$brace_printed)
-                {
-                    add_to_buffer('rules', ' { ');
-                    $brace_printed = 1;
-                }
-                $str = ' $$ = ' . $flds_new[0] . ';';
-                add_to_buffer('rules', $str);
-            }
-            else
-            {
-                # Need to concatenate the results to form our final string
-                if (!$brace_printed)
-                {
-                    add_to_buffer('rules', ' { ');
-                    $brace_printed = 1;
-                }
-                $str =
-                  ' $$ = cat_str(' . $len . ',' . join(',', @flds_new) . ');';
-                add_to_buffer('rules', $str);
-            }
-            add_to_buffer('rules', '}') if ($brace_printed);
-        }
+        add_to_buffer('rules', '}') if ($brace_printed);
     }
     else
     {
         # We're in the "stmt:" rule, where we need to output special actions.
         # This code assumes that no ecpg.addons entry applies.
-        if ($len)
+        if ($line_count)
         {
             # Any regular kind of statement calls output_statement
             add_to_buffer('rules',
-                ' { output_statement($1, 0, ECPGst_normal); }');
+                ' { output_statement(@1, 0, ECPGst_normal); }');
         }
         else
         {
             # The empty production for stmt: do nothing
-            add_to_buffer('rules', ' { $$ = NULL; }');
         }
     }
     return;
@@ -746,8 +610,6 @@ sub emit_default_action
 # entry in %replace_line, then do nothing and return 0.
 sub emit_rule
 {
-    my ($fields) = @_;
-
     # compute tag to be used as lookup key in %replace_line and %addons
     my $tag = $non_term_id . $line;
     $tag =~ tr/ |//d;
@@ -761,7 +623,8 @@ sub emit_rule
             return 0;
         }

-        # non-ignore entries replace the line, but we'd better keep any '|'
+        # non-ignore entries replace the line, but we'd better keep any '|';
+        # we don't bother to update $line_count here.
         if (index($line, '|') != -1)
         {
             $line = '| ' . $rep;
@@ -778,7 +641,7 @@ sub emit_rule

     # Emit $line, then print the appropriate action.
     add_to_buffer('rules', $line);
-    emit_rule_action($tag, $fields);
+    emit_rule_action($tag);
     return 1;
 }

diff --git a/src/interfaces/ecpg/preproc/parser.c b/src/interfaces/ecpg/preproc/parser.c
index 9daeee3303..8807c22cb6 100644
--- a/src/interfaces/ecpg/preproc/parser.c
+++ b/src/interfaces/ecpg/preproc/parser.c
@@ -31,6 +31,7 @@ static YYSTYPE lookahead_yylval;    /* yylval for lookahead token */
 static YYLTYPE lookahead_yylloc;    /* yylloc for lookahead token */
 static char *lookahead_yytext;    /* start current token */

+static int    base_yylex_location(void);
 static bool check_uescapechar(unsigned char escape);
 static bool ecpg_isspace(char ch);

@@ -71,7 +72,7 @@ filtered_base_yylex(void)
         have_lookahead = false;
     }
     else
-        cur_token = base_yylex();
+        cur_token = base_yylex_location();

     /*
      * If this token isn't one that requires lookahead, just return it.
@@ -96,7 +97,7 @@ filtered_base_yylex(void)
     cur_yytext = base_yytext;

     /* Get next token, saving outputs into lookahead variables */
-    next_token = base_yylex();
+    next_token = base_yylex_location();

     lookahead_token = next_token;
     lookahead_yylval = base_yylval;
@@ -184,7 +185,7 @@ filtered_base_yylex(void)
                 cur_yytext = base_yytext;

                 /* Get third token */
-                next_token = base_yylex();
+                next_token = base_yylex_location();

                 if (next_token != SCONST)
                     mmerror(PARSE_ERROR, ET_ERROR, "UESCAPE must be followed by a simple string literal");
@@ -203,6 +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);

                 /* Clear have_lookahead, thereby consuming all three tokens */
                 have_lookahead = false;
@@ -218,6 +220,56 @@ filtered_base_yylex(void)
     return cur_token;
 }

+/*
+ * Call base_yylex() and fill in base_yylloc.
+ *
+ * pgc.l does not worry about setting yylloc, and given what we want for
+ * that, trying to set it there would be pretty inconvenient.  What we
+ * want is: if the returned token has type <str>, then duplicate its
+ * string value as yylloc; otherwise, make a downcased copy of yytext.
+ * The downcasing is ASCII-only because all that we care about there
+ * is producing uniformly-cased output of keywords.  (That's mostly
+ * cosmetic, but there are places in ecpglib that expect to receive
+ * downcased keywords, plus it keeps us regression-test-compatible
+ * with the old implementation of ecpg.)
+ */
+static int
+base_yylex_location(void)
+{
+    int            token = base_yylex();
+
+    switch (token)
+    {
+            /* List a token here if pgc.l assigns to base_yylval.str for it */
+        case Op:
+        case CSTRING:
+        case CPP_LINE:
+        case CVARIABLE:
+        case BCONST:
+        case SCONST:
+        case USCONST:
+        case XCONST:
+        case FCONST:
+        case IDENT:
+        case UIDENT:
+        case IP:
+            /* Duplicate the <str> value */
+            base_yylloc = mm_strdup(base_yylval.str);
+            break;
+        default:
+            /* Else just use the input, i.e., yytext */
+            base_yylloc = mm_strdup(base_yytext);
+            /* Apply an ASCII-only downcasing */
+            for (unsigned char *ptr = (unsigned char *) base_yylloc; *ptr; ptr++)
+            {
+                if (*ptr >= 'A' && *ptr <= 'Z')
+                    *ptr += 'a' - 'A';
+            }
+            break;
+    }
+    return token;
+}
+
 /*
  * check_uescapechar() and ecpg_isspace() should match their equivalents
  * in pgc.l.
diff --git a/src/interfaces/ecpg/preproc/preproc_extern.h b/src/interfaces/ecpg/preproc/preproc_extern.h
index c5fd07fbd8..da93967462 100644
--- a/src/interfaces/ecpg/preproc/preproc_extern.h
+++ b/src/interfaces/ecpg/preproc/preproc_extern.h
@@ -15,6 +15,13 @@
 #define STRUCT_DEPTH 128
 #define EMPTY mm_strdup("")

+/*
+ * "Location tracking" support --- see ecpg.header for more comments.
+ */
+typedef char *YYLTYPE;
+
+#define YYLTYPE_IS_DECLARED 1
+
 /* variables */

 extern bool autocommit,
@@ -65,10 +72,10 @@ extern const uint16 SQLScanKeywordTokens[];
 extern const char *get_dtype(enum ECPGdtype);
 extern void lex_init(void);
 extern void output_line_number(void);
-extern void output_statement(char *stmt, int whenever_mode, enum ECPG_statement_type st);
-extern void output_prepare_statement(char *name, char *stmt);
-extern void output_deallocate_prepare_statement(char *name);
-extern void output_simple_statement(char *stmt, int whenever_mode);
+extern void output_statement(const char *stmt, int whenever_mode, enum ECPG_statement_type st);
+extern void output_prepare_statement(const char *name, const char *stmt);
+extern void output_deallocate_prepare_statement(const char *name);
+extern void output_simple_statement(const char *stmt, int whenever_mode);
 extern char *hashline_number(void);
 extern int    base_yyparse(void);
 extern int    base_yylex(void);
--
2.39.3

From 5776c16640c599d16737468029444c7f8f1b21be Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 18 Apr 2024 13:16:05 -0400
Subject: [PATCH v1 5/6] Move some functions into a new file
 ecpg/preproc/util.c.

mm_alloc and mm_strdup were in type.c, which seems a completely
random choice.  No doubt the original author thought two small
functions didn't deserve their own file.  But I'm about to add
some more memory-management stuff beside them, so let's put them
in a less surprising place.  This seems like a better home for
mmerror and mmfatal, too.
---
 src/interfaces/ecpg/preproc/Makefile    |   1 +
 src/interfaces/ecpg/preproc/ecpg.header |  65 ---------------
 src/interfaces/ecpg/preproc/meson.build |   1 +
 src/interfaces/ecpg/preproc/type.c      |  24 ------
 src/interfaces/ecpg/preproc/util.c      | 105 ++++++++++++++++++++++++
 5 files changed, 107 insertions(+), 89 deletions(-)
 create mode 100644 src/interfaces/ecpg/preproc/util.c

diff --git a/src/interfaces/ecpg/preproc/Makefile b/src/interfaces/ecpg/preproc/Makefile
index 934b7cef1b..7866037cbb 100644
--- a/src/interfaces/ecpg/preproc/Makefile
+++ b/src/interfaces/ecpg/preproc/Makefile
@@ -36,6 +36,7 @@ OBJS = \
     preproc.o \
     type.o \
     typename.o \
+    util.o \
     variable.o

 # where to find gen_keywordlist.pl and subsidiary files
diff --git a/src/interfaces/ecpg/preproc/ecpg.header b/src/interfaces/ecpg/preproc/ecpg.header
index 46023a0106..48a4670191 100644
--- a/src/interfaces/ecpg/preproc/ecpg.header
+++ b/src/interfaces/ecpg/preproc/ecpg.header
@@ -55,73 +55,8 @@ struct variable no_indicator = {"no_indicator", &ecpg_no_indicator, 0, NULL};

 static struct ECPGtype ecpg_query = {ECPGt_char_variable, NULL, NULL, NULL, {NULL}, 0};

-static void vmmerror(int error_code, enum errortype type, const char *error, va_list ap) pg_attribute_printf(3, 0);
-
 static bool check_declared_list(const char *name);

-/*
- * Handle parsing errors and warnings
- */
-static void
-vmmerror(int error_code, enum errortype type, const char *error, va_list ap)
-{
-    /* localize the error message string */
-    error = _(error);
-
-    fprintf(stderr, "%s:%d: ", input_filename, base_yylineno);
-
-    switch (type)
-    {
-        case ET_WARNING:
-            fprintf(stderr, _("WARNING: "));
-            break;
-        case ET_ERROR:
-            fprintf(stderr, _("ERROR: "));
-            break;
-    }
-
-    vfprintf(stderr, error, ap);
-
-    fprintf(stderr, "\n");
-
-    switch (type)
-    {
-        case ET_WARNING:
-            break;
-        case ET_ERROR:
-            ret_value = error_code;
-            break;
-    }
-}
-
-void
-mmerror(int error_code, enum errortype type, const char *error,...)
-{
-    va_list        ap;
-
-    va_start(ap, error);
-    vmmerror(error_code, type, error, ap);
-    va_end(ap);
-}
-
-void
-mmfatal(int error_code, const char *error,...)
-{
-    va_list        ap;
-
-    va_start(ap, error);
-    vmmerror(error_code, ET_ERROR, error, ap);
-    va_end(ap);
-
-    if (base_yyin)
-        fclose(base_yyin);
-    if (base_yyout)
-        fclose(base_yyout);
-
-    if (strcmp(output_filename, "-") != 0 && unlink(output_filename) != 0)
-        fprintf(stderr, _("could not remove output file \"%s\"\n"), output_filename);
-    exit(error_code);
-}

 /*
  * string concatenation
diff --git a/src/interfaces/ecpg/preproc/meson.build b/src/interfaces/ecpg/preproc/meson.build
index ddd7a66547..f680e5d59e 100644
--- a/src/interfaces/ecpg/preproc/meson.build
+++ b/src/interfaces/ecpg/preproc/meson.build
@@ -10,6 +10,7 @@ ecpg_sources = files(
   'output.c',
   'parser.c',
   'type.c',
+  'util.c',
   'variable.c',
 )

diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c
index a842bb6a1f..5610a8dc76 100644
--- a/src/interfaces/ecpg/preproc/type.c
+++ b/src/interfaces/ecpg/preproc/type.c
@@ -8,30 +8,6 @@

 static struct ECPGstruct_member struct_no_indicator = {"no_indicator", &ecpg_no_indicator, NULL};

-/* malloc + error check */
-void *
-mm_alloc(size_t size)
-{
-    void       *ptr = malloc(size);
-
-    if (ptr == NULL)
-        mmfatal(OUT_OF_MEMORY, "out of memory");
-
-    return ptr;
-}
-
-/* strdup + error check */
-char *
-mm_strdup(const char *string)
-{
-    char       *new = strdup(string);
-
-    if (new == NULL)
-        mmfatal(OUT_OF_MEMORY, "out of memory");
-
-    return new;
-}
-
 /* duplicate memberlist */
 struct ECPGstruct_member *
 ECPGstruct_member_dup(struct ECPGstruct_member *rm)
diff --git a/src/interfaces/ecpg/preproc/util.c b/src/interfaces/ecpg/preproc/util.c
new file mode 100644
index 0000000000..b80802ca9f
--- /dev/null
+++ b/src/interfaces/ecpg/preproc/util.c
@@ -0,0 +1,105 @@
+/* src/interfaces/ecpg/preproc/util.c */
+
+#include "postgres_fe.h"
+
+#include <unistd.h>
+
+#include "preproc_extern.h"
+
+static void vmmerror(int error_code, enum errortype type, const char *error, va_list ap) pg_attribute_printf(3, 0);
+
+
+/*
+ * Handle preprocessor errors and warnings
+ */
+static void
+vmmerror(int error_code, enum errortype type, const char *error, va_list ap)
+{
+    /* localize the error message string */
+    error = _(error);
+
+    fprintf(stderr, "%s:%d: ", input_filename, base_yylineno);
+
+    switch (type)
+    {
+        case ET_WARNING:
+            fprintf(stderr, _("WARNING: "));
+            break;
+        case ET_ERROR:
+            fprintf(stderr, _("ERROR: "));
+            break;
+    }
+
+    vfprintf(stderr, error, ap);
+
+    fprintf(stderr, "\n");
+
+    /* If appropriate, set error code to be inspected by ecpg.c */
+    switch (type)
+    {
+        case ET_WARNING:
+            break;
+        case ET_ERROR:
+            ret_value = error_code;
+            break;
+    }
+}
+
+/* Report an error or warning */
+void
+mmerror(int error_code, enum errortype type, const char *error,...)
+{
+    va_list        ap;
+
+    va_start(ap, error);
+    vmmerror(error_code, type, error, ap);
+    va_end(ap);
+}
+
+/* Report an error and abandon execution */
+void
+mmfatal(int error_code, const char *error,...)
+{
+    va_list        ap;
+
+    va_start(ap, error);
+    vmmerror(error_code, ET_ERROR, error, ap);
+    va_end(ap);
+
+    if (base_yyin)
+        fclose(base_yyin);
+    if (base_yyout)
+        fclose(base_yyout);
+
+    if (strcmp(output_filename, "-") != 0 && unlink(output_filename) != 0)
+        fprintf(stderr, _("could not remove output file \"%s\"\n"), output_filename);
+    exit(error_code);
+}
+
+/*
+ * Basic memory management support
+ */
+
+/* malloc + error check */
+void *
+mm_alloc(size_t size)
+{
+    void       *ptr = malloc(size);
+
+    if (ptr == NULL)
+        mmfatal(OUT_OF_MEMORY, "out of memory");
+
+    return ptr;
+}
+
+/* strdup + error check */
+char *
+mm_strdup(const char *string)
+{
+    char       *new = strdup(string);
+
+    if (new == NULL)
+        mmfatal(OUT_OF_MEMORY, "out of memory");
+
+    return new;
+}
--
2.39.3

From 719b679f6ead9e6636d473f47e904f5f4a4e5517 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 18 Apr 2024 19:16:06 -0400
Subject: [PATCH v1 6/6] 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 48a4670191..395b68331e 100644
--- a/src/interfaces/ecpg/preproc/ecpg.header
+++ b/src/interfaces/ecpg/preproc/ecpg.header
@@ -34,8 +34,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;
@@ -59,23 +57,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,...)
 {
@@ -97,28 +96,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;
 }

@@ -154,7 +148,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++)
         {
@@ -174,22 +168,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
@@ -217,12 +208,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;
 }
@@ -242,8 +234,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--)
@@ -265,7 +256,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)
@@ -286,12 +277,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);
@@ -303,10 +294,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),
@@ -318,11 +309,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,
@@ -335,11 +326,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,
@@ -348,10 +339,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),
@@ -362,10 +353,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),
@@ -379,10 +370,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");
             }

             /*
@@ -397,17 +389,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,
@@ -420,11 +412,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,
@@ -433,9 +425,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),
@@ -446,10 +439,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),
@@ -457,10 +450,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),
@@ -472,10 +465,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);
@@ -496,7 +490,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;
@@ -534,8 +528,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,
@@ -555,19 +551,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 d551ada325..cc045e548f 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3538,6 +3538,7 @@ libpq_source
 line_t
 lineno_t
 list_sort_comparator
+loc_chunk
 local_relopt
 local_relopts
 local_source
--
2.39.3


В списке pgsql-hackers по дате отправления:

Предыдущее
От: Michael Paquier
Дата:
Сообщение: Re: ALTER TABLE SET ACCESS METHOD on partitioned tables
Следующее
От: "Hayato Kuroda (Fujitsu)"
Дата:
Сообщение: RE: Disallow changing slot's failover option in transaction block