Align ECPG lexer more closely with the core and psql lexers.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 13 Nov 2018 17:57:52 +0000 (12:57 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 13 Nov 2018 17:57:52 +0000 (12:57 -0500)
Make a bunch of basically-cosmetic changes to reduce the diffs between
the flex rules in scan.l, psqlscan.l, and pgc.l.  Reorder some code,
adjust a lot of whitespace, sync some comments, make use of flex start
condition scopes to do that.

There are a few non-cosmetic changes in the ECPG lexer:

* Bring over the decimalfail rule (and support function
process_integer_literal) so that ECPG will lex "1..10" into
the same tokens as the backend would.  I'm not sure this makes any
visible difference to users, but I'm not sure it doesn't, either.

* <xdc><<EOF>> gets its own rule so as to produce a more on-point
error message.

* Remove duplicate <SQL>{xdstart} rule.

John Naylor, with a few additional changes by me

Discussion: https://postgr.es/m/CAJVSVGWGqY9YBs2EwtRUkbNv=hXkN8yRPOoD1wxE6COgvvrz5g@mail.gmail.com

src/backend/parser/scan.l
src/fe_utils/psqlscan.l
src/interfaces/ecpg/preproc/pgc.l

index 950b8b85918c131fbe39efc1a31a62110a76e119..6c6a6e320f08da29c6fc76d030258ce5bcb1ec77 100644 (file)
@@ -6,7 +6,8 @@
  *
  * NOTE NOTE NOTE:
  *
- * The rules in this file must be kept in sync with src/fe_utils/psqlscan.l!
+ * The rules in this file must be kept in sync with src/fe_utils/psqlscan.l
+ * and src/interfaces/ecpg/preproc/pgc.l!
  *
  * The rules are designed so that the scanner never has to backtrack,
  * in the sense that there is always a rule that can match the input
@@ -168,8 +169,8 @@ extern void core_yyset_column(int column_no, yyscan_t yyscanner);
 %x xc
 %x xd
 %x xh
-%x xe
 %x xq
+%x xe
 %x xdolq
 %x xui
 %x xuiend
@@ -192,7 +193,7 @@ extern void core_yyset_column(int column_no, yyscan_t yyscanner);
  * XXX perhaps \f (formfeed) should be treated as a newline as well?
  *
  * XXX if you change the set of whitespace characters, fix scanner_isspace()
- * to agree, and see also the plpgsql lexer.
+ * to agree.
  */
 
 space          [ \t\n\r\f]
@@ -417,32 +418,36 @@ other         .
                    yyless(2);
                }
 
