pure parsers and reentrant scanners
This patch series changes several parsers in the backend and contrib
modules to use bison pure parsers and flex reentrant scanners. This is
ultimately toward thread-safety, but I think it's also just nicer in
general, and it might also fix a few possible small memory leaks.
I organized this patch series into very small incremental changes so
that it's easier to follow. The final commits should probably combined a
bit more (e.g., one per module).
In this patch series I have so far dealt with
* contrib/cube/
* contrib/seg/
* src/backend/replication/repl_*
* src/backend/replication/syncrep_*
These four needed the whole treatment: pure parser, reentrant scanner,
and updated memory handling.
Also:
* src/backend/utils/adt/jsonpath_scan.l
This one already had a pure parser and palloc-based memory handling, but
not a reentrant scanner, so I just did that.
The above are all pretty similar, so it was relatively easy to work
through them once I had the first one figured out.
A couple of things that are still missing in the above:
* For repl_scanner.l, I want to use yyextra to deal with the static
variables marked /*FIXME*/, but somehow I made that buggy, I'll need to
take another look later.
* For both the replication parser and the syncrep parser, get rid of the
global variables to pass back the results. Again, here I need another
look later. I confused either myself or the compiler on these.
cube, seg, and jsonpath are about as done as I would want them to be.
Not done yet:
* src/backend/utils/misc/guc-file.l
* src/pl/plpgsql/src/pl_gram.y
These have quite different structures and requirements, so I plan to
deal with them separately.
Not relevant for backend thread-safety:
* src/backend/bootstrap/
It might make sense to eventually covert that one as well, just so that
the APIs are kept similar. But that could be for later.
Note that the core scanner and parser are already reentrant+pure.
Also, there are various other scanners and parsers in frontends (psql,
pgbench, ecpg) that are not relevant for this. (Again, it might make
sense to convert some of them later, and some of them are already done.)
AFAICT, all the options and coding techniques used here are already in
use elsewhere in the tree, so there shouldn't be any concerns about
bison or flex version compatibility.
Attachments:
v0-0004-seg-pure-parser-and-reentrant-scanner.patchtext/plain; charset=UTF-8; name=v0-0004-seg-pure-parser-and-reentrant-scanner.patchDownload
From 7a4cd3ffee3009a5f6f5cbfd2fed362e6b87ad2c Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v0 04/15] seg: pure parser and reentrant scanner
---
contrib/seg/seg.c | 9 ++++----
contrib/seg/segdata.h | 13 ++++++++----
contrib/seg/segparse.y | 9 ++++----
contrib/seg/segscan.l | 47 ++++++++++++++++++++----------------------
4 files changed, 41 insertions(+), 37 deletions(-)
diff --git a/contrib/seg/seg.c b/contrib/seg/seg.c
index 7f9fc24eb4b..fd4216edc5d 100644
--- a/contrib/seg/seg.c
+++ b/contrib/seg/seg.c
@@ -105,13 +105,14 @@ seg_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
SEG *result = palloc(sizeof(SEG));
+ yyscan_t scanner;
- seg_scanner_init(str);
+ seg_scanner_init(str, &scanner);
- if (seg_yyparse(result, fcinfo->context) != 0)
- seg_yyerror(result, fcinfo->context, "bogus input");
+ if (seg_yyparse(result, fcinfo->context, scanner) != 0)
+ seg_yyerror(result, fcinfo->context, scanner, "bogus input");
- seg_scanner_finish();
+ seg_scanner_finish(scanner);
PG_RETURN_POINTER(result);
}
diff --git a/contrib/seg/segdata.h b/contrib/seg/segdata.h
index 3d6e3e3f245..7bc7c83dca3 100644
--- a/contrib/seg/segdata.h
+++ b/contrib/seg/segdata.h
@@ -14,12 +14,17 @@ typedef struct SEG
/* in seg.c */
extern int significant_digits(const char *s);
+/* for segscan.l and segparse.y */
+union YYSTYPE;
+typedef void *yyscan_t;
+
/* in segscan.l */
-extern int seg_yylex(void);
+extern int seg_yylex(union YYSTYPE *yylval_param, yyscan_t yyscanner);
extern void seg_yyerror(SEG *result, struct Node *escontext,
+ yyscan_t yyscanner,
const char *message);
-extern void seg_scanner_init(const char *str);
-extern void seg_scanner_finish(void);
+extern void seg_scanner_init(const char *str, yyscan_t *yyscannerp);
+extern void seg_scanner_finish(yyscan_t yyscanner);
/* in segparse.y */
-extern int seg_yyparse(SEG *result, struct Node *escontext);
+extern int seg_yyparse(SEG *result, struct Node *escontext, yyscan_t yyscanner);
diff --git a/contrib/seg/segparse.y b/contrib/seg/segparse.y
index 3e4aa31cada..0358ddb182c 100644
--- a/contrib/seg/segparse.y
+++ b/contrib/seg/segparse.y
@@ -14,10 +14,6 @@
#include "segdata.h"
#include "segparse.h"
-/* silence -Wmissing-variable-declarations */
-extern int seg_yychar;
-extern int seg_yynerrs;
-
/*
* Bison doesn't allocate anything that needs to live across parser calls,
* so we can easily have it use palloc instead of malloc. This prevents
@@ -35,6 +31,9 @@ static int sig_digits(const char *value);
/* BISON Declarations */
%parse-param {SEG *result}
%parse-param {struct Node *escontext}
+%parse-param {yyscan_t yyscanner}
+%lex-param {yyscan_t yyscanner}
+%pure-parser
%expect 0
%name-prefix="seg_yy"
@@ -72,6 +71,8 @@ range: boundary PLUMIN deviation
result->u_sigd = Max(sig_digits(strbuf), Max($1.sigd, $3.sigd));
result->l_ext = '\0';
result->u_ext = '\0';
+
+ (void) yynerrs; /* suppress compiler warning */
}
| boundary RANGE boundary
diff --git a/contrib/seg/segscan.l b/contrib/seg/segscan.l
index 4ad529eccc4..35c3a11ac14 100644
--- a/contrib/seg/segscan.l
+++ b/contrib/seg/segscan.l
@@ -6,12 +6,8 @@
#include "nodes/miscnodes.h"
-/*
- * NB: include segparse.h only AFTER including segdata.h, because segdata.h
- * contains the definition for SEG.
- */
#include "segdata.h"
-#include "segparse.h"
+#include "segparse.h" /* must be after segdata.h SEG */
}
%{
@@ -31,10 +27,11 @@ fprintf_to_ereport(const char *fmt, const char *msg)
}
/* Handles to the buffer that the lexer uses internally */
-static YY_BUFFER_STATE scanbufhandle;
-static char *scanbuf;
+static char *scanbuf; // FIXME
%}
+%option reentrant
+%option bison-bridge
%option 8bit
%option never-interactive
%option nodefault
@@ -53,12 +50,12 @@ float ({integer}|{real})([eE]{integer})?
%%
-{range} seg_yylval.text = yytext; return RANGE;
-{plumin} seg_yylval.text = yytext; return PLUMIN;
-{float} seg_yylval.text = yytext; return SEGFLOAT;
-\< seg_yylval.text = "<"; return EXTENSION;
-\> seg_yylval.text = ">"; return EXTENSION;
-\~ seg_yylval.text = "~"; return EXTENSION;
+{range} yylval->text = yytext; return RANGE;
+{plumin} yylval->text = yytext; return PLUMIN;
+{float} yylval->text = yytext; return SEGFLOAT;
+\< yylval->text = "<"; return EXTENSION;
+\> yylval->text = ">"; return EXTENSION;
+\~ yylval->text = "~"; return EXTENSION;
[ \t\n\r\f\v]+ /* discard spaces */
. return yytext[0]; /* alert parser of the garbage */
@@ -67,8 +64,10 @@ float ({integer}|{real})([eE]{integer})?
/* LCOV_EXCL_STOP */
void
-seg_yyerror(SEG *result, struct Node *escontext, const char *message)
+seg_yyerror(SEG *result, struct Node *escontext, yyscan_t yyscanner, const char *message)
{
+ struct yyguts_t * yyg = (struct yyguts_t *) yyscanner; /* needed for yytext macro */
+
/* if we already reported an error, don't overwrite it */
if (SOFT_ERROR_OCCURRED(escontext))
return;
@@ -96,15 +95,15 @@ seg_yyerror(SEG *result, struct Node *escontext, const char *message)
* Called before any actual parsing is done
*/
void
-seg_scanner_init(const char *str)
+seg_scanner_init(const char *str, yyscan_t *yyscannerp)
{
Size slen = strlen(str);
+ yyscan_t yyscanner;
- /*
- * Might be left over after ereport()
- */
- if (YY_CURRENT_BUFFER)
- yy_delete_buffer(YY_CURRENT_BUFFER);
+ if (yylex_init(yyscannerp) != 0)
+ elog(ERROR, "yylex_init() failed: %m");
+
+ yyscanner = *yyscannerp;
/*
* Make a scan buffer with special termination needed by flex.
@@ -112,9 +111,7 @@ seg_scanner_init(const char *str)
scanbuf = palloc(slen + 2);
memcpy(scanbuf, str, slen);
scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
-
- BEGIN(INITIAL);
+ yy_scan_buffer(scanbuf, slen + 2, yyscanner);
}
@@ -122,8 +119,8 @@ seg_scanner_init(const char *str)
* Called after parsing is done to clean up after seg_scanner_init()
*/
void
-seg_scanner_finish(void)
+seg_scanner_finish(yyscan_t yyscanner)
{
- yy_delete_buffer(scanbufhandle);
+ yylex_destroy(yyscanner);
pfree(scanbuf);
}
--
2.47.1
v0-0001-cube-pure-parser-and-reentrant-scanner.patchtext/plain; charset=UTF-8; name=v0-0001-cube-pure-parser-and-reentrant-scanner.patchDownload
From 01823a7c975fda47ce220db5bb91ecd0959d5123 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v0 01/15] cube: pure parser and reentrant scanner
Use the flex %option reentrant and the bison option %pure-parser to
make the generated scanner and parser pure, reentrant, and
thread-safe.
(There are still some issues in the surrounding integration, see
FIXMEs.)
---
contrib/cube/cube.c | 7 +++---
contrib/cube/cubedata.h | 15 ++++++++----
contrib/cube/cubeparse.y | 15 +++++-------
contrib/cube/cubescan.l | 51 +++++++++++++++++++---------------------
4 files changed, 45 insertions(+), 43 deletions(-)
diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c
index 1fc447511a1..bf8fc489dca 100644
--- a/contrib/cube/cube.c
+++ b/contrib/cube/cube.c
@@ -120,13 +120,14 @@ cube_in(PG_FUNCTION_ARGS)
char *str = PG_GETARG_CSTRING(0);
NDBOX *result;
Size scanbuflen;
+ yyscan_t scanner;
- cube_scanner_init(str, &scanbuflen);
+ cube_scanner_init(str, &scanbuflen, &scanner);
- cube_yyparse(&result, scanbuflen, fcinfo->context);
+ cube_yyparse(&result, scanbuflen, fcinfo->context, scanner);
/* We might as well run this even on failure. */
- cube_scanner_finish();
+ cube_scanner_finish(scanner);
PG_RETURN_NDBOX_P(result);
}
diff --git a/contrib/cube/cubedata.h b/contrib/cube/cubedata.h
index 96fa41a04e7..8bfcc6e99a2 100644
--- a/contrib/cube/cubedata.h
+++ b/contrib/cube/cubedata.h
@@ -59,14 +59,21 @@ typedef struct NDBOX
#define CubeKNNDistanceEuclid 17 /* <-> */
#define CubeKNNDistanceChebyshev 18 /* <=> */
+/* for cubescan.l and cubeparse.y */
+/* All grammar constructs return strings */
+#define YYSTYPE char *
+typedef void *yyscan_t;
+
/* in cubescan.l */
-extern int cube_yylex(void);
+extern int cube_yylex(YYSTYPE *yylval_param, yyscan_t yyscanner);
extern void cube_yyerror(NDBOX **result, Size scanbuflen,
struct Node *escontext,
+ yyscan_t yyscanner,
const char *message);
-extern void cube_scanner_init(const char *str, Size *scanbuflen);
-extern void cube_scanner_finish(void);
+extern void cube_scanner_init(const char *str, Size *scanbuflen, yyscan_t *yyscannerp);
+extern void cube_scanner_finish(yyscan_t yyscanner);
/* in cubeparse.y */
extern int cube_yyparse(NDBOX **result, Size scanbuflen,
- struct Node *escontext);
+ struct Node *escontext,
+ yyscan_t yyscanner);
diff --git a/contrib/cube/cubeparse.y b/contrib/cube/cubeparse.y
index 52622875cbb..a6b7e70630d 100644
--- a/contrib/cube/cubeparse.y
+++ b/contrib/cube/cubeparse.y
@@ -7,19 +7,11 @@
#include "postgres.h"
#include "cubedata.h"
+#include "cubeparse.h" /* must be after cubedata.h for YYSTYPE and NDBOX */
#include "nodes/miscnodes.h"
#include "utils/float.h"
#include "varatt.h"
-/* All grammar constructs return strings */
-#define YYSTYPE char *
-
-#include "cubeparse.h"
-
-/* silence -Wmissing-variable-declarations */
-extern int cube_yychar;
-extern int cube_yynerrs;
-
/*
* Bison doesn't allocate anything that needs to live across parser calls,
* so we can easily have it use palloc instead of malloc. This prevents
@@ -40,6 +32,9 @@ static bool write_point_as_box(int dim, char *str,
%parse-param {NDBOX **result}
%parse-param {Size scanbuflen}
%parse-param {struct Node *escontext}
+%parse-param {yyscan_t yyscanner}
+%lex-param {yyscan_t yyscanner}
+%pure-parser
%expect 0
%name-prefix="cube_yy"
@@ -75,6 +70,8 @@ box: O_BRACKET paren_list COMMA paren_list C_BRACKET
if (!write_box(dim, $2, $4, result, escontext))
YYABORT;
+
+ (void) yynerrs; /* suppress compiler warning */
}
| paren_list COMMA paren_list
diff --git a/contrib/cube/cubescan.l b/contrib/cube/cubescan.l
index a30fbfc3111..09109675711 100644
--- a/contrib/cube/cubescan.l
+++ b/contrib/cube/cubescan.l
@@ -6,13 +6,8 @@
#include "postgres.h"
-/*
- * NB: include cubeparse.h only AFTER defining YYSTYPE (to match cubeparse.y)
- * and cubedata.h for NDBOX.
- */
#include "cubedata.h"
-#define YYSTYPE char *
-#include "cubeparse.h"
+#include "cubeparse.h" /* must be after cubedata.h for YYSTYPE and NDBOX */
}
%{
@@ -32,10 +27,11 @@ fprintf_to_ereport(const char *fmt, const char *msg)
}
/* Handles to the buffer that the lexer uses internally */
-static YY_BUFFER_STATE scanbufhandle;
-static char *scanbuf;
+static char *scanbuf; // FIXME
%}
+%option reentrant
+%option bison-bridge
%option 8bit
%option never-interactive
%option nodefault
@@ -55,14 +51,14 @@ NaN [nN][aA][nN]
%%
-{float} cube_yylval = yytext; return CUBEFLOAT;
-{infinity} cube_yylval = yytext; return CUBEFLOAT;
-{NaN} cube_yylval = yytext; return CUBEFLOAT;
-\[ cube_yylval = "("; return O_BRACKET;
-\] cube_yylval = ")"; return C_BRACKET;
-\( cube_yylval = "("; return O_PAREN;
-\) cube_yylval = ")"; return C_PAREN;
-\, cube_yylval = ","; return COMMA;
+{float} *yylval = yytext; return CUBEFLOAT;
+{infinity} *yylval = yytext; return CUBEFLOAT;
+{NaN} *yylval = yytext; return CUBEFLOAT;
+\[ *yylval = "("; return O_BRACKET;
+\] *yylval = ")"; return C_BRACKET;
+\( *yylval = "("; return O_PAREN;
+\) *yylval = ")"; return C_PAREN;
+\, *yylval = ","; return COMMA;
[ \t\n\r\f\v]+ /* discard spaces */
. return yytext[0]; /* alert parser of the garbage */
@@ -74,8 +70,11 @@ NaN [nN][aA][nN]
void
cube_yyerror(NDBOX **result, Size scanbuflen,
struct Node *escontext,
+ yyscan_t yyscanner,
const char *message)
{
+ struct yyguts_t * yyg = (struct yyguts_t *) yyscanner; /* needed for yytext macro */
+
if (*yytext == YY_END_OF_BUFFER_CHAR)
{
errsave(escontext,
@@ -99,15 +98,15 @@ cube_yyerror(NDBOX **result, Size scanbuflen,
* Called before any actual parsing is done
*/
void
-cube_scanner_init(const char *str, Size *scanbuflen)
+cube_scanner_init(const char *str, Size *scanbuflen, yyscan_t *yyscannerp)
{
Size slen = strlen(str);
+ yyscan_t yyscanner;
- /*
- * Might be left over after ereport()
- */
- if (YY_CURRENT_BUFFER)
- yy_delete_buffer(YY_CURRENT_BUFFER);
+ if (yylex_init(yyscannerp) != 0)
+ elog(ERROR, "yylex_init() failed: %m");
+
+ yyscanner = *yyscannerp;
/*
* Make a scan buffer with special termination needed by flex.
@@ -116,9 +115,7 @@ cube_scanner_init(const char *str, Size *scanbuflen)
scanbuf = palloc(slen + 2);
memcpy(scanbuf, str, slen);
scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
-
- BEGIN(INITIAL);
+ yy_scan_buffer(scanbuf, slen + 2, yyscanner);
}
@@ -126,8 +123,8 @@ cube_scanner_init(const char *str, Size *scanbuflen)
* Called after parsing is done to clean up after cube_scanner_init()
*/
void
-cube_scanner_finish(void)
+cube_scanner_finish(yyscan_t yyscanner)
{
- yy_delete_buffer(scanbufhandle);
+ yylex_destroy(yyscanner);
pfree(scanbuf);
}
base-commit: 2f696453d2b39fea800d5f7d8e5d3e1a2266de24
--
2.47.1
v0-0002-cube-Use-palloc-instead-of-malloc-for-flex.patchtext/plain; charset=UTF-8; name=v0-0002-cube-Use-palloc-instead-of-malloc-for-flex.patchDownload
From f092720cf73b62194d10ddf65948d03bbaafb04b Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v0 02/15] cube: Use palloc() instead of malloc() for flex
Make the generated scanner use palloc() etc. instead of malloc() etc.
Previously, we only used palloc() for the buffer, but flex would still
use malloc() for its internal structures. As a result, there could be
some small memory leaks in case of uncaught errors. (We do catch
normal syntax errors as soft errors.) Now, all the memory is under
palloc() control, so there are no more such issues.
---
contrib/cube/cubescan.l | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/contrib/cube/cubescan.l b/contrib/cube/cubescan.l
index 09109675711..b741e77d7e1 100644
--- a/contrib/cube/cubescan.l
+++ b/contrib/cube/cubescan.l
@@ -38,6 +38,9 @@ static char *scanbuf; // FIXME
%option noinput
%option nounput
%option noyywrap
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
%option warn
%option prefix="cube_yy"
@@ -128,3 +131,30 @@ cube_scanner_finish(yyscan_t yyscanner)
yylex_destroy(yyscanner);
pfree(scanbuf);
}
+
+/*
+ * Interface functions to make flex use palloc() instead of malloc().
+ * It'd be better to make these static, but flex insists otherwise.
+ */
+
+void *
+yyalloc(yy_size_t size, yyscan_t yyscanner)
+{
+ return palloc(size);
+}
+
+void *
+yyrealloc(void *ptr, yy_size_t size, yyscan_t yyscanner)
+{
+ if (ptr)
+ return repalloc(ptr, size);
+ else
+ return palloc(size);
+}
+
+void
+yyfree(void *ptr, yyscan_t yyscanner)
+{
+ if (ptr)
+ pfree(ptr);
+}
--
2.47.1
v0-0003-cube-Simplify-flex-scan-buffer-management.patchtext/plain; charset=UTF-8; name=v0-0003-cube-Simplify-flex-scan-buffer-management.patchDownload
From 0913702c2605fb437c8f140325283006877f11b3 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v0 03/15] cube: Simplify flex scan buffer management
Instead of constructing the buffer from pieces and then using
yy_scan_buffer(), we can just use yy_scan_string(), which does the
same thing internally. (Actually, we use yy_scan_bytes() here because
we already have the length.)
The previous code was necessary because we allocated the buffer with
palloc() and the rest of the state was handled by malloc(). But this
is no longer the case; everything is under palloc() now.
XXX We could even get rid of the yylex_destroy() call and just let the
memory context cleanup handle everything.
---
contrib/cube/cubescan.l | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/contrib/cube/cubescan.l b/contrib/cube/cubescan.l
index b741e77d7e1..eed324d6e3b 100644
--- a/contrib/cube/cubescan.l
+++ b/contrib/cube/cubescan.l
@@ -25,9 +25,6 @@ fprintf_to_ereport(const char *fmt, const char *msg)
{
ereport(ERROR, (errmsg_internal("%s", msg)));
}
-
-/* Handles to the buffer that the lexer uses internally */
-static char *scanbuf; // FIXME
%}
%option reentrant
@@ -111,14 +108,8 @@ cube_scanner_init(const char *str, Size *scanbuflen, yyscan_t *yyscannerp)
yyscanner = *yyscannerp;
- /*
- * Make a scan buffer with special termination needed by flex.
- */
+ yy_scan_bytes(str, slen, yyscanner);
*scanbuflen = slen;
- scanbuf = palloc(slen + 2);
- memcpy(scanbuf, str, slen);
- scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- yy_scan_buffer(scanbuf, slen + 2, yyscanner);
}
@@ -129,7 +120,6 @@ void
cube_scanner_finish(yyscan_t yyscanner)
{
yylex_destroy(yyscanner);
- pfree(scanbuf);
}
/*
--
2.47.1
v0-0005-seg-Use-palloc-instead-of-malloc-for-flex.patchtext/plain; charset=UTF-8; name=v0-0005-seg-Use-palloc-instead-of-malloc-for-flex.patchDownload
From d203f22ea4523c5826556fcfd854db196d9e14dd Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v0 05/15] seg: Use palloc() instead of malloc() for flex
---
contrib/seg/segscan.l | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/contrib/seg/segscan.l b/contrib/seg/segscan.l
index 35c3a11ac14..004ac07fae2 100644
--- a/contrib/seg/segscan.l
+++ b/contrib/seg/segscan.l
@@ -38,6 +38,9 @@ static char *scanbuf; // FIXME
%option noinput
%option nounput
%option noyywrap
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
%option warn
%option prefix="seg_yy"
@@ -124,3 +127,30 @@ seg_scanner_finish(yyscan_t yyscanner)
yylex_destroy(yyscanner);
pfree(scanbuf);
}
+
+/*
+ * Interface functions to make flex use palloc() instead of malloc().
+ * It'd be better to make these static, but flex insists otherwise.
+ */
+
+void *
+yyalloc(yy_size_t size, yyscan_t yyscanner)
+{
+ return palloc(size);
+}
+
+void *
+yyrealloc(void *ptr, yy_size_t size, yyscan_t yyscanner)
+{
+ if (ptr)
+ return repalloc(ptr, size);
+ else
+ return palloc(size);
+}
+
+void
+yyfree(void *ptr, yyscan_t yyscanner)
+{
+ if (ptr)
+ pfree(ptr);
+}
--
2.47.1
v0-0006-seg-Simplify-flex-scan-buffer-management.patchtext/plain; charset=UTF-8; name=v0-0006-seg-Simplify-flex-scan-buffer-management.patchDownload
From c93074ba3949a8edbb5917ceefc80fb020822085 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v0 06/15] seg: Simplify flex scan buffer management
---
contrib/seg/segscan.l | 13 +------------
1 file changed, 1 insertion(+), 12 deletions(-)
diff --git a/contrib/seg/segscan.l b/contrib/seg/segscan.l
index 004ac07fae2..2b35cd4e67a 100644
--- a/contrib/seg/segscan.l
+++ b/contrib/seg/segscan.l
@@ -25,9 +25,6 @@ fprintf_to_ereport(const char *fmt, const char *msg)
{
ereport(ERROR, (errmsg_internal("%s", msg)));
}
-
-/* Handles to the buffer that the lexer uses internally */
-static char *scanbuf; // FIXME
%}
%option reentrant
@@ -100,7 +97,6 @@ seg_yyerror(SEG *result, struct Node *escontext, yyscan_t yyscanner, const char
void
seg_scanner_init(const char *str, yyscan_t *yyscannerp)
{
- Size slen = strlen(str);
yyscan_t yyscanner;
if (yylex_init(yyscannerp) != 0)
@@ -108,13 +104,7 @@ seg_scanner_init(const char *str, yyscan_t *yyscannerp)
yyscanner = *yyscannerp;
- /*
- * Make a scan buffer with special termination needed by flex.
- */
- scanbuf = palloc(slen + 2);
- memcpy(scanbuf, str, slen);
- scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- yy_scan_buffer(scanbuf, slen + 2, yyscanner);
+ yy_scan_string(str, yyscanner);
}
@@ -125,7 +115,6 @@ void
seg_scanner_finish(yyscan_t yyscanner)
{
yylex_destroy(yyscanner);
- pfree(scanbuf);
}
/*
--
2.47.1
v0-0007-replication-parser-pure-parser-and-reentrant-scan.patchtext/plain; charset=UTF-8; name=v0-0007-replication-parser-pure-parser-and-reentrant-scan.patchDownload
From 7e1af47bdec122c1ec939912943c968ec3862d73 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v0 07/15] replication parser: pure parser and reentrant
scanner
---
src/backend/replication/repl_gram.y | 9 ++--
src/backend/replication/repl_scanner.l | 54 ++++++++++-----------
src/backend/replication/walsender.c | 11 +++--
src/include/replication/walsender_private.h | 18 ++++---
4 files changed, 47 insertions(+), 45 deletions(-)
diff --git a/src/backend/replication/repl_gram.y b/src/backend/replication/repl_gram.y
index 06daa954813..4fa71377e20 100644
--- a/src/backend/replication/repl_gram.y
+++ b/src/backend/replication/repl_gram.y
@@ -24,10 +24,6 @@
#include "repl_gram.h"
-/* silence -Wmissing-variable-declarations */
-extern int replication_yychar;
-extern int replication_yynerrs;
-
/* Result of the parsing is returned here */
Node *replication_parse_result;
@@ -43,6 +39,9 @@ Node *replication_parse_result;
%}
+%parse-param {yyscan_t yyscanner}
+%lex-param {yyscan_t yyscanner}
+%pure-parser
%expect 0
%name-prefix="replication_yy"
@@ -106,6 +105,8 @@ Node *replication_parse_result;
firstcmd: command opt_semicolon
{
replication_parse_result = $1;
+
+ (void) yynerrs; /* suppress compiler warning */
}
;
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index e7def800655..99f2efbb8f9 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -38,14 +38,11 @@ fprintf_to_ereport(const char *fmt, const char *msg)
ereport(ERROR, (errmsg_internal("%s", msg)));
}
-/* Handle to the buffer that the lexer uses internally */
-static YY_BUFFER_STATE scanbufhandle;
-
/* Pushed-back token (we only handle one) */
-static int repl_pushed_back_token;
+static int repl_pushed_back_token; /* FIXME */
/* Work area for collecting literals */
-static StringInfoData litbuf;
+static StringInfoData litbuf; /* FIXME */
static void startlit(void);
static char *litbufdup(void);
@@ -56,6 +53,8 @@ static void addlitchar(unsigned char ychar);
%}
+%option reentrant
+%option bison-bridge
%option 8bit
%option never-interactive
%option nodefault
@@ -142,7 +141,7 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
{space}+ { /* do nothing */ }
{digit}+ {
- replication_yylval.uintval = strtoul(yytext, NULL, 10);
+ yylval->uintval = strtoul(yytext, NULL, 10);
return UCONST;
}
@@ -150,8 +149,8 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
uint32 hi,
lo;
if (sscanf(yytext, "%X/%X", &hi, &lo) != 2)
- replication_yyerror("invalid streaming start location");
- replication_yylval.recptr = ((uint64) hi) << 32 | lo;
+ replication_yyerror("invalid streaming start location", yyscanner);
+ yylval->recptr = ((uint64) hi) << 32 | lo;
return RECPTR;
}
@@ -163,7 +162,7 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
<xq>{quotestop} {
yyless(1);
BEGIN(INITIAL);
- replication_yylval.str = litbufdup();
+ yylval->str = litbufdup();
return SCONST;
}
@@ -185,9 +184,9 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
yyless(1);
BEGIN(INITIAL);
- replication_yylval.str = litbufdup();
- len = strlen(replication_yylval.str);
- truncate_identifier(replication_yylval.str, len, true);
+ yylval->str = litbufdup();
+ len = strlen(yylval->str);
+ truncate_identifier(yylval->str, len, true);
return IDENT;
}
@@ -198,7 +197,7 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
{identifier} {
int len = strlen(yytext);
- replication_yylval.str = downcase_truncate_identifier(yytext, len, true);
+ yylval->str = downcase_truncate_identifier(yytext, len, true);
return IDENT;
}
@@ -207,7 +206,7 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
return yytext[0];
}
-<xq,xd><<EOF>> { replication_yyerror("unterminated quoted string"); }
+<xq,xd><<EOF>> { replication_yyerror("unterminated quoted string", yyscanner); }
<<EOF>> {
@@ -243,7 +242,7 @@ addlitchar(unsigned char ychar)
}
void
-replication_yyerror(const char *message)
+replication_yyerror(yyscan_t yyscanner, const char *message)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -252,16 +251,16 @@ replication_yyerror(const char *message)
void
-replication_scanner_init(const char *str)
+replication_scanner_init(const char *str, yyscan_t *yyscannerp)
{
Size slen = strlen(str);
char *scanbuf;
+ yyscan_t yyscanner;
- /*
- * Might be left over after ereport()
- */
- if (YY_CURRENT_BUFFER)
- yy_delete_buffer(YY_CURRENT_BUFFER);
+ if (yylex_init(yyscannerp) != 0)
+ elog(ERROR, "yylex_init() failed: %m");
+
+ yyscanner = *yyscannerp;
/*
* Make a scan buffer with special termination needed by flex.
@@ -269,18 +268,16 @@ replication_scanner_init(const char *str)
scanbuf = (char *) palloc(slen + 2);
memcpy(scanbuf, str, slen);
scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
+ yy_scan_buffer(scanbuf, slen + 2, yyscanner);
/* Make sure we start in proper state */
- BEGIN(INITIAL);
repl_pushed_back_token = 0;
}
void
-replication_scanner_finish(void)
+replication_scanner_finish(yyscan_t yyscanner)
{
- yy_delete_buffer(scanbufhandle);
- scanbufhandle = NULL;
+ yylex_destroy(yyscanner);
}
/*
@@ -292,9 +289,10 @@ replication_scanner_finish(void)
* IDENT token here, although some other cases are possible.
*/
bool
-replication_scanner_is_replication_command(void)
+replication_scanner_is_replication_command(yyscan_t yyscanner)
{
- int first_token = replication_yylex();
+ YYSTYPE dummy;
+ int first_token = replication_yylex(&dummy, yyscanner);
switch (first_token)
{
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 371eef3dddc..4971bcfb765 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1951,6 +1951,7 @@ WalSndWaitForWal(XLogRecPtr loc)
bool
exec_replication_command(const char *cmd_string)
{
+ yyscan_t scanner;
int parse_rc;
Node *cmd_node;
const char *cmdtag;
@@ -1990,15 +1991,15 @@ exec_replication_command(const char *cmd_string)
ALLOCSET_DEFAULT_SIZES);
old_context = MemoryContextSwitchTo(cmd_context);
- replication_scanner_init(cmd_string);
+ replication_scanner_init(cmd_string, &scanner);
/*
* Is it a WalSender command?
*/
- if (!replication_scanner_is_replication_command())
+ if (!replication_scanner_is_replication_command(scanner))
{
/* Nope; clean up and get out. */
- replication_scanner_finish();
+ replication_scanner_finish(scanner);
MemoryContextSwitchTo(old_context);
MemoryContextDelete(cmd_context);
@@ -2016,13 +2017,13 @@ exec_replication_command(const char *cmd_string)
/*
* Looks like a WalSender command, so parse it.
*/
- parse_rc = replication_yyparse();
+ parse_rc = replication_yyparse(scanner);
if (parse_rc != 0)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg_internal("replication command parser returned %d",
parse_rc)));
- replication_scanner_finish();
+ replication_scanner_finish(scanner);
cmd_node = replication_parse_result;
diff --git a/src/include/replication/walsender_private.h b/src/include/replication/walsender_private.h
index 41ac736b953..a82ce33c083 100644
--- a/src/include/replication/walsender_private.h
+++ b/src/include/replication/walsender_private.h
@@ -125,13 +125,15 @@ extern void WalSndSetState(WalSndState state);
* Internal functions for parsing the replication grammar, in repl_gram.y and
* repl_scanner.l
*/
-extern int replication_yyparse(void);
-extern int replication_yylex(void);
-extern void replication_yyerror(const char *message) pg_attribute_noreturn();
-extern void replication_scanner_init(const char *str);
-extern void replication_scanner_finish(void);
-extern bool replication_scanner_is_replication_command(void);
-
-extern PGDLLIMPORT Node *replication_parse_result;
+union YYSTYPE;
+typedef void *yyscan_t;
+extern int replication_yyparse(yyscan_t yyscanner);
+extern int replication_yylex(union YYSTYPE *yylval_param, yyscan_t yyscanner);
+extern void replication_yyerror(yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
+extern void replication_scanner_init(const char *str, yyscan_t *yyscannerp);
+extern void replication_scanner_finish(yyscan_t yyscanner);
+extern bool replication_scanner_is_replication_command(yyscan_t yyscanner);
+
+extern PGDLLIMPORT Node *replication_parse_result; /* FIXME */
#endif /* _WALSENDER_PRIVATE_H */
--
2.47.1
v0-0008-replication-parser-Use-palloc-instead-of-malloc-f.patchtext/plain; charset=UTF-8; name=v0-0008-replication-parser-Use-palloc-instead-of-malloc-f.patchDownload
From dd77c80158720cae6df33131014249afa9d11edc Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v0 08/15] replication parser: Use palloc() instead of malloc()
for flex
---
src/backend/replication/repl_scanner.l | 30 ++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index 99f2efbb8f9..6bd25e77880 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -61,6 +61,9 @@ static void addlitchar(unsigned char ychar);
%option noinput
%option nounput
%option noyywrap
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
%option warn
%option prefix="replication_yy"
@@ -314,3 +317,30 @@ replication_scanner_is_replication_command(yyscan_t yyscanner)
return false;
}
}
+
+/*
+ * Interface functions to make flex use palloc() instead of malloc().
+ * It'd be better to make these static, but flex insists otherwise.
+ */
+
+void *
+yyalloc(yy_size_t size, yyscan_t yyscanner)
+{
+ return palloc(size);
+}
+
+void *
+yyrealloc(void *ptr, yy_size_t size, yyscan_t yyscanner)
+{
+ if (ptr)
+ return repalloc(ptr, size);
+ else
+ return palloc(size);
+}
+
+void
+yyfree(void *ptr, yyscan_t yyscanner)
+{
+ if (ptr)
+ pfree(ptr);
+}
--
2.47.1
v0-0009-replication-parser-Simplify-flex-scan-buffer-mana.patchtext/plain; charset=UTF-8; name=v0-0009-replication-parser-Simplify-flex-scan-buffer-mana.patchDownload
From 2c41a75b06c4e2d3a0bfb70d90d7a6af13c7da92 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v0 09/15] replication parser: Simplify flex scan buffer
management
---
src/backend/replication/repl_scanner.l | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index 6bd25e77880..56f93e681ed 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -256,8 +256,6 @@ replication_yyerror(yyscan_t yyscanner, const char *message)
void
replication_scanner_init(const char *str, yyscan_t *yyscannerp)
{
- Size slen = strlen(str);
- char *scanbuf;
yyscan_t yyscanner;
if (yylex_init(yyscannerp) != 0)
@@ -265,13 +263,7 @@ replication_scanner_init(const char *str, yyscan_t *yyscannerp)
yyscanner = *yyscannerp;
- /*
- * Make a scan buffer with special termination needed by flex.
- */
- scanbuf = (char *) palloc(slen + 2);
- memcpy(scanbuf, str, slen);
- scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- yy_scan_buffer(scanbuf, slen + 2, yyscanner);
+ yy_scan_string(str, yyscanner);
/* Make sure we start in proper state */
repl_pushed_back_token = 0;
--
2.47.1
v0-0010-syncrep-parser-pure-parser-and-reentrant-scanner.patchtext/plain; charset=UTF-8; name=v0-0010-syncrep-parser-pure-parser-and-reentrant-scanner.patchDownload
From 11e17f667d4f90d496c6c1ec99df8b198304492c Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v0 10/15] syncrep parser: pure parser and reentrant scanner
---
src/backend/replication/syncrep.c | 7 ++--
src/backend/replication/syncrep_gram.y | 12 ++++---
src/backend/replication/syncrep_scanner.l | 43 +++++++++++------------
src/include/replication/syncrep.h | 12 ++++---
4 files changed, 38 insertions(+), 36 deletions(-)
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index e1126734ef5..22a2c7fc409 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -992,6 +992,7 @@ check_synchronous_standby_names(char **newval, void **extra, GucSource source)
{
if (*newval != NULL && (*newval)[0] != '\0')
{
+ yyscan_t scanner;
int parse_rc;
SyncRepConfigData *pconf;
@@ -1000,9 +1001,9 @@ check_synchronous_standby_names(char **newval, void **extra, GucSource source)
syncrep_parse_error_msg = NULL;
/* Parse the synchronous_standby_names string */
- syncrep_scanner_init(*newval);
- parse_rc = syncrep_yyparse();
- syncrep_scanner_finish();
+ syncrep_scanner_init(*newval, &scanner);
+ parse_rc = syncrep_yyparse(scanner);
+ syncrep_scanner_finish(scanner);
if (parse_rc != 0 || syncrep_parse_result == NULL)
{
diff --git a/src/backend/replication/syncrep_gram.y b/src/backend/replication/syncrep_gram.y
index e4d9962226c..00b5bf0e522 100644
--- a/src/backend/replication/syncrep_gram.y
+++ b/src/backend/replication/syncrep_gram.y
@@ -26,10 +26,6 @@ char *syncrep_parse_error_msg;
static SyncRepConfigData *create_syncrep_config(const char *num_sync,
List *members, uint8 syncrep_method);
-/* silence -Wmissing-variable-declarations */
-extern int syncrep_yychar;
-extern int syncrep_yynerrs;
-
/*
* Bison doesn't allocate anything that needs to live across parser calls,
* so we can easily have it use palloc instead of malloc. This prevents
@@ -40,6 +36,9 @@ extern int syncrep_yynerrs;
%}
+%parse-param {yyscan_t yyscanner}
+%lex-param {yyscan_t yyscanner}
+%pure-parser
%expect 0
%name-prefix="syncrep_yy"
@@ -60,7 +59,10 @@ extern int syncrep_yynerrs;
%%
result:
- standby_config { syncrep_parse_result = $1; }
+ standby_config {
+ syncrep_parse_result = $1;
+ (void) yynerrs; /* suppress compiler warning */
+ }
;
standby_config:
diff --git a/src/backend/replication/syncrep_scanner.l b/src/backend/replication/syncrep_scanner.l
index 6defb90f13b..b1edb780502 100644
--- a/src/backend/replication/syncrep_scanner.l
+++ b/src/backend/replication/syncrep_scanner.l
@@ -37,15 +37,14 @@ fprintf_to_ereport(const char *fmt, const char *msg)
ereport(ERROR, (errmsg_internal("%s", msg)));
}
-/* Handles to the buffer that the lexer uses internally */
-static YY_BUFFER_STATE scanbufhandle;
-
-static StringInfoData xdbuf;
+static StringInfoData xdbuf; /* FIXME */
/* LCOV_EXCL_START */
%}
+%option reentrant
+%option bison-bridge
%option 8bit
%option never-interactive
%option nodefault
@@ -92,28 +91,28 @@ xdinside [^"]+
appendStringInfoString(&xdbuf, yytext);
}
<xd>{xdstop} {
- syncrep_yylval.str = xdbuf.data;
+ yylval->str = xdbuf.data;
xdbuf.data = NULL;
BEGIN(INITIAL);
return NAME;
}
<xd><<EOF>> {
- syncrep_yyerror("unterminated quoted identifier");
+ syncrep_yyerror("unterminated quoted identifier", yyscanner);
return JUNK;
}
{identifier} {
- syncrep_yylval.str = pstrdup(yytext);
+ yylval->str = pstrdup(yytext);
return NAME;
}
{digit}+ {
- syncrep_yylval.str = pstrdup(yytext);
+ yylval->str = pstrdup(yytext);
return NUM;
}
"*" {
- syncrep_yylval.str = "*";
+ yylval->str = "*";
return NAME;
}
@@ -128,8 +127,10 @@ xdinside [^"]+
/* Needs to be here for access to yytext */
void
-syncrep_yyerror(const char *message)
+syncrep_yyerror(yyscan_t yyscanner, const char *message)
{
+ struct yyguts_t * yyg = (struct yyguts_t *) yyscanner; /* needed for yytext macro */
+
/* report only the first error in a parse operation */
if (syncrep_parse_error_msg)
return;
@@ -142,16 +143,16 @@ syncrep_yyerror(const char *message)
}
void
-syncrep_scanner_init(const char *str)
+syncrep_scanner_init(const char *str, yyscan_t *yyscannerp)
{
Size slen = strlen(str);
char *scanbuf;
+ yyscan_t yyscanner;
- /*
- * Might be left over after ereport()
- */
- if (YY_CURRENT_BUFFER)
- yy_delete_buffer(YY_CURRENT_BUFFER);
+ if (yylex_init(yyscannerp) != 0)
+ elog(ERROR, "yylex_init() failed: %m");
+
+ yyscanner = *yyscannerp;
/*
* Make a scan buffer with special termination needed by flex.
@@ -159,15 +160,11 @@ syncrep_scanner_init(const char *str)
scanbuf = (char *) palloc(slen + 2);
memcpy(scanbuf, str, slen);
scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
-
- /* Make sure we start in proper state */
- BEGIN(INITIAL);
+ yy_scan_buffer(scanbuf, slen + 2, yyscanner);
}
void
-syncrep_scanner_finish(void)
+syncrep_scanner_finish(yyscan_t yyscanner)
{
- yy_delete_buffer(scanbufhandle);
- scanbufhandle = NULL;
+ yylex_destroy(yyscanner);
}
diff --git a/src/include/replication/syncrep.h b/src/include/replication/syncrep.h
index ea439e6da60..b4134dbc91d 100644
--- a/src/include/replication/syncrep.h
+++ b/src/include/replication/syncrep.h
@@ -100,10 +100,12 @@ extern void SyncRepUpdateSyncStandbysDefined(void);
* Internal functions for parsing synchronous_standby_names grammar,
* in syncrep_gram.y and syncrep_scanner.l
*/
-extern int syncrep_yyparse(void);
-extern int syncrep_yylex(void);
-extern void syncrep_yyerror(const char *str);
-extern void syncrep_scanner_init(const char *str);
-extern void syncrep_scanner_finish(void);
+union YYSTYPE;
+typedef void *yyscan_t;
+extern int syncrep_yyparse(yyscan_t yyscanner);
+extern int syncrep_yylex(union YYSTYPE *yylval_param, yyscan_t yyscanner);
+extern void syncrep_yyerror(yyscan_t yyscanner, const char *str);
+extern void syncrep_scanner_init(const char *str, yyscan_t *yyscannerp);
+extern void syncrep_scanner_finish(yyscan_t yyscanner);
#endif /* _SYNCREP_H */
--
2.47.1
v0-0011-syncrep-parser-Use-palloc-instead-of-malloc-for-f.patchtext/plain; charset=UTF-8; name=v0-0011-syncrep-parser-Use-palloc-instead-of-malloc-for-f.patchDownload
From 142dfbb6a92cd970709a24462a538191030462ae Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v0 11/15] syncrep parser: Use palloc() instead of malloc() for
flex
---
src/backend/replication/syncrep_scanner.l | 30 +++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/src/backend/replication/syncrep_scanner.l b/src/backend/replication/syncrep_scanner.l
index b1edb780502..ac2eecd7804 100644
--- a/src/backend/replication/syncrep_scanner.l
+++ b/src/backend/replication/syncrep_scanner.l
@@ -51,6 +51,9 @@ static StringInfoData xdbuf; /* FIXME */
%option noinput
%option nounput
%option noyywrap
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
%option warn
%option prefix="syncrep_yy"
@@ -168,3 +171,30 @@ syncrep_scanner_finish(yyscan_t yyscanner)
{
yylex_destroy(yyscanner);
}
+
+/*
+ * Interface functions to make flex use palloc() instead of malloc().
+ * It'd be better to make these static, but flex insists otherwise.
+ */
+
+void *
+yyalloc(yy_size_t size, yyscan_t yyscanner)
+{
+ return palloc(size);
+}
+
+void *
+yyrealloc(void *ptr, yy_size_t size, yyscan_t yyscanner)
+{
+ if (ptr)
+ return repalloc(ptr, size);
+ else
+ return palloc(size);
+}
+
+void
+yyfree(void *ptr, yyscan_t yyscanner)
+{
+ if (ptr)
+ pfree(ptr);
+}
--
2.47.1
v0-0012-syncrep-parser-Simplify-flex-scan-buffer-manageme.patchtext/plain; charset=UTF-8; name=v0-0012-syncrep-parser-Simplify-flex-scan-buffer-manageme.patchDownload
From 88ca33889a7bc2f6986d0fbb8c8fc3c201d43ab6 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v0 12/15] syncrep parser: Simplify flex scan buffer management
---
src/backend/replication/syncrep_scanner.l | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/src/backend/replication/syncrep_scanner.l b/src/backend/replication/syncrep_scanner.l
index ac2eecd7804..9c0f4fc2c19 100644
--- a/src/backend/replication/syncrep_scanner.l
+++ b/src/backend/replication/syncrep_scanner.l
@@ -148,8 +148,6 @@ syncrep_yyerror(yyscan_t yyscanner, const char *message)
void
syncrep_scanner_init(const char *str, yyscan_t *yyscannerp)
{
- Size slen = strlen(str);
- char *scanbuf;
yyscan_t yyscanner;
if (yylex_init(yyscannerp) != 0)
@@ -157,13 +155,7 @@ syncrep_scanner_init(const char *str, yyscan_t *yyscannerp)
yyscanner = *yyscannerp;
- /*
- * Make a scan buffer with special termination needed by flex.
- */
- scanbuf = (char *) palloc(slen + 2);
- memcpy(scanbuf, str, slen);
- scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- yy_scan_buffer(scanbuf, slen + 2, yyscanner);
+ yy_scan_string(str, yyscanner);
}
void
--
2.47.1
v0-0013-syncrep-parser-Use-flex-yyextra.patchtext/plain; charset=UTF-8; name=v0-0013-syncrep-parser-Use-flex-yyextra.patchDownload
From 0b5c772dd5ce8bf9a32e5072adc64798ca63b0ae Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v0 13/15] syncrep parser: Use flex yyextra
Use flex yyextra to handle context information, instead of global
variables. This complements the earlier patch to make the scanner
reentrant.
---
src/backend/replication/syncrep_scanner.l | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/src/backend/replication/syncrep_scanner.l b/src/backend/replication/syncrep_scanner.l
index 9c0f4fc2c19..85ae4e60604 100644
--- a/src/backend/replication/syncrep_scanner.l
+++ b/src/backend/replication/syncrep_scanner.l
@@ -37,7 +37,11 @@ fprintf_to_ereport(const char *fmt, const char *msg)
ereport(ERROR, (errmsg_internal("%s", msg)));
}
-static StringInfoData xdbuf; /* FIXME */
+struct syncrep_yy_extra_type
+{
+ StringInfoData xdbuf;
+};
+#define YY_EXTRA_TYPE struct syncrep_yy_extra_type *
/* LCOV_EXCL_START */
@@ -84,18 +88,18 @@ xdinside [^"]+
[Ff][Ii][Rr][Ss][Tt] { return FIRST; }
{xdstart} {
- initStringInfo(&xdbuf);
+ initStringInfo(&yyextra->xdbuf);
BEGIN(xd);
}
<xd>{xddouble} {
- appendStringInfoChar(&xdbuf, '"');
+ appendStringInfoChar(&yyextra->xdbuf, '"');
}
<xd>{xdinside} {
- appendStringInfoString(&xdbuf, yytext);
+ appendStringInfoString(&yyextra->xdbuf, yytext);
}
<xd>{xdstop} {
- yylval->str = xdbuf.data;
- xdbuf.data = NULL;
+ yylval->str = yyextra->xdbuf.data;
+ yyextra->xdbuf.data = NULL;
BEGIN(INITIAL);
return NAME;
}
@@ -149,12 +153,15 @@ void
syncrep_scanner_init(const char *str, yyscan_t *yyscannerp)
{
yyscan_t yyscanner;
+ struct syncrep_yy_extra_type yyext;
if (yylex_init(yyscannerp) != 0)
elog(ERROR, "yylex_init() failed: %m");
yyscanner = *yyscannerp;
+ yyset_extra(&yyext, yyscanner);
+
yy_scan_string(str, yyscanner);
}
--
2.47.1
v0-0014-jsonpath-scanner-reentrant-scanner.patchtext/plain; charset=UTF-8; name=v0-0014-jsonpath-scanner-reentrant-scanner.patchDownload
From 1441e2d43496a38c722e01e5dea3ac9152fbdde9 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v0 14/15] jsonpath scanner: reentrant scanner
Note: The parser was already pure.
---
src/backend/utils/adt/jsonpath_gram.y | 2 +
src/backend/utils/adt/jsonpath_internal.h | 9 +-
src/backend/utils/adt/jsonpath_scan.l | 114 ++++++++--------------
3 files changed, 50 insertions(+), 75 deletions(-)
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index 8733a0eac66..de5a455c96d 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -60,8 +60,10 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%name-prefix="jsonpath_yy"
%parse-param {JsonPathParseResult **result}
%parse-param {struct Node *escontext}
+%parse-param {yyscan_t yyscanner}
%lex-param {JsonPathParseResult **result}
%lex-param {struct Node *escontext}
+%lex-param {yyscan_t yyscanner}
%union
{
diff --git a/src/backend/utils/adt/jsonpath_internal.h b/src/backend/utils/adt/jsonpath_internal.h
index 6cd6d8b652d..71f885475dd 100644
--- a/src/backend/utils/adt/jsonpath_internal.h
+++ b/src/backend/utils/adt/jsonpath_internal.h
@@ -22,17 +22,22 @@ typedef struct JsonPathString
int total;
} JsonPathString;
+typedef void *yyscan_t;
+
#include "utils/jsonpath.h"
#include "jsonpath_gram.h"
#define YY_DECL extern int jsonpath_yylex(YYSTYPE *yylval_param, \
JsonPathParseResult **result, \
- struct Node *escontext)
+ struct Node *escontext, \
+ yyscan_t yyscanner)
YY_DECL;
extern int jsonpath_yyparse(JsonPathParseResult **result,
- struct Node *escontext);
+ struct Node *escontext,
+ yyscan_t yyscanner);
extern void jsonpath_yyerror(JsonPathParseResult **result,
struct Node *escontext,
+ yyscan_t yyscanner,
const char *message);
#endif /* JSONPATH_INTERNAL_H */
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index f5a85de36f5..700c17712d0 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -30,18 +30,13 @@
}
%{
-static JsonPathString scanstring;
-
-/* Handles to the buffer that the lexer uses internally */
-static YY_BUFFER_STATE scanbufhandle;
-static char *scanbuf;
-static int scanbuflen;
+static JsonPathString scanstring; /* FIXME */
static void addstring(bool init, char *s, int l);
static void addchar(bool init, char c);
static enum yytokentype checkKeyword(void);
-static bool parseUnicode(char *s, int l, struct Node *escontext);
-static bool parseHexChar(char *s, struct Node *escontext);
+static bool parseUnicode(char *s, int l, struct Node *escontext, yyscan_t yyscanner);
+static bool parseHexChar(char *s, struct Node *escontext, yyscan_t yyscanner);
/* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
#undef fprintf
@@ -65,6 +60,7 @@ fprintf_to_ereport(const char *fmt, const char *msg)
%option noyywrap
%option warn
%option prefix="jsonpath_yy"
+%option reentrant
%option bison-bridge
%option noyyalloc
%option noyyrealloc
@@ -160,23 +156,23 @@ hex_fail \\x{hexdigit}{0,1}
<xnq,xq,xvq>\\v { addchar(false, '\v'); }
<xnq,xq,xvq>{unicode}+ {
- if (!parseUnicode(yytext, yyleng, escontext))
+ if (!parseUnicode(yytext, yyleng, escontext, yyscanner))
yyterminate();
}
<xnq,xq,xvq>{hex_char} {
- if (!parseHexChar(yytext, escontext))
+ if (!parseHexChar(yytext, escontext, yyscanner))
yyterminate();
}
<xnq,xq,xvq>{unicode}*{unicodefail} {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"invalid Unicode escape sequence");
yyterminate();
}
<xnq,xq,xvq>{hex_fail} {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"invalid hexadecimal character sequence");
yyterminate();
}
@@ -184,20 +180,20 @@ hex_fail \\x{hexdigit}{0,1}
<xnq,xq,xvq>{unicode}+\\ {
/* throw back the \\, and treat as unicode */
yyless(yyleng - 1);
- if (!parseUnicode(yytext, yyleng, escontext))
+ if (!parseUnicode(yytext, yyleng, escontext, yyscanner))
yyterminate();
}
<xnq,xq,xvq>\\. { addchar(false, yytext[1]); }
<xnq,xq,xvq>\\ {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"unexpected end after backslash");
yyterminate();
}
<xq,xvq><<EOF>> {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"unterminated quoted string");
yyterminate();
}
@@ -223,7 +219,7 @@ hex_fail \\x{hexdigit}{0,1}
<xc>\* { }
<xc><<EOF>> {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"unexpected end of comment");
yyterminate();
}
@@ -313,22 +309,22 @@ hex_fail \\x{hexdigit}{0,1}
}
{realfail} {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"invalid numeric literal");
yyterminate();
}
{decinteger_junk} {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"trailing junk after numeric literal");
yyterminate();
}
{decimal_junk} {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"trailing junk after numeric literal");
yyterminate();
}
{real_junk} {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"trailing junk after numeric literal");
yyterminate();
}
@@ -356,8 +352,11 @@ hex_fail \\x{hexdigit}{0,1}
void
jsonpath_yyerror(JsonPathParseResult **result, struct Node *escontext,
+ yyscan_t yyscanner,
const char *message)
{
+ struct yyguts_t * yyg = (struct yyguts_t *) yyscanner; /* needed for yytext macro */
+
/* don't overwrite escontext if it's already been set */
if (SOFT_ERROR_OCCURRED(escontext))
return;
@@ -470,44 +469,6 @@ checkKeyword()
return res;
}
-/*
- * Called before any actual parsing is done
- */
-static void
-jsonpath_scanner_init(const char *str, int slen)
-{
- if (slen <= 0)
- slen = strlen(str);
-
- /*
- * Might be left over after ereport()
- */
- yy_init_globals();
-
- /*
- * Make a scan buffer with special termination needed by flex.
- */
-
- scanbuflen = slen;
- scanbuf = palloc(slen + 2);
- memcpy(scanbuf, str, slen);
- scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
-
- BEGIN(INITIAL);
-}
-
-
-/*
- * Called after parsing is done to clean up after jsonpath_scanner_init()
- */
-static void
-jsonpath_scanner_finish(void)
-{
- yy_delete_buffer(scanbufhandle);
- pfree(scanbuf);
-}
-
/*
* Resize scanstring so that it can append string of given length.
* Reinitialize if required.
@@ -556,20 +517,27 @@ JsonPathParseResult *
parsejsonpath(const char *str, int len, struct Node *escontext)
{
JsonPathParseResult *parseresult;
+ yyscan_t scanner;
+
+ if (jsonpath_yylex_init(&scanner) != 0)
+ elog(ERROR, "yylex_init() failed: %m");
+
+ if (len <= 0)
+ len = strlen(str);
- jsonpath_scanner_init(str, len);
+ jsonpath_yy_scan_bytes(str, len, scanner);
- if (jsonpath_yyparse(&parseresult, escontext) != 0)
- jsonpath_yyerror(NULL, escontext, "invalid input"); /* shouldn't happen */
+ if (jsonpath_yyparse(&parseresult, escontext, scanner) != 0)
+ jsonpath_yyerror(NULL, escontext, scanner, "invalid input"); /* shouldn't happen */
- jsonpath_scanner_finish();
+ jsonpath_yylex_destroy(scanner);
return parseresult;
}
/* Turn hex character into integer */
static bool
-hexval(char c, int *result, struct Node *escontext)
+hexval(char c, int *result, struct Node *escontext, yyscan_t yyscanner)
{
if (c >= '0' && c <= '9')
{
@@ -586,7 +554,7 @@ hexval(char c, int *result, struct Node *escontext)
*result = c - 'A' + 0xA;
return true;
}
- jsonpath_yyerror(NULL, escontext, "invalid hexadecimal digit");
+ jsonpath_yyerror(NULL, escontext, yyscanner, "invalid hexadecimal digit");
return false;
}
@@ -666,7 +634,7 @@ addUnicode(int ch, int *hi_surrogate, struct Node *escontext)
* src/backend/utils/adt/json.c
*/
static bool
-parseUnicode(char *s, int l, struct Node *escontext)
+parseUnicode(char *s, int l, struct Node *escontext, yyscan_t yyscanner)
{
int i = 2;
int hi_surrogate = -1;
@@ -680,7 +648,7 @@ parseUnicode(char *s, int l, struct Node *escontext)
{
while (s[++i] != '}' && i < l)
{
- if (!hexval(s[i], &si, escontext))
+ if (!hexval(s[i], &si, escontext, yyscanner))
return false;
ch = (ch << 4) | si;
}
@@ -690,7 +658,7 @@ parseUnicode(char *s, int l, struct Node *escontext)
{
for (j = 0; j < 4 && i < l; j++)
{
- if (!hexval(s[i++], &si, escontext))
+ if (!hexval(s[i++], &si, escontext, yyscanner))
return false;
ch = (ch << 4) | si;
}
@@ -714,12 +682,12 @@ parseUnicode(char *s, int l, struct Node *escontext)
/* Parse sequence of hex-encoded characters */
static bool
-parseHexChar(char *s, struct Node *escontext)
+parseHexChar(char *s, struct Node *escontext, yyscan_t yyscanner)
{
int s2, s3, ch;
- if (!hexval(s[2], &s2, escontext))
+ if (!hexval(s[2], &s2, escontext, yyscanner))
return false;
- if (!hexval(s[3], &s3, escontext))
+ if (!hexval(s[3], &s3, escontext, yyscanner))
return false;
ch = (s2 << 4) | s3;
@@ -733,13 +701,13 @@ parseHexChar(char *s, struct Node *escontext)
*/
void *
-jsonpath_yyalloc(yy_size_t bytes)
+jsonpath_yyalloc(yy_size_t bytes, yyscan_t yyscanner)
{
return palloc(bytes);
}
void *
-jsonpath_yyrealloc(void *ptr, yy_size_t bytes)
+jsonpath_yyrealloc(void *ptr, yy_size_t bytes, yyscan_t yyscanner)
{
if (ptr)
return repalloc(ptr, bytes);
@@ -748,7 +716,7 @@ jsonpath_yyrealloc(void *ptr, yy_size_t bytes)
}
void
-jsonpath_yyfree(void *ptr)
+jsonpath_yyfree(void *ptr, yyscan_t yyscanner)
{
if (ptr)
pfree(ptr);
--
2.47.1
v0-0015-jsonpath-scanner-Use-flex-yyextra.patchtext/plain; charset=UTF-8; name=v0-0015-jsonpath-scanner-Use-flex-yyextra.patchDownload
From 806f4173418991462bb2f9ad029c219ab8abe3d9 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v0 15/15] jsonpath scanner: Use flex yyextra
---
src/backend/utils/adt/jsonpath_scan.l | 171 ++++++++++++++------------
1 file changed, 92 insertions(+), 79 deletions(-)
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index 700c17712d0..8ed6c7ddf63 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -30,11 +30,15 @@
}
%{
-static JsonPathString scanstring; /* FIXME */
+struct jsonpath_yy_extra_type
+{
+ JsonPathString scanstring;
+};
+#define YY_EXTRA_TYPE struct jsonpath_yy_extra_type *
-static void addstring(bool init, char *s, int l);
-static void addchar(bool init, char c);
-static enum yytokentype checkKeyword(void);
+static void addstring(bool init, char *s, int l, yyscan_t yyscanner);
+static void addchar(bool init, char c, yyscan_t yyscanner);
+static enum yytokentype checkKeyword(yyscan_t yyscanner);
static bool parseUnicode(char *s, int l, struct Node *escontext, yyscan_t yyscanner);
static bool parseHexChar(char *s, struct Node *escontext, yyscan_t yyscanner);
@@ -116,44 +120,44 @@ hex_fail \\x{hexdigit}{0,1}
%%
<xnq>{other}+ {
- addstring(false, yytext, yyleng);
+ addstring(false, yytext, yyleng, yyscanner);
}
<xnq>{blank}+ {
- yylval->str = scanstring;
+ yylval->str = yyextra->scanstring;
BEGIN INITIAL;
- return checkKeyword();
+ return checkKeyword(yyscanner);
}
<xnq>\/\* {
- yylval->str = scanstring;
+ yylval->str = yyextra->scanstring;
BEGIN xc;
}
<xnq>({special}|\") {
- yylval->str = scanstring;
+ yylval->str = yyextra->scanstring;
yyless(0);
BEGIN INITIAL;
- return checkKeyword();
+ return checkKeyword(yyscanner);
}
<xnq><<EOF>> {
- yylval->str = scanstring;
+ yylval->str = yyextra->scanstring;
BEGIN INITIAL;
- return checkKeyword();
+ return checkKeyword(yyscanner);
}
-<xnq,xq,xvq>\\b { addchar(false, '\b'); }
+<xnq,xq,xvq>\\b { addchar(false, '\b', yyscanner); }
-<xnq,xq,xvq>\\f { addchar(false, '\f'); }
+<xnq,xq,xvq>\\f { addchar(false, '\f', yyscanner); }
-<xnq,xq,xvq>\\n { addchar(false, '\n'); }
+<xnq,xq,xvq>\\n { addchar(false, '\n', yyscanner); }
-<xnq,xq,xvq>\\r { addchar(false, '\r'); }
+<xnq,xq,xvq>\\r { addchar(false, '\r', yyscanner); }
-<xnq,xq,xvq>\\t { addchar(false, '\t'); }
+<xnq,xq,xvq>\\t { addchar(false, '\t', yyscanner); }
-<xnq,xq,xvq>\\v { addchar(false, '\v'); }
+<xnq,xq,xvq>\\v { addchar(false, '\v', yyscanner); }
<xnq,xq,xvq>{unicode}+ {
if (!parseUnicode(yytext, yyleng, escontext, yyscanner))
@@ -184,7 +188,7 @@ hex_fail \\x{hexdigit}{0,1}
yyterminate();
}
-<xnq,xq,xvq>\\. { addchar(false, yytext[1]); }
+<xnq,xq,xvq>\\. { addchar(false, yytext[1], yyscanner); }
<xnq,xq,xvq>\\ {
jsonpath_yyerror(NULL, escontext, yyscanner,
@@ -199,18 +203,18 @@ hex_fail \\x{hexdigit}{0,1}
}
<xq>\" {
- yylval->str = scanstring;
+ yylval->str = yyextra->scanstring;
BEGIN INITIAL;
return STRING_P;
}
<xvq>\" {
- yylval->str = scanstring;
+ yylval->str = yyextra->scanstring;
BEGIN INITIAL;
return VARIABLE_P;
}
-<xq,xvq>[^\\\"]+ { addstring(false, yytext, yyleng); }
+<xq,xvq>[^\\\"]+ { addstring(false, yytext, yyleng, yyscanner); }
<xc>\*\/ { BEGIN INITIAL; }
@@ -246,14 +250,14 @@ hex_fail \\x{hexdigit}{0,1}
\> { return GREATER_P; }
\${other}+ {
- addstring(true, yytext + 1, yyleng - 1);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext + 1, yyleng - 1, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return VARIABLE_P;
}
\$\" {
- addchar(true, '\0');
+ addchar(true, '\0', yyscanner);
BEGIN xvq;
}
@@ -262,49 +266,49 @@ hex_fail \\x{hexdigit}{0,1}
{blank}+ { /* ignore */ }
\/\* {
- addchar(true, '\0');
+ addchar(true, '\0', yyscanner);
BEGIN xc;
}
{real} {
- addstring(true, yytext, yyleng);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext, yyleng, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return NUMERIC_P;
}
{decimal} {
- addstring(true, yytext, yyleng);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext, yyleng, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return NUMERIC_P;
}
{decinteger} {
- addstring(true, yytext, yyleng);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext, yyleng, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return INT_P;
}
{hexinteger} {
- addstring(true, yytext, yyleng);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext, yyleng, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return INT_P;
}
{octinteger} {
- addstring(true, yytext, yyleng);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext, yyleng, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return INT_P;
}
{bininteger} {
- addstring(true, yytext, yyleng);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext, yyleng, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return INT_P;
}
@@ -329,18 +333,18 @@ hex_fail \\x{hexdigit}{0,1}
yyterminate();
}
\" {
- addchar(true, '\0');
+ addchar(true, '\0', yyscanner);
BEGIN xq;
}
\\ {
yyless(0);
- addchar(true, '\0');
+ addchar(true, '\0', yyscanner);
BEGIN xnq;
}
{other}+ {
- addstring(true, yytext, yyleng);
+ addstring(true, yytext, yyleng, yyscanner);
BEGIN xnq;
}
@@ -350,6 +354,10 @@ hex_fail \\x{hexdigit}{0,1}
/* LCOV_EXCL_STOP */
+/* see scan.l */
+#undef yyextra
+#define yyextra (((struct yyguts_t *) yyscanner)->yyextra_r)
+
void
jsonpath_yyerror(JsonPathParseResult **result, struct Node *escontext,
yyscan_t yyscanner,
@@ -426,9 +434,11 @@ static const JsonPathKeyword keywords[] = {
{ 12,false, TIMESTAMP_TZ_P, "timestamp_tz"},
};
-/* Check if current scanstring value is a keyword */
+/*
+ * Check if current scanstring value is a keyword
+ */
static enum yytokentype
-checkKeyword()
+checkKeyword(yyscan_t yyscanner)
{
int res = IDENT_P;
int diff;
@@ -436,18 +446,18 @@ checkKeyword()
*StopHigh = keywords + lengthof(keywords),
*StopMiddle;
- if (scanstring.len > keywords[lengthof(keywords) - 1].len)
+ if (yyextra->scanstring.len > keywords[lengthof(keywords) - 1].len)
return res;
while (StopLow < StopHigh)
{
StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
- if (StopMiddle->len == scanstring.len)
- diff = pg_strncasecmp(StopMiddle->keyword, scanstring.val,
- scanstring.len);
+ if (StopMiddle->len == yyextra->scanstring.len)
+ diff = pg_strncasecmp(StopMiddle->keyword, yyextra->scanstring.val,
+ yyextra->scanstring.len);
else
- diff = StopMiddle->len - scanstring.len;
+ diff = StopMiddle->len - yyextra->scanstring.len;
if (diff < 0)
StopLow = StopMiddle + 1;
@@ -456,8 +466,8 @@ checkKeyword()
else
{
if (StopMiddle->lowercase)
- diff = strncmp(StopMiddle->keyword, scanstring.val,
- scanstring.len);
+ diff = strncmp(StopMiddle->keyword, yyextra->scanstring.val,
+ yyextra->scanstring.len);
if (diff == 0)
res = StopMiddle->val;
@@ -474,42 +484,42 @@ checkKeyword()
* Reinitialize if required.
*/
static void
-resizeString(bool init, int appendLen)
+resizeString(bool init, int appendLen, yyscan_t yyscanner)
{
if (init)
{
- scanstring.total = Max(32, appendLen);
- scanstring.val = (char *) palloc(scanstring.total);
- scanstring.len = 0;
+ yyextra->scanstring.total = Max(32, appendLen);
+ yyextra->scanstring.val = (char *) palloc(yyextra->scanstring.total);
+ yyextra->scanstring.len = 0;
}
else
{
- if (scanstring.len + appendLen >= scanstring.total)
+ if (yyextra->scanstring.len + appendLen >= yyextra->scanstring.total)
{
- while (scanstring.len + appendLen >= scanstring.total)
- scanstring.total *= 2;
- scanstring.val = repalloc(scanstring.val, scanstring.total);
+ while (yyextra->scanstring.len + appendLen >= yyextra->scanstring.total)
+ yyextra->scanstring.total *= 2;
+ yyextra->scanstring.val = repalloc(yyextra->scanstring.val, yyextra->scanstring.total);
}
}
}
/* Add set of bytes at "s" of length "l" to scanstring */
static void
-addstring(bool init, char *s, int l)
+addstring(bool init, char *s, int l, yyscan_t yyscanner)
{
- resizeString(init, l + 1);
- memcpy(scanstring.val + scanstring.len, s, l);
- scanstring.len += l;
+ resizeString(init, l + 1, yyscanner);
+ memcpy(yyextra->scanstring.val + yyextra->scanstring.len, s, l);
+ yyextra->scanstring.len += l;
}
/* Add single byte "c" to scanstring */
static void
-addchar(bool init, char c)
+addchar(bool init, char c, yyscan_t yyscanner)
{
- resizeString(init, 1);
- scanstring.val[scanstring.len] = c;
+ resizeString(init, 1, yyscanner);
+ yyextra->scanstring.val[yyextra->scanstring.len] = c;
if (c != '\0')
- scanstring.len++;
+ yyextra->scanstring.len++;
}
/* Interface to jsonpath parser */
@@ -518,10 +528,13 @@ parsejsonpath(const char *str, int len, struct Node *escontext)
{
JsonPathParseResult *parseresult;
yyscan_t scanner;
+ struct jsonpath_yy_extra_type yyext;
if (jsonpath_yylex_init(&scanner) != 0)
elog(ERROR, "yylex_init() failed: %m");
+ yyset_extra(&yyext, scanner);
+
if (len <= 0)
len = strlen(str);
@@ -560,7 +573,7 @@ hexval(char c, int *result, struct Node *escontext, yyscan_t yyscanner)
/* Add given unicode character to scanstring */
static bool
-addUnicodeChar(int ch, struct Node *escontext)
+addUnicodeChar(int ch, struct Node *escontext, yyscan_t yyscanner)
{
if (ch == 0)
{
@@ -586,14 +599,14 @@ addUnicodeChar(int ch, struct Node *escontext)
ereturn(escontext, false,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("could not convert Unicode to server encoding")));
- addstring(false, cbuf, strlen(cbuf));
+ addstring(false, cbuf, strlen(cbuf), yyscanner);
}
return true;
}
/* Add unicode character, processing any surrogate pairs */
static bool
-addUnicode(int ch, int *hi_surrogate, struct Node *escontext)
+addUnicode(int ch, int *hi_surrogate, struct Node *escontext, yyscan_t yyscanner)
{
if (is_utf16_surrogate_first(ch))
{
@@ -626,7 +639,7 @@ addUnicode(int ch, int *hi_surrogate, struct Node *escontext)
"surrogate.")));
}
- return addUnicodeChar(ch, escontext);
+ return addUnicodeChar(ch, escontext, yyscanner);
}
/*
@@ -664,7 +677,7 @@ parseUnicode(char *s, int l, struct Node *escontext, yyscan_t yyscanner)
}
}
- if (! addUnicode(ch, &hi_surrogate, escontext))
+ if (! addUnicode(ch, &hi_surrogate, escontext, yyscanner))
return false;
}
@@ -692,7 +705,7 @@ parseHexChar(char *s, struct Node *escontext, yyscan_t yyscanner)
ch = (s2 << 4) | s3;
- return addUnicodeChar(ch, escontext);
+ return addUnicodeChar(ch, escontext, yyscanner);
}
/*
--
2.47.1
On 02.12.24 10:46, Peter Eisentraut wrote:
This patch series changes several parsers in the backend and contrib
modules to use bison pure parsers and flex reentrant scanners. This is
ultimately toward thread-safety, but I think it's also just nicer in
general, and it might also fix a few possible small memory leaks.
I did a bit more work on this, so here is an updated patch set.
Not done yet:
* src/backend/utils/misc/guc-file.l
* src/pl/plpgsql/src/pl_gram.y
I converted both of these scanners to reentrant, but I haven't done the
plpgsql parser yet.
Not relevant for backend thread-safety:
* src/backend/bootstrap/It might make sense to eventually covert that one as well, just so that
the APIs are kept similar. But that could be for later.
I have done this one.
I'll leave it at this for now and wait for some reviews.
Attachments:
v1-0001-cube-pure-parser-and-reentrant-scanner.patchtext/plain; charset=UTF-8; name=v1-0001-cube-pure-parser-and-reentrant-scanner.patchDownload
From b50a844397703d93bf8ed8806860db25280148cf Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v1 01/19] cube: pure parser and reentrant scanner
Use the flex %option reentrant and the bison option %pure-parser to
make the generated scanner and parser pure, reentrant, and
thread-safe.
(There are still some issues in the surrounding integration, see
FIXMEs.)
---
contrib/cube/cube.c | 7 +++---
contrib/cube/cubedata.h | 15 ++++++++----
contrib/cube/cubeparse.y | 15 +++++-------
contrib/cube/cubescan.l | 51 +++++++++++++++++++---------------------
4 files changed, 45 insertions(+), 43 deletions(-)
diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c
index 1fc447511a1..bf8fc489dca 100644
--- a/contrib/cube/cube.c
+++ b/contrib/cube/cube.c
@@ -120,13 +120,14 @@ cube_in(PG_FUNCTION_ARGS)
char *str = PG_GETARG_CSTRING(0);
NDBOX *result;
Size scanbuflen;
+ yyscan_t scanner;
- cube_scanner_init(str, &scanbuflen);
+ cube_scanner_init(str, &scanbuflen, &scanner);
- cube_yyparse(&result, scanbuflen, fcinfo->context);
+ cube_yyparse(&result, scanbuflen, fcinfo->context, scanner);
/* We might as well run this even on failure. */
- cube_scanner_finish();
+ cube_scanner_finish(scanner);
PG_RETURN_NDBOX_P(result);
}
diff --git a/contrib/cube/cubedata.h b/contrib/cube/cubedata.h
index 96fa41a04e7..8bfcc6e99a2 100644
--- a/contrib/cube/cubedata.h
+++ b/contrib/cube/cubedata.h
@@ -59,14 +59,21 @@ typedef struct NDBOX
#define CubeKNNDistanceEuclid 17 /* <-> */
#define CubeKNNDistanceChebyshev 18 /* <=> */
+/* for cubescan.l and cubeparse.y */
+/* All grammar constructs return strings */
+#define YYSTYPE char *
+typedef void *yyscan_t;
+
/* in cubescan.l */
-extern int cube_yylex(void);
+extern int cube_yylex(YYSTYPE *yylval_param, yyscan_t yyscanner);
extern void cube_yyerror(NDBOX **result, Size scanbuflen,
struct Node *escontext,
+ yyscan_t yyscanner,
const char *message);
-extern void cube_scanner_init(const char *str, Size *scanbuflen);
-extern void cube_scanner_finish(void);
+extern void cube_scanner_init(const char *str, Size *scanbuflen, yyscan_t *yyscannerp);
+extern void cube_scanner_finish(yyscan_t yyscanner);
/* in cubeparse.y */
extern int cube_yyparse(NDBOX **result, Size scanbuflen,
- struct Node *escontext);
+ struct Node *escontext,
+ yyscan_t yyscanner);
diff --git a/contrib/cube/cubeparse.y b/contrib/cube/cubeparse.y
index 52622875cbb..a6b7e70630d 100644
--- a/contrib/cube/cubeparse.y
+++ b/contrib/cube/cubeparse.y
@@ -7,19 +7,11 @@
#include "postgres.h"
#include "cubedata.h"
+#include "cubeparse.h" /* must be after cubedata.h for YYSTYPE and NDBOX */
#include "nodes/miscnodes.h"
#include "utils/float.h"
#include "varatt.h"
-/* All grammar constructs return strings */
-#define YYSTYPE char *
-
-#include "cubeparse.h"
-
-/* silence -Wmissing-variable-declarations */
-extern int cube_yychar;
-extern int cube_yynerrs;
-
/*
* Bison doesn't allocate anything that needs to live across parser calls,
* so we can easily have it use palloc instead of malloc. This prevents
@@ -40,6 +32,9 @@ static bool write_point_as_box(int dim, char *str,
%parse-param {NDBOX **result}
%parse-param {Size scanbuflen}
%parse-param {struct Node *escontext}
+%parse-param {yyscan_t yyscanner}
+%lex-param {yyscan_t yyscanner}
+%pure-parser
%expect 0
%name-prefix="cube_yy"
@@ -75,6 +70,8 @@ box: O_BRACKET paren_list COMMA paren_list C_BRACKET
if (!write_box(dim, $2, $4, result, escontext))
YYABORT;
+
+ (void) yynerrs; /* suppress compiler warning */
}
| paren_list COMMA paren_list
diff --git a/contrib/cube/cubescan.l b/contrib/cube/cubescan.l
index a30fbfc3111..09109675711 100644
--- a/contrib/cube/cubescan.l
+++ b/contrib/cube/cubescan.l
@@ -6,13 +6,8 @@
#include "postgres.h"
-/*
- * NB: include cubeparse.h only AFTER defining YYSTYPE (to match cubeparse.y)
- * and cubedata.h for NDBOX.
- */
#include "cubedata.h"
-#define YYSTYPE char *
-#include "cubeparse.h"
+#include "cubeparse.h" /* must be after cubedata.h for YYSTYPE and NDBOX */
}
%{
@@ -32,10 +27,11 @@ fprintf_to_ereport(const char *fmt, const char *msg)
}
/* Handles to the buffer that the lexer uses internally */
-static YY_BUFFER_STATE scanbufhandle;
-static char *scanbuf;
+static char *scanbuf; // FIXME
%}
+%option reentrant
+%option bison-bridge
%option 8bit
%option never-interactive
%option nodefault
@@ -55,14 +51,14 @@ NaN [nN][aA][nN]
%%
-{float} cube_yylval = yytext; return CUBEFLOAT;
-{infinity} cube_yylval = yytext; return CUBEFLOAT;
-{NaN} cube_yylval = yytext; return CUBEFLOAT;
-\[ cube_yylval = "("; return O_BRACKET;
-\] cube_yylval = ")"; return C_BRACKET;
-\( cube_yylval = "("; return O_PAREN;
-\) cube_yylval = ")"; return C_PAREN;
-\, cube_yylval = ","; return COMMA;
+{float} *yylval = yytext; return CUBEFLOAT;
+{infinity} *yylval = yytext; return CUBEFLOAT;
+{NaN} *yylval = yytext; return CUBEFLOAT;
+\[ *yylval = "("; return O_BRACKET;
+\] *yylval = ")"; return C_BRACKET;
+\( *yylval = "("; return O_PAREN;
+\) *yylval = ")"; return C_PAREN;
+\, *yylval = ","; return COMMA;
[ \t\n\r\f\v]+ /* discard spaces */
. return yytext[0]; /* alert parser of the garbage */
@@ -74,8 +70,11 @@ NaN [nN][aA][nN]
void
cube_yyerror(NDBOX **result, Size scanbuflen,
struct Node *escontext,
+ yyscan_t yyscanner,
const char *message)
{
+ struct yyguts_t * yyg = (struct yyguts_t *) yyscanner; /* needed for yytext macro */
+
if (*yytext == YY_END_OF_BUFFER_CHAR)
{
errsave(escontext,
@@ -99,15 +98,15 @@ cube_yyerror(NDBOX **result, Size scanbuflen,
* Called before any actual parsing is done
*/
void
-cube_scanner_init(const char *str, Size *scanbuflen)
+cube_scanner_init(const char *str, Size *scanbuflen, yyscan_t *yyscannerp)
{
Size slen = strlen(str);
+ yyscan_t yyscanner;
- /*
- * Might be left over after ereport()
- */
- if (YY_CURRENT_BUFFER)
- yy_delete_buffer(YY_CURRENT_BUFFER);
+ if (yylex_init(yyscannerp) != 0)
+ elog(ERROR, "yylex_init() failed: %m");
+
+ yyscanner = *yyscannerp;
/*
* Make a scan buffer with special termination needed by flex.
@@ -116,9 +115,7 @@ cube_scanner_init(const char *str, Size *scanbuflen)
scanbuf = palloc(slen + 2);
memcpy(scanbuf, str, slen);
scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
-
- BEGIN(INITIAL);
+ yy_scan_buffer(scanbuf, slen + 2, yyscanner);
}
@@ -126,8 +123,8 @@ cube_scanner_init(const char *str, Size *scanbuflen)
* Called after parsing is done to clean up after cube_scanner_init()
*/
void
-cube_scanner_finish(void)
+cube_scanner_finish(yyscan_t yyscanner)
{
- yy_delete_buffer(scanbufhandle);
+ yylex_destroy(yyscanner);
pfree(scanbuf);
}
base-commit: 39240bcad56dc51a7896d04a1e066efcf988b58f
--
2.47.1
v1-0002-cube-Use-palloc-instead-of-malloc-for-flex.patchtext/plain; charset=UTF-8; name=v1-0002-cube-Use-palloc-instead-of-malloc-for-flex.patchDownload
From bb316e840df92c63f8ac040059b1ac9c3a03044c Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v1 02/19] cube: Use palloc() instead of malloc() for flex
Make the generated scanner use palloc() etc. instead of malloc() etc.
Previously, we only used palloc() for the buffer, but flex would still
use malloc() for its internal structures. As a result, there could be
some small memory leaks in case of uncaught errors. (We do catch
normal syntax errors as soft errors.) Now, all the memory is under
palloc() control, so there are no more such issues.
---
contrib/cube/cubescan.l | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/contrib/cube/cubescan.l b/contrib/cube/cubescan.l
index 09109675711..b741e77d7e1 100644
--- a/contrib/cube/cubescan.l
+++ b/contrib/cube/cubescan.l
@@ -38,6 +38,9 @@ static char *scanbuf; // FIXME
%option noinput
%option nounput
%option noyywrap
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
%option warn
%option prefix="cube_yy"
@@ -128,3 +131,30 @@ cube_scanner_finish(yyscan_t yyscanner)
yylex_destroy(yyscanner);
pfree(scanbuf);
}
+
+/*
+ * Interface functions to make flex use palloc() instead of malloc().
+ * It'd be better to make these static, but flex insists otherwise.
+ */
+
+void *
+yyalloc(yy_size_t size, yyscan_t yyscanner)
+{
+ return palloc(size);
+}
+
+void *
+yyrealloc(void *ptr, yy_size_t size, yyscan_t yyscanner)
+{
+ if (ptr)
+ return repalloc(ptr, size);
+ else
+ return palloc(size);
+}
+
+void
+yyfree(void *ptr, yyscan_t yyscanner)
+{
+ if (ptr)
+ pfree(ptr);
+}
--
2.47.1
v1-0003-cube-Simplify-flex-scan-buffer-management.patchtext/plain; charset=UTF-8; name=v1-0003-cube-Simplify-flex-scan-buffer-management.patchDownload
From 538a96c0cc3fd4d3454be578e32a7cf9afa6b30a Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v1 03/19] cube: Simplify flex scan buffer management
Instead of constructing the buffer from pieces and then using
yy_scan_buffer(), we can just use yy_scan_string(), which does the
same thing internally. (Actually, we use yy_scan_bytes() here because
we already have the length.)
The previous code was necessary because we allocated the buffer with
palloc() and the rest of the state was handled by malloc(). But this
is no longer the case; everything is under palloc() now.
XXX We could even get rid of the yylex_destroy() call and just let the
memory context cleanup handle everything.
---
contrib/cube/cubescan.l | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/contrib/cube/cubescan.l b/contrib/cube/cubescan.l
index b741e77d7e1..eed324d6e3b 100644
--- a/contrib/cube/cubescan.l
+++ b/contrib/cube/cubescan.l
@@ -25,9 +25,6 @@ fprintf_to_ereport(const char *fmt, const char *msg)
{
ereport(ERROR, (errmsg_internal("%s", msg)));
}
-
-/* Handles to the buffer that the lexer uses internally */
-static char *scanbuf; // FIXME
%}
%option reentrant
@@ -111,14 +108,8 @@ cube_scanner_init(const char *str, Size *scanbuflen, yyscan_t *yyscannerp)
yyscanner = *yyscannerp;
- /*
- * Make a scan buffer with special termination needed by flex.
- */
+ yy_scan_bytes(str, slen, yyscanner);
*scanbuflen = slen;
- scanbuf = palloc(slen + 2);
- memcpy(scanbuf, str, slen);
- scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- yy_scan_buffer(scanbuf, slen + 2, yyscanner);
}
@@ -129,7 +120,6 @@ void
cube_scanner_finish(yyscan_t yyscanner)
{
yylex_destroy(yyscanner);
- pfree(scanbuf);
}
/*
--
2.47.1
v1-0004-seg-pure-parser-and-reentrant-scanner.patchtext/plain; charset=UTF-8; name=v1-0004-seg-pure-parser-and-reentrant-scanner.patchDownload
From 2ddc3321d6357d6533177ba5c086ebdf879102dd Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v1 04/19] seg: pure parser and reentrant scanner
---
contrib/seg/seg.c | 9 ++++----
contrib/seg/segdata.h | 13 ++++++++----
contrib/seg/segparse.y | 9 ++++----
contrib/seg/segscan.l | 47 ++++++++++++++++++++----------------------
4 files changed, 41 insertions(+), 37 deletions(-)
diff --git a/contrib/seg/seg.c b/contrib/seg/seg.c
index 7f9fc24eb4b..fd4216edc5d 100644
--- a/contrib/seg/seg.c
+++ b/contrib/seg/seg.c
@@ -105,13 +105,14 @@ seg_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
SEG *result = palloc(sizeof(SEG));
+ yyscan_t scanner;
- seg_scanner_init(str);
+ seg_scanner_init(str, &scanner);
- if (seg_yyparse(result, fcinfo->context) != 0)
- seg_yyerror(result, fcinfo->context, "bogus input");
+ if (seg_yyparse(result, fcinfo->context, scanner) != 0)
+ seg_yyerror(result, fcinfo->context, scanner, "bogus input");
- seg_scanner_finish();
+ seg_scanner_finish(scanner);
PG_RETURN_POINTER(result);
}
diff --git a/contrib/seg/segdata.h b/contrib/seg/segdata.h
index 3d6e3e3f245..7bc7c83dca3 100644
--- a/contrib/seg/segdata.h
+++ b/contrib/seg/segdata.h
@@ -14,12 +14,17 @@ typedef struct SEG
/* in seg.c */
extern int significant_digits(const char *s);
+/* for segscan.l and segparse.y */
+union YYSTYPE;
+typedef void *yyscan_t;
+
/* in segscan.l */
-extern int seg_yylex(void);
+extern int seg_yylex(union YYSTYPE *yylval_param, yyscan_t yyscanner);
extern void seg_yyerror(SEG *result, struct Node *escontext,
+ yyscan_t yyscanner,
const char *message);
-extern void seg_scanner_init(const char *str);
-extern void seg_scanner_finish(void);
+extern void seg_scanner_init(const char *str, yyscan_t *yyscannerp);
+extern void seg_scanner_finish(yyscan_t yyscanner);
/* in segparse.y */
-extern int seg_yyparse(SEG *result, struct Node *escontext);
+extern int seg_yyparse(SEG *result, struct Node *escontext, yyscan_t yyscanner);
diff --git a/contrib/seg/segparse.y b/contrib/seg/segparse.y
index 3e4aa31cada..0358ddb182c 100644
--- a/contrib/seg/segparse.y
+++ b/contrib/seg/segparse.y
@@ -14,10 +14,6 @@
#include "segdata.h"
#include "segparse.h"
-/* silence -Wmissing-variable-declarations */
-extern int seg_yychar;
-extern int seg_yynerrs;
-
/*
* Bison doesn't allocate anything that needs to live across parser calls,
* so we can easily have it use palloc instead of malloc. This prevents
@@ -35,6 +31,9 @@ static int sig_digits(const char *value);
/* BISON Declarations */
%parse-param {SEG *result}
%parse-param {struct Node *escontext}
+%parse-param {yyscan_t yyscanner}
+%lex-param {yyscan_t yyscanner}
+%pure-parser
%expect 0
%name-prefix="seg_yy"
@@ -72,6 +71,8 @@ range: boundary PLUMIN deviation
result->u_sigd = Max(sig_digits(strbuf), Max($1.sigd, $3.sigd));
result->l_ext = '\0';
result->u_ext = '\0';
+
+ (void) yynerrs; /* suppress compiler warning */
}
| boundary RANGE boundary
diff --git a/contrib/seg/segscan.l b/contrib/seg/segscan.l
index 4ad529eccc4..35c3a11ac14 100644
--- a/contrib/seg/segscan.l
+++ b/contrib/seg/segscan.l
@@ -6,12 +6,8 @@
#include "nodes/miscnodes.h"
-/*
- * NB: include segparse.h only AFTER including segdata.h, because segdata.h
- * contains the definition for SEG.
- */
#include "segdata.h"
-#include "segparse.h"
+#include "segparse.h" /* must be after segdata.h SEG */
}
%{
@@ -31,10 +27,11 @@ fprintf_to_ereport(const char *fmt, const char *msg)
}
/* Handles to the buffer that the lexer uses internally */
-static YY_BUFFER_STATE scanbufhandle;
-static char *scanbuf;
+static char *scanbuf; // FIXME
%}
+%option reentrant
+%option bison-bridge
%option 8bit
%option never-interactive
%option nodefault
@@ -53,12 +50,12 @@ float ({integer}|{real})([eE]{integer})?
%%
-{range} seg_yylval.text = yytext; return RANGE;
-{plumin} seg_yylval.text = yytext; return PLUMIN;
-{float} seg_yylval.text = yytext; return SEGFLOAT;
-\< seg_yylval.text = "<"; return EXTENSION;
-\> seg_yylval.text = ">"; return EXTENSION;
-\~ seg_yylval.text = "~"; return EXTENSION;
+{range} yylval->text = yytext; return RANGE;
+{plumin} yylval->text = yytext; return PLUMIN;
+{float} yylval->text = yytext; return SEGFLOAT;
+\< yylval->text = "<"; return EXTENSION;
+\> yylval->text = ">"; return EXTENSION;
+\~ yylval->text = "~"; return EXTENSION;
[ \t\n\r\f\v]+ /* discard spaces */
. return yytext[0]; /* alert parser of the garbage */
@@ -67,8 +64,10 @@ float ({integer}|{real})([eE]{integer})?
/* LCOV_EXCL_STOP */
void
-seg_yyerror(SEG *result, struct Node *escontext, const char *message)
+seg_yyerror(SEG *result, struct Node *escontext, yyscan_t yyscanner, const char *message)
{
+ struct yyguts_t * yyg = (struct yyguts_t *) yyscanner; /* needed for yytext macro */
+
/* if we already reported an error, don't overwrite it */
if (SOFT_ERROR_OCCURRED(escontext))
return;
@@ -96,15 +95,15 @@ seg_yyerror(SEG *result, struct Node *escontext, const char *message)
* Called before any actual parsing is done
*/
void
-seg_scanner_init(const char *str)
+seg_scanner_init(const char *str, yyscan_t *yyscannerp)
{
Size slen = strlen(str);
+ yyscan_t yyscanner;
- /*
- * Might be left over after ereport()
- */
- if (YY_CURRENT_BUFFER)
- yy_delete_buffer(YY_CURRENT_BUFFER);
+ if (yylex_init(yyscannerp) != 0)
+ elog(ERROR, "yylex_init() failed: %m");
+
+ yyscanner = *yyscannerp;
/*
* Make a scan buffer with special termination needed by flex.
@@ -112,9 +111,7 @@ seg_scanner_init(const char *str)
scanbuf = palloc(slen + 2);
memcpy(scanbuf, str, slen);
scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
-
- BEGIN(INITIAL);
+ yy_scan_buffer(scanbuf, slen + 2, yyscanner);
}
@@ -122,8 +119,8 @@ seg_scanner_init(const char *str)
* Called after parsing is done to clean up after seg_scanner_init()
*/
void
-seg_scanner_finish(void)
+seg_scanner_finish(yyscan_t yyscanner)
{
- yy_delete_buffer(scanbufhandle);
+ yylex_destroy(yyscanner);
pfree(scanbuf);
}
--
2.47.1
v1-0018-guc-reentrant-scanner.patchtext/plain; charset=UTF-8; name=v1-0018-guc-reentrant-scanner.patchDownload
From 0543cfce12ee8573c8e57da599626ca810bc2c3e Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Tue, 3 Dec 2024 23:14:01 +0100
Subject: [PATCH v1 18/19] guc: reentrant scanner
---
src/backend/utils/misc/guc-file.l | 30 +++++++++++++++++++-----------
1 file changed, 19 insertions(+), 11 deletions(-)
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index 73be80076da..a0381d9e2a1 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -57,6 +57,7 @@ static int GUC_flex_fatal(const char *msg);
%}
+%option reentrant
%option 8bit
%option never-interactive
%option nodefault
@@ -353,6 +354,8 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
unsigned int save_ConfigFileLineno = ConfigFileLineno;
sigjmp_buf *save_GUC_flex_fatal_jmp = GUC_flex_fatal_jmp;
sigjmp_buf flex_fatal_jmp;
+ yyscan_t scanner;
+ struct yyguts_t * yyg; /* needed for yytext macro */
volatile YY_BUFFER_STATE lex_buffer = NULL;
int errorcount;
int token;
@@ -381,11 +384,15 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
ConfigFileLineno = 1;
errorcount = 0;
- lex_buffer = yy_create_buffer(fp, YY_BUF_SIZE);
- yy_switch_to_buffer(lex_buffer);
+ if (yylex_init(&scanner) != 0)
+ elog(elevel, "yylex_init() failed: %m");
+ yyg = (struct yyguts_t *) scanner;
+
+ lex_buffer = yy_create_buffer(fp, YY_BUF_SIZE, scanner);
+ yy_switch_to_buffer(lex_buffer, scanner);
/* This loop iterates once per logical line */
- while ((token = yylex()))
+ while ((token = yylex(scanner)))
{
char *opt_name = NULL;
char *opt_value = NULL;
@@ -400,9 +407,9 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
opt_name = pstrdup(yytext);
/* next we have an optional equal sign; discard if present */
- token = yylex();
+ token = yylex(scanner);
if (token == GUC_EQUALS)
- token = yylex();
+ token = yylex(scanner);
/* now we must have the option value */
if (token != GUC_ID &&
@@ -417,7 +424,7 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
opt_value = pstrdup(yytext);
/* now we'd like an end of line, or possibly EOF */
- token = yylex();
+ token = yylex(scanner);
if (token != GUC_EOL)
{
if (token != 0)
@@ -438,7 +445,7 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
depth + 1, elevel,
head_p, tail_p))
OK = false;
- yy_switch_to_buffer(lex_buffer);
+ yy_switch_to_buffer(lex_buffer, scanner);
pfree(opt_name);
pfree(opt_value);
}
@@ -453,7 +460,7 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
depth + 1, elevel,
head_p, tail_p))
OK = false;
- yy_switch_to_buffer(lex_buffer);
+ yy_switch_to_buffer(lex_buffer, scanner);
pfree(opt_name);
pfree(opt_value);
}
@@ -468,7 +475,7 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
depth + 1, elevel,
head_p, tail_p))
OK = false;
- yy_switch_to_buffer(lex_buffer);
+ yy_switch_to_buffer(lex_buffer, scanner);
pfree(opt_name);
pfree(opt_value);
}
@@ -545,14 +552,15 @@ parse_error:
/* resync to next end-of-line or EOF */
while (token != GUC_EOL && token != 0)
- token = yylex();
+ token = yylex(scanner);
/* break out of loop on EOF */
if (token == 0)
break;
}
cleanup:
- yy_delete_buffer(lex_buffer);
+ yy_delete_buffer(lex_buffer, scanner);
+ yylex_destroy(scanner);
/* Each recursion level must save and restore these static variables. */
ConfigFileLineno = save_ConfigFileLineno;
GUC_flex_fatal_jmp = save_GUC_flex_fatal_jmp;
--
2.47.1
v1-0019-plpgsql-reentrant-scanner.patchtext/plain; charset=UTF-8; name=v1-0019-plpgsql-reentrant-scanner.patchDownload
From 2cde4c4b10907d0e51a9a6fafc0475feb75cc1b2 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Sat, 7 Dec 2024 00:09:08 +0100
Subject: [PATCH v1 19/19] plpgsql: reentrant scanner
---
src/pl/plpgsql/src/pl_comp.c | 18 +-
src/pl/plpgsql/src/pl_gram.y | 420 +++++++++++++++++---------------
src/pl/plpgsql/src/pl_scanner.c | 38 +--
src/pl/plpgsql/src/plpgsql.h | 16 +-
4 files changed, 256 insertions(+), 236 deletions(-)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 6255a86d75b..917daebef06 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -269,6 +269,7 @@ do_compile(FunctionCallInfo fcinfo,
Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
bool is_dml_trigger = CALLED_AS_TRIGGER(fcinfo);
bool is_event_trigger = CALLED_AS_EVENT_TRIGGER(fcinfo);
+ yyscan_t scanner;
Datum prosrcdatum;
char *proc_source;
HeapTuple typeTup;
@@ -292,11 +293,11 @@ do_compile(FunctionCallInfo fcinfo,
/*
* Setup the scanner input and error info. We assume that this function
* cannot be invoked recursively, so there's no need to save and restore
- * the static variables used here.
+ * the static variables used here. XXX
*/
prosrcdatum = SysCacheGetAttrNotNull(PROCOID, procTup, Anum_pg_proc_prosrc);
proc_source = TextDatumGetCString(prosrcdatum);
- plpgsql_scanner_init(proc_source);
+ scanner = plpgsql_scanner_init(proc_source);
plpgsql_error_funcname = pstrdup(NameStr(procStruct->proname));
@@ -779,12 +780,12 @@ do_compile(FunctionCallInfo fcinfo,
/*
* Now parse the function's text
*/
- parse_rc = plpgsql_yyparse();
+ parse_rc = plpgsql_yyparse(scanner);
if (parse_rc != 0)
elog(ERROR, "plpgsql parser returned %d", parse_rc);
function->action = plpgsql_parse_result;
- plpgsql_scanner_finish();
+ plpgsql_scanner_finish(scanner);
pfree(proc_source);
/*
@@ -841,6 +842,7 @@ do_compile(FunctionCallInfo fcinfo,
PLpgSQL_function *
plpgsql_compile_inline(char *proc_source)
{
+ yyscan_t scanner;
char *func_name = "inline_code_block";
PLpgSQL_function *function;
ErrorContextCallback plerrcontext;
@@ -851,9 +853,9 @@ plpgsql_compile_inline(char *proc_source)
/*
* Setup the scanner input and error info. We assume that this function
* cannot be invoked recursively, so there's no need to save and restore
- * the static variables used here.
+ * the static variables used here. XXX
*/
- plpgsql_scanner_init(proc_source);
+ scanner = plpgsql_scanner_init(proc_source);
plpgsql_error_funcname = func_name;
@@ -935,12 +937,12 @@ plpgsql_compile_inline(char *proc_source)
/*
* Now parse the function's text
*/
- parse_rc = plpgsql_yyparse();
+ parse_rc = plpgsql_yyparse(scanner);
if (parse_rc != 0)
elog(ERROR, "plpgsql parser returned %d", parse_rc);
function->action = plpgsql_parse_result;
- plpgsql_scanner_finish();
+ plpgsql_scanner_finish(scanner);
/*
* If it returns VOID (always true at the moment), we allow control to
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 8182ce28aa1..4d09fa89d99 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -63,7 +63,7 @@ static bool tok_is_keyword(int token, union YYSTYPE *lval,
int kw_token, const char *kw_str);
static void word_is_not_variable(PLword *word, int location);
static void cword_is_not_variable(PLcword *cword, int location);
-static void current_token_is_not_variable(int tok);
+static void current_token_is_not_variable(int tok, yyscan_t yyscanner);
static PLpgSQL_expr *read_sql_construct(int until,
int until2,
int until3,
@@ -72,31 +72,33 @@ static PLpgSQL_expr *read_sql_construct(int until,
bool isexpression,
bool valid_sql,
int *startloc,
- int *endtoken);
+ int *endtoken,
+ yyscan_t yyscanner);
static PLpgSQL_expr *read_sql_expression(int until,
- const char *expected);
+ const char *expected, yyscan_t yyscanner);
static PLpgSQL_expr *read_sql_expression2(int until, int until2,
const char *expected,
- int *endtoken);
-static PLpgSQL_expr *read_sql_stmt(void);
-static PLpgSQL_type *read_datatype(int tok);
+ int *endtoken, yyscan_t yyscanner);
+static PLpgSQL_expr *read_sql_stmt(yyscan_t yyscanner);
+static PLpgSQL_type *read_datatype(int tok, yyscan_t yyscanner);
static PLpgSQL_stmt *make_execsql_stmt(int firsttoken, int location,
- PLword *word);
-static PLpgSQL_stmt_fetch *read_fetch_direction(void);
+ PLword *word, yyscan_t yyscanner);
+static PLpgSQL_stmt_fetch *read_fetch_direction(yyscan_t yyscanner);
static void complete_direction(PLpgSQL_stmt_fetch *fetch,
- bool *check_FROM);
-static PLpgSQL_stmt *make_return_stmt(int location);
-static PLpgSQL_stmt *make_return_next_stmt(int location);
-static PLpgSQL_stmt *make_return_query_stmt(int location);
+ bool *check_FROM, yyscan_t yyscanner);
+static PLpgSQL_stmt *make_return_stmt(int location, yyscan_t yyscanner);
+static PLpgSQL_stmt *make_return_next_stmt(int location, yyscan_t yyscanner);
+static PLpgSQL_stmt *make_return_query_stmt(int location, yyscan_t yyscanner);
static PLpgSQL_stmt *make_case(int location, PLpgSQL_expr *t_expr,
List *case_when_list, List *else_stmts);
static char *NameOfDatum(PLwdatum *wdatum);
static void check_assignable(PLpgSQL_datum *datum, int location);
static void read_into_target(PLpgSQL_variable **target,
- bool *strict);
+ bool *strict, yyscan_t yyscanner);
static PLpgSQL_row *read_into_scalar_list(char *initial_name,
PLpgSQL_datum *initial_datum,
- int initial_location);
+ int initial_location,
+ yyscan_t yyscanner);
static PLpgSQL_row *make_scalar_list1(char *initial_name,
PLpgSQL_datum *initial_datum,
int lineno, int location);
@@ -108,12 +110,14 @@ static void check_labels(const char *start_label,
const char *end_label,
int end_location);
static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor,
- int until);
-static List *read_raise_options(void);
+ int until, yyscan_t yyscanner);
+static List *read_raise_options(yyscan_t yyscanner);
static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%}
+%parse-param {yyscan_t yyscanner}
+%lex-param {yyscan_t yyscanner}
%expect 0
%name-prefix="plpgsql_yy"
%locations
@@ -577,7 +581,7 @@ opt_scrollable :
decl_cursor_query :
{
- $$ = read_sql_stmt();
+ $$ = read_sql_stmt(yyscanner);
}
;
@@ -706,7 +710,7 @@ decl_varname : T_WORD
if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
$1.ident, NULL, NULL,
NULL) != NULL)
- yyerror("duplicate declaration");
+ yyerror(yyscanner, "duplicate declaration");
if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
@@ -734,7 +738,7 @@ decl_varname : T_WORD
if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
$1, NULL, NULL,
NULL) != NULL)
- yyerror("duplicate declaration");
+ yyerror(yyscanner, "duplicate declaration");
if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
@@ -766,7 +770,7 @@ decl_datatype :
* consume it, and then we must tell bison to forget
* it.
*/
- $$ = read_datatype(yychar);
+ $$ = read_datatype(yychar, yyscanner);
yyclearin;
}
;
@@ -799,7 +803,7 @@ decl_defval : ';'
{ $$ = NULL; }
| decl_defkey
{
- $$ = read_sql_expression(';', ";");
+ $$ = read_sql_expression(';', ";", yyscanner);
}
;
@@ -900,7 +904,8 @@ stmt_perform : K_PERFORM
new->expr = read_sql_construct(';', 0, 0, ";",
RAW_PARSE_DEFAULT,
false, false,
- &startloc, NULL);
+ &startloc, NULL,
+ yyscanner);
/* overwrite "perform" ... */
memcpy(new->expr->query, " SELECT", 7);
/* left-justify to get rid of the leading space */
@@ -923,7 +928,7 @@ stmt_call : K_CALL
new->lineno = plpgsql_location_to_lineno(@1);
new->stmtid = ++plpgsql_curr_compile->nstatements;
plpgsql_push_back_token(K_CALL);
- new->expr = read_sql_stmt();
+ new->expr = read_sql_stmt(yyscanner);
new->is_call = true;
/* Remember we may need a procedure resource owner */
@@ -942,7 +947,7 @@ stmt_call : K_CALL
new->lineno = plpgsql_location_to_lineno(@1);
new->stmtid = ++plpgsql_curr_compile->nstatements;
plpgsql_push_back_token(K_DO);
- new->expr = read_sql_stmt();
+ new->expr = read_sql_stmt(yyscanner);
new->is_call = false;
/* Remember we may need a procedure resource owner */
@@ -986,7 +991,8 @@ stmt_assign : T_DATUM
new->expr = read_sql_construct(';', 0, 0, ";",
pmode,
false, true,
- NULL, NULL);
+ NULL, NULL,
+ yyscanner);
$$ = (PLpgSQL_stmt *) new;
}
@@ -1093,7 +1099,7 @@ getdiag_list_item : getdiag_target assign_operator getdiag_item
getdiag_item :
{
- int tok = yylex();
+ int tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_ROW_COUNT, "row_count"))
@@ -1135,7 +1141,7 @@ getdiag_item :
K_RETURNED_SQLSTATE, "returned_sqlstate"))
$$ = PLPGSQL_GETDIAG_RETURNED_SQLSTATE;
else
- yyerror("unrecognized GET DIAGNOSTICS item");
+ yyerror(yyscanner, "unrecognized GET DIAGNOSTICS item");
}
;
@@ -1148,7 +1154,7 @@ getdiag_target : T_DATUM
*/
if ($1.datum->dtype == PLPGSQL_DTYPE_ROW ||
$1.datum->dtype == PLPGSQL_DTYPE_REC ||
- plpgsql_peek() == '[')
+ plpgsql_peek(yyscanner) == '[')
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a scalar variable",
@@ -1222,12 +1228,12 @@ stmt_case : K_CASE opt_expr_until_when case_when_list opt_case_else K_END K_CAS
opt_expr_until_when :
{
PLpgSQL_expr *expr = NULL;
- int tok = yylex();
+ int tok = yylex(yyscanner);
if (tok != K_WHEN)
{
plpgsql_push_back_token(tok);
- expr = read_sql_expression(K_WHEN, "WHEN");
+ expr = read_sql_expression(K_WHEN, "WHEN", yyscanner);
}
plpgsql_push_back_token(K_WHEN);
$$ = expr;
@@ -1347,7 +1353,7 @@ stmt_for : opt_loop_label K_FOR for_control loop_body
for_control : for_variable K_IN
{
- int tok = yylex();
+ int tok = yylex(yyscanner);
int tokloc = yylloc;
if (tok == K_EXECUTE)
@@ -1359,7 +1365,7 @@ for_control : for_variable K_IN
expr = read_sql_expression2(K_LOOP, K_USING,
"LOOP or USING",
- &term);
+ &term, yyscanner);
new = palloc0(sizeof(PLpgSQL_stmt_dynfors));
new->cmd_type = PLPGSQL_STMT_DYNFORS;
@@ -1392,7 +1398,7 @@ for_control : for_variable K_IN
{
expr = read_sql_expression2(',', K_LOOP,
", or LOOP",
- &term);
+ &term, yyscanner);
new->params = lappend(new->params, expr);
} while (term == ',');
}
@@ -1427,8 +1433,7 @@ for_control : for_variable K_IN
parser_errposition(tokloc)));
/* collect cursor's parameters if any */
- new->argquery = read_cursor_args(cursor,
- K_LOOP);
+ new->argquery = read_cursor_args(cursor, K_LOOP, yyscanner);
/* create loop's private RECORD variable */
new->var = (PLpgSQL_variable *)
@@ -1479,7 +1484,8 @@ for_control : for_variable K_IN
true,
false,
&expr1loc,
- &tok);
+ &tok,
+ yyscanner);
if (tok == DOT_DOT)
{
@@ -1500,12 +1506,12 @@ for_control : for_variable K_IN
/* Read and check the second one */
expr2 = read_sql_expression2(K_LOOP, K_BY,
"LOOP",
- &tok);
+ &tok, yyscanner);
/* Get the BY clause if any */
if (tok == K_BY)
expr_by = read_sql_expression(K_LOOP,
- "LOOP");
+ "LOOP", yyscanner);
else
expr_by = NULL;
@@ -1620,13 +1626,14 @@ for_variable : T_DATUM
$$.scalar = $1.datum;
$$.row = NULL;
/* check for comma-separated list */
- tok = yylex();
+ tok = yylex(yyscanner);
plpgsql_push_back_token(tok);
if (tok == ',')
$$.row = (PLpgSQL_datum *)
read_into_scalar_list($$.name,
$$.scalar,
- @1);
+ @1,
+ yyscanner);
}
}
| T_WORD
@@ -1638,7 +1645,7 @@ for_variable : T_DATUM
$$.scalar = NULL;
$$.row = NULL;
/* check for comma-separated list */
- tok = yylex();
+ tok = yylex(yyscanner);
plpgsql_push_back_token(tok);
if (tok == ',')
word_is_not_variable(&($1), @1);
@@ -1765,24 +1772,24 @@ stmt_return : K_RETURN
{
int tok;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == 0)
- yyerror("unexpected end of function definition");
+ yyerror(yyscanner, "unexpected end of function definition");
if (tok_is_keyword(tok, &yylval,
K_NEXT, "next"))
{
- $$ = make_return_next_stmt(@1);
+ $$ = make_return_next_stmt(@1, yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_QUERY, "query"))
{
- $$ = make_return_query_stmt(@1);
+ $$ = make_return_query_stmt(@1, yyscanner);
}
else
{
plpgsql_push_back_token(tok);
- $$ = make_return_stmt(@1);
+ $$ = make_return_stmt(@1, yyscanner);
}
}
;
@@ -1803,9 +1810,9 @@ stmt_raise : K_RAISE
new->params = NIL;
new->options = NIL;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == 0)
- yyerror("unexpected end of function definition");
+ yyerror(yyscanner, "unexpected end of function definition");
/*
* We could have just RAISE, meaning to re-throw
@@ -1820,40 +1827,40 @@ stmt_raise : K_RAISE
K_EXCEPTION, "exception"))
{
new->elog_level = ERROR;
- tok = yylex();
+ tok = yylex(yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_WARNING, "warning"))
{
new->elog_level = WARNING;
- tok = yylex();
+ tok = yylex(yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_NOTICE, "notice"))
{
new->elog_level = NOTICE;
- tok = yylex();
+ tok = yylex(yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_INFO, "info"))
{
new->elog_level = INFO;
- tok = yylex();
+ tok = yylex(yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_LOG, "log"))
{
new->elog_level = LOG;
- tok = yylex();
+ tok = yylex(yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_DEBUG, "debug"))
{
new->elog_level = DEBUG1;
- tok = yylex();
+ tok = yylex(yyscanner);
}
if (tok == 0)
- yyerror("unexpected end of function definition");
+ yyerror(yyscanner, "unexpected end of function definition");
/*
* Next we can have a condition name, or
@@ -1871,9 +1878,9 @@ stmt_raise : K_RAISE
* begins the list of parameter expressions,
* or USING to begin the options list.
*/
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok != ',' && tok != ';' && tok != K_USING)
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
while (tok == ',')
{
@@ -1883,7 +1890,8 @@ stmt_raise : K_RAISE
", or ; or USING",
RAW_PARSE_PLPGSQL_EXPR,
true, true,
- NULL, &tok);
+ NULL, &tok,
+ yyscanner);
new->params = lappend(new->params, expr);
}
}
@@ -1896,14 +1904,14 @@ stmt_raise : K_RAISE
/* next token should be a string literal */
char *sqlstatestr;
- if (yylex() != SCONST)
- yyerror("syntax error");
+ if (yylex(yyscanner) != SCONST)
+ yyerror(yyscanner, "syntax error");
sqlstatestr = yylval.str;
if (strlen(sqlstatestr) != 5)
- yyerror("invalid SQLSTATE code");
+ yyerror(yyscanner, "invalid SQLSTATE code");
if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
- yyerror("invalid SQLSTATE code");
+ yyerror(yyscanner, "invalid SQLSTATE code");
new->condname = sqlstatestr;
}
else
@@ -1913,17 +1921,17 @@ stmt_raise : K_RAISE
else if (plpgsql_token_is_unreserved_keyword(tok))
new->condname = pstrdup(yylval.keyword);
else
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
plpgsql_recognize_err_condition(new->condname,
false);
}
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok != ';' && tok != K_USING)
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
}
if (tok == K_USING)
- new->options = read_raise_options();
+ new->options = read_raise_options(yyscanner);
}
check_raise_parameters(new);
@@ -1945,10 +1953,10 @@ stmt_assert : K_ASSERT
new->cond = read_sql_expression2(',', ';',
", or ;",
- &tok);
+ &tok, yyscanner);
if (tok == ',')
- new->message = read_sql_expression(';', ";");
+ new->message = read_sql_expression(';', ";", yyscanner);
else
new->message = NULL;
@@ -1976,37 +1984,37 @@ loop_body : proc_sect K_END K_LOOP opt_label ';'
*/
stmt_execsql : K_IMPORT
{
- $$ = make_execsql_stmt(K_IMPORT, @1, NULL);
+ $$ = make_execsql_stmt(K_IMPORT, @1, NULL, yyscanner);
}
| K_INSERT
{
- $$ = make_execsql_stmt(K_INSERT, @1, NULL);
+ $$ = make_execsql_stmt(K_INSERT, @1, NULL, yyscanner);
}
| K_MERGE
{
- $$ = make_execsql_stmt(K_MERGE, @1, NULL);
+ $$ = make_execsql_stmt(K_MERGE, @1, NULL, yyscanner);
}
| T_WORD
{
int tok;
- tok = yylex();
+ tok = yylex(yyscanner);
plpgsql_push_back_token(tok);
if (tok == '=' || tok == COLON_EQUALS ||
tok == '[' || tok == '.')
word_is_not_variable(&($1), @1);
- $$ = make_execsql_stmt(T_WORD, @1, &($1));
+ $$ = make_execsql_stmt(T_WORD, @1, &($1), yyscanner);
}
| T_CWORD
{
int tok;
- tok = yylex();
+ tok = yylex(yyscanner);
plpgsql_push_back_token(tok);
if (tok == '=' || tok == COLON_EQUALS ||
tok == '[' || tok == '.')
cword_is_not_variable(&($1), @1);
- $$ = make_execsql_stmt(T_CWORD, @1, NULL);
+ $$ = make_execsql_stmt(T_CWORD, @1, NULL, yyscanner);
}
;
@@ -2020,7 +2028,8 @@ stmt_dynexecute : K_EXECUTE
"INTO or USING or ;",
RAW_PARSE_PLPGSQL_EXPR,
true, true,
- NULL, &endtoken);
+ NULL, &endtoken,
+ yyscanner);
new = palloc(sizeof(PLpgSQL_stmt_dynexecute));
new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
@@ -2044,29 +2053,30 @@ stmt_dynexecute : K_EXECUTE
if (endtoken == K_INTO)
{
if (new->into) /* multiple INTO */
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
new->into = true;
- read_into_target(&new->target, &new->strict);
- endtoken = yylex();
+ read_into_target(&new->target, &new->strict, yyscanner);
+ endtoken = yylex(yyscanner);
}
else if (endtoken == K_USING)
{
if (new->params) /* multiple USING */
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
do
{
expr = read_sql_construct(',', ';', K_INTO,
", or ; or INTO",
RAW_PARSE_PLPGSQL_EXPR,
true, true,
- NULL, &endtoken);
+ NULL, &endtoken,
+ yyscanner);
new->params = lappend(new->params, expr);
} while (endtoken == ',');
}
else if (endtoken == ';')
break;
else
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
}
$$ = (PLpgSQL_stmt *) new;
@@ -2089,29 +2099,29 @@ stmt_open : K_OPEN cursor_variable
if ($2->cursor_explicit_expr == NULL)
{
/* be nice if we could use opt_scrollable here */
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_NO, "no"))
{
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_SCROLL, "scroll"))
{
new->cursor_options |= CURSOR_OPT_NO_SCROLL;
- tok = yylex();
+ tok = yylex(yyscanner);
}
}
else if (tok_is_keyword(tok, &yylval,
K_SCROLL, "scroll"))
{
new->cursor_options |= CURSOR_OPT_SCROLL;
- tok = yylex();
+ tok = yylex(yyscanner);
}
if (tok != K_FOR)
- yyerror("syntax error, expected \"FOR\"");
+ yyerror(yyscanner, "syntax error, expected \"FOR\"");
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == K_EXECUTE)
{
int endtoken;
@@ -2119,7 +2129,7 @@ stmt_open : K_OPEN cursor_variable
new->dynquery =
read_sql_expression2(K_USING, ';',
"USING or ;",
- &endtoken);
+ &endtoken, yyscanner);
/* If we found "USING", collect argument(s) */
if (endtoken == K_USING)
@@ -2130,7 +2140,7 @@ stmt_open : K_OPEN cursor_variable
{
expr = read_sql_expression2(',', ';',
", or ;",
- &endtoken);
+ &endtoken, yyscanner);
new->params = lappend(new->params,
expr);
} while (endtoken == ',');
@@ -2139,13 +2149,13 @@ stmt_open : K_OPEN cursor_variable
else
{
plpgsql_push_back_token(tok);
- new->query = read_sql_stmt();
+ new->query = read_sql_stmt(yyscanner);
}
}
else
{
/* predefined cursor query, so read args */
- new->argquery = read_cursor_args($2, ';');
+ new->argquery = read_cursor_args($2, ';', yyscanner);
}
$$ = (PLpgSQL_stmt *) new;
@@ -2158,10 +2168,10 @@ stmt_fetch : K_FETCH opt_fetch_direction cursor_variable K_INTO
PLpgSQL_variable *target;
/* We have already parsed everything through the INTO keyword */
- read_into_target(&target, NULL);
+ read_into_target(&target, NULL, yyscanner);
- if (yylex() != ';')
- yyerror("syntax error");
+ if (yylex(yyscanner) != ';')
+ yyerror(yyscanner, "syntax error");
/*
* We don't allow multiple rows in PL/pgSQL's FETCH
@@ -2196,7 +2206,7 @@ stmt_move : K_MOVE opt_fetch_direction cursor_variable ';'
opt_fetch_direction :
{
- $$ = read_fetch_direction();
+ $$ = read_fetch_direction(yyscanner);
}
;
@@ -2264,7 +2274,7 @@ cursor_variable : T_DATUM
* just throw an error if next token is '['.
*/
if ($1.datum->dtype != PLPGSQL_DTYPE_VAR ||
- plpgsql_peek() == '[')
+ plpgsql_peek(yyscanner) == '[')
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cursor variable must be a simple variable"),
@@ -2384,14 +2394,14 @@ proc_condition : any_identifier
char *sqlstatestr;
/* next token should be a string literal */
- if (yylex() != SCONST)
- yyerror("syntax error");
+ if (yylex(yyscanner) != SCONST)
+ yyerror(yyscanner, "syntax error");
sqlstatestr = yylval.str;
if (strlen(sqlstatestr) != 5)
- yyerror("invalid SQLSTATE code");
+ yyerror(yyscanner, "invalid SQLSTATE code");
if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
- yyerror("invalid SQLSTATE code");
+ yyerror(yyscanner, "invalid SQLSTATE code");
new = palloc(sizeof(PLpgSQL_condition));
new->sqlerrstate =
@@ -2409,15 +2419,15 @@ proc_condition : any_identifier
;
expr_until_semi :
- { $$ = read_sql_expression(';', ";"); }
+ { $$ = read_sql_expression(';', ";", yyscanner); }
;
expr_until_then :
- { $$ = read_sql_expression(K_THEN, "THEN"); }
+ { $$ = read_sql_expression(K_THEN, "THEN", yyscanner); }
;
expr_until_loop :
- { $$ = read_sql_expression(K_LOOP, "LOOP"); }
+ { $$ = read_sql_expression(K_LOOP, "LOOP", yyscanner); }
;
opt_block_label :
@@ -2475,7 +2485,7 @@ any_identifier : T_WORD
| T_DATUM
{
if ($1.ident == NULL) /* composite name not OK */
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
$$ = $1.ident;
}
;
@@ -2627,42 +2637,45 @@ cword_is_not_variable(PLcword *cword, int location)
* look at yylval and yylloc.
*/
static void
-current_token_is_not_variable(int tok)
+current_token_is_not_variable(int tok, yyscan_t yyscanner)
{
if (tok == T_WORD)
word_is_not_variable(&(yylval.word), yylloc);
else if (tok == T_CWORD)
cword_is_not_variable(&(yylval.cword), yylloc);
else
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
}
/* Convenience routine to read an expression with one possible terminator */
static PLpgSQL_expr *
-read_sql_expression(int until, const char *expected)
+read_sql_expression(int until, const char *expected, yyscan_t yyscanner)
{
return read_sql_construct(until, 0, 0, expected,
RAW_PARSE_PLPGSQL_EXPR,
- true, true, NULL, NULL);
+ true, true, NULL, NULL,
+ yyscanner);
}
/* Convenience routine to read an expression with two possible terminators */
static PLpgSQL_expr *
read_sql_expression2(int until, int until2, const char *expected,
- int *endtoken)
+ int *endtoken, yyscan_t yyscanner)
{
return read_sql_construct(until, until2, 0, expected,
RAW_PARSE_PLPGSQL_EXPR,
- true, true, NULL, endtoken);
+ true, true, NULL, endtoken,
+ yyscanner);
}
/* Convenience routine to read a SQL statement that must end with ';' */
static PLpgSQL_expr *
-read_sql_stmt(void)
+read_sql_stmt(yyscan_t yyscanner)
{
return read_sql_construct(';', 0, 0, ";",
RAW_PARSE_DEFAULT,
- false, true, NULL, NULL);
+ false, true, NULL, NULL,
+ yyscanner);
}
/*
@@ -2688,7 +2701,8 @@ read_sql_construct(int until,
bool isexpression,
bool valid_sql,
int *startloc,
- int *endtoken)
+ int *endtoken,
+ yyscan_t yyscanner)
{
int tok;
StringInfoData ds;
@@ -2706,7 +2720,7 @@ read_sql_construct(int until,
for (;;)
{
- tok = yylex();
+ tok = yylex(yyscanner);
if (startlocation < 0) /* remember loc of first token */
startlocation = yylloc;
if (tok == until && parenlevel == 0)
@@ -2721,7 +2735,7 @@ read_sql_construct(int until,
{
parenlevel--;
if (parenlevel < 0)
- yyerror("mismatched parentheses");
+ yyerror(yyscanner, "mismatched parentheses");
}
/*
* End of function definition is an error, and we don't expect to
@@ -2731,7 +2745,7 @@ read_sql_construct(int until,
if (tok == 0 || tok == ';')
{
if (parenlevel != 0)
- yyerror("mismatched parentheses");
+ yyerror(yyscanner, "mismatched parentheses");
if (isexpression)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -2760,9 +2774,9 @@ read_sql_construct(int until,
if (startlocation >= endlocation)
{
if (isexpression)
- yyerror("missing expression");
+ yyerror(yyscanner, "missing expression");
else
- yyerror("missing SQL statement");
+ yyerror(yyscanner, "missing SQL statement");
}
/*
@@ -2794,7 +2808,7 @@ read_sql_construct(int until,
* Returns a PLpgSQL_type struct.
*/
static PLpgSQL_type *
-read_datatype(int tok)
+read_datatype(int tok, yyscan_t yyscanner)
{
StringInfoData ds;
char *type_name;
@@ -2807,7 +2821,7 @@ read_datatype(int tok)
/* Often there will be a lookahead token, but if not, get one */
if (tok == YYEMPTY)
- tok = yylex();
+ tok = yylex(yyscanner);
/* The current token is the start of what we'll pass to parse_datatype */
startlocation = yylloc;
@@ -2820,10 +2834,10 @@ read_datatype(int tok)
{
char *dtname = yylval.word.ident;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == '%')
{
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_TYPE, "type"))
result = plpgsql_parse_wordtype(dtname);
@@ -2836,10 +2850,10 @@ read_datatype(int tok)
{
char *dtname = pstrdup(yylval.keyword);
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == '%')
{
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_TYPE, "type"))
result = plpgsql_parse_wordtype(dtname);
@@ -2852,10 +2866,10 @@ read_datatype(int tok)
{
List *dtnames = yylval.cword.idents;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == '%')
{
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_TYPE, "type"))
result = plpgsql_parse_cwordtype(dtnames);
@@ -2877,22 +2891,22 @@ read_datatype(int tok)
{
bool is_array = false;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_ARRAY, "array"))
{
is_array = true;
- tok = yylex();
+ tok = yylex(yyscanner);
}
while (tok == '[')
{
is_array = true;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == ICONST)
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok != ']')
- yyerror("syntax error, expected \"]\"");
- tok = yylex();
+ yyerror(yyscanner, "syntax error, expected \"]\"");
+ tok = yylex(yyscanner);
}
plpgsql_push_back_token(tok);
@@ -2913,9 +2927,9 @@ read_datatype(int tok)
if (tok == 0)
{
if (parenlevel != 0)
- yyerror("mismatched parentheses");
+ yyerror(yyscanner, "mismatched parentheses");
else
- yyerror("incomplete data type declaration");
+ yyerror(yyscanner, "incomplete data type declaration");
}
/* Possible followers for datatype in a declaration */
if (tok == K_COLLATE || tok == K_NOT ||
@@ -2929,7 +2943,7 @@ read_datatype(int tok)
else if (tok == ')')
parenlevel--;
- tok = yylex();
+ tok = yylex(yyscanner);
}
/* set up ds to contain complete typename text */
@@ -2938,7 +2952,7 @@ read_datatype(int tok)
type_name = ds.data;
if (type_name[0] == '\0')
- yyerror("missing data type declaration");
+ yyerror(yyscanner, "missing data type declaration");
result = parse_datatype(type_name, startlocation);
@@ -2955,7 +2969,7 @@ read_datatype(int tok)
* If firsttoken == T_WORD, pass its yylval value as "word", else pass NULL.
*/
static PLpgSQL_stmt *
-make_execsql_stmt(int firsttoken, int location, PLword *word)
+make_execsql_stmt(int firsttoken, int location, PLword *word, yyscan_t yyscanner)
{
StringInfoData ds;
IdentifierLookup save_IdentifierLookup;
@@ -3024,7 +3038,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word)
for (;;)
{
prev_tok = tok;
- tok = yylex();
+ tok = yylex(yyscanner);
if (have_into && into_end_loc < 0)
into_end_loc = yylloc; /* token after the INTO part */
/* Detect CREATE [OR REPLACE] {FUNCTION|PROCEDURE} */
@@ -3063,7 +3077,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word)
if (tok == ';' && paren_depth == 0 && begin_depth == 0)
break;
if (tok == 0)
- yyerror("unexpected end of function definition");
+ yyerror(yyscanner, "unexpected end of function definition");
if (tok == K_INTO)
{
if (prev_tok == K_INSERT)
@@ -3073,11 +3087,11 @@ make_execsql_stmt(int firsttoken, int location, PLword *word)
if (firsttoken == K_IMPORT)
continue; /* IMPORT ... INTO is not an INTO-target */
if (have_into)
- yyerror("INTO specified more than once");
+ yyerror(yyscanner, "INTO specified more than once");
have_into = true;
into_start_loc = yylloc;
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
- read_into_target(&target, &have_strict);
+ read_into_target(&target, &have_strict, yyscanner);
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_EXPR;
}
}
@@ -3130,7 +3144,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word)
* Read FETCH or MOVE direction clause (everything through FROM/IN).
*/
static PLpgSQL_stmt_fetch *
-read_fetch_direction(void)
+read_fetch_direction(yyscan_t yyscanner)
{
PLpgSQL_stmt_fetch *fetch;
int tok;
@@ -3149,9 +3163,9 @@ read_fetch_direction(void)
fetch->expr = NULL;
fetch->returns_multiple_rows = false;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == 0)
- yyerror("unexpected end of function definition");
+ yyerror(yyscanner, "unexpected end of function definition");
if (tok_is_keyword(tok, &yylval,
K_NEXT, "next"))
@@ -3180,7 +3194,7 @@ read_fetch_direction(void)
fetch->direction = FETCH_ABSOLUTE;
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"FROM or IN",
- NULL);
+ NULL, yyscanner);
check_FROM = false;
}
else if (tok_is_keyword(tok, &yylval,
@@ -3189,7 +3203,7 @@ read_fetch_direction(void)
fetch->direction = FETCH_RELATIVE;
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"FROM or IN",
- NULL);
+ NULL, yyscanner);
check_FROM = false;
}
else if (tok_is_keyword(tok, &yylval,
@@ -3201,13 +3215,13 @@ read_fetch_direction(void)
else if (tok_is_keyword(tok, &yylval,
K_FORWARD, "forward"))
{
- complete_direction(fetch, &check_FROM);
+ complete_direction(fetch, &check_FROM, yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_BACKWARD, "backward"))
{
fetch->direction = FETCH_BACKWARD;
- complete_direction(fetch, &check_FROM);
+ complete_direction(fetch, &check_FROM, yyscanner);
}
else if (tok == K_FROM || tok == K_IN)
{
@@ -3233,7 +3247,7 @@ read_fetch_direction(void)
plpgsql_push_back_token(tok);
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"FROM or IN",
- NULL);
+ NULL, yyscanner);
fetch->returns_multiple_rows = true;
check_FROM = false;
}
@@ -3241,9 +3255,9 @@ read_fetch_direction(void)
/* check FROM or IN keyword after direction's specification */
if (check_FROM)
{
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok != K_FROM && tok != K_IN)
- yyerror("expected FROM or IN");
+ yyerror(yyscanner, "expected FROM or IN");
}
return fetch;
@@ -3256,13 +3270,13 @@ read_fetch_direction(void)
* BACKWARD expr, BACKWARD ALL, BACKWARD
*/
static void
-complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM)
+complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM, yyscan_t yyscanner)
{
int tok;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == 0)
- yyerror("unexpected end of function definition");
+ yyerror(yyscanner, "unexpected end of function definition");
if (tok == K_FROM || tok == K_IN)
{
@@ -3281,14 +3295,14 @@ complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM)
plpgsql_push_back_token(tok);
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"FROM or IN",
- NULL);
+ NULL, yyscanner);
fetch->returns_multiple_rows = true;
*check_FROM = false;
}
static PLpgSQL_stmt *
-make_return_stmt(int location)
+make_return_stmt(int location, yyscan_t yyscanner)
{
PLpgSQL_stmt_return *new;
@@ -3301,7 +3315,7 @@ make_return_stmt(int location)
if (plpgsql_curr_compile->fn_retset)
{
- if (yylex() != ';')
+ if (yylex(yyscanner) != ';')
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("RETURN cannot have a parameter in function returning set"),
@@ -3310,7 +3324,7 @@ make_return_stmt(int location)
}
else if (plpgsql_curr_compile->fn_rettype == VOIDOID)
{
- if (yylex() != ';')
+ if (yylex(yyscanner) != ';')
{
if (plpgsql_curr_compile->fn_prokind == PROKIND_PROCEDURE)
ereport(ERROR,
@@ -3326,7 +3340,7 @@ make_return_stmt(int location)
}
else if (plpgsql_curr_compile->out_param_varno >= 0)
{
- if (yylex() != ';')
+ if (yylex(yyscanner) != ';')
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("RETURN cannot have a parameter in function with OUT parameters"),
@@ -3339,9 +3353,9 @@ make_return_stmt(int location)
* We want to special-case simple variable references for efficiency.
* So peek ahead to see if that's what we have.
*/
- int tok = yylex();
+ int tok = yylex(yyscanner);
- if (tok == T_DATUM && plpgsql_peek() == ';' &&
+ if (tok == T_DATUM && plpgsql_peek(yyscanner) == ';' &&
(yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_VAR ||
yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_PROMISE ||
yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
@@ -3349,7 +3363,7 @@ make_return_stmt(int location)
{
new->retvarno = yylval.wdatum.datum->dno;
/* eat the semicolon token that we only peeked at above */
- tok = yylex();
+ tok = yylex(yyscanner);
Assert(tok == ';');
}
else
@@ -3361,7 +3375,7 @@ make_return_stmt(int location)
* anything else is a compile-time error.
*/
plpgsql_push_back_token(tok);
- new->expr = read_sql_expression(';', ";");
+ new->expr = read_sql_expression(';', ";", yyscanner);
}
}
@@ -3370,7 +3384,7 @@ make_return_stmt(int location)
static PLpgSQL_stmt *
-make_return_next_stmt(int location)
+make_return_next_stmt(int location, yyscan_t yyscanner)
{
PLpgSQL_stmt_return_next *new;
@@ -3389,7 +3403,7 @@ make_return_next_stmt(int location)
if (plpgsql_curr_compile->out_param_varno >= 0)
{
- if (yylex() != ';')
+ if (yylex(yyscanner) != ';')
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("RETURN NEXT cannot have a parameter in function with OUT parameters"),
@@ -3402,9 +3416,9 @@ make_return_next_stmt(int location)
* We want to special-case simple variable references for efficiency.
* So peek ahead to see if that's what we have.
*/
- int tok = yylex();
+ int tok = yylex(yyscanner);
- if (tok == T_DATUM && plpgsql_peek() == ';' &&
+ if (tok == T_DATUM && plpgsql_peek(yyscanner) == ';' &&
(yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_VAR ||
yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_PROMISE ||
yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
@@ -3412,7 +3426,7 @@ make_return_next_stmt(int location)
{
new->retvarno = yylval.wdatum.datum->dno;
/* eat the semicolon token that we only peeked at above */
- tok = yylex();
+ tok = yylex(yyscanner);
Assert(tok == ';');
}
else
@@ -3424,7 +3438,7 @@ make_return_next_stmt(int location)
* anything else is a compile-time error.
*/
plpgsql_push_back_token(tok);
- new->expr = read_sql_expression(';', ";");
+ new->expr = read_sql_expression(';', ";", yyscanner);
}
}
@@ -3433,7 +3447,7 @@ make_return_next_stmt(int location)
static PLpgSQL_stmt *
-make_return_query_stmt(int location)
+make_return_query_stmt(int location, yyscan_t yyscanner)
{
PLpgSQL_stmt_return_query *new;
int tok;
@@ -3450,11 +3464,11 @@ make_return_query_stmt(int location)
new->stmtid = ++plpgsql_curr_compile->nstatements;
/* check for RETURN QUERY EXECUTE */
- if ((tok = yylex()) != K_EXECUTE)
+ if ((tok = yylex(yyscanner)) != K_EXECUTE)
{
/* ordinary static query */
plpgsql_push_back_token(tok);
- new->query = read_sql_stmt();
+ new->query = read_sql_stmt(yyscanner);
}
else
{
@@ -3462,14 +3476,14 @@ make_return_query_stmt(int location)
int term;
new->dynquery = read_sql_expression2(';', K_USING, "; or USING",
- &term);
+ &term, yyscanner);
if (term == K_USING)
{
do
{
PLpgSQL_expr *expr;
- expr = read_sql_expression2(',', ';', ", or ;", &term);
+ expr = read_sql_expression2(',', ';', ", or ;", &term, yyscanner);
new->params = lappend(new->params, expr);
} while (term == ',');
}
@@ -3523,7 +3537,7 @@ check_assignable(PLpgSQL_datum *datum, int location)
* INTO keyword.
*/
static void
-read_into_target(PLpgSQL_variable **target, bool *strict)
+read_into_target(PLpgSQL_variable **target, bool *strict, yyscan_t yyscanner)
{
int tok;
@@ -3532,11 +3546,11 @@ read_into_target(PLpgSQL_variable **target, bool *strict)
if (strict)
*strict = false;
- tok = yylex();
+ tok = yylex(yyscanner);
if (strict && tok == K_STRICT)
{
*strict = true;
- tok = yylex();
+ tok = yylex(yyscanner);
}
/*
@@ -3555,7 +3569,7 @@ read_into_target(PLpgSQL_variable **target, bool *strict)
check_assignable(yylval.wdatum.datum, yylloc);
*target = (PLpgSQL_variable *) yylval.wdatum.datum;
- if ((tok = yylex()) == ',')
+ if ((tok = yylex(yyscanner)) == ',')
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("record variable cannot be part of multiple-item INTO list"),
@@ -3566,13 +3580,13 @@ read_into_target(PLpgSQL_variable **target, bool *strict)
{
*target = (PLpgSQL_variable *)
read_into_scalar_list(NameOfDatum(&(yylval.wdatum)),
- yylval.wdatum.datum, yylloc);
+ yylval.wdatum.datum, yylloc, yyscanner);
}
break;
default:
/* just to give a better message than "syntax error" */
- current_token_is_not_variable(tok);
+ current_token_is_not_variable(tok, yyscanner);
}
}
@@ -3585,7 +3599,8 @@ read_into_target(PLpgSQL_variable **target, bool *strict)
static PLpgSQL_row *
read_into_scalar_list(char *initial_name,
PLpgSQL_datum *initial_datum,
- int initial_location)
+ int initial_location,
+ yyscan_t yyscanner)
{
int nfields;
char *fieldnames[1024];
@@ -3598,7 +3613,7 @@ read_into_scalar_list(char *initial_name,
varnos[0] = initial_datum->dno;
nfields = 1;
- while ((tok = yylex()) == ',')
+ while ((tok = yylex(yyscanner)) == ',')
{
/* Check for array overflow */
if (nfields >= 1024)
@@ -3607,7 +3622,7 @@ read_into_scalar_list(char *initial_name,
errmsg("too many INTO variables specified"),
parser_errposition(yylloc)));
- tok = yylex();
+ tok = yylex(yyscanner);
switch (tok)
{
case T_DATUM:
@@ -3625,7 +3640,7 @@ read_into_scalar_list(char *initial_name,
default:
/* just to give a better message than "syntax error" */
- current_token_is_not_variable(tok);
+ current_token_is_not_variable(tok, yyscanner);
}
}
@@ -3836,7 +3851,7 @@ check_labels(const char *start_label, const char *end_label, int end_location)
* parens).
*/
static PLpgSQL_expr *
-read_cursor_args(PLpgSQL_var *cursor, int until)
+read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
{
PLpgSQL_expr *expr;
PLpgSQL_row *row;
@@ -3846,7 +3861,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
StringInfoData ds;
bool any_named = false;
- tok = yylex();
+ tok = yylex(yyscanner);
if (cursor->cursor_explicit_argrow < 0)
{
/* No arguments expected */
@@ -3858,7 +3873,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
parser_errposition(yylloc)));
if (tok != until)
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
return NULL;
}
@@ -3887,7 +3902,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
int arglocation;
/* Check if it's a named parameter: "param := value" */
- plpgsql_peek2(&tok1, &tok2, &arglocation, NULL);
+ plpgsql_peek2(&tok1, &tok2, &arglocation, NULL, yyscanner);
if (tok1 == IDENT && tok2 == COLON_EQUALS)
{
char *argname;
@@ -3896,7 +3911,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
/* Read the argument name, ignoring any matching variable */
save_IdentifierLookup = plpgsql_IdentifierLookup;
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_DECLARE;
- yylex();
+ yylex(yyscanner);
argname = yylval.str;
plpgsql_IdentifierLookup = save_IdentifierLookup;
@@ -3917,9 +3932,9 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
* Eat the ":=". We already peeked, so the error should never
* happen.
*/
- tok2 = yylex();
+ tok2 = yylex(yyscanner);
if (tok2 != COLON_EQUALS)
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
any_named = true;
}
@@ -3943,7 +3958,8 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
",\" or \")",
RAW_PARSE_PLPGSQL_EXPR,
true, true,
- NULL, &endtoken);
+ NULL, &endtoken,
+ yyscanner);
argv[argpos] = item->query;
@@ -3990,9 +4006,9 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
pfree(ds.data);
/* Next we'd better find the until token */
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok != until)
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
return expr;
}
@@ -4001,7 +4017,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
* Parse RAISE ... USING options
*/
static List *
-read_raise_options(void)
+read_raise_options(yyscan_t yyscanner)
{
List *result = NIL;
@@ -4010,8 +4026,8 @@ read_raise_options(void)
PLpgSQL_raise_option *opt;
int tok;
- if ((tok = yylex()) == 0)
- yyerror("unexpected end of function definition");
+ if ((tok = yylex(yyscanner)) == 0)
+ yyerror(yyscanner, "unexpected end of function definition");
opt = (PLpgSQL_raise_option *) palloc(sizeof(PLpgSQL_raise_option));
@@ -4043,13 +4059,13 @@ read_raise_options(void)
K_SCHEMA, "schema"))
opt->opt_type = PLPGSQL_RAISEOPTION_SCHEMA;
else
- yyerror("unrecognized RAISE statement option");
+ yyerror(yyscanner, "unrecognized RAISE statement option");
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok != '=' && tok != COLON_EQUALS)
- yyerror("syntax error, expected \"=\"");
+ yyerror(yyscanner, "syntax error, expected \"=\"");
- opt->expr = read_sql_expression2(',', ';', ", or ;", &tok);
+ opt->expr = read_sql_expression2(',', ';', ", or ;", &tok, yyscanner);
result = lappend(result, opt);
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
index 9407da51efa..e24b107909b 100644
--- a/src/pl/plpgsql/src/pl_scanner.c
+++ b/src/pl/plpgsql/src/pl_scanner.c
@@ -103,7 +103,6 @@ typedef struct
*/
/* The stuff the core lexer needs */
-static core_yyscan_t yyscanner = NULL;
static core_yy_extra_type core_yy;
/* The original input string */
@@ -128,7 +127,7 @@ static const char *cur_line_end;
static int cur_line_num;
/* Internal functions */
-static int internal_yylex(TokenAuxData *auxdata);
+static int internal_yylex(TokenAuxData *auxdata, yyscan_t yyscanner);
static void push_back_token(int token, TokenAuxData *auxdata);
static void location_lineno_init(void);
@@ -143,37 +142,37 @@ static void location_lineno_init(void);
* matches one of those.
*/
int
-plpgsql_yylex(void)
+plpgsql_yylex(yyscan_t yyscanner)
{
int tok1;
TokenAuxData aux1;
int kwnum;
- tok1 = internal_yylex(&aux1);
+ tok1 = internal_yylex(&aux1, yyscanner);
if (tok1 == IDENT || tok1 == PARAM)
{
int tok2;
TokenAuxData aux2;
- tok2 = internal_yylex(&aux2);
+ tok2 = internal_yylex(&aux2, yyscanner);
if (tok2 == '.')
{
int tok3;
TokenAuxData aux3;
- tok3 = internal_yylex(&aux3);
+ tok3 = internal_yylex(&aux3, yyscanner);
if (tok3 == IDENT)
{
int tok4;
TokenAuxData aux4;
- tok4 = internal_yylex(&aux4);
+ tok4 = internal_yylex(&aux4, yyscanner);
if (tok4 == '.')
{
int tok5;
TokenAuxData aux5;
- tok5 = internal_yylex(&aux5);
+ tok5 = internal_yylex(&aux5, yyscanner);
if (tok5 == IDENT)
{
if (plpgsql_parse_tripword(aux1.lval.str,
@@ -322,7 +321,7 @@ plpgsql_token_length(void)
* interfacing from the core_YYSTYPE to YYSTYPE union.
*/
static int
-internal_yylex(TokenAuxData *auxdata)
+internal_yylex(TokenAuxData *auxdata, yyscan_t yyscanner)
{
int token;
const char *yytext;
@@ -434,12 +433,12 @@ plpgsql_append_source_text(StringInfo buf,
* be returned as IDENT. Reserved keywords are resolved as usual.
*/
int
-plpgsql_peek(void)
+plpgsql_peek(yyscan_t yyscanner)
{
int tok1;
TokenAuxData aux1;
- tok1 = internal_yylex(&aux1);
+ tok1 = internal_yylex(&aux1, yyscanner);
push_back_token(tok1, &aux1);
return tok1;
}
@@ -453,15 +452,15 @@ plpgsql_peek(void)
* be returned as IDENT. Reserved keywords are resolved as usual.
*/
void
-plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc, int *tok2_loc)
+plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc, int *tok2_loc, yyscan_t yyscanner)
{
int tok1,
tok2;
TokenAuxData aux1,
aux2;
- tok1 = internal_yylex(&aux1);
- tok2 = internal_yylex(&aux2);
+ tok1 = internal_yylex(&aux1, yyscanner);
+ tok2 = internal_yylex(&aux2, yyscanner);
*tok1_p = tok1;
if (tok1_loc)
@@ -513,7 +512,7 @@ plpgsql_scanner_errposition(int location)
* be misleading!
*/
void
-plpgsql_yyerror(const char *message)
+plpgsql_yyerror(yyscan_t yyscanner, const char *message)
{
char *yytext = core_yy.scanbuf + plpgsql_yylloc;
@@ -599,9 +598,11 @@ plpgsql_latest_lineno(void)
* Although it is not fed directly to flex, we need the original string
* to cite in error messages.
*/
-void
+yyscan_t
plpgsql_scanner_init(const char *str)
{
+ yyscan_t yyscanner;
+
/* Start up the core scanner */
yyscanner = scanner_init(str, &core_yy,
&ReservedPLKeywords, ReservedPLKeywordTokens);
@@ -621,17 +622,18 @@ plpgsql_scanner_init(const char *str)
num_pushbacks = 0;
location_lineno_init();
+
+ return yyscanner;
}
/*
* Called after parsing is done to clean up after plpgsql_scanner_init()
*/
void
-plpgsql_scanner_finish(void)
+plpgsql_scanner_finish(yyscan_t yyscanner)
{
/* release storage */
scanner_finish(yyscanner);
/* avoid leaving any dangling pointers */
- yyscanner = NULL;
scanorig = NULL;
}
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 50c3b28472b..dab4e16c72e 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -1315,26 +1315,26 @@ extern void plpgsql_dumptree(PLpgSQL_function *func);
/*
* Scanner functions in pl_scanner.c
*/
-extern int plpgsql_base_yylex(void);
-extern int plpgsql_yylex(void);
+typedef void *yyscan_t;
+extern int plpgsql_yylex(yyscan_t yyscanner);
extern int plpgsql_token_length(void);
extern void plpgsql_push_back_token(int token);
extern bool plpgsql_token_is_unreserved_keyword(int token);
extern void plpgsql_append_source_text(StringInfo buf,
int startlocation, int endlocation);
-extern int plpgsql_peek(void);
+extern int plpgsql_peek(yyscan_t yyscanner);
extern void plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc,
- int *tok2_loc);
+ int *tok2_loc, yyscan_t yyscanner);
extern int plpgsql_scanner_errposition(int location);
-extern void plpgsql_yyerror(const char *message) pg_attribute_noreturn();
+extern void plpgsql_yyerror(yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
extern int plpgsql_location_to_lineno(int location);
extern int plpgsql_latest_lineno(void);
-extern void plpgsql_scanner_init(const char *str);
-extern void plpgsql_scanner_finish(void);
+extern yyscan_t plpgsql_scanner_init(const char *str);
+extern void plpgsql_scanner_finish(yyscan_t yyscanner);
/*
* Externs in gram.y
*/
-extern int plpgsql_yyparse(void);
+extern int plpgsql_yyparse(yyscan_t yyscanner);
#endif /* PLPGSQL_H */
--
2.47.1
v1-0005-seg-Use-palloc-instead-of-malloc-for-flex.patchtext/plain; charset=UTF-8; name=v1-0005-seg-Use-palloc-instead-of-malloc-for-flex.patchDownload
From 4bae9413f75ab9d12dd5fedfccc633e7efce7e42 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v1 05/19] seg: Use palloc() instead of malloc() for flex
---
contrib/seg/segscan.l | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/contrib/seg/segscan.l b/contrib/seg/segscan.l
index 35c3a11ac14..004ac07fae2 100644
--- a/contrib/seg/segscan.l
+++ b/contrib/seg/segscan.l
@@ -38,6 +38,9 @@ static char *scanbuf; // FIXME
%option noinput
%option nounput
%option noyywrap
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
%option warn
%option prefix="seg_yy"
@@ -124,3 +127,30 @@ seg_scanner_finish(yyscan_t yyscanner)
yylex_destroy(yyscanner);
pfree(scanbuf);
}
+
+/*
+ * Interface functions to make flex use palloc() instead of malloc().
+ * It'd be better to make these static, but flex insists otherwise.
+ */
+
+void *
+yyalloc(yy_size_t size, yyscan_t yyscanner)
+{
+ return palloc(size);
+}
+
+void *
+yyrealloc(void *ptr, yy_size_t size, yyscan_t yyscanner)
+{
+ if (ptr)
+ return repalloc(ptr, size);
+ else
+ return palloc(size);
+}
+
+void
+yyfree(void *ptr, yyscan_t yyscanner)
+{
+ if (ptr)
+ pfree(ptr);
+}
--
2.47.1
v1-0006-seg-Simplify-flex-scan-buffer-management.patchtext/plain; charset=UTF-8; name=v1-0006-seg-Simplify-flex-scan-buffer-management.patchDownload
From 9f08aeaf4a5597065ff609a35b33f5cdb4904a0d Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v1 06/19] seg: Simplify flex scan buffer management
---
contrib/seg/segscan.l | 13 +------------
1 file changed, 1 insertion(+), 12 deletions(-)
diff --git a/contrib/seg/segscan.l b/contrib/seg/segscan.l
index 004ac07fae2..2b35cd4e67a 100644
--- a/contrib/seg/segscan.l
+++ b/contrib/seg/segscan.l
@@ -25,9 +25,6 @@ fprintf_to_ereport(const char *fmt, const char *msg)
{
ereport(ERROR, (errmsg_internal("%s", msg)));
}
-
-/* Handles to the buffer that the lexer uses internally */
-static char *scanbuf; // FIXME
%}
%option reentrant
@@ -100,7 +97,6 @@ seg_yyerror(SEG *result, struct Node *escontext, yyscan_t yyscanner, const char
void
seg_scanner_init(const char *str, yyscan_t *yyscannerp)
{
- Size slen = strlen(str);
yyscan_t yyscanner;
if (yylex_init(yyscannerp) != 0)
@@ -108,13 +104,7 @@ seg_scanner_init(const char *str, yyscan_t *yyscannerp)
yyscanner = *yyscannerp;
- /*
- * Make a scan buffer with special termination needed by flex.
- */
- scanbuf = palloc(slen + 2);
- memcpy(scanbuf, str, slen);
- scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- yy_scan_buffer(scanbuf, slen + 2, yyscanner);
+ yy_scan_string(str, yyscanner);
}
@@ -125,7 +115,6 @@ void
seg_scanner_finish(yyscan_t yyscanner)
{
yylex_destroy(yyscanner);
- pfree(scanbuf);
}
/*
--
2.47.1
v1-0007-replication-parser-pure-parser-and-reentrant-scan.patchtext/plain; charset=UTF-8; name=v1-0007-replication-parser-pure-parser-and-reentrant-scan.patchDownload
From 980d9947b6d9425220c17496c4c71dd46e47106e Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v1 07/19] replication parser: pure parser and reentrant
scanner
---
src/backend/replication/repl_gram.y | 9 ++--
src/backend/replication/repl_scanner.l | 54 ++++++++++-----------
src/backend/replication/walsender.c | 11 +++--
src/include/replication/walsender_private.h | 18 ++++---
4 files changed, 47 insertions(+), 45 deletions(-)
diff --git a/src/backend/replication/repl_gram.y b/src/backend/replication/repl_gram.y
index 06daa954813..4fa71377e20 100644
--- a/src/backend/replication/repl_gram.y
+++ b/src/backend/replication/repl_gram.y
@@ -24,10 +24,6 @@
#include "repl_gram.h"
-/* silence -Wmissing-variable-declarations */
-extern int replication_yychar;
-extern int replication_yynerrs;
-
/* Result of the parsing is returned here */
Node *replication_parse_result;
@@ -43,6 +39,9 @@ Node *replication_parse_result;
%}
+%parse-param {yyscan_t yyscanner}
+%lex-param {yyscan_t yyscanner}
+%pure-parser
%expect 0
%name-prefix="replication_yy"
@@ -106,6 +105,8 @@ Node *replication_parse_result;
firstcmd: command opt_semicolon
{
replication_parse_result = $1;
+
+ (void) yynerrs; /* suppress compiler warning */
}
;
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index e7def800655..99f2efbb8f9 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -38,14 +38,11 @@ fprintf_to_ereport(const char *fmt, const char *msg)
ereport(ERROR, (errmsg_internal("%s", msg)));
}
-/* Handle to the buffer that the lexer uses internally */
-static YY_BUFFER_STATE scanbufhandle;
-
/* Pushed-back token (we only handle one) */
-static int repl_pushed_back_token;
+static int repl_pushed_back_token; /* FIXME */
/* Work area for collecting literals */
-static StringInfoData litbuf;
+static StringInfoData litbuf; /* FIXME */
static void startlit(void);
static char *litbufdup(void);
@@ -56,6 +53,8 @@ static void addlitchar(unsigned char ychar);
%}
+%option reentrant
+%option bison-bridge
%option 8bit
%option never-interactive
%option nodefault
@@ -142,7 +141,7 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
{space}+ { /* do nothing */ }
{digit}+ {
- replication_yylval.uintval = strtoul(yytext, NULL, 10);
+ yylval->uintval = strtoul(yytext, NULL, 10);
return UCONST;
}
@@ -150,8 +149,8 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
uint32 hi,
lo;
if (sscanf(yytext, "%X/%X", &hi, &lo) != 2)
- replication_yyerror("invalid streaming start location");
- replication_yylval.recptr = ((uint64) hi) << 32 | lo;
+ replication_yyerror("invalid streaming start location", yyscanner);
+ yylval->recptr = ((uint64) hi) << 32 | lo;
return RECPTR;
}
@@ -163,7 +162,7 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
<xq>{quotestop} {
yyless(1);
BEGIN(INITIAL);
- replication_yylval.str = litbufdup();
+ yylval->str = litbufdup();
return SCONST;
}
@@ -185,9 +184,9 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
yyless(1);
BEGIN(INITIAL);
- replication_yylval.str = litbufdup();
- len = strlen(replication_yylval.str);
- truncate_identifier(replication_yylval.str, len, true);
+ yylval->str = litbufdup();
+ len = strlen(yylval->str);
+ truncate_identifier(yylval->str, len, true);
return IDENT;
}
@@ -198,7 +197,7 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
{identifier} {
int len = strlen(yytext);
- replication_yylval.str = downcase_truncate_identifier(yytext, len, true);
+ yylval->str = downcase_truncate_identifier(yytext, len, true);
return IDENT;
}
@@ -207,7 +206,7 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
return yytext[0];
}
-<xq,xd><<EOF>> { replication_yyerror("unterminated quoted string"); }
+<xq,xd><<EOF>> { replication_yyerror("unterminated quoted string", yyscanner); }
<<EOF>> {
@@ -243,7 +242,7 @@ addlitchar(unsigned char ychar)
}
void
-replication_yyerror(const char *message)
+replication_yyerror(yyscan_t yyscanner, const char *message)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -252,16 +251,16 @@ replication_yyerror(const char *message)
void
-replication_scanner_init(const char *str)
+replication_scanner_init(const char *str, yyscan_t *yyscannerp)
{
Size slen = strlen(str);
char *scanbuf;
+ yyscan_t yyscanner;
- /*
- * Might be left over after ereport()
- */
- if (YY_CURRENT_BUFFER)
- yy_delete_buffer(YY_CURRENT_BUFFER);
+ if (yylex_init(yyscannerp) != 0)
+ elog(ERROR, "yylex_init() failed: %m");
+
+ yyscanner = *yyscannerp;
/*
* Make a scan buffer with special termination needed by flex.
@@ -269,18 +268,16 @@ replication_scanner_init(const char *str)
scanbuf = (char *) palloc(slen + 2);
memcpy(scanbuf, str, slen);
scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
+ yy_scan_buffer(scanbuf, slen + 2, yyscanner);
/* Make sure we start in proper state */
- BEGIN(INITIAL);
repl_pushed_back_token = 0;
}
void
-replication_scanner_finish(void)
+replication_scanner_finish(yyscan_t yyscanner)
{
- yy_delete_buffer(scanbufhandle);
- scanbufhandle = NULL;
+ yylex_destroy(yyscanner);
}
/*
@@ -292,9 +289,10 @@ replication_scanner_finish(void)
* IDENT token here, although some other cases are possible.
*/
bool
-replication_scanner_is_replication_command(void)
+replication_scanner_is_replication_command(yyscan_t yyscanner)
{
- int first_token = replication_yylex();
+ YYSTYPE dummy;
+ int first_token = replication_yylex(&dummy, yyscanner);
switch (first_token)
{
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 371eef3dddc..4971bcfb765 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1951,6 +1951,7 @@ WalSndWaitForWal(XLogRecPtr loc)
bool
exec_replication_command(const char *cmd_string)
{
+ yyscan_t scanner;
int parse_rc;
Node *cmd_node;
const char *cmdtag;
@@ -1990,15 +1991,15 @@ exec_replication_command(const char *cmd_string)
ALLOCSET_DEFAULT_SIZES);
old_context = MemoryContextSwitchTo(cmd_context);
- replication_scanner_init(cmd_string);
+ replication_scanner_init(cmd_string, &scanner);
/*
* Is it a WalSender command?
*/
- if (!replication_scanner_is_replication_command())
+ if (!replication_scanner_is_replication_command(scanner))
{
/* Nope; clean up and get out. */
- replication_scanner_finish();
+ replication_scanner_finish(scanner);
MemoryContextSwitchTo(old_context);
MemoryContextDelete(cmd_context);
@@ -2016,13 +2017,13 @@ exec_replication_command(const char *cmd_string)
/*
* Looks like a WalSender command, so parse it.
*/
- parse_rc = replication_yyparse();
+ parse_rc = replication_yyparse(scanner);
if (parse_rc != 0)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg_internal("replication command parser returned %d",
parse_rc)));
- replication_scanner_finish();
+ replication_scanner_finish(scanner);
cmd_node = replication_parse_result;
diff --git a/src/include/replication/walsender_private.h b/src/include/replication/walsender_private.h
index 41ac736b953..a82ce33c083 100644
--- a/src/include/replication/walsender_private.h
+++ b/src/include/replication/walsender_private.h
@@ -125,13 +125,15 @@ extern void WalSndSetState(WalSndState state);
* Internal functions for parsing the replication grammar, in repl_gram.y and
* repl_scanner.l
*/
-extern int replication_yyparse(void);
-extern int replication_yylex(void);
-extern void replication_yyerror(const char *message) pg_attribute_noreturn();
-extern void replication_scanner_init(const char *str);
-extern void replication_scanner_finish(void);
-extern bool replication_scanner_is_replication_command(void);
-
-extern PGDLLIMPORT Node *replication_parse_result;
+union YYSTYPE;
+typedef void *yyscan_t;
+extern int replication_yyparse(yyscan_t yyscanner);
+extern int replication_yylex(union YYSTYPE *yylval_param, yyscan_t yyscanner);
+extern void replication_yyerror(yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
+extern void replication_scanner_init(const char *str, yyscan_t *yyscannerp);
+extern void replication_scanner_finish(yyscan_t yyscanner);
+extern bool replication_scanner_is_replication_command(yyscan_t yyscanner);
+
+extern PGDLLIMPORT Node *replication_parse_result; /* FIXME */
#endif /* _WALSENDER_PRIVATE_H */
--
2.47.1
v1-0008-replication-parser-Use-palloc-instead-of-malloc-f.patchtext/plain; charset=UTF-8; name=v1-0008-replication-parser-Use-palloc-instead-of-malloc-f.patchDownload
From da950c6e9fcac2855692d079fae7b611efaf6459 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v1 08/19] replication parser: Use palloc() instead of malloc()
for flex
---
src/backend/replication/repl_scanner.l | 30 ++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index 99f2efbb8f9..6bd25e77880 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -61,6 +61,9 @@ static void addlitchar(unsigned char ychar);
%option noinput
%option nounput
%option noyywrap
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
%option warn
%option prefix="replication_yy"
@@ -314,3 +317,30 @@ replication_scanner_is_replication_command(yyscan_t yyscanner)
return false;
}
}
+
+/*
+ * Interface functions to make flex use palloc() instead of malloc().
+ * It'd be better to make these static, but flex insists otherwise.
+ */
+
+void *
+yyalloc(yy_size_t size, yyscan_t yyscanner)
+{
+ return palloc(size);
+}
+
+void *
+yyrealloc(void *ptr, yy_size_t size, yyscan_t yyscanner)
+{
+ if (ptr)
+ return repalloc(ptr, size);
+ else
+ return palloc(size);
+}
+
+void
+yyfree(void *ptr, yyscan_t yyscanner)
+{
+ if (ptr)
+ pfree(ptr);
+}
--
2.47.1
v1-0009-replication-parser-Simplify-flex-scan-buffer-mana.patchtext/plain; charset=UTF-8; name=v1-0009-replication-parser-Simplify-flex-scan-buffer-mana.patchDownload
From 493a07c3d7e397da4499502301b0fea142526f29 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v1 09/19] replication parser: Simplify flex scan buffer
management
---
src/backend/replication/repl_scanner.l | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index 6bd25e77880..56f93e681ed 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -256,8 +256,6 @@ replication_yyerror(yyscan_t yyscanner, const char *message)
void
replication_scanner_init(const char *str, yyscan_t *yyscannerp)
{
- Size slen = strlen(str);
- char *scanbuf;
yyscan_t yyscanner;
if (yylex_init(yyscannerp) != 0)
@@ -265,13 +263,7 @@ replication_scanner_init(const char *str, yyscan_t *yyscannerp)
yyscanner = *yyscannerp;
- /*
- * Make a scan buffer with special termination needed by flex.
- */
- scanbuf = (char *) palloc(slen + 2);
- memcpy(scanbuf, str, slen);
- scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- yy_scan_buffer(scanbuf, slen + 2, yyscanner);
+ yy_scan_string(str, yyscanner);
/* Make sure we start in proper state */
repl_pushed_back_token = 0;
--
2.47.1
v1-0010-syncrep-parser-pure-parser-and-reentrant-scanner.patchtext/plain; charset=UTF-8; name=v1-0010-syncrep-parser-pure-parser-and-reentrant-scanner.patchDownload
From 172e52feb05b37adbca1b3688c14a054baf712fb Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v1 10/19] syncrep parser: pure parser and reentrant scanner
---
src/backend/replication/syncrep.c | 7 ++--
src/backend/replication/syncrep_gram.y | 12 ++++---
src/backend/replication/syncrep_scanner.l | 43 +++++++++++------------
src/include/replication/syncrep.h | 12 ++++---
4 files changed, 38 insertions(+), 36 deletions(-)
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index e1126734ef5..22a2c7fc409 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -992,6 +992,7 @@ check_synchronous_standby_names(char **newval, void **extra, GucSource source)
{
if (*newval != NULL && (*newval)[0] != '\0')
{
+ yyscan_t scanner;
int parse_rc;
SyncRepConfigData *pconf;
@@ -1000,9 +1001,9 @@ check_synchronous_standby_names(char **newval, void **extra, GucSource source)
syncrep_parse_error_msg = NULL;
/* Parse the synchronous_standby_names string */
- syncrep_scanner_init(*newval);
- parse_rc = syncrep_yyparse();
- syncrep_scanner_finish();
+ syncrep_scanner_init(*newval, &scanner);
+ parse_rc = syncrep_yyparse(scanner);
+ syncrep_scanner_finish(scanner);
if (parse_rc != 0 || syncrep_parse_result == NULL)
{
diff --git a/src/backend/replication/syncrep_gram.y b/src/backend/replication/syncrep_gram.y
index e4d9962226c..00b5bf0e522 100644
--- a/src/backend/replication/syncrep_gram.y
+++ b/src/backend/replication/syncrep_gram.y
@@ -26,10 +26,6 @@ char *syncrep_parse_error_msg;
static SyncRepConfigData *create_syncrep_config(const char *num_sync,
List *members, uint8 syncrep_method);
-/* silence -Wmissing-variable-declarations */
-extern int syncrep_yychar;
-extern int syncrep_yynerrs;
-
/*
* Bison doesn't allocate anything that needs to live across parser calls,
* so we can easily have it use palloc instead of malloc. This prevents
@@ -40,6 +36,9 @@ extern int syncrep_yynerrs;
%}
+%parse-param {yyscan_t yyscanner}
+%lex-param {yyscan_t yyscanner}
+%pure-parser
%expect 0
%name-prefix="syncrep_yy"
@@ -60,7 +59,10 @@ extern int syncrep_yynerrs;
%%
result:
- standby_config { syncrep_parse_result = $1; }
+ standby_config {
+ syncrep_parse_result = $1;
+ (void) yynerrs; /* suppress compiler warning */
+ }
;
standby_config:
diff --git a/src/backend/replication/syncrep_scanner.l b/src/backend/replication/syncrep_scanner.l
index 6defb90f13b..b1edb780502 100644
--- a/src/backend/replication/syncrep_scanner.l
+++ b/src/backend/replication/syncrep_scanner.l
@@ -37,15 +37,14 @@ fprintf_to_ereport(const char *fmt, const char *msg)
ereport(ERROR, (errmsg_internal("%s", msg)));
}
-/* Handles to the buffer that the lexer uses internally */
-static YY_BUFFER_STATE scanbufhandle;
-
-static StringInfoData xdbuf;
+static StringInfoData xdbuf; /* FIXME */
/* LCOV_EXCL_START */
%}
+%option reentrant
+%option bison-bridge
%option 8bit
%option never-interactive
%option nodefault
@@ -92,28 +91,28 @@ xdinside [^"]+
appendStringInfoString(&xdbuf, yytext);
}
<xd>{xdstop} {
- syncrep_yylval.str = xdbuf.data;
+ yylval->str = xdbuf.data;
xdbuf.data = NULL;
BEGIN(INITIAL);
return NAME;
}
<xd><<EOF>> {
- syncrep_yyerror("unterminated quoted identifier");
+ syncrep_yyerror("unterminated quoted identifier", yyscanner);
return JUNK;
}
{identifier} {
- syncrep_yylval.str = pstrdup(yytext);
+ yylval->str = pstrdup(yytext);
return NAME;
}
{digit}+ {
- syncrep_yylval.str = pstrdup(yytext);
+ yylval->str = pstrdup(yytext);
return NUM;
}
"*" {
- syncrep_yylval.str = "*";
+ yylval->str = "*";
return NAME;
}
@@ -128,8 +127,10 @@ xdinside [^"]+
/* Needs to be here for access to yytext */
void
-syncrep_yyerror(const char *message)
+syncrep_yyerror(yyscan_t yyscanner, const char *message)
{
+ struct yyguts_t * yyg = (struct yyguts_t *) yyscanner; /* needed for yytext macro */
+
/* report only the first error in a parse operation */
if (syncrep_parse_error_msg)
return;
@@ -142,16 +143,16 @@ syncrep_yyerror(const char *message)
}
void
-syncrep_scanner_init(const char *str)
+syncrep_scanner_init(const char *str, yyscan_t *yyscannerp)
{
Size slen = strlen(str);
char *scanbuf;
+ yyscan_t yyscanner;
- /*
- * Might be left over after ereport()
- */
- if (YY_CURRENT_BUFFER)
- yy_delete_buffer(YY_CURRENT_BUFFER);
+ if (yylex_init(yyscannerp) != 0)
+ elog(ERROR, "yylex_init() failed: %m");
+
+ yyscanner = *yyscannerp;
/*
* Make a scan buffer with special termination needed by flex.
@@ -159,15 +160,11 @@ syncrep_scanner_init(const char *str)
scanbuf = (char *) palloc(slen + 2);
memcpy(scanbuf, str, slen);
scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
-
- /* Make sure we start in proper state */
- BEGIN(INITIAL);
+ yy_scan_buffer(scanbuf, slen + 2, yyscanner);
}
void
-syncrep_scanner_finish(void)
+syncrep_scanner_finish(yyscan_t yyscanner)
{
- yy_delete_buffer(scanbufhandle);
- scanbufhandle = NULL;
+ yylex_destroy(yyscanner);
}
diff --git a/src/include/replication/syncrep.h b/src/include/replication/syncrep.h
index ea439e6da60..b4134dbc91d 100644
--- a/src/include/replication/syncrep.h
+++ b/src/include/replication/syncrep.h
@@ -100,10 +100,12 @@ extern void SyncRepUpdateSyncStandbysDefined(void);
* Internal functions for parsing synchronous_standby_names grammar,
* in syncrep_gram.y and syncrep_scanner.l
*/
-extern int syncrep_yyparse(void);
-extern int syncrep_yylex(void);
-extern void syncrep_yyerror(const char *str);
-extern void syncrep_scanner_init(const char *str);
-extern void syncrep_scanner_finish(void);
+union YYSTYPE;
+typedef void *yyscan_t;
+extern int syncrep_yyparse(yyscan_t yyscanner);
+extern int syncrep_yylex(union YYSTYPE *yylval_param, yyscan_t yyscanner);
+extern void syncrep_yyerror(yyscan_t yyscanner, const char *str);
+extern void syncrep_scanner_init(const char *str, yyscan_t *yyscannerp);
+extern void syncrep_scanner_finish(yyscan_t yyscanner);
#endif /* _SYNCREP_H */
--
2.47.1
v1-0011-syncrep-parser-Use-palloc-instead-of-malloc-for-f.patchtext/plain; charset=UTF-8; name=v1-0011-syncrep-parser-Use-palloc-instead-of-malloc-for-f.patchDownload
From f59ae387f2c63019911b15d150b2176050f5e30d Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v1 11/19] syncrep parser: Use palloc() instead of malloc() for
flex
---
src/backend/replication/syncrep_scanner.l | 30 +++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/src/backend/replication/syncrep_scanner.l b/src/backend/replication/syncrep_scanner.l
index b1edb780502..ac2eecd7804 100644
--- a/src/backend/replication/syncrep_scanner.l
+++ b/src/backend/replication/syncrep_scanner.l
@@ -51,6 +51,9 @@ static StringInfoData xdbuf; /* FIXME */
%option noinput
%option nounput
%option noyywrap
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
%option warn
%option prefix="syncrep_yy"
@@ -168,3 +171,30 @@ syncrep_scanner_finish(yyscan_t yyscanner)
{
yylex_destroy(yyscanner);
}
+
+/*
+ * Interface functions to make flex use palloc() instead of malloc().
+ * It'd be better to make these static, but flex insists otherwise.
+ */
+
+void *
+yyalloc(yy_size_t size, yyscan_t yyscanner)
+{
+ return palloc(size);
+}
+
+void *
+yyrealloc(void *ptr, yy_size_t size, yyscan_t yyscanner)
+{
+ if (ptr)
+ return repalloc(ptr, size);
+ else
+ return palloc(size);
+}
+
+void
+yyfree(void *ptr, yyscan_t yyscanner)
+{
+ if (ptr)
+ pfree(ptr);
+}
--
2.47.1
v1-0012-syncrep-parser-Simplify-flex-scan-buffer-manageme.patchtext/plain; charset=UTF-8; name=v1-0012-syncrep-parser-Simplify-flex-scan-buffer-manageme.patchDownload
From 5404582d2bfb6dfa9ed6ed6d7d7ab6877ab13597 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v1 12/19] syncrep parser: Simplify flex scan buffer management
---
src/backend/replication/syncrep_scanner.l | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/src/backend/replication/syncrep_scanner.l b/src/backend/replication/syncrep_scanner.l
index ac2eecd7804..9c0f4fc2c19 100644
--- a/src/backend/replication/syncrep_scanner.l
+++ b/src/backend/replication/syncrep_scanner.l
@@ -148,8 +148,6 @@ syncrep_yyerror(yyscan_t yyscanner, const char *message)
void
syncrep_scanner_init(const char *str, yyscan_t *yyscannerp)
{
- Size slen = strlen(str);
- char *scanbuf;
yyscan_t yyscanner;
if (yylex_init(yyscannerp) != 0)
@@ -157,13 +155,7 @@ syncrep_scanner_init(const char *str, yyscan_t *yyscannerp)
yyscanner = *yyscannerp;
- /*
- * Make a scan buffer with special termination needed by flex.
- */
- scanbuf = (char *) palloc(slen + 2);
- memcpy(scanbuf, str, slen);
- scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- yy_scan_buffer(scanbuf, slen + 2, yyscanner);
+ yy_scan_string(str, yyscanner);
}
void
--
2.47.1
v1-0013-syncrep-parser-Use-flex-yyextra.patchtext/plain; charset=UTF-8; name=v1-0013-syncrep-parser-Use-flex-yyextra.patchDownload
From 06e41d472b990dbdeda2727fae031dbf68758562 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v1 13/19] syncrep parser: Use flex yyextra
Use flex yyextra to handle context information, instead of global
variables. This complements the earlier patch to make the scanner
reentrant.
---
src/backend/replication/syncrep_scanner.l | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/src/backend/replication/syncrep_scanner.l b/src/backend/replication/syncrep_scanner.l
index 9c0f4fc2c19..85ae4e60604 100644
--- a/src/backend/replication/syncrep_scanner.l
+++ b/src/backend/replication/syncrep_scanner.l
@@ -37,7 +37,11 @@ fprintf_to_ereport(const char *fmt, const char *msg)
ereport(ERROR, (errmsg_internal("%s", msg)));
}
-static StringInfoData xdbuf; /* FIXME */
+struct syncrep_yy_extra_type
+{
+ StringInfoData xdbuf;
+};
+#define YY_EXTRA_TYPE struct syncrep_yy_extra_type *
/* LCOV_EXCL_START */
@@ -84,18 +88,18 @@ xdinside [^"]+
[Ff][Ii][Rr][Ss][Tt] { return FIRST; }
{xdstart} {
- initStringInfo(&xdbuf);
+ initStringInfo(&yyextra->xdbuf);
BEGIN(xd);
}
<xd>{xddouble} {
- appendStringInfoChar(&xdbuf, '"');
+ appendStringInfoChar(&yyextra->xdbuf, '"');
}
<xd>{xdinside} {
- appendStringInfoString(&xdbuf, yytext);
+ appendStringInfoString(&yyextra->xdbuf, yytext);
}
<xd>{xdstop} {
- yylval->str = xdbuf.data;
- xdbuf.data = NULL;
+ yylval->str = yyextra->xdbuf.data;
+ yyextra->xdbuf.data = NULL;
BEGIN(INITIAL);
return NAME;
}
@@ -149,12 +153,15 @@ void
syncrep_scanner_init(const char *str, yyscan_t *yyscannerp)
{
yyscan_t yyscanner;
+ struct syncrep_yy_extra_type yyext;
if (yylex_init(yyscannerp) != 0)
elog(ERROR, "yylex_init() failed: %m");
yyscanner = *yyscannerp;
+ yyset_extra(&yyext, yyscanner);
+
yy_scan_string(str, yyscanner);
}
--
2.47.1
v1-0014-jsonpath-scanner-reentrant-scanner.patchtext/plain; charset=UTF-8; name=v1-0014-jsonpath-scanner-reentrant-scanner.patchDownload
From e4b4c3e37e16e18ef0c6d8cafdbbb03a95feacf5 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v1 14/19] jsonpath scanner: reentrant scanner
Note: The parser was already pure.
---
src/backend/utils/adt/jsonpath_gram.y | 2 +
src/backend/utils/adt/jsonpath_internal.h | 9 +-
src/backend/utils/adt/jsonpath_scan.l | 114 ++++++++--------------
3 files changed, 50 insertions(+), 75 deletions(-)
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index 8733a0eac66..de5a455c96d 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -60,8 +60,10 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%name-prefix="jsonpath_yy"
%parse-param {JsonPathParseResult **result}
%parse-param {struct Node *escontext}
+%parse-param {yyscan_t yyscanner}
%lex-param {JsonPathParseResult **result}
%lex-param {struct Node *escontext}
+%lex-param {yyscan_t yyscanner}
%union
{
diff --git a/src/backend/utils/adt/jsonpath_internal.h b/src/backend/utils/adt/jsonpath_internal.h
index 6cd6d8b652d..71f885475dd 100644
--- a/src/backend/utils/adt/jsonpath_internal.h
+++ b/src/backend/utils/adt/jsonpath_internal.h
@@ -22,17 +22,22 @@ typedef struct JsonPathString
int total;
} JsonPathString;
+typedef void *yyscan_t;
+
#include "utils/jsonpath.h"
#include "jsonpath_gram.h"
#define YY_DECL extern int jsonpath_yylex(YYSTYPE *yylval_param, \
JsonPathParseResult **result, \
- struct Node *escontext)
+ struct Node *escontext, \
+ yyscan_t yyscanner)
YY_DECL;
extern int jsonpath_yyparse(JsonPathParseResult **result,
- struct Node *escontext);
+ struct Node *escontext,
+ yyscan_t yyscanner);
extern void jsonpath_yyerror(JsonPathParseResult **result,
struct Node *escontext,
+ yyscan_t yyscanner,
const char *message);
#endif /* JSONPATH_INTERNAL_H */
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index f5a85de36f5..700c17712d0 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -30,18 +30,13 @@
}
%{
-static JsonPathString scanstring;
-
-/* Handles to the buffer that the lexer uses internally */
-static YY_BUFFER_STATE scanbufhandle;
-static char *scanbuf;
-static int scanbuflen;
+static JsonPathString scanstring; /* FIXME */
static void addstring(bool init, char *s, int l);
static void addchar(bool init, char c);
static enum yytokentype checkKeyword(void);
-static bool parseUnicode(char *s, int l, struct Node *escontext);
-static bool parseHexChar(char *s, struct Node *escontext);
+static bool parseUnicode(char *s, int l, struct Node *escontext, yyscan_t yyscanner);
+static bool parseHexChar(char *s, struct Node *escontext, yyscan_t yyscanner);
/* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
#undef fprintf
@@ -65,6 +60,7 @@ fprintf_to_ereport(const char *fmt, const char *msg)
%option noyywrap
%option warn
%option prefix="jsonpath_yy"
+%option reentrant
%option bison-bridge
%option noyyalloc
%option noyyrealloc
@@ -160,23 +156,23 @@ hex_fail \\x{hexdigit}{0,1}
<xnq,xq,xvq>\\v { addchar(false, '\v'); }
<xnq,xq,xvq>{unicode}+ {
- if (!parseUnicode(yytext, yyleng, escontext))
+ if (!parseUnicode(yytext, yyleng, escontext, yyscanner))
yyterminate();
}
<xnq,xq,xvq>{hex_char} {
- if (!parseHexChar(yytext, escontext))
+ if (!parseHexChar(yytext, escontext, yyscanner))
yyterminate();
}
<xnq,xq,xvq>{unicode}*{unicodefail} {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"invalid Unicode escape sequence");
yyterminate();
}
<xnq,xq,xvq>{hex_fail} {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"invalid hexadecimal character sequence");
yyterminate();
}
@@ -184,20 +180,20 @@ hex_fail \\x{hexdigit}{0,1}
<xnq,xq,xvq>{unicode}+\\ {
/* throw back the \\, and treat as unicode */
yyless(yyleng - 1);
- if (!parseUnicode(yytext, yyleng, escontext))
+ if (!parseUnicode(yytext, yyleng, escontext, yyscanner))
yyterminate();
}
<xnq,xq,xvq>\\. { addchar(false, yytext[1]); }
<xnq,xq,xvq>\\ {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"unexpected end after backslash");
yyterminate();
}
<xq,xvq><<EOF>> {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"unterminated quoted string");
yyterminate();
}
@@ -223,7 +219,7 @@ hex_fail \\x{hexdigit}{0,1}
<xc>\* { }
<xc><<EOF>> {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"unexpected end of comment");
yyterminate();
}
@@ -313,22 +309,22 @@ hex_fail \\x{hexdigit}{0,1}
}
{realfail} {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"invalid numeric literal");
yyterminate();
}
{decinteger_junk} {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"trailing junk after numeric literal");
yyterminate();
}
{decimal_junk} {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"trailing junk after numeric literal");
yyterminate();
}
{real_junk} {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"trailing junk after numeric literal");
yyterminate();
}
@@ -356,8 +352,11 @@ hex_fail \\x{hexdigit}{0,1}
void
jsonpath_yyerror(JsonPathParseResult **result, struct Node *escontext,
+ yyscan_t yyscanner,
const char *message)
{
+ struct yyguts_t * yyg = (struct yyguts_t *) yyscanner; /* needed for yytext macro */
+
/* don't overwrite escontext if it's already been set */
if (SOFT_ERROR_OCCURRED(escontext))
return;
@@ -470,44 +469,6 @@ checkKeyword()
return res;
}
-/*
- * Called before any actual parsing is done
- */
-static void
-jsonpath_scanner_init(const char *str, int slen)
-{
- if (slen <= 0)
- slen = strlen(str);
-
- /*
- * Might be left over after ereport()
- */
- yy_init_globals();
-
- /*
- * Make a scan buffer with special termination needed by flex.
- */
-
- scanbuflen = slen;
- scanbuf = palloc(slen + 2);
- memcpy(scanbuf, str, slen);
- scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
-
- BEGIN(INITIAL);
-}
-
-
-/*
- * Called after parsing is done to clean up after jsonpath_scanner_init()
- */
-static void
-jsonpath_scanner_finish(void)
-{
- yy_delete_buffer(scanbufhandle);
- pfree(scanbuf);
-}
-
/*
* Resize scanstring so that it can append string of given length.
* Reinitialize if required.
@@ -556,20 +517,27 @@ JsonPathParseResult *
parsejsonpath(const char *str, int len, struct Node *escontext)
{
JsonPathParseResult *parseresult;
+ yyscan_t scanner;
+
+ if (jsonpath_yylex_init(&scanner) != 0)
+ elog(ERROR, "yylex_init() failed: %m");
+
+ if (len <= 0)
+ len = strlen(str);
- jsonpath_scanner_init(str, len);
+ jsonpath_yy_scan_bytes(str, len, scanner);
- if (jsonpath_yyparse(&parseresult, escontext) != 0)
- jsonpath_yyerror(NULL, escontext, "invalid input"); /* shouldn't happen */
+ if (jsonpath_yyparse(&parseresult, escontext, scanner) != 0)
+ jsonpath_yyerror(NULL, escontext, scanner, "invalid input"); /* shouldn't happen */
- jsonpath_scanner_finish();
+ jsonpath_yylex_destroy(scanner);
return parseresult;
}
/* Turn hex character into integer */
static bool
-hexval(char c, int *result, struct Node *escontext)
+hexval(char c, int *result, struct Node *escontext, yyscan_t yyscanner)
{
if (c >= '0' && c <= '9')
{
@@ -586,7 +554,7 @@ hexval(char c, int *result, struct Node *escontext)
*result = c - 'A' + 0xA;
return true;
}
- jsonpath_yyerror(NULL, escontext, "invalid hexadecimal digit");
+ jsonpath_yyerror(NULL, escontext, yyscanner, "invalid hexadecimal digit");
return false;
}
@@ -666,7 +634,7 @@ addUnicode(int ch, int *hi_surrogate, struct Node *escontext)
* src/backend/utils/adt/json.c
*/
static bool
-parseUnicode(char *s, int l, struct Node *escontext)
+parseUnicode(char *s, int l, struct Node *escontext, yyscan_t yyscanner)
{
int i = 2;
int hi_surrogate = -1;
@@ -680,7 +648,7 @@ parseUnicode(char *s, int l, struct Node *escontext)
{
while (s[++i] != '}' && i < l)
{
- if (!hexval(s[i], &si, escontext))
+ if (!hexval(s[i], &si, escontext, yyscanner))
return false;
ch = (ch << 4) | si;
}
@@ -690,7 +658,7 @@ parseUnicode(char *s, int l, struct Node *escontext)
{
for (j = 0; j < 4 && i < l; j++)
{
- if (!hexval(s[i++], &si, escontext))
+ if (!hexval(s[i++], &si, escontext, yyscanner))
return false;
ch = (ch << 4) | si;
}
@@ -714,12 +682,12 @@ parseUnicode(char *s, int l, struct Node *escontext)
/* Parse sequence of hex-encoded characters */
static bool
-parseHexChar(char *s, struct Node *escontext)
+parseHexChar(char *s, struct Node *escontext, yyscan_t yyscanner)
{
int s2, s3, ch;
- if (!hexval(s[2], &s2, escontext))
+ if (!hexval(s[2], &s2, escontext, yyscanner))
return false;
- if (!hexval(s[3], &s3, escontext))
+ if (!hexval(s[3], &s3, escontext, yyscanner))
return false;
ch = (s2 << 4) | s3;
@@ -733,13 +701,13 @@ parseHexChar(char *s, struct Node *escontext)
*/
void *
-jsonpath_yyalloc(yy_size_t bytes)
+jsonpath_yyalloc(yy_size_t bytes, yyscan_t yyscanner)
{
return palloc(bytes);
}
void *
-jsonpath_yyrealloc(void *ptr, yy_size_t bytes)
+jsonpath_yyrealloc(void *ptr, yy_size_t bytes, yyscan_t yyscanner)
{
if (ptr)
return repalloc(ptr, bytes);
@@ -748,7 +716,7 @@ jsonpath_yyrealloc(void *ptr, yy_size_t bytes)
}
void
-jsonpath_yyfree(void *ptr)
+jsonpath_yyfree(void *ptr, yyscan_t yyscanner)
{
if (ptr)
pfree(ptr);
--
2.47.1
v1-0015-jsonpath-scanner-Use-flex-yyextra.patchtext/plain; charset=UTF-8; name=v1-0015-jsonpath-scanner-Use-flex-yyextra.patchDownload
From fa4592d5fbc26b536b71b6eae79a65a59cc6e2bc Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v1 15/19] jsonpath scanner: Use flex yyextra
---
src/backend/utils/adt/jsonpath_scan.l | 171 ++++++++++++++------------
1 file changed, 92 insertions(+), 79 deletions(-)
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index 700c17712d0..8ed6c7ddf63 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -30,11 +30,15 @@
}
%{
-static JsonPathString scanstring; /* FIXME */
+struct jsonpath_yy_extra_type
+{
+ JsonPathString scanstring;
+};
+#define YY_EXTRA_TYPE struct jsonpath_yy_extra_type *
-static void addstring(bool init, char *s, int l);
-static void addchar(bool init, char c);
-static enum yytokentype checkKeyword(void);
+static void addstring(bool init, char *s, int l, yyscan_t yyscanner);
+static void addchar(bool init, char c, yyscan_t yyscanner);
+static enum yytokentype checkKeyword(yyscan_t yyscanner);
static bool parseUnicode(char *s, int l, struct Node *escontext, yyscan_t yyscanner);
static bool parseHexChar(char *s, struct Node *escontext, yyscan_t yyscanner);
@@ -116,44 +120,44 @@ hex_fail \\x{hexdigit}{0,1}
%%
<xnq>{other}+ {
- addstring(false, yytext, yyleng);
+ addstring(false, yytext, yyleng, yyscanner);
}
<xnq>{blank}+ {
- yylval->str = scanstring;
+ yylval->str = yyextra->scanstring;
BEGIN INITIAL;
- return checkKeyword();
+ return checkKeyword(yyscanner);
}
<xnq>\/\* {
- yylval->str = scanstring;
+ yylval->str = yyextra->scanstring;
BEGIN xc;
}
<xnq>({special}|\") {
- yylval->str = scanstring;
+ yylval->str = yyextra->scanstring;
yyless(0);
BEGIN INITIAL;
- return checkKeyword();
+ return checkKeyword(yyscanner);
}
<xnq><<EOF>> {
- yylval->str = scanstring;
+ yylval->str = yyextra->scanstring;
BEGIN INITIAL;
- return checkKeyword();
+ return checkKeyword(yyscanner);
}
-<xnq,xq,xvq>\\b { addchar(false, '\b'); }
+<xnq,xq,xvq>\\b { addchar(false, '\b', yyscanner); }
-<xnq,xq,xvq>\\f { addchar(false, '\f'); }
+<xnq,xq,xvq>\\f { addchar(false, '\f', yyscanner); }
-<xnq,xq,xvq>\\n { addchar(false, '\n'); }
+<xnq,xq,xvq>\\n { addchar(false, '\n', yyscanner); }
-<xnq,xq,xvq>\\r { addchar(false, '\r'); }
+<xnq,xq,xvq>\\r { addchar(false, '\r', yyscanner); }
-<xnq,xq,xvq>\\t { addchar(false, '\t'); }
+<xnq,xq,xvq>\\t { addchar(false, '\t', yyscanner); }
-<xnq,xq,xvq>\\v { addchar(false, '\v'); }
+<xnq,xq,xvq>\\v { addchar(false, '\v', yyscanner); }
<xnq,xq,xvq>{unicode}+ {
if (!parseUnicode(yytext, yyleng, escontext, yyscanner))
@@ -184,7 +188,7 @@ hex_fail \\x{hexdigit}{0,1}
yyterminate();
}
-<xnq,xq,xvq>\\. { addchar(false, yytext[1]); }
+<xnq,xq,xvq>\\. { addchar(false, yytext[1], yyscanner); }
<xnq,xq,xvq>\\ {
jsonpath_yyerror(NULL, escontext, yyscanner,
@@ -199,18 +203,18 @@ hex_fail \\x{hexdigit}{0,1}
}
<xq>\" {
- yylval->str = scanstring;
+ yylval->str = yyextra->scanstring;
BEGIN INITIAL;
return STRING_P;
}
<xvq>\" {
- yylval->str = scanstring;
+ yylval->str = yyextra->scanstring;
BEGIN INITIAL;
return VARIABLE_P;
}
-<xq,xvq>[^\\\"]+ { addstring(false, yytext, yyleng); }
+<xq,xvq>[^\\\"]+ { addstring(false, yytext, yyleng, yyscanner); }
<xc>\*\/ { BEGIN INITIAL; }
@@ -246,14 +250,14 @@ hex_fail \\x{hexdigit}{0,1}
\> { return GREATER_P; }
\${other}+ {
- addstring(true, yytext + 1, yyleng - 1);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext + 1, yyleng - 1, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return VARIABLE_P;
}
\$\" {
- addchar(true, '\0');
+ addchar(true, '\0', yyscanner);
BEGIN xvq;
}
@@ -262,49 +266,49 @@ hex_fail \\x{hexdigit}{0,1}
{blank}+ { /* ignore */ }
\/\* {
- addchar(true, '\0');
+ addchar(true, '\0', yyscanner);
BEGIN xc;
}
{real} {
- addstring(true, yytext, yyleng);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext, yyleng, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return NUMERIC_P;
}
{decimal} {
- addstring(true, yytext, yyleng);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext, yyleng, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return NUMERIC_P;
}
{decinteger} {
- addstring(true, yytext, yyleng);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext, yyleng, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return INT_P;
}
{hexinteger} {
- addstring(true, yytext, yyleng);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext, yyleng, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return INT_P;
}
{octinteger} {
- addstring(true, yytext, yyleng);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext, yyleng, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return INT_P;
}
{bininteger} {
- addstring(true, yytext, yyleng);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext, yyleng, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return INT_P;
}
@@ -329,18 +333,18 @@ hex_fail \\x{hexdigit}{0,1}
yyterminate();
}
\" {
- addchar(true, '\0');
+ addchar(true, '\0', yyscanner);
BEGIN xq;
}
\\ {
yyless(0);
- addchar(true, '\0');
+ addchar(true, '\0', yyscanner);
BEGIN xnq;
}
{other}+ {
- addstring(true, yytext, yyleng);
+ addstring(true, yytext, yyleng, yyscanner);
BEGIN xnq;
}
@@ -350,6 +354,10 @@ hex_fail \\x{hexdigit}{0,1}
/* LCOV_EXCL_STOP */
+/* see scan.l */
+#undef yyextra
+#define yyextra (((struct yyguts_t *) yyscanner)->yyextra_r)
+
void
jsonpath_yyerror(JsonPathParseResult **result, struct Node *escontext,
yyscan_t yyscanner,
@@ -426,9 +434,11 @@ static const JsonPathKeyword keywords[] = {
{ 12,false, TIMESTAMP_TZ_P, "timestamp_tz"},
};
-/* Check if current scanstring value is a keyword */
+/*
+ * Check if current scanstring value is a keyword
+ */
static enum yytokentype
-checkKeyword()
+checkKeyword(yyscan_t yyscanner)
{
int res = IDENT_P;
int diff;
@@ -436,18 +446,18 @@ checkKeyword()
*StopHigh = keywords + lengthof(keywords),
*StopMiddle;
- if (scanstring.len > keywords[lengthof(keywords) - 1].len)
+ if (yyextra->scanstring.len > keywords[lengthof(keywords) - 1].len)
return res;
while (StopLow < StopHigh)
{
StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
- if (StopMiddle->len == scanstring.len)
- diff = pg_strncasecmp(StopMiddle->keyword, scanstring.val,
- scanstring.len);
+ if (StopMiddle->len == yyextra->scanstring.len)
+ diff = pg_strncasecmp(StopMiddle->keyword, yyextra->scanstring.val,
+ yyextra->scanstring.len);
else
- diff = StopMiddle->len - scanstring.len;
+ diff = StopMiddle->len - yyextra->scanstring.len;
if (diff < 0)
StopLow = StopMiddle + 1;
@@ -456,8 +466,8 @@ checkKeyword()
else
{
if (StopMiddle->lowercase)
- diff = strncmp(StopMiddle->keyword, scanstring.val,
- scanstring.len);
+ diff = strncmp(StopMiddle->keyword, yyextra->scanstring.val,
+ yyextra->scanstring.len);
if (diff == 0)
res = StopMiddle->val;
@@ -474,42 +484,42 @@ checkKeyword()
* Reinitialize if required.
*/
static void
-resizeString(bool init, int appendLen)
+resizeString(bool init, int appendLen, yyscan_t yyscanner)
{
if (init)
{
- scanstring.total = Max(32, appendLen);
- scanstring.val = (char *) palloc(scanstring.total);
- scanstring.len = 0;
+ yyextra->scanstring.total = Max(32, appendLen);
+ yyextra->scanstring.val = (char *) palloc(yyextra->scanstring.total);
+ yyextra->scanstring.len = 0;
}
else
{
- if (scanstring.len + appendLen >= scanstring.total)
+ if (yyextra->scanstring.len + appendLen >= yyextra->scanstring.total)
{
- while (scanstring.len + appendLen >= scanstring.total)
- scanstring.total *= 2;
- scanstring.val = repalloc(scanstring.val, scanstring.total);
+ while (yyextra->scanstring.len + appendLen >= yyextra->scanstring.total)
+ yyextra->scanstring.total *= 2;
+ yyextra->scanstring.val = repalloc(yyextra->scanstring.val, yyextra->scanstring.total);
}
}
}
/* Add set of bytes at "s" of length "l" to scanstring */
static void
-addstring(bool init, char *s, int l)
+addstring(bool init, char *s, int l, yyscan_t yyscanner)
{
- resizeString(init, l + 1);
- memcpy(scanstring.val + scanstring.len, s, l);
- scanstring.len += l;
+ resizeString(init, l + 1, yyscanner);
+ memcpy(yyextra->scanstring.val + yyextra->scanstring.len, s, l);
+ yyextra->scanstring.len += l;
}
/* Add single byte "c" to scanstring */
static void
-addchar(bool init, char c)
+addchar(bool init, char c, yyscan_t yyscanner)
{
- resizeString(init, 1);
- scanstring.val[scanstring.len] = c;
+ resizeString(init, 1, yyscanner);
+ yyextra->scanstring.val[yyextra->scanstring.len] = c;
if (c != '\0')
- scanstring.len++;
+ yyextra->scanstring.len++;
}
/* Interface to jsonpath parser */
@@ -518,10 +528,13 @@ parsejsonpath(const char *str, int len, struct Node *escontext)
{
JsonPathParseResult *parseresult;
yyscan_t scanner;
+ struct jsonpath_yy_extra_type yyext;
if (jsonpath_yylex_init(&scanner) != 0)
elog(ERROR, "yylex_init() failed: %m");
+ yyset_extra(&yyext, scanner);
+
if (len <= 0)
len = strlen(str);
@@ -560,7 +573,7 @@ hexval(char c, int *result, struct Node *escontext, yyscan_t yyscanner)
/* Add given unicode character to scanstring */
static bool
-addUnicodeChar(int ch, struct Node *escontext)
+addUnicodeChar(int ch, struct Node *escontext, yyscan_t yyscanner)
{
if (ch == 0)
{
@@ -586,14 +599,14 @@ addUnicodeChar(int ch, struct Node *escontext)
ereturn(escontext, false,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("could not convert Unicode to server encoding")));
- addstring(false, cbuf, strlen(cbuf));
+ addstring(false, cbuf, strlen(cbuf), yyscanner);
}
return true;
}
/* Add unicode character, processing any surrogate pairs */
static bool
-addUnicode(int ch, int *hi_surrogate, struct Node *escontext)
+addUnicode(int ch, int *hi_surrogate, struct Node *escontext, yyscan_t yyscanner)
{
if (is_utf16_surrogate_first(ch))
{
@@ -626,7 +639,7 @@ addUnicode(int ch, int *hi_surrogate, struct Node *escontext)
"surrogate.")));
}
- return addUnicodeChar(ch, escontext);
+ return addUnicodeChar(ch, escontext, yyscanner);
}
/*
@@ -664,7 +677,7 @@ parseUnicode(char *s, int l, struct Node *escontext, yyscan_t yyscanner)
}
}
- if (! addUnicode(ch, &hi_surrogate, escontext))
+ if (! addUnicode(ch, &hi_surrogate, escontext, yyscanner))
return false;
}
@@ -692,7 +705,7 @@ parseHexChar(char *s, struct Node *escontext, yyscan_t yyscanner)
ch = (s2 << 4) | s3;
- return addUnicodeChar(ch, escontext);
+ return addUnicodeChar(ch, escontext, yyscanner);
}
/*
--
2.47.1
v1-0016-bootstrap-pure-parser-and-reentrant-scanner.patchtext/plain; charset=UTF-8; name=v1-0016-bootstrap-pure-parser-and-reentrant-scanner.patchDownload
From ab6e50ec0ac5e7ef79a373cccf9319ba9a985782 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Tue, 3 Dec 2024 15:23:36 +0100
Subject: [PATCH v1 16/19] bootstrap: pure parser and reentrant scanner
---
src/backend/bootstrap/bootparse.y | 9 +++--
src/backend/bootstrap/bootscanner.l | 61 +++++++++++++++--------------
src/backend/bootstrap/bootstrap.c | 6 ++-
src/include/bootstrap/bootstrap.h | 9 +++--
4 files changed, 47 insertions(+), 38 deletions(-)
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 73a7592fb71..05ef26c07d0 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -34,10 +34,6 @@
#include "bootparse.h"
-/* silence -Wmissing-variable-declarations */
-extern int boot_yychar;
-extern int boot_yynerrs;
-
/*
* Bison doesn't allocate anything that needs to live across parser calls,
@@ -81,6 +77,9 @@ static int num_columns_read = 0;
%}
+%parse-param {yyscan_t yyscanner}
+%lex-param {yyscan_t yyscanner}
+%pure-parser
%expect 0
%name-prefix="boot_yy"
@@ -141,6 +140,8 @@ Boot_OpenStmt:
do_start();
boot_openrel($2);
do_end();
+
+ (void) yynerrs; /* suppress compiler warning */
}
;
diff --git a/src/backend/bootstrap/bootscanner.l b/src/backend/bootstrap/bootscanner.l
index 31d63ff3015..c4e7139fa41 100644
--- a/src/backend/bootstrap/bootscanner.l
+++ b/src/backend/bootstrap/bootscanner.l
@@ -39,11 +39,10 @@ fprintf_to_ereport(const char *fmt, const char *msg)
ereport(ERROR, (errmsg_internal("%s", msg)));
}
-
-static int yyline = 1; /* line number for error reporting */
-
%}
+%option reentrant
+%option bison-bridge
%option 8bit
%option never-interactive
%option nodefault
@@ -58,7 +57,7 @@ id [-A-Za-z0-9_]+
sid \'([^']|\'\')*\'
/*
- * Keyword tokens return the keyword text (as a constant string) in boot_yylval.kw,
+ * Keyword tokens return the keyword text (as a constant string) in yylval->kw,
* just in case that's needed because we want to treat the keyword as an
* unreserved identifier. Note that _null_ is not treated as a keyword
* for this purpose; it's the one "reserved word" in the bootstrap syntax.
@@ -66,23 +65,23 @@ sid \'([^']|\'\')*\'
* Notice that all the keywords are case-sensitive, and for historical
* reasons some must be upper case.
*
- * String tokens return a palloc'd string in boot_yylval.str.
+ * String tokens return a palloc'd string in yylval->str.
*/
%%
-open { boot_yylval.kw = "open"; return OPEN; }
+open { yylval->kw = "open"; return OPEN; }
-close { boot_yylval.kw = "close"; return XCLOSE; }
+close { yylval->kw = "close"; return XCLOSE; }
-create { boot_yylval.kw = "create"; return XCREATE; }
+create { yylval->kw = "create"; return XCREATE; }
-OID { boot_yylval.kw = "OID"; return OBJ_ID; }
-bootstrap { boot_yylval.kw = "bootstrap"; return XBOOTSTRAP; }
-shared_relation { boot_yylval.kw = "shared_relation"; return XSHARED_RELATION; }
-rowtype_oid { boot_yylval.kw = "rowtype_oid"; return XROWTYPE_OID; }
+OID { yylval->kw = "OID"; return OBJ_ID; }
+bootstrap { yylval->kw = "bootstrap"; return XBOOTSTRAP; }
+shared_relation { yylval->kw = "shared_relation"; return XSHARED_RELATION; }
+rowtype_oid { yylval->kw = "rowtype_oid"; return XROWTYPE_OID; }
-insert { boot_yylval.kw = "insert"; return INSERT_TUPLE; }
+insert { yylval->kw = "insert"; return INSERT_TUPLE; }
_null_ { return NULLVAL; }
@@ -91,35 +90,35 @@ _null_ { return NULLVAL; }
"(" { return LPAREN; }
")" { return RPAREN; }
-[\n] { yyline++; }
+[\n] { yylineno++; }
[\r\t ] ;
^\#[^\n]* ; /* drop everything after "#" for comments */
-declare { boot_yylval.kw = "declare"; return XDECLARE; }
-build { boot_yylval.kw = "build"; return XBUILD; }
-indices { boot_yylval.kw = "indices"; return INDICES; }
-unique { boot_yylval.kw = "unique"; return UNIQUE; }
-index { boot_yylval.kw = "index"; return INDEX; }
-on { boot_yylval.kw = "on"; return ON; }
-using { boot_yylval.kw = "using"; return USING; }
-toast { boot_yylval.kw = "toast"; return XTOAST; }
-FORCE { boot_yylval.kw = "FORCE"; return XFORCE; }
-NOT { boot_yylval.kw = "NOT"; return XNOT; }
-NULL { boot_yylval.kw = "NULL"; return XNULL; }
+declare { yylval->kw = "declare"; return XDECLARE; }
+build { yylval->kw = "build"; return XBUILD; }
+indices { yylval->kw = "indices"; return INDICES; }
+unique { yylval->kw = "unique"; return UNIQUE; }
+index { yylval->kw = "index"; return INDEX; }
+on { yylval->kw = "on"; return ON; }
+using { yylval->kw = "using"; return USING; }
+toast { yylval->kw = "toast"; return XTOAST; }
+FORCE { yylval->kw = "FORCE"; return XFORCE; }
+NOT { yylval->kw = "NOT"; return XNOT; }
+NULL { yylval->kw = "NULL"; return XNULL; }
{id} {
- boot_yylval.str = pstrdup(yytext);
+ yylval->str = pstrdup(yytext);
return ID;
}
{sid} {
/* strip quotes and escapes */
- boot_yylval.str = DeescapeQuotedString(yytext);
+ yylval->str = DeescapeQuotedString(yytext);
return ID;
}
. {
- elog(ERROR, "syntax error at line %d: unexpected character \"%s\"", yyline, yytext);
+ elog(ERROR, "syntax error at line %d: unexpected character \"%s\"", yylineno, yytext);
}
%%
@@ -127,7 +126,9 @@ NULL { boot_yylval.kw = "NULL"; return XNULL; }
/* LCOV_EXCL_STOP */
void
-boot_yyerror(const char *message)
+boot_yyerror(yyscan_t yyscanner, const char *message)
{
- elog(ERROR, "%s at line %d", message, yyline);
+ struct yyguts_t * yyg = (struct yyguts_t *) yyscanner; /* needed for yylineno macro */
+
+ elog(ERROR, "%s at line %d", message, yylineno);
}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index a5217773ffc..388458a5724 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -202,6 +202,7 @@ BootstrapModeMain(int argc, char *argv[], bool check_only)
int flag;
char *userDoption = NULL;
uint32 bootstrap_data_checksum_version = 0; /* No checksum */
+ yyscan_t scanner;
Assert(!IsUnderPostmaster);
@@ -372,11 +373,14 @@ BootstrapModeMain(int argc, char *argv[], bool check_only)
Nulls[i] = false;
}
+ if (boot_yylex_init(&scanner) != 0)
+ elog(ERROR, "yylex_init() failed: %m");
+
/*
* Process bootstrap input.
*/
StartTransactionCommand();
- boot_yyparse();
+ boot_yyparse(scanner);
CommitTransactionCommand();
/*
diff --git a/src/include/bootstrap/bootstrap.h b/src/include/bootstrap/bootstrap.h
index 73b78b31335..ccec9415acb 100644
--- a/src/include/bootstrap/bootstrap.h
+++ b/src/include/bootstrap/bootstrap.h
@@ -55,9 +55,12 @@ extern void boot_get_type_io_data(Oid typid,
Oid *typinput,
Oid *typoutput);
-extern int boot_yyparse(void);
+union YYSTYPE;
+typedef void *yyscan_t;
-extern int boot_yylex(void);
-extern void boot_yyerror(const char *message) pg_attribute_noreturn();
+extern int boot_yyparse(yyscan_t yyscanner);
+extern int boot_yylex_init(yyscan_t *yyscannerp);
+extern int boot_yylex(union YYSTYPE *yylval_param, yyscan_t yyscanner);
+extern void boot_yyerror(yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
#endif /* BOOTSTRAP_H */
--
2.47.1
v1-0017-bootstrap-Use-palloc-instead-of-malloc-for-flex.patchtext/plain; charset=UTF-8; name=v1-0017-bootstrap-Use-palloc-instead-of-malloc-for-flex.patchDownload
From dfb1624981c5c5b517e5049b5274fc5d45eac177 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Tue, 3 Dec 2024 15:28:16 +0100
Subject: [PATCH v1 17/19] bootstrap: Use palloc() instead of malloc() for flex
---
src/backend/bootstrap/bootscanner.l | 30 +++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/src/backend/bootstrap/bootscanner.l b/src/backend/bootstrap/bootscanner.l
index c4e7139fa41..ad33b4e3fd8 100644
--- a/src/backend/bootstrap/bootscanner.l
+++ b/src/backend/bootstrap/bootscanner.l
@@ -49,6 +49,9 @@ fprintf_to_ereport(const char *fmt, const char *msg)
%option noinput
%option nounput
%option noyywrap
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
%option warn
%option prefix="boot_yy"
@@ -132,3 +135,30 @@ boot_yyerror(yyscan_t yyscanner, const char *message)
elog(ERROR, "%s at line %d", message, yylineno);
}
+
+/*
+ * Interface functions to make flex use palloc() instead of malloc().
+ * It'd be better to make these static, but flex insists otherwise.
+ */
+
+void *
+yyalloc(yy_size_t size, yyscan_t yyscanner)
+{
+ return palloc(size);
+}
+
+void *
+yyrealloc(void *ptr, yy_size_t size, yyscan_t yyscanner)
+{
+ if (ptr)
+ return repalloc(ptr, size);
+ else
+ return palloc(size);
+}
+
+void
+yyfree(void *ptr, yyscan_t yyscanner)
+{
+ if (ptr)
+ pfree(ptr);
+}
--
2.47.1
On 16/12/2024 09:39, Peter Eisentraut wrote:
On 02.12.24 10:46, Peter Eisentraut wrote:
This patch series changes several parsers in the backend and contrib
modules to use bison pure parsers and flex reentrant scanners. This is
ultimately toward thread-safety, but I think it's also just nicer in
general, and it might also fix a few possible small memory leaks.I did a bit more work on this, so here is an updated patch set.
Looks good to me. There's more work to be done, but this is all good
steps in the right direction.
--
Heikki Linnakangas
Neon (https://neon.tech)
On 12/16/24 8:39 AM, Peter Eisentraut wrote:
I'll leave it at this for now and wait for some reviews.
I really like this work since it makes the code cleaner to read on top
of paving the way for threading.
Reviewed the patches and found a couple of issues.
- Shouldn't yyext in syncrep_scanner_init() be allocated on the heap? Or
at least on the stack but by the caller?
- I think you have flipped the parameters of replication_yyerror(), see
attached fixup patch.
- Some white space issues fixed in an attached fixup patch.
- Also fixed the static remaining variables in the replication parser in
an attached patch.
- There seems to be a lot left to do to make the plpgsql scanner
actually re-entrant so I do not think it would makes sense to commit the
patch which sets the re-entrant option before that is done.
Andreas
Attachments:
v2-0020-fixup-replication-parser-pure-parser-and-reentran.patchtext/x-patch; charset=UTF-8; name=v2-0020-fixup-replication-parser-pure-parser-and-reentran.patchDownload
From 6cada3e9f2d8929e8646bc2e3894ad74ca10eb43 Mon Sep 17 00:00:00 2001
From: Andreas Karlsson <andreas@proxel.se>
Date: Tue, 17 Dec 2024 00:32:23 +0100
Subject: [PATCH v2 20/22] fixup! replication parser: pure parser and reentrant
scanner
Fix argument order.
---
src/backend/replication/repl_scanner.l | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index 56f93e681ed..6776d3a85a8 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -152,7 +152,7 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
uint32 hi,
lo;
if (sscanf(yytext, "%X/%X", &hi, &lo) != 2)
- replication_yyerror("invalid streaming start location", yyscanner);
+ replication_yyerror(yyscanner, "invalid streaming start location");
yylval->recptr = ((uint64) hi) << 32 | lo;
return RECPTR;
}
@@ -209,7 +209,7 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
return yytext[0];
}
-<xq,xd><<EOF>> { replication_yyerror("unterminated quoted string", yyscanner); }
+<xq,xd><<EOF>> { replication_yyerror(yyscanner, "unterminated quoted string"); }
<<EOF>> {
--
2.45.2
v2-0021-fixup-replication-parser-pure-parser-and-reentran.patchtext/x-patch; charset=UTF-8; name=v2-0021-fixup-replication-parser-pure-parser-and-reentran.patchDownload
From a05de695de6c73878e1fe6a74c24936d8c69f05a Mon Sep 17 00:00:00 2001
From: Andreas Karlsson <andreas@proxel.se>
Date: Tue, 17 Dec 2024 00:32:47 +0100
Subject: [PATCH v2 21/22] fixup! replication parser: pure parser and reentrant
scanner
Fix whitespace.
---
src/backend/replication/repl_scanner.l | 1 -
src/backend/replication/walsender.c | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index 6776d3a85a8..6388024a598 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -252,7 +252,6 @@ replication_yyerror(yyscan_t yyscanner, const char *message)
errmsg_internal("%s", message)));
}
-
void
replication_scanner_init(const char *str, yyscan_t *yyscannerp)
{
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 4971bcfb765..dc25dd6af91 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1951,7 +1951,7 @@ WalSndWaitForWal(XLogRecPtr loc)
bool
exec_replication_command(const char *cmd_string)
{
- yyscan_t scanner;
+ yyscan_t scanner;
int parse_rc;
Node *cmd_node;
const char *cmdtag;
--
2.45.2
v2-0022-replication-parser-Use-flex-yyextra.patchtext/x-patch; charset=UTF-8; name=v2-0022-replication-parser-Use-flex-yyextra.patchDownload
From 676ed65db9cac70189c1f33269fbbac519f59ca9 Mon Sep 17 00:00:00 2001
From: Andreas Karlsson <andreas@proxel.se>
Date: Mon, 16 Dec 2024 23:13:36 +0100
Subject: [PATCH v2 22/22] replication parser: Use flex yyextra
Use flex yyextra to handle context information, instead of global
variables. This complements the earlier patch to make the scanner
reentrant.
---
src/backend/replication/repl_scanner.l | 75 +++++++++++++++-----------
1 file changed, 45 insertions(+), 30 deletions(-)
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index 6388024a598..1c4fc302df0 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -38,16 +38,20 @@ fprintf_to_ereport(const char *fmt, const char *msg)
ereport(ERROR, (errmsg_internal("%s", msg)));
}
-/* Pushed-back token (we only handle one) */
-static int repl_pushed_back_token; /* FIXME */
+struct repl_yy_extra_type
+{
+ /* Pushed-back token (we only handle one) */
+ int repl_pushed_back_token;
-/* Work area for collecting literals */
-static StringInfoData litbuf; /* FIXME */
+ /* Work area for collecting literals */
+ StringInfoData litbuf;
+};
+#define YY_EXTRA_TYPE struct repl_yy_extra_type *
-static void startlit(void);
-static char *litbufdup(void);
-static void addlit(char *ytext, int yleng);
-static void addlitchar(unsigned char ychar);
+static void startlit(yyscan_t yyscanner);
+static char *litbufdup(yyscan_t yyscanner);
+static void addlit(char *ytext, int yleng, yyscan_t yyscanner);
+static void addlitchar(unsigned char ychar, yyscan_t yyscanner);
/* LCOV_EXCL_START */
@@ -110,11 +114,11 @@ identifier {ident_start}{ident_cont}*
/* This code is inserted at the start of replication_yylex() */
/* If we have a pushed-back token, return that. */
- if (repl_pushed_back_token)
+ if (yyextra->repl_pushed_back_token)
{
- int result = repl_pushed_back_token;
+ int result = yyextra->repl_pushed_back_token;
- repl_pushed_back_token = 0;
+ yyextra->repl_pushed_back_token = 0;
return result;
}
%}
@@ -159,27 +163,27 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
{xqstart} {
BEGIN(xq);
- startlit();
+ startlit(yyscanner);
}
<xq>{quotestop} {
yyless(1);
BEGIN(INITIAL);
- yylval->str = litbufdup();
+ yylval->str = litbufdup(yyscanner);
return SCONST;
}
<xq>{xqdouble} {
- addlitchar('\'');
+ addlitchar('\'', yyscanner);
}
<xq>{xqinside} {
- addlit(yytext, yyleng);
+ addlit(yytext, yyleng, yyscanner);
}
{xdstart} {
BEGIN(xd);
- startlit();
+ startlit(yyscanner);
}
<xd>{xdstop} {
@@ -187,14 +191,14 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
yyless(1);
BEGIN(INITIAL);
- yylval->str = litbufdup();
+ yylval->str = litbufdup(yyscanner);
len = strlen(yylval->str);
truncate_identifier(yylval->str, len, true);
return IDENT;
}
<xd>{xdinside} {
- addlit(yytext, yyleng);
+ addlit(yytext, yyleng, yyscanner);
}
{identifier} {
@@ -220,28 +224,37 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
/* LCOV_EXCL_STOP */
+/*
+ * Arrange access to yyextra for subroutines of the main yylex() function.
+ * We expect each subroutine to have a yyscanner parameter. Rather than
+ * use the yyget_xxx functions, which might or might not get inlined by the
+ * compiler, we cheat just a bit and cast yyscanner to the right type.
+ */
+#undef yyextra
+#define yyextra (((struct yyguts_t *) yyscanner)->yyextra_r)
+
static void
-startlit(void)
+startlit(yyscan_t yyscanner)
{
- initStringInfo(&litbuf);
+ initStringInfo(&yyextra->litbuf);
}
static char *
-litbufdup(void)
+litbufdup(yyscan_t yyscanner)
{
- return litbuf.data;
+ return yyextra->litbuf.data;
}
static void
-addlit(char *ytext, int yleng)
+addlit(char *ytext, int yleng, yyscan_t yyscanner)
{
- appendBinaryStringInfo(&litbuf, ytext, yleng);
+ appendBinaryStringInfo(&yyextra->litbuf, ytext, yleng);
}
static void
-addlitchar(unsigned char ychar)
+addlitchar(unsigned char ychar, yyscan_t yyscanner)
{
- appendStringInfoChar(&litbuf, ychar);
+ appendStringInfoChar(&yyextra->litbuf, ychar);
}
void
@@ -256,16 +269,18 @@ void
replication_scanner_init(const char *str, yyscan_t *yyscannerp)
{
yyscan_t yyscanner;
+ struct repl_yy_extra_type *yyext = palloc(sizeof(struct repl_yy_extra_type));
if (yylex_init(yyscannerp) != 0)
elog(ERROR, "yylex_init() failed: %m");
yyscanner = *yyscannerp;
- yy_scan_string(str, yyscanner);
+ yyset_extra(yyext, yyscanner);
+
+ yyext->repl_pushed_back_token = 0;
- /* Make sure we start in proper state */
- repl_pushed_back_token = 0;
+ yy_scan_string(str, yyscanner);
}
void
@@ -301,7 +316,7 @@ replication_scanner_is_replication_command(yyscan_t yyscanner)
case K_UPLOAD_MANIFEST:
case K_SHOW:
/* Yes; push back the first token so we can parse later. */
- repl_pushed_back_token = first_token;
+ yyextra->repl_pushed_back_token = first_token;
return true;
default:
/* Nope; we don't bother to push back the token. */
--
2.45.2
I started committing the cube and seg pieces. There were a couple of
complaints from the buildfarm, like
ccache clang -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith
-Wdeclaration-after-statement -Werror=vla
-Werror=unguarded-availability-new -Wendif-labels
-Wmissing-format-attribute -Wcast-function-type -Wformat-security
-Wmissing-variable-declarations -fno-strict-aliasing -fwrapv
-Wno-unused-command-line-argument -Wno-compound-token-split-by-macro -g
-O2 -fno-common -fsanitize=alignment -fsanitize-trap=alignment
-Wno-deprecated-declarations -Werror -fvisibility=hidden -I. -I.
-I../../src/include -isysroot
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.3.sdk
-DWRITE_READ_PARSE_PLAN_TREES -DSTRESS_SORT_INT_MIN
-DENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS
-I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include
-I/usr/local/include -I/usr/local/ssl/include -c -o segscan.o segscan.c
segscan.c:348:15: error: redefinition of typedef 'yyscan_t' is a C11
feature [-Werror,-Wtypedef-redefinition]
typedef void* yyscan_t;
^
./segdata.h:19:15: note: previous definition is here
typedef void *yyscan_t;
^
I can fix that with the attached patch.
The symbol YY_TYPEDEF_YY_SCANNER_T isn't documented, but we already use
it elsewhere in the code.
Note that in replication/syncrep.h and replication/walsender_private.h
we have to have an #ifndef wrapper because there are files that end up
including both headers. Maybe we should put that #ifndef wrapper
everywhere for consistency?
Any thoughts?
(Also, we should probably figure out a way to get these warnings before
things hit the buildfarm.)
Attachments:
typedef-yyscan_t-fix.patch.nocfbottext/plain; charset=UTF-8; name=typedef-yyscan_t-fix.patch.nocfbotDownload
diff --git a/contrib/cube/cubedata.h b/contrib/cube/cubedata.h
index 8bfcc6e99a2..006a6d2c8c4 100644
--- a/contrib/cube/cubedata.h
+++ b/contrib/cube/cubedata.h
@@ -62,6 +62,7 @@ typedef struct NDBOX
/* for cubescan.l and cubeparse.y */
/* All grammar constructs return strings */
#define YYSTYPE char *
+#define YY_TYPEDEF_YY_SCANNER_T
typedef void *yyscan_t;
/* in cubescan.l */
diff --git a/contrib/seg/segdata.h b/contrib/seg/segdata.h
index 7bc7c83dca3..b55ff78880b 100644
--- a/contrib/seg/segdata.h
+++ b/contrib/seg/segdata.h
@@ -16,6 +16,7 @@ extern int significant_digits(const char *s);
/* for segscan.l and segparse.y */
union YYSTYPE;
+#define YY_TYPEDEF_YY_SCANNER_T
typedef void *yyscan_t;
/* in segscan.l */
diff --git a/src/backend/utils/adt/jsonpath_internal.h b/src/backend/utils/adt/jsonpath_internal.h
index 71f885475dd..0d8b89abea2 100644
--- a/src/backend/utils/adt/jsonpath_internal.h
+++ b/src/backend/utils/adt/jsonpath_internal.h
@@ -22,6 +22,7 @@ typedef struct JsonPathString
int total;
} JsonPathString;
+#define YY_TYPEDEF_YY_SCANNER_T
typedef void *yyscan_t;
#include "utils/jsonpath.h"
diff --git a/src/include/bootstrap/bootstrap.h b/src/include/bootstrap/bootstrap.h
index ccec9415acb..782a2829c71 100644
--- a/src/include/bootstrap/bootstrap.h
+++ b/src/include/bootstrap/bootstrap.h
@@ -56,6 +56,7 @@ extern void boot_get_type_io_data(Oid typid,
Oid *typoutput);
union YYSTYPE;
+#define YY_TYPEDEF_YY_SCANNER_T
typedef void *yyscan_t;
extern int boot_yyparse(yyscan_t yyscanner);
diff --git a/src/include/replication/syncrep.h b/src/include/replication/syncrep.h
index b4134dbc91d..3fe44a880cf 100644
--- a/src/include/replication/syncrep.h
+++ b/src/include/replication/syncrep.h
@@ -101,7 +101,10 @@ extern void SyncRepUpdateSyncStandbysDefined(void);
* in syncrep_gram.y and syncrep_scanner.l
*/
union YYSTYPE;
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
typedef void *yyscan_t;
+#endif
extern int syncrep_yyparse(yyscan_t yyscanner);
extern int syncrep_yylex(union YYSTYPE *yylval_param, yyscan_t yyscanner);
extern void syncrep_yyerror(yyscan_t yyscanner, const char *str);
diff --git a/src/include/replication/walsender_private.h b/src/include/replication/walsender_private.h
index a82ce33c083..f487d3ad9e9 100644
--- a/src/include/replication/walsender_private.h
+++ b/src/include/replication/walsender_private.h
@@ -126,7 +126,10 @@ extern void WalSndSetState(WalSndState state);
* repl_scanner.l
*/
union YYSTYPE;
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
typedef void *yyscan_t;
+#endif
extern int replication_yyparse(yyscan_t yyscanner);
extern int replication_yylex(union YYSTYPE *yylval_param, yyscan_t yyscanner);
extern void replication_yyerror(yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
Peter Eisentraut <peter@eisentraut.org> writes:
I started committing the cube and seg pieces. There were a couple of
complaints from the buildfarm, like
segscan.c:348:15: error: redefinition of typedef 'yyscan_t' is a C11
feature [-Werror,-Wtypedef-redefinition]
typedef void* yyscan_t;
...
(Also, we should probably figure out a way to get these warnings before
things hit the buildfarm.)
Interestingly, while sifaka shows that, its sibling indri doesn't.
Same compiler, same CFLAGS. I think the relevant difference must
be that sifaka is using a much older Bison version (the Apple-supplied
2.3, versus MacPorts' up-to-the-minute version). I think that sort of
thing is exactly why we have the buildfarm. It would not be
reasonable to expect CI to cover that many cases. Trying to do so
would just make CI slow enough that we'd start looking for a new test
phase to put in front of it.
regards, tom lane
On 12/18/24 10:42 AM, Peter Eisentraut wrote:
I can fix that with the attached patch.
The symbol YY_TYPEDEF_YY_SCANNER_T isn't documented, but we already use
it elsewhere in the code.Note that in replication/syncrep.h and replication/walsender_private.h
we have to have an #ifndef wrapper because there are files that end up
including both headers. Maybe we should put that #ifndef wrapper
everywhere for consistency?Any thoughts?
Seems like a sane fix to me and as for the ifndef I have no strong
opinion either way but I would personally probably have added it
everywhere for consistency.
Andreas
On 17.12.24 01:46, Andreas Karlsson wrote:
On 12/16/24 8:39 AM, Peter Eisentraut wrote:
I'll leave it at this for now and wait for some reviews.
I really like this work since it makes the code cleaner to read on top
of paving the way for threading.Reviewed the patches and found a couple of issues.
- Shouldn't yyext in syncrep_scanner_init() be allocated on the heap? Or
at least on the stack but by the caller?
I think it's correct the way it is. It's only a temporary space for the
scanner, so we can allocate it in the innermost scope.
- I think you have flipped the parameters of replication_yyerror(), see
attached fixup patch.
Good catch. There was also a similar issue with syncrep_yyerror().
- Some white space issues fixed in an attached fixup patch.
committed
- Also fixed the static remaining variables in the replication parser in
an attached patch.
Thanks, I'll take a look at that.
- There seems to be a lot left to do to make the plpgsql scanner
actually re-entrant so I do not think it would makes sense to commit the
patch which sets the re-entrant option before that is done.
Ok, we can hold that one back until the full stack including the parser
is done.
On 18.12.24 18:43, Tom Lane wrote:
Peter Eisentraut <peter@eisentraut.org> writes:
I started committing the cube and seg pieces. There were a couple of
complaints from the buildfarm, like
segscan.c:348:15: error: redefinition of typedef 'yyscan_t' is a C11
feature [-Werror,-Wtypedef-redefinition]
typedef void* yyscan_t;
...
(Also, we should probably figure out a way to get these warnings before
things hit the buildfarm.)Interestingly, while sifaka shows that, its sibling indri doesn't.
Same compiler, same CFLAGS. I think the relevant difference must
be that sifaka is using a much older Bison version (the Apple-supplied
2.3, versus MacPorts' up-to-the-minute version). I think that sort of
thing is exactly why we have the buildfarm. It would not be
reasonable to expect CI to cover that many cases. Trying to do so
would just make CI slow enough that we'd start looking for a new test
phase to put in front of it.
The situation is that most current compilers default to some newer C
standard version. And so they won't complain about use of C11 features.
But the affected buildfarm members for whatever reason run with
CC='clang -std=gnu99', and so they correctly reject C11 features. We
could do something similar in the Cirrus configuration. I'll start a
separate thread about that.
On 19.12.24 13:48, Peter Eisentraut wrote:
On 17.12.24 01:46, Andreas Karlsson wrote:
On 12/16/24 8:39 AM, Peter Eisentraut wrote:
I'll leave it at this for now and wait for some reviews.
I really like this work since it makes the code cleaner to read on top
of paving the way for threading.Reviewed the patches and found a couple of issues.
- Shouldn't yyext in syncrep_scanner_init() be allocated on the heap?
Or at least on the stack but by the caller?I think it's correct the way it is. It's only a temporary space for the
scanner, so we can allocate it in the innermost scope.
- Also fixed the static remaining variables in the replication parser
in an attached patch.Thanks, I'll take a look at that.
I see what was going on here. I was allocating yyext as a local
variable in the init function and then it would go out of scope while
the scanner is still in use. That's why this didn't work for me. I had
written essentially the same patch as you for the replication scanner
yyextra but with a local variable, and it was "mysteriously" failing the
tests for me. Your solution is better. (For the jsonpath scanner, the
local variable works because the scanner init and shutdown are called
from the same function.)
Here is an updated patch set on top of what has been committed so far,
with all the issues you pointed out addressed.
Attachments:
v2-0001-replication-parser-pure-parser-and-reentrant-scan.patchtext/plain; charset=UTF-8; name=v2-0001-replication-parser-pure-parser-and-reentrant-scan.patchDownload
From 8795bab6faff0d58586062cbf5719c7e8f77815e Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v2 01/11] replication parser: pure parser and reentrant
scanner
---
src/backend/replication/repl_gram.y | 9 ++--
src/backend/replication/repl_scanner.l | 54 ++++++++++-----------
src/backend/replication/walsender.c | 11 +++--
src/include/replication/walsender_private.h | 21 +++++---
4 files changed, 50 insertions(+), 45 deletions(-)
diff --git a/src/backend/replication/repl_gram.y b/src/backend/replication/repl_gram.y
index 06daa954813..4fa71377e20 100644
--- a/src/backend/replication/repl_gram.y
+++ b/src/backend/replication/repl_gram.y
@@ -24,10 +24,6 @@
#include "repl_gram.h"
-/* silence -Wmissing-variable-declarations */
-extern int replication_yychar;
-extern int replication_yynerrs;
-
/* Result of the parsing is returned here */
Node *replication_parse_result;
@@ -43,6 +39,9 @@ Node *replication_parse_result;
%}
+%parse-param {yyscan_t yyscanner}
+%lex-param {yyscan_t yyscanner}
+%pure-parser
%expect 0
%name-prefix="replication_yy"
@@ -106,6 +105,8 @@ Node *replication_parse_result;
firstcmd: command opt_semicolon
{
replication_parse_result = $1;
+
+ (void) yynerrs; /* suppress compiler warning */
}
;
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index 5b10658a58b..32eedb812fe 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -38,14 +38,11 @@ fprintf_to_ereport(const char *fmt, const char *msg)
ereport(ERROR, (errmsg_internal("%s", msg)));
}
-/* Handle to the buffer that the lexer uses internally */
-static YY_BUFFER_STATE scanbufhandle;
-
/* Pushed-back token (we only handle one) */
-static int repl_pushed_back_token;
+static int repl_pushed_back_token; /* FIXME */
/* Work area for collecting literals */
-static StringInfoData litbuf;
+static StringInfoData litbuf; /* FIXME */
static void startlit(void);
static char *litbufdup(void);
@@ -56,6 +53,8 @@ static void addlitchar(unsigned char ychar);
%}
+%option reentrant
+%option bison-bridge
%option 8bit
%option never-interactive
%option nodefault
@@ -142,7 +141,7 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
{space}+ { /* do nothing */ }
{digit}+ {
- replication_yylval.uintval = strtoul(yytext, NULL, 10);
+ yylval->uintval = strtoul(yytext, NULL, 10);
return UCONST;
}
@@ -150,8 +149,8 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
uint32 hi,
lo;
if (sscanf(yytext, "%X/%X", &hi, &lo) != 2)
- replication_yyerror("invalid streaming start location");
- replication_yylval.recptr = ((uint64) hi) << 32 | lo;
+ replication_yyerror(yyscanner, "invalid streaming start location");
+ yylval->recptr = ((uint64) hi) << 32 | lo;
return RECPTR;
}
@@ -163,7 +162,7 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
<xq>{quotestop} {
yyless(1);
BEGIN(INITIAL);
- replication_yylval.str = litbufdup();
+ yylval->str = litbufdup();
return SCONST;
}
@@ -185,9 +184,9 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
yyless(1);
BEGIN(INITIAL);
- replication_yylval.str = litbufdup();
- len = strlen(replication_yylval.str);
- truncate_identifier(replication_yylval.str, len, true);
+ yylval->str = litbufdup();
+ len = strlen(yylval->str);
+ truncate_identifier(yylval->str, len, true);
return IDENT;
}
@@ -198,7 +197,7 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
{identifier} {
int len = strlen(yytext);
- replication_yylval.str = downcase_truncate_identifier(yytext, len, true);
+ yylval->str = downcase_truncate_identifier(yytext, len, true);
return IDENT;
}
@@ -207,7 +206,7 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
return yytext[0];
}
-<xq,xd><<EOF>> { replication_yyerror("unterminated quoted string"); }
+<xq,xd><<EOF>> { replication_yyerror(yyscanner, "unterminated quoted string"); }
<<EOF>> {
@@ -243,7 +242,7 @@ addlitchar(unsigned char ychar)
}
void
-replication_yyerror(const char *message)
+replication_yyerror(yyscan_t yyscanner, const char *message)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -251,16 +250,16 @@ replication_yyerror(const char *message)
}
void
-replication_scanner_init(const char *str)
+replication_scanner_init(const char *str, yyscan_t *yyscannerp)
{
Size slen = strlen(str);
char *scanbuf;
+ yyscan_t yyscanner;
- /*
- * Might be left over after ereport()
- */
- if (YY_CURRENT_BUFFER)
- yy_delete_buffer(YY_CURRENT_BUFFER);
+ if (yylex_init(yyscannerp) != 0)
+ elog(ERROR, "yylex_init() failed: %m");
+
+ yyscanner = *yyscannerp;
/*
* Make a scan buffer with special termination needed by flex.
@@ -268,18 +267,16 @@ replication_scanner_init(const char *str)
scanbuf = (char *) palloc(slen + 2);
memcpy(scanbuf, str, slen);
scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
+ yy_scan_buffer(scanbuf, slen + 2, yyscanner);
/* Make sure we start in proper state */
- BEGIN(INITIAL);
repl_pushed_back_token = 0;
}
void
-replication_scanner_finish(void)
+replication_scanner_finish(yyscan_t yyscanner)
{
- yy_delete_buffer(scanbufhandle);
- scanbufhandle = NULL;
+ yylex_destroy(yyscanner);
}
/*
@@ -291,9 +288,10 @@ replication_scanner_finish(void)
* IDENT token here, although some other cases are possible.
*/
bool
-replication_scanner_is_replication_command(void)
+replication_scanner_is_replication_command(yyscan_t yyscanner)
{
- int first_token = replication_yylex();
+ YYSTYPE dummy;
+ int first_token = replication_yylex(&dummy, yyscanner);
switch (first_token)
{
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 371eef3dddc..dc25dd6af91 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1951,6 +1951,7 @@ WalSndWaitForWal(XLogRecPtr loc)
bool
exec_replication_command(const char *cmd_string)
{
+ yyscan_t scanner;
int parse_rc;
Node *cmd_node;
const char *cmdtag;
@@ -1990,15 +1991,15 @@ exec_replication_command(const char *cmd_string)
ALLOCSET_DEFAULT_SIZES);
old_context = MemoryContextSwitchTo(cmd_context);
- replication_scanner_init(cmd_string);
+ replication_scanner_init(cmd_string, &scanner);
/*
* Is it a WalSender command?
*/
- if (!replication_scanner_is_replication_command())
+ if (!replication_scanner_is_replication_command(scanner))
{
/* Nope; clean up and get out. */
- replication_scanner_finish();
+ replication_scanner_finish(scanner);
MemoryContextSwitchTo(old_context);
MemoryContextDelete(cmd_context);
@@ -2016,13 +2017,13 @@ exec_replication_command(const char *cmd_string)
/*
* Looks like a WalSender command, so parse it.
*/
- parse_rc = replication_yyparse();
+ parse_rc = replication_yyparse(scanner);
if (parse_rc != 0)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg_internal("replication command parser returned %d",
parse_rc)));
- replication_scanner_finish();
+ replication_scanner_finish(scanner);
cmd_node = replication_parse_result;
diff --git a/src/include/replication/walsender_private.h b/src/include/replication/walsender_private.h
index 41ac736b953..f487d3ad9e9 100644
--- a/src/include/replication/walsender_private.h
+++ b/src/include/replication/walsender_private.h
@@ -125,13 +125,18 @@ extern void WalSndSetState(WalSndState state);
* Internal functions for parsing the replication grammar, in repl_gram.y and
* repl_scanner.l
*/
-extern int replication_yyparse(void);
-extern int replication_yylex(void);
-extern void replication_yyerror(const char *message) pg_attribute_noreturn();
-extern void replication_scanner_init(const char *str);
-extern void replication_scanner_finish(void);
-extern bool replication_scanner_is_replication_command(void);
-
-extern PGDLLIMPORT Node *replication_parse_result;
+union YYSTYPE;
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void *yyscan_t;
+#endif
+extern int replication_yyparse(yyscan_t yyscanner);
+extern int replication_yylex(union YYSTYPE *yylval_param, yyscan_t yyscanner);
+extern void replication_yyerror(yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
+extern void replication_scanner_init(const char *str, yyscan_t *yyscannerp);
+extern void replication_scanner_finish(yyscan_t yyscanner);
+extern bool replication_scanner_is_replication_command(yyscan_t yyscanner);
+
+extern PGDLLIMPORT Node *replication_parse_result; /* FIXME */
#endif /* _WALSENDER_PRIVATE_H */
base-commit: 31b0a8f040042c1dfb9ac359fffbb6b8f9375999
--
2.47.1
v2-0002-replication-parser-Use-palloc-instead-of-malloc-f.patchtext/plain; charset=UTF-8; name=v2-0002-replication-parser-Use-palloc-instead-of-malloc-f.patchDownload
From a109f5e6972d156422cb9ee969bd4938b655e4c5 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v2 02/11] replication parser: Use palloc() instead of malloc()
for flex
---
src/backend/replication/repl_scanner.l | 30 ++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index 32eedb812fe..be633a6e826 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -61,6 +61,9 @@ static void addlitchar(unsigned char ychar);
%option noinput
%option nounput
%option noyywrap
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
%option warn
%option prefix="replication_yy"
@@ -313,3 +316,30 @@ replication_scanner_is_replication_command(yyscan_t yyscanner)
return false;
}
}
+
+/*
+ * Interface functions to make flex use palloc() instead of malloc().
+ * It'd be better to make these static, but flex insists otherwise.
+ */
+
+void *
+yyalloc(yy_size_t size, yyscan_t yyscanner)
+{
+ return palloc(size);
+}
+
+void *
+yyrealloc(void *ptr, yy_size_t size, yyscan_t yyscanner)
+{
+ if (ptr)
+ return repalloc(ptr, size);
+ else
+ return palloc(size);
+}
+
+void
+yyfree(void *ptr, yyscan_t yyscanner)
+{
+ if (ptr)
+ pfree(ptr);
+}
--
2.47.1
v2-0003-replication-parser-Simplify-flex-scan-buffer-mana.patchtext/plain; charset=UTF-8; name=v2-0003-replication-parser-Simplify-flex-scan-buffer-mana.patchDownload
From 1be2b04607353cfa6576bd7c6ab42eee5b9009cc Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v2 03/11] replication parser: Simplify flex scan buffer
management
---
src/backend/replication/repl_scanner.l | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index be633a6e826..6388024a598 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -255,8 +255,6 @@ replication_yyerror(yyscan_t yyscanner, const char *message)
void
replication_scanner_init(const char *str, yyscan_t *yyscannerp)
{
- Size slen = strlen(str);
- char *scanbuf;
yyscan_t yyscanner;
if (yylex_init(yyscannerp) != 0)
@@ -264,13 +262,7 @@ replication_scanner_init(const char *str, yyscan_t *yyscannerp)
yyscanner = *yyscannerp;
- /*
- * Make a scan buffer with special termination needed by flex.
- */
- scanbuf = (char *) palloc(slen + 2);
- memcpy(scanbuf, str, slen);
- scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- yy_scan_buffer(scanbuf, slen + 2, yyscanner);
+ yy_scan_string(str, yyscanner);
/* Make sure we start in proper state */
repl_pushed_back_token = 0;
--
2.47.1
v2-0004-replication-parser-Use-flex-yyextra.patchtext/plain; charset=UTF-8; name=v2-0004-replication-parser-Use-flex-yyextra.patchDownload
From 6c50b086029a3bf955d72e454384bb90e61debb1 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Thu, 19 Dec 2024 21:20:10 +0100
Subject: [PATCH v2 04/11] replication parser: Use flex yyextra
Use flex yyextra to handle context information, instead of global
variables. This complements the earlier patch to make the scanner
reentrant.
Co-authored-by: Andreas Karlsson <andreas@proxel.se>
---
src/backend/replication/repl_scanner.l | 69 +++++++++++++++-----------
src/backend/replication/walsender.c | 1 +
2 files changed, 40 insertions(+), 30 deletions(-)
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index 6388024a598..899114d901a 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -38,16 +38,20 @@ fprintf_to_ereport(const char *fmt, const char *msg)
ereport(ERROR, (errmsg_internal("%s", msg)));
}
-/* Pushed-back token (we only handle one) */
-static int repl_pushed_back_token; /* FIXME */
+struct replication_yy_extra_type
+{
+ /* Pushed-back token (we only handle one) */
+ int repl_pushed_back_token;
-/* Work area for collecting literals */
-static StringInfoData litbuf; /* FIXME */
+ /* Work area for collecting literals */
+ StringInfoData litbuf;
+};
+#define YY_EXTRA_TYPE struct replication_yy_extra_type *
-static void startlit(void);
-static char *litbufdup(void);
-static void addlit(char *ytext, int yleng);
-static void addlitchar(unsigned char ychar);
+static void startlit(yyscan_t yyscanner);
+static char *litbufdup(yyscan_t yyscanner);
+static void addlit(char *ytext, int yleng, yyscan_t yyscanner);
+static void addlitchar(unsigned char ychar, yyscan_t yyscanner);
/* LCOV_EXCL_START */
@@ -110,11 +114,11 @@ identifier {ident_start}{ident_cont}*
/* This code is inserted at the start of replication_yylex() */
/* If we have a pushed-back token, return that. */
- if (repl_pushed_back_token)
+ if (yyextra->repl_pushed_back_token)
{
- int result = repl_pushed_back_token;
+ int result = yyextra->repl_pushed_back_token;
- repl_pushed_back_token = 0;
+ yyextra->repl_pushed_back_token = 0;
return result;
}
%}
@@ -159,27 +163,27 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
{xqstart} {
BEGIN(xq);
- startlit();
+ startlit(yyscanner);
}
<xq>{quotestop} {
yyless(1);
BEGIN(INITIAL);
- yylval->str = litbufdup();
+ yylval->str = litbufdup(yyscanner);
return SCONST;
}
<xq>{xqdouble} {
- addlitchar('\'');
+ addlitchar('\'', yyscanner);
}
<xq>{xqinside} {
- addlit(yytext, yyleng);
+ addlit(yytext, yyleng, yyscanner);
}
{xdstart} {
BEGIN(xd);
- startlit();
+ startlit(yyscanner);
}
<xd>{xdstop} {
@@ -187,14 +191,14 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
yyless(1);
BEGIN(INITIAL);
- yylval->str = litbufdup();
+ yylval->str = litbufdup(yyscanner);
len = strlen(yylval->str);
truncate_identifier(yylval->str, len, true);
return IDENT;
}
<xd>{xdinside} {
- addlit(yytext, yyleng);
+ addlit(yytext, yyleng, yyscanner);
}
{identifier} {
@@ -220,28 +224,32 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
/* LCOV_EXCL_STOP */
+/* see scan.l */
+#undef yyextra
+#define yyextra (((struct yyguts_t *) yyscanner)->yyextra_r)
+
static void
-startlit(void)
+startlit(yyscan_t yyscanner)
{
- initStringInfo(&litbuf);
+ initStringInfo(&yyextra->litbuf);
}
static char *
-litbufdup(void)
+litbufdup(yyscan_t yyscanner)
{
- return litbuf.data;
+ return yyextra->litbuf.data;
}
static void
-addlit(char *ytext, int yleng)
+addlit(char *ytext, int yleng, yyscan_t yyscanner)
{
- appendBinaryStringInfo(&litbuf, ytext, yleng);
+ appendBinaryStringInfo(&yyextra->litbuf, ytext, yleng);
}
static void
-addlitchar(unsigned char ychar)
+addlitchar(unsigned char ychar, yyscan_t yyscanner)
{
- appendStringInfoChar(&litbuf, ychar);
+ appendStringInfoChar(&yyextra->litbuf, ychar);
}
void
@@ -256,21 +264,22 @@ void
replication_scanner_init(const char *str, yyscan_t *yyscannerp)
{
yyscan_t yyscanner;
+ struct replication_yy_extra_type *yyext = palloc0_object(struct replication_yy_extra_type);
if (yylex_init(yyscannerp) != 0)
elog(ERROR, "yylex_init() failed: %m");
yyscanner = *yyscannerp;
- yy_scan_string(str, yyscanner);
+ yyset_extra(yyext, yyscanner);
- /* Make sure we start in proper state */
- repl_pushed_back_token = 0;
+ yy_scan_string(str, yyscanner);
}
void
replication_scanner_finish(yyscan_t yyscanner)
{
+ pfree(yyextra);
yylex_destroy(yyscanner);
}
@@ -301,7 +310,7 @@ replication_scanner_is_replication_command(yyscan_t yyscanner)
case K_UPLOAD_MANIFEST:
case K_SHOW:
/* Yes; push back the first token so we can parse later. */
- repl_pushed_back_token = first_token;
+ yyextra->repl_pushed_back_token = first_token;
return true;
default:
/* Nope; we don't bother to push back the token. */
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index dc25dd6af91..559dba48962 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1998,6 +1998,7 @@ exec_replication_command(const char *cmd_string)
*/
if (!replication_scanner_is_replication_command(scanner))
{
+ elog(WARNING, "CHECK1: not-repl: %s", cmd_string);
/* Nope; clean up and get out. */
replication_scanner_finish(scanner);
--
2.47.1
v2-0005-syncrep-parser-pure-parser-and-reentrant-scanner.patchtext/plain; charset=UTF-8; name=v2-0005-syncrep-parser-pure-parser-and-reentrant-scanner.patchDownload
From 8a5fe6eb7e613b6a25329b8ad976939e654aa2df Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v2 05/11] syncrep parser: pure parser and reentrant scanner
---
src/backend/replication/syncrep.c | 7 ++--
src/backend/replication/syncrep_gram.y | 12 ++++---
src/backend/replication/syncrep_scanner.l | 43 +++++++++++------------
src/include/replication/syncrep.h | 15 +++++---
4 files changed, 41 insertions(+), 36 deletions(-)
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index e1126734ef5..22a2c7fc409 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -992,6 +992,7 @@ check_synchronous_standby_names(char **newval, void **extra, GucSource source)
{
if (*newval != NULL && (*newval)[0] != '\0')
{
+ yyscan_t scanner;
int parse_rc;
SyncRepConfigData *pconf;
@@ -1000,9 +1001,9 @@ check_synchronous_standby_names(char **newval, void **extra, GucSource source)
syncrep_parse_error_msg = NULL;
/* Parse the synchronous_standby_names string */
- syncrep_scanner_init(*newval);
- parse_rc = syncrep_yyparse();
- syncrep_scanner_finish();
+ syncrep_scanner_init(*newval, &scanner);
+ parse_rc = syncrep_yyparse(scanner);
+ syncrep_scanner_finish(scanner);
if (parse_rc != 0 || syncrep_parse_result == NULL)
{
diff --git a/src/backend/replication/syncrep_gram.y b/src/backend/replication/syncrep_gram.y
index e4d9962226c..00b5bf0e522 100644
--- a/src/backend/replication/syncrep_gram.y
+++ b/src/backend/replication/syncrep_gram.y
@@ -26,10 +26,6 @@ char *syncrep_parse_error_msg;
static SyncRepConfigData *create_syncrep_config(const char *num_sync,
List *members, uint8 syncrep_method);
-/* silence -Wmissing-variable-declarations */
-extern int syncrep_yychar;
-extern int syncrep_yynerrs;
-
/*
* Bison doesn't allocate anything that needs to live across parser calls,
* so we can easily have it use palloc instead of malloc. This prevents
@@ -40,6 +36,9 @@ extern int syncrep_yynerrs;
%}
+%parse-param {yyscan_t yyscanner}
+%lex-param {yyscan_t yyscanner}
+%pure-parser
%expect 0
%name-prefix="syncrep_yy"
@@ -60,7 +59,10 @@ extern int syncrep_yynerrs;
%%
result:
- standby_config { syncrep_parse_result = $1; }
+ standby_config {
+ syncrep_parse_result = $1;
+ (void) yynerrs; /* suppress compiler warning */
+ }
;
standby_config:
diff --git a/src/backend/replication/syncrep_scanner.l b/src/backend/replication/syncrep_scanner.l
index 6defb90f13b..add936fdd32 100644
--- a/src/backend/replication/syncrep_scanner.l
+++ b/src/backend/replication/syncrep_scanner.l
@@ -37,15 +37,14 @@ fprintf_to_ereport(const char *fmt, const char *msg)
ereport(ERROR, (errmsg_internal("%s", msg)));
}
-/* Handles to the buffer that the lexer uses internally */
-static YY_BUFFER_STATE scanbufhandle;
-
-static StringInfoData xdbuf;
+static StringInfoData xdbuf; /* FIXME */
/* LCOV_EXCL_START */
%}
+%option reentrant
+%option bison-bridge
%option 8bit
%option never-interactive
%option nodefault
@@ -92,28 +91,28 @@ xdinside [^"]+
appendStringInfoString(&xdbuf, yytext);
}
<xd>{xdstop} {
- syncrep_yylval.str = xdbuf.data;
+ yylval->str = xdbuf.data;
xdbuf.data = NULL;
BEGIN(INITIAL);
return NAME;
}
<xd><<EOF>> {
- syncrep_yyerror("unterminated quoted identifier");
+ syncrep_yyerror(yyscanner, "unterminated quoted identifier");
return JUNK;
}
{identifier} {
- syncrep_yylval.str = pstrdup(yytext);
+ yylval->str = pstrdup(yytext);
return NAME;
}
{digit}+ {
- syncrep_yylval.str = pstrdup(yytext);
+ yylval->str = pstrdup(yytext);
return NUM;
}
"*" {
- syncrep_yylval.str = "*";
+ yylval->str = "*";
return NAME;
}
@@ -128,8 +127,10 @@ xdinside [^"]+
/* Needs to be here for access to yytext */
void
-syncrep_yyerror(const char *message)
+syncrep_yyerror(yyscan_t yyscanner, const char *message)
{
+ struct yyguts_t * yyg = (struct yyguts_t *) yyscanner; /* needed for yytext macro */
+
/* report only the first error in a parse operation */
if (syncrep_parse_error_msg)
return;
@@ -142,16 +143,16 @@ syncrep_yyerror(const char *message)
}
void
-syncrep_scanner_init(const char *str)
+syncrep_scanner_init(const char *str, yyscan_t *yyscannerp)
{
Size slen = strlen(str);
char *scanbuf;
+ yyscan_t yyscanner;
- /*
- * Might be left over after ereport()
- */
- if (YY_CURRENT_BUFFER)
- yy_delete_buffer(YY_CURRENT_BUFFER);
+ if (yylex_init(yyscannerp) != 0)
+ elog(ERROR, "yylex_init() failed: %m");
+
+ yyscanner = *yyscannerp;
/*
* Make a scan buffer with special termination needed by flex.
@@ -159,15 +160,11 @@ syncrep_scanner_init(const char *str)
scanbuf = (char *) palloc(slen + 2);
memcpy(scanbuf, str, slen);
scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
-
- /* Make sure we start in proper state */
- BEGIN(INITIAL);
+ yy_scan_buffer(scanbuf, slen + 2, yyscanner);
}
void
-syncrep_scanner_finish(void)
+syncrep_scanner_finish(yyscan_t yyscanner)
{
- yy_delete_buffer(scanbufhandle);
- scanbufhandle = NULL;
+ yylex_destroy(yyscanner);
}
diff --git a/src/include/replication/syncrep.h b/src/include/replication/syncrep.h
index ea439e6da60..3fe44a880cf 100644
--- a/src/include/replication/syncrep.h
+++ b/src/include/replication/syncrep.h
@@ -100,10 +100,15 @@ extern void SyncRepUpdateSyncStandbysDefined(void);
* Internal functions for parsing synchronous_standby_names grammar,
* in syncrep_gram.y and syncrep_scanner.l
*/
-extern int syncrep_yyparse(void);
-extern int syncrep_yylex(void);
-extern void syncrep_yyerror(const char *str);
-extern void syncrep_scanner_init(const char *str);
-extern void syncrep_scanner_finish(void);
+union YYSTYPE;
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void *yyscan_t;
+#endif
+extern int syncrep_yyparse(yyscan_t yyscanner);
+extern int syncrep_yylex(union YYSTYPE *yylval_param, yyscan_t yyscanner);
+extern void syncrep_yyerror(yyscan_t yyscanner, const char *str);
+extern void syncrep_scanner_init(const char *str, yyscan_t *yyscannerp);
+extern void syncrep_scanner_finish(yyscan_t yyscanner);
#endif /* _SYNCREP_H */
--
2.47.1
v2-0006-syncrep-parser-Use-palloc-instead-of-malloc-for-f.patchtext/plain; charset=UTF-8; name=v2-0006-syncrep-parser-Use-palloc-instead-of-malloc-for-f.patchDownload
From 172e23683e602930318b7d82e232abb5aa0eaf37 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v2 06/11] syncrep parser: Use palloc() instead of malloc() for
flex
---
src/backend/replication/syncrep_scanner.l | 30 +++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/src/backend/replication/syncrep_scanner.l b/src/backend/replication/syncrep_scanner.l
index add936fdd32..347e54cfb06 100644
--- a/src/backend/replication/syncrep_scanner.l
+++ b/src/backend/replication/syncrep_scanner.l
@@ -51,6 +51,9 @@ static StringInfoData xdbuf; /* FIXME */
%option noinput
%option nounput
%option noyywrap
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
%option warn
%option prefix="syncrep_yy"
@@ -168,3 +171,30 @@ syncrep_scanner_finish(yyscan_t yyscanner)
{
yylex_destroy(yyscanner);
}
+
+/*
+ * Interface functions to make flex use palloc() instead of malloc().
+ * It'd be better to make these static, but flex insists otherwise.
+ */
+
+void *
+yyalloc(yy_size_t size, yyscan_t yyscanner)
+{
+ return palloc(size);
+}
+
+void *
+yyrealloc(void *ptr, yy_size_t size, yyscan_t yyscanner)
+{
+ if (ptr)
+ return repalloc(ptr, size);
+ else
+ return palloc(size);
+}
+
+void
+yyfree(void *ptr, yyscan_t yyscanner)
+{
+ if (ptr)
+ pfree(ptr);
+}
--
2.47.1
v2-0007-syncrep-parser-Simplify-flex-scan-buffer-manageme.patchtext/plain; charset=UTF-8; name=v2-0007-syncrep-parser-Simplify-flex-scan-buffer-manageme.patchDownload
From 34ec85bfe38062b67f0fd5542389c961d14d9fd6 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v2 07/11] syncrep parser: Simplify flex scan buffer management
---
src/backend/replication/syncrep_scanner.l | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/src/backend/replication/syncrep_scanner.l b/src/backend/replication/syncrep_scanner.l
index 347e54cfb06..9eac11229c6 100644
--- a/src/backend/replication/syncrep_scanner.l
+++ b/src/backend/replication/syncrep_scanner.l
@@ -148,8 +148,6 @@ syncrep_yyerror(yyscan_t yyscanner, const char *message)
void
syncrep_scanner_init(const char *str, yyscan_t *yyscannerp)
{
- Size slen = strlen(str);
- char *scanbuf;
yyscan_t yyscanner;
if (yylex_init(yyscannerp) != 0)
@@ -157,13 +155,7 @@ syncrep_scanner_init(const char *str, yyscan_t *yyscannerp)
yyscanner = *yyscannerp;
- /*
- * Make a scan buffer with special termination needed by flex.
- */
- scanbuf = (char *) palloc(slen + 2);
- memcpy(scanbuf, str, slen);
- scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- yy_scan_buffer(scanbuf, slen + 2, yyscanner);
+ yy_scan_string(str, yyscanner);
}
void
--
2.47.1
v2-0008-syncrep-parser-Use-flex-yyextra.patchtext/plain; charset=UTF-8; name=v2-0008-syncrep-parser-Use-flex-yyextra.patchDownload
From 5df539dda3589ff8764ed1d0efd391964666337b Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v2 08/11] syncrep parser: Use flex yyextra
Use flex yyextra to handle context information, instead of global
variables. This complements the earlier patch to make the scanner
reentrant.
---
src/backend/replication/syncrep_scanner.l | 24 +++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/src/backend/replication/syncrep_scanner.l b/src/backend/replication/syncrep_scanner.l
index 9eac11229c6..42e6852ea15 100644
--- a/src/backend/replication/syncrep_scanner.l
+++ b/src/backend/replication/syncrep_scanner.l
@@ -37,7 +37,11 @@ fprintf_to_ereport(const char *fmt, const char *msg)
ereport(ERROR, (errmsg_internal("%s", msg)));
}
-static StringInfoData xdbuf; /* FIXME */
+struct syncrep_yy_extra_type
+{
+ StringInfoData xdbuf;
+};
+#define YY_EXTRA_TYPE struct syncrep_yy_extra_type *
/* LCOV_EXCL_START */
@@ -84,18 +88,18 @@ xdinside [^"]+
[Ff][Ii][Rr][Ss][Tt] { return FIRST; }
{xdstart} {
- initStringInfo(&xdbuf);
+ initStringInfo(&yyextra->xdbuf);
BEGIN(xd);
}
<xd>{xddouble} {
- appendStringInfoChar(&xdbuf, '"');
+ appendStringInfoChar(&yyextra->xdbuf, '"');
}
<xd>{xdinside} {
- appendStringInfoString(&xdbuf, yytext);
+ appendStringInfoString(&yyextra->xdbuf, yytext);
}
<xd>{xdstop} {
- yylval->str = xdbuf.data;
- xdbuf.data = NULL;
+ yylval->str = yyextra->xdbuf.data;
+ yyextra->xdbuf.data = NULL;
BEGIN(INITIAL);
return NAME;
}
@@ -128,6 +132,10 @@ xdinside [^"]+
/* LCOV_EXCL_STOP */
+/* see scan.l */
+#undef yyextra
+#define yyextra (((struct yyguts_t *) yyscanner)->yyextra_r)
+
/* Needs to be here for access to yytext */
void
syncrep_yyerror(yyscan_t yyscanner, const char *message)
@@ -149,18 +157,22 @@ void
syncrep_scanner_init(const char *str, yyscan_t *yyscannerp)
{
yyscan_t yyscanner;
+ struct syncrep_yy_extra_type *yyext = palloc0_object(struct syncrep_yy_extra_type);
if (yylex_init(yyscannerp) != 0)
elog(ERROR, "yylex_init() failed: %m");
yyscanner = *yyscannerp;
+ yyset_extra(yyext, yyscanner);
+
yy_scan_string(str, yyscanner);
}
void
syncrep_scanner_finish(yyscan_t yyscanner)
{
+ pfree(yyextra);
yylex_destroy(yyscanner);
}
--
2.47.1
v2-0009-jsonpath-scanner-reentrant-scanner.patchtext/plain; charset=UTF-8; name=v2-0009-jsonpath-scanner-reentrant-scanner.patchDownload
From 53d565e66b4456f94409f73482854ab138863166 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 2 Dec 2024 10:35:37 +0100
Subject: [PATCH v2 09/11] jsonpath scanner: reentrant scanner
Use the flex %option reentrant to make the generated scanner
reentrant and thread-safe. Note: The parser was already pure.
Simplify flex scan buffer management: Instead of constructing the
buffer from pieces and then using yy_scan_buffer(), we can just use
yy_scan_string(), which does the same thing internally. (Actually, we
use yy_scan_bytes() here because we already have the length.)
Use flex yyextra to handle context information, instead of global
variables. This complements the other changes to make the scanner
reentrant.
Reviewed-by: Heikki Linnakangas <hlinnaka@iki.fi>
Reviewed-by: Andreas Karlsson <andreas@proxel.se>
Discussion: https://www.postgresql.org/message-id/flat/eb6faeac-2a8a-4b69-9189-c33c520e5b7b@eisentraut.org
---
src/backend/utils/adt/jsonpath_gram.y | 2 +
src/backend/utils/adt/jsonpath_internal.h | 12 +-
src/backend/utils/adt/jsonpath_scan.l | 283 ++++++++++------------
3 files changed, 144 insertions(+), 153 deletions(-)
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index 8733a0eac66..de5a455c96d 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -60,8 +60,10 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%name-prefix="jsonpath_yy"
%parse-param {JsonPathParseResult **result}
%parse-param {struct Node *escontext}
+%parse-param {yyscan_t yyscanner}
%lex-param {JsonPathParseResult **result}
%lex-param {struct Node *escontext}
+%lex-param {yyscan_t yyscanner}
%union
{
diff --git a/src/backend/utils/adt/jsonpath_internal.h b/src/backend/utils/adt/jsonpath_internal.h
index 6cd6d8b652d..dbb5e67fe2b 100644
--- a/src/backend/utils/adt/jsonpath_internal.h
+++ b/src/backend/utils/adt/jsonpath_internal.h
@@ -22,17 +22,25 @@ typedef struct JsonPathString
int total;
} JsonPathString;
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void *yyscan_t;
+#endif
+
#include "utils/jsonpath.h"
#include "jsonpath_gram.h"
#define YY_DECL extern int jsonpath_yylex(YYSTYPE *yylval_param, \
JsonPathParseResult **result, \
- struct Node *escontext)
+ struct Node *escontext, \
+ yyscan_t yyscanner)
YY_DECL;
extern int jsonpath_yyparse(JsonPathParseResult **result,
- struct Node *escontext);
+ struct Node *escontext,
+ yyscan_t yyscanner);
extern void jsonpath_yyerror(JsonPathParseResult **result,
struct Node *escontext,
+ yyscan_t yyscanner,
const char *message);
#endif /* JSONPATH_INTERNAL_H */
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index f5a85de36f5..8ed6c7ddf63 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -30,18 +30,17 @@
}
%{
-static JsonPathString scanstring;
-
-/* Handles to the buffer that the lexer uses internally */
-static YY_BUFFER_STATE scanbufhandle;
-static char *scanbuf;
-static int scanbuflen;
+struct jsonpath_yy_extra_type
+{
+ JsonPathString scanstring;
+};
+#define YY_EXTRA_TYPE struct jsonpath_yy_extra_type *
-static void addstring(bool init, char *s, int l);
-static void addchar(bool init, char c);
-static enum yytokentype checkKeyword(void);
-static bool parseUnicode(char *s, int l, struct Node *escontext);
-static bool parseHexChar(char *s, struct Node *escontext);
+static void addstring(bool init, char *s, int l, yyscan_t yyscanner);
+static void addchar(bool init, char c, yyscan_t yyscanner);
+static enum yytokentype checkKeyword(yyscan_t yyscanner);
+static bool parseUnicode(char *s, int l, struct Node *escontext, yyscan_t yyscanner);
+static bool parseHexChar(char *s, struct Node *escontext, yyscan_t yyscanner);
/* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
#undef fprintf
@@ -65,6 +64,7 @@ fprintf_to_ereport(const char *fmt, const char *msg)
%option noyywrap
%option warn
%option prefix="jsonpath_yy"
+%option reentrant
%option bison-bridge
%option noyyalloc
%option noyyrealloc
@@ -120,63 +120,63 @@ hex_fail \\x{hexdigit}{0,1}
%%
<xnq>{other}+ {
- addstring(false, yytext, yyleng);
+ addstring(false, yytext, yyleng, yyscanner);
}
<xnq>{blank}+ {
- yylval->str = scanstring;
+ yylval->str = yyextra->scanstring;
BEGIN INITIAL;
- return checkKeyword();
+ return checkKeyword(yyscanner);
}
<xnq>\/\* {
- yylval->str = scanstring;
+ yylval->str = yyextra->scanstring;
BEGIN xc;
}
<xnq>({special}|\") {
- yylval->str = scanstring;
+ yylval->str = yyextra->scanstring;
yyless(0);
BEGIN INITIAL;
- return checkKeyword();
+ return checkKeyword(yyscanner);
}
<xnq><<EOF>> {
- yylval->str = scanstring;
+ yylval->str = yyextra->scanstring;
BEGIN INITIAL;
- return checkKeyword();
+ return checkKeyword(yyscanner);
}
-<xnq,xq,xvq>\\b { addchar(false, '\b'); }
+<xnq,xq,xvq>\\b { addchar(false, '\b', yyscanner); }
-<xnq,xq,xvq>\\f { addchar(false, '\f'); }
+<xnq,xq,xvq>\\f { addchar(false, '\f', yyscanner); }
-<xnq,xq,xvq>\\n { addchar(false, '\n'); }
+<xnq,xq,xvq>\\n { addchar(false, '\n', yyscanner); }
-<xnq,xq,xvq>\\r { addchar(false, '\r'); }
+<xnq,xq,xvq>\\r { addchar(false, '\r', yyscanner); }
-<xnq,xq,xvq>\\t { addchar(false, '\t'); }
+<xnq,xq,xvq>\\t { addchar(false, '\t', yyscanner); }
-<xnq,xq,xvq>\\v { addchar(false, '\v'); }
+<xnq,xq,xvq>\\v { addchar(false, '\v', yyscanner); }
<xnq,xq,xvq>{unicode}+ {
- if (!parseUnicode(yytext, yyleng, escontext))
+ if (!parseUnicode(yytext, yyleng, escontext, yyscanner))
yyterminate();
}
<xnq,xq,xvq>{hex_char} {
- if (!parseHexChar(yytext, escontext))
+ if (!parseHexChar(yytext, escontext, yyscanner))
yyterminate();
}
<xnq,xq,xvq>{unicode}*{unicodefail} {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"invalid Unicode escape sequence");
yyterminate();
}
<xnq,xq,xvq>{hex_fail} {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"invalid hexadecimal character sequence");
yyterminate();
}
@@ -184,37 +184,37 @@ hex_fail \\x{hexdigit}{0,1}
<xnq,xq,xvq>{unicode}+\\ {
/* throw back the \\, and treat as unicode */
yyless(yyleng - 1);
- if (!parseUnicode(yytext, yyleng, escontext))
+ if (!parseUnicode(yytext, yyleng, escontext, yyscanner))
yyterminate();
}
-<xnq,xq,xvq>\\. { addchar(false, yytext[1]); }
+<xnq,xq,xvq>\\. { addchar(false, yytext[1], yyscanner); }
<xnq,xq,xvq>\\ {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"unexpected end after backslash");
yyterminate();
}
<xq,xvq><<EOF>> {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"unterminated quoted string");
yyterminate();
}
<xq>\" {
- yylval->str = scanstring;
+ yylval->str = yyextra->scanstring;
BEGIN INITIAL;
return STRING_P;
}
<xvq>\" {
- yylval->str = scanstring;
+ yylval->str = yyextra->scanstring;
BEGIN INITIAL;
return VARIABLE_P;
}
-<xq,xvq>[^\\\"]+ { addstring(false, yytext, yyleng); }
+<xq,xvq>[^\\\"]+ { addstring(false, yytext, yyleng, yyscanner); }
<xc>\*\/ { BEGIN INITIAL; }
@@ -223,7 +223,7 @@ hex_fail \\x{hexdigit}{0,1}
<xc>\* { }
<xc><<EOF>> {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"unexpected end of comment");
yyterminate();
}
@@ -250,14 +250,14 @@ hex_fail \\x{hexdigit}{0,1}
\> { return GREATER_P; }
\${other}+ {
- addstring(true, yytext + 1, yyleng - 1);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext + 1, yyleng - 1, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return VARIABLE_P;
}
\$\" {
- addchar(true, '\0');
+ addchar(true, '\0', yyscanner);
BEGIN xvq;
}
@@ -266,85 +266,85 @@ hex_fail \\x{hexdigit}{0,1}
{blank}+ { /* ignore */ }
\/\* {
- addchar(true, '\0');
+ addchar(true, '\0', yyscanner);
BEGIN xc;
}
{real} {
- addstring(true, yytext, yyleng);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext, yyleng, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return NUMERIC_P;
}
{decimal} {
- addstring(true, yytext, yyleng);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext, yyleng, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return NUMERIC_P;
}
{decinteger} {
- addstring(true, yytext, yyleng);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext, yyleng, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return INT_P;
}
{hexinteger} {
- addstring(true, yytext, yyleng);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext, yyleng, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return INT_P;
}
{octinteger} {
- addstring(true, yytext, yyleng);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext, yyleng, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return INT_P;
}
{bininteger} {
- addstring(true, yytext, yyleng);
- addchar(false, '\0');
- yylval->str = scanstring;
+ addstring(true, yytext, yyleng, yyscanner);
+ addchar(false, '\0', yyscanner);
+ yylval->str = yyextra->scanstring;
return INT_P;
}
{realfail} {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"invalid numeric literal");
yyterminate();
}
{decinteger_junk} {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"trailing junk after numeric literal");
yyterminate();
}
{decimal_junk} {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"trailing junk after numeric literal");
yyterminate();
}
{real_junk} {
- jsonpath_yyerror(NULL, escontext,
+ jsonpath_yyerror(NULL, escontext, yyscanner,
"trailing junk after numeric literal");
yyterminate();
}
\" {
- addchar(true, '\0');
+ addchar(true, '\0', yyscanner);
BEGIN xq;
}
\\ {
yyless(0);
- addchar(true, '\0');
+ addchar(true, '\0', yyscanner);
BEGIN xnq;
}
{other}+ {
- addstring(true, yytext, yyleng);
+ addstring(true, yytext, yyleng, yyscanner);
BEGIN xnq;
}
@@ -354,10 +354,17 @@ hex_fail \\x{hexdigit}{0,1}
/* LCOV_EXCL_STOP */
+/* see scan.l */
+#undef yyextra
+#define yyextra (((struct yyguts_t *) yyscanner)->yyextra_r)
+
void
jsonpath_yyerror(JsonPathParseResult **result, struct Node *escontext,
+ yyscan_t yyscanner,
const char *message)
{
+ struct yyguts_t * yyg = (struct yyguts_t *) yyscanner; /* needed for yytext macro */
+
/* don't overwrite escontext if it's already been set */
if (SOFT_ERROR_OCCURRED(escontext))
return;
@@ -427,9 +434,11 @@ static const JsonPathKeyword keywords[] = {
{ 12,false, TIMESTAMP_TZ_P, "timestamp_tz"},
};
-/* Check if current scanstring value is a keyword */
+/*
+ * Check if current scanstring value is a keyword
+ */
static enum yytokentype
-checkKeyword()
+checkKeyword(yyscan_t yyscanner)
{
int res = IDENT_P;
int diff;
@@ -437,18 +446,18 @@ checkKeyword()
*StopHigh = keywords + lengthof(keywords),
*StopMiddle;
- if (scanstring.len > keywords[lengthof(keywords) - 1].len)
+ if (yyextra->scanstring.len > keywords[lengthof(keywords) - 1].len)
return res;
while (StopLow < StopHigh)
{
StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
- if (StopMiddle->len == scanstring.len)
- diff = pg_strncasecmp(StopMiddle->keyword, scanstring.val,
- scanstring.len);
+ if (StopMiddle->len == yyextra->scanstring.len)
+ diff = pg_strncasecmp(StopMiddle->keyword, yyextra->scanstring.val,
+ yyextra->scanstring.len);
else
- diff = StopMiddle->len - scanstring.len;
+ diff = StopMiddle->len - yyextra->scanstring.len;
if (diff < 0)
StopLow = StopMiddle + 1;
@@ -457,8 +466,8 @@ checkKeyword()
else
{
if (StopMiddle->lowercase)
- diff = strncmp(StopMiddle->keyword, scanstring.val,
- scanstring.len);
+ diff = strncmp(StopMiddle->keyword, yyextra->scanstring.val,
+ yyextra->scanstring.len);
if (diff == 0)
res = StopMiddle->val;
@@ -470,85 +479,47 @@ checkKeyword()
return res;
}
-/*
- * Called before any actual parsing is done
- */
-static void
-jsonpath_scanner_init(const char *str, int slen)
-{
- if (slen <= 0)
- slen = strlen(str);
-
- /*
- * Might be left over after ereport()
- */
- yy_init_globals();
-
- /*
- * Make a scan buffer with special termination needed by flex.
- */
-
- scanbuflen = slen;
- scanbuf = palloc(slen + 2);
- memcpy(scanbuf, str, slen);
- scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
- scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
-
- BEGIN(INITIAL);
-}
-
-
-/*
- * Called after parsing is done to clean up after jsonpath_scanner_init()
- */
-static void
-jsonpath_scanner_finish(void)
-{
- yy_delete_buffer(scanbufhandle);
- pfree(scanbuf);
-}
-
/*
* Resize scanstring so that it can append string of given length.
* Reinitialize if required.
*/
static void
-resizeString(bool init, int appendLen)
+resizeString(bool init, int appendLen, yyscan_t yyscanner)
{
if (init)
{
- scanstring.total = Max(32, appendLen);
- scanstring.val = (char *) palloc(scanstring.total);
- scanstring.len = 0;
+ yyextra->scanstring.total = Max(32, appendLen);
+ yyextra->scanstring.val = (char *) palloc(yyextra->scanstring.total);
+ yyextra->scanstring.len = 0;
}
else
{
- if (scanstring.len + appendLen >= scanstring.total)
+ if (yyextra->scanstring.len + appendLen >= yyextra->scanstring.total)
{
- while (scanstring.len + appendLen >= scanstring.total)
- scanstring.total *= 2;
- scanstring.val = repalloc(scanstring.val, scanstring.total);
+ while (yyextra->scanstring.len + appendLen >= yyextra->scanstring.total)
+ yyextra->scanstring.total *= 2;
+ yyextra->scanstring.val = repalloc(yyextra->scanstring.val, yyextra->scanstring.total);
}
}
}
/* Add set of bytes at "s" of length "l" to scanstring */
static void
-addstring(bool init, char *s, int l)
+addstring(bool init, char *s, int l, yyscan_t yyscanner)
{
- resizeString(init, l + 1);
- memcpy(scanstring.val + scanstring.len, s, l);
- scanstring.len += l;
+ resizeString(init, l + 1, yyscanner);
+ memcpy(yyextra->scanstring.val + yyextra->scanstring.len, s, l);
+ yyextra->scanstring.len += l;
}
/* Add single byte "c" to scanstring */
static void
-addchar(bool init, char c)
+addchar(bool init, char c, yyscan_t yyscanner)
{
- resizeString(init, 1);
- scanstring.val[scanstring.len] = c;
+ resizeString(init, 1, yyscanner);
+ yyextra->scanstring.val[yyextra->scanstring.len] = c;
if (c != '\0')
- scanstring.len++;
+ yyextra->scanstring.len++;
}
/* Interface to jsonpath parser */
@@ -556,20 +527,30 @@ JsonPathParseResult *
parsejsonpath(const char *str, int len, struct Node *escontext)
{
JsonPathParseResult *parseresult;
+ yyscan_t scanner;
+ struct jsonpath_yy_extra_type yyext;
+
+ if (jsonpath_yylex_init(&scanner) != 0)
+ elog(ERROR, "yylex_init() failed: %m");
+
+ yyset_extra(&yyext, scanner);
+
+ if (len <= 0)
+ len = strlen(str);
- jsonpath_scanner_init(str, len);
+ jsonpath_yy_scan_bytes(str, len, scanner);
- if (jsonpath_yyparse(&parseresult, escontext) != 0)
- jsonpath_yyerror(NULL, escontext, "invalid input"); /* shouldn't happen */
+ if (jsonpath_yyparse(&parseresult, escontext, scanner) != 0)
+ jsonpath_yyerror(NULL, escontext, scanner, "invalid input"); /* shouldn't happen */
- jsonpath_scanner_finish();
+ jsonpath_yylex_destroy(scanner);
return parseresult;
}
/* Turn hex character into integer */
static bool
-hexval(char c, int *result, struct Node *escontext)
+hexval(char c, int *result, struct Node *escontext, yyscan_t yyscanner)
{
if (c >= '0' && c <= '9')
{
@@ -586,13 +567,13 @@ hexval(char c, int *result, struct Node *escontext)
*result = c - 'A' + 0xA;
return true;
}
- jsonpath_yyerror(NULL, escontext, "invalid hexadecimal digit");
+ jsonpath_yyerror(NULL, escontext, yyscanner, "invalid hexadecimal digit");
return false;
}
/* Add given unicode character to scanstring */
static bool
-addUnicodeChar(int ch, struct Node *escontext)
+addUnicodeChar(int ch, struct Node *escontext, yyscan_t yyscanner)
{
if (ch == 0)
{
@@ -618,14 +599,14 @@ addUnicodeChar(int ch, struct Node *escontext)
ereturn(escontext, false,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("could not convert Unicode to server encoding")));
- addstring(false, cbuf, strlen(cbuf));
+ addstring(false, cbuf, strlen(cbuf), yyscanner);
}
return true;
}
/* Add unicode character, processing any surrogate pairs */
static bool
-addUnicode(int ch, int *hi_surrogate, struct Node *escontext)
+addUnicode(int ch, int *hi_surrogate, struct Node *escontext, yyscan_t yyscanner)
{
if (is_utf16_surrogate_first(ch))
{
@@ -658,7 +639,7 @@ addUnicode(int ch, int *hi_surrogate, struct Node *escontext)
"surrogate.")));
}
- return addUnicodeChar(ch, escontext);
+ return addUnicodeChar(ch, escontext, yyscanner);
}
/*
@@ -666,7 +647,7 @@ addUnicode(int ch, int *hi_surrogate, struct Node *escontext)
* src/backend/utils/adt/json.c
*/
static bool
-parseUnicode(char *s, int l, struct Node *escontext)
+parseUnicode(char *s, int l, struct Node *escontext, yyscan_t yyscanner)
{
int i = 2;
int hi_surrogate = -1;
@@ -680,7 +661,7 @@ parseUnicode(char *s, int l, struct Node *escontext)
{
while (s[++i] != '}' && i < l)
{
- if (!hexval(s[i], &si, escontext))
+ if (!hexval(s[i], &si, escontext, yyscanner))
return false;
ch = (ch << 4) | si;
}
@@ -690,13 +671,13 @@ parseUnicode(char *s, int l, struct Node *escontext)
{
for (j = 0; j < 4 && i < l; j++)
{
- if (!hexval(s[i++], &si, escontext))
+ if (!hexval(s[i++], &si, escontext, yyscanner))
return false;
ch = (ch << 4) | si;
}
}
- if (! addUnicode(ch, &hi_surrogate, escontext))
+ if (! addUnicode(ch, &hi_surrogate, escontext, yyscanner))
return false;
}
@@ -714,17 +695,17 @@ parseUnicode(char *s, int l, struct Node *escontext)
/* Parse sequence of hex-encoded characters */
static bool
-parseHexChar(char *s, struct Node *escontext)
+parseHexChar(char *s, struct Node *escontext, yyscan_t yyscanner)
{
int s2, s3, ch;
- if (!hexval(s[2], &s2, escontext))
+ if (!hexval(s[2], &s2, escontext, yyscanner))
return false;
- if (!hexval(s[3], &s3, escontext))
+ if (!hexval(s[3], &s3, escontext, yyscanner))
return false;
ch = (s2 << 4) | s3;
- return addUnicodeChar(ch, escontext);
+ return addUnicodeChar(ch, escontext, yyscanner);
}
/*
@@ -733,13 +714,13 @@ parseHexChar(char *s, struct Node *escontext)
*/
void *
-jsonpath_yyalloc(yy_size_t bytes)
+jsonpath_yyalloc(yy_size_t bytes, yyscan_t yyscanner)
{
return palloc(bytes);
}
void *
-jsonpath_yyrealloc(void *ptr, yy_size_t bytes)
+jsonpath_yyrealloc(void *ptr, yy_size_t bytes, yyscan_t yyscanner)
{
if (ptr)
return repalloc(ptr, bytes);
@@ -748,7 +729,7 @@ jsonpath_yyrealloc(void *ptr, yy_size_t bytes)
}
void
-jsonpath_yyfree(void *ptr)
+jsonpath_yyfree(void *ptr, yyscan_t yyscanner)
{
if (ptr)
pfree(ptr);
--
2.47.1
v2-0010-guc-reentrant-scanner.patchtext/plain; charset=UTF-8; name=v2-0010-guc-reentrant-scanner.patchDownload
From a0e30b524c3b5d40ed007e50774584abf06765ca Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Tue, 3 Dec 2024 23:14:01 +0100
Subject: [PATCH v2 10/11] guc: reentrant scanner
---
src/backend/utils/misc/guc-file.l | 30 +++++++++++++++++++-----------
1 file changed, 19 insertions(+), 11 deletions(-)
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index 73be80076da..a0381d9e2a1 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -57,6 +57,7 @@ static int GUC_flex_fatal(const char *msg);
%}
+%option reentrant
%option 8bit
%option never-interactive
%option nodefault
@@ -353,6 +354,8 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
unsigned int save_ConfigFileLineno = ConfigFileLineno;
sigjmp_buf *save_GUC_flex_fatal_jmp = GUC_flex_fatal_jmp;
sigjmp_buf flex_fatal_jmp;
+ yyscan_t scanner;
+ struct yyguts_t * yyg; /* needed for yytext macro */
volatile YY_BUFFER_STATE lex_buffer = NULL;
int errorcount;
int token;
@@ -381,11 +384,15 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
ConfigFileLineno = 1;
errorcount = 0;
- lex_buffer = yy_create_buffer(fp, YY_BUF_SIZE);
- yy_switch_to_buffer(lex_buffer);
+ if (yylex_init(&scanner) != 0)
+ elog(elevel, "yylex_init() failed: %m");
+ yyg = (struct yyguts_t *) scanner;
+
+ lex_buffer = yy_create_buffer(fp, YY_BUF_SIZE, scanner);
+ yy_switch_to_buffer(lex_buffer, scanner);
/* This loop iterates once per logical line */
- while ((token = yylex()))
+ while ((token = yylex(scanner)))
{
char *opt_name = NULL;
char *opt_value = NULL;
@@ -400,9 +407,9 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
opt_name = pstrdup(yytext);
/* next we have an optional equal sign; discard if present */
- token = yylex();
+ token = yylex(scanner);
if (token == GUC_EQUALS)
- token = yylex();
+ token = yylex(scanner);
/* now we must have the option value */
if (token != GUC_ID &&
@@ -417,7 +424,7 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
opt_value = pstrdup(yytext);
/* now we'd like an end of line, or possibly EOF */
- token = yylex();
+ token = yylex(scanner);
if (token != GUC_EOL)
{
if (token != 0)
@@ -438,7 +445,7 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
depth + 1, elevel,
head_p, tail_p))
OK = false;
- yy_switch_to_buffer(lex_buffer);
+ yy_switch_to_buffer(lex_buffer, scanner);
pfree(opt_name);
pfree(opt_value);
}
@@ -453,7 +460,7 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
depth + 1, elevel,
head_p, tail_p))
OK = false;
- yy_switch_to_buffer(lex_buffer);
+ yy_switch_to_buffer(lex_buffer, scanner);
pfree(opt_name);
pfree(opt_value);
}
@@ -468,7 +475,7 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
depth + 1, elevel,
head_p, tail_p))
OK = false;
- yy_switch_to_buffer(lex_buffer);
+ yy_switch_to_buffer(lex_buffer, scanner);
pfree(opt_name);
pfree(opt_value);
}
@@ -545,14 +552,15 @@ parse_error:
/* resync to next end-of-line or EOF */
while (token != GUC_EOL && token != 0)
- token = yylex();
+ token = yylex(scanner);
/* break out of loop on EOF */
if (token == 0)
break;
}
cleanup:
- yy_delete_buffer(lex_buffer);
+ yy_delete_buffer(lex_buffer, scanner);
+ yylex_destroy(scanner);
/* Each recursion level must save and restore these static variables. */
ConfigFileLineno = save_ConfigFileLineno;
GUC_flex_fatal_jmp = save_GUC_flex_fatal_jmp;
--
2.47.1
v2-0011-plpgsql-reentrant-scanner.patchtext/plain; charset=UTF-8; name=v2-0011-plpgsql-reentrant-scanner.patchDownload
From 0728266e298c78e9943cf6f7ac81cd187c236f90 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Sat, 7 Dec 2024 00:09:08 +0100
Subject: [PATCH v2 11/11] plpgsql: reentrant scanner
---
src/pl/plpgsql/src/pl_comp.c | 18 +-
src/pl/plpgsql/src/pl_gram.y | 420 +++++++++++++++++---------------
src/pl/plpgsql/src/pl_scanner.c | 38 +--
src/pl/plpgsql/src/plpgsql.h | 16 +-
4 files changed, 256 insertions(+), 236 deletions(-)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 6255a86d75b..917daebef06 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -269,6 +269,7 @@ do_compile(FunctionCallInfo fcinfo,
Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
bool is_dml_trigger = CALLED_AS_TRIGGER(fcinfo);
bool is_event_trigger = CALLED_AS_EVENT_TRIGGER(fcinfo);
+ yyscan_t scanner;
Datum prosrcdatum;
char *proc_source;
HeapTuple typeTup;
@@ -292,11 +293,11 @@ do_compile(FunctionCallInfo fcinfo,
/*
* Setup the scanner input and error info. We assume that this function
* cannot be invoked recursively, so there's no need to save and restore
- * the static variables used here.
+ * the static variables used here. XXX
*/
prosrcdatum = SysCacheGetAttrNotNull(PROCOID, procTup, Anum_pg_proc_prosrc);
proc_source = TextDatumGetCString(prosrcdatum);
- plpgsql_scanner_init(proc_source);
+ scanner = plpgsql_scanner_init(proc_source);
plpgsql_error_funcname = pstrdup(NameStr(procStruct->proname));
@@ -779,12 +780,12 @@ do_compile(FunctionCallInfo fcinfo,
/*
* Now parse the function's text
*/
- parse_rc = plpgsql_yyparse();
+ parse_rc = plpgsql_yyparse(scanner);
if (parse_rc != 0)
elog(ERROR, "plpgsql parser returned %d", parse_rc);
function->action = plpgsql_parse_result;
- plpgsql_scanner_finish();
+ plpgsql_scanner_finish(scanner);
pfree(proc_source);
/*
@@ -841,6 +842,7 @@ do_compile(FunctionCallInfo fcinfo,
PLpgSQL_function *
plpgsql_compile_inline(char *proc_source)
{
+ yyscan_t scanner;
char *func_name = "inline_code_block";
PLpgSQL_function *function;
ErrorContextCallback plerrcontext;
@@ -851,9 +853,9 @@ plpgsql_compile_inline(char *proc_source)
/*
* Setup the scanner input and error info. We assume that this function
* cannot be invoked recursively, so there's no need to save and restore
- * the static variables used here.
+ * the static variables used here. XXX
*/
- plpgsql_scanner_init(proc_source);
+ scanner = plpgsql_scanner_init(proc_source);
plpgsql_error_funcname = func_name;
@@ -935,12 +937,12 @@ plpgsql_compile_inline(char *proc_source)
/*
* Now parse the function's text
*/
- parse_rc = plpgsql_yyparse();
+ parse_rc = plpgsql_yyparse(scanner);
if (parse_rc != 0)
elog(ERROR, "plpgsql parser returned %d", parse_rc);
function->action = plpgsql_parse_result;
- plpgsql_scanner_finish();
+ plpgsql_scanner_finish(scanner);
/*
* If it returns VOID (always true at the moment), we allow control to
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 8182ce28aa1..4d09fa89d99 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -63,7 +63,7 @@ static bool tok_is_keyword(int token, union YYSTYPE *lval,
int kw_token, const char *kw_str);
static void word_is_not_variable(PLword *word, int location);
static void cword_is_not_variable(PLcword *cword, int location);
-static void current_token_is_not_variable(int tok);
+static void current_token_is_not_variable(int tok, yyscan_t yyscanner);
static PLpgSQL_expr *read_sql_construct(int until,
int until2,
int until3,
@@ -72,31 +72,33 @@ static PLpgSQL_expr *read_sql_construct(int until,
bool isexpression,
bool valid_sql,
int *startloc,
- int *endtoken);
+ int *endtoken,
+ yyscan_t yyscanner);
static PLpgSQL_expr *read_sql_expression(int until,
- const char *expected);
+ const char *expected, yyscan_t yyscanner);
static PLpgSQL_expr *read_sql_expression2(int until, int until2,
const char *expected,
- int *endtoken);
-static PLpgSQL_expr *read_sql_stmt(void);
-static PLpgSQL_type *read_datatype(int tok);
+ int *endtoken, yyscan_t yyscanner);
+static PLpgSQL_expr *read_sql_stmt(yyscan_t yyscanner);
+static PLpgSQL_type *read_datatype(int tok, yyscan_t yyscanner);
static PLpgSQL_stmt *make_execsql_stmt(int firsttoken, int location,
- PLword *word);
-static PLpgSQL_stmt_fetch *read_fetch_direction(void);
+ PLword *word, yyscan_t yyscanner);
+static PLpgSQL_stmt_fetch *read_fetch_direction(yyscan_t yyscanner);
static void complete_direction(PLpgSQL_stmt_fetch *fetch,
- bool *check_FROM);
-static PLpgSQL_stmt *make_return_stmt(int location);
-static PLpgSQL_stmt *make_return_next_stmt(int location);
-static PLpgSQL_stmt *make_return_query_stmt(int location);
+ bool *check_FROM, yyscan_t yyscanner);
+static PLpgSQL_stmt *make_return_stmt(int location, yyscan_t yyscanner);
+static PLpgSQL_stmt *make_return_next_stmt(int location, yyscan_t yyscanner);
+static PLpgSQL_stmt *make_return_query_stmt(int location, yyscan_t yyscanner);
static PLpgSQL_stmt *make_case(int location, PLpgSQL_expr *t_expr,
List *case_when_list, List *else_stmts);
static char *NameOfDatum(PLwdatum *wdatum);
static void check_assignable(PLpgSQL_datum *datum, int location);
static void read_into_target(PLpgSQL_variable **target,
- bool *strict);
+ bool *strict, yyscan_t yyscanner);
static PLpgSQL_row *read_into_scalar_list(char *initial_name,
PLpgSQL_datum *initial_datum,
- int initial_location);
+ int initial_location,
+ yyscan_t yyscanner);
static PLpgSQL_row *make_scalar_list1(char *initial_name,
PLpgSQL_datum *initial_datum,
int lineno, int location);
@@ -108,12 +110,14 @@ static void check_labels(const char *start_label,
const char *end_label,
int end_location);
static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor,
- int until);
-static List *read_raise_options(void);
+ int until, yyscan_t yyscanner);
+static List *read_raise_options(yyscan_t yyscanner);
static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%}
+%parse-param {yyscan_t yyscanner}
+%lex-param {yyscan_t yyscanner}
%expect 0
%name-prefix="plpgsql_yy"
%locations
@@ -577,7 +581,7 @@ opt_scrollable :
decl_cursor_query :
{
- $$ = read_sql_stmt();
+ $$ = read_sql_stmt(yyscanner);
}
;
@@ -706,7 +710,7 @@ decl_varname : T_WORD
if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
$1.ident, NULL, NULL,
NULL) != NULL)
- yyerror("duplicate declaration");
+ yyerror(yyscanner, "duplicate declaration");
if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
@@ -734,7 +738,7 @@ decl_varname : T_WORD
if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
$1, NULL, NULL,
NULL) != NULL)
- yyerror("duplicate declaration");
+ yyerror(yyscanner, "duplicate declaration");
if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
@@ -766,7 +770,7 @@ decl_datatype :
* consume it, and then we must tell bison to forget
* it.
*/
- $$ = read_datatype(yychar);
+ $$ = read_datatype(yychar, yyscanner);
yyclearin;
}
;
@@ -799,7 +803,7 @@ decl_defval : ';'
{ $$ = NULL; }
| decl_defkey
{
- $$ = read_sql_expression(';', ";");
+ $$ = read_sql_expression(';', ";", yyscanner);
}
;
@@ -900,7 +904,8 @@ stmt_perform : K_PERFORM
new->expr = read_sql_construct(';', 0, 0, ";",
RAW_PARSE_DEFAULT,
false, false,
- &startloc, NULL);
+ &startloc, NULL,
+ yyscanner);
/* overwrite "perform" ... */
memcpy(new->expr->query, " SELECT", 7);
/* left-justify to get rid of the leading space */
@@ -923,7 +928,7 @@ stmt_call : K_CALL
new->lineno = plpgsql_location_to_lineno(@1);
new->stmtid = ++plpgsql_curr_compile->nstatements;
plpgsql_push_back_token(K_CALL);
- new->expr = read_sql_stmt();
+ new->expr = read_sql_stmt(yyscanner);
new->is_call = true;
/* Remember we may need a procedure resource owner */
@@ -942,7 +947,7 @@ stmt_call : K_CALL
new->lineno = plpgsql_location_to_lineno(@1);
new->stmtid = ++plpgsql_curr_compile->nstatements;
plpgsql_push_back_token(K_DO);
- new->expr = read_sql_stmt();
+ new->expr = read_sql_stmt(yyscanner);
new->is_call = false;
/* Remember we may need a procedure resource owner */
@@ -986,7 +991,8 @@ stmt_assign : T_DATUM
new->expr = read_sql_construct(';', 0, 0, ";",
pmode,
false, true,
- NULL, NULL);
+ NULL, NULL,
+ yyscanner);
$$ = (PLpgSQL_stmt *) new;
}
@@ -1093,7 +1099,7 @@ getdiag_list_item : getdiag_target assign_operator getdiag_item
getdiag_item :
{
- int tok = yylex();
+ int tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_ROW_COUNT, "row_count"))
@@ -1135,7 +1141,7 @@ getdiag_item :
K_RETURNED_SQLSTATE, "returned_sqlstate"))
$$ = PLPGSQL_GETDIAG_RETURNED_SQLSTATE;
else
- yyerror("unrecognized GET DIAGNOSTICS item");
+ yyerror(yyscanner, "unrecognized GET DIAGNOSTICS item");
}
;
@@ -1148,7 +1154,7 @@ getdiag_target : T_DATUM
*/
if ($1.datum->dtype == PLPGSQL_DTYPE_ROW ||
$1.datum->dtype == PLPGSQL_DTYPE_REC ||
- plpgsql_peek() == '[')
+ plpgsql_peek(yyscanner) == '[')
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a scalar variable",
@@ -1222,12 +1228,12 @@ stmt_case : K_CASE opt_expr_until_when case_when_list opt_case_else K_END K_CAS
opt_expr_until_when :
{
PLpgSQL_expr *expr = NULL;
- int tok = yylex();
+ int tok = yylex(yyscanner);
if (tok != K_WHEN)
{
plpgsql_push_back_token(tok);
- expr = read_sql_expression(K_WHEN, "WHEN");
+ expr = read_sql_expression(K_WHEN, "WHEN", yyscanner);
}
plpgsql_push_back_token(K_WHEN);
$$ = expr;
@@ -1347,7 +1353,7 @@ stmt_for : opt_loop_label K_FOR for_control loop_body
for_control : for_variable K_IN
{
- int tok = yylex();
+ int tok = yylex(yyscanner);
int tokloc = yylloc;
if (tok == K_EXECUTE)
@@ -1359,7 +1365,7 @@ for_control : for_variable K_IN
expr = read_sql_expression2(K_LOOP, K_USING,
"LOOP or USING",
- &term);
+ &term, yyscanner);
new = palloc0(sizeof(PLpgSQL_stmt_dynfors));
new->cmd_type = PLPGSQL_STMT_DYNFORS;
@@ -1392,7 +1398,7 @@ for_control : for_variable K_IN
{
expr = read_sql_expression2(',', K_LOOP,
", or LOOP",
- &term);
+ &term, yyscanner);
new->params = lappend(new->params, expr);
} while (term == ',');
}
@@ -1427,8 +1433,7 @@ for_control : for_variable K_IN
parser_errposition(tokloc)));
/* collect cursor's parameters if any */
- new->argquery = read_cursor_args(cursor,
- K_LOOP);
+ new->argquery = read_cursor_args(cursor, K_LOOP, yyscanner);
/* create loop's private RECORD variable */
new->var = (PLpgSQL_variable *)
@@ -1479,7 +1484,8 @@ for_control : for_variable K_IN
true,
false,
&expr1loc,
- &tok);
+ &tok,
+ yyscanner);
if (tok == DOT_DOT)
{
@@ -1500,12 +1506,12 @@ for_control : for_variable K_IN
/* Read and check the second one */
expr2 = read_sql_expression2(K_LOOP, K_BY,
"LOOP",
- &tok);
+ &tok, yyscanner);
/* Get the BY clause if any */
if (tok == K_BY)
expr_by = read_sql_expression(K_LOOP,
- "LOOP");
+ "LOOP", yyscanner);
else
expr_by = NULL;
@@ -1620,13 +1626,14 @@ for_variable : T_DATUM
$$.scalar = $1.datum;
$$.row = NULL;
/* check for comma-separated list */
- tok = yylex();
+ tok = yylex(yyscanner);
plpgsql_push_back_token(tok);
if (tok == ',')
$$.row = (PLpgSQL_datum *)
read_into_scalar_list($$.name,
$$.scalar,
- @1);
+ @1,
+ yyscanner);
}
}
| T_WORD
@@ -1638,7 +1645,7 @@ for_variable : T_DATUM
$$.scalar = NULL;
$$.row = NULL;
/* check for comma-separated list */
- tok = yylex();
+ tok = yylex(yyscanner);
plpgsql_push_back_token(tok);
if (tok == ',')
word_is_not_variable(&($1), @1);
@@ -1765,24 +1772,24 @@ stmt_return : K_RETURN
{
int tok;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == 0)
- yyerror("unexpected end of function definition");
+ yyerror(yyscanner, "unexpected end of function definition");
if (tok_is_keyword(tok, &yylval,
K_NEXT, "next"))
{
- $$ = make_return_next_stmt(@1);
+ $$ = make_return_next_stmt(@1, yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_QUERY, "query"))
{
- $$ = make_return_query_stmt(@1);
+ $$ = make_return_query_stmt(@1, yyscanner);
}
else
{
plpgsql_push_back_token(tok);
- $$ = make_return_stmt(@1);
+ $$ = make_return_stmt(@1, yyscanner);
}
}
;
@@ -1803,9 +1810,9 @@ stmt_raise : K_RAISE
new->params = NIL;
new->options = NIL;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == 0)
- yyerror("unexpected end of function definition");
+ yyerror(yyscanner, "unexpected end of function definition");
/*
* We could have just RAISE, meaning to re-throw
@@ -1820,40 +1827,40 @@ stmt_raise : K_RAISE
K_EXCEPTION, "exception"))
{
new->elog_level = ERROR;
- tok = yylex();
+ tok = yylex(yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_WARNING, "warning"))
{
new->elog_level = WARNING;
- tok = yylex();
+ tok = yylex(yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_NOTICE, "notice"))
{
new->elog_level = NOTICE;
- tok = yylex();
+ tok = yylex(yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_INFO, "info"))
{
new->elog_level = INFO;
- tok = yylex();
+ tok = yylex(yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_LOG, "log"))
{
new->elog_level = LOG;
- tok = yylex();
+ tok = yylex(yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_DEBUG, "debug"))
{
new->elog_level = DEBUG1;
- tok = yylex();
+ tok = yylex(yyscanner);
}
if (tok == 0)
- yyerror("unexpected end of function definition");
+ yyerror(yyscanner, "unexpected end of function definition");
/*
* Next we can have a condition name, or
@@ -1871,9 +1878,9 @@ stmt_raise : K_RAISE
* begins the list of parameter expressions,
* or USING to begin the options list.
*/
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok != ',' && tok != ';' && tok != K_USING)
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
while (tok == ',')
{
@@ -1883,7 +1890,8 @@ stmt_raise : K_RAISE
", or ; or USING",
RAW_PARSE_PLPGSQL_EXPR,
true, true,
- NULL, &tok);
+ NULL, &tok,
+ yyscanner);
new->params = lappend(new->params, expr);
}
}
@@ -1896,14 +1904,14 @@ stmt_raise : K_RAISE
/* next token should be a string literal */
char *sqlstatestr;
- if (yylex() != SCONST)
- yyerror("syntax error");
+ if (yylex(yyscanner) != SCONST)
+ yyerror(yyscanner, "syntax error");
sqlstatestr = yylval.str;
if (strlen(sqlstatestr) != 5)
- yyerror("invalid SQLSTATE code");
+ yyerror(yyscanner, "invalid SQLSTATE code");
if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
- yyerror("invalid SQLSTATE code");
+ yyerror(yyscanner, "invalid SQLSTATE code");
new->condname = sqlstatestr;
}
else
@@ -1913,17 +1921,17 @@ stmt_raise : K_RAISE
else if (plpgsql_token_is_unreserved_keyword(tok))
new->condname = pstrdup(yylval.keyword);
else
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
plpgsql_recognize_err_condition(new->condname,
false);
}
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok != ';' && tok != K_USING)
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
}
if (tok == K_USING)
- new->options = read_raise_options();
+ new->options = read_raise_options(yyscanner);
}
check_raise_parameters(new);
@@ -1945,10 +1953,10 @@ stmt_assert : K_ASSERT
new->cond = read_sql_expression2(',', ';',
", or ;",
- &tok);
+ &tok, yyscanner);
if (tok == ',')
- new->message = read_sql_expression(';', ";");
+ new->message = read_sql_expression(';', ";", yyscanner);
else
new->message = NULL;
@@ -1976,37 +1984,37 @@ loop_body : proc_sect K_END K_LOOP opt_label ';'
*/
stmt_execsql : K_IMPORT
{
- $$ = make_execsql_stmt(K_IMPORT, @1, NULL);
+ $$ = make_execsql_stmt(K_IMPORT, @1, NULL, yyscanner);
}
| K_INSERT
{
- $$ = make_execsql_stmt(K_INSERT, @1, NULL);
+ $$ = make_execsql_stmt(K_INSERT, @1, NULL, yyscanner);
}
| K_MERGE
{
- $$ = make_execsql_stmt(K_MERGE, @1, NULL);
+ $$ = make_execsql_stmt(K_MERGE, @1, NULL, yyscanner);
}
| T_WORD
{
int tok;
- tok = yylex();
+ tok = yylex(yyscanner);
plpgsql_push_back_token(tok);
if (tok == '=' || tok == COLON_EQUALS ||
tok == '[' || tok == '.')
word_is_not_variable(&($1), @1);
- $$ = make_execsql_stmt(T_WORD, @1, &($1));
+ $$ = make_execsql_stmt(T_WORD, @1, &($1), yyscanner);
}
| T_CWORD
{
int tok;
- tok = yylex();
+ tok = yylex(yyscanner);
plpgsql_push_back_token(tok);
if (tok == '=' || tok == COLON_EQUALS ||
tok == '[' || tok == '.')
cword_is_not_variable(&($1), @1);
- $$ = make_execsql_stmt(T_CWORD, @1, NULL);
+ $$ = make_execsql_stmt(T_CWORD, @1, NULL, yyscanner);
}
;
@@ -2020,7 +2028,8 @@ stmt_dynexecute : K_EXECUTE
"INTO or USING or ;",
RAW_PARSE_PLPGSQL_EXPR,
true, true,
- NULL, &endtoken);
+ NULL, &endtoken,
+ yyscanner);
new = palloc(sizeof(PLpgSQL_stmt_dynexecute));
new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
@@ -2044,29 +2053,30 @@ stmt_dynexecute : K_EXECUTE
if (endtoken == K_INTO)
{
if (new->into) /* multiple INTO */
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
new->into = true;
- read_into_target(&new->target, &new->strict);
- endtoken = yylex();
+ read_into_target(&new->target, &new->strict, yyscanner);
+ endtoken = yylex(yyscanner);
}
else if (endtoken == K_USING)
{
if (new->params) /* multiple USING */
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
do
{
expr = read_sql_construct(',', ';', K_INTO,
", or ; or INTO",
RAW_PARSE_PLPGSQL_EXPR,
true, true,
- NULL, &endtoken);
+ NULL, &endtoken,
+ yyscanner);
new->params = lappend(new->params, expr);
} while (endtoken == ',');
}
else if (endtoken == ';')
break;
else
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
}
$$ = (PLpgSQL_stmt *) new;
@@ -2089,29 +2099,29 @@ stmt_open : K_OPEN cursor_variable
if ($2->cursor_explicit_expr == NULL)
{
/* be nice if we could use opt_scrollable here */
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_NO, "no"))
{
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_SCROLL, "scroll"))
{
new->cursor_options |= CURSOR_OPT_NO_SCROLL;
- tok = yylex();
+ tok = yylex(yyscanner);
}
}
else if (tok_is_keyword(tok, &yylval,
K_SCROLL, "scroll"))
{
new->cursor_options |= CURSOR_OPT_SCROLL;
- tok = yylex();
+ tok = yylex(yyscanner);
}
if (tok != K_FOR)
- yyerror("syntax error, expected \"FOR\"");
+ yyerror(yyscanner, "syntax error, expected \"FOR\"");
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == K_EXECUTE)
{
int endtoken;
@@ -2119,7 +2129,7 @@ stmt_open : K_OPEN cursor_variable
new->dynquery =
read_sql_expression2(K_USING, ';',
"USING or ;",
- &endtoken);
+ &endtoken, yyscanner);
/* If we found "USING", collect argument(s) */
if (endtoken == K_USING)
@@ -2130,7 +2140,7 @@ stmt_open : K_OPEN cursor_variable
{
expr = read_sql_expression2(',', ';',
", or ;",
- &endtoken);
+ &endtoken, yyscanner);
new->params = lappend(new->params,
expr);
} while (endtoken == ',');
@@ -2139,13 +2149,13 @@ stmt_open : K_OPEN cursor_variable
else
{
plpgsql_push_back_token(tok);
- new->query = read_sql_stmt();
+ new->query = read_sql_stmt(yyscanner);
}
}
else
{
/* predefined cursor query, so read args */
- new->argquery = read_cursor_args($2, ';');
+ new->argquery = read_cursor_args($2, ';', yyscanner);
}
$$ = (PLpgSQL_stmt *) new;
@@ -2158,10 +2168,10 @@ stmt_fetch : K_FETCH opt_fetch_direction cursor_variable K_INTO
PLpgSQL_variable *target;
/* We have already parsed everything through the INTO keyword */
- read_into_target(&target, NULL);
+ read_into_target(&target, NULL, yyscanner);
- if (yylex() != ';')
- yyerror("syntax error");
+ if (yylex(yyscanner) != ';')
+ yyerror(yyscanner, "syntax error");
/*
* We don't allow multiple rows in PL/pgSQL's FETCH
@@ -2196,7 +2206,7 @@ stmt_move : K_MOVE opt_fetch_direction cursor_variable ';'
opt_fetch_direction :
{
- $$ = read_fetch_direction();
+ $$ = read_fetch_direction(yyscanner);
}
;
@@ -2264,7 +2274,7 @@ cursor_variable : T_DATUM
* just throw an error if next token is '['.
*/
if ($1.datum->dtype != PLPGSQL_DTYPE_VAR ||
- plpgsql_peek() == '[')
+ plpgsql_peek(yyscanner) == '[')
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cursor variable must be a simple variable"),
@@ -2384,14 +2394,14 @@ proc_condition : any_identifier
char *sqlstatestr;
/* next token should be a string literal */
- if (yylex() != SCONST)
- yyerror("syntax error");
+ if (yylex(yyscanner) != SCONST)
+ yyerror(yyscanner, "syntax error");
sqlstatestr = yylval.str;
if (strlen(sqlstatestr) != 5)
- yyerror("invalid SQLSTATE code");
+ yyerror(yyscanner, "invalid SQLSTATE code");
if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
- yyerror("invalid SQLSTATE code");
+ yyerror(yyscanner, "invalid SQLSTATE code");
new = palloc(sizeof(PLpgSQL_condition));
new->sqlerrstate =
@@ -2409,15 +2419,15 @@ proc_condition : any_identifier
;
expr_until_semi :
- { $$ = read_sql_expression(';', ";"); }
+ { $$ = read_sql_expression(';', ";", yyscanner); }
;
expr_until_then :
- { $$ = read_sql_expression(K_THEN, "THEN"); }
+ { $$ = read_sql_expression(K_THEN, "THEN", yyscanner); }
;
expr_until_loop :
- { $$ = read_sql_expression(K_LOOP, "LOOP"); }
+ { $$ = read_sql_expression(K_LOOP, "LOOP", yyscanner); }
;
opt_block_label :
@@ -2475,7 +2485,7 @@ any_identifier : T_WORD
| T_DATUM
{
if ($1.ident == NULL) /* composite name not OK */
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
$$ = $1.ident;
}
;
@@ -2627,42 +2637,45 @@ cword_is_not_variable(PLcword *cword, int location)
* look at yylval and yylloc.
*/
static void
-current_token_is_not_variable(int tok)
+current_token_is_not_variable(int tok, yyscan_t yyscanner)
{
if (tok == T_WORD)
word_is_not_variable(&(yylval.word), yylloc);
else if (tok == T_CWORD)
cword_is_not_variable(&(yylval.cword), yylloc);
else
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
}
/* Convenience routine to read an expression with one possible terminator */
static PLpgSQL_expr *
-read_sql_expression(int until, const char *expected)
+read_sql_expression(int until, const char *expected, yyscan_t yyscanner)
{
return read_sql_construct(until, 0, 0, expected,
RAW_PARSE_PLPGSQL_EXPR,
- true, true, NULL, NULL);
+ true, true, NULL, NULL,
+ yyscanner);
}
/* Convenience routine to read an expression with two possible terminators */
static PLpgSQL_expr *
read_sql_expression2(int until, int until2, const char *expected,
- int *endtoken)
+ int *endtoken, yyscan_t yyscanner)
{
return read_sql_construct(until, until2, 0, expected,
RAW_PARSE_PLPGSQL_EXPR,
- true, true, NULL, endtoken);
+ true, true, NULL, endtoken,
+ yyscanner);
}
/* Convenience routine to read a SQL statement that must end with ';' */
static PLpgSQL_expr *
-read_sql_stmt(void)
+read_sql_stmt(yyscan_t yyscanner)
{
return read_sql_construct(';', 0, 0, ";",
RAW_PARSE_DEFAULT,
- false, true, NULL, NULL);
+ false, true, NULL, NULL,
+ yyscanner);
}
/*
@@ -2688,7 +2701,8 @@ read_sql_construct(int until,
bool isexpression,
bool valid_sql,
int *startloc,
- int *endtoken)
+ int *endtoken,
+ yyscan_t yyscanner)
{
int tok;
StringInfoData ds;
@@ -2706,7 +2720,7 @@ read_sql_construct(int until,
for (;;)
{
- tok = yylex();
+ tok = yylex(yyscanner);
if (startlocation < 0) /* remember loc of first token */
startlocation = yylloc;
if (tok == until && parenlevel == 0)
@@ -2721,7 +2735,7 @@ read_sql_construct(int until,
{
parenlevel--;
if (parenlevel < 0)
- yyerror("mismatched parentheses");
+ yyerror(yyscanner, "mismatched parentheses");
}
/*
* End of function definition is an error, and we don't expect to
@@ -2731,7 +2745,7 @@ read_sql_construct(int until,
if (tok == 0 || tok == ';')
{
if (parenlevel != 0)
- yyerror("mismatched parentheses");
+ yyerror(yyscanner, "mismatched parentheses");
if (isexpression)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -2760,9 +2774,9 @@ read_sql_construct(int until,
if (startlocation >= endlocation)
{
if (isexpression)
- yyerror("missing expression");
+ yyerror(yyscanner, "missing expression");
else
- yyerror("missing SQL statement");
+ yyerror(yyscanner, "missing SQL statement");
}
/*
@@ -2794,7 +2808,7 @@ read_sql_construct(int until,
* Returns a PLpgSQL_type struct.
*/
static PLpgSQL_type *
-read_datatype(int tok)
+read_datatype(int tok, yyscan_t yyscanner)
{
StringInfoData ds;
char *type_name;
@@ -2807,7 +2821,7 @@ read_datatype(int tok)
/* Often there will be a lookahead token, but if not, get one */
if (tok == YYEMPTY)
- tok = yylex();
+ tok = yylex(yyscanner);
/* The current token is the start of what we'll pass to parse_datatype */
startlocation = yylloc;
@@ -2820,10 +2834,10 @@ read_datatype(int tok)
{
char *dtname = yylval.word.ident;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == '%')
{
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_TYPE, "type"))
result = plpgsql_parse_wordtype(dtname);
@@ -2836,10 +2850,10 @@ read_datatype(int tok)
{
char *dtname = pstrdup(yylval.keyword);
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == '%')
{
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_TYPE, "type"))
result = plpgsql_parse_wordtype(dtname);
@@ -2852,10 +2866,10 @@ read_datatype(int tok)
{
List *dtnames = yylval.cword.idents;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == '%')
{
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_TYPE, "type"))
result = plpgsql_parse_cwordtype(dtnames);
@@ -2877,22 +2891,22 @@ read_datatype(int tok)
{
bool is_array = false;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_ARRAY, "array"))
{
is_array = true;
- tok = yylex();
+ tok = yylex(yyscanner);
}
while (tok == '[')
{
is_array = true;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == ICONST)
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok != ']')
- yyerror("syntax error, expected \"]\"");
- tok = yylex();
+ yyerror(yyscanner, "syntax error, expected \"]\"");
+ tok = yylex(yyscanner);
}
plpgsql_push_back_token(tok);
@@ -2913,9 +2927,9 @@ read_datatype(int tok)
if (tok == 0)
{
if (parenlevel != 0)
- yyerror("mismatched parentheses");
+ yyerror(yyscanner, "mismatched parentheses");
else
- yyerror("incomplete data type declaration");
+ yyerror(yyscanner, "incomplete data type declaration");
}
/* Possible followers for datatype in a declaration */
if (tok == K_COLLATE || tok == K_NOT ||
@@ -2929,7 +2943,7 @@ read_datatype(int tok)
else if (tok == ')')
parenlevel--;
- tok = yylex();
+ tok = yylex(yyscanner);
}
/* set up ds to contain complete typename text */
@@ -2938,7 +2952,7 @@ read_datatype(int tok)
type_name = ds.data;
if (type_name[0] == '\0')
- yyerror("missing data type declaration");
+ yyerror(yyscanner, "missing data type declaration");
result = parse_datatype(type_name, startlocation);
@@ -2955,7 +2969,7 @@ read_datatype(int tok)
* If firsttoken == T_WORD, pass its yylval value as "word", else pass NULL.
*/
static PLpgSQL_stmt *
-make_execsql_stmt(int firsttoken, int location, PLword *word)
+make_execsql_stmt(int firsttoken, int location, PLword *word, yyscan_t yyscanner)
{
StringInfoData ds;
IdentifierLookup save_IdentifierLookup;
@@ -3024,7 +3038,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word)
for (;;)
{
prev_tok = tok;
- tok = yylex();
+ tok = yylex(yyscanner);
if (have_into && into_end_loc < 0)
into_end_loc = yylloc; /* token after the INTO part */
/* Detect CREATE [OR REPLACE] {FUNCTION|PROCEDURE} */
@@ -3063,7 +3077,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word)
if (tok == ';' && paren_depth == 0 && begin_depth == 0)
break;
if (tok == 0)
- yyerror("unexpected end of function definition");
+ yyerror(yyscanner, "unexpected end of function definition");
if (tok == K_INTO)
{
if (prev_tok == K_INSERT)
@@ -3073,11 +3087,11 @@ make_execsql_stmt(int firsttoken, int location, PLword *word)
if (firsttoken == K_IMPORT)
continue; /* IMPORT ... INTO is not an INTO-target */
if (have_into)
- yyerror("INTO specified more than once");
+ yyerror(yyscanner, "INTO specified more than once");
have_into = true;
into_start_loc = yylloc;
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
- read_into_target(&target, &have_strict);
+ read_into_target(&target, &have_strict, yyscanner);
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_EXPR;
}
}
@@ -3130,7 +3144,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word)
* Read FETCH or MOVE direction clause (everything through FROM/IN).
*/
static PLpgSQL_stmt_fetch *
-read_fetch_direction(void)
+read_fetch_direction(yyscan_t yyscanner)
{
PLpgSQL_stmt_fetch *fetch;
int tok;
@@ -3149,9 +3163,9 @@ read_fetch_direction(void)
fetch->expr = NULL;
fetch->returns_multiple_rows = false;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == 0)
- yyerror("unexpected end of function definition");
+ yyerror(yyscanner, "unexpected end of function definition");
if (tok_is_keyword(tok, &yylval,
K_NEXT, "next"))
@@ -3180,7 +3194,7 @@ read_fetch_direction(void)
fetch->direction = FETCH_ABSOLUTE;
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"FROM or IN",
- NULL);
+ NULL, yyscanner);
check_FROM = false;
}
else if (tok_is_keyword(tok, &yylval,
@@ -3189,7 +3203,7 @@ read_fetch_direction(void)
fetch->direction = FETCH_RELATIVE;
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"FROM or IN",
- NULL);
+ NULL, yyscanner);
check_FROM = false;
}
else if (tok_is_keyword(tok, &yylval,
@@ -3201,13 +3215,13 @@ read_fetch_direction(void)
else if (tok_is_keyword(tok, &yylval,
K_FORWARD, "forward"))
{
- complete_direction(fetch, &check_FROM);
+ complete_direction(fetch, &check_FROM, yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_BACKWARD, "backward"))
{
fetch->direction = FETCH_BACKWARD;
- complete_direction(fetch, &check_FROM);
+ complete_direction(fetch, &check_FROM, yyscanner);
}
else if (tok == K_FROM || tok == K_IN)
{
@@ -3233,7 +3247,7 @@ read_fetch_direction(void)
plpgsql_push_back_token(tok);
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"FROM or IN",
- NULL);
+ NULL, yyscanner);
fetch->returns_multiple_rows = true;
check_FROM = false;
}
@@ -3241,9 +3255,9 @@ read_fetch_direction(void)
/* check FROM or IN keyword after direction's specification */
if (check_FROM)
{
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok != K_FROM && tok != K_IN)
- yyerror("expected FROM or IN");
+ yyerror(yyscanner, "expected FROM or IN");
}
return fetch;
@@ -3256,13 +3270,13 @@ read_fetch_direction(void)
* BACKWARD expr, BACKWARD ALL, BACKWARD
*/
static void
-complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM)
+complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM, yyscan_t yyscanner)
{
int tok;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == 0)
- yyerror("unexpected end of function definition");
+ yyerror(yyscanner, "unexpected end of function definition");
if (tok == K_FROM || tok == K_IN)
{
@@ -3281,14 +3295,14 @@ complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM)
plpgsql_push_back_token(tok);
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"FROM or IN",
- NULL);
+ NULL, yyscanner);
fetch->returns_multiple_rows = true;
*check_FROM = false;
}
static PLpgSQL_stmt *
-make_return_stmt(int location)
+make_return_stmt(int location, yyscan_t yyscanner)
{
PLpgSQL_stmt_return *new;
@@ -3301,7 +3315,7 @@ make_return_stmt(int location)
if (plpgsql_curr_compile->fn_retset)
{
- if (yylex() != ';')
+ if (yylex(yyscanner) != ';')
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("RETURN cannot have a parameter in function returning set"),
@@ -3310,7 +3324,7 @@ make_return_stmt(int location)
}
else if (plpgsql_curr_compile->fn_rettype == VOIDOID)
{
- if (yylex() != ';')
+ if (yylex(yyscanner) != ';')
{
if (plpgsql_curr_compile->fn_prokind == PROKIND_PROCEDURE)
ereport(ERROR,
@@ -3326,7 +3340,7 @@ make_return_stmt(int location)
}
else if (plpgsql_curr_compile->out_param_varno >= 0)
{
- if (yylex() != ';')
+ if (yylex(yyscanner) != ';')
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("RETURN cannot have a parameter in function with OUT parameters"),
@@ -3339,9 +3353,9 @@ make_return_stmt(int location)
* We want to special-case simple variable references for efficiency.
* So peek ahead to see if that's what we have.
*/
- int tok = yylex();
+ int tok = yylex(yyscanner);
- if (tok == T_DATUM && plpgsql_peek() == ';' &&
+ if (tok == T_DATUM && plpgsql_peek(yyscanner) == ';' &&
(yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_VAR ||
yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_PROMISE ||
yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
@@ -3349,7 +3363,7 @@ make_return_stmt(int location)
{
new->retvarno = yylval.wdatum.datum->dno;
/* eat the semicolon token that we only peeked at above */
- tok = yylex();
+ tok = yylex(yyscanner);
Assert(tok == ';');
}
else
@@ -3361,7 +3375,7 @@ make_return_stmt(int location)
* anything else is a compile-time error.
*/
plpgsql_push_back_token(tok);
- new->expr = read_sql_expression(';', ";");
+ new->expr = read_sql_expression(';', ";", yyscanner);
}
}
@@ -3370,7 +3384,7 @@ make_return_stmt(int location)
static PLpgSQL_stmt *
-make_return_next_stmt(int location)
+make_return_next_stmt(int location, yyscan_t yyscanner)
{
PLpgSQL_stmt_return_next *new;
@@ -3389,7 +3403,7 @@ make_return_next_stmt(int location)
if (plpgsql_curr_compile->out_param_varno >= 0)
{
- if (yylex() != ';')
+ if (yylex(yyscanner) != ';')
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("RETURN NEXT cannot have a parameter in function with OUT parameters"),
@@ -3402,9 +3416,9 @@ make_return_next_stmt(int location)
* We want to special-case simple variable references for efficiency.
* So peek ahead to see if that's what we have.
*/
- int tok = yylex();
+ int tok = yylex(yyscanner);
- if (tok == T_DATUM && plpgsql_peek() == ';' &&
+ if (tok == T_DATUM && plpgsql_peek(yyscanner) == ';' &&
(yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_VAR ||
yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_PROMISE ||
yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
@@ -3412,7 +3426,7 @@ make_return_next_stmt(int location)
{
new->retvarno = yylval.wdatum.datum->dno;
/* eat the semicolon token that we only peeked at above */
- tok = yylex();
+ tok = yylex(yyscanner);
Assert(tok == ';');
}
else
@@ -3424,7 +3438,7 @@ make_return_next_stmt(int location)
* anything else is a compile-time error.
*/
plpgsql_push_back_token(tok);
- new->expr = read_sql_expression(';', ";");
+ new->expr = read_sql_expression(';', ";", yyscanner);
}
}
@@ -3433,7 +3447,7 @@ make_return_next_stmt(int location)
static PLpgSQL_stmt *
-make_return_query_stmt(int location)
+make_return_query_stmt(int location, yyscan_t yyscanner)
{
PLpgSQL_stmt_return_query *new;
int tok;
@@ -3450,11 +3464,11 @@ make_return_query_stmt(int location)
new->stmtid = ++plpgsql_curr_compile->nstatements;
/* check for RETURN QUERY EXECUTE */
- if ((tok = yylex()) != K_EXECUTE)
+ if ((tok = yylex(yyscanner)) != K_EXECUTE)
{
/* ordinary static query */
plpgsql_push_back_token(tok);
- new->query = read_sql_stmt();
+ new->query = read_sql_stmt(yyscanner);
}
else
{
@@ -3462,14 +3476,14 @@ make_return_query_stmt(int location)
int term;
new->dynquery = read_sql_expression2(';', K_USING, "; or USING",
- &term);
+ &term, yyscanner);
if (term == K_USING)
{
do
{
PLpgSQL_expr *expr;
- expr = read_sql_expression2(',', ';', ", or ;", &term);
+ expr = read_sql_expression2(',', ';', ", or ;", &term, yyscanner);
new->params = lappend(new->params, expr);
} while (term == ',');
}
@@ -3523,7 +3537,7 @@ check_assignable(PLpgSQL_datum *datum, int location)
* INTO keyword.
*/
static void
-read_into_target(PLpgSQL_variable **target, bool *strict)
+read_into_target(PLpgSQL_variable **target, bool *strict, yyscan_t yyscanner)
{
int tok;
@@ -3532,11 +3546,11 @@ read_into_target(PLpgSQL_variable **target, bool *strict)
if (strict)
*strict = false;
- tok = yylex();
+ tok = yylex(yyscanner);
if (strict && tok == K_STRICT)
{
*strict = true;
- tok = yylex();
+ tok = yylex(yyscanner);
}
/*
@@ -3555,7 +3569,7 @@ read_into_target(PLpgSQL_variable **target, bool *strict)
check_assignable(yylval.wdatum.datum, yylloc);
*target = (PLpgSQL_variable *) yylval.wdatum.datum;
- if ((tok = yylex()) == ',')
+ if ((tok = yylex(yyscanner)) == ',')
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("record variable cannot be part of multiple-item INTO list"),
@@ -3566,13 +3580,13 @@ read_into_target(PLpgSQL_variable **target, bool *strict)
{
*target = (PLpgSQL_variable *)
read_into_scalar_list(NameOfDatum(&(yylval.wdatum)),
- yylval.wdatum.datum, yylloc);
+ yylval.wdatum.datum, yylloc, yyscanner);
}
break;
default:
/* just to give a better message than "syntax error" */
- current_token_is_not_variable(tok);
+ current_token_is_not_variable(tok, yyscanner);
}
}
@@ -3585,7 +3599,8 @@ read_into_target(PLpgSQL_variable **target, bool *strict)
static PLpgSQL_row *
read_into_scalar_list(char *initial_name,
PLpgSQL_datum *initial_datum,
- int initial_location)
+ int initial_location,
+ yyscan_t yyscanner)
{
int nfields;
char *fieldnames[1024];
@@ -3598,7 +3613,7 @@ read_into_scalar_list(char *initial_name,
varnos[0] = initial_datum->dno;
nfields = 1;
- while ((tok = yylex()) == ',')
+ while ((tok = yylex(yyscanner)) == ',')
{
/* Check for array overflow */
if (nfields >= 1024)
@@ -3607,7 +3622,7 @@ read_into_scalar_list(char *initial_name,
errmsg("too many INTO variables specified"),
parser_errposition(yylloc)));
- tok = yylex();
+ tok = yylex(yyscanner);
switch (tok)
{
case T_DATUM:
@@ -3625,7 +3640,7 @@ read_into_scalar_list(char *initial_name,
default:
/* just to give a better message than "syntax error" */
- current_token_is_not_variable(tok);
+ current_token_is_not_variable(tok, yyscanner);
}
}
@@ -3836,7 +3851,7 @@ check_labels(const char *start_label, const char *end_label, int end_location)
* parens).
*/
static PLpgSQL_expr *
-read_cursor_args(PLpgSQL_var *cursor, int until)
+read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
{
PLpgSQL_expr *expr;
PLpgSQL_row *row;
@@ -3846,7 +3861,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
StringInfoData ds;
bool any_named = false;
- tok = yylex();
+ tok = yylex(yyscanner);
if (cursor->cursor_explicit_argrow < 0)
{
/* No arguments expected */
@@ -3858,7 +3873,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
parser_errposition(yylloc)));
if (tok != until)
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
return NULL;
}
@@ -3887,7 +3902,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
int arglocation;
/* Check if it's a named parameter: "param := value" */
- plpgsql_peek2(&tok1, &tok2, &arglocation, NULL);
+ plpgsql_peek2(&tok1, &tok2, &arglocation, NULL, yyscanner);
if (tok1 == IDENT && tok2 == COLON_EQUALS)
{
char *argname;
@@ -3896,7 +3911,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
/* Read the argument name, ignoring any matching variable */
save_IdentifierLookup = plpgsql_IdentifierLookup;
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_DECLARE;
- yylex();
+ yylex(yyscanner);
argname = yylval.str;
plpgsql_IdentifierLookup = save_IdentifierLookup;
@@ -3917,9 +3932,9 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
* Eat the ":=". We already peeked, so the error should never
* happen.
*/
- tok2 = yylex();
+ tok2 = yylex(yyscanner);
if (tok2 != COLON_EQUALS)
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
any_named = true;
}
@@ -3943,7 +3958,8 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
",\" or \")",
RAW_PARSE_PLPGSQL_EXPR,
true, true,
- NULL, &endtoken);
+ NULL, &endtoken,
+ yyscanner);
argv[argpos] = item->query;
@@ -3990,9 +4006,9 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
pfree(ds.data);
/* Next we'd better find the until token */
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok != until)
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
return expr;
}
@@ -4001,7 +4017,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
* Parse RAISE ... USING options
*/
static List *
-read_raise_options(void)
+read_raise_options(yyscan_t yyscanner)
{
List *result = NIL;
@@ -4010,8 +4026,8 @@ read_raise_options(void)
PLpgSQL_raise_option *opt;
int tok;
- if ((tok = yylex()) == 0)
- yyerror("unexpected end of function definition");
+ if ((tok = yylex(yyscanner)) == 0)
+ yyerror(yyscanner, "unexpected end of function definition");
opt = (PLpgSQL_raise_option *) palloc(sizeof(PLpgSQL_raise_option));
@@ -4043,13 +4059,13 @@ read_raise_options(void)
K_SCHEMA, "schema"))
opt->opt_type = PLPGSQL_RAISEOPTION_SCHEMA;
else
- yyerror("unrecognized RAISE statement option");
+ yyerror(yyscanner, "unrecognized RAISE statement option");
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok != '=' && tok != COLON_EQUALS)
- yyerror("syntax error, expected \"=\"");
+ yyerror(yyscanner, "syntax error, expected \"=\"");
- opt->expr = read_sql_expression2(',', ';', ", or ;", &tok);
+ opt->expr = read_sql_expression2(',', ';', ", or ;", &tok, yyscanner);
result = lappend(result, opt);
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
index 9407da51efa..e24b107909b 100644
--- a/src/pl/plpgsql/src/pl_scanner.c
+++ b/src/pl/plpgsql/src/pl_scanner.c
@@ -103,7 +103,6 @@ typedef struct
*/
/* The stuff the core lexer needs */
-static core_yyscan_t yyscanner = NULL;
static core_yy_extra_type core_yy;
/* The original input string */
@@ -128,7 +127,7 @@ static const char *cur_line_end;
static int cur_line_num;
/* Internal functions */
-static int internal_yylex(TokenAuxData *auxdata);
+static int internal_yylex(TokenAuxData *auxdata, yyscan_t yyscanner);
static void push_back_token(int token, TokenAuxData *auxdata);
static void location_lineno_init(void);
@@ -143,37 +142,37 @@ static void location_lineno_init(void);
* matches one of those.
*/
int
-plpgsql_yylex(void)
+plpgsql_yylex(yyscan_t yyscanner)
{
int tok1;
TokenAuxData aux1;
int kwnum;
- tok1 = internal_yylex(&aux1);
+ tok1 = internal_yylex(&aux1, yyscanner);
if (tok1 == IDENT || tok1 == PARAM)
{
int tok2;
TokenAuxData aux2;
- tok2 = internal_yylex(&aux2);
+ tok2 = internal_yylex(&aux2, yyscanner);
if (tok2 == '.')
{
int tok3;
TokenAuxData aux3;
- tok3 = internal_yylex(&aux3);
+ tok3 = internal_yylex(&aux3, yyscanner);
if (tok3 == IDENT)
{
int tok4;
TokenAuxData aux4;
- tok4 = internal_yylex(&aux4);
+ tok4 = internal_yylex(&aux4, yyscanner);
if (tok4 == '.')
{
int tok5;
TokenAuxData aux5;
- tok5 = internal_yylex(&aux5);
+ tok5 = internal_yylex(&aux5, yyscanner);
if (tok5 == IDENT)
{
if (plpgsql_parse_tripword(aux1.lval.str,
@@ -322,7 +321,7 @@ plpgsql_token_length(void)
* interfacing from the core_YYSTYPE to YYSTYPE union.
*/
static int
-internal_yylex(TokenAuxData *auxdata)
+internal_yylex(TokenAuxData *auxdata, yyscan_t yyscanner)
{
int token;
const char *yytext;
@@ -434,12 +433,12 @@ plpgsql_append_source_text(StringInfo buf,
* be returned as IDENT. Reserved keywords are resolved as usual.
*/
int
-plpgsql_peek(void)
+plpgsql_peek(yyscan_t yyscanner)
{
int tok1;
TokenAuxData aux1;
- tok1 = internal_yylex(&aux1);
+ tok1 = internal_yylex(&aux1, yyscanner);
push_back_token(tok1, &aux1);
return tok1;
}
@@ -453,15 +452,15 @@ plpgsql_peek(void)
* be returned as IDENT. Reserved keywords are resolved as usual.
*/
void
-plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc, int *tok2_loc)
+plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc, int *tok2_loc, yyscan_t yyscanner)
{
int tok1,
tok2;
TokenAuxData aux1,
aux2;
- tok1 = internal_yylex(&aux1);
- tok2 = internal_yylex(&aux2);
+ tok1 = internal_yylex(&aux1, yyscanner);
+ tok2 = internal_yylex(&aux2, yyscanner);
*tok1_p = tok1;
if (tok1_loc)
@@ -513,7 +512,7 @@ plpgsql_scanner_errposition(int location)
* be misleading!
*/
void
-plpgsql_yyerror(const char *message)
+plpgsql_yyerror(yyscan_t yyscanner, const char *message)
{
char *yytext = core_yy.scanbuf + plpgsql_yylloc;
@@ -599,9 +598,11 @@ plpgsql_latest_lineno(void)
* Although it is not fed directly to flex, we need the original string
* to cite in error messages.
*/
-void
+yyscan_t
plpgsql_scanner_init(const char *str)
{
+ yyscan_t yyscanner;
+
/* Start up the core scanner */
yyscanner = scanner_init(str, &core_yy,
&ReservedPLKeywords, ReservedPLKeywordTokens);
@@ -621,17 +622,18 @@ plpgsql_scanner_init(const char *str)
num_pushbacks = 0;
location_lineno_init();
+
+ return yyscanner;
}
/*
* Called after parsing is done to clean up after plpgsql_scanner_init()
*/
void
-plpgsql_scanner_finish(void)
+plpgsql_scanner_finish(yyscan_t yyscanner)
{
/* release storage */
scanner_finish(yyscanner);
/* avoid leaving any dangling pointers */
- yyscanner = NULL;
scanorig = NULL;
}
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 50c3b28472b..dab4e16c72e 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -1315,26 +1315,26 @@ extern void plpgsql_dumptree(PLpgSQL_function *func);
/*
* Scanner functions in pl_scanner.c
*/
-extern int plpgsql_base_yylex(void);
-extern int plpgsql_yylex(void);
+typedef void *yyscan_t;
+extern int plpgsql_yylex(yyscan_t yyscanner);
extern int plpgsql_token_length(void);
extern void plpgsql_push_back_token(int token);
extern bool plpgsql_token_is_unreserved_keyword(int token);
extern void plpgsql_append_source_text(StringInfo buf,
int startlocation, int endlocation);
-extern int plpgsql_peek(void);
+extern int plpgsql_peek(yyscan_t yyscanner);
extern void plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc,
- int *tok2_loc);
+ int *tok2_loc, yyscan_t yyscanner);
extern int plpgsql_scanner_errposition(int location);
-extern void plpgsql_yyerror(const char *message) pg_attribute_noreturn();
+extern void plpgsql_yyerror(yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
extern int plpgsql_location_to_lineno(int location);
extern int plpgsql_latest_lineno(void);
-extern void plpgsql_scanner_init(const char *str);
-extern void plpgsql_scanner_finish(void);
+extern yyscan_t plpgsql_scanner_init(const char *str);
+extern void plpgsql_scanner_finish(yyscan_t yyscanner);
/*
* Externs in gram.y
*/
-extern int plpgsql_yyparse(void);
+extern int plpgsql_yyparse(yyscan_t yyscanner);
#endif /* PLPGSQL_H */
--
2.47.1
I noticed that lapwing is bleating about
ccache gcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -O2 -fPIC -fvisibility=hidden -I. -I. -I../../src/include -DENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS -D_GNU_SOURCE -I/usr/include/libxml2 -I/usr/include/et -c -o cubescan.o cubescan.c
cubescan.c:1689:5: warning: no previous prototype for 'cube_yyget_column' [-Wmissing-prototypes]
cubescan.c:1765:6: warning: no previous prototype for 'cube_yyset_column' [-Wmissing-prototypes]
and likewise in segscan.c. lapwing is using flex 2.5.35, so probably
this is the same bug worked around in parser/scan.l:
/*
* Work around a bug in flex 2.5.35: it emits a couple of functions that
* it forgets to emit declarations for. Since we use -Wmissing-prototypes,
* this would cause warnings. Providing our own declarations should be
* harmless even when the bug gets fixed.
*/
extern int core_yyget_column(yyscan_t yyscanner);
extern void core_yyset_column(int column_no, yyscan_t yyscanner);
regards, tom lane
On 20.12.24 02:07, Tom Lane wrote:
I noticed that lapwing is bleating about
ccache gcc -std=gnu99 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -O2 -fPIC -fvisibility=hidden -I. -I. -I../../src/include -DENFORCE_REGRESSION_TEST_NAME_RESTRICTIONS -D_GNU_SOURCE -I/usr/include/libxml2 -I/usr/include/et -c -o cubescan.o cubescan.c
cubescan.c:1689:5: warning: no previous prototype for 'cube_yyget_column' [-Wmissing-prototypes]
cubescan.c:1765:6: warning: no previous prototype for 'cube_yyset_column' [-Wmissing-prototypes]and likewise in segscan.c. lapwing is using flex 2.5.35, so probably
this is the same bug worked around in parser/scan.l:
Ok, we can fix that, but maybe this is also a good moment to think about
whether that is useful. I could not reproduce the issue with flex
2.5.39. I could find no download of flex 2.5.35. The github site only
offers back to 2.5.39, the sourceforce site back to 2.5.36. lapwing
says it's Debian 7.0, which went out of support in 2016 and out of
super-duper-extended support in 2020. It also doesn't have a supported
OpenSSL version anymore, and IIRC, it has a weird old compiler that
occasionally gives bogus warnings. I think it's time to stop supporting
this.
Peter Eisentraut <peter@eisentraut.org> writes:
On 20.12.24 02:07, Tom Lane wrote:
I noticed that lapwing is bleating about
cubescan.c:1689:5: warning: no previous prototype for 'cube_yyget_column' [-Wmissing-prototypes]
cubescan.c:1765:6: warning: no previous prototype for 'cube_yyset_column' [-Wmissing-prototypes]
and likewise in segscan.c. lapwing is using flex 2.5.35, so probably
this is the same bug worked around in parser/scan.l:
Ok, we can fix that, but maybe this is also a good moment to think about
whether that is useful. I could not reproduce the issue with flex
2.5.39. I could find no download of flex 2.5.35. The github site only
offers back to 2.5.39, the sourceforce site back to 2.5.36. lapwing
says it's Debian 7.0, which went out of support in 2016 and out of
super-duper-extended support in 2020. It also doesn't have a supported
OpenSSL version anymore, and IIRC, it has a weird old compiler that
occasionally gives bogus warnings. I think it's time to stop supporting
this.
OK, that's fair. I do see lapwing called out a lot in the commit log,
though it's not clear how much of that is about 32-bitness and how
much about old tools. It's surely still valuable to have i386
machines in the buildfarm, but I agree that supporting unobtainable
tool versions is a bit much. Could we get that animal updated to
some newer OS version?
Presumably, we should also rip out the existing yyget_column and
yyset_column kluges in
src/backend/parser/scan.l: extern int core_yyget_column(yyscan_t yyscanner);
src/bin/psql/psqlscanslash.l: extern int slash_yyget_column(yyscan_t yyscanner);
src/bin/pgbench/exprscan.l: extern int expr_yyget_column(yyscan_t yyscanner);
src/fe_utils/psqlscan.l: extern int psql_yyget_column(yyscan_t yyscanner);
regards, tom lane
On Fri, Dec 20, 2024 at 11:23 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Could we get that animal updated to
some newer OS version?
There is already adder animal that is running debian sid on i386. The
only remaining interest in lapwing is to have older versions of
everything, so if that's useless I can just trash that vm.
Julien Rouhaud <rjuju123@gmail.com> writes:
On Fri, Dec 20, 2024 at 11:23 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Could we get that animal updated to
some newer OS version?
There is already adder animal that is running debian sid on i386. The
only remaining interest in lapwing is to have older versions of
everything, so if that's useless I can just trash that vm.
Hmm, sid is the opposite extreme no? Maybe switching lapwing to
whatever is currently the oldest supported Debian release would
be a good answer.
regards, tom lane
On 20.12.24 16:35, Tom Lane wrote:
Julien Rouhaud <rjuju123@gmail.com> writes:
On Fri, Dec 20, 2024 at 11:23 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Could we get that animal updated to
some newer OS version?There is already adder animal that is running debian sid on i386. The
only remaining interest in lapwing is to have older versions of
everything, so if that's useless I can just trash that vm.Hmm, sid is the opposite extreme no? Maybe switching lapwing to
whatever is currently the oldest supported Debian release would
be a good answer.
Yeah, Debian stable or oldstable on i386 could certainly be useful.
On 12/19/24 9:57 PM, Peter Eisentraut wrote:
Here is an updated patch set on top of what has been committed so far,
with all the issues you pointed out addressed.
Other than the discussion of how old versions of flex we should support
I think this set of patches is ready to be committed. I looked at it
again and everything looks good.
Andreas
On 22.12.24 22:43, Andreas Karlsson wrote:
On 12/19/24 9:57 PM, Peter Eisentraut wrote:
Here is an updated patch set on top of what has been committed so far,
with all the issues you pointed out addressed.Other than the discussion of how old versions of flex we should support
I think this set of patches is ready to be committed. I looked at it
again and everything looks good.
I have committed these except the plpgsql one, which was still work in
progress. But I have progressed on this now and also converted the
parser and put the local state into yyextra. This gets rid of all
internal global state now. The patches for this are attached. It's a
lot of churn, but otherwise pretty standard stuff.
Along the way I noticed that the flex documentation now recommends a
different way to set the yyextra type. So I have changed the ones we
already have to that newer style. I inspected the generated C code and
there wasn't any significant difference, so I'm not sure, but I figure
if we're making changes in this area we might as well use the modern style.
Attachments:
v3-0001-flex-code-modernization-Replace-YY_EXTRA_TYPE-def.patchtext/plain; charset=UTF-8; name=v3-0001-flex-code-modernization-Replace-YY_EXTRA_TYPE-def.patchDownload
From 8987797444ec2fc6c8f920b3e46d6f12d2cbf864 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Thu, 26 Dec 2024 11:41:21 +0100
Subject: [PATCH v3 1/4] flex code modernization: Replace YY_EXTRA_TYPE define
with flex option
Replace #define YY_EXTRA_TYPE with %option extra-type. The latter is
the way recommended by the flex manual (available since flex 2.5.34).
---
src/backend/parser/scan.l | 7 +------
src/backend/replication/repl_scanner.l | 2 +-
src/backend/replication/syncrep_scanner.l | 2 +-
src/backend/utils/adt/jsonpath_scan.l | 2 +-
src/bin/psql/psqlscanslash.l | 12 ++++++------
src/fe_utils/psqlscan.l | 12 ++++++------
6 files changed, 16 insertions(+), 21 deletions(-)
diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l
index a152cff4117..7808e1c1484 100644
--- a/src/backend/parser/scan.l
+++ b/src/backend/parser/scan.l
@@ -89,12 +89,6 @@ const uint16 ScanKeywordTokens[] = {
*/
#define YYSTYPE core_YYSTYPE
-/*
- * Set the type of yyextra. All state variables used by the scanner should
- * be in yyextra, *not* statically allocated.
- */
-#define YY_EXTRA_TYPE core_yy_extra_type *
-
/*
* Each call to yylex must set yylloc to the location of the found token
* (expressed as a byte offset from the start of the input text).
@@ -161,6 +155,7 @@ extern void core_yyset_column(int column_no, yyscan_t yyscanner);
%option noyyfree
%option warn
%option prefix="core_yy"
+%option extra-type="core_yy_extra_type *"
/*
* OK, here is a short description of lex/flex rules behavior.
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index 14684c6f61e..9fa2f191abb 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -46,7 +46,6 @@ struct replication_yy_extra_type
/* Work area for collecting literals */
StringInfoData litbuf;
};
-#define YY_EXTRA_TYPE struct replication_yy_extra_type *
static void startlit(yyscan_t yyscanner);
static char *litbufdup(yyscan_t yyscanner);
@@ -70,6 +69,7 @@ static void addlitchar(unsigned char ychar, yyscan_t yyscanner);
%option noyyfree
%option warn
%option prefix="replication_yy"
+%option extra-type="struct replication_yy_extra_type *"
/*
* Exclusive states:
diff --git a/src/backend/replication/syncrep_scanner.l b/src/backend/replication/syncrep_scanner.l
index fe4a41b3f20..170f8d5665f 100644
--- a/src/backend/replication/syncrep_scanner.l
+++ b/src/backend/replication/syncrep_scanner.l
@@ -41,7 +41,6 @@ struct syncrep_yy_extra_type
{
StringInfoData xdbuf;
};
-#define YY_EXTRA_TYPE struct syncrep_yy_extra_type *
/* LCOV_EXCL_START */
@@ -60,6 +59,7 @@ struct syncrep_yy_extra_type
%option noyyfree
%option warn
%option prefix="syncrep_yy"
+%option extra-type="struct syncrep_yy_extra_type *"
/*
* <xd> delimited identifiers (double-quoted identifiers)
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index 268c139f01b..3bd7e594117 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -34,7 +34,6 @@ struct jsonpath_yy_extra_type
{
JsonPathString scanstring;
};
-#define YY_EXTRA_TYPE struct jsonpath_yy_extra_type *
static void addstring(bool init, char *s, int l, yyscan_t yyscanner);
static void addchar(bool init, char c, yyscan_t yyscanner);
@@ -64,6 +63,7 @@ fprintf_to_ereport(const char *fmt, const char *msg)
%option noyywrap
%option warn
%option prefix="jsonpath_yy"
+%option extra-type="struct jsonpath_yy_extra_type *"
%option reentrant
%option bison-bridge
%option noyyalloc
diff --git a/src/bin/psql/psqlscanslash.l b/src/bin/psql/psqlscanslash.l
index cfce9038016..0cf12805788 100644
--- a/src/bin/psql/psqlscanslash.l
+++ b/src/bin/psql/psqlscanslash.l
@@ -38,12 +38,6 @@
*/
typedef int YYSTYPE;
-/*
- * Set the type of yyextra; we use it as a pointer back to the containing
- * PsqlScanState.
- */
-#define YY_EXTRA_TYPE PsqlScanState
-
/*
* These variables do not need to be saved across calls. Yeah, it's a bit
* of a hack, but putting them into PsqlScanStateData would be klugy too.
@@ -88,6 +82,12 @@ extern void slash_yyset_column(int column_no, yyscan_t yyscanner);
%option warn
%option prefix="slash_yy"
+/*
+ * Set the type of yyextra; we use it as a pointer back to the containing
+ * PsqlScanState.
+ */
+%option extra-type="PsqlScanState"
+
/*
* OK, here is a short description of lex/flex rules behavior.
* The longest pattern which matches an input string is always chosen.
diff --git a/src/fe_utils/psqlscan.l b/src/fe_utils/psqlscan.l
index 0e40be994a8..b1a60a99a94 100644
--- a/src/fe_utils/psqlscan.l
+++ b/src/fe_utils/psqlscan.l
@@ -52,12 +52,6 @@
*/
typedef int YYSTYPE;
-/*
- * Set the type of yyextra; we use it as a pointer back to the containing
- * PsqlScanState.
- */
-#define YY_EXTRA_TYPE PsqlScanState
-
/* Return values from yylex() */
#define LEXRES_EOL 0 /* end of input */
@@ -89,6 +83,12 @@ extern void psql_yyset_column(int column_no, yyscan_t yyscanner);
%option warn
%option prefix="psql_yy"
+/*
+ * Set the type of yyextra; we use it as a pointer back to the containing
+ * PsqlScanState.
+ */
+%option extra-type="PsqlScanState"
+
/*
* All of the following definitions and rules should exactly match
* src/backend/parser/scan.l so far as the flex patterns are concerned.
base-commit: 3f2d72b4934945da76f6bc60dfe3fc5ca42e7526
--
2.47.1
v3-0002-plpgsql-reentrant-scanner.patchtext/plain; charset=UTF-8; name=v3-0002-plpgsql-reentrant-scanner.patchDownload
From 4020d8cae2601f5c4f4a0e450df2273f7efa8599 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Sat, 7 Dec 2024 00:09:08 +0100
Subject: [PATCH v3 2/4] plpgsql: reentrant scanner
---
src/pl/plpgsql/src/nls.mk | 2 +-
src/pl/plpgsql/src/pl_comp.c | 14 +-
src/pl/plpgsql/src/pl_gram.y | 420 +++++++++++++++++---------------
src/pl/plpgsql/src/pl_scanner.c | 38 +--
src/pl/plpgsql/src/plpgsql.h | 19 +-
5 files changed, 258 insertions(+), 235 deletions(-)
diff --git a/src/pl/plpgsql/src/nls.mk b/src/pl/plpgsql/src/nls.mk
index e1d3bde6c37..ec7c7035add 100644
--- a/src/pl/plpgsql/src/nls.mk
+++ b/src/pl/plpgsql/src/nls.mk
@@ -6,5 +6,5 @@ GETTEXT_FILES = pl_comp.c \
pl_funcs.c \
pl_handler.c \
pl_scanner.c
-GETTEXT_TRIGGERS = $(BACKEND_COMMON_GETTEXT_TRIGGERS) yyerror plpgsql_yyerror
+GETTEXT_TRIGGERS = $(BACKEND_COMMON_GETTEXT_TRIGGERS) yyerror:2 plpgsql_yyerror:2
GETTEXT_FLAGS = $(BACKEND_COMMON_GETTEXT_FLAGS)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index d8c05ca95d3..57685c4e948 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -269,6 +269,7 @@ do_compile(FunctionCallInfo fcinfo,
Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
bool is_dml_trigger = CALLED_AS_TRIGGER(fcinfo);
bool is_event_trigger = CALLED_AS_EVENT_TRIGGER(fcinfo);
+ yyscan_t scanner;
Datum prosrcdatum;
char *proc_source;
HeapTuple typeTup;
@@ -296,7 +297,7 @@ do_compile(FunctionCallInfo fcinfo,
*/
prosrcdatum = SysCacheGetAttrNotNull(PROCOID, procTup, Anum_pg_proc_prosrc);
proc_source = TextDatumGetCString(prosrcdatum);
- plpgsql_scanner_init(proc_source);
+ scanner = plpgsql_scanner_init(proc_source);
plpgsql_error_funcname = pstrdup(NameStr(procStruct->proname));
@@ -779,12 +780,12 @@ do_compile(FunctionCallInfo fcinfo,
/*
* Now parse the function's text
*/
- parse_rc = plpgsql_yyparse();
+ parse_rc = plpgsql_yyparse(scanner);
if (parse_rc != 0)
elog(ERROR, "plpgsql parser returned %d", parse_rc);
function->action = plpgsql_parse_result;
- plpgsql_scanner_finish();
+ plpgsql_scanner_finish(scanner);
pfree(proc_source);
/*
@@ -841,6 +842,7 @@ do_compile(FunctionCallInfo fcinfo,
PLpgSQL_function *
plpgsql_compile_inline(char *proc_source)
{
+ yyscan_t scanner;
char *func_name = "inline_code_block";
PLpgSQL_function *function;
ErrorContextCallback plerrcontext;
@@ -853,7 +855,7 @@ plpgsql_compile_inline(char *proc_source)
* cannot be invoked recursively, so there's no need to save and restore
* the static variables used here.
*/
- plpgsql_scanner_init(proc_source);
+ scanner = plpgsql_scanner_init(proc_source);
plpgsql_error_funcname = func_name;
@@ -935,12 +937,12 @@ plpgsql_compile_inline(char *proc_source)
/*
* Now parse the function's text
*/
- parse_rc = plpgsql_yyparse();
+ parse_rc = plpgsql_yyparse(scanner);
if (parse_rc != 0)
elog(ERROR, "plpgsql parser returned %d", parse_rc);
function->action = plpgsql_parse_result;
- plpgsql_scanner_finish();
+ plpgsql_scanner_finish(scanner);
/*
* If it returns VOID (always true at the moment), we allow control to
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 11714430c13..e8f81d287a1 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -63,7 +63,7 @@ static bool tok_is_keyword(int token, union YYSTYPE *lval,
int kw_token, const char *kw_str);
static void word_is_not_variable(PLword *word, int location);
static void cword_is_not_variable(PLcword *cword, int location);
-static void current_token_is_not_variable(int tok);
+static void current_token_is_not_variable(int tok, yyscan_t yyscanner);
static PLpgSQL_expr *read_sql_construct(int until,
int until2,
int until3,
@@ -72,31 +72,33 @@ static PLpgSQL_expr *read_sql_construct(int until,
bool isexpression,
bool valid_sql,
int *startloc,
- int *endtoken);
+ int *endtoken,
+ yyscan_t yyscanner);
static PLpgSQL_expr *read_sql_expression(int until,
- const char *expected);
+ const char *expected, yyscan_t yyscanner);
static PLpgSQL_expr *read_sql_expression2(int until, int until2,
const char *expected,
- int *endtoken);
-static PLpgSQL_expr *read_sql_stmt(void);
-static PLpgSQL_type *read_datatype(int tok);
+ int *endtoken, yyscan_t yyscanner);
+static PLpgSQL_expr *read_sql_stmt(yyscan_t yyscanner);
+static PLpgSQL_type *read_datatype(int tok, yyscan_t yyscanner);
static PLpgSQL_stmt *make_execsql_stmt(int firsttoken, int location,
- PLword *word);
-static PLpgSQL_stmt_fetch *read_fetch_direction(void);
+ PLword *word, yyscan_t yyscanner);
+static PLpgSQL_stmt_fetch *read_fetch_direction(yyscan_t yyscanner);
static void complete_direction(PLpgSQL_stmt_fetch *fetch,
- bool *check_FROM);
-static PLpgSQL_stmt *make_return_stmt(int location);
-static PLpgSQL_stmt *make_return_next_stmt(int location);
-static PLpgSQL_stmt *make_return_query_stmt(int location);
+ bool *check_FROM, yyscan_t yyscanner);
+static PLpgSQL_stmt *make_return_stmt(int location, yyscan_t yyscanner);
+static PLpgSQL_stmt *make_return_next_stmt(int location, yyscan_t yyscanner);
+static PLpgSQL_stmt *make_return_query_stmt(int location, yyscan_t yyscanner);
static PLpgSQL_stmt *make_case(int location, PLpgSQL_expr *t_expr,
List *case_when_list, List *else_stmts);
static char *NameOfDatum(PLwdatum *wdatum);
static void check_assignable(PLpgSQL_datum *datum, int location);
static void read_into_target(PLpgSQL_variable **target,
- bool *strict);
+ bool *strict, yyscan_t yyscanner);
static PLpgSQL_row *read_into_scalar_list(char *initial_name,
PLpgSQL_datum *initial_datum,
- int initial_location);
+ int initial_location,
+ yyscan_t yyscanner);
static PLpgSQL_row *make_scalar_list1(char *initial_name,
PLpgSQL_datum *initial_datum,
int lineno, int location);
@@ -108,12 +110,14 @@ static void check_labels(const char *start_label,
const char *end_label,
int end_location);
static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor,
- int until);
-static List *read_raise_options(void);
+ int until, yyscan_t yyscanner);
+static List *read_raise_options(yyscan_t yyscanner);
static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%}
+%parse-param {yyscan_t yyscanner}
+%lex-param {yyscan_t yyscanner}
%expect 0
%name-prefix="plpgsql_yy"
%locations
@@ -577,7 +581,7 @@ opt_scrollable :
decl_cursor_query :
{
- $$ = read_sql_stmt();
+ $$ = read_sql_stmt(yyscanner);
}
;
@@ -706,7 +710,7 @@ decl_varname : T_WORD
if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
$1.ident, NULL, NULL,
NULL) != NULL)
- yyerror("duplicate declaration");
+ yyerror(yyscanner, "duplicate declaration");
if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
@@ -734,7 +738,7 @@ decl_varname : T_WORD
if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
$1, NULL, NULL,
NULL) != NULL)
- yyerror("duplicate declaration");
+ yyerror(yyscanner, "duplicate declaration");
if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
@@ -766,7 +770,7 @@ decl_datatype :
* consume it, and then we must tell bison to forget
* it.
*/
- $$ = read_datatype(yychar);
+ $$ = read_datatype(yychar, yyscanner);
yyclearin;
}
;
@@ -799,7 +803,7 @@ decl_defval : ';'
{ $$ = NULL; }
| decl_defkey
{
- $$ = read_sql_expression(';', ";");
+ $$ = read_sql_expression(';', ";", yyscanner);
}
;
@@ -900,7 +904,8 @@ stmt_perform : K_PERFORM
new->expr = read_sql_construct(';', 0, 0, ";",
RAW_PARSE_DEFAULT,
false, false,
- &startloc, NULL);
+ &startloc, NULL,
+ yyscanner);
/* overwrite "perform" ... */
memcpy(new->expr->query, " SELECT", 7);
/* left-justify to get rid of the leading space */
@@ -923,7 +928,7 @@ stmt_call : K_CALL
new->lineno = plpgsql_location_to_lineno(@1);
new->stmtid = ++plpgsql_curr_compile->nstatements;
plpgsql_push_back_token(K_CALL);
- new->expr = read_sql_stmt();
+ new->expr = read_sql_stmt(yyscanner);
new->is_call = true;
/* Remember we may need a procedure resource owner */
@@ -942,7 +947,7 @@ stmt_call : K_CALL
new->lineno = plpgsql_location_to_lineno(@1);
new->stmtid = ++plpgsql_curr_compile->nstatements;
plpgsql_push_back_token(K_DO);
- new->expr = read_sql_stmt();
+ new->expr = read_sql_stmt(yyscanner);
new->is_call = false;
/* Remember we may need a procedure resource owner */
@@ -986,7 +991,8 @@ stmt_assign : T_DATUM
new->expr = read_sql_construct(';', 0, 0, ";",
pmode,
false, true,
- NULL, NULL);
+ NULL, NULL,
+ yyscanner);
$$ = (PLpgSQL_stmt *) new;
}
@@ -1093,7 +1099,7 @@ getdiag_list_item : getdiag_target assign_operator getdiag_item
getdiag_item :
{
- int tok = yylex();
+ int tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_ROW_COUNT, "row_count"))
@@ -1135,7 +1141,7 @@ getdiag_item :
K_RETURNED_SQLSTATE, "returned_sqlstate"))
$$ = PLPGSQL_GETDIAG_RETURNED_SQLSTATE;
else
- yyerror("unrecognized GET DIAGNOSTICS item");
+ yyerror(yyscanner, "unrecognized GET DIAGNOSTICS item");
}
;
@@ -1148,7 +1154,7 @@ getdiag_target : T_DATUM
*/
if ($1.datum->dtype == PLPGSQL_DTYPE_ROW ||
$1.datum->dtype == PLPGSQL_DTYPE_REC ||
- plpgsql_peek() == '[')
+ plpgsql_peek(yyscanner) == '[')
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a scalar variable",
@@ -1222,12 +1228,12 @@ stmt_case : K_CASE opt_expr_until_when case_when_list opt_case_else K_END K_CAS
opt_expr_until_when :
{
PLpgSQL_expr *expr = NULL;
- int tok = yylex();
+ int tok = yylex(yyscanner);
if (tok != K_WHEN)
{
plpgsql_push_back_token(tok);
- expr = read_sql_expression(K_WHEN, "WHEN");
+ expr = read_sql_expression(K_WHEN, "WHEN", yyscanner);
}
plpgsql_push_back_token(K_WHEN);
$$ = expr;
@@ -1347,7 +1353,7 @@ stmt_for : opt_loop_label K_FOR for_control loop_body
for_control : for_variable K_IN
{
- int tok = yylex();
+ int tok = yylex(yyscanner);
int tokloc = yylloc;
if (tok == K_EXECUTE)
@@ -1359,7 +1365,7 @@ for_control : for_variable K_IN
expr = read_sql_expression2(K_LOOP, K_USING,
"LOOP or USING",
- &term);
+ &term, yyscanner);
new = palloc0(sizeof(PLpgSQL_stmt_dynfors));
new->cmd_type = PLPGSQL_STMT_DYNFORS;
@@ -1392,7 +1398,7 @@ for_control : for_variable K_IN
{
expr = read_sql_expression2(',', K_LOOP,
", or LOOP",
- &term);
+ &term, yyscanner);
new->params = lappend(new->params, expr);
} while (term == ',');
}
@@ -1427,8 +1433,7 @@ for_control : for_variable K_IN
parser_errposition(tokloc)));
/* collect cursor's parameters if any */
- new->argquery = read_cursor_args(cursor,
- K_LOOP);
+ new->argquery = read_cursor_args(cursor, K_LOOP, yyscanner);
/* create loop's private RECORD variable */
new->var = (PLpgSQL_variable *)
@@ -1479,7 +1484,8 @@ for_control : for_variable K_IN
true,
false,
&expr1loc,
- &tok);
+ &tok,
+ yyscanner);
if (tok == DOT_DOT)
{
@@ -1500,12 +1506,12 @@ for_control : for_variable K_IN
/* Read and check the second one */
expr2 = read_sql_expression2(K_LOOP, K_BY,
"LOOP",
- &tok);
+ &tok, yyscanner);
/* Get the BY clause if any */
if (tok == K_BY)
expr_by = read_sql_expression(K_LOOP,
- "LOOP");
+ "LOOP", yyscanner);
else
expr_by = NULL;
@@ -1620,13 +1626,14 @@ for_variable : T_DATUM
$$.scalar = $1.datum;
$$.row = NULL;
/* check for comma-separated list */
- tok = yylex();
+ tok = yylex(yyscanner);
plpgsql_push_back_token(tok);
if (tok == ',')
$$.row = (PLpgSQL_datum *)
read_into_scalar_list($$.name,
$$.scalar,
- @1);
+ @1,
+ yyscanner);
}
}
| T_WORD
@@ -1638,7 +1645,7 @@ for_variable : T_DATUM
$$.scalar = NULL;
$$.row = NULL;
/* check for comma-separated list */
- tok = yylex();
+ tok = yylex(yyscanner);
plpgsql_push_back_token(tok);
if (tok == ',')
word_is_not_variable(&($1), @1);
@@ -1765,24 +1772,24 @@ stmt_return : K_RETURN
{
int tok;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == 0)
- yyerror("unexpected end of function definition");
+ yyerror(yyscanner, "unexpected end of function definition");
if (tok_is_keyword(tok, &yylval,
K_NEXT, "next"))
{
- $$ = make_return_next_stmt(@1);
+ $$ = make_return_next_stmt(@1, yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_QUERY, "query"))
{
- $$ = make_return_query_stmt(@1);
+ $$ = make_return_query_stmt(@1, yyscanner);
}
else
{
plpgsql_push_back_token(tok);
- $$ = make_return_stmt(@1);
+ $$ = make_return_stmt(@1, yyscanner);
}
}
;
@@ -1803,9 +1810,9 @@ stmt_raise : K_RAISE
new->params = NIL;
new->options = NIL;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == 0)
- yyerror("unexpected end of function definition");
+ yyerror(yyscanner, "unexpected end of function definition");
/*
* We could have just RAISE, meaning to re-throw
@@ -1820,40 +1827,40 @@ stmt_raise : K_RAISE
K_EXCEPTION, "exception"))
{
new->elog_level = ERROR;
- tok = yylex();
+ tok = yylex(yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_WARNING, "warning"))
{
new->elog_level = WARNING;
- tok = yylex();
+ tok = yylex(yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_NOTICE, "notice"))
{
new->elog_level = NOTICE;
- tok = yylex();
+ tok = yylex(yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_INFO, "info"))
{
new->elog_level = INFO;
- tok = yylex();
+ tok = yylex(yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_LOG, "log"))
{
new->elog_level = LOG;
- tok = yylex();
+ tok = yylex(yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_DEBUG, "debug"))
{
new->elog_level = DEBUG1;
- tok = yylex();
+ tok = yylex(yyscanner);
}
if (tok == 0)
- yyerror("unexpected end of function definition");
+ yyerror(yyscanner, "unexpected end of function definition");
/*
* Next we can have a condition name, or
@@ -1871,9 +1878,9 @@ stmt_raise : K_RAISE
* begins the list of parameter expressions,
* or USING to begin the options list.
*/
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok != ',' && tok != ';' && tok != K_USING)
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
while (tok == ',')
{
@@ -1883,7 +1890,8 @@ stmt_raise : K_RAISE
", or ; or USING",
RAW_PARSE_PLPGSQL_EXPR,
true, true,
- NULL, &tok);
+ NULL, &tok,
+ yyscanner);
new->params = lappend(new->params, expr);
}
}
@@ -1896,14 +1904,14 @@ stmt_raise : K_RAISE
/* next token should be a string literal */
char *sqlstatestr;
- if (yylex() != SCONST)
- yyerror("syntax error");
+ if (yylex(yyscanner) != SCONST)
+ yyerror(yyscanner, "syntax error");
sqlstatestr = yylval.str;
if (strlen(sqlstatestr) != 5)
- yyerror("invalid SQLSTATE code");
+ yyerror(yyscanner, "invalid SQLSTATE code");
if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
- yyerror("invalid SQLSTATE code");
+ yyerror(yyscanner, "invalid SQLSTATE code");
new->condname = sqlstatestr;
}
else
@@ -1913,17 +1921,17 @@ stmt_raise : K_RAISE
else if (plpgsql_token_is_unreserved_keyword(tok))
new->condname = pstrdup(yylval.keyword);
else
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
plpgsql_recognize_err_condition(new->condname,
false);
}
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok != ';' && tok != K_USING)
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
}
if (tok == K_USING)
- new->options = read_raise_options();
+ new->options = read_raise_options(yyscanner);
}
check_raise_parameters(new);
@@ -1945,10 +1953,10 @@ stmt_assert : K_ASSERT
new->cond = read_sql_expression2(',', ';',
", or ;",
- &tok);
+ &tok, yyscanner);
if (tok == ',')
- new->message = read_sql_expression(';', ";");
+ new->message = read_sql_expression(';', ";", yyscanner);
else
new->message = NULL;
@@ -1976,37 +1984,37 @@ loop_body : proc_sect K_END K_LOOP opt_label ';'
*/
stmt_execsql : K_IMPORT
{
- $$ = make_execsql_stmt(K_IMPORT, @1, NULL);
+ $$ = make_execsql_stmt(K_IMPORT, @1, NULL, yyscanner);
}
| K_INSERT
{
- $$ = make_execsql_stmt(K_INSERT, @1, NULL);
+ $$ = make_execsql_stmt(K_INSERT, @1, NULL, yyscanner);
}
| K_MERGE
{
- $$ = make_execsql_stmt(K_MERGE, @1, NULL);
+ $$ = make_execsql_stmt(K_MERGE, @1, NULL, yyscanner);
}
| T_WORD
{
int tok;
- tok = yylex();
+ tok = yylex(yyscanner);
plpgsql_push_back_token(tok);
if (tok == '=' || tok == COLON_EQUALS ||
tok == '[' || tok == '.')
word_is_not_variable(&($1), @1);
- $$ = make_execsql_stmt(T_WORD, @1, &($1));
+ $$ = make_execsql_stmt(T_WORD, @1, &($1), yyscanner);
}
| T_CWORD
{
int tok;
- tok = yylex();
+ tok = yylex(yyscanner);
plpgsql_push_back_token(tok);
if (tok == '=' || tok == COLON_EQUALS ||
tok == '[' || tok == '.')
cword_is_not_variable(&($1), @1);
- $$ = make_execsql_stmt(T_CWORD, @1, NULL);
+ $$ = make_execsql_stmt(T_CWORD, @1, NULL, yyscanner);
}
;
@@ -2020,7 +2028,8 @@ stmt_dynexecute : K_EXECUTE
"INTO or USING or ;",
RAW_PARSE_PLPGSQL_EXPR,
true, true,
- NULL, &endtoken);
+ NULL, &endtoken,
+ yyscanner);
new = palloc(sizeof(PLpgSQL_stmt_dynexecute));
new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
@@ -2044,29 +2053,30 @@ stmt_dynexecute : K_EXECUTE
if (endtoken == K_INTO)
{
if (new->into) /* multiple INTO */
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
new->into = true;
- read_into_target(&new->target, &new->strict);
- endtoken = yylex();
+ read_into_target(&new->target, &new->strict, yyscanner);
+ endtoken = yylex(yyscanner);
}
else if (endtoken == K_USING)
{
if (new->params) /* multiple USING */
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
do
{
expr = read_sql_construct(',', ';', K_INTO,
", or ; or INTO",
RAW_PARSE_PLPGSQL_EXPR,
true, true,
- NULL, &endtoken);
+ NULL, &endtoken,
+ yyscanner);
new->params = lappend(new->params, expr);
} while (endtoken == ',');
}
else if (endtoken == ';')
break;
else
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
}
$$ = (PLpgSQL_stmt *) new;
@@ -2089,29 +2099,29 @@ stmt_open : K_OPEN cursor_variable
if ($2->cursor_explicit_expr == NULL)
{
/* be nice if we could use opt_scrollable here */
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_NO, "no"))
{
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_SCROLL, "scroll"))
{
new->cursor_options |= CURSOR_OPT_NO_SCROLL;
- tok = yylex();
+ tok = yylex(yyscanner);
}
}
else if (tok_is_keyword(tok, &yylval,
K_SCROLL, "scroll"))
{
new->cursor_options |= CURSOR_OPT_SCROLL;
- tok = yylex();
+ tok = yylex(yyscanner);
}
if (tok != K_FOR)
- yyerror("syntax error, expected \"FOR\"");
+ yyerror(yyscanner, "syntax error, expected \"FOR\"");
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == K_EXECUTE)
{
int endtoken;
@@ -2119,7 +2129,7 @@ stmt_open : K_OPEN cursor_variable
new->dynquery =
read_sql_expression2(K_USING, ';',
"USING or ;",
- &endtoken);
+ &endtoken, yyscanner);
/* If we found "USING", collect argument(s) */
if (endtoken == K_USING)
@@ -2130,7 +2140,7 @@ stmt_open : K_OPEN cursor_variable
{
expr = read_sql_expression2(',', ';',
", or ;",
- &endtoken);
+ &endtoken, yyscanner);
new->params = lappend(new->params,
expr);
} while (endtoken == ',');
@@ -2139,13 +2149,13 @@ stmt_open : K_OPEN cursor_variable
else
{
plpgsql_push_back_token(tok);
- new->query = read_sql_stmt();
+ new->query = read_sql_stmt(yyscanner);
}
}
else
{
/* predefined cursor query, so read args */
- new->argquery = read_cursor_args($2, ';');
+ new->argquery = read_cursor_args($2, ';', yyscanner);
}
$$ = (PLpgSQL_stmt *) new;
@@ -2158,10 +2168,10 @@ stmt_fetch : K_FETCH opt_fetch_direction cursor_variable K_INTO
PLpgSQL_variable *target;
/* We have already parsed everything through the INTO keyword */
- read_into_target(&target, NULL);
+ read_into_target(&target, NULL, yyscanner);
- if (yylex() != ';')
- yyerror("syntax error");
+ if (yylex(yyscanner) != ';')
+ yyerror(yyscanner, "syntax error");
/*
* We don't allow multiple rows in PL/pgSQL's FETCH
@@ -2196,7 +2206,7 @@ stmt_move : K_MOVE opt_fetch_direction cursor_variable ';'
opt_fetch_direction :
{
- $$ = read_fetch_direction();
+ $$ = read_fetch_direction(yyscanner);
}
;
@@ -2264,7 +2274,7 @@ cursor_variable : T_DATUM
* just throw an error if next token is '['.
*/
if ($1.datum->dtype != PLPGSQL_DTYPE_VAR ||
- plpgsql_peek() == '[')
+ plpgsql_peek(yyscanner) == '[')
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cursor variable must be a simple variable"),
@@ -2384,14 +2394,14 @@ proc_condition : any_identifier
char *sqlstatestr;
/* next token should be a string literal */
- if (yylex() != SCONST)
- yyerror("syntax error");
+ if (yylex(yyscanner) != SCONST)
+ yyerror(yyscanner, "syntax error");
sqlstatestr = yylval.str;
if (strlen(sqlstatestr) != 5)
- yyerror("invalid SQLSTATE code");
+ yyerror(yyscanner, "invalid SQLSTATE code");
if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
- yyerror("invalid SQLSTATE code");
+ yyerror(yyscanner, "invalid SQLSTATE code");
new = palloc(sizeof(PLpgSQL_condition));
new->sqlerrstate =
@@ -2409,15 +2419,15 @@ proc_condition : any_identifier
;
expr_until_semi :
- { $$ = read_sql_expression(';', ";"); }
+ { $$ = read_sql_expression(';', ";", yyscanner); }
;
expr_until_then :
- { $$ = read_sql_expression(K_THEN, "THEN"); }
+ { $$ = read_sql_expression(K_THEN, "THEN", yyscanner); }
;
expr_until_loop :
- { $$ = read_sql_expression(K_LOOP, "LOOP"); }
+ { $$ = read_sql_expression(K_LOOP, "LOOP", yyscanner); }
;
opt_block_label :
@@ -2475,7 +2485,7 @@ any_identifier : T_WORD
| T_DATUM
{
if ($1.ident == NULL) /* composite name not OK */
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
$$ = $1.ident;
}
;
@@ -2627,42 +2637,45 @@ cword_is_not_variable(PLcword *cword, int location)
* look at yylval and yylloc.
*/
static void
-current_token_is_not_variable(int tok)
+current_token_is_not_variable(int tok, yyscan_t yyscanner)
{
if (tok == T_WORD)
word_is_not_variable(&(yylval.word), yylloc);
else if (tok == T_CWORD)
cword_is_not_variable(&(yylval.cword), yylloc);
else
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
}
/* Convenience routine to read an expression with one possible terminator */
static PLpgSQL_expr *
-read_sql_expression(int until, const char *expected)
+read_sql_expression(int until, const char *expected, yyscan_t yyscanner)
{
return read_sql_construct(until, 0, 0, expected,
RAW_PARSE_PLPGSQL_EXPR,
- true, true, NULL, NULL);
+ true, true, NULL, NULL,
+ yyscanner);
}
/* Convenience routine to read an expression with two possible terminators */
static PLpgSQL_expr *
read_sql_expression2(int until, int until2, const char *expected,
- int *endtoken)
+ int *endtoken, yyscan_t yyscanner)
{
return read_sql_construct(until, until2, 0, expected,
RAW_PARSE_PLPGSQL_EXPR,
- true, true, NULL, endtoken);
+ true, true, NULL, endtoken,
+ yyscanner);
}
/* Convenience routine to read a SQL statement that must end with ';' */
static PLpgSQL_expr *
-read_sql_stmt(void)
+read_sql_stmt(yyscan_t yyscanner)
{
return read_sql_construct(';', 0, 0, ";",
RAW_PARSE_DEFAULT,
- false, true, NULL, NULL);
+ false, true, NULL, NULL,
+ yyscanner);
}
/*
@@ -2688,7 +2701,8 @@ read_sql_construct(int until,
bool isexpression,
bool valid_sql,
int *startloc,
- int *endtoken)
+ int *endtoken,
+ yyscan_t yyscanner)
{
int tok;
StringInfoData ds;
@@ -2706,7 +2720,7 @@ read_sql_construct(int until,
for (;;)
{
- tok = yylex();
+ tok = yylex(yyscanner);
if (startlocation < 0) /* remember loc of first token */
startlocation = yylloc;
if (tok == until && parenlevel == 0)
@@ -2721,7 +2735,7 @@ read_sql_construct(int until,
{
parenlevel--;
if (parenlevel < 0)
- yyerror("mismatched parentheses");
+ yyerror(yyscanner, "mismatched parentheses");
}
/*
@@ -2732,7 +2746,7 @@ read_sql_construct(int until,
if (tok == 0 || tok == ';')
{
if (parenlevel != 0)
- yyerror("mismatched parentheses");
+ yyerror(yyscanner, "mismatched parentheses");
if (isexpression)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -2761,9 +2775,9 @@ read_sql_construct(int until,
if (startlocation >= endlocation)
{
if (isexpression)
- yyerror("missing expression");
+ yyerror(yyscanner, "missing expression");
else
- yyerror("missing SQL statement");
+ yyerror(yyscanner, "missing SQL statement");
}
/*
@@ -2795,7 +2809,7 @@ read_sql_construct(int until,
* Returns a PLpgSQL_type struct.
*/
static PLpgSQL_type *
-read_datatype(int tok)
+read_datatype(int tok, yyscan_t yyscanner)
{
StringInfoData ds;
char *type_name;
@@ -2808,7 +2822,7 @@ read_datatype(int tok)
/* Often there will be a lookahead token, but if not, get one */
if (tok == YYEMPTY)
- tok = yylex();
+ tok = yylex(yyscanner);
/* The current token is the start of what we'll pass to parse_datatype */
startlocation = yylloc;
@@ -2821,10 +2835,10 @@ read_datatype(int tok)
{
char *dtname = yylval.word.ident;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == '%')
{
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_TYPE, "type"))
result = plpgsql_parse_wordtype(dtname);
@@ -2837,10 +2851,10 @@ read_datatype(int tok)
{
char *dtname = pstrdup(yylval.keyword);
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == '%')
{
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_TYPE, "type"))
result = plpgsql_parse_wordtype(dtname);
@@ -2853,10 +2867,10 @@ read_datatype(int tok)
{
List *dtnames = yylval.cword.idents;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == '%')
{
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_TYPE, "type"))
result = plpgsql_parse_cwordtype(dtnames);
@@ -2878,22 +2892,22 @@ read_datatype(int tok)
{
bool is_array = false;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok_is_keyword(tok, &yylval,
K_ARRAY, "array"))
{
is_array = true;
- tok = yylex();
+ tok = yylex(yyscanner);
}
while (tok == '[')
{
is_array = true;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == ICONST)
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok != ']')
- yyerror("syntax error, expected \"]\"");
- tok = yylex();
+ yyerror(yyscanner, "syntax error, expected \"]\"");
+ tok = yylex(yyscanner);
}
plpgsql_push_back_token(tok);
@@ -2914,9 +2928,9 @@ read_datatype(int tok)
if (tok == 0)
{
if (parenlevel != 0)
- yyerror("mismatched parentheses");
+ yyerror(yyscanner, "mismatched parentheses");
else
- yyerror("incomplete data type declaration");
+ yyerror(yyscanner, "incomplete data type declaration");
}
/* Possible followers for datatype in a declaration */
if (tok == K_COLLATE || tok == K_NOT ||
@@ -2930,7 +2944,7 @@ read_datatype(int tok)
else if (tok == ')')
parenlevel--;
- tok = yylex();
+ tok = yylex(yyscanner);
}
/* set up ds to contain complete typename text */
@@ -2939,7 +2953,7 @@ read_datatype(int tok)
type_name = ds.data;
if (type_name[0] == '\0')
- yyerror("missing data type declaration");
+ yyerror(yyscanner, "missing data type declaration");
result = parse_datatype(type_name, startlocation);
@@ -2956,7 +2970,7 @@ read_datatype(int tok)
* If firsttoken == T_WORD, pass its yylval value as "word", else pass NULL.
*/
static PLpgSQL_stmt *
-make_execsql_stmt(int firsttoken, int location, PLword *word)
+make_execsql_stmt(int firsttoken, int location, PLword *word, yyscan_t yyscanner)
{
StringInfoData ds;
IdentifierLookup save_IdentifierLookup;
@@ -3025,7 +3039,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word)
for (;;)
{
prev_tok = tok;
- tok = yylex();
+ tok = yylex(yyscanner);
if (have_into && into_end_loc < 0)
into_end_loc = yylloc; /* token after the INTO part */
/* Detect CREATE [OR REPLACE] {FUNCTION|PROCEDURE} */
@@ -3064,7 +3078,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word)
if (tok == ';' && paren_depth == 0 && begin_depth == 0)
break;
if (tok == 0)
- yyerror("unexpected end of function definition");
+ yyerror(yyscanner, "unexpected end of function definition");
if (tok == K_INTO)
{
if (prev_tok == K_INSERT)
@@ -3074,11 +3088,11 @@ make_execsql_stmt(int firsttoken, int location, PLword *word)
if (firsttoken == K_IMPORT)
continue; /* IMPORT ... INTO is not an INTO-target */
if (have_into)
- yyerror("INTO specified more than once");
+ yyerror(yyscanner, "INTO specified more than once");
have_into = true;
into_start_loc = yylloc;
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
- read_into_target(&target, &have_strict);
+ read_into_target(&target, &have_strict, yyscanner);
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_EXPR;
}
}
@@ -3131,7 +3145,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word)
* Read FETCH or MOVE direction clause (everything through FROM/IN).
*/
static PLpgSQL_stmt_fetch *
-read_fetch_direction(void)
+read_fetch_direction(yyscan_t yyscanner)
{
PLpgSQL_stmt_fetch *fetch;
int tok;
@@ -3150,9 +3164,9 @@ read_fetch_direction(void)
fetch->expr = NULL;
fetch->returns_multiple_rows = false;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == 0)
- yyerror("unexpected end of function definition");
+ yyerror(yyscanner, "unexpected end of function definition");
if (tok_is_keyword(tok, &yylval,
K_NEXT, "next"))
@@ -3181,7 +3195,7 @@ read_fetch_direction(void)
fetch->direction = FETCH_ABSOLUTE;
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"FROM or IN",
- NULL);
+ NULL, yyscanner);
check_FROM = false;
}
else if (tok_is_keyword(tok, &yylval,
@@ -3190,7 +3204,7 @@ read_fetch_direction(void)
fetch->direction = FETCH_RELATIVE;
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"FROM or IN",
- NULL);
+ NULL, yyscanner);
check_FROM = false;
}
else if (tok_is_keyword(tok, &yylval,
@@ -3202,13 +3216,13 @@ read_fetch_direction(void)
else if (tok_is_keyword(tok, &yylval,
K_FORWARD, "forward"))
{
- complete_direction(fetch, &check_FROM);
+ complete_direction(fetch, &check_FROM, yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_BACKWARD, "backward"))
{
fetch->direction = FETCH_BACKWARD;
- complete_direction(fetch, &check_FROM);
+ complete_direction(fetch, &check_FROM, yyscanner);
}
else if (tok == K_FROM || tok == K_IN)
{
@@ -3234,7 +3248,7 @@ read_fetch_direction(void)
plpgsql_push_back_token(tok);
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"FROM or IN",
- NULL);
+ NULL, yyscanner);
fetch->returns_multiple_rows = true;
check_FROM = false;
}
@@ -3242,9 +3256,9 @@ read_fetch_direction(void)
/* check FROM or IN keyword after direction's specification */
if (check_FROM)
{
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok != K_FROM && tok != K_IN)
- yyerror("expected FROM or IN");
+ yyerror(yyscanner, "expected FROM or IN");
}
return fetch;
@@ -3257,13 +3271,13 @@ read_fetch_direction(void)
* BACKWARD expr, BACKWARD ALL, BACKWARD
*/
static void
-complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM)
+complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM, yyscan_t yyscanner)
{
int tok;
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok == 0)
- yyerror("unexpected end of function definition");
+ yyerror(yyscanner, "unexpected end of function definition");
if (tok == K_FROM || tok == K_IN)
{
@@ -3282,14 +3296,14 @@ complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM)
plpgsql_push_back_token(tok);
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"FROM or IN",
- NULL);
+ NULL, yyscanner);
fetch->returns_multiple_rows = true;
*check_FROM = false;
}
static PLpgSQL_stmt *
-make_return_stmt(int location)
+make_return_stmt(int location, yyscan_t yyscanner)
{
PLpgSQL_stmt_return *new;
@@ -3302,7 +3316,7 @@ make_return_stmt(int location)
if (plpgsql_curr_compile->fn_retset)
{
- if (yylex() != ';')
+ if (yylex(yyscanner) != ';')
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("RETURN cannot have a parameter in function returning set"),
@@ -3311,7 +3325,7 @@ make_return_stmt(int location)
}
else if (plpgsql_curr_compile->fn_rettype == VOIDOID)
{
- if (yylex() != ';')
+ if (yylex(yyscanner) != ';')
{
if (plpgsql_curr_compile->fn_prokind == PROKIND_PROCEDURE)
ereport(ERROR,
@@ -3327,7 +3341,7 @@ make_return_stmt(int location)
}
else if (plpgsql_curr_compile->out_param_varno >= 0)
{
- if (yylex() != ';')
+ if (yylex(yyscanner) != ';')
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("RETURN cannot have a parameter in function with OUT parameters"),
@@ -3340,9 +3354,9 @@ make_return_stmt(int location)
* We want to special-case simple variable references for efficiency.
* So peek ahead to see if that's what we have.
*/
- int tok = yylex();
+ int tok = yylex(yyscanner);
- if (tok == T_DATUM && plpgsql_peek() == ';' &&
+ if (tok == T_DATUM && plpgsql_peek(yyscanner) == ';' &&
(yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_VAR ||
yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_PROMISE ||
yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
@@ -3350,7 +3364,7 @@ make_return_stmt(int location)
{
new->retvarno = yylval.wdatum.datum->dno;
/* eat the semicolon token that we only peeked at above */
- tok = yylex();
+ tok = yylex(yyscanner);
Assert(tok == ';');
}
else
@@ -3362,7 +3376,7 @@ make_return_stmt(int location)
* else is a compile-time error.
*/
plpgsql_push_back_token(tok);
- new->expr = read_sql_expression(';', ";");
+ new->expr = read_sql_expression(';', ";", yyscanner);
}
}
@@ -3371,7 +3385,7 @@ make_return_stmt(int location)
static PLpgSQL_stmt *
-make_return_next_stmt(int location)
+make_return_next_stmt(int location, yyscan_t yyscanner)
{
PLpgSQL_stmt_return_next *new;
@@ -3390,7 +3404,7 @@ make_return_next_stmt(int location)
if (plpgsql_curr_compile->out_param_varno >= 0)
{
- if (yylex() != ';')
+ if (yylex(yyscanner) != ';')
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("RETURN NEXT cannot have a parameter in function with OUT parameters"),
@@ -3403,9 +3417,9 @@ make_return_next_stmt(int location)
* We want to special-case simple variable references for efficiency.
* So peek ahead to see if that's what we have.
*/
- int tok = yylex();
+ int tok = yylex(yyscanner);
- if (tok == T_DATUM && plpgsql_peek() == ';' &&
+ if (tok == T_DATUM && plpgsql_peek(yyscanner) == ';' &&
(yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_VAR ||
yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_PROMISE ||
yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
@@ -3413,7 +3427,7 @@ make_return_next_stmt(int location)
{
new->retvarno = yylval.wdatum.datum->dno;
/* eat the semicolon token that we only peeked at above */
- tok = yylex();
+ tok = yylex(yyscanner);
Assert(tok == ';');
}
else
@@ -3425,7 +3439,7 @@ make_return_next_stmt(int location)
* else is a compile-time error.
*/
plpgsql_push_back_token(tok);
- new->expr = read_sql_expression(';', ";");
+ new->expr = read_sql_expression(';', ";", yyscanner);
}
}
@@ -3434,7 +3448,7 @@ make_return_next_stmt(int location)
static PLpgSQL_stmt *
-make_return_query_stmt(int location)
+make_return_query_stmt(int location, yyscan_t yyscanner)
{
PLpgSQL_stmt_return_query *new;
int tok;
@@ -3451,11 +3465,11 @@ make_return_query_stmt(int location)
new->stmtid = ++plpgsql_curr_compile->nstatements;
/* check for RETURN QUERY EXECUTE */
- if ((tok = yylex()) != K_EXECUTE)
+ if ((tok = yylex(yyscanner)) != K_EXECUTE)
{
/* ordinary static query */
plpgsql_push_back_token(tok);
- new->query = read_sql_stmt();
+ new->query = read_sql_stmt(yyscanner);
}
else
{
@@ -3463,14 +3477,14 @@ make_return_query_stmt(int location)
int term;
new->dynquery = read_sql_expression2(';', K_USING, "; or USING",
- &term);
+ &term, yyscanner);
if (term == K_USING)
{
do
{
PLpgSQL_expr *expr;
- expr = read_sql_expression2(',', ';', ", or ;", &term);
+ expr = read_sql_expression2(',', ';', ", or ;", &term, yyscanner);
new->params = lappend(new->params, expr);
} while (term == ',');
}
@@ -3524,7 +3538,7 @@ check_assignable(PLpgSQL_datum *datum, int location)
* INTO keyword.
*/
static void
-read_into_target(PLpgSQL_variable **target, bool *strict)
+read_into_target(PLpgSQL_variable **target, bool *strict, yyscan_t yyscanner)
{
int tok;
@@ -3533,11 +3547,11 @@ read_into_target(PLpgSQL_variable **target, bool *strict)
if (strict)
*strict = false;
- tok = yylex();
+ tok = yylex(yyscanner);
if (strict && tok == K_STRICT)
{
*strict = true;
- tok = yylex();
+ tok = yylex(yyscanner);
}
/*
@@ -3556,7 +3570,7 @@ read_into_target(PLpgSQL_variable **target, bool *strict)
check_assignable(yylval.wdatum.datum, yylloc);
*target = (PLpgSQL_variable *) yylval.wdatum.datum;
- if ((tok = yylex()) == ',')
+ if ((tok = yylex(yyscanner)) == ',')
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("record variable cannot be part of multiple-item INTO list"),
@@ -3567,13 +3581,13 @@ read_into_target(PLpgSQL_variable **target, bool *strict)
{
*target = (PLpgSQL_variable *)
read_into_scalar_list(NameOfDatum(&(yylval.wdatum)),
- yylval.wdatum.datum, yylloc);
+ yylval.wdatum.datum, yylloc, yyscanner);
}
break;
default:
/* just to give a better message than "syntax error" */
- current_token_is_not_variable(tok);
+ current_token_is_not_variable(tok, yyscanner);
}
}
@@ -3586,7 +3600,8 @@ read_into_target(PLpgSQL_variable **target, bool *strict)
static PLpgSQL_row *
read_into_scalar_list(char *initial_name,
PLpgSQL_datum *initial_datum,
- int initial_location)
+ int initial_location,
+ yyscan_t yyscanner)
{
int nfields;
char *fieldnames[1024];
@@ -3599,7 +3614,7 @@ read_into_scalar_list(char *initial_name,
varnos[0] = initial_datum->dno;
nfields = 1;
- while ((tok = yylex()) == ',')
+ while ((tok = yylex(yyscanner)) == ',')
{
/* Check for array overflow */
if (nfields >= 1024)
@@ -3608,7 +3623,7 @@ read_into_scalar_list(char *initial_name,
errmsg("too many INTO variables specified"),
parser_errposition(yylloc)));
- tok = yylex();
+ tok = yylex(yyscanner);
switch (tok)
{
case T_DATUM:
@@ -3626,7 +3641,7 @@ read_into_scalar_list(char *initial_name,
default:
/* just to give a better message than "syntax error" */
- current_token_is_not_variable(tok);
+ current_token_is_not_variable(tok, yyscanner);
}
}
@@ -3837,7 +3852,7 @@ check_labels(const char *start_label, const char *end_label, int end_location)
* parens).
*/
static PLpgSQL_expr *
-read_cursor_args(PLpgSQL_var *cursor, int until)
+read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
{
PLpgSQL_expr *expr;
PLpgSQL_row *row;
@@ -3847,7 +3862,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
StringInfoData ds;
bool any_named = false;
- tok = yylex();
+ tok = yylex(yyscanner);
if (cursor->cursor_explicit_argrow < 0)
{
/* No arguments expected */
@@ -3859,7 +3874,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
parser_errposition(yylloc)));
if (tok != until)
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
return NULL;
}
@@ -3888,7 +3903,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
int arglocation;
/* Check if it's a named parameter: "param := value" */
- plpgsql_peek2(&tok1, &tok2, &arglocation, NULL);
+ plpgsql_peek2(&tok1, &tok2, &arglocation, NULL, yyscanner);
if (tok1 == IDENT && tok2 == COLON_EQUALS)
{
char *argname;
@@ -3897,7 +3912,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
/* Read the argument name, ignoring any matching variable */
save_IdentifierLookup = plpgsql_IdentifierLookup;
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_DECLARE;
- yylex();
+ yylex(yyscanner);
argname = yylval.str;
plpgsql_IdentifierLookup = save_IdentifierLookup;
@@ -3918,9 +3933,9 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
* Eat the ":=". We already peeked, so the error should never
* happen.
*/
- tok2 = yylex();
+ tok2 = yylex(yyscanner);
if (tok2 != COLON_EQUALS)
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
any_named = true;
}
@@ -3944,7 +3959,8 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
",\" or \")",
RAW_PARSE_PLPGSQL_EXPR,
true, true,
- NULL, &endtoken);
+ NULL, &endtoken,
+ yyscanner);
argv[argpos] = item->query;
@@ -3991,9 +4007,9 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
pfree(ds.data);
/* Next we'd better find the until token */
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok != until)
- yyerror("syntax error");
+ yyerror(yyscanner, "syntax error");
return expr;
}
@@ -4002,7 +4018,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
* Parse RAISE ... USING options
*/
static List *
-read_raise_options(void)
+read_raise_options(yyscan_t yyscanner)
{
List *result = NIL;
@@ -4011,8 +4027,8 @@ read_raise_options(void)
PLpgSQL_raise_option *opt;
int tok;
- if ((tok = yylex()) == 0)
- yyerror("unexpected end of function definition");
+ if ((tok = yylex(yyscanner)) == 0)
+ yyerror(yyscanner, "unexpected end of function definition");
opt = (PLpgSQL_raise_option *) palloc(sizeof(PLpgSQL_raise_option));
@@ -4044,13 +4060,13 @@ read_raise_options(void)
K_SCHEMA, "schema"))
opt->opt_type = PLPGSQL_RAISEOPTION_SCHEMA;
else
- yyerror("unrecognized RAISE statement option");
+ yyerror(yyscanner, "unrecognized RAISE statement option");
- tok = yylex();
+ tok = yylex(yyscanner);
if (tok != '=' && tok != COLON_EQUALS)
- yyerror("syntax error, expected \"=\"");
+ yyerror(yyscanner, "syntax error, expected \"=\"");
- opt->expr = read_sql_expression2(',', ';', ", or ;", &tok);
+ opt->expr = read_sql_expression2(',', ';', ", or ;", &tok, yyscanner);
result = lappend(result, opt);
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
index 45fb5265851..eeb40c301db 100644
--- a/src/pl/plpgsql/src/pl_scanner.c
+++ b/src/pl/plpgsql/src/pl_scanner.c
@@ -103,7 +103,6 @@ typedef struct
*/
/* The stuff the core lexer needs */
-static core_yyscan_t yyscanner = NULL;
static core_yy_extra_type core_yy_extra;
/* The original input string */
@@ -128,7 +127,7 @@ static const char *cur_line_end;
static int cur_line_num;
/* Internal functions */
-static int internal_yylex(TokenAuxData *auxdata);
+static int internal_yylex(TokenAuxData *auxdata, yyscan_t yyscanner);
static void push_back_token(int token, TokenAuxData *auxdata);
static void location_lineno_init(void);
@@ -143,37 +142,37 @@ static void location_lineno_init(void);
* matches one of those.
*/
int
-plpgsql_yylex(void)
+plpgsql_yylex(yyscan_t yyscanner)
{
int tok1;
TokenAuxData aux1;
int kwnum;
- tok1 = internal_yylex(&aux1);
+ tok1 = internal_yylex(&aux1, yyscanner);
if (tok1 == IDENT || tok1 == PARAM)
{
int tok2;
TokenAuxData aux2;
- tok2 = internal_yylex(&aux2);
+ tok2 = internal_yylex(&aux2, yyscanner);
if (tok2 == '.')
{
int tok3;
TokenAuxData aux3;
- tok3 = internal_yylex(&aux3);
+ tok3 = internal_yylex(&aux3, yyscanner);
if (tok3 == IDENT)
{
int tok4;
TokenAuxData aux4;
- tok4 = internal_yylex(&aux4);
+ tok4 = internal_yylex(&aux4, yyscanner);
if (tok4 == '.')
{
int tok5;
TokenAuxData aux5;
- tok5 = internal_yylex(&aux5);
+ tok5 = internal_yylex(&aux5, yyscanner);
if (tok5 == IDENT)
{
if (plpgsql_parse_tripword(aux1.lval.str,
@@ -322,7 +321,7 @@ plpgsql_token_length(void)
* interfacing from the core_YYSTYPE to YYSTYPE union.
*/
static int
-internal_yylex(TokenAuxData *auxdata)
+internal_yylex(TokenAuxData *auxdata, yyscan_t yyscanner)
{
int token;
const char *yytext;
@@ -434,12 +433,12 @@ plpgsql_append_source_text(StringInfo buf,
* be returned as IDENT. Reserved keywords are resolved as usual.
*/
int
-plpgsql_peek(void)
+plpgsql_peek(yyscan_t yyscanner)
{
int tok1;
TokenAuxData aux1;
- tok1 = internal_yylex(&aux1);
+ tok1 = internal_yylex(&aux1, yyscanner);
push_back_token(tok1, &aux1);
return tok1;
}
@@ -453,15 +452,15 @@ plpgsql_peek(void)
* be returned as IDENT. Reserved keywords are resolved as usual.
*/
void
-plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc, int *tok2_loc)
+plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc, int *tok2_loc, yyscan_t yyscanner)
{
int tok1,
tok2;
TokenAuxData aux1,
aux2;
- tok1 = internal_yylex(&aux1);
- tok2 = internal_yylex(&aux2);
+ tok1 = internal_yylex(&aux1, yyscanner);
+ tok2 = internal_yylex(&aux2, yyscanner);
*tok1_p = tok1;
if (tok1_loc)
@@ -513,7 +512,7 @@ plpgsql_scanner_errposition(int location)
* be misleading!
*/
void
-plpgsql_yyerror(const char *message)
+plpgsql_yyerror(yyscan_t yyscanner, const char *message)
{
char *yytext = core_yy_extra.scanbuf + plpgsql_yylloc;
@@ -599,9 +598,11 @@ plpgsql_latest_lineno(void)
* Although it is not fed directly to flex, we need the original string
* to cite in error messages.
*/
-void
+yyscan_t
plpgsql_scanner_init(const char *str)
{
+ yyscan_t yyscanner;
+
/* Start up the core scanner */
yyscanner = scanner_init(str, &core_yy_extra,
&ReservedPLKeywords, ReservedPLKeywordTokens);
@@ -621,17 +622,18 @@ plpgsql_scanner_init(const char *str)
num_pushbacks = 0;
location_lineno_init();
+
+ return yyscanner;
}
/*
* Called after parsing is done to clean up after plpgsql_scanner_init()
*/
void
-plpgsql_scanner_finish(void)
+plpgsql_scanner_finish(yyscan_t yyscanner)
{
/* release storage */
scanner_finish(yyscanner);
/* avoid leaving any dangling pointers */
- yyscanner = NULL;
scanorig = NULL;
}
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 50c3b28472b..07dcfa84f88 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -1315,26 +1315,29 @@ extern void plpgsql_dumptree(PLpgSQL_function *func);
/*
* Scanner functions in pl_scanner.c
*/
-extern int plpgsql_base_yylex(void);
-extern int plpgsql_yylex(void);
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void *yyscan_t;
+#endif
+extern int plpgsql_yylex(yyscan_t yyscanner);
extern int plpgsql_token_length(void);
extern void plpgsql_push_back_token(int token);
extern bool plpgsql_token_is_unreserved_keyword(int token);
extern void plpgsql_append_source_text(StringInfo buf,
int startlocation, int endlocation);
-extern int plpgsql_peek(void);
+extern int plpgsql_peek(yyscan_t yyscanner);
extern void plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc,
- int *tok2_loc);
+ int *tok2_loc, yyscan_t yyscanner);
extern int plpgsql_scanner_errposition(int location);
-extern void plpgsql_yyerror(const char *message) pg_attribute_noreturn();
+extern void plpgsql_yyerror(yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
extern int plpgsql_location_to_lineno(int location);
extern int plpgsql_latest_lineno(void);
-extern void plpgsql_scanner_init(const char *str);
-extern void plpgsql_scanner_finish(void);
+extern yyscan_t plpgsql_scanner_init(const char *str);
+extern void plpgsql_scanner_finish(yyscan_t yyscanner);
/*
* Externs in gram.y
*/
-extern int plpgsql_yyparse(void);
+extern int plpgsql_yyparse(yyscan_t yyscanner);
#endif /* PLPGSQL_H */
--
2.47.1
v3-0003-plpgsql-pure-parser.patchtext/plain; charset=UTF-8; name=v3-0003-plpgsql-pure-parser.patchDownload
From 5821e9ce7ff85f7304c56f8ff0483f2e07b3be8e Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Thu, 26 Dec 2024 00:08:25 +0100
Subject: [PATCH v3 3/4] plpgsql: pure parser
---
src/pl/plpgsql/src/nls.mk | 2 +-
src/pl/plpgsql/src/pl_gram.y | 602 ++++++++++++++++----------------
src/pl/plpgsql/src/pl_scanner.c | 20 +-
src/pl/plpgsql/src/plpgsql.h | 8 +-
4 files changed, 318 insertions(+), 314 deletions(-)
diff --git a/src/pl/plpgsql/src/nls.mk b/src/pl/plpgsql/src/nls.mk
index ec7c7035add..eb06336675c 100644
--- a/src/pl/plpgsql/src/nls.mk
+++ b/src/pl/plpgsql/src/nls.mk
@@ -6,5 +6,5 @@ GETTEXT_FILES = pl_comp.c \
pl_funcs.c \
pl_handler.c \
pl_scanner.c
-GETTEXT_TRIGGERS = $(BACKEND_COMMON_GETTEXT_TRIGGERS) yyerror:2 plpgsql_yyerror:2
+GETTEXT_TRIGGERS = $(BACKEND_COMMON_GETTEXT_TRIGGERS) yyerror:3 plpgsql_yyerror:3
GETTEXT_FLAGS = $(BACKEND_COMMON_GETTEXT_FLAGS)
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index e8f81d287a1..cb5c2dca186 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -28,10 +28,6 @@
#include "pl_gram.h"
-/* silence -Wmissing-variable-declarations */
-extern int plpgsql_yychar;
-extern int plpgsql_yynerrs;
-
/* Location tracking support --- simpler than bison's default */
#define YYLLOC_DEFAULT(Current, Rhs, N) \
do { \
@@ -63,7 +59,7 @@ static bool tok_is_keyword(int token, union YYSTYPE *lval,
int kw_token, const char *kw_str);
static void word_is_not_variable(PLword *word, int location);
static void cword_is_not_variable(PLcword *cword, int location);
-static void current_token_is_not_variable(int tok, yyscan_t yyscanner);
+static void current_token_is_not_variable(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
static PLpgSQL_expr *read_sql_construct(int until,
int until2,
int until3,
@@ -73,31 +69,33 @@ static PLpgSQL_expr *read_sql_construct(int until,
bool valid_sql,
int *startloc,
int *endtoken,
+ YYSTYPE *yylvalp, YYLTYPE *yyllocp,
yyscan_t yyscanner);
-static PLpgSQL_expr *read_sql_expression(int until,
- const char *expected, yyscan_t yyscanner);
+static PLpgSQL_expr *read_sql_expression(int until, const char *expected,
+ YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
static PLpgSQL_expr *read_sql_expression2(int until, int until2,
- const char *expected,
- int *endtoken, yyscan_t yyscanner);
-static PLpgSQL_expr *read_sql_stmt(yyscan_t yyscanner);
-static PLpgSQL_type *read_datatype(int tok, yyscan_t yyscanner);
+ const char *expected, int *endtoken,
+ YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
+static PLpgSQL_expr *read_sql_stmt(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
+static PLpgSQL_type *read_datatype(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
static PLpgSQL_stmt *make_execsql_stmt(int firsttoken, int location,
- PLword *word, yyscan_t yyscanner);
-static PLpgSQL_stmt_fetch *read_fetch_direction(yyscan_t yyscanner);
+ PLword *word, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
+static PLpgSQL_stmt_fetch *read_fetch_direction(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
static void complete_direction(PLpgSQL_stmt_fetch *fetch,
- bool *check_FROM, yyscan_t yyscanner);
-static PLpgSQL_stmt *make_return_stmt(int location, yyscan_t yyscanner);
-static PLpgSQL_stmt *make_return_next_stmt(int location, yyscan_t yyscanner);
-static PLpgSQL_stmt *make_return_query_stmt(int location, yyscan_t yyscanner);
+ bool *check_FROM, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
+static PLpgSQL_stmt *make_return_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
+static PLpgSQL_stmt *make_return_next_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
+static PLpgSQL_stmt *make_return_query_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
static PLpgSQL_stmt *make_case(int location, PLpgSQL_expr *t_expr,
List *case_when_list, List *else_stmts);
static char *NameOfDatum(PLwdatum *wdatum);
static void check_assignable(PLpgSQL_datum *datum, int location);
-static void read_into_target(PLpgSQL_variable **target,
- bool *strict, yyscan_t yyscanner);
+static void read_into_target(PLpgSQL_variable **target, bool *strict,
+ YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
static PLpgSQL_row *read_into_scalar_list(char *initial_name,
PLpgSQL_datum *initial_datum,
int initial_location,
+ YYSTYPE *yylvalp, YYLTYPE *yyllocp,
yyscan_t yyscanner);
static PLpgSQL_row *make_scalar_list1(char *initial_name,
PLpgSQL_datum *initial_datum,
@@ -109,15 +107,16 @@ static PLpgSQL_type *parse_datatype(const char *string, int location);
static void check_labels(const char *start_label,
const char *end_label,
int end_location);
-static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor,
- int until, yyscan_t yyscanner);
-static List *read_raise_options(yyscan_t yyscanner);
+static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor, int until,
+ YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
+static List *read_raise_options(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%}
%parse-param {yyscan_t yyscanner}
%lex-param {yyscan_t yyscanner}
+%pure-parser
%expect 0
%name-prefix="plpgsql_yy"
%locations
@@ -581,7 +580,7 @@ opt_scrollable :
decl_cursor_query :
{
- $$ = read_sql_stmt(yyscanner);
+ $$ = read_sql_stmt(&yylval, &yylloc, yyscanner);
}
;
@@ -710,7 +709,7 @@ decl_varname : T_WORD
if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
$1.ident, NULL, NULL,
NULL) != NULL)
- yyerror(yyscanner, "duplicate declaration");
+ yyerror(&yylloc, yyscanner, "duplicate declaration");
if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
@@ -738,7 +737,7 @@ decl_varname : T_WORD
if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
$1, NULL, NULL,
NULL) != NULL)
- yyerror(yyscanner, "duplicate declaration");
+ yyerror(&yylloc, yyscanner, "duplicate declaration");
if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
@@ -770,7 +769,7 @@ decl_datatype :
* consume it, and then we must tell bison to forget
* it.
*/
- $$ = read_datatype(yychar, yyscanner);
+ $$ = read_datatype(yychar, &yylval, &yylloc, yyscanner);
yyclearin;
}
;
@@ -803,7 +802,7 @@ decl_defval : ';'
{ $$ = NULL; }
| decl_defkey
{
- $$ = read_sql_expression(';', ";", yyscanner);
+ $$ = read_sql_expression(';', ";", &yylval, &yylloc, yyscanner);
}
;
@@ -891,7 +890,7 @@ stmt_perform : K_PERFORM
new->cmd_type = PLPGSQL_STMT_PERFORM;
new->lineno = plpgsql_location_to_lineno(@1);
new->stmtid = ++plpgsql_curr_compile->nstatements;
- plpgsql_push_back_token(K_PERFORM);
+ plpgsql_push_back_token(K_PERFORM, &yylval, &yylloc);
/*
* Since PERFORM isn't legal SQL, we have to cheat to
@@ -905,7 +904,7 @@ stmt_perform : K_PERFORM
RAW_PARSE_DEFAULT,
false, false,
&startloc, NULL,
- yyscanner);
+ &yylval, &yylloc, yyscanner);
/* overwrite "perform" ... */
memcpy(new->expr->query, " SELECT", 7);
/* left-justify to get rid of the leading space */
@@ -927,8 +926,8 @@ stmt_call : K_CALL
new->cmd_type = PLPGSQL_STMT_CALL;
new->lineno = plpgsql_location_to_lineno(@1);
new->stmtid = ++plpgsql_curr_compile->nstatements;
- plpgsql_push_back_token(K_CALL);
- new->expr = read_sql_stmt(yyscanner);
+ plpgsql_push_back_token(K_CALL, &yylval, &yylloc);
+ new->expr = read_sql_stmt(&yylval, &yylloc, yyscanner);
new->is_call = true;
/* Remember we may need a procedure resource owner */
@@ -946,8 +945,8 @@ stmt_call : K_CALL
new->cmd_type = PLPGSQL_STMT_CALL;
new->lineno = plpgsql_location_to_lineno(@1);
new->stmtid = ++plpgsql_curr_compile->nstatements;
- plpgsql_push_back_token(K_DO);
- new->expr = read_sql_stmt(yyscanner);
+ plpgsql_push_back_token(K_DO, &yylval, &yylloc);
+ new->expr = read_sql_stmt(&yylval, &yylloc, yyscanner);
new->is_call = false;
/* Remember we may need a procedure resource owner */
@@ -987,12 +986,12 @@ stmt_assign : T_DATUM
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->varno = $1.datum->dno;
/* Push back the head name to include it in the stmt */
- plpgsql_push_back_token(T_DATUM);
+ plpgsql_push_back_token(T_DATUM, &yylval, &yylloc);
new->expr = read_sql_construct(';', 0, 0, ";",
pmode,
false, true,
NULL, NULL,
- yyscanner);
+ &yylval, &yylloc, yyscanner);
$$ = (PLpgSQL_stmt *) new;
}
@@ -1099,7 +1098,7 @@ getdiag_list_item : getdiag_target assign_operator getdiag_item
getdiag_item :
{
- int tok = yylex(yyscanner);
+ int tok = yylex(&yylval, &yylloc, yyscanner);
if (tok_is_keyword(tok, &yylval,
K_ROW_COUNT, "row_count"))
@@ -1141,7 +1140,7 @@ getdiag_item :
K_RETURNED_SQLSTATE, "returned_sqlstate"))
$$ = PLPGSQL_GETDIAG_RETURNED_SQLSTATE;
else
- yyerror(yyscanner, "unrecognized GET DIAGNOSTICS item");
+ yyerror(&yylloc, yyscanner, "unrecognized GET DIAGNOSTICS item");
}
;
@@ -1228,14 +1227,14 @@ stmt_case : K_CASE opt_expr_until_when case_when_list opt_case_else K_END K_CAS
opt_expr_until_when :
{
PLpgSQL_expr *expr = NULL;
- int tok = yylex(yyscanner);
+ int tok = yylex(&yylval, &yylloc, yyscanner);
if (tok != K_WHEN)
{
- plpgsql_push_back_token(tok);
- expr = read_sql_expression(K_WHEN, "WHEN", yyscanner);
+ plpgsql_push_back_token(tok, &yylval, &yylloc);
+ expr = read_sql_expression(K_WHEN, "WHEN", &yylval, &yylloc, yyscanner);
}
- plpgsql_push_back_token(K_WHEN);
+ plpgsql_push_back_token(K_WHEN, &yylval, &yylloc);
$$ = expr;
}
;
@@ -1353,7 +1352,7 @@ stmt_for : opt_loop_label K_FOR for_control loop_body
for_control : for_variable K_IN
{
- int tok = yylex(yyscanner);
+ int tok = yylex(&yylval, &yylloc, yyscanner);
int tokloc = yylloc;
if (tok == K_EXECUTE)
@@ -1365,7 +1364,7 @@ for_control : for_variable K_IN
expr = read_sql_expression2(K_LOOP, K_USING,
"LOOP or USING",
- &term, yyscanner);
+ &term, &yylval, &yylloc, yyscanner);
new = palloc0(sizeof(PLpgSQL_stmt_dynfors));
new->cmd_type = PLPGSQL_STMT_DYNFORS;
@@ -1398,7 +1397,7 @@ for_control : for_variable K_IN
{
expr = read_sql_expression2(',', K_LOOP,
", or LOOP",
- &term, yyscanner);
+ &term, &yylval, &yylloc, yyscanner);
new->params = lappend(new->params, expr);
} while (term == ',');
}
@@ -1433,7 +1432,7 @@ for_control : for_variable K_IN
parser_errposition(tokloc)));
/* collect cursor's parameters if any */
- new->argquery = read_cursor_args(cursor, K_LOOP, yyscanner);
+ new->argquery = read_cursor_args(cursor, K_LOOP, &yylval, &yylloc, yyscanner);
/* create loop's private RECORD variable */
new->var = (PLpgSQL_variable *)
@@ -1467,7 +1466,7 @@ for_control : for_variable K_IN
K_REVERSE, "reverse"))
reverse = true;
else
- plpgsql_push_back_token(tok);
+ plpgsql_push_back_token(tok, &yylval, &yylloc);
/*
* Read tokens until we see either a ".."
@@ -1485,7 +1484,7 @@ for_control : for_variable K_IN
false,
&expr1loc,
&tok,
- yyscanner);
+ &yylval, &yylloc, yyscanner);
if (tok == DOT_DOT)
{
@@ -1506,12 +1505,12 @@ for_control : for_variable K_IN
/* Read and check the second one */
expr2 = read_sql_expression2(K_LOOP, K_BY,
"LOOP",
- &tok, yyscanner);
+ &tok, &yylval, &yylloc, yyscanner);
/* Get the BY clause if any */
if (tok == K_BY)
expr_by = read_sql_expression(K_LOOP,
- "LOOP", yyscanner);
+ "LOOP", &yylval, &yylloc, yyscanner);
else
expr_by = NULL;
@@ -1626,13 +1625,14 @@ for_variable : T_DATUM
$$.scalar = $1.datum;
$$.row = NULL;
/* check for comma-separated list */
- tok = yylex(yyscanner);
- plpgsql_push_back_token(tok);
+ tok = yylex(&yylval, &yylloc, yyscanner);
+ plpgsql_push_back_token(tok, &yylval, &yylloc);
if (tok == ',')
$$.row = (PLpgSQL_datum *)
read_into_scalar_list($$.name,
$$.scalar,
@1,
+ &yylval, &yylloc,
yyscanner);
}
}
@@ -1645,8 +1645,8 @@ for_variable : T_DATUM
$$.scalar = NULL;
$$.row = NULL;
/* check for comma-separated list */
- tok = yylex(yyscanner);
- plpgsql_push_back_token(tok);
+ tok = yylex(&yylval, &yylloc, yyscanner);
+ plpgsql_push_back_token(tok, &yylval, &yylloc);
if (tok == ',')
word_is_not_variable(&($1), @1);
}
@@ -1772,24 +1772,24 @@ stmt_return : K_RETURN
{
int tok;
- tok = yylex(yyscanner);
+ tok = yylex(&yylval, &yylloc, yyscanner);
if (tok == 0)
- yyerror(yyscanner, "unexpected end of function definition");
+ yyerror(&yylloc, yyscanner, "unexpected end of function definition");
if (tok_is_keyword(tok, &yylval,
K_NEXT, "next"))
{
- $$ = make_return_next_stmt(@1, yyscanner);
+ $$ = make_return_next_stmt(@1, &yylval, &yylloc, yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_QUERY, "query"))
{
- $$ = make_return_query_stmt(@1, yyscanner);
+ $$ = make_return_query_stmt(@1, &yylval, &yylloc, yyscanner);
}
else
{
- plpgsql_push_back_token(tok);
- $$ = make_return_stmt(@1, yyscanner);
+ plpgsql_push_back_token(tok, &yylval, &yylloc);
+ $$ = make_return_stmt(@1, &yylval, &yylloc, yyscanner);
}
}
;
@@ -1810,9 +1810,9 @@ stmt_raise : K_RAISE
new->params = NIL;
new->options = NIL;
- tok = yylex(yyscanner);
+ tok = yylex(&yylval, &yylloc, yyscanner);
if (tok == 0)
- yyerror(yyscanner, "unexpected end of function definition");
+ yyerror(&yylloc, yyscanner, "unexpected end of function definition");
/*
* We could have just RAISE, meaning to re-throw
@@ -1827,40 +1827,40 @@ stmt_raise : K_RAISE
K_EXCEPTION, "exception"))
{
new->elog_level = ERROR;
- tok = yylex(yyscanner);
+ tok = yylex(&yylval, &yylloc, yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_WARNING, "warning"))
{
new->elog_level = WARNING;
- tok = yylex(yyscanner);
+ tok = yylex(&yylval, &yylloc, yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_NOTICE, "notice"))
{
new->elog_level = NOTICE;
- tok = yylex(yyscanner);
+ tok = yylex(&yylval, &yylloc, yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_INFO, "info"))
{
new->elog_level = INFO;
- tok = yylex(yyscanner);
+ tok = yylex(&yylval, &yylloc, yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_LOG, "log"))
{
new->elog_level = LOG;
- tok = yylex(yyscanner);
+ tok = yylex(&yylval, &yylloc, yyscanner);
}
else if (tok_is_keyword(tok, &yylval,
K_DEBUG, "debug"))
{
new->elog_level = DEBUG1;
- tok = yylex(yyscanner);
+ tok = yylex(&yylval, &yylloc, yyscanner);
}
if (tok == 0)
- yyerror(yyscanner, "unexpected end of function definition");
+ yyerror(&yylloc, yyscanner, "unexpected end of function definition");
/*
* Next we can have a condition name, or
@@ -1878,9 +1878,9 @@ stmt_raise : K_RAISE
* begins the list of parameter expressions,
* or USING to begin the options list.
*/
- tok = yylex(yyscanner);
+ tok = yylex(&yylval, &yylloc, yyscanner);
if (tok != ',' && tok != ';' && tok != K_USING)
- yyerror(yyscanner, "syntax error");
+ yyerror(&yylloc, yyscanner, "syntax error");
while (tok == ',')
{
@@ -1891,7 +1891,7 @@ stmt_raise : K_RAISE
RAW_PARSE_PLPGSQL_EXPR,
true, true,
NULL, &tok,
- yyscanner);
+ &yylval, &yylloc, yyscanner);
new->params = lappend(new->params, expr);
}
}
@@ -1904,14 +1904,14 @@ stmt_raise : K_RAISE
/* next token should be a string literal */
char *sqlstatestr;
- if (yylex(yyscanner) != SCONST)
- yyerror(yyscanner, "syntax error");
+ if (yylex(&yylval, &yylloc, yyscanner) != SCONST)
+ yyerror(&yylloc, yyscanner, "syntax error");
sqlstatestr = yylval.str;
if (strlen(sqlstatestr) != 5)
- yyerror(yyscanner, "invalid SQLSTATE code");
+ yyerror(&yylloc, yyscanner, "invalid SQLSTATE code");
if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
- yyerror(yyscanner, "invalid SQLSTATE code");
+ yyerror(&yylloc, yyscanner, "invalid SQLSTATE code");
new->condname = sqlstatestr;
}
else
@@ -1921,17 +1921,17 @@ stmt_raise : K_RAISE
else if (plpgsql_token_is_unreserved_keyword(tok))
new->condname = pstrdup(yylval.keyword);
else
- yyerror(yyscanner, "syntax error");
+ yyerror(&yylloc, yyscanner, "syntax error");
plpgsql_recognize_err_condition(new->condname,
false);
}
- tok = yylex(yyscanner);
+ tok = yylex(&yylval, &yylloc, yyscanner);
if (tok != ';' && tok != K_USING)
- yyerror(yyscanner, "syntax error");
+ yyerror(&yylloc, yyscanner, "syntax error");
}
if (tok == K_USING)
- new->options = read_raise_options(yyscanner);
+ new->options = read_raise_options(&yylval, &yylloc, yyscanner);
}
check_raise_parameters(new);
@@ -1953,10 +1953,10 @@ stmt_assert : K_ASSERT
new->cond = read_sql_expression2(',', ';',
", or ;",
- &tok, yyscanner);
+ &tok, &yylval, &yylloc, yyscanner);
if (tok == ',')
- new->message = read_sql_expression(';', ";", yyscanner);
+ new->message = read_sql_expression(';', ";", &yylval, &yylloc, yyscanner);
else
new->message = NULL;
@@ -1984,37 +1984,37 @@ loop_body : proc_sect K_END K_LOOP opt_label ';'
*/
stmt_execsql : K_IMPORT
{
- $$ = make_execsql_stmt(K_IMPORT, @1, NULL, yyscanner);
+ $$ = make_execsql_stmt(K_IMPORT, @1, NULL, &yylval, &yylloc, yyscanner);
}
| K_INSERT
{
- $$ = make_execsql_stmt(K_INSERT, @1, NULL, yyscanner);
+ $$ = make_execsql_stmt(K_INSERT, @1, NULL, &yylval, &yylloc, yyscanner);
}
| K_MERGE
{
- $$ = make_execsql_stmt(K_MERGE, @1, NULL, yyscanner);
+ $$ = make_execsql_stmt(K_MERGE, @1, NULL, &yylval, &yylloc, yyscanner);
}
| T_WORD
{
int tok;
- tok = yylex(yyscanner);
- plpgsql_push_back_token(tok);
+ tok = yylex(&yylval, &yylloc, yyscanner);
+ plpgsql_push_back_token(tok, &yylval, &yylloc);
if (tok == '=' || tok == COLON_EQUALS ||
tok == '[' || tok == '.')
word_is_not_variable(&($1), @1);
- $$ = make_execsql_stmt(T_WORD, @1, &($1), yyscanner);
+ $$ = make_execsql_stmt(T_WORD, @1, &($1), &yylval, &yylloc, yyscanner);
}
| T_CWORD
{
int tok;
- tok = yylex(yyscanner);
- plpgsql_push_back_token(tok);
+ tok = yylex(&yylval, &yylloc, yyscanner);
+ plpgsql_push_back_token(tok, &yylval, &yylloc);
if (tok == '=' || tok == COLON_EQUALS ||
tok == '[' || tok == '.')
cword_is_not_variable(&($1), @1);
- $$ = make_execsql_stmt(T_CWORD, @1, NULL, yyscanner);
+ $$ = make_execsql_stmt(T_CWORD, @1, NULL, &yylval, &yylloc, yyscanner);
}
;
@@ -2029,7 +2029,7 @@ stmt_dynexecute : K_EXECUTE
RAW_PARSE_PLPGSQL_EXPR,
true, true,
NULL, &endtoken,
- yyscanner);
+ &yylval, &yylloc, yyscanner);
new = palloc(sizeof(PLpgSQL_stmt_dynexecute));
new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
@@ -2053,15 +2053,15 @@ stmt_dynexecute : K_EXECUTE
if (endtoken == K_INTO)
{
if (new->into) /* multiple INTO */
- yyerror(yyscanner, "syntax error");
+ yyerror(&yylloc, yyscanner, "syntax error");
new->into = true;
- read_into_target(&new->target, &new->strict, yyscanner);
- endtoken = yylex(yyscanner);
+ read_into_target(&new->target, &new->strict, &yylval, &yylloc, yyscanner);
+ endtoken = yylex(&yylval, &yylloc, yyscanner);
}
else if (endtoken == K_USING)
{
if (new->params) /* multiple USING */
- yyerror(yyscanner, "syntax error");
+ yyerror(&yylloc, yyscanner, "syntax error");
do
{
expr = read_sql_construct(',', ';', K_INTO,
@@ -2069,14 +2069,14 @@ stmt_dynexecute : K_EXECUTE
RAW_PARSE_PLPGSQL_EXPR,
true, true,
NULL, &endtoken,
- yyscanner);
+ &yylval, &yylloc, yyscanner);
new->params = lappend(new->params, expr);
} while (endtoken == ',');
}
else if (endtoken == ';')
break;
else
- yyerror(yyscanner, "syntax error");
+ yyerror(&yylloc, yyscanner, "syntax error");
}
$$ = (PLpgSQL_stmt *) new;
@@ -2099,29 +2099,29 @@ stmt_open : K_OPEN cursor_variable
if ($2->cursor_explicit_expr == NULL)
{
/* be nice if we could use opt_scrollable here */
- tok = yylex(yyscanner);
+ tok = yylex(&yylval, &yylloc, yyscanner);
if (tok_is_keyword(tok, &yylval,
K_NO, "no"))
{
- tok = yylex(yyscanner);
+ tok = yylex(&yylval, &yylloc, yyscanner);
if (tok_is_keyword(tok, &yylval,
K_SCROLL, "scroll"))
{
new->cursor_options |= CURSOR_OPT_NO_SCROLL;
- tok = yylex(yyscanner);
+ tok = yylex(&yylval, &yylloc, yyscanner);
}
}
else if (tok_is_keyword(tok, &yylval,
K_SCROLL, "scroll"))
{
new->cursor_options |= CURSOR_OPT_SCROLL;
- tok = yylex(yyscanner);
+ tok = yylex(&yylval, &yylloc, yyscanner);
}
if (tok != K_FOR)
- yyerror(yyscanner, "syntax error, expected \"FOR\"");
+ yyerror(&yylloc, yyscanner, "syntax error, expected \"FOR\"");
- tok = yylex(yyscanner);
+ tok = yylex(&yylval, &yylloc, yyscanner);
if (tok == K_EXECUTE)
{
int endtoken;
@@ -2129,7 +2129,7 @@ stmt_open : K_OPEN cursor_variable
new->dynquery =
read_sql_expression2(K_USING, ';',
"USING or ;",
- &endtoken, yyscanner);
+ &endtoken, &yylval, &yylloc, yyscanner);
/* If we found "USING", collect argument(s) */
if (endtoken == K_USING)
@@ -2140,7 +2140,7 @@ stmt_open : K_OPEN cursor_variable
{
expr = read_sql_expression2(',', ';',
", or ;",
- &endtoken, yyscanner);
+ &endtoken, &yylval, &yylloc, yyscanner);
new->params = lappend(new->params,
expr);
} while (endtoken == ',');
@@ -2148,14 +2148,14 @@ stmt_open : K_OPEN cursor_variable
}
else
{
- plpgsql_push_back_token(tok);
- new->query = read_sql_stmt(yyscanner);
+ plpgsql_push_back_token(tok, &yylval, &yylloc);
+ new->query = read_sql_stmt(&yylval, &yylloc, yyscanner);
}
}
else
{
/* predefined cursor query, so read args */
- new->argquery = read_cursor_args($2, ';', yyscanner);
+ new->argquery = read_cursor_args($2, ';', &yylval, &yylloc, yyscanner);
}
$$ = (PLpgSQL_stmt *) new;
@@ -2168,10 +2168,10 @@ stmt_fetch : K_FETCH opt_fetch_direction cursor_variable K_INTO
PLpgSQL_variable *target;
/* We have already parsed everything through the INTO keyword */
- read_into_target(&target, NULL, yyscanner);
+ read_into_target(&target, NULL, &yylval, &yylloc, yyscanner);
- if (yylex(yyscanner) != ';')
- yyerror(yyscanner, "syntax error");
+ if (yylex(&yylval, &yylloc, yyscanner) != ';')
+ yyerror(&yylloc, yyscanner, "syntax error");
/*
* We don't allow multiple rows in PL/pgSQL's FETCH
@@ -2206,7 +2206,7 @@ stmt_move : K_MOVE opt_fetch_direction cursor_variable ';'
opt_fetch_direction :
{
- $$ = read_fetch_direction(yyscanner);
+ $$ = read_fetch_direction(&yylval, &yylloc, yyscanner);
}
;
@@ -2394,14 +2394,14 @@ proc_condition : any_identifier
char *sqlstatestr;
/* next token should be a string literal */
- if (yylex(yyscanner) != SCONST)
- yyerror(yyscanner, "syntax error");
+ if (yylex(&yylval, &yylloc, yyscanner) != SCONST)
+ yyerror(&yylloc, yyscanner, "syntax error");
sqlstatestr = yylval.str;
if (strlen(sqlstatestr) != 5)
- yyerror(yyscanner, "invalid SQLSTATE code");
+ yyerror(&yylloc, yyscanner, "invalid SQLSTATE code");
if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
- yyerror(yyscanner, "invalid SQLSTATE code");
+ yyerror(&yylloc, yyscanner, "invalid SQLSTATE code");
new = palloc(sizeof(PLpgSQL_condition));
new->sqlerrstate =
@@ -2419,15 +2419,15 @@ proc_condition : any_identifier
;
expr_until_semi :
- { $$ = read_sql_expression(';', ";", yyscanner); }
+ { $$ = read_sql_expression(';', ";", &yylval, &yylloc, yyscanner); }
;
expr_until_then :
- { $$ = read_sql_expression(K_THEN, "THEN", yyscanner); }
+ { $$ = read_sql_expression(K_THEN, "THEN", &yylval, &yylloc, yyscanner); }
;
expr_until_loop :
- { $$ = read_sql_expression(K_LOOP, "LOOP", yyscanner); }
+ { $$ = read_sql_expression(K_LOOP, "LOOP", &yylval, &yylloc, yyscanner); }
;
opt_block_label :
@@ -2485,7 +2485,7 @@ any_identifier : T_WORD
| T_DATUM
{
if ($1.ident == NULL) /* composite name not OK */
- yyerror(yyscanner, "syntax error");
+ yyerror(&yylloc, yyscanner, "syntax error");
$$ = $1.ident;
}
;
@@ -2637,45 +2637,45 @@ cword_is_not_variable(PLcword *cword, int location)
* look at yylval and yylloc.
*/
static void
-current_token_is_not_variable(int tok, yyscan_t yyscanner)
+current_token_is_not_variable(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
{
if (tok == T_WORD)
- word_is_not_variable(&(yylval.word), yylloc);
+ word_is_not_variable(&(yylvalp->word), *yyllocp);
else if (tok == T_CWORD)
- cword_is_not_variable(&(yylval.cword), yylloc);
+ cword_is_not_variable(&(yylvalp->cword), *yyllocp);
else
- yyerror(yyscanner, "syntax error");
+ yyerror(yyllocp, yyscanner, "syntax error");
}
/* Convenience routine to read an expression with one possible terminator */
static PLpgSQL_expr *
-read_sql_expression(int until, const char *expected, yyscan_t yyscanner)
+read_sql_expression(int until, const char *expected, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
{
return read_sql_construct(until, 0, 0, expected,
RAW_PARSE_PLPGSQL_EXPR,
true, true, NULL, NULL,
- yyscanner);
+ yylvalp, yyllocp, yyscanner);
}
/* Convenience routine to read an expression with two possible terminators */
static PLpgSQL_expr *
read_sql_expression2(int until, int until2, const char *expected,
- int *endtoken, yyscan_t yyscanner)
+ int *endtoken, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
{
return read_sql_construct(until, until2, 0, expected,
RAW_PARSE_PLPGSQL_EXPR,
true, true, NULL, endtoken,
- yyscanner);
+ yylvalp, yyllocp, yyscanner);
}
/* Convenience routine to read a SQL statement that must end with ';' */
static PLpgSQL_expr *
-read_sql_stmt(yyscan_t yyscanner)
+read_sql_stmt(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
{
return read_sql_construct(';', 0, 0, ";",
RAW_PARSE_DEFAULT,
false, true, NULL, NULL,
- yyscanner);
+ yylvalp, yyllocp, yyscanner);
}
/*
@@ -2702,6 +2702,7 @@ read_sql_construct(int until,
bool valid_sql,
int *startloc,
int *endtoken,
+ YYSTYPE *yylvalp, YYLTYPE *yyllocp,
yyscan_t yyscanner)
{
int tok;
@@ -2720,9 +2721,9 @@ read_sql_construct(int until,
for (;;)
{
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
if (startlocation < 0) /* remember loc of first token */
- startlocation = yylloc;
+ startlocation = *yyllocp;
if (tok == until && parenlevel == 0)
break;
if (tok == until2 && parenlevel == 0)
@@ -2735,7 +2736,7 @@ read_sql_construct(int until,
{
parenlevel--;
if (parenlevel < 0)
- yyerror(yyscanner, "mismatched parentheses");
+ yyerror(yyllocp, yyscanner, "mismatched parentheses");
}
/*
@@ -2746,22 +2747,22 @@ read_sql_construct(int until,
if (tok == 0 || tok == ';')
{
if (parenlevel != 0)
- yyerror(yyscanner, "mismatched parentheses");
+ yyerror(yyllocp, yyscanner, "mismatched parentheses");
if (isexpression)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("missing \"%s\" at end of SQL expression",
expected),
- parser_errposition(yylloc)));
+ parser_errposition(*yyllocp)));
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("missing \"%s\" at end of SQL statement",
expected),
- parser_errposition(yylloc)));
+ parser_errposition(*yyllocp)));
}
/* Remember end+1 location of last accepted token */
- endlocation = yylloc + plpgsql_token_length();
+ endlocation = *yyllocp + plpgsql_token_length();
}
plpgsql_IdentifierLookup = save_IdentifierLookup;
@@ -2775,9 +2776,9 @@ read_sql_construct(int until,
if (startlocation >= endlocation)
{
if (isexpression)
- yyerror(yyscanner, "missing expression");
+ yyerror(yyllocp, yyscanner, "missing expression");
else
- yyerror(yyscanner, "missing SQL statement");
+ yyerror(yyllocp, yyscanner, "missing SQL statement");
}
/*
@@ -2809,7 +2810,7 @@ read_sql_construct(int until,
* Returns a PLpgSQL_type struct.
*/
static PLpgSQL_type *
-read_datatype(int tok, yyscan_t yyscanner)
+read_datatype(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
{
StringInfoData ds;
char *type_name;
@@ -2822,10 +2823,10 @@ read_datatype(int tok, yyscan_t yyscanner)
/* Often there will be a lookahead token, but if not, get one */
if (tok == YYEMPTY)
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
/* The current token is the start of what we'll pass to parse_datatype */
- startlocation = yylloc;
+ startlocation = *yyllocp;
/*
* If we have a simple or composite identifier, check for %TYPE and
@@ -2833,48 +2834,48 @@ read_datatype(int tok, yyscan_t yyscanner)
*/
if (tok == T_WORD)
{
- char *dtname = yylval.word.ident;
+ char *dtname = yylvalp->word.ident;
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
if (tok == '%')
{
- tok = yylex(yyscanner);
- if (tok_is_keyword(tok, &yylval,
+ tok = yylex(yylvalp, yyllocp, yyscanner);
+ if (tok_is_keyword(tok, yylvalp,
K_TYPE, "type"))
result = plpgsql_parse_wordtype(dtname);
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_ROWTYPE, "rowtype"))
result = plpgsql_parse_wordrowtype(dtname);
}
}
else if (plpgsql_token_is_unreserved_keyword(tok))
{
- char *dtname = pstrdup(yylval.keyword);
+ char *dtname = pstrdup(yylvalp->keyword);
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
if (tok == '%')
{
- tok = yylex(yyscanner);
- if (tok_is_keyword(tok, &yylval,
+ tok = yylex(yylvalp, yyllocp, yyscanner);
+ if (tok_is_keyword(tok, yylvalp,
K_TYPE, "type"))
result = plpgsql_parse_wordtype(dtname);
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_ROWTYPE, "rowtype"))
result = plpgsql_parse_wordrowtype(dtname);
}
}
else if (tok == T_CWORD)
{
- List *dtnames = yylval.cword.idents;
+ List *dtnames = yylvalp->cword.idents;
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
if (tok == '%')
{
- tok = yylex(yyscanner);
- if (tok_is_keyword(tok, &yylval,
+ tok = yylex(yylvalp, yyllocp, yyscanner);
+ if (tok_is_keyword(tok, yylvalp,
K_TYPE, "type"))
result = plpgsql_parse_cwordtype(dtnames);
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_ROWTYPE, "rowtype"))
result = plpgsql_parse_cwordrowtype(dtnames);
}
@@ -2892,24 +2893,24 @@ read_datatype(int tok, yyscan_t yyscanner)
{
bool is_array = false;
- tok = yylex(yyscanner);
- if (tok_is_keyword(tok, &yylval,
+ tok = yylex(yylvalp, yyllocp, yyscanner);
+ if (tok_is_keyword(tok, yylvalp,
K_ARRAY, "array"))
{
is_array = true;
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
}
while (tok == '[')
{
is_array = true;
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
if (tok == ICONST)
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
if (tok != ']')
- yyerror(yyscanner, "syntax error, expected \"]\"");
- tok = yylex(yyscanner);
+ yyerror(yyllocp, yyscanner, "syntax error, expected \"]\"");
+ tok = yylex(yylvalp, yyllocp, yyscanner);
}
- plpgsql_push_back_token(tok);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp);
if (is_array)
result = plpgsql_build_datatype_arrayof(result);
@@ -2928,9 +2929,9 @@ read_datatype(int tok, yyscan_t yyscanner)
if (tok == 0)
{
if (parenlevel != 0)
- yyerror(yyscanner, "mismatched parentheses");
+ yyerror(yyllocp, yyscanner, "mismatched parentheses");
else
- yyerror(yyscanner, "incomplete data type declaration");
+ yyerror(yyllocp, yyscanner, "incomplete data type declaration");
}
/* Possible followers for datatype in a declaration */
if (tok == K_COLLATE || tok == K_NOT ||
@@ -2944,22 +2945,22 @@ read_datatype(int tok, yyscan_t yyscanner)
else if (tok == ')')
parenlevel--;
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
}
/* set up ds to contain complete typename text */
initStringInfo(&ds);
- plpgsql_append_source_text(&ds, startlocation, yylloc);
+ plpgsql_append_source_text(&ds, startlocation, *yyllocp);
type_name = ds.data;
if (type_name[0] == '\0')
- yyerror(yyscanner, "missing data type declaration");
+ yyerror(yyllocp, yyscanner, "missing data type declaration");
result = parse_datatype(type_name, startlocation);
pfree(ds.data);
- plpgsql_push_back_token(tok);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp);
return result;
}
@@ -2970,7 +2971,7 @@ read_datatype(int tok, yyscan_t yyscanner)
* If firsttoken == T_WORD, pass its yylval value as "word", else pass NULL.
*/
static PLpgSQL_stmt *
-make_execsql_stmt(int firsttoken, int location, PLword *word, yyscan_t yyscanner)
+make_execsql_stmt(int firsttoken, int location, PLword *word, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
{
StringInfoData ds;
IdentifierLookup save_IdentifierLookup;
@@ -3039,22 +3040,22 @@ make_execsql_stmt(int firsttoken, int location, PLword *word, yyscan_t yyscanner
for (;;)
{
prev_tok = tok;
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
if (have_into && into_end_loc < 0)
- into_end_loc = yylloc; /* token after the INTO part */
+ into_end_loc = *yyllocp; /* token after the INTO part */
/* Detect CREATE [OR REPLACE] {FUNCTION|PROCEDURE} */
if (tokens[0] == 'c' && token_count < sizeof(tokens))
{
if (tok == K_OR)
tokens[token_count] = 'o';
else if (tok == T_WORD &&
- strcmp(yylval.word.ident, "replace") == 0)
+ strcmp(yylvalp->word.ident, "replace") == 0)
tokens[token_count] = 'r';
else if (tok == T_WORD &&
- strcmp(yylval.word.ident, "function") == 0)
+ strcmp(yylvalp->word.ident, "function") == 0)
tokens[token_count] = 'f';
else if (tok == T_WORD &&
- strcmp(yylval.word.ident, "procedure") == 0)
+ strcmp(yylvalp->word.ident, "procedure") == 0)
tokens[token_count] = 'f'; /* treat same as "function" */
if (tokens[1] == 'f' ||
(tokens[1] == 'o' && tokens[2] == 'r' && tokens[3] == 'f'))
@@ -3078,7 +3079,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word, yyscan_t yyscanner
if (tok == ';' && paren_depth == 0 && begin_depth == 0)
break;
if (tok == 0)
- yyerror(yyscanner, "unexpected end of function definition");
+ yyerror(yyllocp, yyscanner, "unexpected end of function definition");
if (tok == K_INTO)
{
if (prev_tok == K_INSERT)
@@ -3088,11 +3089,11 @@ make_execsql_stmt(int firsttoken, int location, PLword *word, yyscan_t yyscanner
if (firsttoken == K_IMPORT)
continue; /* IMPORT ... INTO is not an INTO-target */
if (have_into)
- yyerror(yyscanner, "INTO specified more than once");
+ yyerror(yyllocp, yyscanner, "INTO specified more than once");
have_into = true;
- into_start_loc = yylloc;
+ into_start_loc = *yyllocp;
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
- read_into_target(&target, &have_strict, yyscanner);
+ read_into_target(&target, &have_strict, yylvalp, yyllocp, yyscanner);
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_EXPR;
}
}
@@ -3108,10 +3109,10 @@ make_execsql_stmt(int firsttoken, int location, PLword *word, yyscan_t yyscanner
*/
plpgsql_append_source_text(&ds, location, into_start_loc);
appendStringInfoSpaces(&ds, into_end_loc - into_start_loc);
- plpgsql_append_source_text(&ds, into_end_loc, yylloc);
+ plpgsql_append_source_text(&ds, into_end_loc, *yyllocp);
}
else
- plpgsql_append_source_text(&ds, location, yylloc);
+ plpgsql_append_source_text(&ds, location, *yyllocp);
/* trim any trailing whitespace, for neatness */
while (ds.len > 0 && scanner_isspace(ds.data[ds.len - 1]))
@@ -3145,7 +3146,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word, yyscan_t yyscanner
* Read FETCH or MOVE direction clause (everything through FROM/IN).
*/
static PLpgSQL_stmt_fetch *
-read_fetch_direction(yyscan_t yyscanner)
+read_fetch_direction(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
{
PLpgSQL_stmt_fetch *fetch;
int tok;
@@ -3164,65 +3165,65 @@ read_fetch_direction(yyscan_t yyscanner)
fetch->expr = NULL;
fetch->returns_multiple_rows = false;
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
if (tok == 0)
- yyerror(yyscanner, "unexpected end of function definition");
+ yyerror(yyllocp, yyscanner, "unexpected end of function definition");
- if (tok_is_keyword(tok, &yylval,
+ if (tok_is_keyword(tok, yylvalp,
K_NEXT, "next"))
{
/* use defaults */
}
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_PRIOR, "prior"))
{
fetch->direction = FETCH_BACKWARD;
}
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_FIRST, "first"))
{
fetch->direction = FETCH_ABSOLUTE;
}
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_LAST, "last"))
{
fetch->direction = FETCH_ABSOLUTE;
fetch->how_many = -1;
}
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_ABSOLUTE, "absolute"))
{
fetch->direction = FETCH_ABSOLUTE;
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"FROM or IN",
- NULL, yyscanner);
+ NULL, yylvalp, yyllocp, yyscanner);
check_FROM = false;
}
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_RELATIVE, "relative"))
{
fetch->direction = FETCH_RELATIVE;
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"FROM or IN",
- NULL, yyscanner);
+ NULL, yylvalp, yyllocp, yyscanner);
check_FROM = false;
}
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_ALL, "all"))
{
fetch->how_many = FETCH_ALL;
fetch->returns_multiple_rows = true;
}
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_FORWARD, "forward"))
{
- complete_direction(fetch, &check_FROM, yyscanner);
+ complete_direction(fetch, &check_FROM, yylvalp, yyllocp, yyscanner);
}
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_BACKWARD, "backward"))
{
fetch->direction = FETCH_BACKWARD;
- complete_direction(fetch, &check_FROM, yyscanner);
+ complete_direction(fetch, &check_FROM, yylvalp, yyllocp, yyscanner);
}
else if (tok == K_FROM || tok == K_IN)
{
@@ -3232,7 +3233,7 @@ read_fetch_direction(yyscan_t yyscanner)
else if (tok == T_DATUM)
{
/* Assume there's no direction clause and tok is a cursor name */
- plpgsql_push_back_token(tok);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp);
check_FROM = false;
}
else
@@ -3245,10 +3246,10 @@ read_fetch_direction(yyscan_t yyscanner)
* will trigger. Perhaps this can be improved someday, but it hardly
* seems worth a lot of work.
*/
- plpgsql_push_back_token(tok);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp);
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"FROM or IN",
- NULL, yyscanner);
+ NULL, yylvalp, yyllocp, yyscanner);
fetch->returns_multiple_rows = true;
check_FROM = false;
}
@@ -3256,9 +3257,9 @@ read_fetch_direction(yyscan_t yyscanner)
/* check FROM or IN keyword after direction's specification */
if (check_FROM)
{
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
if (tok != K_FROM && tok != K_IN)
- yyerror(yyscanner, "expected FROM or IN");
+ yyerror(yyllocp, yyscanner, "expected FROM or IN");
}
return fetch;
@@ -3271,13 +3272,13 @@ read_fetch_direction(yyscan_t yyscanner)
* BACKWARD expr, BACKWARD ALL, BACKWARD
*/
static void
-complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM, yyscan_t yyscanner)
+complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
{
int tok;
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
if (tok == 0)
- yyerror(yyscanner, "unexpected end of function definition");
+ yyerror(yyllocp, yyscanner, "unexpected end of function definition");
if (tok == K_FROM || tok == K_IN)
{
@@ -3293,17 +3294,17 @@ complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM, yyscan_t yyscann
return;
}
- plpgsql_push_back_token(tok);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp);
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"FROM or IN",
- NULL, yyscanner);
+ NULL, yylvalp, yyllocp, yyscanner);
fetch->returns_multiple_rows = true;
*check_FROM = false;
}
static PLpgSQL_stmt *
-make_return_stmt(int location, yyscan_t yyscanner)
+make_return_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
{
PLpgSQL_stmt_return *new;
@@ -3316,36 +3317,36 @@ make_return_stmt(int location, yyscan_t yyscanner)
if (plpgsql_curr_compile->fn_retset)
{
- if (yylex(yyscanner) != ';')
+ if (yylex(yylvalp, yyllocp, yyscanner) != ';')
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("RETURN cannot have a parameter in function returning set"),
errhint("Use RETURN NEXT or RETURN QUERY."),
- parser_errposition(yylloc)));
+ parser_errposition(*yyllocp)));
}
else if (plpgsql_curr_compile->fn_rettype == VOIDOID)
{
- if (yylex(yyscanner) != ';')
+ if (yylex(yylvalp, yyllocp, yyscanner) != ';')
{
if (plpgsql_curr_compile->fn_prokind == PROKIND_PROCEDURE)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("RETURN cannot have a parameter in a procedure"),
- parser_errposition(yylloc)));
+ parser_errposition(*yyllocp)));
else
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("RETURN cannot have a parameter in function returning void"),
- parser_errposition(yylloc)));
+ parser_errposition(*yyllocp)));
}
}
else if (plpgsql_curr_compile->out_param_varno >= 0)
{
- if (yylex(yyscanner) != ';')
+ if (yylex(yylvalp, yyllocp, yyscanner) != ';')
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("RETURN cannot have a parameter in function with OUT parameters"),
- parser_errposition(yylloc)));
+ parser_errposition(*yyllocp)));
new->retvarno = plpgsql_curr_compile->out_param_varno;
}
else
@@ -3354,17 +3355,17 @@ make_return_stmt(int location, yyscan_t yyscanner)
* We want to special-case simple variable references for efficiency.
* So peek ahead to see if that's what we have.
*/
- int tok = yylex(yyscanner);
+ int tok = yylex(yylvalp, yyllocp, yyscanner);
if (tok == T_DATUM && plpgsql_peek(yyscanner) == ';' &&
- (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_VAR ||
- yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_PROMISE ||
- yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
- yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC))
+ (yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_VAR ||
+ yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_PROMISE ||
+ yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
+ yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_REC))
{
- new->retvarno = yylval.wdatum.datum->dno;
+ new->retvarno = yylvalp->wdatum.datum->dno;
/* eat the semicolon token that we only peeked at above */
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
Assert(tok == ';');
}
else
@@ -3375,8 +3376,8 @@ make_return_stmt(int location, yyscan_t yyscanner)
* Note that a well-formed expression is _required_ here; anything
* else is a compile-time error.
*/
- plpgsql_push_back_token(tok);
- new->expr = read_sql_expression(';', ";", yyscanner);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp);
+ new->expr = read_sql_expression(';', ";", yylvalp, yyllocp, yyscanner);
}
}
@@ -3385,7 +3386,7 @@ make_return_stmt(int location, yyscan_t yyscanner)
static PLpgSQL_stmt *
-make_return_next_stmt(int location, yyscan_t yyscanner)
+make_return_next_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
{
PLpgSQL_stmt_return_next *new;
@@ -3404,11 +3405,11 @@ make_return_next_stmt(int location, yyscan_t yyscanner)
if (plpgsql_curr_compile->out_param_varno >= 0)
{
- if (yylex(yyscanner) != ';')
+ if (yylex(yylvalp, yyllocp, yyscanner) != ';')
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("RETURN NEXT cannot have a parameter in function with OUT parameters"),
- parser_errposition(yylloc)));
+ parser_errposition(*yyllocp)));
new->retvarno = plpgsql_curr_compile->out_param_varno;
}
else
@@ -3417,17 +3418,17 @@ make_return_next_stmt(int location, yyscan_t yyscanner)
* We want to special-case simple variable references for efficiency.
* So peek ahead to see if that's what we have.
*/
- int tok = yylex(yyscanner);
+ int tok = yylex(yylvalp, yyllocp, yyscanner);
if (tok == T_DATUM && plpgsql_peek(yyscanner) == ';' &&
- (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_VAR ||
- yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_PROMISE ||
- yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
- yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC))
+ (yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_VAR ||
+ yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_PROMISE ||
+ yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
+ yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_REC))
{
- new->retvarno = yylval.wdatum.datum->dno;
+ new->retvarno = yylvalp->wdatum.datum->dno;
/* eat the semicolon token that we only peeked at above */
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
Assert(tok == ';');
}
else
@@ -3438,8 +3439,8 @@ make_return_next_stmt(int location, yyscan_t yyscanner)
* Note that a well-formed expression is _required_ here; anything
* else is a compile-time error.
*/
- plpgsql_push_back_token(tok);
- new->expr = read_sql_expression(';', ";", yyscanner);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp);
+ new->expr = read_sql_expression(';', ";", yylvalp, yyllocp, yyscanner);
}
}
@@ -3448,7 +3449,7 @@ make_return_next_stmt(int location, yyscan_t yyscanner)
static PLpgSQL_stmt *
-make_return_query_stmt(int location, yyscan_t yyscanner)
+make_return_query_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
{
PLpgSQL_stmt_return_query *new;
int tok;
@@ -3465,11 +3466,11 @@ make_return_query_stmt(int location, yyscan_t yyscanner)
new->stmtid = ++plpgsql_curr_compile->nstatements;
/* check for RETURN QUERY EXECUTE */
- if ((tok = yylex(yyscanner)) != K_EXECUTE)
+ if ((tok = yylex(yylvalp, yyllocp, yyscanner)) != K_EXECUTE)
{
/* ordinary static query */
- plpgsql_push_back_token(tok);
- new->query = read_sql_stmt(yyscanner);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp);
+ new->query = read_sql_stmt(yylvalp, yyllocp, yyscanner);
}
else
{
@@ -3477,14 +3478,14 @@ make_return_query_stmt(int location, yyscan_t yyscanner)
int term;
new->dynquery = read_sql_expression2(';', K_USING, "; or USING",
- &term, yyscanner);
+ &term, yylvalp, yyllocp, yyscanner);
if (term == K_USING)
{
do
{
PLpgSQL_expr *expr;
- expr = read_sql_expression2(',', ';', ", or ;", &term, yyscanner);
+ expr = read_sql_expression2(',', ';', ", or ;", &term, yylvalp, yyllocp, yyscanner);
new->params = lappend(new->params, expr);
} while (term == ',');
}
@@ -3538,7 +3539,7 @@ check_assignable(PLpgSQL_datum *datum, int location)
* INTO keyword.
*/
static void
-read_into_target(PLpgSQL_variable **target, bool *strict, yyscan_t yyscanner)
+read_into_target(PLpgSQL_variable **target, bool *strict, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
{
int tok;
@@ -3547,11 +3548,11 @@ read_into_target(PLpgSQL_variable **target, bool *strict, yyscan_t yyscanner)
if (strict)
*strict = false;
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
if (strict && tok == K_STRICT)
{
*strict = true;
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
}
/*
@@ -3564,30 +3565,30 @@ read_into_target(PLpgSQL_variable **target, bool *strict, yyscan_t yyscanner)
switch (tok)
{
case T_DATUM:
- if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
- yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
+ if (yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
+ yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
{
- check_assignable(yylval.wdatum.datum, yylloc);
- *target = (PLpgSQL_variable *) yylval.wdatum.datum;
+ check_assignable(yylvalp->wdatum.datum, *yyllocp);
+ *target = (PLpgSQL_variable *) yylvalp->wdatum.datum;
- if ((tok = yylex(yyscanner)) == ',')
+ if ((tok = yylex(yylvalp, yyllocp, yyscanner)) == ',')
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("record variable cannot be part of multiple-item INTO list"),
- parser_errposition(yylloc)));
- plpgsql_push_back_token(tok);
+ parser_errposition(*yyllocp)));
+ plpgsql_push_back_token(tok, yylvalp, yyllocp);
}
else
{
*target = (PLpgSQL_variable *)
- read_into_scalar_list(NameOfDatum(&(yylval.wdatum)),
- yylval.wdatum.datum, yylloc, yyscanner);
+ read_into_scalar_list(NameOfDatum(&(yylvalp->wdatum)),
+ yylvalp->wdatum.datum, *yyllocp, yylvalp, yyllocp, yyscanner);
}
break;
default:
/* just to give a better message than "syntax error" */
- current_token_is_not_variable(tok, yyscanner);
+ current_token_is_not_variable(tok, yylvalp, yyllocp, yyscanner);
}
}
@@ -3601,6 +3602,7 @@ static PLpgSQL_row *
read_into_scalar_list(char *initial_name,
PLpgSQL_datum *initial_datum,
int initial_location,
+ YYSTYPE *yylvalp, YYLTYPE *yyllocp,
yyscan_t yyscanner)
{
int nfields;
@@ -3614,34 +3616,34 @@ read_into_scalar_list(char *initial_name,
varnos[0] = initial_datum->dno;
nfields = 1;
- while ((tok = yylex(yyscanner)) == ',')
+ while ((tok = yylex(yylvalp, yyllocp, yyscanner)) == ',')
{
/* Check for array overflow */
if (nfields >= 1024)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many INTO variables specified"),
- parser_errposition(yylloc)));
+ parser_errposition(*yyllocp)));
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
switch (tok)
{
case T_DATUM:
- check_assignable(yylval.wdatum.datum, yylloc);
- if (yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
- yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
+ check_assignable(yylvalp->wdatum.datum, *yyllocp);
+ if (yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
+ yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a scalar variable",
- NameOfDatum(&(yylval.wdatum))),
- parser_errposition(yylloc)));
- fieldnames[nfields] = NameOfDatum(&(yylval.wdatum));
- varnos[nfields++] = yylval.wdatum.datum->dno;
+ NameOfDatum(&(yylvalp->wdatum))),
+ parser_errposition(*yyllocp)));
+ fieldnames[nfields] = NameOfDatum(&(yylvalp->wdatum));
+ varnos[nfields++] = yylvalp->wdatum.datum->dno;
break;
default:
/* just to give a better message than "syntax error" */
- current_token_is_not_variable(tok, yyscanner);
+ current_token_is_not_variable(tok, yylvalp, yyllocp, yyscanner);
}
}
@@ -3649,7 +3651,7 @@ read_into_scalar_list(char *initial_name,
* We read an extra, non-comma token from yylex(), so push it back onto
* the input stream
*/
- plpgsql_push_back_token(tok);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp);
row = palloc0(sizeof(PLpgSQL_row));
row->dtype = PLPGSQL_DTYPE_ROW;
@@ -3852,7 +3854,7 @@ check_labels(const char *start_label, const char *end_label, int end_location)
* parens).
*/
static PLpgSQL_expr *
-read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
+read_cursor_args(PLpgSQL_var *cursor, int until, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
{
PLpgSQL_expr *expr;
PLpgSQL_row *row;
@@ -3862,7 +3864,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
StringInfoData ds;
bool any_named = false;
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
if (cursor->cursor_explicit_argrow < 0)
{
/* No arguments expected */
@@ -3871,10 +3873,10 @@ read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cursor \"%s\" has no arguments",
cursor->refname),
- parser_errposition(yylloc)));
+ parser_errposition(*yyllocp)));
if (tok != until)
- yyerror(yyscanner, "syntax error");
+ yyerror(yyllocp, yyscanner, "syntax error");
return NULL;
}
@@ -3885,7 +3887,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cursor \"%s\" has arguments",
cursor->refname),
- parser_errposition(yylloc)));
+ parser_errposition(*yyllocp)));
/*
* Read the arguments, one by one.
@@ -3912,8 +3914,8 @@ read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
/* Read the argument name, ignoring any matching variable */
save_IdentifierLookup = plpgsql_IdentifierLookup;
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_DECLARE;
- yylex(yyscanner);
- argname = yylval.str;
+ yylex(yylvalp, yyllocp, yyscanner);
+ argname = yylvalp->str;
plpgsql_IdentifierLookup = save_IdentifierLookup;
/* Match argument name to cursor arguments */
@@ -3927,15 +3929,15 @@ read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cursor \"%s\" has no argument named \"%s\"",
cursor->refname, argname),
- parser_errposition(yylloc)));
+ parser_errposition(*yyllocp)));
/*
* Eat the ":=". We already peeked, so the error should never
* happen.
*/
- tok2 = yylex(yyscanner);
+ tok2 = yylex(yylvalp, yyllocp, yyscanner);
if (tok2 != COLON_EQUALS)
- yyerror(yyscanner, "syntax error");
+ yyerror(yyllocp, yyscanner, "syntax error");
any_named = true;
}
@@ -3960,7 +3962,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
RAW_PARSE_PLPGSQL_EXPR,
true, true,
NULL, &endtoken,
- yyscanner);
+ yylvalp, yyllocp, yyscanner);
argv[argpos] = item->query;
@@ -3969,14 +3971,14 @@ read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("not enough arguments for cursor \"%s\"",
cursor->refname),
- parser_errposition(yylloc)));
+ parser_errposition(*yyllocp)));
if (endtoken == ',' && (argc == row->nfields - 1))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("too many arguments for cursor \"%s\"",
cursor->refname),
- parser_errposition(yylloc)));
+ parser_errposition(*yyllocp)));
}
/* Make positional argument list */
@@ -4007,9 +4009,9 @@ read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
pfree(ds.data);
/* Next we'd better find the until token */
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
if (tok != until)
- yyerror(yyscanner, "syntax error");
+ yyerror(yyllocp, yyscanner, "syntax error");
return expr;
}
@@ -4018,7 +4020,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, yyscan_t yyscanner)
* Parse RAISE ... USING options
*/
static List *
-read_raise_options(yyscan_t yyscanner)
+read_raise_options(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
{
List *result = NIL;
@@ -4027,46 +4029,46 @@ read_raise_options(yyscan_t yyscanner)
PLpgSQL_raise_option *opt;
int tok;
- if ((tok = yylex(yyscanner)) == 0)
- yyerror(yyscanner, "unexpected end of function definition");
+ if ((tok = yylex(yylvalp, yyllocp, yyscanner)) == 0)
+ yyerror(yyllocp, yyscanner, "unexpected end of function definition");
opt = (PLpgSQL_raise_option *) palloc(sizeof(PLpgSQL_raise_option));
- if (tok_is_keyword(tok, &yylval,
+ if (tok_is_keyword(tok, yylvalp,
K_ERRCODE, "errcode"))
opt->opt_type = PLPGSQL_RAISEOPTION_ERRCODE;
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_MESSAGE, "message"))
opt->opt_type = PLPGSQL_RAISEOPTION_MESSAGE;
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_DETAIL, "detail"))
opt->opt_type = PLPGSQL_RAISEOPTION_DETAIL;
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_HINT, "hint"))
opt->opt_type = PLPGSQL_RAISEOPTION_HINT;
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_COLUMN, "column"))
opt->opt_type = PLPGSQL_RAISEOPTION_COLUMN;
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_CONSTRAINT, "constraint"))
opt->opt_type = PLPGSQL_RAISEOPTION_CONSTRAINT;
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_DATATYPE, "datatype"))
opt->opt_type = PLPGSQL_RAISEOPTION_DATATYPE;
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_TABLE, "table"))
opt->opt_type = PLPGSQL_RAISEOPTION_TABLE;
- else if (tok_is_keyword(tok, &yylval,
+ else if (tok_is_keyword(tok, yylvalp,
K_SCHEMA, "schema"))
opt->opt_type = PLPGSQL_RAISEOPTION_SCHEMA;
else
- yyerror(yyscanner, "unrecognized RAISE statement option");
+ yyerror(yyllocp, yyscanner, "unrecognized RAISE statement option");
- tok = yylex(yyscanner);
+ tok = yylex(yylvalp, yyllocp, yyscanner);
if (tok != '=' && tok != COLON_EQUALS)
- yyerror(yyscanner, "syntax error, expected \"=\"");
+ yyerror(yyllocp, yyscanner, "syntax error, expected \"=\"");
- opt->expr = read_sql_expression2(',', ';', ", or ;", &tok, yyscanner);
+ opt->expr = read_sql_expression2(',', ';', ", or ;", &tok, yylvalp, yyllocp, yyscanner);
result = lappend(result, opt);
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
index eeb40c301db..abf2d40c07a 100644
--- a/src/pl/plpgsql/src/pl_scanner.c
+++ b/src/pl/plpgsql/src/pl_scanner.c
@@ -142,7 +142,7 @@ static void location_lineno_init(void);
* matches one of those.
*/
int
-plpgsql_yylex(yyscan_t yyscanner)
+plpgsql_yylex(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
{
int tok1;
TokenAuxData aux1;
@@ -296,8 +296,8 @@ plpgsql_yylex(yyscan_t yyscanner)
*/
}
- plpgsql_yylval = aux1.lval;
- plpgsql_yylloc = aux1.lloc;
+ *yylvalp = aux1.lval;
+ *yyllocp = aux1.lloc;
plpgsql_yyleng = aux1.leng;
plpgsql_yytoken = tok1;
return tok1;
@@ -383,12 +383,12 @@ push_back_token(int token, TokenAuxData *auxdata)
* is not a good idea to push back a token code other than what you read.
*/
void
-plpgsql_push_back_token(int token)
+plpgsql_push_back_token(int token, YYSTYPE *yylvalp, YYLTYPE *yyllocp)
{
TokenAuxData auxdata;
- auxdata.lval = plpgsql_yylval;
- auxdata.lloc = plpgsql_yylloc;
+ auxdata.lval = *yylvalp;
+ auxdata.lloc = *yyllocp;
auxdata.leng = plpgsql_yyleng;
push_back_token(token, &auxdata);
}
@@ -512,9 +512,9 @@ plpgsql_scanner_errposition(int location)
* be misleading!
*/
void
-plpgsql_yyerror(yyscan_t yyscanner, const char *message)
+plpgsql_yyerror(YYLTYPE *yyllocp, yyscan_t yyscanner, const char *message)
{
- char *yytext = core_yy_extra.scanbuf + plpgsql_yylloc;
+ char *yytext = core_yy_extra.scanbuf + *yyllocp;
if (*yytext == '\0')
{
@@ -522,7 +522,7 @@ plpgsql_yyerror(yyscan_t yyscanner, const char *message)
(errcode(ERRCODE_SYNTAX_ERROR),
/* translator: %s is typically the translation of "syntax error" */
errmsg("%s at end of input", _(message)),
- plpgsql_scanner_errposition(plpgsql_yylloc)));
+ plpgsql_scanner_errposition(*yyllocp)));
}
else
{
@@ -538,7 +538,7 @@ plpgsql_yyerror(yyscan_t yyscanner, const char *message)
(errcode(ERRCODE_SYNTAX_ERROR),
/* translator: first %s is typically the translation of "syntax error" */
errmsg("%s at or near \"%s\"", _(message), yytext),
- plpgsql_scanner_errposition(plpgsql_yylloc)));
+ plpgsql_scanner_errposition(*yyllocp)));
}
}
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 07dcfa84f88..151ae7943df 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -1315,13 +1315,15 @@ extern void plpgsql_dumptree(PLpgSQL_function *func);
/*
* Scanner functions in pl_scanner.c
*/
+union YYSTYPE;
+#define YYLTYPE int
#ifndef YY_TYPEDEF_YY_SCANNER_T
#define YY_TYPEDEF_YY_SCANNER_T
typedef void *yyscan_t;
#endif
-extern int plpgsql_yylex(yyscan_t yyscanner);
+extern int plpgsql_yylex(union YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
extern int plpgsql_token_length(void);
-extern void plpgsql_push_back_token(int token);
+extern void plpgsql_push_back_token(int token, union YYSTYPE *yylvalp, YYLTYPE *yyllocp);
extern bool plpgsql_token_is_unreserved_keyword(int token);
extern void plpgsql_append_source_text(StringInfo buf,
int startlocation, int endlocation);
@@ -1329,7 +1331,7 @@ extern int plpgsql_peek(yyscan_t yyscanner);
extern void plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc,
int *tok2_loc, yyscan_t yyscanner);
extern int plpgsql_scanner_errposition(int location);
-extern void plpgsql_yyerror(yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
+extern void plpgsql_yyerror(YYLTYPE *yyllocp, yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
extern int plpgsql_location_to_lineno(int location);
extern int plpgsql_latest_lineno(void);
extern yyscan_t plpgsql_scanner_init(const char *str);
--
2.47.1
v3-0004-plpgsql-yyextra.patchtext/plain; charset=UTF-8; name=v3-0004-plpgsql-yyextra.patchDownload
From 4c3e4f0da9a2fa1f646da9362b39974e7e44117e Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Thu, 26 Dec 2024 17:01:39 +0100
Subject: [PATCH v3 4/4] plpgsql: yyextra
---
src/pl/plpgsql/src/pl_comp.c | 33 +++--
src/pl/plpgsql/src/pl_gram.y | 234 ++++++++++++++++----------------
src/pl/plpgsql/src/pl_scanner.c | 189 ++++++++++++++------------
src/pl/plpgsql/src/plpgsql.h | 13 +-
4 files changed, 250 insertions(+), 219 deletions(-)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 57685c4e948..f443423b98c 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -237,6 +237,12 @@ plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator)
return function;
}
+struct compile_error_callback_arg
+{
+ const char *proc_source;
+ yyscan_t yyscanner;
+};
+
/*
* This is the slow part of plpgsql_compile().
*
@@ -277,6 +283,7 @@ do_compile(FunctionCallInfo fcinfo,
PLpgSQL_variable *var;
PLpgSQL_rec *rec;
int i;
+ struct compile_error_callback_arg cbarg;
ErrorContextCallback plerrcontext;
int parse_rc;
Oid rettypeid;
@@ -291,9 +298,7 @@ do_compile(FunctionCallInfo fcinfo,
MemoryContext func_cxt;
/*
- * Setup the scanner input and error info. We assume that this function
- * cannot be invoked recursively, so there's no need to save and restore
- * the static variables used here.
+ * Setup the scanner input and error info.
*/
prosrcdatum = SysCacheGetAttrNotNull(PROCOID, procTup, Anum_pg_proc_prosrc);
proc_source = TextDatumGetCString(prosrcdatum);
@@ -304,8 +309,10 @@ do_compile(FunctionCallInfo fcinfo,
/*
* Setup error traceback support for ereport()
*/
+ cbarg.proc_source = forValidator ? proc_source : NULL;
+ cbarg.yyscanner = scanner;
plerrcontext.callback = plpgsql_compile_error_callback;
- plerrcontext.arg = forValidator ? proc_source : NULL;
+ plerrcontext.arg = &cbarg;
plerrcontext.previous = error_context_stack;
error_context_stack = &plerrcontext;
@@ -845,15 +852,14 @@ plpgsql_compile_inline(char *proc_source)
yyscan_t scanner;
char *func_name = "inline_code_block";
PLpgSQL_function *function;
+ struct compile_error_callback_arg cbarg;
ErrorContextCallback plerrcontext;
PLpgSQL_variable *var;
int parse_rc;
MemoryContext func_cxt;
/*
- * Setup the scanner input and error info. We assume that this function
- * cannot be invoked recursively, so there's no need to save and restore
- * the static variables used here.
+ * Setup the scanner input and error info.
*/
scanner = plpgsql_scanner_init(proc_source);
@@ -862,8 +868,10 @@ plpgsql_compile_inline(char *proc_source)
/*
* Setup error traceback support for ereport()
*/
+ cbarg.proc_source = proc_source;
+ cbarg.yyscanner = scanner;
plerrcontext.callback = plpgsql_compile_error_callback;
- plerrcontext.arg = proc_source;
+ plerrcontext.arg = &cbarg;
plerrcontext.previous = error_context_stack;
error_context_stack = &plerrcontext;
@@ -980,13 +988,16 @@ plpgsql_compile_inline(char *proc_source)
static void
plpgsql_compile_error_callback(void *arg)
{
- if (arg)
+ struct compile_error_callback_arg *cbarg = (struct compile_error_callback_arg *) arg;
+ yyscan_t yyscanner = cbarg->yyscanner;
+
+ if (cbarg->proc_source)
{
/*
* Try to convert syntax error position to reference text of original
* CREATE FUNCTION or DO command.
*/
- if (function_parse_error_transpose((const char *) arg))
+ if (function_parse_error_transpose(cbarg->proc_source))
return;
/*
@@ -997,7 +1008,7 @@ plpgsql_compile_error_callback(void *arg)
if (plpgsql_error_funcname)
errcontext("compilation of PL/pgSQL function \"%s\" near line %d",
- plpgsql_error_funcname, plpgsql_latest_lineno());
+ plpgsql_error_funcname, plpgsql_latest_lineno(yyscanner));
}
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index cb5c2dca186..5f712198e5c 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -49,16 +49,17 @@
typedef struct
{
int location;
+ yyscan_t yyscanner;
} sql_error_callback_arg;
-#define parser_errposition(pos) plpgsql_scanner_errposition(pos)
+#define parser_errposition(pos) plpgsql_scanner_errposition(pos, yyscanner)
union YYSTYPE; /* need forward reference for tok_is_keyword */
static bool tok_is_keyword(int token, union YYSTYPE *lval,
int kw_token, const char *kw_str);
-static void word_is_not_variable(PLword *word, int location);
-static void cword_is_not_variable(PLcword *cword, int location);
+static void word_is_not_variable(PLword *word, int location, yyscan_t yyscanner);
+static void cword_is_not_variable(PLcword *cword, int location, yyscan_t yyscanner);
static void current_token_is_not_variable(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
static PLpgSQL_expr *read_sql_construct(int until,
int until2,
@@ -87,9 +88,9 @@ static PLpgSQL_stmt *make_return_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *y
static PLpgSQL_stmt *make_return_next_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
static PLpgSQL_stmt *make_return_query_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
static PLpgSQL_stmt *make_case(int location, PLpgSQL_expr *t_expr,
- List *case_when_list, List *else_stmts);
+ List *case_when_list, List *else_stmts, yyscan_t yyscanner);
static char *NameOfDatum(PLwdatum *wdatum);
-static void check_assignable(PLpgSQL_datum *datum, int location);
+static void check_assignable(PLpgSQL_datum *datum, int location, yyscan_t yyscanner);
static void read_into_target(PLpgSQL_variable **target, bool *strict,
YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
static PLpgSQL_row *read_into_scalar_list(char *initial_name,
@@ -99,14 +100,15 @@ static PLpgSQL_row *read_into_scalar_list(char *initial_name,
yyscan_t yyscanner);
static PLpgSQL_row *make_scalar_list1(char *initial_name,
PLpgSQL_datum *initial_datum,
- int lineno, int location);
+ int lineno, int location, yyscan_t yyscanner);
static void check_sql_expr(const char *stmt,
- RawParseMode parseMode, int location);
+ RawParseMode parseMode, int location, yyscan_t yyscanner);
static void plpgsql_sql_error_callback(void *arg);
static PLpgSQL_type *parse_datatype(const char *string, int location);
static void check_labels(const char *start_label,
const char *end_label,
- int end_location);
+ int end_location,
+ yyscan_t yyscanner);
static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor, int until,
YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
static List *read_raise_options(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
@@ -421,7 +423,7 @@ pl_block : decl_sect K_BEGIN proc_sect exception_sect K_END opt_label
new = palloc0(sizeof(PLpgSQL_stmt_block));
new->cmd_type = PLPGSQL_STMT_BLOCK;
- new->lineno = plpgsql_location_to_lineno(@2);
+ new->lineno = plpgsql_location_to_lineno(@2, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->label = $1.label;
new->n_initvars = $1.n_initvars;
@@ -429,7 +431,7 @@ pl_block : decl_sect K_BEGIN proc_sect exception_sect K_END opt_label
new->body = $3;
new->exceptions = $4;
- check_labels($1.label, $6, @6);
+ check_labels($1.label, $6, @6, yyscanner);
plpgsql_ns_pop();
$$ = (PLpgSQL_stmt *) new;
@@ -597,7 +599,7 @@ decl_cursor_args :
new = palloc0(sizeof(PLpgSQL_row));
new->dtype = PLPGSQL_DTYPE_ROW;
new->refname = "(unnamed row)";
- new->lineno = plpgsql_location_to_lineno(@1);
+ new->lineno = plpgsql_location_to_lineno(@1, yyscanner);
new->rowtupdesc = NULL;
new->nfields = list_length($2);
new->fieldnames = palloc(new->nfields * sizeof(char *));
@@ -701,7 +703,7 @@ decl_aliasitem : T_WORD
decl_varname : T_WORD
{
$$.name = $1.ident;
- $$.lineno = plpgsql_location_to_lineno(@1);
+ $$.lineno = plpgsql_location_to_lineno(@1, yyscanner);
/*
* Check to make sure name isn't already declared
* in the current block.
@@ -729,7 +731,7 @@ decl_varname : T_WORD
| unreserved_keyword
{
$$.name = pstrdup($1);
- $$.lineno = plpgsql_location_to_lineno(@1);
+ $$.lineno = plpgsql_location_to_lineno(@1, yyscanner);
/*
* Check to make sure name isn't already declared
* in the current block.
@@ -888,9 +890,9 @@ stmt_perform : K_PERFORM
new = palloc0(sizeof(PLpgSQL_stmt_perform));
new->cmd_type = PLPGSQL_STMT_PERFORM;
- new->lineno = plpgsql_location_to_lineno(@1);
+ new->lineno = plpgsql_location_to_lineno(@1, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
- plpgsql_push_back_token(K_PERFORM, &yylval, &yylloc);
+ plpgsql_push_back_token(K_PERFORM, &yylval, &yylloc, yyscanner);
/*
* Since PERFORM isn't legal SQL, we have to cheat to
@@ -912,7 +914,7 @@ stmt_perform : K_PERFORM
strlen(new->expr->query));
/* offset syntax error position to account for that */
check_sql_expr(new->expr->query, new->expr->parseMode,
- startloc + 1);
+ startloc + 1, yyscanner);
$$ = (PLpgSQL_stmt *) new;
}
@@ -924,9 +926,9 @@ stmt_call : K_CALL
new = palloc0(sizeof(PLpgSQL_stmt_call));
new->cmd_type = PLPGSQL_STMT_CALL;
- new->lineno = plpgsql_location_to_lineno(@1);
+ new->lineno = plpgsql_location_to_lineno(@1, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
- plpgsql_push_back_token(K_CALL, &yylval, &yylloc);
+ plpgsql_push_back_token(K_CALL, &yylval, &yylloc, yyscanner);
new->expr = read_sql_stmt(&yylval, &yylloc, yyscanner);
new->is_call = true;
@@ -943,9 +945,9 @@ stmt_call : K_CALL
new = palloc0(sizeof(PLpgSQL_stmt_call));
new->cmd_type = PLPGSQL_STMT_CALL;
- new->lineno = plpgsql_location_to_lineno(@1);
+ new->lineno = plpgsql_location_to_lineno(@1, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
- plpgsql_push_back_token(K_DO, &yylval, &yylloc);
+ plpgsql_push_back_token(K_DO, &yylval, &yylloc, yyscanner);
new->expr = read_sql_stmt(&yylval, &yylloc, yyscanner);
new->is_call = false;
@@ -979,14 +981,14 @@ stmt_assign : T_DATUM
pmode = 0; /* keep compiler quiet */
}
- check_assignable($1.datum, @1);
+ check_assignable($1.datum, @1, yyscanner);
new = palloc0(sizeof(PLpgSQL_stmt_assign));
new->cmd_type = PLPGSQL_STMT_ASSIGN;
- new->lineno = plpgsql_location_to_lineno(@1);
+ new->lineno = plpgsql_location_to_lineno(@1, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->varno = $1.datum->dno;
/* Push back the head name to include it in the stmt */
- plpgsql_push_back_token(T_DATUM, &yylval, &yylloc);
+ plpgsql_push_back_token(T_DATUM, &yylval, &yylloc, yyscanner);
new->expr = read_sql_construct(';', 0, 0, ";",
pmode,
false, true,
@@ -1004,7 +1006,7 @@ stmt_getdiag : K_GET getdiag_area_opt K_DIAGNOSTICS getdiag_list ';'
new = palloc0(sizeof(PLpgSQL_stmt_getdiag));
new->cmd_type = PLPGSQL_STMT_GETDIAG;
- new->lineno = plpgsql_location_to_lineno(@1);
+ new->lineno = plpgsql_location_to_lineno(@1, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->is_stacked = $2;
new->diag_items = $4;
@@ -1159,18 +1161,18 @@ getdiag_target : T_DATUM
errmsg("\"%s\" is not a scalar variable",
NameOfDatum(&($1))),
parser_errposition(@1)));
- check_assignable($1.datum, @1);
+ check_assignable($1.datum, @1, yyscanner);
$$ = $1.datum;
}
| T_WORD
{
/* just to give a better message than "syntax error" */
- word_is_not_variable(&($1), @1);
+ word_is_not_variable(&($1), @1, yyscanner);
}
| T_CWORD
{
/* just to give a better message than "syntax error" */
- cword_is_not_variable(&($1), @1);
+ cword_is_not_variable(&($1), @1, yyscanner);
}
;
@@ -1180,7 +1182,7 @@ stmt_if : K_IF expr_until_then proc_sect stmt_elsifs stmt_else K_END K_IF ';'
new = palloc0(sizeof(PLpgSQL_stmt_if));
new->cmd_type = PLPGSQL_STMT_IF;
- new->lineno = plpgsql_location_to_lineno(@1);
+ new->lineno = plpgsql_location_to_lineno(@1, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->cond = $2;
new->then_body = $3;
@@ -1200,7 +1202,7 @@ stmt_elsifs :
PLpgSQL_if_elsif *new;
new = palloc0(sizeof(PLpgSQL_if_elsif));
- new->lineno = plpgsql_location_to_lineno(@2);
+ new->lineno = plpgsql_location_to_lineno(@2, yyscanner);
new->cond = $3;
new->stmts = $4;
@@ -1220,7 +1222,7 @@ stmt_else :
stmt_case : K_CASE opt_expr_until_when case_when_list opt_case_else K_END K_CASE ';'
{
- $$ = make_case(@1, $2, $3, $4);
+ $$ = make_case(@1, $2, $3, $4, yyscanner);
}
;
@@ -1231,10 +1233,10 @@ opt_expr_until_when :
if (tok != K_WHEN)
{
- plpgsql_push_back_token(tok, &yylval, &yylloc);
+ plpgsql_push_back_token(tok, &yylval, &yylloc, yyscanner);
expr = read_sql_expression(K_WHEN, "WHEN", &yylval, &yylloc, yyscanner);
}
- plpgsql_push_back_token(K_WHEN, &yylval, &yylloc);
+ plpgsql_push_back_token(K_WHEN, &yylval, &yylloc, yyscanner);
$$ = expr;
}
;
@@ -1253,7 +1255,7 @@ case_when : K_WHEN expr_until_then proc_sect
{
PLpgSQL_case_when *new = palloc(sizeof(PLpgSQL_case_when));
- new->lineno = plpgsql_location_to_lineno(@1);
+ new->lineno = plpgsql_location_to_lineno(@1, yyscanner);
new->expr = $2;
new->stmts = $3;
$$ = new;
@@ -1285,12 +1287,12 @@ stmt_loop : opt_loop_label K_LOOP loop_body
new = palloc0(sizeof(PLpgSQL_stmt_loop));
new->cmd_type = PLPGSQL_STMT_LOOP;
- new->lineno = plpgsql_location_to_lineno(@2);
+ new->lineno = plpgsql_location_to_lineno(@2, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->label = $1;
new->body = $3.stmts;
- check_labels($1, $3.end_label, $3.end_label_location);
+ check_labels($1, $3.end_label, $3.end_label_location, yyscanner);
plpgsql_ns_pop();
$$ = (PLpgSQL_stmt *) new;
@@ -1303,13 +1305,13 @@ stmt_while : opt_loop_label K_WHILE expr_until_loop loop_body
new = palloc0(sizeof(PLpgSQL_stmt_while));
new->cmd_type = PLPGSQL_STMT_WHILE;
- new->lineno = plpgsql_location_to_lineno(@2);
+ new->lineno = plpgsql_location_to_lineno(@2, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->label = $1;
new->cond = $3;
new->body = $4.stmts;
- check_labels($1, $4.end_label, $4.end_label_location);
+ check_labels($1, $4.end_label, $4.end_label_location, yyscanner);
plpgsql_ns_pop();
$$ = (PLpgSQL_stmt *) new;
@@ -1324,7 +1326,7 @@ stmt_for : opt_loop_label K_FOR for_control loop_body
PLpgSQL_stmt_fori *new;
new = (PLpgSQL_stmt_fori *) $3;
- new->lineno = plpgsql_location_to_lineno(@2);
+ new->lineno = plpgsql_location_to_lineno(@2, yyscanner);
new->label = $1;
new->body = $4.stmts;
$$ = (PLpgSQL_stmt *) new;
@@ -1338,13 +1340,13 @@ stmt_for : opt_loop_label K_FOR for_control loop_body
$3->cmd_type == PLPGSQL_STMT_DYNFORS);
/* forq is the common supertype of all three */
new = (PLpgSQL_stmt_forq *) $3;
- new->lineno = plpgsql_location_to_lineno(@2);
+ new->lineno = plpgsql_location_to_lineno(@2, yyscanner);
new->label = $1;
new->body = $4.stmts;
$$ = (PLpgSQL_stmt *) new;
}
- check_labels($1, $4.end_label, $4.end_label_location);
+ check_labels($1, $4.end_label, $4.end_label_location, yyscanner);
/* close namespace started in opt_loop_label */
plpgsql_ns_pop();
}
@@ -1372,14 +1374,14 @@ for_control : for_variable K_IN
if ($1.row)
{
new->var = (PLpgSQL_variable *) $1.row;
- check_assignable($1.row, @1);
+ check_assignable($1.row, @1, yyscanner);
}
else if ($1.scalar)
{
/* convert single scalar to list */
new->var = (PLpgSQL_variable *)
make_scalar_list1($1.name, $1.scalar,
- $1.lineno, @1);
+ $1.lineno, @1, yyscanner);
/* make_scalar_list1 did check_assignable */
}
else
@@ -1466,7 +1468,7 @@ for_control : for_variable K_IN
K_REVERSE, "reverse"))
reverse = true;
else
- plpgsql_push_back_token(tok, &yylval, &yylloc);
+ plpgsql_push_back_token(tok, &yylval, &yylloc, yyscanner);
/*
* Read tokens until we see either a ".."
@@ -1500,7 +1502,7 @@ for_control : for_variable K_IN
*/
expr1->parseMode = RAW_PARSE_PLPGSQL_EXPR;
check_sql_expr(expr1->query, expr1->parseMode,
- expr1loc);
+ expr1loc, yyscanner);
/* Read and check the second one */
expr2 = read_sql_expression2(K_LOOP, K_BY,
@@ -1557,7 +1559,7 @@ for_control : for_variable K_IN
/* Check syntax as a regular query */
check_sql_expr(expr1->query, expr1->parseMode,
- expr1loc);
+ expr1loc, yyscanner);
new = palloc0(sizeof(PLpgSQL_stmt_fors));
new->cmd_type = PLPGSQL_STMT_FORS;
@@ -1565,14 +1567,14 @@ for_control : for_variable K_IN
if ($1.row)
{
new->var = (PLpgSQL_variable *) $1.row;
- check_assignable($1.row, @1);
+ check_assignable($1.row, @1, yyscanner);
}
else if ($1.scalar)
{
/* convert single scalar to list */
new->var = (PLpgSQL_variable *)
make_scalar_list1($1.name, $1.scalar,
- $1.lineno, @1);
+ $1.lineno, @1, yyscanner);
/* make_scalar_list1 did check_assignable */
}
else
@@ -1611,7 +1613,7 @@ for_control : for_variable K_IN
for_variable : T_DATUM
{
$$.name = NameOfDatum(&($1));
- $$.lineno = plpgsql_location_to_lineno(@1);
+ $$.lineno = plpgsql_location_to_lineno(@1, yyscanner);
if ($1.datum->dtype == PLPGSQL_DTYPE_ROW ||
$1.datum->dtype == PLPGSQL_DTYPE_REC)
{
@@ -1626,7 +1628,7 @@ for_variable : T_DATUM
$$.row = NULL;
/* check for comma-separated list */
tok = yylex(&yylval, &yylloc, yyscanner);
- plpgsql_push_back_token(tok, &yylval, &yylloc);
+ plpgsql_push_back_token(tok, &yylval, &yylloc, yyscanner);
if (tok == ',')
$$.row = (PLpgSQL_datum *)
read_into_scalar_list($$.name,
@@ -1641,19 +1643,19 @@ for_variable : T_DATUM
int tok;
$$.name = $1.ident;
- $$.lineno = plpgsql_location_to_lineno(@1);
+ $$.lineno = plpgsql_location_to_lineno(@1, yyscanner);
$$.scalar = NULL;
$$.row = NULL;
/* check for comma-separated list */
tok = yylex(&yylval, &yylloc, yyscanner);
- plpgsql_push_back_token(tok, &yylval, &yylloc);
+ plpgsql_push_back_token(tok, &yylval, &yylloc, yyscanner);
if (tok == ',')
- word_is_not_variable(&($1), @1);
+ word_is_not_variable(&($1), @1, yyscanner);
}
| T_CWORD
{
/* just to give a better message than "syntax error" */
- cword_is_not_variable(&($1), @1);
+ cword_is_not_variable(&($1), @1, yyscanner);
}
;
@@ -1663,7 +1665,7 @@ stmt_foreach_a : opt_loop_label K_FOREACH for_variable foreach_slice K_IN K_ARRA
new = palloc0(sizeof(PLpgSQL_stmt_foreach_a));
new->cmd_type = PLPGSQL_STMT_FOREACH_A;
- new->lineno = plpgsql_location_to_lineno(@2);
+ new->lineno = plpgsql_location_to_lineno(@2, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->label = $1;
new->slice = $4;
@@ -1673,12 +1675,12 @@ stmt_foreach_a : opt_loop_label K_FOREACH for_variable foreach_slice K_IN K_ARRA
if ($3.row)
{
new->varno = $3.row->dno;
- check_assignable($3.row, @3);
+ check_assignable($3.row, @3, yyscanner);
}
else if ($3.scalar)
{
new->varno = $3.scalar->dno;
- check_assignable($3.scalar, @3);
+ check_assignable($3.scalar, @3, yyscanner);
}
else
{
@@ -1688,7 +1690,7 @@ stmt_foreach_a : opt_loop_label K_FOREACH for_variable foreach_slice K_IN K_ARRA
parser_errposition(@3)));
}
- check_labels($1, $8.end_label, $8.end_label_location);
+ check_labels($1, $8.end_label, $8.end_label_location, yyscanner);
plpgsql_ns_pop();
$$ = (PLpgSQL_stmt *) new;
@@ -1713,7 +1715,7 @@ stmt_exit : exit_type opt_label opt_exitcond
new->cmd_type = PLPGSQL_STMT_EXIT;
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->is_exit = $1;
- new->lineno = plpgsql_location_to_lineno(@1);
+ new->lineno = plpgsql_location_to_lineno(@1, yyscanner);
new->label = $2;
new->cond = $3;
@@ -1788,7 +1790,7 @@ stmt_return : K_RETURN
}
else
{
- plpgsql_push_back_token(tok, &yylval, &yylloc);
+ plpgsql_push_back_token(tok, &yylval, &yylloc, yyscanner);
$$ = make_return_stmt(@1, &yylval, &yylloc, yyscanner);
}
}
@@ -1802,7 +1804,7 @@ stmt_raise : K_RAISE
new = palloc(sizeof(PLpgSQL_stmt_raise));
new->cmd_type = PLPGSQL_STMT_RAISE;
- new->lineno = plpgsql_location_to_lineno(@1);
+ new->lineno = plpgsql_location_to_lineno(@1, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->elog_level = ERROR; /* default */
new->condname = NULL;
@@ -1948,7 +1950,7 @@ stmt_assert : K_ASSERT
new = palloc(sizeof(PLpgSQL_stmt_assert));
new->cmd_type = PLPGSQL_STMT_ASSERT;
- new->lineno = plpgsql_location_to_lineno(@1);
+ new->lineno = plpgsql_location_to_lineno(@1, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->cond = read_sql_expression2(',', ';',
@@ -1999,10 +2001,10 @@ stmt_execsql : K_IMPORT
int tok;
tok = yylex(&yylval, &yylloc, yyscanner);
- plpgsql_push_back_token(tok, &yylval, &yylloc);
+ plpgsql_push_back_token(tok, &yylval, &yylloc, yyscanner);
if (tok == '=' || tok == COLON_EQUALS ||
tok == '[' || tok == '.')
- word_is_not_variable(&($1), @1);
+ word_is_not_variable(&($1), @1, yyscanner);
$$ = make_execsql_stmt(T_WORD, @1, &($1), &yylval, &yylloc, yyscanner);
}
| T_CWORD
@@ -2010,10 +2012,10 @@ stmt_execsql : K_IMPORT
int tok;
tok = yylex(&yylval, &yylloc, yyscanner);
- plpgsql_push_back_token(tok, &yylval, &yylloc);
+ plpgsql_push_back_token(tok, &yylval, &yylloc, yyscanner);
if (tok == '=' || tok == COLON_EQUALS ||
tok == '[' || tok == '.')
- cword_is_not_variable(&($1), @1);
+ cword_is_not_variable(&($1), @1, yyscanner);
$$ = make_execsql_stmt(T_CWORD, @1, NULL, &yylval, &yylloc, yyscanner);
}
;
@@ -2033,7 +2035,7 @@ stmt_dynexecute : K_EXECUTE
new = palloc(sizeof(PLpgSQL_stmt_dynexecute));
new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
- new->lineno = plpgsql_location_to_lineno(@1);
+ new->lineno = plpgsql_location_to_lineno(@1, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->query = expr;
new->into = false;
@@ -2091,7 +2093,7 @@ stmt_open : K_OPEN cursor_variable
new = palloc0(sizeof(PLpgSQL_stmt_open));
new->cmd_type = PLPGSQL_STMT_OPEN;
- new->lineno = plpgsql_location_to_lineno(@1);
+ new->lineno = plpgsql_location_to_lineno(@1, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->curvar = $2->dno;
new->cursor_options = CURSOR_OPT_FAST_PLAN;
@@ -2148,7 +2150,7 @@ stmt_open : K_OPEN cursor_variable
}
else
{
- plpgsql_push_back_token(tok, &yylval, &yylloc);
+ plpgsql_push_back_token(tok, &yylval, &yylloc, yyscanner);
new->query = read_sql_stmt(&yylval, &yylloc, yyscanner);
}
}
@@ -2183,7 +2185,7 @@ stmt_fetch : K_FETCH opt_fetch_direction cursor_variable K_INTO
errmsg("FETCH statement cannot return multiple rows"),
parser_errposition(@1)));
- fetch->lineno = plpgsql_location_to_lineno(@1);
+ fetch->lineno = plpgsql_location_to_lineno(@1, yyscanner);
fetch->target = target;
fetch->curvar = $3->dno;
fetch->is_move = false;
@@ -2196,7 +2198,7 @@ stmt_move : K_MOVE opt_fetch_direction cursor_variable ';'
{
PLpgSQL_stmt_fetch *fetch = $2;
- fetch->lineno = plpgsql_location_to_lineno(@1);
+ fetch->lineno = plpgsql_location_to_lineno(@1, yyscanner);
fetch->curvar = $3->dno;
fetch->is_move = true;
@@ -2216,7 +2218,7 @@ stmt_close : K_CLOSE cursor_variable ';'
new = palloc(sizeof(PLpgSQL_stmt_close));
new->cmd_type = PLPGSQL_STMT_CLOSE;
- new->lineno = plpgsql_location_to_lineno(@1);
+ new->lineno = plpgsql_location_to_lineno(@1, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->curvar = $2->dno;
@@ -2237,7 +2239,7 @@ stmt_commit : K_COMMIT opt_transaction_chain ';'
new = palloc(sizeof(PLpgSQL_stmt_commit));
new->cmd_type = PLPGSQL_STMT_COMMIT;
- new->lineno = plpgsql_location_to_lineno(@1);
+ new->lineno = plpgsql_location_to_lineno(@1, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->chain = $2;
@@ -2251,7 +2253,7 @@ stmt_rollback : K_ROLLBACK opt_transaction_chain ';'
new = palloc(sizeof(PLpgSQL_stmt_rollback));
new->cmd_type = PLPGSQL_STMT_ROLLBACK;
- new->lineno = plpgsql_location_to_lineno(@1);
+ new->lineno = plpgsql_location_to_lineno(@1, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->chain = $2;
@@ -2291,12 +2293,12 @@ cursor_variable : T_DATUM
| T_WORD
{
/* just to give a better message than "syntax error" */
- word_is_not_variable(&($1), @1);
+ word_is_not_variable(&($1), @1, yyscanner);
}
| T_CWORD
{
/* just to give a better message than "syntax error" */
- cword_is_not_variable(&($1), @1);
+ cword_is_not_variable(&($1), @1, yyscanner);
}
;
@@ -2311,7 +2313,7 @@ exception_sect :
* scope of the names extends to the end of the
* current block.
*/
- int lineno = plpgsql_location_to_lineno(@1);
+ int lineno = plpgsql_location_to_lineno(@1, yyscanner);
PLpgSQL_exception_block *new = palloc(sizeof(PLpgSQL_exception_block));
PLpgSQL_variable *var;
@@ -2359,7 +2361,7 @@ proc_exception : K_WHEN proc_conditions K_THEN proc_sect
PLpgSQL_exception *new;
new = palloc0(sizeof(PLpgSQL_exception));
- new->lineno = plpgsql_location_to_lineno(@1);
+ new->lineno = plpgsql_location_to_lineno(@1, yyscanner);
new->conditions = $2;
new->action = $4;
@@ -2611,7 +2613,7 @@ tok_is_keyword(int token, union YYSTYPE *lval,
* ie, unrecognized variable.
*/
static void
-word_is_not_variable(PLword *word, int location)
+word_is_not_variable(PLword *word, int location, yyscan_t yyscanner)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -2622,7 +2624,7 @@ word_is_not_variable(PLword *word, int location)
/* Same, for a CWORD */
static void
-cword_is_not_variable(PLcword *cword, int location)
+cword_is_not_variable(PLcword *cword, int location, yyscan_t yyscanner)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -2640,9 +2642,9 @@ static void
current_token_is_not_variable(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
{
if (tok == T_WORD)
- word_is_not_variable(&(yylvalp->word), *yyllocp);
+ word_is_not_variable(&(yylvalp->word), *yyllocp, yyscanner);
else if (tok == T_CWORD)
- cword_is_not_variable(&(yylvalp->cword), *yyllocp);
+ cword_is_not_variable(&(yylvalp->cword), *yyllocp, yyscanner);
else
yyerror(yyllocp, yyscanner, "syntax error");
}
@@ -2762,7 +2764,7 @@ read_sql_construct(int until,
parser_errposition(*yyllocp)));
}
/* Remember end+1 location of last accepted token */
- endlocation = *yyllocp + plpgsql_token_length();
+ endlocation = *yyllocp + plpgsql_token_length(yyscanner);
}
plpgsql_IdentifierLookup = save_IdentifierLookup;
@@ -2788,7 +2790,7 @@ read_sql_construct(int until,
* whitespace by hand, but that causes problems if there's a "-- comment"
* in front of said whitespace.)
*/
- plpgsql_append_source_text(&ds, startlocation, endlocation);
+ plpgsql_append_source_text(&ds, startlocation, endlocation, yyscanner);
expr = palloc0(sizeof(PLpgSQL_expr));
expr->query = pstrdup(ds.data);
@@ -2800,7 +2802,7 @@ read_sql_construct(int until,
pfree(ds.data);
if (valid_sql)
- check_sql_expr(expr->query, expr->parseMode, startlocation);
+ check_sql_expr(expr->query, expr->parseMode, startlocation, yyscanner);
return expr;
}
@@ -2910,7 +2912,7 @@ read_datatype(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
yyerror(yyllocp, yyscanner, "syntax error, expected \"]\"");
tok = yylex(yylvalp, yyllocp, yyscanner);
}
- plpgsql_push_back_token(tok, yylvalp, yyllocp);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp, yyscanner);
if (is_array)
result = plpgsql_build_datatype_arrayof(result);
@@ -2950,7 +2952,7 @@ read_datatype(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
/* set up ds to contain complete typename text */
initStringInfo(&ds);
- plpgsql_append_source_text(&ds, startlocation, *yyllocp);
+ plpgsql_append_source_text(&ds, startlocation, *yyllocp, yyscanner);
type_name = ds.data;
if (type_name[0] == '\0')
@@ -2960,7 +2962,7 @@ read_datatype(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
pfree(ds.data);
- plpgsql_push_back_token(tok, yylvalp, yyllocp);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp, yyscanner);
return result;
}
@@ -3107,12 +3109,12 @@ make_execsql_stmt(int firsttoken, int location, PLword *word, YYSTYPE *yylvalp,
* text, so that locations within the redacted SQL statement still
* line up with those in the original source text.
*/
- plpgsql_append_source_text(&ds, location, into_start_loc);
+ plpgsql_append_source_text(&ds, location, into_start_loc, yyscanner);
appendStringInfoSpaces(&ds, into_end_loc - into_start_loc);
- plpgsql_append_source_text(&ds, into_end_loc, *yyllocp);
+ plpgsql_append_source_text(&ds, into_end_loc, *yyllocp, yyscanner);
}
else
- plpgsql_append_source_text(&ds, location, *yyllocp);
+ plpgsql_append_source_text(&ds, location, *yyllocp, yyscanner);
/* trim any trailing whitespace, for neatness */
while (ds.len > 0 && scanner_isspace(ds.data[ds.len - 1]))
@@ -3127,11 +3129,11 @@ make_execsql_stmt(int firsttoken, int location, PLword *word, YYSTYPE *yylvalp,
expr->ns = plpgsql_ns_top();
pfree(ds.data);
- check_sql_expr(expr->query, expr->parseMode, location);
+ check_sql_expr(expr->query, expr->parseMode, location, yyscanner);
execsql = palloc0(sizeof(PLpgSQL_stmt_execsql));
execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
- execsql->lineno = plpgsql_location_to_lineno(location);
+ execsql->lineno = plpgsql_location_to_lineno(location, yyscanner);
execsql->stmtid = ++plpgsql_curr_compile->nstatements;
execsql->sqlstmt = expr;
execsql->into = have_into;
@@ -3233,7 +3235,7 @@ read_fetch_direction(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
else if (tok == T_DATUM)
{
/* Assume there's no direction clause and tok is a cursor name */
- plpgsql_push_back_token(tok, yylvalp, yyllocp);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp, yyscanner);
check_FROM = false;
}
else
@@ -3246,7 +3248,7 @@ read_fetch_direction(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
* will trigger. Perhaps this can be improved someday, but it hardly
* seems worth a lot of work.
*/
- plpgsql_push_back_token(tok, yylvalp, yyllocp);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp, yyscanner);
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"FROM or IN",
NULL, yylvalp, yyllocp, yyscanner);
@@ -3294,7 +3296,7 @@ complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM, YYSTYPE *yylvalp
return;
}
- plpgsql_push_back_token(tok, yylvalp, yyllocp);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp, yyscanner);
fetch->expr = read_sql_expression2(K_FROM, K_IN,
"FROM or IN",
NULL, yylvalp, yyllocp, yyscanner);
@@ -3310,7 +3312,7 @@ make_return_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yysc
new = palloc0(sizeof(PLpgSQL_stmt_return));
new->cmd_type = PLPGSQL_STMT_RETURN;
- new->lineno = plpgsql_location_to_lineno(location);
+ new->lineno = plpgsql_location_to_lineno(location, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->expr = NULL;
new->retvarno = -1;
@@ -3376,7 +3378,7 @@ make_return_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yysc
* Note that a well-formed expression is _required_ here; anything
* else is a compile-time error.
*/
- plpgsql_push_back_token(tok, yylvalp, yyllocp);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp, yyscanner);
new->expr = read_sql_expression(';', ";", yylvalp, yyllocp, yyscanner);
}
}
@@ -3398,7 +3400,7 @@ make_return_next_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t
new = palloc0(sizeof(PLpgSQL_stmt_return_next));
new->cmd_type = PLPGSQL_STMT_RETURN_NEXT;
- new->lineno = plpgsql_location_to_lineno(location);
+ new->lineno = plpgsql_location_to_lineno(location, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->expr = NULL;
new->retvarno = -1;
@@ -3439,7 +3441,7 @@ make_return_next_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t
* Note that a well-formed expression is _required_ here; anything
* else is a compile-time error.
*/
- plpgsql_push_back_token(tok, yylvalp, yyllocp);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp, yyscanner);
new->expr = read_sql_expression(';', ";", yylvalp, yyllocp, yyscanner);
}
}
@@ -3462,14 +3464,14 @@ make_return_query_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_
new = palloc0(sizeof(PLpgSQL_stmt_return_query));
new->cmd_type = PLPGSQL_STMT_RETURN_QUERY;
- new->lineno = plpgsql_location_to_lineno(location);
+ new->lineno = plpgsql_location_to_lineno(location, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
/* check for RETURN QUERY EXECUTE */
if ((tok = yylex(yylvalp, yyllocp, yyscanner)) != K_EXECUTE)
{
/* ordinary static query */
- plpgsql_push_back_token(tok, yylvalp, yyllocp);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp, yyscanner);
new->query = read_sql_stmt(yylvalp, yyllocp, yyscanner);
}
else
@@ -3506,7 +3508,7 @@ NameOfDatum(PLwdatum *wdatum)
}
static void
-check_assignable(PLpgSQL_datum *datum, int location)
+check_assignable(PLpgSQL_datum *datum, int location, yyscan_t yyscanner)
{
switch (datum->dtype)
{
@@ -3526,7 +3528,7 @@ check_assignable(PLpgSQL_datum *datum, int location)
case PLPGSQL_DTYPE_RECFIELD:
/* assignable if parent record is */
check_assignable(plpgsql_Datums[((PLpgSQL_recfield *) datum)->recparentno],
- location);
+ location, yyscanner);
break;
default:
elog(ERROR, "unrecognized dtype: %d", datum->dtype);
@@ -3568,7 +3570,7 @@ read_into_target(PLpgSQL_variable **target, bool *strict, YYSTYPE *yylvalp, YYLT
if (yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
{
- check_assignable(yylvalp->wdatum.datum, *yyllocp);
+ check_assignable(yylvalp->wdatum.datum, *yyllocp, yyscanner);
*target = (PLpgSQL_variable *) yylvalp->wdatum.datum;
if ((tok = yylex(yylvalp, yyllocp, yyscanner)) == ',')
@@ -3576,7 +3578,7 @@ read_into_target(PLpgSQL_variable **target, bool *strict, YYSTYPE *yylvalp, YYLT
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("record variable cannot be part of multiple-item INTO list"),
parser_errposition(*yyllocp)));
- plpgsql_push_back_token(tok, yylvalp, yyllocp);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp, yyscanner);
}
else
{
@@ -3611,7 +3613,7 @@ read_into_scalar_list(char *initial_name,
PLpgSQL_row *row;
int tok;
- check_assignable(initial_datum, initial_location);
+ check_assignable(initial_datum, initial_location, yyscanner);
fieldnames[0] = initial_name;
varnos[0] = initial_datum->dno;
nfields = 1;
@@ -3629,7 +3631,7 @@ read_into_scalar_list(char *initial_name,
switch (tok)
{
case T_DATUM:
- check_assignable(yylvalp->wdatum.datum, *yyllocp);
+ check_assignable(yylvalp->wdatum.datum, *yyllocp, yyscanner);
if (yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
yylvalp->wdatum.datum->dtype == PLPGSQL_DTYPE_REC)
ereport(ERROR,
@@ -3651,12 +3653,12 @@ read_into_scalar_list(char *initial_name,
* We read an extra, non-comma token from yylex(), so push it back onto
* the input stream
*/
- plpgsql_push_back_token(tok, yylvalp, yyllocp);
+ plpgsql_push_back_token(tok, yylvalp, yyllocp, yyscanner);
row = palloc0(sizeof(PLpgSQL_row));
row->dtype = PLPGSQL_DTYPE_ROW;
row->refname = "(unnamed row)";
- row->lineno = plpgsql_location_to_lineno(initial_location);
+ row->lineno = plpgsql_location_to_lineno(initial_location, yyscanner);
row->rowtupdesc = NULL;
row->nfields = nfields;
row->fieldnames = palloc(sizeof(char *) * nfields);
@@ -3682,11 +3684,11 @@ read_into_scalar_list(char *initial_name,
static PLpgSQL_row *
make_scalar_list1(char *initial_name,
PLpgSQL_datum *initial_datum,
- int lineno, int location)
+ int lineno, int location, yyscan_t yyscanner)
{
PLpgSQL_row *row;
- check_assignable(initial_datum, location);
+ check_assignable(initial_datum, location, yyscanner);
row = palloc0(sizeof(PLpgSQL_row));
row->dtype = PLPGSQL_DTYPE_ROW;
@@ -3727,7 +3729,7 @@ make_scalar_list1(char *initial_name,
* If no error cursor is provided, we'll just point at "location".
*/
static void
-check_sql_expr(const char *stmt, RawParseMode parseMode, int location)
+check_sql_expr(const char *stmt, RawParseMode parseMode, int location, yyscan_t yyscanner)
{
sql_error_callback_arg cbarg;
ErrorContextCallback syntax_errcontext;
@@ -3737,6 +3739,7 @@ check_sql_expr(const char *stmt, RawParseMode parseMode, int location)
return;
cbarg.location = location;
+ cbarg.yyscanner = yyscanner;
syntax_errcontext.callback = plpgsql_sql_error_callback;
syntax_errcontext.arg = &cbarg;
@@ -3755,6 +3758,7 @@ static void
plpgsql_sql_error_callback(void *arg)
{
sql_error_callback_arg *cbarg = (sql_error_callback_arg *) arg;
+ yyscan_t yyscanner = cbarg->yyscanner;
int errpos;
/*
@@ -3823,7 +3827,7 @@ parse_datatype(const char *string, int location)
* Check block starting and ending labels match.
*/
static void
-check_labels(const char *start_label, const char *end_label, int end_location)
+check_labels(const char *start_label, const char *end_label, int end_location, yyscan_t yyscanner)
{
if (end_label)
{
@@ -4119,13 +4123,13 @@ check_raise_parameters(PLpgSQL_stmt_raise *stmt)
*/
static PLpgSQL_stmt *
make_case(int location, PLpgSQL_expr *t_expr,
- List *case_when_list, List *else_stmts)
+ List *case_when_list, List *else_stmts, yyscan_t yyscanner)
{
PLpgSQL_stmt_case *new;
new = palloc(sizeof(PLpgSQL_stmt_case));
new->cmd_type = PLPGSQL_STMT_CASE;
- new->lineno = plpgsql_location_to_lineno(location);
+ new->lineno = plpgsql_location_to_lineno(location, yyscanner);
new->stmtid = ++plpgsql_curr_compile->nstatements;
new->t_expr = t_expr;
new->t_varno = 0;
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
index abf2d40c07a..e3cff48b322 100644
--- a/src/pl/plpgsql/src/pl_scanner.c
+++ b/src/pl/plpgsql/src/pl_scanner.c
@@ -95,41 +95,56 @@ typedef struct
int leng; /* length in bytes */
} TokenAuxData;
+#define MAX_PUSHBACKS 4
+
/*
- * Scanner working state. At some point we might wish to fold all this
- * into a YY_EXTRA struct. For the moment, there is no need for plpgsql's
- * lexer to be re-entrant, and the notational burden of passing a yyscanner
- * pointer around is great enough to not want to do it without need.
+ * Scanner working state.
*/
+struct plpgsql_yy_extra_type
+{
+ /* The stuff the core lexer needs */
+ core_yy_extra_type core_yy_extra;
-/* The stuff the core lexer needs */
-static core_yy_extra_type core_yy_extra;
-
-/* The original input string */
-static const char *scanorig;
-
-/* Current token's length (corresponds to plpgsql_yylval and plpgsql_yylloc) */
-static int plpgsql_yyleng;
+ /* The original input string */
+ const char *scanorig;
-/* Current token's code (corresponds to plpgsql_yylval and plpgsql_yylloc) */
-static int plpgsql_yytoken;
+ /*
+ * Current token's length (corresponds to plpgsql_yylval and
+ * plpgsql_yylloc)
+ */
+ int plpgsql_yyleng;
-/* Token pushback stack */
-#define MAX_PUSHBACKS 4
+ /* Current token's code (corresponds to plpgsql_yylval and plpgsql_yylloc) */
+ int plpgsql_yytoken;
-static int num_pushbacks;
-static int pushback_token[MAX_PUSHBACKS];
-static TokenAuxData pushback_auxdata[MAX_PUSHBACKS];
+ /* Token pushback stack */
+ int num_pushbacks;
+ int pushback_token[MAX_PUSHBACKS];
+ TokenAuxData pushback_auxdata[MAX_PUSHBACKS];
-/* State for plpgsql_location_to_lineno() */
-static const char *cur_line_start;
-static const char *cur_line_end;
-static int cur_line_num;
+ /* State for plpgsql_location_to_lineno() */
+ const char *cur_line_start;
+ const char *cur_line_end;
+ int cur_line_num;
+};
/* Internal functions */
static int internal_yylex(TokenAuxData *auxdata, yyscan_t yyscanner);
-static void push_back_token(int token, TokenAuxData *auxdata);
-static void location_lineno_init(void);
+static void push_back_token(int token, TokenAuxData *auxdata, yyscan_t yyscanner);
+static void location_lineno_init(yyscan_t yyscanner);
+
+/*
+ * This is normally provided by the generated flex code, but we don't have
+ * that here, so we make a minimal version ourselves.
+ */
+struct yyguts_t
+{
+ struct plpgsql_yy_extra_type *yyextra_r;
+};
+
+/* see scan.l */
+#undef yyextra
+#define yyextra (((struct yyguts_t *) yyscanner)->yyextra_r)
/*
@@ -189,8 +204,8 @@ plpgsql_yylex(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
else
{
/* not A.B.C, so just process A.B */
- push_back_token(tok5, &aux5);
- push_back_token(tok4, &aux4);
+ push_back_token(tok5, &aux5, yyscanner);
+ push_back_token(tok4, &aux4, yyscanner);
if (plpgsql_parse_dblword(aux1.lval.str,
aux3.lval.str,
&aux1.lval.wdatum,
@@ -205,7 +220,7 @@ plpgsql_yylex(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
else
{
/* not A.B.C, so just process A.B */
- push_back_token(tok4, &aux4);
+ push_back_token(tok4, &aux4, yyscanner);
if (plpgsql_parse_dblword(aux1.lval.str,
aux3.lval.str,
&aux1.lval.wdatum,
@@ -220,10 +235,10 @@ plpgsql_yylex(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
else
{
/* not A.B, so just process A */
- push_back_token(tok3, &aux3);
- push_back_token(tok2, &aux2);
+ push_back_token(tok3, &aux3, yyscanner);
+ push_back_token(tok2, &aux2, yyscanner);
if (plpgsql_parse_word(aux1.lval.str,
- core_yy_extra.scanbuf + aux1.lloc,
+ yyextra->core_yy_extra.scanbuf + aux1.lloc,
true,
&aux1.lval.wdatum,
&aux1.lval.word))
@@ -243,7 +258,7 @@ plpgsql_yylex(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
else
{
/* not A.B, so just process A */
- push_back_token(tok2, &aux2);
+ push_back_token(tok2, &aux2, yyscanner);
/*
* See if it matches a variable name, except in the context where
@@ -263,8 +278,8 @@ plpgsql_yylex(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
* non-variable cases.
*/
if (plpgsql_parse_word(aux1.lval.str,
- core_yy_extra.scanbuf + aux1.lloc,
- (!AT_STMT_START(plpgsql_yytoken) ||
+ yyextra->core_yy_extra.scanbuf + aux1.lloc,
+ (!AT_STMT_START(yyextra->plpgsql_yytoken) ||
(tok2 == '=' || tok2 == COLON_EQUALS ||
tok2 == '[')),
&aux1.lval.wdatum,
@@ -298,8 +313,8 @@ plpgsql_yylex(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
*yylvalp = aux1.lval;
*yyllocp = aux1.lloc;
- plpgsql_yyleng = aux1.leng;
- plpgsql_yytoken = tok1;
+ yyextra->plpgsql_yyleng = aux1.leng;
+ yyextra->plpgsql_yytoken = tok1;
return tok1;
}
@@ -309,9 +324,9 @@ plpgsql_yylex(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
* In the case of compound tokens, the length includes all the parts.
*/
int
-plpgsql_token_length(void)
+plpgsql_token_length(yyscan_t yyscanner)
{
- return plpgsql_yyleng;
+ return yyextra->plpgsql_yyleng;
}
/*
@@ -326,11 +341,11 @@ internal_yylex(TokenAuxData *auxdata, yyscan_t yyscanner)
int token;
const char *yytext;
- if (num_pushbacks > 0)
+ if (yyextra->num_pushbacks > 0)
{
- num_pushbacks--;
- token = pushback_token[num_pushbacks];
- *auxdata = pushback_auxdata[num_pushbacks];
+ yyextra->num_pushbacks--;
+ token = yyextra->pushback_token[yyextra->num_pushbacks];
+ *auxdata = yyextra->pushback_auxdata[yyextra->num_pushbacks];
}
else
{
@@ -339,7 +354,7 @@ internal_yylex(TokenAuxData *auxdata, yyscan_t yyscanner)
yyscanner);
/* remember the length of yytext before it gets changed */
- yytext = core_yy_extra.scanbuf + auxdata->lloc;
+ yytext = yyextra->core_yy_extra.scanbuf + auxdata->lloc;
auxdata->leng = strlen(yytext);
/* Check for << >> and #, which the core considers operators */
@@ -367,13 +382,13 @@ internal_yylex(TokenAuxData *auxdata, yyscan_t yyscanner)
* Push back a token to be re-read by next internal_yylex() call.
*/
static void
-push_back_token(int token, TokenAuxData *auxdata)
+push_back_token(int token, TokenAuxData *auxdata, yyscan_t yyscanner)
{
- if (num_pushbacks >= MAX_PUSHBACKS)
+ if (yyextra->num_pushbacks >= MAX_PUSHBACKS)
elog(ERROR, "too many tokens pushed back");
- pushback_token[num_pushbacks] = token;
- pushback_auxdata[num_pushbacks] = *auxdata;
- num_pushbacks++;
+ yyextra->pushback_token[yyextra->num_pushbacks] = token;
+ yyextra->pushback_auxdata[yyextra->num_pushbacks] = *auxdata;
+ yyextra->num_pushbacks++;
}
/*
@@ -383,14 +398,14 @@ push_back_token(int token, TokenAuxData *auxdata)
* is not a good idea to push back a token code other than what you read.
*/
void
-plpgsql_push_back_token(int token, YYSTYPE *yylvalp, YYLTYPE *yyllocp)
+plpgsql_push_back_token(int token, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
{
TokenAuxData auxdata;
auxdata.lval = *yylvalp;
auxdata.lloc = *yyllocp;
- auxdata.leng = plpgsql_yyleng;
- push_back_token(token, &auxdata);
+ auxdata.leng = yyextra->plpgsql_yyleng;
+ push_back_token(token, &auxdata, yyscanner);
}
/*
@@ -418,10 +433,11 @@ plpgsql_token_is_unreserved_keyword(int token)
*/
void
plpgsql_append_source_text(StringInfo buf,
- int startlocation, int endlocation)
+ int startlocation, int endlocation,
+ yyscan_t yyscanner)
{
Assert(startlocation <= endlocation);
- appendBinaryStringInfo(buf, scanorig + startlocation,
+ appendBinaryStringInfo(buf, yyextra->scanorig + startlocation,
endlocation - startlocation);
}
@@ -439,7 +455,7 @@ plpgsql_peek(yyscan_t yyscanner)
TokenAuxData aux1;
tok1 = internal_yylex(&aux1, yyscanner);
- push_back_token(tok1, &aux1);
+ push_back_token(tok1, &aux1, yyscanner);
return tok1;
}
@@ -469,8 +485,8 @@ plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc, int *tok2_loc, yyscan_t y
if (tok2_loc)
*tok2_loc = aux2.lloc;
- push_back_token(tok2, &aux2);
- push_back_token(tok1, &aux1);
+ push_back_token(tok2, &aux2, yyscanner);
+ push_back_token(tok1, &aux1, yyscanner);
}
/*
@@ -485,19 +501,19 @@ plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc, int *tok2_loc, yyscan_t y
* to still be available.
*/
int
-plpgsql_scanner_errposition(int location)
+plpgsql_scanner_errposition(int location, yyscan_t yyscanner)
{
int pos;
- if (location < 0 || scanorig == NULL)
+ if (location < 0 || yyextra->scanorig == NULL)
return 0; /* no-op if location is unknown */
/* Convert byte offset to character number */
- pos = pg_mbstrlen_with_len(scanorig, location) + 1;
+ pos = pg_mbstrlen_with_len(yyextra->scanorig, location) + 1;
/* And pass it to the ereport mechanism */
(void) internalerrposition(pos);
/* Also pass the function body string */
- return internalerrquery(scanorig);
+ return internalerrquery(yyextra->scanorig);
}
/*
@@ -514,7 +530,7 @@ plpgsql_scanner_errposition(int location)
void
plpgsql_yyerror(YYLTYPE *yyllocp, yyscan_t yyscanner, const char *message)
{
- char *yytext = core_yy_extra.scanbuf + *yyllocp;
+ char *yytext = yyextra->core_yy_extra.scanbuf + *yyllocp;
if (*yytext == '\0')
{
@@ -522,7 +538,7 @@ plpgsql_yyerror(YYLTYPE *yyllocp, yyscan_t yyscanner, const char *message)
(errcode(ERRCODE_SYNTAX_ERROR),
/* translator: %s is typically the translation of "syntax error" */
errmsg("%s at end of input", _(message)),
- plpgsql_scanner_errposition(*yyllocp)));
+ plpgsql_scanner_errposition(*yyllocp, yyscanner)));
}
else
{
@@ -532,13 +548,13 @@ plpgsql_yyerror(YYLTYPE *yyllocp, yyscan_t yyscanner, const char *message)
* only the single token here. This modifies scanbuf but we no longer
* care about that.
*/
- yytext[plpgsql_yyleng] = '\0';
+ yytext[yyextra->plpgsql_yyleng] = '\0';
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
/* translator: first %s is typically the translation of "syntax error" */
errmsg("%s at or near \"%s\"", _(message), yytext),
- plpgsql_scanner_errposition(*yyllocp)));
+ plpgsql_scanner_errposition(*yyllocp, yyscanner)));
}
}
@@ -551,43 +567,43 @@ plpgsql_yyerror(YYLTYPE *yyllocp, yyscan_t yyscanner, const char *message)
* of the "current" line.
*/
int
-plpgsql_location_to_lineno(int location)
+plpgsql_location_to_lineno(int location, yyscan_t yyscanner)
{
const char *loc;
- if (location < 0 || scanorig == NULL)
+ if (location < 0 || yyextra->scanorig == NULL)
return 0; /* garbage in, garbage out */
- loc = scanorig + location;
+ loc = yyextra->scanorig + location;
/* be correct, but not fast, if input location goes backwards */
- if (loc < cur_line_start)
- location_lineno_init();
+ if (loc < yyextra->cur_line_start)
+ location_lineno_init(yyscanner);
- while (cur_line_end != NULL && loc > cur_line_end)
+ while (yyextra->cur_line_end != NULL && loc > yyextra->cur_line_end)
{
- cur_line_start = cur_line_end + 1;
- cur_line_num++;
- cur_line_end = strchr(cur_line_start, '\n');
+ yyextra->cur_line_start = yyextra->cur_line_end + 1;
+ yyextra->cur_line_num++;
+ yyextra->cur_line_end = strchr(yyextra->cur_line_start, '\n');
}
- return cur_line_num;
+ return yyextra->cur_line_num;
}
/* initialize or reset the state for plpgsql_location_to_lineno */
static void
-location_lineno_init(void)
+location_lineno_init(yyscan_t yyscanner)
{
- cur_line_start = scanorig;
- cur_line_num = 1;
+ yyextra->cur_line_start = yyextra->scanorig;
+ yyextra->cur_line_num = 1;
- cur_line_end = strchr(cur_line_start, '\n');
+ yyextra->cur_line_end = strchr(yyextra->cur_line_start, '\n');
}
/* return the most recently computed lineno */
int
-plpgsql_latest_lineno(void)
+plpgsql_latest_lineno(yyscan_t yyscanner)
{
- return cur_line_num;
+ return yyextra->cur_line_num;
}
@@ -602,9 +618,10 @@ yyscan_t
plpgsql_scanner_init(const char *str)
{
yyscan_t yyscanner;
+ struct plpgsql_yy_extra_type *yyext = palloc0_object(struct plpgsql_yy_extra_type);
/* Start up the core scanner */
- yyscanner = scanner_init(str, &core_yy_extra,
+ yyscanner = scanner_init(str, (core_yy_extra_type *) yyext,
&ReservedPLKeywords, ReservedPLKeywordTokens);
/*
@@ -613,15 +630,15 @@ plpgsql_scanner_init(const char *str)
* yytext points into scanbuf, we rely on being able to apply locations
* (offsets from string start) to scanorig as well.
*/
- scanorig = str;
+ yyext->scanorig = str;
/* Other setup */
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
- plpgsql_yytoken = 0;
+ yyext->plpgsql_yytoken = 0;
- num_pushbacks = 0;
+ yyext->num_pushbacks = 0;
- location_lineno_init();
+ location_lineno_init(yyscanner);
return yyscanner;
}
@@ -634,6 +651,4 @@ plpgsql_scanner_finish(yyscan_t yyscanner)
{
/* release storage */
scanner_finish(yyscanner);
- /* avoid leaving any dangling pointers */
- scanorig = NULL;
}
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 151ae7943df..2562c816a1b 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -1322,18 +1322,19 @@ union YYSTYPE;
typedef void *yyscan_t;
#endif
extern int plpgsql_yylex(union YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
-extern int plpgsql_token_length(void);
-extern void plpgsql_push_back_token(int token, union YYSTYPE *yylvalp, YYLTYPE *yyllocp);
+extern int plpgsql_token_length(yyscan_t yyscanner);
+extern void plpgsql_push_back_token(int token, union YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
extern bool plpgsql_token_is_unreserved_keyword(int token);
extern void plpgsql_append_source_text(StringInfo buf,
- int startlocation, int endlocation);
+ int startlocation, int endlocation,
+ yyscan_t yyscanner);
extern int plpgsql_peek(yyscan_t yyscanner);
extern void plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc,
int *tok2_loc, yyscan_t yyscanner);
-extern int plpgsql_scanner_errposition(int location);
+extern int plpgsql_scanner_errposition(int location, yyscan_t yyscanner);
extern void plpgsql_yyerror(YYLTYPE *yyllocp, yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
-extern int plpgsql_location_to_lineno(int location);
-extern int plpgsql_latest_lineno(void);
+extern int plpgsql_location_to_lineno(int location, yyscan_t yyscanner);
+extern int plpgsql_latest_lineno(yyscan_t yyscanner);
extern yyscan_t plpgsql_scanner_init(const char *str);
extern void plpgsql_scanner_finish(yyscan_t yyscanner);
--
2.47.1
On 26/12/2024 20:27, Peter Eisentraut wrote:
On 22.12.24 22:43, Andreas Karlsson wrote:
On 12/19/24 9:57 PM, Peter Eisentraut wrote:
Here is an updated patch set on top of what has been committed so
far, with all the issues you pointed out addressed.Other than the discussion of how old versions of flex we should
support I think this set of patches is ready to be committed. I looked
at it again and everything looks good.I have committed these except the plpgsql one, which was still work in
progress. But I have progressed on this now and also converted the
parser and put the local state into yyextra. This gets rid of all
internal global state now. The patches for this are attached. It's a
lot of churn, but otherwise pretty standard stuff.
Looks good to me.
Along the way I noticed that the flex documentation now recommends a
different way to set the yyextra type. So I have changed the ones we
already have to that newer style. I inspected the generated C code and
there wasn't any significant difference, so I'm not sure, but I figure
if we're making changes in this area we might as well use the modern style.
+1. According to the flex NEWS file, this syntax was added in flex
2.5.34, and we already require 2.5.35.
--
Heikki Linnakangas
Neon (https://neon.tech)
On 27.12.24 10:19, Heikki Linnakangas wrote:
On 26/12/2024 20:27, Peter Eisentraut wrote:
On 22.12.24 22:43, Andreas Karlsson wrote:
On 12/19/24 9:57 PM, Peter Eisentraut wrote:
Here is an updated patch set on top of what has been committed so
far, with all the issues you pointed out addressed.Other than the discussion of how old versions of flex we should
support I think this set of patches is ready to be committed. I
looked at it again and everything looks good.I have committed these except the plpgsql one, which was still work in
progress. But I have progressed on this now and also converted the
parser and put the local state into yyextra. This gets rid of all
internal global state now. The patches for this are attached. It's a
lot of churn, but otherwise pretty standard stuff.Looks good to me.
Along the way I noticed that the flex documentation now recommends a
different way to set the yyextra type. So I have changed the ones we
already have to that newer style. I inspected the generated C code
and there wasn't any significant difference, so I'm not sure, but I
figure if we're making changes in this area we might as well use the
modern style.+1. According to the flex NEWS file, this syntax was added in flex
2.5.34, and we already require 2.5.35.
These have been committed. This concludes the main body of this work.
I'll let it take a lap around the buildfarm and will follow up in a few
days on what if anything lapwing has to say about it in terms of
warnings and what we want to do about it.
On 20.12.24 16:23, Tom Lane wrote:
Ok, we can fix that, but maybe this is also a good moment to think about
whether that is useful. I could not reproduce the issue with flex
2.5.39. I could find no download of flex 2.5.35. The github site only
offers back to 2.5.39, the sourceforce site back to 2.5.36. lapwing
says it's Debian 7.0, which went out of support in 2016 and out of
super-duper-extended support in 2020. It also doesn't have a supported
OpenSSL version anymore, and IIRC, it has a weird old compiler that
occasionally gives bogus warnings. I think it's time to stop supporting
this.OK, that's fair. I do see lapwing called out a lot in the commit log,
though it's not clear how much of that is about 32-bitness and how
much about old tools. It's surely still valuable to have i386
machines in the buildfarm, but I agree that supporting unobtainable
tool versions is a bit much. Could we get that animal updated to
some newer OS version?Presumably, we should also rip out the existing yyget_column and
yyset_column kluges insrc/backend/parser/scan.l: extern int core_yyget_column(yyscan_t yyscanner);
src/bin/psql/psqlscanslash.l: extern int slash_yyget_column(yyscan_t yyscanner);
src/bin/pgbench/exprscan.l: extern int expr_yyget_column(yyscan_t yyscanner);
src/fe_utils/psqlscan.l: extern int psql_yyget_column(yyscan_t yyscanner);
All my flex-related patches are in now.
Here is a patch that removes the workarounds for compiler warnings with
flex 2.5.35. This ended up being a whole lot, including the whole
fix-old-flex-code.pl script.
The second patch contemplates raising the minimum required flex version,
but what to?
The most recent incrementing was exactly because 2.5.35 was the oldest
in the buildfarm. The previous incrementings were apparently because
certain features were required or some bugs had to be avoided.
Options:
- Leave at 2.5.35 as long as it's present in the buildfarm.
- Set to 2.5.36 because it's the oldest that compiles without warnings.
Also, the oldest you can still download from the flex sourceforge site.
- Set to 2.5.37 because that's the next oldest in the buildfarm (for
CentOS/RHEL 7, so it will stay around for a while).
- Set to 2.5.34 because that's the oldest we actually require as of
commit b1ef48980dd.
- Remove version check, because these are all so old that no one cares
anymore.
Attachments:
0001-Drop-warning-free-support-for-flex-2.5.35.patchtext/plain; charset=UTF-8; name=0001-Drop-warning-free-support-for-flex-2.5.35.patchDownload
From fc1e8aedfe7371ae563ec51dd04346c0efd69fff Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Thu, 9 Jan 2025 08:36:16 +0100
Subject: [PATCH 1/2] Drop warning-free support for flex 2.5.35
This removes all the various workarounds for avoiding compiler
warnings with flex 2.5.35. Several recent patches have added
additional warnings that would either need to be fixed along the lines
of the existing workarounds, or we decide to no longer care about
this, which we do here.
Flex 2.5.35 is extremely outdated, and you can't even download it
anymore from any of the Flex project sites, so it's nearly impossible
to support.
After this, using flex 2.5.35 will still work, but the generated code
will produce numerous compiler warnings.
Discussion: https://www.postgresql.org/message-id/1a204ccd-7ae6-478c-a431-407b5c48ccc6@eisentraut.org
---
src/Makefile.global.in | 1 -
src/backend/parser/Makefile | 1 -
src/backend/parser/meson.build | 2 +-
src/backend/parser/scan.l | 9 -----
src/bin/pgbench/exprscan.l | 9 -----
src/bin/psql/Makefile | 1 -
src/bin/psql/meson.build | 2 +-
src/bin/psql/psqlscanslash.l | 9 -----
src/fe_utils/Makefile | 1 -
src/fe_utils/meson.build | 2 +-
src/fe_utils/psqlscan.l | 9 -----
src/tools/fix-old-flex-code.pl | 66 ----------------------------------
src/tools/pgflex | 13 -------
13 files changed, 3 insertions(+), 122 deletions(-)
delete mode 100644 src/tools/fix-old-flex-code.pl
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index eac3d001211..1278b7744f4 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -788,7 +788,6 @@ TAS = @TAS@
%.c: %.l
$(FLEX) $(if $(FLEX_NO_BACKUP),-b) $(FLEXFLAGS) -o'$@' $<
@$(if $(FLEX_NO_BACKUP),if [ `wc -l <lex.backup` -eq 1 ]; then rm lex.backup; else echo "Scanner requires backup; see lex.backup." 1>&2; exit 1; fi)
- $(if $(FLEX_FIX_WARNING),$(PERL) $(top_srcdir)/src/tools/fix-old-flex-code.pl '$@')
%.c: %.y
$(if $(BISON_CHECK_CMD),$(BISON_CHECK_CMD))
diff --git a/src/backend/parser/Makefile b/src/backend/parser/Makefile
index 3162a01f302..8c0fe28d63f 100644
--- a/src/backend/parser/Makefile
+++ b/src/backend/parser/Makefile
@@ -59,7 +59,6 @@ gram.c: BISON_CHECK_CMD = $(PERL) $(srcdir)/check_keywords.pl $< $(top_srcdir)/s
scan.c: FLEXFLAGS = -CF -p -p
scan.c: FLEX_NO_BACKUP=yes
-scan.c: FLEX_FIX_WARNING=yes
# Force these dependencies to be known even without dependency info built:
diff --git a/src/backend/parser/meson.build b/src/backend/parser/meson.build
index 4c3ca25dd49..874aa749aa6 100644
--- a/src/backend/parser/meson.build
+++ b/src/backend/parser/meson.build
@@ -30,7 +30,7 @@ parser_sources = files('parser.c')
backend_scanner = custom_target('scan',
input: 'scan.l',
output: 'scan.c',
- command: [flex_cmd, '--no-backup', '--fix-warnings', '--', '-CF', '-p', '-p'],
+ command: [flex_cmd, '--no-backup', '--', '-CF', '-p', '-p'],
)
generated_sources += backend_scanner
parser_sources += backend_scanner
diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l
index 8031a78b908..08990831fe8 100644
--- a/src/backend/parser/scan.l
+++ b/src/backend/parser/scan.l
@@ -130,15 +130,6 @@ static void addunicode(pg_wchar c, yyscan_t yyscanner);
static void check_string_escape_warning(unsigned char ychar, core_yyscan_t yyscanner);
static void check_escape_warning(core_yyscan_t yyscanner);
-/*
- * Work around a bug in flex 2.5.35: it emits a couple of functions that
- * it forgets to emit declarations for. Since we use -Wmissing-prototypes,
- * this would cause warnings. Providing our own declarations should be
- * harmless even when the bug gets fixed.
- */
-extern int core_yyget_column(yyscan_t yyscanner);
-extern void core_yyset_column(int column_no, yyscan_t yyscanner);
-
%}
%option reentrant
diff --git a/src/bin/pgbench/exprscan.l b/src/bin/pgbench/exprscan.l
index a1cd232248f..46f6ea05121 100644
--- a/src/bin/pgbench/exprscan.l
+++ b/src/bin/pgbench/exprscan.l
@@ -44,15 +44,6 @@ static const char *expr_command = NULL;
/* indicates whether last yylex() call read a newline */
static bool last_was_newline = false;
-/*
- * Work around a bug in flex 2.5.35: it emits a couple of functions that
- * it forgets to emit declarations for. Since we use -Wmissing-prototypes,
- * this would cause warnings. Providing our own declarations should be
- * harmless even when the bug gets fixed.
- */
-extern int expr_yyget_column(yyscan_t yyscanner);
-extern void expr_yyset_column(int column_no, yyscan_t yyscanner);
-
/* LCOV_EXCL_START */
%}
diff --git a/src/bin/psql/Makefile b/src/bin/psql/Makefile
index e9e4ade1d2e..5b1545d9948 100644
--- a/src/bin/psql/Makefile
+++ b/src/bin/psql/Makefile
@@ -60,7 +60,6 @@ sql_help.h: create_help.pl $(wildcard $(REFDOCDIR)/*.sgml)
psqlscanslash.c: FLEXFLAGS = -Cfe -p -p
psqlscanslash.c: FLEX_NO_BACKUP=yes
-psqlscanslash.c: FLEX_FIX_WARNING=yes
tab-complete.c: gen_tabcomplete.pl tab-complete.in.c
$(PERL) $^ --outfile $@
diff --git a/src/bin/psql/meson.build b/src/bin/psql/meson.build
index c2ef14ccead..f795ff28271 100644
--- a/src/bin/psql/meson.build
+++ b/src/bin/psql/meson.build
@@ -19,7 +19,7 @@ psql_sources = files(
psqlscanslash = custom_target('psqlscanslash',
input: 'psqlscanslash.l',
output: 'psqlscanslash.c',
- command: [flex_cmd, '--no-backup', '--fix-warnings', '--', '-Cfe', '-p', '-p'])
+ command: [flex_cmd, '--no-backup', '--', '-Cfe', '-p', '-p'])
generated_sources += psqlscanslash
psql_sources += psqlscanslash
diff --git a/src/bin/psql/psqlscanslash.l b/src/bin/psql/psqlscanslash.l
index f76b7722ac7..ae7602a61df 100644
--- a/src/bin/psql/psqlscanslash.l
+++ b/src/bin/psql/psqlscanslash.l
@@ -57,15 +57,6 @@ static void evaluate_backtick(PsqlScanState state);
#define ECHO psqlscan_emit(cur_state, yytext, yyleng)
-/*
- * Work around a bug in flex 2.5.35: it emits a couple of functions that
- * it forgets to emit declarations for. Since we use -Wmissing-prototypes,
- * this would cause warnings. Providing our own declarations should be
- * harmless even when the bug gets fixed.
- */
-extern int slash_yyget_column(yyscan_t yyscanner);
-extern void slash_yyset_column(int column_no, yyscan_t yyscanner);
-
/* LCOV_EXCL_START */
%}
diff --git a/src/fe_utils/Makefile b/src/fe_utils/Makefile
index f9ca5de2805..28196ce0f6a 100644
--- a/src/fe_utils/Makefile
+++ b/src/fe_utils/Makefile
@@ -51,7 +51,6 @@ libpgfeutils.a: $(OBJS)
psqlscan.c: FLEXFLAGS = -Cfe -p -p
psqlscan.c: FLEX_NO_BACKUP=yes
-psqlscan.c: FLEX_FIX_WARNING=yes
# libpgfeutils could be useful to contrib, so install it
install: all installdirs
diff --git a/src/fe_utils/meson.build b/src/fe_utils/meson.build
index 171268d65e1..a18cbc939e4 100644
--- a/src/fe_utils/meson.build
+++ b/src/fe_utils/meson.build
@@ -23,7 +23,7 @@ fe_utils_sources = files(
psqlscan = custom_target('psqlscan',
input: 'psqlscan.l',
output: 'psqlscan.c',
- command: [flex_cmd, '--no-backup', '--fix-warnings', '--', '-Cfe', '-p', '-p'],
+ command: [flex_cmd, '--no-backup', '--', '-Cfe', '-p', '-p'],
)
generated_sources += psqlscan
fe_utils_sources += psqlscan
diff --git a/src/fe_utils/psqlscan.l b/src/fe_utils/psqlscan.l
index e1cb726f326..b3c6b88e9ca 100644
--- a/src/fe_utils/psqlscan.l
+++ b/src/fe_utils/psqlscan.l
@@ -61,15 +61,6 @@ typedef int YYSTYPE;
#define ECHO psqlscan_emit(cur_state, yytext, yyleng)
-/*
- * Work around a bug in flex 2.5.35: it emits a couple of functions that
- * it forgets to emit declarations for. Since we use -Wmissing-prototypes,
- * this would cause warnings. Providing our own declarations should be
- * harmless even when the bug gets fixed.
- */
-extern int psql_yyget_column(yyscan_t yyscanner);
-extern void psql_yyset_column(int column_no, yyscan_t yyscanner);
-
%}
%option reentrant
diff --git a/src/tools/fix-old-flex-code.pl b/src/tools/fix-old-flex-code.pl
deleted file mode 100644
index c1c91db114c..00000000000
--- a/src/tools/fix-old-flex-code.pl
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/perl
-#----------------------------------------------------------------------
-#
-# fix-old-flex-code.pl
-#
-# flex versions before 2.5.36, with certain option combinations, produce
-# code that causes an "unused variable" warning. That's annoying, so
-# let's suppress it by inserting a dummy reference to the variable.
-# (That's exactly what 2.5.36 and later do ...)
-#
-# Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
-# Portions Copyright (c) 1994, Regents of the University of California
-#
-# src/tools/fix-old-flex-code.pl
-#
-#----------------------------------------------------------------------
-
-use strict;
-use warnings FATAL => 'all';
-
-# Get command line argument.
-usage() if $#ARGV != 0;
-my $filename = shift;
-
-# Suck in the whole file.
-local $/ = undef;
-my $cfile;
-open($cfile, '<', $filename) || die "opening $filename for reading: $!";
-my $ccode = <$cfile>;
-close($cfile);
-
-# No need to do anything if it's not flex 2.5.x for x < 36.
-exit 0 if $ccode !~ m/^#define YY_FLEX_MAJOR_VERSION 2$/m;
-exit 0 if $ccode !~ m/^#define YY_FLEX_MINOR_VERSION 5$/m;
-exit 0 if $ccode !~ m/^#define YY_FLEX_SUBMINOR_VERSION (\d+)$/m;
-exit 0 if $1 >= 36;
-
-# Apply the desired patch.
-$ccode =~
- s|(struct yyguts_t \* yyg = \(struct yyguts_t\*\)yyscanner; /\* This var may be unused depending upon options. \*/
-.*?)
- return yy_is_jam \? 0 : yy_current_state;
-|$1
- (void) yyg;
- return yy_is_jam ? 0 : yy_current_state;
-|s;
-
-# Write the modified file back out.
-open($cfile, '>', $filename) || die "opening $filename for writing: $!";
-print $cfile $ccode;
-close($cfile);
-
-exit 0;
-
-
-sub usage
-{
- die <<EOM;
-Usage: fix-old-flex-code.pl c-file-name
-
-fix-old-flex-code.pl modifies a flex output file to suppress
-an unused-variable warning that occurs with older flex versions.
-
-Report bugs to <pgsql-bugs\@lists.postgresql.org>.
-EOM
-}
diff --git a/src/tools/pgflex b/src/tools/pgflex
index baabe2df1c8..3986b06874e 100755
--- a/src/tools/pgflex
+++ b/src/tools/pgflex
@@ -4,7 +4,6 @@
# Wrapper around flex that:
# - ensures lex.backup is created in a private directory
# - can error out if lex.backup is created (--no-backup)
-# - can fix warnings (--fix-warnings)
# - works around concurrency issues with win_flex.exe:
# https://github.com/lexxmark/winflexbison/issues/86
@@ -28,8 +27,6 @@ parser.add_argument('-o', dest='output_file', type=abspath, required=True,
parser.add_argument('-i', dest='input_file', type=abspath, help='input file')
-parser.add_argument('--fix-warnings', action='store_true',
- help='whether to fix warnings in generated file')
parser.add_argument('--no-backup', action='store_true',
help='whether no_backup is enabled or not')
@@ -72,14 +69,4 @@ if args.no_backup:
sys.exit('Scanner requires backup; see lex.backup.')
os.remove('lex.backup')
-# fix warnings
-if args.fix_warnings:
- fix_warning_script = os.path.join(args.srcdir,
- 'src/tools/fix-old-flex-code.pl')
-
- command = [args.perl, fix_warning_script, args.output_file]
- sp = subprocess.run(command)
- if sp.returncode != 0:
- sys.exit(sp.returncode)
-
sys.exit(0)
base-commit: 69ab44651422c49a6256d1b6cca6c20b5060ad92
--
2.47.1
0002-Raise-minimum-flex-version-to-2.5.37.patchtext/plain; charset=UTF-8; name=0002-Raise-minimum-flex-version-to-2.5.37.patchDownload
From b33f36166fdb5b9dec746ba721881d696241e291 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Thu, 9 Jan 2025 08:41:28 +0100
Subject: [PATCH 2/2] Raise minimum flex version to 2.5.37
---
config/programs.m4 | 8 ++++----
configure | 4 ++--
doc/src/sgml/installation.sgml | 4 ++--
meson.build | 2 +-
src/backend/utils/misc/guc-file.l | 2 +-
5 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/config/programs.m4 b/config/programs.m4
index 490ec9fe9f5..417a4f6d06c 100644
--- a/config/programs.m4
+++ b/config/programs.m4
@@ -59,8 +59,8 @@ AC_SUBST(BISONFLAGS)
# PGAC_PATH_FLEX
# --------------
# Look for Flex, set the output variable FLEX to its path if found.
-# Reject versions before 2.5.35 (the earliest version in the buildfarm
-# as of 2022). Also find Flex if its installed under `lex', but do not
+# Reject versions before 2.5.37 (the earliest version in the buildfarm
+# as of 2025). Also find Flex if its installed under `lex', but do not
# accept other Lex programs.
AC_DEFUN([PGAC_PATH_FLEX],
@@ -84,14 +84,14 @@ else
echo '%%' > conftest.l
if $pgac_candidate -t conftest.l 2>/dev/null | grep FLEX_SCANNER >/dev/null 2>&1; then
pgac_flex_version=`$pgac_candidate --version 2>/dev/null`
- if echo "$pgac_flex_version" | sed ['s/[.a-z]/ /g'] | $AWK '{ if ([$]1 == 2 && ([$]2 > 5 || ([$]2 == 5 && [$]3 >= 35))) exit 0; else exit 1;}'
+ if echo "$pgac_flex_version" | sed ['s/[.a-z]/ /g'] | $AWK '{ if ([$]1 == 2 && ([$]2 > 5 || ([$]2 == 5 && [$]3 >= 37))) exit 0; else exit 1;}'
then
pgac_cv_path_flex=$pgac_candidate
break 2
else
AC_MSG_ERROR([
*** The installed version of Flex, $pgac_candidate, is too old to use with PostgreSQL.
-*** Flex version 2.5.35 or later is required, but this is $pgac_flex_version.])
+*** Flex version 2.5.37 or later is required, but this is $pgac_flex_version.])
fi
fi
fi
diff --git a/configure b/configure
index a0b5e10ca39..6f50ab4d7eb 100755
--- a/configure
+++ b/configure
@@ -9955,14 +9955,14 @@ else
echo '%%' > conftest.l
if $pgac_candidate -t conftest.l 2>/dev/null | grep FLEX_SCANNER >/dev/null 2>&1; then
pgac_flex_version=`$pgac_candidate --version 2>/dev/null`
- if echo "$pgac_flex_version" | sed 's/[.a-z]/ /g' | $AWK '{ if ($1 == 2 && ($2 > 5 || ($2 == 5 && $3 >= 35))) exit 0; else exit 1;}'
+ if echo "$pgac_flex_version" | sed 's/[.a-z]/ /g' | $AWK '{ if ($1 == 2 && ($2 > 5 || ($2 == 5 && $3 >= 37))) exit 0; else exit 1;}'
then
pgac_cv_path_flex=$pgac_candidate
break 2
else
as_fn_error $? "
*** The installed version of Flex, $pgac_candidate, is too old to use with PostgreSQL.
-*** Flex version 2.5.35 or later is required, but this is $pgac_flex_version." "$LINENO" 5
+*** Flex version 2.5.37 or later is required, but this is $pgac_flex_version." "$LINENO" 5
fi
fi
fi
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index ebdb5b3bc2d..c6dfbf0bfd3 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -100,7 +100,7 @@ <title>Requirements</title>
<primary>yacc</primary>
</indexterm>
- <application>Flex</application> 2.5.35 or later and
+ <application>Flex</application> 2.5.37 or later and
<application>Bison</application> 2.3 or later are required. Other
<application>lex</application> and <application>yacc</application>
programs cannot be used.
@@ -3813,7 +3813,7 @@ <title>Requirements</title>
<para>
<productname>Bison</productname> and <productname>Flex</productname> are
required. Only <productname>Bison</productname> versions 2.3 and later
- will work. <productname>Flex</productname> must be version 2.5.35 or later.
+ will work. <productname>Flex</productname> must be version 2.5.37 or later.
Binaries can be downloaded from <ulink
url="https://github.com/lexxmark/winflexbison"></ulink>.
</para>
diff --git a/meson.build b/meson.build
index cfd654d2916..ae0dfa852af 100644
--- a/meson.build
+++ b/meson.build
@@ -334,7 +334,7 @@ endif
# External programs
perl = find_program(get_option('PERL'), required: true, native: true)
python = find_program(get_option('PYTHON'), required: true, native: true)
-flex = find_program(get_option('FLEX'), native: true, version: '>= 2.5.35')
+flex = find_program(get_option('FLEX'), native: true, version: '>= 2.5.37')
bison = find_program(get_option('BISON'), native: true, version: '>= 2.3')
sed = find_program(get_option('SED'), 'sed', native: true, required: false)
prove = find_program(get_option('PROVE'), native: true, required: false)
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index c0ecb8b2ce2..280c3320954 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -302,7 +302,7 @@ record_config_file_error(const char *errmsg,
/*
* Flex fatal errors bring us here. Stash the error message and jump back to
* ParseConfigFp(). Assume all msg arguments point to string constants; this
- * holds for flex 2.5.35 (earliest we support). Otherwise, we would need to
+ * holds for flex 2.5.37 (earliest we support). Otherwise, we would need to
* copy the message.
*
* We return "int" since this takes the place of calls to fprintf().
--
2.47.1
Peter Eisentraut <peter@eisentraut.org> writes:
The second patch contemplates raising the minimum required flex version,
but what to?
Meh, let's just rip out the version check. It's no longer very
relevant. Nobody is going to be using anything older than 2.5.35.
While 2.5.35 produces compile warnings, it does still work, so
rejecting it with a changed version check seems unnecessary.
regards, tom lane
On 09.01.25 15:53, Tom Lane wrote:
Peter Eisentraut <peter@eisentraut.org> writes:
The second patch contemplates raising the minimum required flex version,
but what to?Meh, let's just rip out the version check. It's no longer very
relevant. Nobody is going to be using anything older than 2.5.35.
While 2.5.35 produces compile warnings, it does still work, so
rejecting it with a changed version check seems unnecessary.
This has been done.
Here are some more patches. This should cover the last sub-topic of
this topic: not passing the yyparse() result via global variables. This
uses techniques that are already in use in some parsers in the tree, for
example cube and jsonpath. The syncrep parser was a bit trickier,
because there we need to pass the syncrep_parse_error variable all the
way down to the scanner (not just the parser), but overall it's all
still pretty compact and standard.
Attachments:
0001-plpgsql-Return-parse-result-not-via-global-variable.patchtext/plain; charset=UTF-8; name=0001-plpgsql-Return-parse-result-not-via-global-variable.patchDownload
From da9afde1eea45881749ea832e7ded4491f756272 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Fri, 17 Jan 2025 12:59:56 +0100
Subject: [PATCH 1/4] plpgsql: Return parse result not via global variable
Instead of passing the parse result from yyparse() via a global
variable, pass it via a function output argument.
---
src/pl/plpgsql/src/nls.mk | 2 +-
src/pl/plpgsql/src/pl_comp.c | 8 +---
src/pl/plpgsql/src/pl_gram.y | 85 +++++++++++++++++----------------
src/pl/plpgsql/src/pl_scanner.c | 5 +-
src/pl/plpgsql/src/plpgsql.h | 6 +--
5 files changed, 52 insertions(+), 54 deletions(-)
diff --git a/src/pl/plpgsql/src/nls.mk b/src/pl/plpgsql/src/nls.mk
index eb06336675c..dd1c1e1440f 100644
--- a/src/pl/plpgsql/src/nls.mk
+++ b/src/pl/plpgsql/src/nls.mk
@@ -6,5 +6,5 @@ GETTEXT_FILES = pl_comp.c \
pl_funcs.c \
pl_handler.c \
pl_scanner.c
-GETTEXT_TRIGGERS = $(BACKEND_COMMON_GETTEXT_TRIGGERS) yyerror:3 plpgsql_yyerror:3
+GETTEXT_TRIGGERS = $(BACKEND_COMMON_GETTEXT_TRIGGERS) yyerror:4 plpgsql_yyerror:4
GETTEXT_FLAGS = $(BACKEND_COMMON_GETTEXT_FLAGS)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 9dc8218292d..a2de0880fb7 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -38,8 +38,6 @@
* Our own local and global variables
* ----------
*/
-PLpgSQL_stmt_block *plpgsql_parse_result;
-
static int datums_alloc;
int plpgsql_nDatums;
PLpgSQL_datum **plpgsql_Datums;
@@ -787,10 +785,9 @@ do_compile(FunctionCallInfo fcinfo,
/*
* Now parse the function's text
*/
- parse_rc = plpgsql_yyparse(scanner);
+ parse_rc = plpgsql_yyparse(&function->action, scanner);
if (parse_rc != 0)
elog(ERROR, "plpgsql parser returned %d", parse_rc);
- function->action = plpgsql_parse_result;
plpgsql_scanner_finish(scanner);
pfree(proc_source);
@@ -945,10 +942,9 @@ plpgsql_compile_inline(char *proc_source)
/*
* Now parse the function's text
*/
- parse_rc = plpgsql_yyparse(scanner);
+ parse_rc = plpgsql_yyparse(&function->action, scanner);
if (parse_rc != 0)
elog(ERROR, "plpgsql parser returned %d", parse_rc);
- function->action = plpgsql_parse_result;
plpgsql_scanner_finish(scanner);
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 063ed81f053..64d2c362bf9 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -116,6 +116,7 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%}
+%parse-param {PLpgSQL_stmt_block **plpgsql_parse_result_p}
%parse-param {yyscan_t yyscanner}
%lex-param {yyscan_t yyscanner}
%pure-parser
@@ -368,7 +369,7 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
pl_function : comp_options pl_block opt_semi
{
- plpgsql_parse_result = (PLpgSQL_stmt_block *) $2;
+ *plpgsql_parse_result_p = (PLpgSQL_stmt_block *) $2;
(void) yynerrs; /* suppress compiler warning */
}
;
@@ -712,7 +713,7 @@ decl_varname : T_WORD
if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
$1.ident, NULL, NULL,
NULL) != NULL)
- yyerror(&yylloc, yyscanner, "duplicate declaration");
+ yyerror(&yylloc, NULL, yyscanner, "duplicate declaration");
if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
@@ -740,7 +741,7 @@ decl_varname : T_WORD
if (plpgsql_ns_lookup(plpgsql_ns_top(), true,
$1, NULL, NULL,
NULL) != NULL)
- yyerror(&yylloc, yyscanner, "duplicate declaration");
+ yyerror(&yylloc, NULL, yyscanner, "duplicate declaration");
if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_SHADOWVAR ||
plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_SHADOWVAR)
@@ -1143,7 +1144,7 @@ getdiag_item :
K_RETURNED_SQLSTATE, "returned_sqlstate"))
$$ = PLPGSQL_GETDIAG_RETURNED_SQLSTATE;
else
- yyerror(&yylloc, yyscanner, "unrecognized GET DIAGNOSTICS item");
+ yyerror(&yylloc, NULL, yyscanner, "unrecognized GET DIAGNOSTICS item");
}
;
@@ -1777,7 +1778,7 @@ stmt_return : K_RETURN
tok = yylex(&yylval, &yylloc, yyscanner);
if (tok == 0)
- yyerror(&yylloc, yyscanner, "unexpected end of function definition");
+ yyerror(&yylloc, NULL, yyscanner, "unexpected end of function definition");
if (tok_is_keyword(tok, &yylval,
K_NEXT, "next"))
@@ -1815,7 +1816,7 @@ stmt_raise : K_RAISE
tok = yylex(&yylval, &yylloc, yyscanner);
if (tok == 0)
- yyerror(&yylloc, yyscanner, "unexpected end of function definition");
+ yyerror(&yylloc, NULL, yyscanner, "unexpected end of function definition");
/*
* We could have just RAISE, meaning to re-throw
@@ -1863,7 +1864,7 @@ stmt_raise : K_RAISE
tok = yylex(&yylval, &yylloc, yyscanner);
}
if (tok == 0)
- yyerror(&yylloc, yyscanner, "unexpected end of function definition");
+ yyerror(&yylloc, NULL, yyscanner, "unexpected end of function definition");
/*
* Next we can have a condition name, or
@@ -1883,7 +1884,7 @@ stmt_raise : K_RAISE
*/
tok = yylex(&yylval, &yylloc, yyscanner);
if (tok != ',' && tok != ';' && tok != K_USING)
- yyerror(&yylloc, yyscanner, "syntax error");
+ yyerror(&yylloc, NULL, yyscanner, "syntax error");
while (tok == ',')
{
@@ -1908,13 +1909,13 @@ stmt_raise : K_RAISE
char *sqlstatestr;
if (yylex(&yylval, &yylloc, yyscanner) != SCONST)
- yyerror(&yylloc, yyscanner, "syntax error");
+ yyerror(&yylloc, NULL, yyscanner, "syntax error");
sqlstatestr = yylval.str;
if (strlen(sqlstatestr) != 5)
- yyerror(&yylloc, yyscanner, "invalid SQLSTATE code");
+ yyerror(&yylloc, NULL, yyscanner, "invalid SQLSTATE code");
if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
- yyerror(&yylloc, yyscanner, "invalid SQLSTATE code");
+ yyerror(&yylloc, NULL, yyscanner, "invalid SQLSTATE code");
new->condname = sqlstatestr;
}
else
@@ -1924,13 +1925,13 @@ stmt_raise : K_RAISE
else if (plpgsql_token_is_unreserved_keyword(tok))
new->condname = pstrdup(yylval.keyword);
else
- yyerror(&yylloc, yyscanner, "syntax error");
+ yyerror(&yylloc, NULL, yyscanner, "syntax error");
plpgsql_recognize_err_condition(new->condname,
false);
}
tok = yylex(&yylval, &yylloc, yyscanner);
if (tok != ';' && tok != K_USING)
- yyerror(&yylloc, yyscanner, "syntax error");
+ yyerror(&yylloc, NULL, yyscanner, "syntax error");
}
if (tok == K_USING)
@@ -2056,7 +2057,7 @@ stmt_dynexecute : K_EXECUTE
if (endtoken == K_INTO)
{
if (new->into) /* multiple INTO */
- yyerror(&yylloc, yyscanner, "syntax error");
+ yyerror(&yylloc, NULL, yyscanner, "syntax error");
new->into = true;
read_into_target(&new->target, &new->strict, &yylval, &yylloc, yyscanner);
endtoken = yylex(&yylval, &yylloc, yyscanner);
@@ -2064,7 +2065,7 @@ stmt_dynexecute : K_EXECUTE
else if (endtoken == K_USING)
{
if (new->params) /* multiple USING */
- yyerror(&yylloc, yyscanner, "syntax error");
+ yyerror(&yylloc, NULL, yyscanner, "syntax error");
do
{
expr = read_sql_construct(',', ';', K_INTO,
@@ -2079,7 +2080,7 @@ stmt_dynexecute : K_EXECUTE
else if (endtoken == ';')
break;
else
- yyerror(&yylloc, yyscanner, "syntax error");
+ yyerror(&yylloc, NULL, yyscanner, "syntax error");
}
$$ = (PLpgSQL_stmt *) new;
@@ -2122,7 +2123,7 @@ stmt_open : K_OPEN cursor_variable
}
if (tok != K_FOR)
- yyerror(&yylloc, yyscanner, "syntax error, expected \"FOR\"");
+ yyerror(&yylloc, NULL, yyscanner, "syntax error, expected \"FOR\"");
tok = yylex(&yylval, &yylloc, yyscanner);
if (tok == K_EXECUTE)
@@ -2174,7 +2175,7 @@ stmt_fetch : K_FETCH opt_fetch_direction cursor_variable K_INTO
read_into_target(&target, NULL, &yylval, &yylloc, yyscanner);
if (yylex(&yylval, &yylloc, yyscanner) != ';')
- yyerror(&yylloc, yyscanner, "syntax error");
+ yyerror(&yylloc, NULL, yyscanner, "syntax error");
/*
* We don't allow multiple rows in PL/pgSQL's FETCH
@@ -2398,13 +2399,13 @@ proc_condition : any_identifier
/* next token should be a string literal */
if (yylex(&yylval, &yylloc, yyscanner) != SCONST)
- yyerror(&yylloc, yyscanner, "syntax error");
+ yyerror(&yylloc, NULL, yyscanner, "syntax error");
sqlstatestr = yylval.str;
if (strlen(sqlstatestr) != 5)
- yyerror(&yylloc, yyscanner, "invalid SQLSTATE code");
+ yyerror(&yylloc, NULL, yyscanner, "invalid SQLSTATE code");
if (strspn(sqlstatestr, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != 5)
- yyerror(&yylloc, yyscanner, "invalid SQLSTATE code");
+ yyerror(&yylloc, NULL, yyscanner, "invalid SQLSTATE code");
new = palloc(sizeof(PLpgSQL_condition));
new->sqlerrstate =
@@ -2488,7 +2489,7 @@ any_identifier : T_WORD
| T_DATUM
{
if ($1.ident == NULL) /* composite name not OK */
- yyerror(&yylloc, yyscanner, "syntax error");
+ yyerror(&yylloc, NULL, yyscanner, "syntax error");
$$ = $1.ident;
}
;
@@ -2647,7 +2648,7 @@ current_token_is_not_variable(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yysca
else if (tok == T_CWORD)
cword_is_not_variable(&(yylvalp->cword), *yyllocp, yyscanner);
else
- yyerror(yyllocp, yyscanner, "syntax error");
+ yyerror(yyllocp, NULL, yyscanner, "syntax error");
}
/* Convenience routine to read an expression with one possible terminator */
@@ -2739,7 +2740,7 @@ read_sql_construct(int until,
{
parenlevel--;
if (parenlevel < 0)
- yyerror(yyllocp, yyscanner, "mismatched parentheses");
+ yyerror(yyllocp, NULL, yyscanner, "mismatched parentheses");
}
/*
@@ -2750,7 +2751,7 @@ read_sql_construct(int until,
if (tok == 0 || tok == ';')
{
if (parenlevel != 0)
- yyerror(yyllocp, yyscanner, "mismatched parentheses");
+ yyerror(yyllocp, NULL, yyscanner, "mismatched parentheses");
if (isexpression)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -2779,9 +2780,9 @@ read_sql_construct(int until,
if (startlocation >= endlocation)
{
if (isexpression)
- yyerror(yyllocp, yyscanner, "missing expression");
+ yyerror(yyllocp, NULL, yyscanner, "missing expression");
else
- yyerror(yyllocp, yyscanner, "missing SQL statement");
+ yyerror(yyllocp, NULL, yyscanner, "missing SQL statement");
}
/*
@@ -2910,7 +2911,7 @@ read_datatype(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
if (tok == ICONST)
tok = yylex(yylvalp, yyllocp, yyscanner);
if (tok != ']')
- yyerror(yyllocp, yyscanner, "syntax error, expected \"]\"");
+ yyerror(yyllocp, NULL, yyscanner, "syntax error, expected \"]\"");
tok = yylex(yylvalp, yyllocp, yyscanner);
}
plpgsql_push_back_token(tok, yylvalp, yyllocp, yyscanner);
@@ -2932,9 +2933,9 @@ read_datatype(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
if (tok == 0)
{
if (parenlevel != 0)
- yyerror(yyllocp, yyscanner, "mismatched parentheses");
+ yyerror(yyllocp, NULL, yyscanner, "mismatched parentheses");
else
- yyerror(yyllocp, yyscanner, "incomplete data type declaration");
+ yyerror(yyllocp, NULL, yyscanner, "incomplete data type declaration");
}
/* Possible followers for datatype in a declaration */
if (tok == K_COLLATE || tok == K_NOT ||
@@ -2957,7 +2958,7 @@ read_datatype(int tok, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
type_name = ds.data;
if (type_name[0] == '\0')
- yyerror(yyllocp, yyscanner, "missing data type declaration");
+ yyerror(yyllocp, NULL, yyscanner, "missing data type declaration");
result = parse_datatype(type_name, startlocation, yyscanner);
@@ -3082,7 +3083,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word, YYSTYPE *yylvalp,
if (tok == ';' && paren_depth == 0 && begin_depth == 0)
break;
if (tok == 0)
- yyerror(yyllocp, yyscanner, "unexpected end of function definition");
+ yyerror(yyllocp, NULL, yyscanner, "unexpected end of function definition");
if (tok == K_INTO)
{
if (prev_tok == K_INSERT)
@@ -3092,7 +3093,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word, YYSTYPE *yylvalp,
if (firsttoken == K_IMPORT)
continue; /* IMPORT ... INTO is not an INTO-target */
if (have_into)
- yyerror(yyllocp, yyscanner, "INTO specified more than once");
+ yyerror(yyllocp, NULL, yyscanner, "INTO specified more than once");
have_into = true;
into_start_loc = *yyllocp;
plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
@@ -3170,7 +3171,7 @@ read_fetch_direction(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
tok = yylex(yylvalp, yyllocp, yyscanner);
if (tok == 0)
- yyerror(yyllocp, yyscanner, "unexpected end of function definition");
+ yyerror(yyllocp, NULL, yyscanner, "unexpected end of function definition");
if (tok_is_keyword(tok, yylvalp,
K_NEXT, "next"))
@@ -3262,7 +3263,7 @@ read_fetch_direction(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
{
tok = yylex(yylvalp, yyllocp, yyscanner);
if (tok != K_FROM && tok != K_IN)
- yyerror(yyllocp, yyscanner, "expected FROM or IN");
+ yyerror(yyllocp, NULL, yyscanner, "expected FROM or IN");
}
return fetch;
@@ -3281,7 +3282,7 @@ complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM, YYSTYPE *yylvalp
tok = yylex(yylvalp, yyllocp, yyscanner);
if (tok == 0)
- yyerror(yyllocp, yyscanner, "unexpected end of function definition");
+ yyerror(yyllocp, NULL, yyscanner, "unexpected end of function definition");
if (tok == K_FROM || tok == K_IN)
{
@@ -3882,7 +3883,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, YYSTYPE *yylvalp, YYLTYPE *yyll
parser_errposition(*yyllocp)));
if (tok != until)
- yyerror(yyllocp, yyscanner, "syntax error");
+ yyerror(yyllocp, NULL, yyscanner, "syntax error");
return NULL;
}
@@ -3943,7 +3944,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, YYSTYPE *yylvalp, YYLTYPE *yyll
*/
tok2 = yylex(yylvalp, yyllocp, yyscanner);
if (tok2 != COLON_EQUALS)
- yyerror(yyllocp, yyscanner, "syntax error");
+ yyerror(yyllocp, NULL, yyscanner, "syntax error");
any_named = true;
}
@@ -4017,7 +4018,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, YYSTYPE *yylvalp, YYLTYPE *yyll
/* Next we'd better find the until token */
tok = yylex(yylvalp, yyllocp, yyscanner);
if (tok != until)
- yyerror(yyllocp, yyscanner, "syntax error");
+ yyerror(yyllocp, NULL, yyscanner, "syntax error");
return expr;
}
@@ -4036,7 +4037,7 @@ read_raise_options(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
int tok;
if ((tok = yylex(yylvalp, yyllocp, yyscanner)) == 0)
- yyerror(yyllocp, yyscanner, "unexpected end of function definition");
+ yyerror(yyllocp, NULL, yyscanner, "unexpected end of function definition");
opt = (PLpgSQL_raise_option *) palloc(sizeof(PLpgSQL_raise_option));
@@ -4068,11 +4069,11 @@ read_raise_options(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
K_SCHEMA, "schema"))
opt->opt_type = PLPGSQL_RAISEOPTION_SCHEMA;
else
- yyerror(yyllocp, yyscanner, "unrecognized RAISE statement option");
+ yyerror(yyllocp, NULL, yyscanner, "unrecognized RAISE statement option");
tok = yylex(yylvalp, yyllocp, yyscanner);
if (tok != '=' && tok != COLON_EQUALS)
- yyerror(yyllocp, yyscanner, "syntax error, expected \"=\"");
+ yyerror(yyllocp, NULL, yyscanner, "syntax error, expected \"=\"");
opt->expr = read_sql_expression2(',', ';', ", or ;", &tok, yylvalp, yyllocp, yyscanner);
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
index 11d79a8a683..d08187dafcb 100644
--- a/src/pl/plpgsql/src/pl_scanner.c
+++ b/src/pl/plpgsql/src/pl_scanner.c
@@ -526,9 +526,12 @@ plpgsql_scanner_errposition(int location, yyscan_t yyscanner)
* parsers report error as soon as the first unparsable token is reached.
* Beware of using yyerror for other purposes, as the cursor position might
* be misleading!
+ *
+ * (The second argument is enforced by Bison to match the second argument of
+ * yyparse(), but it is not used here.)
*/
void
-plpgsql_yyerror(YYLTYPE *yyllocp, yyscan_t yyscanner, const char *message)
+plpgsql_yyerror(YYLTYPE *yyllocp, PLpgSQL_stmt_block **plpgsql_parse_result_p, yyscan_t yyscanner, const char *message)
{
char *yytext = yyextra->core_yy_extra.scanbuf + *yyllocp;
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index c3ce4161a32..441df5354e2 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -1212,8 +1212,6 @@ extern int plpgsql_extra_errors;
extern bool plpgsql_check_syntax;
extern bool plpgsql_DumpExecTree;
-extern PLpgSQL_stmt_block *plpgsql_parse_result;
-
extern int plpgsql_nDatums;
extern PLpgSQL_datum **plpgsql_Datums;
@@ -1332,7 +1330,7 @@ extern int plpgsql_peek(yyscan_t yyscanner);
extern void plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc,
int *tok2_loc, yyscan_t yyscanner);
extern int plpgsql_scanner_errposition(int location, yyscan_t yyscanner);
-extern void plpgsql_yyerror(YYLTYPE *yyllocp, yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
+extern void plpgsql_yyerror(YYLTYPE *yyllocp, PLpgSQL_stmt_block **plpgsql_parse_result_p, yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
extern int plpgsql_location_to_lineno(int location, yyscan_t yyscanner);
extern int plpgsql_latest_lineno(yyscan_t yyscanner);
extern yyscan_t plpgsql_scanner_init(const char *str);
@@ -1341,6 +1339,6 @@ extern void plpgsql_scanner_finish(yyscan_t yyscanner);
/*
* Externs in pl_gram.y
*/
-extern int plpgsql_yyparse(yyscan_t yyscanner);
+extern int plpgsql_yyparse(PLpgSQL_stmt_block **plpgsql_parse_result_p, yyscan_t yyscanner);
#endif /* PLPGSQL_H */
base-commit: 43830ecb8a9b6a1bc322298a77a5e0d87c2e83d4
--
2.47.1
0002-pgbench-Return-parse-result-not-via-global-variable.patchtext/plain; charset=UTF-8; name=0002-pgbench-Return-parse-result-not-via-global-variable.patchDownload
From 5925f806205ae9f868fb732da24e529ee67ea87d Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Fri, 17 Jan 2025 13:14:16 +0100
Subject: [PATCH 2/4] pgbench: Return parse result not via global variable
Instead of passing the parse result from yyparse() via a global
variable, pass it via a function output argument.
---
src/bin/pgbench/exprparse.y | 5 ++---
src/bin/pgbench/exprscan.l | 6 +++++-
src/bin/pgbench/pgbench.c | 4 +---
src/bin/pgbench/pgbench.h | 6 ++----
4 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/src/bin/pgbench/exprparse.y b/src/bin/pgbench/exprparse.y
index 9b5ab8074eb..68a37e49e45 100644
--- a/src/bin/pgbench/exprparse.y
+++ b/src/bin/pgbench/exprparse.y
@@ -21,8 +21,6 @@
#define PGBENCH_NARGS_HASH (-3)
#define PGBENCH_NARGS_PERMUTE (-4)
-PgBenchExpr *expr_parse_result;
-
static PgBenchExprList *make_elist(PgBenchExpr *expr, PgBenchExprList *list);
static PgBenchExpr *make_null_constant(void);
static PgBenchExpr *make_boolean_constant(bool bval);
@@ -42,6 +40,7 @@ static PgBenchExpr *make_case(yyscan_t yyscanner, PgBenchExprList *when_then_lis
%expect 0
%name-prefix="expr_yy"
+%parse-param {PgBenchExpr **expr_parse_result_p}
%parse-param {yyscan_t yyscanner}
%lex-param {yyscan_t yyscanner}
@@ -81,7 +80,7 @@ static PgBenchExpr *make_case(yyscan_t yyscanner, PgBenchExprList *when_then_lis
%%
result: expr {
- expr_parse_result = $1;
+ *expr_parse_result_p = $1;
(void) yynerrs; /* suppress compiler warning */
}
diff --git a/src/bin/pgbench/exprscan.l b/src/bin/pgbench/exprscan.l
index 46f6ea05121..8943a52e9f0 100644
--- a/src/bin/pgbench/exprscan.l
+++ b/src/bin/pgbench/exprscan.l
@@ -296,8 +296,12 @@ expr_yyerror_more(yyscan_t yyscanner, const char *message, const char *more)
message, more, error_detection_offset - expr_start_offset);
}
+/*
+ * (The first argument is enforced by Bison to match the first argument of
+ * yyparse(), but it is not used here.)
+ */
void
-expr_yyerror(yyscan_t yyscanner, const char *message)
+expr_yyerror(PgBenchExpr **expr_parse_result_p, yyscan_t yyscanner, const char *message)
{
expr_yyerror_more(yyscanner, message, NULL);
}
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index c415e0f32c1..40592e62606 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -5706,14 +5706,12 @@ process_backslash_command(PsqlScanState sstate, const char *source)
yyscanner = expr_scanner_init(sstate, source, lineno, start_offset,
my_command->argv[0]);
- if (expr_yyparse(yyscanner) != 0)
+ if (expr_yyparse(&my_command->expr, yyscanner) != 0)
{
/* dead code: exit done from syntax_error called by yyerror */
exit(1);
}
- my_command->expr = expr_parse_result;
-
/* Save line, trimming any trailing newline */
my_command->first_line =
expr_scanner_get_substring(sstate,
diff --git a/src/bin/pgbench/pgbench.h b/src/bin/pgbench/pgbench.h
index 4b607b7ee04..f6a883611c5 100644
--- a/src/bin/pgbench/pgbench.h
+++ b/src/bin/pgbench/pgbench.h
@@ -138,11 +138,9 @@ struct PgBenchExprList
PgBenchExprLink *tail;
};
-extern PgBenchExpr *expr_parse_result;
-
-extern int expr_yyparse(yyscan_t yyscanner);
+extern int expr_yyparse(PgBenchExpr **expr_parse_result_p, yyscan_t yyscanner);
extern int expr_yylex(union YYSTYPE *yylval_param, yyscan_t yyscanner);
-extern void expr_yyerror(yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
+extern void expr_yyerror(PgBenchExpr **expr_parse_result_p, yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
extern void expr_yyerror_more(yyscan_t yyscanner, const char *message,
const char *more) pg_attribute_noreturn();
extern bool expr_lex_one_word(PsqlScanState state, PQExpBuffer word_buf,
--
2.47.1
0003-replication-parser-Return-parse-result-not-via-globa.patchtext/plain; charset=UTF-8; name=0003-replication-parser-Return-parse-result-not-via-globa.patchDownload
From 0d2703ecae444499a179af7c5cb6e9001c44005d Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Fri, 17 Jan 2025 13:18:38 +0100
Subject: [PATCH 3/4] replication parser: Return parse result not via global
variable
Instead of passing the parse result from yyparse() via a global
variable, pass it via a function output argument.
---
src/backend/nls.mk | 2 +-
src/backend/replication/repl_gram.y | 7 ++-----
src/backend/replication/repl_scanner.l | 10 +++++++---
src/backend/replication/walsender.c | 4 +---
src/include/replication/walsender_private.h | 6 ++----
5 files changed, 13 insertions(+), 16 deletions(-)
diff --git a/src/backend/nls.mk b/src/backend/nls.mk
index 5a5298b33f7..210893c7b72 100644
--- a/src/backend/nls.mk
+++ b/src/backend/nls.mk
@@ -9,7 +9,7 @@ GETTEXT_TRIGGERS = $(BACKEND_COMMON_GETTEXT_TRIGGERS) \
yyerror \
jsonpath_yyerror:4 \
parser_yyerror \
- replication_yyerror:2 \
+ replication_yyerror:3 \
scanner_yyerror \
syncrep_yyerror:2 \
report_invalid_record:2 \
diff --git a/src/backend/replication/repl_gram.y b/src/backend/replication/repl_gram.y
index 61e68d0727a..7440aae5a1a 100644
--- a/src/backend/replication/repl_gram.y
+++ b/src/backend/replication/repl_gram.y
@@ -25,10 +25,6 @@
#include "repl_gram.h"
-/* Result of the parsing is returned here */
-Node *replication_parse_result;
-
-
/*
* Bison doesn't allocate anything that needs to live across parser calls,
* so we can easily have it use palloc instead of malloc. This prevents
@@ -39,6 +35,7 @@ Node *replication_parse_result;
%}
+%parse-param {Node **replication_parse_result_p}
%parse-param {yyscan_t yyscanner}
%lex-param {yyscan_t yyscanner}
%pure-parser
@@ -104,7 +101,7 @@ Node *replication_parse_result;
firstcmd: command opt_semicolon
{
- replication_parse_result = $1;
+ *replication_parse_result_p = $1;
(void) yynerrs; /* suppress compiler warning */
}
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index de10cb5abd1..014ea8d25c6 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -156,7 +156,7 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
uint32 hi,
lo;
if (sscanf(yytext, "%X/%X", &hi, &lo) != 2)
- replication_yyerror(yyscanner, "invalid streaming start location");
+ replication_yyerror(NULL, yyscanner, "invalid streaming start location");
yylval->recptr = ((uint64) hi) << 32 | lo;
return RECPTR;
}
@@ -213,7 +213,7 @@ UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
return yytext[0];
}
-<xq,xd><<EOF>> { replication_yyerror(yyscanner, "unterminated quoted string"); }
+<xq,xd><<EOF>> { replication_yyerror(NULL, yyscanner, "unterminated quoted string"); }
<<EOF>> {
@@ -252,8 +252,12 @@ addlitchar(unsigned char ychar, yyscan_t yyscanner)
appendStringInfoChar(&yyextra->litbuf, ychar);
}
+/*
+ * (The first argument is enforced by Bison to match the first argument of
+ * yyparse(), but it is not used here.)
+ */
void
-replication_yyerror(yyscan_t yyscanner, const char *message)
+replication_yyerror(Node **replication_parse_result_p, yyscan_t yyscanner, const char *message)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index a0782b1bbf6..bac504b554e 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -2017,7 +2017,7 @@ exec_replication_command(const char *cmd_string)
/*
* Looks like a WalSender command, so parse it.
*/
- parse_rc = replication_yyparse(scanner);
+ parse_rc = replication_yyparse(&cmd_node, scanner);
if (parse_rc != 0)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -2025,8 +2025,6 @@ exec_replication_command(const char *cmd_string)
parse_rc)));
replication_scanner_finish(scanner);
- cmd_node = replication_parse_result;
-
/*
* Report query to various monitoring facilities. For this purpose, we
* report replication commands just like SQL commands.
diff --git a/src/include/replication/walsender_private.h b/src/include/replication/walsender_private.h
index 5ab96ed5f7d..814b812432a 100644
--- a/src/include/replication/walsender_private.h
+++ b/src/include/replication/walsender_private.h
@@ -130,13 +130,11 @@ union YYSTYPE;
#define YY_TYPEDEF_YY_SCANNER_T
typedef void *yyscan_t;
#endif
-extern int replication_yyparse(yyscan_t yyscanner);
+extern int replication_yyparse(Node **replication_parse_result_p, yyscan_t yyscanner);
extern int replication_yylex(union YYSTYPE *yylval_param, yyscan_t yyscanner);
-extern void replication_yyerror(yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
+extern void replication_yyerror(Node **replication_parse_result_p, yyscan_t yyscanner, const char *message) pg_attribute_noreturn();
extern void replication_scanner_init(const char *str, yyscan_t *yyscannerp);
extern void replication_scanner_finish(yyscan_t yyscanner);
extern bool replication_scanner_is_replication_command(yyscan_t yyscanner);
-extern PGDLLIMPORT Node *replication_parse_result;
-
#endif /* _WALSENDER_PRIVATE_H */
--
2.47.1
0004-syncrep-parser-Return-parse-result-not-via-global-va.patchtext/plain; charset=UTF-8; name=0004-syncrep-parser-Return-parse-result-not-via-global-va.patchDownload
From 4d8bc0e8ad06b1e9edecdd682ddf17dc6800a4dd Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Fri, 17 Jan 2025 15:41:11 +0100
Subject: [PATCH 4/4] syncrep parser: Return parse result not via global
variable
Instead of passing the parse result from yyparse() via a global
variable, pass it via a function output argument.
---
src/backend/nls.mk | 2 +-
src/backend/replication/syncrep.c | 8 ++++----
src/backend/replication/syncrep_gram.y | 9 ++++-----
src/backend/replication/syncrep_scanner.l | 22 +++++++++++++++++++---
src/include/replication/syncrep.h | 10 +++-------
5 files changed, 31 insertions(+), 20 deletions(-)
diff --git a/src/backend/nls.mk b/src/backend/nls.mk
index 210893c7b72..b7d5dd46e45 100644
--- a/src/backend/nls.mk
+++ b/src/backend/nls.mk
@@ -11,7 +11,7 @@ GETTEXT_TRIGGERS = $(BACKEND_COMMON_GETTEXT_TRIGGERS) \
parser_yyerror \
replication_yyerror:3 \
scanner_yyerror \
- syncrep_yyerror:2 \
+ syncrep_yyerror:4 \
report_invalid_record:2 \
ereport_startup_progress \
json_token_error:2 \
diff --git a/src/backend/replication/syncrep.c b/src/backend/replication/syncrep.c
index 1ce8bc7533f..d75e3968035 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -996,13 +996,13 @@ check_synchronous_standby_names(char **newval, void **extra, GucSource source)
int parse_rc;
SyncRepConfigData *pconf;
- /* Reset communication variables to ensure a fresh start */
- syncrep_parse_result = NULL;
- syncrep_parse_error_msg = NULL;
+ /* Result of parsing is returned in one of these two variables */
+ SyncRepConfigData *syncrep_parse_result = NULL;
+ char *syncrep_parse_error_msg = NULL;
/* Parse the synchronous_standby_names string */
syncrep_scanner_init(*newval, &scanner);
- parse_rc = syncrep_yyparse(scanner);
+ parse_rc = syncrep_yyparse(&syncrep_parse_result, &syncrep_parse_error_msg, scanner);
syncrep_scanner_finish(scanner);
if (parse_rc != 0 || syncrep_parse_result == NULL)
diff --git a/src/backend/replication/syncrep_gram.y b/src/backend/replication/syncrep_gram.y
index 1a3b89ca674..22297bb151a 100644
--- a/src/backend/replication/syncrep_gram.y
+++ b/src/backend/replication/syncrep_gram.y
@@ -19,10 +19,6 @@
#include "syncrep_gram.h"
-/* Result of parsing is returned in one of these two variables */
-SyncRepConfigData *syncrep_parse_result;
-char *syncrep_parse_error_msg;
-
static SyncRepConfigData *create_syncrep_config(const char *num_sync,
List *members, uint8 syncrep_method);
@@ -36,7 +32,10 @@ static SyncRepConfigData *create_syncrep_config(const char *num_sync,
%}
+%parse-param {SyncRepConfigData **syncrep_parse_result_p}
+%parse-param {char **syncrep_parse_error_msg_p}
%parse-param {yyscan_t yyscanner}
+%lex-param {char **syncrep_parse_error_msg_p}
%lex-param {yyscan_t yyscanner}
%pure-parser
%expect 0
@@ -60,7 +59,7 @@ static SyncRepConfigData *create_syncrep_config(const char *num_sync,
%%
result:
standby_config {
- syncrep_parse_result = $1;
+ *syncrep_parse_result_p = $1;
(void) yynerrs; /* suppress compiler warning */
}
;
diff --git a/src/backend/replication/syncrep_scanner.l b/src/backend/replication/syncrep_scanner.l
index 05e5fdecf1b..7dec1f869c7 100644
--- a/src/backend/replication/syncrep_scanner.l
+++ b/src/backend/replication/syncrep_scanner.l
@@ -42,6 +42,13 @@ struct syncrep_yy_extra_type
StringInfoData xdbuf;
};
+/*
+ * Better keep this definition here than put it in replication/syncrep.h and
+ * save a bit of duplication. Putting it in replication/syncrep.h would leak
+ * the definition to other parts and possibly affect other scanners.
+*/
+#define YY_DECL extern int syncrep_yylex(union YYSTYPE *yylval_param, char **syncrep_parse_error_msg_p, yyscan_t yyscanner)
+
/* LCOV_EXCL_START */
%}
@@ -104,7 +111,7 @@ xdinside [^"]+
return NAME;
}
<xd><<EOF>> {
- syncrep_yyerror(yyscanner, "unterminated quoted identifier");
+ syncrep_yyerror(NULL, syncrep_parse_error_msg_p, yyscanner, "unterminated quoted identifier");
return JUNK;
}
@@ -136,12 +143,21 @@ xdinside [^"]+
#undef yyextra
#define yyextra (((struct yyguts_t *) yyscanner)->yyextra_r)
-/* Needs to be here for access to yytext */
+/*
+ * This yyerror() function does not raise an error (elog or similar), it just
+ * collects the error message in *syncrep_parse_error_msg_p and leaves it to
+ * the ultimate caller of the syncrep parser to raise the error. (The
+ * ultimate caller will do that with special GUC error functions.)
+ *
+ * (The first argument is enforced by Bison to match the first argument of
+ * yyparse(), but it is not used here.)
+ */
void
-syncrep_yyerror(yyscan_t yyscanner, const char *message)
+syncrep_yyerror(SyncRepConfigData **syncrep_parse_result_p, char **syncrep_parse_error_msg_p, yyscan_t yyscanner, const char *message)
{
struct yyguts_t *yyg = (struct yyguts_t *) yyscanner; /* needed for yytext
* macro */
+ char *syncrep_parse_error_msg = *syncrep_parse_error_msg_p;
/* report only the first error in a parse operation */
if (syncrep_parse_error_msg)
diff --git a/src/include/replication/syncrep.h b/src/include/replication/syncrep.h
index 33b9cdb18f7..675669a79f7 100644
--- a/src/include/replication/syncrep.h
+++ b/src/include/replication/syncrep.h
@@ -73,10 +73,6 @@ typedef struct SyncRepConfigData
extern PGDLLIMPORT SyncRepConfigData *SyncRepConfig;
-/* communication variables for parsing synchronous_standby_names GUC */
-extern PGDLLIMPORT SyncRepConfigData *syncrep_parse_result;
-extern PGDLLIMPORT char *syncrep_parse_error_msg;
-
/* user-settable parameters for synchronous replication */
extern PGDLLIMPORT char *SyncRepStandbyNames;
@@ -105,9 +101,9 @@ union YYSTYPE;
#define YY_TYPEDEF_YY_SCANNER_T
typedef void *yyscan_t;
#endif
-extern int syncrep_yyparse(yyscan_t yyscanner);
-extern int syncrep_yylex(union YYSTYPE *yylval_param, yyscan_t yyscanner);
-extern void syncrep_yyerror(yyscan_t yyscanner, const char *str);
+extern int syncrep_yyparse(SyncRepConfigData **syncrep_parse_result_p, char **syncrep_parse_error_msg_p, yyscan_t yyscanner);
+extern int syncrep_yylex(union YYSTYPE *yylval_param, char **syncrep_parse_error_msg_p, yyscan_t yyscanner);
+extern void syncrep_yyerror(SyncRepConfigData **syncrep_parse_result_p, char **syncrep_parse_error_msg_p, yyscan_t yyscanner, const char *str);
extern void syncrep_scanner_init(const char *str, yyscan_t *yyscannerp);
extern void syncrep_scanner_finish(yyscan_t yyscanner);
--
2.47.1
On 17.01.25 16:40, Peter Eisentraut wrote:
Here are some more patches. This should cover the last sub-topic of
this topic: not passing the yyparse() result via global variables. This
uses techniques that are already in use in some parsers in the tree, for
example cube and jsonpath. The syncrep parser was a bit trickier,
because there we need to pass the syncrep_parse_error variable all the
way down to the scanner (not just the parser), but overall it's all
still pretty compact and standard.
This has been committed.
I think this concludes this topic.
For those who haven't seen it, I wrote a blog post about this:
https://peter.eisentraut.org/blog/2025/01/21/implementing-thread-safe-scanners-and-parsers-in-postgresql