-<xc>{xcstart}  {
+<xc>{
+{xcstart}      {
                    (yyextra->xcdepth)++;
                    /* Put back any characters past slash-star; see above */
                    yyless(2);
                }
 
-<xc>{xcstop}   {
+{xcstop}       {
                    if (yyextra->xcdepth <= 0)
                        BEGIN(INITIAL);
                    else
                        (yyextra->xcdepth)--;
                }
 
-<xc>{xcinside} {
+{xcinside}     {
                    /* ignore */
                }
 
-<xc>{op_chars} {
+{op_chars}     {
                    /* ignore */
                }
 
-<xc>\*+            {
+\*+                {
                    /* ignore */
                }
 
-<xc><<EOF>>        { yyerror("unterminated /* comment"); }
+<<EOF>>            {
+                   yyerror("unterminated /* comment");
+               }
+} /* <xc> */
 
 {xbstart}      {
                    /* Binary bit type.
index fdf49875a7296a7dec2293ab3f3538303e174199..ae5418e7da09983102c51ea0985b0f07978f9d8b 100644 (file)
@@ -23,6 +23,7 @@
  *
  * See psqlscan_int.h for additional commentary.
  *
+ *
  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
@@ -39,6 +40,9 @@
 }
 
 %{
+
+/* LCOV_EXCL_START */
+
 #include "fe_utils/psqlscan_int.h"
 
 /*
@@ -71,8 +75,6 @@ typedef int YYSTYPE;
 extern int psql_yyget_column(yyscan_t yyscanner);
 extern void psql_yyset_column(int column_no, yyscan_t yyscanner);
 
-/* LCOV_EXCL_START */
-
 %}
 
 %option reentrant
@@ -128,8 +130,8 @@ extern void psql_yyset_column(int column_no, yyscan_t yyscanner);
 %x xc
 %x xd
 %x xh
-%x xe
 %x xq
+%x xe
 %x xdolq
 %x xui
 %x xuiend
@@ -151,7 +153,7 @@ extern void psql_yyset_column(int column_no, yyscan_t yyscanner);
  * XXX perhaps \f (formfeed) should be treated as a newline as well?
  *
  * XXX if you change the set of whitespace characters, fix scanner_isspace()
- * to agree, and see also the plpgsql lexer.
+ * to agree.
  */
 
 space          [ \t\n\r\f]
@@ -402,14 +404,15 @@ other         .
                    ECHO;
                }
 
-<xc>{xcstart}  {
+<xc>{
+{xcstart}      {
                    cur_state->xcdepth++;
                    /* Put back any characters past slash-star; see above */
                    yyless(2);
                    ECHO;
                }
 
-<xc>{xcstop}   {
+{xcstop}       {
                    if (cur_state->xcdepth <= 0)
                        BEGIN(INITIAL);
                    else
@@ -417,17 +420,18 @@ other         .
                    ECHO;
                }
 
-<xc>{xcinside} {
+{xcinside}     {
                    ECHO;
                }
 
-<xc>{op_chars} {
+{op_chars}     {
                    ECHO;
                }
 
-<xc>\*+            {
+\*+                {
                    ECHO;
                }
+} /* <xc> */
 
 {xbstart}      {
                    BEGIN(xb);
index 0792118cfe3f8921de60456cec302463bb0ec19e..91ee44f091e4480f87e05d50e606741a33d957d5 100644 (file)
@@ -10,7 +10,6 @@
  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *
  * IDENTIFICATION
  *   src/interfaces/ecpg/preproc/pgc.l
  *
@@ -28,6 +27,9 @@
 }
 
 %{
+
+/* LCOV_EXCL_START */
+
 extern YYSTYPE base_yylval;
 
 static int     xcdepth = 0;    /* depth of nesting in slash-star comments */
@@ -53,8 +55,9 @@ static bool       include_next;
 
 #define startlit() (literalbuf[0] = '\0', literallen = 0)
 static void addlit(char *ytext, int yleng);
-static void addlitchar (unsigned char);
-static void parse_include (void);
+static void addlitchar(unsigned char);
+static int process_integer_literal(const char *token, YYSTYPE *lval);
+static void parse_include(void);
 static bool ecpg_isspace(char ch);
 static bool isdefine(void);
 static bool isinformixdefine(void);
@@ -81,8 +84,6 @@ static struct _if_value
    short else_branch;
 } stacked_if_value[MAX_NESTED_IF];
 
-/* LCOV_EXCL_START */
-
 %}
 
 %option 8bit
@@ -91,11 +92,8 @@ static struct _if_value
 %option noinput
 %option noyywrap
 %option warn
-%option prefix="base_yy"
-
 %option yylineno
-
-%x C SQL incl def def_ident undef
+%option prefix="base_yy"
 
 /*
  * OK, here is a short description of lex/flex rules behavior.
@@ -108,18 +106,24 @@ static struct _if_value
  * We use exclusive states for quoted strings, extended comments,
  * and to eliminate parsing troubles for numeric strings.
  * Exclusive states:
- * <xb> bit string literal
- * <xcc> extended C-style comments in C
- * <xcsql> extended C-style comments in SQL
- * <xd> delimited identifiers (double-quoted identifiers) - thomas 1997-10-27
- * <xh> hexadecimal numeric string - thomas 1997-11-16
- * <xq> standard quoted strings - thomas 1997-07-30
- * <xqc> standard quoted strings in C - michael
- * <xe> extended quoted strings (support backslash escape sequences)
- * <xn> national character quoted strings
+ *  <xb> bit string literal
+ *  <xcc> extended C-style comments in C
+ *  <xcsql> extended C-style comments in SQL
+ *  <xd> delimited identifiers (double-quoted identifiers)
+ *  <xdc> double-quoted strings in C
+ *  <xh> hexadecimal numeric string
+ *  <xn> national character quoted strings
+ *  <xq> standard quoted strings
+ *  <xe> extended quoted strings (support backslash escape sequences)
+ *  <xqc> single-quoted strings in C
  *  <xdolq> $foo$ quoted strings
  *  <xui> quoted identifier with Unicode escapes
  *  <xus> quoted string with Unicode escapes
+ *  <xcond> condition of an EXEC SQL IFDEF construct
+ *  <xskip> skipping the inactive part of an EXEC SQL IFDEF construct
+ *
+ * Remember to add an <<EOF>> case whenever you add a new exclusive state!
+ * The default one is probably not the right thing.
  */
 
 %x xb
@@ -128,15 +132,60 @@ static struct _if_value
 %x xd
 %x xdc
 %x xh
-%x xe
 %x xn
 %x xq
+%x xe
 %x xqc
 %x xdolq
-%x xcond
-%x xskip
 %x xui
 %x xus
+%x xcond
+%x xskip
+
+/* Additional exclusive states that are specific to ECPG */
+%x C SQL incl def def_ident undef
+
+/*
+ * In order to make the world safe for Windows and Mac clients as well as
+ * Unix ones, we accept either \n or \r as a newline.  A DOS-style \r\n
+ * sequence will be seen as two successive newlines, but that doesn't cause
+ * any problems.  SQL-style comments, which start with -- and extend to the
+ * next newline, are treated as equivalent to a single whitespace character.
+ *
+ * NOTE a fine point: if there is no newline following --, we will absorb
+ * everything to the end of the input as a comment.  This is correct.  Older
+ * versions of Postgres failed to recognize -- as a comment if the input
+ * did not end with a newline.
+ *
+ * XXX perhaps \f (formfeed) should be treated as a newline as well?
+ *
+ * XXX if you change the set of whitespace characters, fix ecpg_isspace()
+ * to agree.
+ */
+
+space          [ \t\n\r\f]
+horiz_space        [ \t\f]
+newline            [\n\r]
+non_newline        [^\n\r]
+
+comment            ("--"{non_newline}*)
+
+whitespace     ({space}+|{comment})
+
+/*
+ * SQL requires at least one newline in the whitespace separating
+ * string literals that are to be concatenated.  Silly, but who are we
+ * to argue?  Note that {whitespace_with_newline} should not have * after
+ * it, whereas {whitespace} should generally have a * after it...
+ */
+
+horiz_whitespace       ({horiz_space}|{comment})
+whitespace_with_newline    ({horiz_whitespace}*{newline}{whitespace}*)
+
+quote          '
+quotestop      {quote}{whitespace}*
+quotecontinue  {quote}{whitespace_with_newline}{quote}
+quotefail      {quote}{whitespace}*"-"
 
 /* Bit string
  */
@@ -158,9 +207,6 @@ xeoctesc        [\\][0-7]{1,3}
 xehexesc       [\\]x[0-9A-Fa-f]{1,2}
 xeunicode      [\\](u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})
 
-/* C version of hex number */
-xch                0[xX][0-9A-Fa-f]*
-
 /* Extended quote
  * xqdouble implements embedded quote, ''''
  */
@@ -194,7 +240,9 @@ xddouble        {dquote}{dquote}
 xdinside       [^"]+
 
 /* Unicode escapes */
-/* (The ecpg scanner is not backup-free, so the fail rules in scan.l are not needed here, but could be added if desired.) */
+/* (The ecpg scanner is not backup-free, so the fail rules in scan.l are
+ * not needed here, but could be added if desired.)
+ */
 uescape            [uU][eE][sS][cC][aA][pP][eE]{whitespace}*{quote}[^']{quote}
 
 /* Quoted identifier with Unicode escapes */
@@ -211,22 +259,23 @@ xdcqdq            \\\"
 xdcother       [^"]
 xdcinside      ({xdcqq}|{xdcqdq}|{xdcother})
 
+
 /* C-style comments
  *
  * The "extended comment" syntax closely resembles allowable operator syntax.
  * The tricky part here is to get lex to recognize a string starting with
  * slash-star as a comment, when interpreting it as an operator would produce
- * a longer match --- remember lex will prefer a longer match! Also, if we
+ * a longer match --- remember lex will prefer a longer match!  Also, if we
  * have something like plus-slash-star, lex will think this is a 3-character
  * operator whereas we want to see it as a + operator and a comment start.
  * The solution is two-fold:
  * 1. append {op_chars}* to xcstart so that it matches as much text as
- *   {operator} would. Then the tie-breaker (first matching rule of same
- *   length) ensures xcstart wins.  We put back the extra stuff with yyless()
- *   in case it contains a star-slash that should terminate the comment.
+ *    {operator} would. Then the tie-breaker (first matching rule of same
+ *    length) ensures xcstart wins.  We put back the extra stuff with yyless()
+ *    in case it contains a star-slash that should terminate the comment.
  * 2. In the operator rule, check for slash-star within the operator, and
- *   if found throw it back with yyless().  This handles the plus-slash-star
- *   problem.
+ *    if found throw it back with yyless().  This handles the plus-slash-star
+ *    problem.
  * Dash-dash comments have similar interactions with the operator rule.
  */
 xcstart            \/\*{op_chars}*
@@ -262,7 +311,7 @@ not_equals      "!="
 
 /*
  * "self" is the set of chars that should be returned as single-character
- * tokens. "op_chars" is the set of chars that can make up "Op" tokens,
+ * tokens.  "op_chars" is the set of chars that can make up "Op" tokens,
  * which can be one or more characters long (but if a single-char token
  * appears in the "self" set, it is not to be returned as an Op).  Note
  * that the sets overlap, but each has some chars that are not in the other.
@@ -278,68 +327,40 @@ operator      {op_chars}+
  * instead we pass it separately to parser. there it gets
  * coerced via doNegate() -- Leon aug 20 1999
  *
+ * {decimalfail} is used because we would like "1..10" to lex as 1, dot_dot, 10.
+ *
  * {realfail1} and {realfail2} are added to prevent the need for scanner
  * backup when the {real} rule fails to match completely.
  */
 
 integer            {digit}+
 decimal            (({digit}*\.{digit}+)|({digit}+\.{digit}*))
+decimalfail        {digit}+\.\.
 real           ({integer}|{decimal})[Ee][-+]?{digit}+
 realfail1      ({integer}|{decimal})[Ee]
 realfail2      ({integer}|{decimal})[Ee][-+]
 
 param          \${integer}
 
-/*
- * In order to make the world safe for Windows and Mac clients as well as
- * Unix ones, we accept either \n or \r as a newline.  A DOS-style \r\n
- * sequence will be seen as two successive newlines, but that doesn't cause
- * any problems.  SQL-style comments, which start with -- and extend to the
- * next newline, are treated as equivalent to a single whitespace character.
- *
- * NOTE a fine point: if there is no newline following --, we will absorb
- * everything to the end of the input as a comment.  This is correct.  Older
- * versions of Postgres failed to recognize -- as a comment if the input
- * did not end with a newline.
- *
- * XXX perhaps \f (formfeed) should be treated as a newline as well?
- *
- * XXX if you change the set of whitespace characters, fix ecpg_isspace()
- * to agree.
- */
-
-ccomment       "//".*\n
-
-space          [ \t\n\r\f]
-horiz_space        [ \t\f]
-newline            [\n\r]
-non_newline        [^\n\r]
-
-comment            ("--"{non_newline}*)
-
-whitespace     ({space}+|{comment})
-
-/*
- * SQL requires at least one newline in the whitespace separating
- * string literals that are to be concatenated.  Silly, but who are we
- * to argue?  Note that {whitespace_with_newline} should not have * after
- * it, whereas {whitespace} should generally have a * after it...
- */
-
-horiz_whitespace   ({horiz_space}|{comment})
-whitespace_with_newline ({horiz_whitespace}*{newline}{whitespace}*)
-
-quote          '
-quotestop      {quote}{whitespace}*
-quotecontinue  {quote}{whitespace_with_newline}{quote}
-quotefail      {quote}{whitespace}*"-"
-
 /* special characters for other dbms */
 /* we have to react differently in compat mode */
 informix_special   [\$]
 
 other          .
 
+/*
+ * Dollar quoted strings are totally opaque, and no escaping is done on them.
+ * Other quoted strings must allow some special characters such as single-quote
+ *  and newline.
+ * Embedded single-quotes are implemented both in the SQL standard
+ *  style of two adjacent single quotes "''" and in the Postgres/Java style
+ *  of escaped-quote "\'".
+ * Other embedded escaped characters are matched explicitly and the leading
+ *  backslash is dropped from the string.
+ * Note that xcstart must appear before operator, as explained above!
+ *  Also whitespace (comment) must appear before operator.
+ */
+
 /* some stuff needed for ecpg */
 exec           [eE][xX][eE][cC]
 sql                [sS][qQ][lL]
@@ -349,6 +370,11 @@ include_next   [iI][nN][cC][lL][uU][dD][eE]_[nN][eE][xX][tT]
 import         [iI][mM][pP][oO][rR][tT]
 undef          [uU][nN][dD][eE][fF]
 
+/* C version of hex number */
+xch                0[xX][0-9A-Fa-f]*
+
+ccomment       "//".*\n
+
 if             [iI][fF]
 ifdef          [iI][fF][dD][eE][fF]
 ifndef         [iI][fF][nN][dD][eE][fF]
@@ -366,24 +392,12 @@ ip                {ipdigit}\.{ipdigit}\.{ipdigit}\.{ipdigit}
 cppinclude     {space}*#{include}{space}*
 cppinclude_next        {space}*#{include_next}{space}*
 
-/* take care of cpp lines, they may also be continuated */
+/* take care of cpp lines, they may also be continued */
 /* first a general line for all commands not starting with "i" */
 /* and then the other commands starting with "i", we have to add these
- * separately because the cppline production would match on "include" too */
-cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+\/)|.|\\{space}*{newline})*{newline}
-
-/*
- * Dollar quoted strings are totally opaque, and no escaping is done on them.
- * Other quoted strings must allow some special characters such as single-quote
- * and newline.
- * Embedded single-quotes are implemented both in the SQL standard
- * style of two adjacent single quotes "''" and in the Postgres/Java style
- * of escaped-quote "\'".
- * Other embedded escaped characters are matched explicitly and the leading
- * backslash is dropped from the string. - thomas 1997-09-24
- * Note that xcstart must appear before operator, as explained above!
- * Also whitespace (comment) must appear before operator.
+ * separately because the cppline production would match on "include" too
  */
+cppline            {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+\/)|.|\\{space}*{newline})*{newline}
 
 %%
 
@@ -392,22 +406,27 @@ cppline           {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
        token_start = NULL;
 %}
 
-<SQL>{whitespace}  { /* ignore */ }
+<SQL>{
+{whitespace}   {
+                   /* ignore */
+               }
 
-<C>{xcstart}       {
+{xcstart}      {
                    token_start = yytext;
                    state_before = YYSTATE;
                    xcdepth = 0;
-                   BEGIN(xcc);
+                   BEGIN(xcsql);
                    /* Put back any characters past slash-star; see above */
                    yyless(2);
                    fputs("/*", yyout);
                }
-<SQL>{xcstart}     {
+} /* <SQL> */
+
+<C>{xcstart}   {
                    token_start = yytext;
                    state_before = YYSTATE;
                    xcdepth = 0;
-                   BEGIN(xcsql);
+                   BEGIN(xcc);
                    /* Put back any characters past slash-star; see above */
                    yyless(2);
                    fputs("/*", yyout);
@@ -437,20 +456,36 @@ cppline           {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                    BEGIN(state_before);
                    token_start = NULL;
                }
-<xcc,xcsql>{xcinside}  { ECHO; }
-<xcc,xcsql>{op_chars}  { ECHO; }
-<xcc,xcsql>\*+     { ECHO; }
 
-<xcc,xcsql><<EOF>>     { mmfatal(PARSE_ERROR, "unterminated /* comment"); }
+<xcc,xcsql>{
+{xcinside}     {
+                   ECHO;
+               }
+
+{op_chars}     {
+                   ECHO;
+               }
+
+\*+                {
+                   ECHO;
+               }
+
+<<EOF>>            {
+                   mmfatal(PARSE_ERROR, "unterminated /* comment");
+               }
+} /* <xcc,xcsql> */
 
-<SQL>{xbstart} {
+<SQL>{
+{xbstart}      {
                    token_start = yytext;
                    BEGIN(xb);
                    startlit();
                    addlitchar('b');
                }
-<xb>{quotestop} |
-<xb>{quotefail}    {
+} /* <SQL> */
+
+<xb>{quotestop}    |
+<xb>{quotefail} {
                    yyless(1);
                    BEGIN(SQL);
                    if (literalbuf[strspn(literalbuf, "01") + 1] != '\0')
@@ -458,11 +493,14 @@ cppline           {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                    base_yylval.str = mm_strdup(literalbuf);
                    return BCONST;
                }
-
 <xh>{xhinside} |
-<xb>{xbinside} { addlit(yytext, yyleng); }
+<xb>{xbinside} {
+                   addlit(yytext, yyleng);
+               }
 <xh>{quotecontinue}    |
-<xb>{quotecontinue}    { /* ignore */ }
+<xb>{quotecontinue}    {
+                   /* ignore */
+               }
 <xb><<EOF>>        { mmfatal(PARSE_ERROR, "unterminated bit string literal"); }
 
 <SQL>{xhstart} {
@@ -472,186 +510,251 @@ cppline         {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                    addlitchar('x');
                }
 <xh>{quotestop}    |
-<xh>{quotefail}    {
-               yyless(1);
-               BEGIN(SQL);
-               base_yylval.str = mm_strdup(literalbuf);
-               return XCONST;
-           }
+<xh>{quotefail} {
+                   yyless(1);
+                   BEGIN(SQL);
+                   base_yylval.str = mm_strdup(literalbuf);
+                   return XCONST;
+               }
 
 <xh><<EOF>>        { mmfatal(PARSE_ERROR, "unterminated hexadecimal string literal"); }
-<SQL>{xnstart} {
-               /* National character.
-                * Transfer it as-is to the backend.
-                */
-               token_start = yytext;
-               state_before = YYSTATE;
-               BEGIN(xn);
-               startlit();
-           }
+
 <C>{xqstart}   {
-               token_start = yytext;
-               state_before = YYSTATE;
-               BEGIN(xqc);
-               startlit();
-           }
-<SQL>{xqstart} {
-               token_start = yytext;
-               state_before = YYSTATE;
-               BEGIN(xq);
-               startlit();
-           }
-<SQL>{xestart} {
-               token_start = yytext;
-               state_before = YYSTATE;
-               BEGIN(xe);
-               startlit();
-           }
-<SQL>{xusstart}    {
-               token_start = yytext;
-               state_before = YYSTATE;
-               BEGIN(xus);
-               startlit();
-               addlit(yytext, yyleng);
-           }
+                   token_start = yytext;
+                   state_before = YYSTATE;
+                   BEGIN(xqc);
+                   startlit();
+               }
+
+<SQL>{
+{xnstart}      {
+                   /* National character.
+                    * Transfer it as-is to the backend.
+                    */
+                   token_start = yytext;
+                   state_before = YYSTATE;
+                   BEGIN(xn);
+                   startlit();
+               }
+
+{xqstart}      {
+                   token_start = yytext;
+                   state_before = YYSTATE;
+                   BEGIN(xq);
+                   startlit();
+               }
+{xestart}      {
+                   token_start = yytext;
+                   state_before = YYSTATE;
+                   BEGIN(xe);
+                   startlit();
+               }
+{xusstart}     {
+                   token_start = yytext;
+                   state_before = YYSTATE;
+                   BEGIN(xus);
+                   startlit();
+                   addlit(yytext, yyleng);
+               }
+} /* <SQL> */
+
 <xq,xqc>{quotestop} |
 <xq,xqc>{quotefail} {
-               yyless(1);
-               BEGIN(state_before);
-               base_yylval.str = mm_strdup(literalbuf);
-               return SCONST;
-           }
+                   yyless(1);
+                   BEGIN(state_before);
+                   base_yylval.str = mm_strdup(literalbuf);
+                   return SCONST;
+               }
 <xe>{quotestop} |
 <xe>{quotefail} {
-               yyless(1);
-               BEGIN(state_before);
-               base_yylval.str = mm_strdup(literalbuf);
-               return ECONST;
-           }
+                   yyless(1);
+                   BEGIN(state_before);
+                   base_yylval.str = mm_strdup(literalbuf);
+                   return ECONST;
+               }
 <xn>{quotestop} |
 <xn>{quotefail} {
-               yyless(1);
-               BEGIN(state_before);
-               base_yylval.str = mm_strdup(literalbuf);
-               return NCONST;
-           }
+                   yyless(1);
+                   BEGIN(state_before);
+                   base_yylval.str = mm_strdup(literalbuf);
+                   return NCONST;
+               }
 <xus>{xusstop} {
-               addlit(yytext, yyleng);
-               BEGIN(state_before);
-               base_yylval.str = mm_strdup(literalbuf);
-               return UCONST;
-           }
+                   addlit(yytext, yyleng);
+                   BEGIN(state_before);
+                   base_yylval.str = mm_strdup(literalbuf);
+                   return UCONST;
+               }
 <xq,xe,xn,xus>{xqdouble}   { addlitchar('\''); }
-<xqc>{xqcquote}        {
-               addlitchar('\\');
-               addlitchar('\'');
-           }
+<xqc>{xqcquote}    {
+                   addlitchar('\\');
+                   addlitchar('\'');
+               }
 <xq,xqc,xn,xus>{xqinside}  { addlit(yytext, yyleng); }
-<xe>{xeinside}     { addlit(yytext, yyleng); }
-<xe>{xeunicode}        { addlit(yytext, yyleng); }
-<xe>{xeescape}     { addlit(yytext, yyleng); }
-<xe>{xeoctesc}     { addlit(yytext, yyleng); }
-<xe>{xehexesc}     { addlit(yytext, yyleng); }
-<xq,xqc,xe,xn,xus>{quotecontinue}  { /* ignore */ }
-<xe>.      {
-              /* This is only needed for \ just before EOF */
-              addlitchar(yytext[0]);
-           }
+<xe>{xeinside}  {
+                   addlit(yytext, yyleng);
+               }
+<xe>{xeunicode} {
+                   addlit(yytext, yyleng);
+               }
+<xe>{xeescape}  {
+                   addlit(yytext, yyleng);
+               }
+<xe>{xeoctesc}  {
+                   addlit(yytext, yyleng);
+               }
+<xe>{xehexesc}  {
+                   addlit(yytext, yyleng);
+               }
+<xq,xqc,xe,xn,xus>{quotecontinue}  {
+                   /* ignore */
+               }
+<xe>.          {
+                   /* This is only needed for \ just before EOF */
+                   addlitchar(yytext[0]);
+               }
 <xq,xqc,xe,xn,xus><<EOF>>  { mmfatal(PARSE_ERROR, "unterminated quoted string"); }
-<SQL>{dolqfailed}  {
-               /* throw back all but the initial "$" */
-               yyless(1);
-               /* and treat it as {other} */
-               return yytext[0];
-           }
-<SQL>{dolqdelim} {
-               token_start = yytext;
-               if (dolqstart)
-                   free(dolqstart);
-               dolqstart = mm_strdup(yytext);
-               BEGIN(xdolq);
-               startlit();
-               addlit(yytext, yyleng);
-           }
-<xdolq>{dolqdelim} {
-               if (strcmp(yytext, dolqstart) == 0)
-               {
+
+<SQL>{
+{dolqdelim}        {
+                   token_start = yytext;
+                   if (dolqstart)
+                       free(dolqstart);
+                   dolqstart = mm_strdup(yytext);
+                   BEGIN(xdolq);
+                   startlit();
                    addlit(yytext, yyleng);
-                   free(dolqstart);
-                   dolqstart = NULL;
-                   BEGIN(SQL);
-                   base_yylval.str = mm_strdup(literalbuf);
-                   return DOLCONST;
                }
-               else
-               {
-                   /*
-                    * When we fail to match $...$ to dolqstart, transfer
-                    * the $... part to the output, but put back the final
-                    * $ for rescanning.  Consider $delim$...$junk$delim$
-                    */
-                   addlit(yytext, yyleng-1);
-                   yyless(yyleng-1);
+{dolqfailed}   {
+                   /* throw back all but the initial "$" */
+                   yyless(1);
+                   /* and treat it as {other} */
+                   return yytext[0];
                }
-           }
-<xdolq>{dolqinside}    { addlit(yytext, yyleng); }
-<xdolq>{dolqfailed}    { addlit(yytext, yyleng); }
-<xdolq>{other}     {
-               /* single quote or dollar sign */
-               addlitchar(yytext[0]);
-           }
-<xdolq><<EOF>>     { base_yyerror("unterminated dollar-quoted string"); }
-<SQL>{xdstart}     {
-                       state_before = YYSTATE;
-                       BEGIN(xd);
-                       startlit();
-                   }
-<SQL>{xuistart}        {
-                       state_before = YYSTATE;
-                       BEGIN(xui);
-                       startlit();
-                       addlit(yytext, yyleng);
-                   }
-<xd>{xdstop}       {
-                       BEGIN(state_before);
-                       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. */
-                       base_yylval.str = mm_strdup(literalbuf);
-                       return CSTRING;
-                   }
-<xdc>{xdstop}      {
-                       BEGIN(state_before);
-                       base_yylval.str = mm_strdup(literalbuf);
-                       return CSTRING;
-                   }
-<xui>{xuistop}     {
-                       BEGIN(state_before);
-                       if (literallen == 2) /* "U&" */
-                           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. */
+} /* <SQL> */
+
+<xdolq>{dolqdelim} {
+                   if (strcmp(yytext, dolqstart) == 0)
+                   {
                        addlit(yytext, yyleng);
+                       free(dolqstart);
+                       dolqstart = NULL;
+                       BEGIN(SQL);
                        base_yylval.str = mm_strdup(literalbuf);
-                       return UIDENT;
+                       return DOLCONST;
                    }
-<xd,xui>{xddouble}     { addlitchar('"'); }
-<xd,xui>{xdinside}     { addlit(yytext, yyleng); }
-<xd,xdc,xui><<EOF>>        { mmfatal(PARSE_ERROR, "unterminated quoted identifier"); }
-<C,SQL>{xdstart}   {
-                       state_before = YYSTATE;
-                       BEGIN(xdc);
-                       startlit();
+                   else
+                   {
+                       /*
+                        * When we fail to match $...$ to dolqstart, transfer
+                        * the $... part to the output, but put back the final
+                        * $ for rescanning.  Consider $delim$...$junk$delim$
+                        */
+                       addlit(yytext, yyleng - 1);
+                       yyless(yyleng - 1);
                    }
-<xdc>{xdcinside}   { addlit(yytext, yyleng); }
-<SQL>{typecast}        { return TYPECAST; }
-<SQL>{dot_dot}     { return DOT_DOT; }
-<SQL>{colon_equals}    { return COLON_EQUALS; }
-<SQL>{equals_greater} { return EQUALS_GREATER; }
-<SQL>{less_equals} { return LESS_EQUALS; }
-<SQL>{greater_equals} { return GREATER_EQUALS; }
-<SQL>{less_greater}    { return NOT_EQUALS; }
-<SQL>{not_equals}  { return NOT_EQUALS; }
-<SQL>{informix_special}    {
+               }
+<xdolq>{dolqinside} {
+                   addlit(yytext, yyleng);
+               }
+<xdolq>{dolqfailed} {
+                   addlit(yytext, yyleng);
+               }
+<xdolq>.       {
+                   /* single quote or dollar sign */
+                   addlitchar(yytext[0]);
+               }
+<xdolq><<EOF>> { mmfatal(PARSE_ERROR, "unterminated dollar-quoted string"); }
+
+<SQL>{
+{xdstart}      {
+                   state_before = YYSTATE;
+                   BEGIN(xd);
+                   startlit();
+               }
+{xuistart}     {
+                   state_before = YYSTATE;
+                   BEGIN(xui);
+                   startlit();
+                   addlit(yytext, yyleng);
+               }
+} /* <SQL> */
+
+<xd>{xdstop}   {
+                   BEGIN(state_before);
+                   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. */
+                   base_yylval.str = mm_strdup(literalbuf);
+                   return CSTRING;
+               }
+<xdc>{xdstop}  {
+                   BEGIN(state_before);
+                   base_yylval.str = mm_strdup(literalbuf);
+                   return CSTRING;
+               }
+<xui>{xuistop} {
+                   BEGIN(state_before);
+                   if (literallen == 2) /* "U&" */
+                       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. */
+                   addlit(yytext, yyleng);
+                   base_yylval.str = mm_strdup(literalbuf);
+                   return UIDENT;
+               }
+<xd,xui>{xddouble} {
+                   addlitchar('"');
+               }
+<xd,xui>{xdinside} {
+                   addlit(yytext, yyleng);
+               }
+<xd,xui><<EOF>>    { mmfatal(PARSE_ERROR, "unterminated quoted identifier"); }
+<C>{xdstart}   {
+                   state_before = YYSTATE;
+                   BEGIN(xdc);
+                   startlit();
+               }
+<xdc>{xdcinside}   {
+                   addlit(yytext, yyleng);
+               }
+<xdc><<EOF>>   { mmfatal(PARSE_ERROR, "unterminated quoted string"); }
+
+<SQL>{
+{typecast}     {
+                   return TYPECAST;
+               }
+
+{dot_dot}      {
+                   return DOT_DOT;
+               }
+
+{colon_equals} {
+                   return COLON_EQUALS;
+               }
+
+{equals_greater} {
+                   return EQUALS_GREATER;
+               }
+
+{less_equals}  {
+                   return LESS_EQUALS;
+               }
+
+{greater_equals} {
+                   return GREATER_EQUALS;
+               }
+
+{less_greater} {
+                   /* We accept both "<>" and "!=" as meaning NOT_EQUALS */
+                   return NOT_EQUALS;
+               }
+
+{not_equals}   {
+                   /* We accept both "<>" and "!=" as meaning NOT_EQUALS */
+                   return NOT_EQUALS;
+               }
+
+{informix_special} {
              /* are we simulating Informix? */
                if (INFORMIX_MODE)
                {
@@ -660,184 +763,205 @@ cppline         {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
                else
                    return yytext[0];
                }
-<SQL>{self}            { /*
-                      * 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);
-                     return yytext[0];
-                   }
-<SQL>{operator}        {
-                       /*
-                        * Check for embedded slash-star or dash-dash; those
-                        * are comment starts, so operator must stop there.
-                        * Note that slash-star or dash-dash at the first
-                        * character will match a prior rule, not this one.
-                        */
-                       int     nchars = yyleng;
-                       char   *slashstar = strstr(yytext, "/*");
-                       char   *dashdash = strstr(yytext, "--");
 
-                       if (slashstar && dashdash)
-                       {
-                           /* if both appear, take the first one */
-                           if (slashstar > dashdash)
-                               slashstar = dashdash;
-                       }
-                       else if (!slashstar)
+{self}         {
+                   /*
+                    * 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);
+                   return yytext[0];
+               }
+
+{operator}     {
+                   /*
+                    * Check for embedded slash-star or dash-dash; those
+                    * are comment starts, so operator must stop there.
+                    * Note that slash-star or dash-dash at the first
+                    * character will match a prior rule, not this one.
+                    */
+                   int         nchars = yyleng;
+                   char       *slashstar = strstr(yytext, "/*");
+                   char       *dashdash = strstr(yytext, "--");
+
+                   if (slashstar && dashdash)
+                   {
+                       /* if both appear, take the first one */
+                       if (slashstar > dashdash)
                            slashstar = dashdash;
-                       if (slashstar)
-                           nchars = slashstar - yytext;
+                   }
+                   else if (!slashstar)
+                       slashstar = dashdash;
+                   if (slashstar)
+                       nchars = slashstar - yytext;
 
-                       /*
-                        * For SQL compatibility, '+' and '-' cannot be the
-                        * last char of a multi-char operator unless the operator
-                        * contains chars that are not in SQL operators.
-                        * The idea is to lex '=-' as two operators, but not
-                        * to forbid operator names like '?-' that could not be
-                        * sequences of SQL operators.
-                        */
-                       if (nchars > 1 &&
-                           (yytext[nchars - 1] == '+' ||
-                            yytext[nchars - 1] == '-'))
-                       {
-                           int     ic;
+                   /*
+                    * For SQL compatibility, '+' and '-' cannot be the
+                    * last char of a multi-char operator unless the operator
+                    * contains chars that are not in SQL operators.
+                    * The idea is to lex '=-' as two operators, but not
+                    * to forbid operator names like '?-' that could not be
+                    * sequences of SQL operators.
+                    */
+                   if (nchars > 1 &&
+                       (yytext[nchars - 1] == '+' ||
+                        yytext[nchars - 1] == '-'))
+                   {
+                       int         ic;
 
-                           for (ic = nchars - 2; ic >= 0; ic--)
-                           {
-                               char c = yytext[ic];
-                               if (c == '~' || c == '!' || c == '@' ||
-                                   c == '#' || c == '^' || c == '&' ||
-                                   c == '|' || c == '`' || c == '?' ||
-                                   c == '%')
-                                   break;
-                           }
-                           if (ic < 0)
-                           {
-                               /*
-                                * didn't find a qualifying character, so remove
-                                * all trailing [+-]
-                                */
-                               do {
-                                   nchars--;
-                               } while (nchars > 1 &&
-                                    (yytext[nchars - 1] == '+' ||
-                                     yytext[nchars - 1] == '-'));
-                           }
+                       for (ic = nchars - 2; ic >= 0; ic--)
+                       {
+                           char c = yytext[ic];
+                           if (c == '~' || c == '!' || c == '@' ||
+                               c == '#' || c == '^' || c == '&' ||
+                               c == '|' || c == '`' || c == '?' ||
+                               c == '%')
+                               break;
                        }
-
-                       if (nchars < yyleng)
+                       if (ic < 0)
                        {
-                           /* 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
-                            * return it as a character token the same way
-                            * that the "self" rule would have.
-                            */
-                           if (nchars == 1 &&
-                               strchr(",()[].;:+-*/%^<>=", yytext[0]))
-                               return yytext[0];
                            /*
-                            * Likewise, if what we have left is two chars, and
-                            * those match the tokens ">=", "<=", "=>", "<>" or
-                            * "!=", then we must return the appropriate token
-                            * rather than the generic Op.
+                            * didn't find a qualifying character, so remove
+                            * all trailing [+-]
                             */
-                           if (nchars == 2)
-                           {
-                               if (yytext[0] == '=' && yytext[1] == '>')
-                                   return EQUALS_GREATER;
-                               if (yytext[0] == '>' && yytext[1] == '=')
-                                   return GREATER_EQUALS;
-                               if (yytext[0] == '<' && yytext[1] == '=')
-                                   return LESS_EQUALS;
-                               if (yytext[0] == '<' && yytext[1] == '>')
-                                   return NOT_EQUALS;
-                               if (yytext[0] == '!' && yytext[1] == '=')
-                                   return NOT_EQUALS;
-                           }
+                           do {
+                               nchars--;
+                           } while (nchars > 1 &&
+                                (yytext[nchars - 1] == '+' ||
+                                 yytext[nchars - 1] == '-'));
                        }
-
-                       base_yylval.str = mm_strdup(yytext);
-                       return Op;
-                   }
-<SQL>{param}       {
-                       base_yylval.ival = atol(yytext+1);
-                       return PARAM;
                    }
-<C,SQL>{integer}   {
-                       int val;
-                       char* endptr;
 
-                       errno = 0;
-                       val = strtoint(yytext, &endptr, 10);
-                       if (*endptr != '\0' || errno == ERANGE)
+                   if (nchars < yyleng)
+                   {
+                       /* 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
+                        * return it as a character token the same way
+                        * that the "self" rule would have.
+                        */
+                       if (nchars == 1 &&
+                           strchr(",()[].;:+-*/%^<>=", yytext[0]))
+                           return yytext[0];
+                       /*
+                        * Likewise, if what we have left is two chars, and
+                        * those match the tokens ">=", "<=", "=>", "<>" or
+                        * "!=", then we must return the appropriate token
+                        * rather than the generic Op.
+                        */
+                       if (nchars == 2)
                        {
-                           errno = 0;
-                           base_yylval.str = mm_strdup(yytext);
-                           return FCONST;
+                           if (yytext[0] == '=' && yytext[1] == '>')
+                               return EQUALS_GREATER;
+                           if (yytext[0] == '>' && yytext[1] == '=')
+                               return GREATER_EQUALS;
+                           if (yytext[0] == '<' && yytext[1] == '=')
+                               return LESS_EQUALS;
+                           if (yytext[0] == '<' && yytext[1] == '>')
+                               return NOT_EQUALS;
+                           if (yytext[0] == '!' && yytext[1] == '=')
+                               return NOT_EQUALS;
                        }
-                       base_yylval.ival = val;
-                       return ICONST;
                    }
-<SQL>{ip}          {
-                       base_yylval.str = mm_strdup(yytext);
-                       return IP;
+
+                   base_yylval.str = mm_strdup(yytext);
+                   return Op;
                }
-<C,SQL>{decimal}   {
-                       base_yylval.str = mm_strdup(yytext);
-                       return FCONST;
-           }
-<C,SQL>{real}      {
-                       base_yylval.str = mm_strdup(yytext);
-                       return FCONST;
-           }
-<SQL>{realfail1}   {
-                       yyless(yyleng-1);
-                       base_yylval.str = mm_strdup(yytext);
-                       return FCONST;
-                   }
-<SQL>{realfail2}   {
-                       yyless(yyleng-2);
+
+{param}            {
+                   base_yylval.ival = atol(yytext+1);
+                   return PARAM;
+               }
+
+{ip}           {
+                   base_yylval.str = mm_strdup(yytext);
+                   return IP;
+               }
+}  /* <SQL> */
+
+<C,SQL>{
+{integer}      {
+                   return process_integer_literal(yytext, &base_yylval);
+               }
+{decimal}      {
+                   base_yylval.str = mm_strdup(yytext);
+                   return FCONST;
+               }
+{decimalfail}  {
+                   /* throw back the .., and treat as integer */
+                   yyless(yyleng - 2);
+                   return process_integer_literal(yytext, &base_yylval);
+               }
+{real}         {
+                   base_yylval.str = mm_strdup(yytext);
+                   return FCONST;
+               }
+{realfail1}        {
+                   /*
+                    * throw back the [Ee], and treat as {decimal}.  Note
+                    * that it is possible the input is actually {integer},
+                    * but since this case will almost certainly lead to a
+                    * syntax error anyway, we don't bother to distinguish.
+                    */
+                   yyless(yyleng - 1);
+                   base_yylval.str = mm_strdup(yytext);
+                   return FCONST;
+               }
+{realfail2}        {
+                   /* throw back the [Ee][+-], and proceed as above */
+                   yyless(yyleng - 2);
+                   base_yylval.str = mm_strdup(yytext);
+                   return FCONST;
+               }
+} /* <C,SQL> */
+
+<SQL>{
+:{identifier}((("->"|\.){identifier})|(\[{array}\]))*  {
+                   base_yylval.str = mm_strdup(yytext+1);
+                   return CVARIABLE;
+               }
+
+{identifier}   {
+                   const ScanKeyword  *keyword;
+
+                   if (!isdefine())
+                   {
+                       /* Is it an SQL/ECPG keyword? */
+                       keyword = ScanECPGKeywordLookup(yytext);
+                       if (keyword != NULL)
+                           return keyword->value;
+
+                       /* Is it a C keyword? */
+                       keyword = ScanCKeywordLookup(yytext);
+                       if (keyword != NULL)
+                           return keyword->value;
+
+                       /*
+                        * None of the above.  Return it as an identifier.
+                        *
+                        * 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.
+                        */
                        base_yylval.str = mm_strdup(yytext);
-                       return FCONST;
-                   }
-<SQL>:{identifier}((("->"|\.){identifier})|(\[{array}\]))* {
-                       base_yylval.str = mm_strdup(yytext+1);
-                       return CVARIABLE;
+                       return IDENT;
                    }
-<SQL>{identifier}  {
-                       const ScanKeyword  *keyword;
+               }
 
-                       if (!isdefine())
-                       {
-                           /* Is it an SQL/ECPG keyword? */
-                           keyword = ScanECPGKeywordLookup(yytext);
-                           if (keyword != NULL)
-                               return keyword->value;
+{other}            {
+                   return yytext[0];
+               }
+} /* <SQL> */
 
-                           /* Is it a C keyword? */
-                           keyword = ScanCKeywordLookup(yytext);
-                           if (keyword != NULL)
-                               return keyword->value;
+   /*
+    * Begin ECPG-specific rules
+    */
 
-                           /*
-                            * None of the above.  Return it as an identifier.
-                            *
-                            * 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.
-                            */
-                           base_yylval.str = mm_strdup(yytext);
-                           return IDENT;
-                       }
-                   }
-<SQL>{other}       { return yytext[0]; }
 <C>{exec_sql}      { BEGIN(SQL); return SQL_START; }
 <C>{informix_special}  {
                        /* are we simulating Informix? */
@@ -1288,6 +1412,7 @@ cppline           {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+
 
                    }
                }
+
 <INITIAL>{other}|\n    { mmfatal(PARSE_ERROR, "internal error: unreachable state; please report this to <pgsql-bugs@postgresql.org>"); }
 
 %%
@@ -1350,6 +1475,24 @@ addlitchar(unsigned char ychar)
    literalbuf[literallen] = '\0';
 }
 
+static int
+process_integer_literal(const char *token, YYSTYPE *lval)
+{
+   int         val;
+   char       *endptr;
+
+   errno = 0;
+   val = strtoint(token, &endptr, 10);
+   if (*endptr != '\0' || errno == ERANGE)
+   {
+       /* integer too large, treat it as a float */
+       lval->str = mm_strdup(token);
+       return FCONST;
+   }
+   lval->ival = val;
+   return ICONST;
+}
+
 static void
 parse_include(void)
 {