Here are the rest. Most of it was pretty straightforward, with the
main exception of jsonpath_scan.c, which is not quite finished. That
one passes tests but still has one compiler warning. I'm unsure how
much of what is there already is really necessary or was cargo-culted
from elsewhere without explanation. For starters, I'm not sure why the
grammar has a forward declaration of "union YYSTYPE". It's noteworthy
that it used to compile standalone, but with a bit more stuff, and
that was reverted in 550b9d26f80fa30. I can hack on it some more later
but I ran out of steam today.
Other questions thus far:
- "BISONFLAGS += -d" is now in every make file with a .y file -- can
we just force that everywhere?
- Include order seems to matter for the grammar's .h file. I didn't
test if that was the case every time, and after a few miscompiles just
always made it the last inclusion, but I'm wondering if we should keep
those inclusions outside %top{} and put it at the start of the next
%{} ?
- contrib/cubeparse.y now has a global variable -- not terrific, but I
wanted to get something working first.
- I'm actually okay with guc-file.c now, but I'll still welcome
comments on that.
--
John Naylor
EDB: http://www.enterprisedb.com
From da2b610b8608e6759f5ed9cc32b487ea8e750ce4 Mon Sep 17 00:00:00 2001
From: John Naylor <[email protected]>
Date: Fri, 12 Aug 2022 17:09:45 +0700
Subject: [PATCH v201 3/9] Build repl_scanner.c standalone
---
src/backend/replication/Makefile | 11 +++++++++--
src/backend/replication/repl_gram.y | 2 --
src/backend/replication/repl_scanner.l | 27 +++++++++++++++-----------
3 files changed, 25 insertions(+), 15 deletions(-)
diff --git a/src/backend/replication/Makefile b/src/backend/replication/Makefile
index 2bffac58c0..bc8170418f 100644
--- a/src/backend/replication/Makefile
+++ b/src/backend/replication/Makefile
@@ -16,6 +16,7 @@ override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
OBJS = \
repl_gram.o \
+ repl_scanner.o \
slot.o \
slotfuncs.o \
syncrep.o \
@@ -28,8 +29,14 @@ SUBDIRS = logical
include $(top_srcdir)/src/backend/common.mk
-# repl_scanner is compiled as part of repl_gram
-repl_gram.o: repl_scanner.c
+# See notes in src/backend/parser/Makefile about the following two rules
+repl_gram.h: repl_gram.c
+ touch $@
+
+repl_gram.c: BISONFLAGS += -d
+
+# Force these dependencies to be known even without dependency info built:
+repl_gram.o repl_scanner.o: repl_gram.h
# syncrep_scanner is compiled as part of syncrep_gram
syncrep_gram.o: syncrep_scanner.c
diff --git a/src/backend/replication/repl_gram.y b/src/backend/replication/repl_gram.y
index 4cf087e602..b343f108d3 100644
--- a/src/backend/replication/repl_gram.y
+++ b/src/backend/replication/repl_gram.y
@@ -416,5 +416,3 @@ ident_or_keyword:
;
%%
-
-#include "repl_scanner.c"
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index 586f0d3a5c..95a933c9a8 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -1,4 +1,4 @@
-%{
+%top{
/*-------------------------------------------------------------------------
*
* repl_scanner.l
@@ -17,7 +17,12 @@
#include "utils/builtins.h"
#include "parser/scansup.h"
+#include "replication/walsender_private.h"
+
+#include "repl_gram.h"
+}
+%{
/* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
#undef fprintf
#define fprintf(file, fmt, msg) fprintf_to_ereport(fmt, msg)
@@ -130,7 +135,7 @@ WAIT { return K_WAIT; }
{space}+ { /* do nothing */ }
{digit}+ {
- yylval.uintval = strtoul(yytext, NULL, 10);
+ replication_yylval.uintval = strtoul(yytext, NULL, 10);
return UCONST;
}
@@ -138,8 +143,8 @@ WAIT { return K_WAIT; }
uint32 hi,
lo;
if (sscanf(yytext, "%X/%X", &hi, &lo) != 2)
- yyerror("invalid streaming start location");
- yylval.recptr = ((uint64) hi) << 32 | lo;
+ replication_yyerror("invalid streaming start location");
+ replication_yylval.recptr = ((uint64) hi) << 32 | lo;
return RECPTR;
}
@@ -151,7 +156,7 @@ WAIT { return K_WAIT; }
<xq>{quotestop} {
yyless(1);
BEGIN(INITIAL);
- yylval.str = litbufdup();
+ replication_yylval.str = litbufdup();
return SCONST;
}
@@ -173,9 +178,9 @@ WAIT { return K_WAIT; }
yyless(1);
BEGIN(INITIAL);
- yylval.str = litbufdup();
- len = strlen(yylval.str);
- truncate_identifier(yylval.str, len, true);
+ replication_yylval.str = litbufdup();
+ len = strlen(replication_yylval.str);
+ truncate_identifier(replication_yylval.str, len, true);
return IDENT;
}
@@ -186,7 +191,7 @@ WAIT { return K_WAIT; }
{identifier} {
int len = strlen(yytext);
- yylval.str = downcase_truncate_identifier(yytext, len, true);
+ replication_yylval.str = downcase_truncate_identifier(yytext, len, true);
return IDENT;
}
@@ -195,7 +200,7 @@ WAIT { return K_WAIT; }
return yytext[0];
}
-<xq,xd><<EOF>> { yyerror("unterminated quoted string"); }
+<xq,xd><<EOF>> { replication_yyerror("unterminated quoted string"); }
<<EOF>> {
@@ -231,7 +236,7 @@ addlitchar(unsigned char ychar)
}
void
-yyerror(const char *message)
+replication_yyerror(const char *message)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
--
2.36.1
From d723ba14acf56fd432e9e263db937fcc13fc0355 Mon Sep 17 00:00:00 2001
From: John Naylor <[email protected]>
Date: Thu, 11 Aug 2022 19:38:37 +0700
Subject: [PATCH v201 1/9] Build guc-file.c standalone
The proposed Meson build system will need a way to ignore certain
generated files in order to coexist with the autoconf build system,
and #include'd C files generated by Flex make this more difficult.
Build guc-file.c separately from guc.c, as was done in 72b1e3a21.
TODO: other Flex-generated files
Discussion: https://www.postgresql.org/message-id/20220810171935.7k5zgnjwqzalzmtm%40awork3.anarazel.de
---
src/backend/utils/misc/Makefile | 5 +-
src/backend/utils/misc/guc-file.l | 367 +-----------------------------
src/backend/utils/misc/guc.c | 362 ++++++++++++++++++++++++++++-
src/include/utils/guc.h | 9 +
4 files changed, 370 insertions(+), 373 deletions(-)
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index 1d5327cf64..cf7ce9bc83 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -16,6 +16,7 @@ override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
OBJS = \
guc.o \
+ guc-file.o \
help_config.o \
pg_config.o \
pg_controldata.o \
@@ -37,10 +38,6 @@ endif
include $(top_srcdir)/src/backend/common.mk
-# guc-file is compiled as part of guc
-guc.o: guc-file.c
-
# Note: guc-file.c is not deleted by 'make clean',
# since we want to ship it in distribution tarballs.
clean:
- @rm -f lex.yy.c
diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l
index ce5633844c..08adb454de 100644
--- a/src/backend/utils/misc/guc-file.l
+++ b/src/backend/utils/misc/guc-file.l
@@ -7,7 +7,7 @@
* src/backend/utils/misc/guc-file.l
*/
-%{
+%top{
#include "postgres.h"
@@ -17,9 +17,12 @@
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "storage/fd.h"
+#include <sys/stat.h>
#include "utils/guc.h"
+#include "utils/memutils.h"
+}
-
+%{
/*
* flex emits a yy_fatal_error() function that it calls in response to
* critical errors like malloc failure, file I/O errors, and detection of
@@ -48,12 +51,6 @@ static sigjmp_buf *GUC_flex_fatal_jmp;
static void FreeConfigVariable(ConfigVariable *item);
-static void record_config_file_error(const char *errmsg,
- const char *config_file,
- int lineno,
- ConfigVariable **head_p,
- ConfigVariable **tail_p);
-
static int GUC_flex_fatal(const char *msg);
/* LCOV_EXCL_START */
@@ -159,358 +156,6 @@ ProcessConfigFile(GucContext context)
MemoryContextDelete(config_cxt);
}
-/*
- * This function handles both actual config file (re)loads and execution of
- * show_all_file_settings() (i.e., the pg_file_settings view). In the latter
- * case we don't apply any of the settings, but we make all the usual validity
- * checks, and we return the ConfigVariable list so that it can be printed out
- * by show_all_file_settings().
- */
-static ConfigVariable *
-ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
-{
- bool error = false;
- bool applying = false;
- const char *ConfFileWithError;
- ConfigVariable *item,
- *head,
- *tail;
- int i;
-
- /* Parse the main config file into a list of option names and values */
- ConfFileWithError = ConfigFileName;
- head = tail = NULL;
-
- if (!ParseConfigFile(ConfigFileName, true,
- NULL, 0, 0, elevel,
- &head, &tail))
- {
- /* Syntax error(s) detected in the file, so bail out */
- error = true;
- goto bail_out;
- }
-
- /*
- * Parse the PG_AUTOCONF_FILENAME file, if present, after the main file to
- * replace any parameters set by ALTER SYSTEM command. Because this file
- * is in the data directory, we can't read it until the DataDir has been
- * set.
- */
- if (DataDir)
- {
- if (!ParseConfigFile(PG_AUTOCONF_FILENAME, false,
- NULL, 0, 0, elevel,
- &head, &tail))
- {
- /* Syntax error(s) detected in the file, so bail out */
- error = true;
- ConfFileWithError = PG_AUTOCONF_FILENAME;
- goto bail_out;
- }
- }
- else
- {
- /*
- * If DataDir is not set, the PG_AUTOCONF_FILENAME file cannot be
- * read. In this case, we don't want to accept any settings but
- * data_directory from postgresql.conf, because they might be
- * overwritten with settings in the PG_AUTOCONF_FILENAME file which
- * will be read later. OTOH, since data_directory isn't allowed in the
- * PG_AUTOCONF_FILENAME file, it will never be overwritten later.
- */
- ConfigVariable *newlist = NULL;
-
- /*
- * Prune all items except the last "data_directory" from the list.
- */
- for (item = head; item; item = item->next)
- {
- if (!item->ignore &&
- strcmp(item->name, "data_directory") == 0)
- newlist = item;
- }
-
- if (newlist)
- newlist->next = NULL;
- head = tail = newlist;
-
- /*
- * Quick exit if data_directory is not present in file.
- *
- * We need not do any further processing, in particular we don't set
- * PgReloadTime; that will be set soon by subsequent full loading of
- * the config file.
- */
- if (head == NULL)
- goto bail_out;
- }
-
- /*
- * Mark all extant GUC variables as not present in the config file. We
- * need this so that we can tell below which ones have been removed from
- * the file since we last processed it.
- */
- for (i = 0; i < num_guc_variables; i++)
- {
- struct config_generic *gconf = guc_variables[i];
-
- gconf->status &= ~GUC_IS_IN_FILE;
- }
-
- /*
- * Check if all the supplied option names are valid, as an additional
- * quasi-syntactic check on the validity of the config file. It is
- * important that the postmaster and all backends agree on the results of
- * this phase, else we will have strange inconsistencies about which
- * processes accept a config file update and which don't. Hence, unknown
- * custom variable names have to be accepted without complaint. For the
- * same reason, we don't attempt to validate the options' values here.
- *
- * In addition, the GUC_IS_IN_FILE flag is set on each existing GUC
- * variable mentioned in the file; and we detect duplicate entries in the
- * file and mark the earlier occurrences as ignorable.
- */
- for (item = head; item; item = item->next)
- {
- struct config_generic *record;
-
- /* Ignore anything already marked as ignorable */
- if (item->ignore)
- continue;
-
- /*
- * Try to find the variable; but do not create a custom placeholder if
- * it's not there already.
- */
- record = find_option(item->name, false, true, elevel);
-
- if (record)
- {
- /* If it's already marked, then this is a duplicate entry */
- if (record->status & GUC_IS_IN_FILE)
- {
- /*
- * Mark the earlier occurrence(s) as dead/ignorable. We could
- * avoid the O(N^2) behavior here with some additional state,
- * but it seems unlikely to be worth the trouble.
- */
- ConfigVariable *pitem;
-
- for (pitem = head; pitem != item; pitem = pitem->next)
- {
- if (!pitem->ignore &&
- strcmp(pitem->name, item->name) == 0)
- pitem->ignore = true;
- }
- }
- /* Now mark it as present in file */
- record->status |= GUC_IS_IN_FILE;
- }
- else if (!valid_custom_variable_name(item->name))
- {
- /* Invalid non-custom variable, so complain */
- ereport(elevel,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("unrecognized configuration parameter \"%s\" in file \"%s\" line %d",
- item->name,
- item->filename, item->sourceline)));
- item->errmsg = pstrdup("unrecognized configuration parameter");
- error = true;
- ConfFileWithError = item->filename;
- }
- }
-
- /*
- * If we've detected any errors so far, we don't want to risk applying any
- * changes.
- */
- if (error)
- goto bail_out;
-
- /* Otherwise, set flag that we're beginning to apply changes */
- applying = true;
-
- /*
- * Check for variables having been removed from the config file, and
- * revert their reset values (and perhaps also effective values) to the
- * boot-time defaults. If such a variable can't be changed after startup,
- * report that and continue.
- */
- for (i = 0; i < num_guc_variables; i++)
- {
- struct config_generic *gconf = guc_variables[i];
- GucStack *stack;
-
- if (gconf->reset_source != PGC_S_FILE ||
- (gconf->status & GUC_IS_IN_FILE))
- continue;
- if (gconf->context < PGC_SIGHUP)
- {
- /* The removal can't be effective without a restart */
- gconf->status |= GUC_PENDING_RESTART;
- ereport(elevel,
- (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
- errmsg("parameter \"%s\" cannot be changed without restarting the server",
- gconf->name)));
- record_config_file_error(psprintf("parameter \"%s\" cannot be changed without restarting the server",
- gconf->name),
- NULL, 0,
- &head, &tail);
- error = true;
- continue;
- }
-
- /* No more to do if we're just doing show_all_file_settings() */
- if (!applySettings)
- continue;
-
- /*
- * Reset any "file" sources to "default", else set_config_option will
- * not override those settings.
- */
- if (gconf->reset_source == PGC_S_FILE)
- gconf->reset_source = PGC_S_DEFAULT;
- if (gconf->source == PGC_S_FILE)
- gconf->source = PGC_S_DEFAULT;
- for (stack = gconf->stack; stack; stack = stack->prev)
- {
- if (stack->source == PGC_S_FILE)
- stack->source = PGC_S_DEFAULT;
- }
-
- /* Now we can re-apply the wired-in default (i.e., the boot_val) */
- if (set_config_option(gconf->name, NULL,
- context, PGC_S_DEFAULT,
- GUC_ACTION_SET, true, 0, false) > 0)
- {
- /* Log the change if appropriate */
- if (context == PGC_SIGHUP)
- ereport(elevel,
- (errmsg("parameter \"%s\" removed from configuration file, reset to default",
- gconf->name)));
- }
- }
-
- /*
- * Restore any variables determined by environment variables or
- * dynamically-computed defaults. This is a no-op except in the case
- * where one of these had been in the config file and is now removed.
- *
- * In particular, we *must not* do this during the postmaster's initial
- * loading of the file, since the timezone functions in particular should
- * be run only after initialization is complete.
- *
- * XXX this is an unmaintainable crock, because we have to know how to set
- * (or at least what to call to set) every non-PGC_INTERNAL variable that
- * could potentially have PGC_S_DYNAMIC_DEFAULT or PGC_S_ENV_VAR source.
- */
- if (context == PGC_SIGHUP && applySettings)
- {
- InitializeGUCOptionsFromEnvironment();
- pg_timezone_abbrev_initialize();
- /* this selects SQL_ASCII in processes not connected to a database */
- SetConfigOption("client_encoding", GetDatabaseEncodingName(),
- PGC_BACKEND, PGC_S_DYNAMIC_DEFAULT);
- }
-
- /*
- * Now apply the values from the config file.
- */
- for (item = head; item; item = item->next)
- {
- char *pre_value = NULL;
- int scres;
-
- /* Ignore anything marked as ignorable */
- if (item->ignore)
- continue;
-
- /* In SIGHUP cases in the postmaster, we want to report changes */
- if (context == PGC_SIGHUP && applySettings && !IsUnderPostmaster)
- {
- const char *preval = GetConfigOption(item->name, true, false);
-
- /* If option doesn't exist yet or is NULL, treat as empty string */
- if (!preval)
- preval = "";
- /* must dup, else might have dangling pointer below */
- pre_value = pstrdup(preval);
- }
-
- scres = set_config_option(item->name, item->value,
- context, PGC_S_FILE,
- GUC_ACTION_SET, applySettings, 0, false);
- if (scres > 0)
- {
- /* variable was updated, so log the change if appropriate */
- if (pre_value)
- {
- const char *post_value = GetConfigOption(item->name, true, false);
-
- if (!post_value)
- post_value = "";
- if (strcmp(pre_value, post_value) != 0)
- ereport(elevel,
- (errmsg("parameter \"%s\" changed to \"%s\"",
- item->name, item->value)));
- }
- item->applied = true;
- }
- else if (scres == 0)
- {
- error = true;
- item->errmsg = pstrdup("setting could not be applied");
- ConfFileWithError = item->filename;
- }
- else
- {
- /* no error, but variable's active value was not changed */
- item->applied = true;
- }
-
- /*
- * We should update source location unless there was an error, since
- * even if the active value didn't change, the reset value might have.
- * (In the postmaster, there won't be a difference, but it does matter
- * in backends.)
- */
- if (scres != 0 && applySettings)
- set_config_sourcefile(item->name, item->filename,
- item->sourceline);
-
- if (pre_value)
- pfree(pre_value);
- }
-
- /* Remember when we last successfully loaded the config file. */
- if (applySettings)
- PgReloadTime = GetCurrentTimestamp();
-
-bail_out:
- if (error && applySettings)
- {
- /* During postmaster startup, any error is fatal */
- if (context == PGC_POSTMASTER)
- ereport(ERROR,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("configuration file \"%s\" contains errors",
- ConfFileWithError)));
- else if (applying)
- ereport(elevel,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("configuration file \"%s\" contains errors; unaffected changes were applied",
- ConfFileWithError)));
- else
- ereport(elevel,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("configuration file \"%s\" contains errors; no changes were applied",
- ConfFileWithError)));
- }
-
- /* Successful or otherwise, return the collected data list */
- return head;
-}
-
/*
* Given a configuration file or directory location that may be a relative
* path, return an absolute one. We consider the location to be relative to
@@ -659,7 +304,7 @@ cleanup:
* Capture an error message in the ConfigVariable list returned by
* config file parsing.
*/
-static void
+void
record_config_file_error(const char *errmsg,
const char *config_file,
int lineno,
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 5db5df6285..e4a00f1205 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -243,10 +243,6 @@ static void assign_recovery_target_lsn(const char *newval, void *extra);
static bool check_primary_slot_name(char **newval, void **extra, GucSource source);
static bool check_default_with_oids(bool *newval, void **extra, GucSource source);
-/* Private functions in guc-file.l that need to be called from guc.c */
-static ConfigVariable *ProcessConfigFileInternal(GucContext context,
- bool applySettings, int elevel);
-
/*
* Track whether there were any deferred checks for custom resource managers
* specified in wal_consistency_checking.
@@ -5164,8 +5160,8 @@ static bool report_needed; /* true if any GUC_REPORT reports are needed */
static int GUCNestLevel = 0; /* 1 when in main transaction */
+static struct config_generic *find_option(const char *name, bool create_placeholders, bool skip_errors, int elevel);
static int guc_var_compare(const void *a, const void *b);
-static int guc_name_compare(const char *namea, const char *nameb);
static void InitializeGUCOptionsFromEnvironment(void);
static void InitializeOneGUCOption(struct config_generic *gconf);
static void push_old_value(struct config_generic *gconf, GucAction action);
@@ -5184,7 +5180,359 @@ static bool validate_option_array_item(const char *name, const char *value,
static void write_auto_conf_file(int fd, const char *filename, ConfigVariable *head_p);
static void replace_auto_config_value(ConfigVariable **head_p, ConfigVariable **tail_p,
const char *name, const char *value);
+static bool valid_custom_variable_name(const char *name);
+
+/*
+ * This function handles both actual config file (re)loads and execution of
+ * show_all_file_settings() (i.e., the pg_file_settings view). In the latter
+ * case we don't apply any of the settings, but we make all the usual validity
+ * checks, and we return the ConfigVariable list so that it can be printed out
+ * by show_all_file_settings().
+ */
+ConfigVariable *
+ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
+{
+ bool error = false;
+ bool applying = false;
+ const char *ConfFileWithError;
+ ConfigVariable *item,
+ *head,
+ *tail;
+ int i;
+
+ /* Parse the main config file into a list of option names and values */
+ ConfFileWithError = ConfigFileName;
+ head = tail = NULL;
+
+ if (!ParseConfigFile(ConfigFileName, true,
+ NULL, 0, 0, elevel,
+ &head, &tail))
+ {
+ /* Syntax error(s) detected in the file, so bail out */
+ error = true;
+ goto bail_out;
+ }
+
+ /*
+ * Parse the PG_AUTOCONF_FILENAME file, if present, after the main file to
+ * replace any parameters set by ALTER SYSTEM command. Because this file
+ * is in the data directory, we can't read it until the DataDir has been
+ * set.
+ */
+ if (DataDir)
+ {
+ if (!ParseConfigFile(PG_AUTOCONF_FILENAME, false,
+ NULL, 0, 0, elevel,
+ &head, &tail))
+ {
+ /* Syntax error(s) detected in the file, so bail out */
+ error = true;
+ ConfFileWithError = PG_AUTOCONF_FILENAME;
+ goto bail_out;
+ }
+ }
+ else
+ {
+ /*
+ * If DataDir is not set, the PG_AUTOCONF_FILENAME file cannot be
+ * read. In this case, we don't want to accept any settings but
+ * data_directory from postgresql.conf, because they might be
+ * overwritten with settings in the PG_AUTOCONF_FILENAME file which
+ * will be read later. OTOH, since data_directory isn't allowed in the
+ * PG_AUTOCONF_FILENAME file, it will never be overwritten later.
+ */
+ ConfigVariable *newlist = NULL;
+
+ /*
+ * Prune all items except the last "data_directory" from the list.
+ */
+ for (item = head; item; item = item->next)
+ {
+ if (!item->ignore &&
+ strcmp(item->name, "data_directory") == 0)
+ newlist = item;
+ }
+
+ if (newlist)
+ newlist->next = NULL;
+ head = tail = newlist;
+
+ /*
+ * Quick exit if data_directory is not present in file.
+ *
+ * We need not do any further processing, in particular we don't set
+ * PgReloadTime; that will be set soon by subsequent full loading of
+ * the config file.
+ */
+ if (head == NULL)
+ goto bail_out;
+ }
+
+ /*
+ * Mark all extant GUC variables as not present in the config file. We
+ * need this so that we can tell below which ones have been removed from
+ * the file since we last processed it.
+ */
+ for (i = 0; i < num_guc_variables; i++)
+ {
+ struct config_generic *gconf = guc_variables[i];
+
+ gconf->status &= ~GUC_IS_IN_FILE;
+ }
+
+ /*
+ * Check if all the supplied option names are valid, as an additional
+ * quasi-syntactic check on the validity of the config file. It is
+ * important that the postmaster and all backends agree on the results of
+ * this phase, else we will have strange inconsistencies about which
+ * processes accept a config file update and which don't. Hence, unknown
+ * custom variable names have to be accepted without complaint. For the
+ * same reason, we don't attempt to validate the options' values here.
+ *
+ * In addition, the GUC_IS_IN_FILE flag is set on each existing GUC
+ * variable mentioned in the file; and we detect duplicate entries in the
+ * file and mark the earlier occurrences as ignorable.
+ */
+ for (item = head; item; item = item->next)
+ {
+ struct config_generic *record;
+
+ /* Ignore anything already marked as ignorable */
+ if (item->ignore)
+ continue;
+
+ /*
+ * Try to find the variable; but do not create a custom placeholder if
+ * it's not there already.
+ */
+ record = find_option(item->name, false, true, elevel);
+
+ if (record)
+ {
+ /* If it's already marked, then this is a duplicate entry */
+ if (record->status & GUC_IS_IN_FILE)
+ {
+ /*
+ * Mark the earlier occurrence(s) as dead/ignorable. We could
+ * avoid the O(N^2) behavior here with some additional state,
+ * but it seems unlikely to be worth the trouble.
+ */
+ ConfigVariable *pitem;
+
+ for (pitem = head; pitem != item; pitem = pitem->next)
+ {
+ if (!pitem->ignore &&
+ strcmp(pitem->name, item->name) == 0)
+ pitem->ignore = true;
+ }
+ }
+ /* Now mark it as present in file */
+ record->status |= GUC_IS_IN_FILE;
+ }
+ else if (!valid_custom_variable_name(item->name))
+ {
+ /* Invalid non-custom variable, so complain */
+ ereport(elevel,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("unrecognized configuration parameter \"%s\" in file \"%s\" line %d",
+ item->name,
+ item->filename, item->sourceline)));
+ item->errmsg = pstrdup("unrecognized configuration parameter");
+ error = true;
+ ConfFileWithError = item->filename;
+ }
+ }
+
+ /*
+ * If we've detected any errors so far, we don't want to risk applying any
+ * changes.
+ */
+ if (error)
+ goto bail_out;
+
+ /* Otherwise, set flag that we're beginning to apply changes */
+ applying = true;
+
+ /*
+ * Check for variables having been removed from the config file, and
+ * revert their reset values (and perhaps also effective values) to the
+ * boot-time defaults. If such a variable can't be changed after startup,
+ * report that and continue.
+ */
+ for (i = 0; i < num_guc_variables; i++)
+ {
+ struct config_generic *gconf = guc_variables[i];
+ GucStack *stack;
+
+ if (gconf->reset_source != PGC_S_FILE ||
+ (gconf->status & GUC_IS_IN_FILE))
+ continue;
+ if (gconf->context < PGC_SIGHUP)
+ {
+ /* The removal can't be effective without a restart */
+ gconf->status |= GUC_PENDING_RESTART;
+ ereport(elevel,
+ (errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
+ errmsg("parameter \"%s\" cannot be changed without restarting the server",
+ gconf->name)));
+ record_config_file_error(psprintf("parameter \"%s\" cannot be changed without restarting the server",
+ gconf->name),
+ NULL, 0,
+ &head, &tail);
+ error = true;
+ continue;
+ }
+
+ /* No more to do if we're just doing show_all_file_settings() */
+ if (!applySettings)
+ continue;
+
+ /*
+ * Reset any "file" sources to "default", else set_config_option will
+ * not override those settings.
+ */
+ if (gconf->reset_source == PGC_S_FILE)
+ gconf->reset_source = PGC_S_DEFAULT;
+ if (gconf->source == PGC_S_FILE)
+ gconf->source = PGC_S_DEFAULT;
+ for (stack = gconf->stack; stack; stack = stack->prev)
+ {
+ if (stack->source == PGC_S_FILE)
+ stack->source = PGC_S_DEFAULT;
+ }
+
+ /* Now we can re-apply the wired-in default (i.e., the boot_val) */
+ if (set_config_option(gconf->name, NULL,
+ context, PGC_S_DEFAULT,
+ GUC_ACTION_SET, true, 0, false) > 0)
+ {
+ /* Log the change if appropriate */
+ if (context == PGC_SIGHUP)
+ ereport(elevel,
+ (errmsg("parameter \"%s\" removed from configuration file, reset to default",
+ gconf->name)));
+ }
+ }
+
+ /*
+ * Restore any variables determined by environment variables or
+ * dynamically-computed defaults. This is a no-op except in the case
+ * where one of these had been in the config file and is now removed.
+ *
+ * In particular, we *must not* do this during the postmaster's initial
+ * loading of the file, since the timezone functions in particular should
+ * be run only after initialization is complete.
+ *
+ * XXX this is an unmaintainable crock, because we have to know how to set
+ * (or at least what to call to set) every non-PGC_INTERNAL variable that
+ * could potentially have PGC_S_DYNAMIC_DEFAULT or PGC_S_ENV_VAR source.
+ */
+ if (context == PGC_SIGHUP && applySettings)
+ {
+ InitializeGUCOptionsFromEnvironment();
+ pg_timezone_abbrev_initialize();
+ /* this selects SQL_ASCII in processes not connected to a database */
+ SetConfigOption("client_encoding", GetDatabaseEncodingName(),
+ PGC_BACKEND, PGC_S_DYNAMIC_DEFAULT);
+ }
+ /*
+ * Now apply the values from the config file.
+ */
+ for (item = head; item; item = item->next)
+ {
+ char *pre_value = NULL;
+ int scres;
+
+ /* Ignore anything marked as ignorable */
+ if (item->ignore)
+ continue;
+
+ /* In SIGHUP cases in the postmaster, we want to report changes */
+ if (context == PGC_SIGHUP && applySettings && !IsUnderPostmaster)
+ {
+ const char *preval = GetConfigOption(item->name, true, false);
+
+ /* If option doesn't exist yet or is NULL, treat as empty string */
+ if (!preval)
+ preval = "";
+ /* must dup, else might have dangling pointer below */
+ pre_value = pstrdup(preval);
+ }
+
+ scres = set_config_option(item->name, item->value,
+ context, PGC_S_FILE,
+ GUC_ACTION_SET, applySettings, 0, false);
+ if (scres > 0)
+ {
+ /* variable was updated, so log the change if appropriate */
+ if (pre_value)
+ {
+ const char *post_value = GetConfigOption(item->name, true, false);
+
+ if (!post_value)
+ post_value = "";
+ if (strcmp(pre_value, post_value) != 0)
+ ereport(elevel,
+ (errmsg("parameter \"%s\" changed to \"%s\"",
+ item->name, item->value)));
+ }
+ item->applied = true;
+ }
+ else if (scres == 0)
+ {
+ error = true;
+ item->errmsg = pstrdup("setting could not be applied");
+ ConfFileWithError = item->filename;
+ }
+ else
+ {
+ /* no error, but variable's active value was not changed */
+ item->applied = true;
+ }
+
+ /*
+ * We should update source location unless there was an error, since
+ * even if the active value didn't change, the reset value might have.
+ * (In the postmaster, there won't be a difference, but it does matter
+ * in backends.)
+ */
+ if (scres != 0 && applySettings)
+ set_config_sourcefile(item->name, item->filename,
+ item->sourceline);
+
+ if (pre_value)
+ pfree(pre_value);
+ }
+
+ /* Remember when we last successfully loaded the config file. */
+ if (applySettings)
+ PgReloadTime = GetCurrentTimestamp();
+
+bail_out:
+ if (error && applySettings)
+ {
+ /* During postmaster startup, any error is fatal */
+ if (context == PGC_POSTMASTER)
+ ereport(ERROR,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("configuration file \"%s\" contains errors",
+ ConfFileWithError)));
+ else if (applying)
+ ereport(elevel,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("configuration file \"%s\" contains errors; unaffected changes were applied",
+ ConfFileWithError)));
+ else
+ ereport(elevel,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("configuration file \"%s\" contains errors; no changes were applied",
+ ConfFileWithError)));
+ }
+
+ /* Successful or otherwise, return the collected data list */
+ return head;
+}
/*
* Some infrastructure for checking malloc/strdup/realloc calls
@@ -5741,7 +6089,7 @@ guc_var_compare(const void *a, const void *b)
/*
* the bare comparison function for GUC names
*/
-static int
+int
guc_name_compare(const char *namea, const char *nameb)
{
/*
@@ -12988,5 +13336,3 @@ check_default_with_oids(bool *newval, void **extra, GucSource source)
return true;
}
-
-#include "guc-file.c"
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index e734493a48..aae071cd82 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -442,6 +442,15 @@ extern void GUC_check_errcode(int sqlerrcode);
pre_format_elog_string(errno, TEXTDOMAIN), \
GUC_check_errhint_string = format_elog_string
+/* functions shared between guc.c and guc-file.l */
+extern int guc_name_compare(const char *namea, const char *nameb);
+extern ConfigVariable *ProcessConfigFileInternal(GucContext context,
+ bool applySettings, int elevel);
+extern void record_config_file_error(const char *errmsg,
+ const char *config_file,
+ int lineno,
+ ConfigVariable **head_p,
+ ConfigVariable **tail_p);
/*
* The following functions are not in guc.c, but are declared here to avoid
--
2.36.1
From 4c105b7b415f0937e7fa42e8313c9452a8db1ad4 Mon Sep 17 00:00:00 2001
From: John Naylor <[email protected]>
Date: Sat, 13 Aug 2022 09:34:17 +0700
Subject: [PATCH v201 5/9] Build specscanner.c standalone
---
src/test/isolation/.gitignore | 1 +
src/test/isolation/Makefile | 15 +++++++++++----
src/test/isolation/specparse.y | 2 --
src/test/isolation/specscanner.l | 25 ++++++++++++++++---------
4 files changed, 28 insertions(+), 15 deletions(-)
diff --git a/src/test/isolation/.gitignore b/src/test/isolation/.gitignore
index 870dac4d28..2c13b4bf98 100644
--- a/src/test/isolation/.gitignore
+++ b/src/test/isolation/.gitignore
@@ -3,6 +3,7 @@
/pg_isolation_regress
# Local generated source files
+/specparse.h
/specparse.c
/specscanner.c
diff --git a/src/test/isolation/Makefile b/src/test/isolation/Makefile
index 0d452c89d4..b8738b7c1b 100644
--- a/src/test/isolation/Makefile
+++ b/src/test/isolation/Makefile
@@ -15,7 +15,8 @@ override CPPFLAGS := -I. -I$(srcdir) -I$(libpq_srcdir) \
OBJS = \
$(WIN32RES) \
isolationtester.o \
- specparse.o
+ specparse.o \
+ specscanner.o
all: isolationtester$(X) pg_isolation_regress$(X)
@@ -44,8 +45,14 @@ isolationtester$(X): $(OBJS) | submake-libpq submake-libpgport
distprep: specparse.c specscanner.c
-# specscanner is compiled as part of specparse
-specparse.o: specscanner.c
+# See notes in src/backend/parser/Makefile about the following two rules
+specparse.h: specparse.c
+ touch $@
+
+specparse.c: BISONFLAGS += -d
+
+# Force these dependencies to be known even without dependency info built:
+specparse.o specscanner.o: specparse.h
# specparse.c and specscanner.c are in the distribution tarball,
# so do not clean them here
@@ -55,7 +62,7 @@ clean distclean:
rm -rf $(pg_regress_clean_files)
maintainer-clean: distclean
- rm -f specparse.c specscanner.c
+ rm -f specparse.h specparse.c specscanner.c
installcheck: all
$(pg_isolation_regress_installcheck) --schedule=$(srcdir)/isolation_schedule
diff --git a/src/test/isolation/specparse.y b/src/test/isolation/specparse.y
index eb368184b8..657285cc23 100644
--- a/src/test/isolation/specparse.y
+++ b/src/test/isolation/specparse.y
@@ -276,5 +276,3 @@ blocker:
;
%%
-
-#include "specscanner.c"
diff --git a/src/test/isolation/specscanner.l b/src/test/isolation/specscanner.l
index aa6e89268e..2dc292c21d 100644
--- a/src/test/isolation/specscanner.l
+++ b/src/test/isolation/specscanner.l
@@ -1,4 +1,4 @@
-%{
+%top{
/*-------------------------------------------------------------------------
*
* specscanner.l
@@ -9,7 +9,14 @@
*
*-------------------------------------------------------------------------
*/
+#include "postgres_fe.h"
+
+#include "isolationtester.h"
+#include "specparse.h"
+}
+
+%{
static int yyline = 1; /* line number for error reporting */
#define LITBUF_INIT 1024 /* initial size of litbuf */
@@ -75,7 +82,7 @@ teardown { return TEARDOWN; }
/* Plain identifiers */
{identifier} {
- yylval.str = pg_strdup(yytext);
+ spec_yylval.str = pg_strdup(yytext);
return(identifier);
}
@@ -87,13 +94,13 @@ teardown { return TEARDOWN; }
<qident>\"\" { addlitchar(yytext[0]); }
<qident>\" {
litbuf[litbufpos] = '\0';
- yylval.str = pg_strdup(litbuf);
+ spec_yylval.str = pg_strdup(litbuf);
BEGIN(INITIAL);
return(identifier);
}
<qident>. { addlitchar(yytext[0]); }
-<qident>\n { yyerror("unexpected newline in quoted identifier"); }
-<qident><<EOF>> { yyerror("unterminated quoted identifier"); }
+<qident>\n { spec_yyerror("unexpected newline in quoted identifier"); }
+<qident><<EOF>> { spec_yyerror("unterminated quoted identifier"); }
/* SQL blocks: { UPDATE ... } */
/* We trim leading/trailing whitespace, otherwise they're unprocessed */
@@ -104,7 +111,7 @@ teardown { return TEARDOWN; }
}
<sql>{space}*"}" {
litbuf[litbufpos] = '\0';
- yylval.str = pg_strdup(litbuf);
+ spec_yylval.str = pg_strdup(litbuf);
BEGIN(INITIAL);
return(sqlblock);
}
@@ -116,12 +123,12 @@ teardown { return TEARDOWN; }
addlitchar(yytext[0]);
}
<sql><<EOF>> {
- yyerror("unterminated sql block");
+ spec_yyerror("unterminated sql block");
}
/* Numbers and punctuation */
{digit}+ {
- yylval.integer = atoi(yytext);
+ spec_yylval.integer = atoi(yytext);
return INTEGER;
}
@@ -150,7 +157,7 @@ addlitchar(char c)
}
void
-yyerror(const char *message)
+spec_yyerror(const char *message)
{
fprintf(stderr, "%s at line %d\n", message, yyline);
exit(1);
--
2.36.1
From 92d48ac8354bce8bb57a43e01448835e1cd75871 Mon Sep 17 00:00:00 2001
From: John Naylor <[email protected]>
Date: Fri, 12 Aug 2022 18:27:39 +0700
Subject: [PATCH v201 4/9] Build syncrep_scanner.c standalone
---
src/backend/replication/.gitignore | 1 +
src/backend/replication/Makefile | 11 +++++++++--
src/backend/replication/syncrep_gram.y | 2 --
src/backend/replication/syncrep_scanner.l | 17 +++++++++++------
4 files changed, 21 insertions(+), 10 deletions(-)
diff --git a/src/backend/replication/.gitignore b/src/backend/replication/.gitignore
index d1df6147bd..ad138c0c98 100644
--- a/src/backend/replication/.gitignore
+++ b/src/backend/replication/.gitignore
@@ -1,4 +1,5 @@
/repl_gram.c
/repl_scanner.c
+/syncrep_gram.h
/syncrep_gram.c
/syncrep_scanner.c
diff --git a/src/backend/replication/Makefile b/src/backend/replication/Makefile
index bc8170418f..23f29ba545 100644
--- a/src/backend/replication/Makefile
+++ b/src/backend/replication/Makefile
@@ -21,6 +21,7 @@ OBJS = \
slotfuncs.o \
syncrep.o \
syncrep_gram.o \
+ syncrep_scanner.o \
walreceiver.o \
walreceiverfuncs.o \
walsender.o
@@ -38,8 +39,14 @@ repl_gram.c: BISONFLAGS += -d
# Force these dependencies to be known even without dependency info built:
repl_gram.o repl_scanner.o: repl_gram.h
-# syncrep_scanner is compiled as part of syncrep_gram
-syncrep_gram.o: syncrep_scanner.c
+# See notes in src/backend/parser/Makefile about the following two rules
+syncrep_gram.h: syncrep_gram.c
+ touch $@
+
+syncrep_gram.c: BISONFLAGS += -d
+
+# Force these dependencies to be known even without dependency info built:
+syncrep_gram.o syncrep_scanner.o: syncrep_gram.h
# repl_gram.c, repl_scanner.c, syncrep_gram.c and syncrep_scanner.c
# are in the distribution tarball, so they are not cleaned here.
diff --git a/src/backend/replication/syncrep_gram.y b/src/backend/replication/syncrep_gram.y
index d932f2cda3..4fc3647da1 100644
--- a/src/backend/replication/syncrep_gram.y
+++ b/src/backend/replication/syncrep_gram.y
@@ -112,5 +112,3 @@ create_syncrep_config(const char *num_sync, List *members, uint8 syncrep_method)
return config;
}
-
-#include "syncrep_scanner.c"
diff --git a/src/backend/replication/syncrep_scanner.l b/src/backend/replication/syncrep_scanner.l
index 1952c8c6e0..8f38cb4613 100644
--- a/src/backend/replication/syncrep_scanner.l
+++ b/src/backend/replication/syncrep_scanner.l
@@ -1,4 +1,4 @@
-%{
+%top{
/*-------------------------------------------------------------------------
*
* syncrep_scanner.l
@@ -16,7 +16,12 @@
#include "postgres.h"
#include "lib/stringinfo.h"
+#include "replication/syncrep.h"
+
+#include "syncrep_gram.h"
+}
+%{
/* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
#undef fprintf
#define fprintf(file, fmt, msg) fprintf_to_ereport(fmt, msg)
@@ -82,28 +87,28 @@ xdinside [^"]+
appendStringInfoString(&xdbuf, yytext);
}
<xd>{xdstop} {
- yylval.str = xdbuf.data;
+ syncrep_yylval.str = xdbuf.data;
xdbuf.data = NULL;
BEGIN(INITIAL);
return NAME;
}
<xd><<EOF>> {
- yyerror("unterminated quoted identifier");
+ syncrep_yyerror("unterminated quoted identifier");
return JUNK;
}
{identifier} {
- yylval.str = pstrdup(yytext);
+ syncrep_yylval.str = pstrdup(yytext);
return NAME;
}
{digit}+ {
- yylval.str = pstrdup(yytext);
+ syncrep_yylval.str = pstrdup(yytext);
return NUM;
}
"*" {
- yylval.str = "*";
+ syncrep_yylval.str = "*";
return NAME;
}
--
2.36.1
From 7d4ecfcb3e91f3b45e94b9e64c7c40f1bbd22aa8 Mon Sep 17 00:00:00 2001
From: John Naylor <[email protected]>
Date: Fri, 12 Aug 2022 15:45:24 +0700
Subject: [PATCH v201 2/9] Build booscanner.c standalone
---
src/backend/bootstrap/.gitignore | 1 +
src/backend/bootstrap/Makefile | 11 +++++-
src/backend/bootstrap/bootparse.y | 2 -
src/backend/bootstrap/bootscanner.l | 57 +++++++++++++++--------------
4 files changed, 40 insertions(+), 31 deletions(-)
diff --git a/src/backend/bootstrap/.gitignore b/src/backend/bootstrap/.gitignore
index 1ffe8ca39e..6351b920fd 100644
--- a/src/backend/bootstrap/.gitignore
+++ b/src/backend/bootstrap/.gitignore
@@ -1,2 +1,3 @@
+/bootparse.h
/bootparse.c
/bootscanner.c
diff --git a/src/backend/bootstrap/Makefile b/src/backend/bootstrap/Makefile
index 6421efb227..c39eb7089c 100644
--- a/src/backend/bootstrap/Makefile
+++ b/src/backend/bootstrap/Makefile
@@ -14,12 +14,19 @@ override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
OBJS = \
bootparse.o \
+ bootscanner.o \
bootstrap.o
include $(top_srcdir)/src/backend/common.mk
-# bootscanner is compiled as part of bootparse
-bootparse.o: bootscanner.c
+# See notes in src/backend/parser/Makefile about the following two rules
+bootparse.h: bootparse.c
+ touch $@
+
+bootparse.c: BISONFLAGS += -d
+
+# Force these dependencies to be known even without dependency info built:
+bootparse.o bootscan.o: bootparse.h
# bootparse.c and bootscanner.c are in the distribution tarball, so
# they are not cleaned here.
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 7d7655d295..c45ddde67f 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -488,5 +488,3 @@ boot_ident:
| XNULL { $$ = pstrdup($1); }
;
%%
-
-#include "bootscanner.c"
diff --git a/src/backend/bootstrap/bootscanner.l b/src/backend/bootstrap/bootscanner.l
index 3094ccb93f..3f916fbb93 100644
--- a/src/backend/bootstrap/bootscanner.l
+++ b/src/backend/bootstrap/bootscanner.l
@@ -1,4 +1,4 @@
-%{
+%top{
/*-------------------------------------------------------------------------
*
* bootscanner.l
@@ -18,8 +18,11 @@
#include "bootstrap/bootstrap.h"
#include "utils/guc.h"
-/* Not needed now that this file is compiled as part of bootparse. */
-/* #include "bootparse.h" */
+/* XXX must be included after bootstrap.h */
+#include "bootparse.h"
+}
+
+%{
/* LCOV_EXCL_START */
@@ -52,7 +55,7 @@ id [-A-Za-z0-9_]+
sid \'([^']|\'\')*\'
/*
- * Keyword tokens return the keyword text (as a constant string) in yylval.kw,
+ * Keyword tokens return the keyword text (as a constant string) in boot_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.
@@ -60,23 +63,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 yylval.str.
+ * String tokens return a palloc'd string in boot_yylval.str.
*/
%%
-open { yylval.kw = "open"; return OPEN; }
+open { boot_yylval.kw = "open"; return OPEN; }
-close { yylval.kw = "close"; return XCLOSE; }
+close { boot_yylval.kw = "close"; return XCLOSE; }
-create { yylval.kw = "create"; return XCREATE; }
+create { boot_yylval.kw = "create"; return XCREATE; }
-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; }
+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; }
-insert { yylval.kw = "insert"; return INSERT_TUPLE; }
+insert { boot_yylval.kw = "insert"; return INSERT_TUPLE; }
_null_ { return NULLVAL; }
@@ -90,25 +93,25 @@ _null_ { return NULLVAL; }
^\#[^\n]* ; /* drop everything after "#" for comments */
-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; }
+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; }
{id} {
- yylval.str = pstrdup(yytext);
+ boot_yylval.str = pstrdup(yytext);
return ID;
}
{sid} {
/* strip quotes and escapes */
- yylval.str = DeescapeQuotedString(yytext);
+ boot_yylval.str = DeescapeQuotedString(yytext);
return ID;
}
@@ -121,7 +124,7 @@ NULL { yylval.kw = "NULL"; return XNULL; }
/* LCOV_EXCL_STOP */
void
-yyerror(const char *message)
+boot_yyerror(const char *message)
{
elog(ERROR, "%s at line %d", message, yyline);
}
--
2.36.1
From 75168640fbd4e2a23f2765b2918cd6a0676d0f74 Mon Sep 17 00:00:00 2001
From: John Naylor <[email protected]>
Date: Sat, 13 Aug 2022 11:18:06 +0700
Subject: [PATCH v201 6/9] Build cubescan.c standalone
---
contrib/cube/.gitignore | 1 +
contrib/cube/Makefile | 16 ++++++++++------
contrib/cube/cubedata.h | 4 ++++
contrib/cube/cubeparse.y | 9 ++-------
contrib/cube/cubescan.l | 33 +++++++++++++++++++--------------
5 files changed, 36 insertions(+), 27 deletions(-)
diff --git a/contrib/cube/.gitignore b/contrib/cube/.gitignore
index cb4c989fff..f788440c79 100644
--- a/contrib/cube/.gitignore
+++ b/contrib/cube/.gitignore
@@ -1,3 +1,4 @@
+/cubeparse.h
/cubeparse.c
/cubescan.c
# Generated subdirectories
diff --git a/contrib/cube/Makefile b/contrib/cube/Makefile
index cf195506c7..4fd19aac35 100644
--- a/contrib/cube/Makefile
+++ b/contrib/cube/Makefile
@@ -4,7 +4,8 @@ MODULE_big = cube
OBJS = \
$(WIN32RES) \
cube.o \
- cubeparse.o
+ cubeparse.o \
+ cubescan.o
EXTENSION = cube
DATA = cube--1.2.sql cube--1.2--1.3.sql cube--1.3--1.4.sql cube--1.4--1.5.sql \
@@ -15,8 +16,6 @@ HEADERS = cubedata.h
REGRESS = cube cube_sci
-EXTRA_CLEAN = y.tab.c y.tab.h
-
SHLIB_LINK += $(filter -lm, $(LIBS))
ifdef USE_PGXS
@@ -30,11 +29,16 @@ include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
+# See notes in src/backend/parser/Makefile about the following two rules
+cubeparse.h: cubeparse.c
+ touch $@
+
+cubeparse.c: BISONFLAGS += -d
-# cubescan is compiled as part of cubeparse
-cubeparse.o: cubescan.c
+# Force these dependencies to be known even without dependency info built:
+cubeparse.o cubescan.o: cubeparse.h
distprep: cubeparse.c cubescan.c
maintainer-clean:
- rm -f cubeparse.c cubescan.c
+ rm -f cubeparse.h cubeparse.c cubescan.c
diff --git a/contrib/cube/cubedata.h b/contrib/cube/cubedata.h
index dbe7d4f742..0b373048b5 100644
--- a/contrib/cube/cubedata.h
+++ b/contrib/cube/cubedata.h
@@ -67,3 +67,7 @@ extern void cube_scanner_finish(void);
/* in cubeparse.y */
extern int cube_yyparse(NDBOX **result);
+
+/* All grammar constructs return strings */
+#define YYSTYPE char *
+extern int scanbuflen;
diff --git a/contrib/cube/cubeparse.y b/contrib/cube/cubeparse.y
index 7577c4515c..d3fd1fb475 100644
--- a/contrib/cube/cubeparse.y
+++ b/contrib/cube/cubeparse.y
@@ -9,9 +9,6 @@
#include "cubedata.h"
#include "utils/float.h"
-/* All grammar constructs return strings */
-#define YYSTYPE char *
-
/*
* 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
@@ -23,8 +20,8 @@
#define YYMALLOC palloc
#define YYFREE pfree
-static char *scanbuf;
-static int scanbuflen;
+// TODO: get rid of global var
+int scanbuflen;
static int item_count(const char *s, char delim);
static NDBOX *write_box(int dim, char *str1, char *str2);
@@ -265,5 +262,3 @@ write_point_as_box(int dim, char *str)
return bp;
}
-
-#include "cubescan.c"
diff --git a/contrib/cube/cubescan.l b/contrib/cube/cubescan.l
index bd400e3684..b7d35c6f78 100644
--- a/contrib/cube/cubescan.l
+++ b/contrib/cube/cubescan.l
@@ -1,9 +1,16 @@
-%{
+%top{
/*
* A scanner for EMP-style numeric ranges
* contrib/cube/cubescan.l
*/
+#include "postgres.h"
+#include "cubedata.h"
+
+#include "cubeparse.h"
+}
+
+%{
/* LCOV_EXCL_START */
/* No reason to constrain amount of data slurped */
@@ -21,9 +28,7 @@ fprintf_to_ereport(const char *fmt, const char *msg)
/* Handles to the buffer that the lexer uses internally */
static YY_BUFFER_STATE scanbufhandle;
-/* this is now declared in cubeparse.y: */
-/* static char *scanbuf; */
-/* static int scanbuflen; */
+static char *scanbuf;
%}
%option 8bit
@@ -45,14 +50,14 @@ NaN [nN][aA][nN]
%%
-{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;
+{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;
[ \t\n\r\f]+ /* discard spaces */
. return yytext[0]; /* alert parser of the garbage */
@@ -62,7 +67,7 @@ NaN [nN][aA][nN]
/* result is not used, but Bison expects this signature */
void
-yyerror(NDBOX **result, const char *message)
+cube_yyerror(NDBOX **result, const char *message)
{
if (*yytext == YY_END_OF_BUFFER_CHAR)
{
@@ -89,7 +94,7 @@ yyerror(NDBOX **result, const char *message)
void
cube_scanner_init(const char *str)
{
- Size slen = strlen(str);
+ Size slen = strlen(str);
/*
* Might be left over after ereport()
--
2.36.1
From 5170d8fac8bbb9e101e10f18ed280e446b305e26 Mon Sep 17 00:00:00 2001
From: John Naylor <[email protected]>
Date: Sat, 13 Aug 2022 12:00:33 +0700
Subject: [PATCH v201 7/9] Build segscan.c standalone
---
contrib/seg/.gitignore | 1 +
contrib/seg/Makefile | 15 +++++++++++----
contrib/seg/segparse.y | 3 ---
contrib/seg/segscan.l | 25 +++++++++++++++----------
4 files changed, 27 insertions(+), 17 deletions(-)
diff --git a/contrib/seg/.gitignore b/contrib/seg/.gitignore
index 69e73d2096..fa247a4e67 100644
--- a/contrib/seg/.gitignore
+++ b/contrib/seg/.gitignore
@@ -1,3 +1,4 @@
+/segparse.h
/segparse.c
/segscan.c
# Generated subdirectories
diff --git a/contrib/seg/Makefile b/contrib/seg/Makefile
index bb63e83506..c6c134b8f1 100644
--- a/contrib/seg/Makefile
+++ b/contrib/seg/Makefile
@@ -4,7 +4,8 @@ MODULE_big = seg
OBJS = \
$(WIN32RES) \
seg.o \
- segparse.o
+ segparse.o \
+ segscan.o
EXTENSION = seg
DATA = seg--1.1.sql seg--1.1--1.2.sql seg--1.2--1.3.sql seg--1.3--1.4.sql \
@@ -29,10 +30,16 @@ include $(top_srcdir)/contrib/contrib-global.mk
endif
-# segscan is compiled as part of segparse
-segparse.o: segscan.c
+# See notes in src/backend/parser/Makefile about the following two rules
+segparse.h: segparse.c
+ touch $@
+
+segparse.c: BISONFLAGS += -d
+
+# Force these dependencies to be known even without dependency info built:
+segparse.o segscan.o: segparse.h
distprep: segparse.c segscan.c
maintainer-clean:
- rm -f segparse.c segscan.c
+ rm -f segparse.h segparse.c segscan.c
diff --git a/contrib/seg/segparse.y b/contrib/seg/segparse.y
index 33e3a9f35f..637eacd1a6 100644
--- a/contrib/seg/segparse.y
+++ b/contrib/seg/segparse.y
@@ -160,6 +160,3 @@ seg_atof(const char *value)
datum = DirectFunctionCall1(float4in, CStringGetDatum(value));
return DatumGetFloat4(datum);
}
-
-
-#include "segscan.c"
diff --git a/contrib/seg/segscan.l b/contrib/seg/segscan.l
index 5f6595e9eb..db0db1aa70 100644
--- a/contrib/seg/segscan.l
+++ b/contrib/seg/segscan.l
@@ -1,8 +1,15 @@
-%{
+%top{
/*
* A scanner for EMP-style numeric ranges
*/
+#include "postgres.h"
+
+#include "segdata.h"
+#include "segparse.h"
+}
+
+%{
/* LCOV_EXCL_START */
/* No reason to constrain amount of data slurped */
@@ -21,7 +28,6 @@ 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 int scanbuflen;
%}
%option 8bit
@@ -42,12 +48,12 @@ float ({integer}|{real})([eE]{integer})?
%%
-{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;
+{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;
[ \t\n\r\f]+ /* discard spaces */
. return yytext[0]; /* alert parser of the garbage */
@@ -56,7 +62,7 @@ float ({integer}|{real})([eE]{integer})?
/* LCOV_EXCL_STOP */
void
-yyerror(SEG *result, const char *message)
+seg_yyerror(SEG *result, const char *message)
{
if (*yytext == YY_END_OF_BUFFER_CHAR)
{
@@ -94,7 +100,6 @@ seg_scanner_init(const char *str)
/*
* 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;
--
2.36.1
From 4d16b395978e8bc830e91d363cf9cadda0c00365 Mon Sep 17 00:00:00 2001
From: John Naylor <[email protected]>
Date: Sat, 13 Aug 2022 12:35:55 +0700
Subject: [PATCH v201 8/9] Build jsonpath_scan.c standalone
XXX: warnings about missing jsonpath_yylex
---
src/backend/utils/adt/.gitignore | 1 +
src/backend/utils/adt/Makefile | 11 +++++++++--
src/backend/utils/adt/jsonpath_gram.y | 23 -----------------------
src/backend/utils/adt/jsonpath_scan.l | 25 +++++++++++++++----------
src/include/utils/jsonpath.h | 13 +++++++++++++
5 files changed, 38 insertions(+), 35 deletions(-)
diff --git a/src/backend/utils/adt/.gitignore b/src/backend/utils/adt/.gitignore
index 48cf941a52..7fab054407 100644
--- a/src/backend/utils/adt/.gitignore
+++ b/src/backend/utils/adt/.gitignore
@@ -1,2 +1,3 @@
+/jsonpath_gram.h
/jsonpath_gram.c
/jsonpath_scan.c
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 7c722ea2ce..d03f897478 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -57,6 +57,7 @@ OBJS = \
jsonpath.o \
jsonpath_exec.o \
jsonpath_gram.o \
+ jsonpath_scan.o \
like.o \
like_support.o \
lockfuncs.o \
@@ -119,11 +120,17 @@ OBJS = \
xid8funcs.o \
xml.o
+# See notes in src/backend/parser/Makefile about the following two rules
+jsonpath_gram.h: jsonpath_gram.c
+ touch $@
+
+jsonpath_gram.c: BISONFLAGS += -d
+
jsonpath_scan.c: FLEXFLAGS = -CF -p -p
jsonpath_scan.c: FLEX_NO_BACKUP=yes
-# jsonpath_scan is compiled as part of jsonpath_gram
-jsonpath_gram.o: jsonpath_scan.c
+# Force these dependencies to be known even without dependency info built:
+jsonpath_gram.o jsonpath_gram.o jsonpath_parser.o: jsonpath_gram.h
# jsonpath_gram.c and jsonpath_scan.c are in the distribution tarball,
# so they are not cleaned here.
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index f903dba3e3..a7557d325e 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -24,21 +24,8 @@
#include "utils/builtins.h"
#include "utils/jsonpath.h"
-/* struct JsonPathString is shared between scan and gram */
-typedef struct JsonPathString
-{
- char *val;
- int len;
- int total;
-} JsonPathString;
-
union YYSTYPE;
-/* flex 2.5.4 doesn't bother with a decl for this */
-int jsonpath_yylex(union YYSTYPE *yylval_param);
-int jsonpath_yyparse(JsonPathParseResult **result);
-void jsonpath_yyerror(JsonPathParseResult **result, const char *message);
-
static JsonPathParseItem *makeItemType(JsonPathItemType type);
static JsonPathParseItem *makeItemString(JsonPathString *s);
static JsonPathParseItem *makeItemVariable(JsonPathString *s);
@@ -593,13 +580,3 @@ jspConvertRegexFlags(uint32 xflags)
return cflags;
}
-
-/*
- * jsonpath_scan.l is compiled as part of jsonpath_gram.y. Currently, this is
- * unavoidable because jsonpath_gram does not create a .h file to export its
- * token symbols. If these files ever grow large enough to be worth compiling
- * separately, that could be fixed; but for now it seems like useless
- * complication.
- */
-
-#include "jsonpath_scan.c"
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index 4351f6ec98..edd9d1c706 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -1,4 +1,4 @@
-%{
+%top{
/*-------------------------------------------------------------------------
*
* jsonpath_scan.l
@@ -17,9 +17,14 @@
#include "postgres.h"
+#include "utils/jsonpath.h"
#include "mb/pg_wchar.h"
#include "nodes/pg_list.h"
+#include "jsonpath_gram.h"
+}
+
+%{
static JsonPathString scanstring;
/* Handles to the buffer that the lexer uses internally */
@@ -142,9 +147,9 @@ hex_fail \\x{hex_dig}{0,1}
<xnq,xq,xvq>{hex_char} { parseHexChar(yytext); }
-<xnq,xq,xvq>{unicode}*{unicodefail} { yyerror(NULL, "invalid unicode sequence"); }
+<xnq,xq,xvq>{unicode}*{unicodefail} { jsonpath_yyerror(NULL, "invalid unicode sequence"); }
-<xnq,xq,xvq>{hex_fail} { yyerror(NULL, "invalid hex character sequence"); }
+<xnq,xq,xvq>{hex_fail} { jsonpath_yyerror(NULL, "invalid hex character sequence"); }
<xnq,xq,xvq>{unicode}+\\ {
/* throw back the \\, and treat as unicode */
@@ -154,9 +159,9 @@ hex_fail \\x{hex_dig}{0,1}
<xnq,xq,xvq>\\. { addchar(false, yytext[1]); }
-<xnq,xq,xvq>\\ { yyerror(NULL, "unexpected end after backslash"); }
+<xnq,xq,xvq>\\ { jsonpath_yyerror(NULL, "unexpected end after backslash"); }
-<xq,xvq><<EOF>> { yyerror(NULL, "unexpected end of quoted string"); }
+<xq,xvq><<EOF>> { jsonpath_yyerror(NULL, "unexpected end of quoted string"); }
<xq>\" {
yylval->str = scanstring;
@@ -178,7 +183,7 @@ hex_fail \\x{hex_dig}{0,1}
<xc>\* { }
-<xc><<EOF>> { yyerror(NULL, "unexpected end of comment"); }
+<xc><<EOF>> { jsonpath_yyerror(NULL, "unexpected end of comment"); }
\&\& { return AND_P; }
@@ -244,10 +249,10 @@ hex_fail \\x{hex_dig}{0,1}
return INT_P;
}
-{realfail} { yyerror(NULL, "invalid numeric literal"); }
-{integer_junk} { yyerror(NULL, "trailing junk after numeric literal"); }
-{decimal_junk} { yyerror(NULL, "trailing junk after numeric literal"); }
-{real_junk} { yyerror(NULL, "trailing junk after numeric literal"); }
+{realfail} { jsonpath_yyerror(NULL, "invalid numeric literal"); }
+{integer_junk} { jsonpath_yyerror(NULL, "trailing junk after numeric literal"); }
+{decimal_junk} { jsonpath_yyerror(NULL, "trailing junk after numeric literal"); }
+{real_junk} { jsonpath_yyerror(NULL, "trailing junk after numeric literal"); }
\" {
addchar(true, '\0');
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index 8e79b8dc9f..b9b56209b2 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -21,6 +21,14 @@
#include "utils/jsonb.h"
#include "utils/jsonfuncs.h"
+/* struct JsonPathString is shared between scan and gram */
+typedef struct JsonPathString
+{
+ char *val;
+ int len;
+ int total;
+} JsonPathString;
+
typedef struct
{
int32 vl_len_; /* varlena header (do not touch directly!) */
@@ -250,6 +258,11 @@ typedef struct JsonPathParseResult
extern JsonPathParseResult *parsejsonpath(const char *str, int len);
+/* flex 2.5.4 doesn't bother with a decl for this */
+//int jsonpath_yylex(union YYSTYPE *yylval_param);
+//int jsonpath_yyparse(JsonPathParseResult **result);
+void jsonpath_yyerror(JsonPathParseResult **result, const char *message);
+
extern int jspConvertRegexFlags(uint32 xflags);
/*
--
2.36.1
From cae4dccc5aaee93e0ed258448f60d4178163ec1c Mon Sep 17 00:00:00 2001
From: John Naylor <[email protected]>
Date: Sat, 13 Aug 2022 13:35:14 +0700
Subject: [PATCH v201 9/9] Build exprscan.c standalone
---
src/bin/pgbench/.gitignore | 1 +
src/bin/pgbench/Makefile | 13 ++++++++++---
src/bin/pgbench/exprparse.y | 15 ---------------
src/bin/pgbench/exprscan.l | 9 ++++++++-
4 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/src/bin/pgbench/.gitignore b/src/bin/pgbench/.gitignore
index 983a3cd7a6..07492a993c 100644
--- a/src/bin/pgbench/.gitignore
+++ b/src/bin/pgbench/.gitignore
@@ -1,3 +1,4 @@
+/exprparse.h
/exprparse.c
/exprscan.c
/pgbench
diff --git a/src/bin/pgbench/Makefile b/src/bin/pgbench/Makefile
index f402fe7b91..6647c9fe97 100644
--- a/src/bin/pgbench/Makefile
+++ b/src/bin/pgbench/Makefile
@@ -10,6 +10,7 @@ include $(top_builddir)/src/Makefile.global
OBJS = \
$(WIN32RES) \
exprparse.o \
+ exprscan.o \
pgbench.o
override CPPFLAGS := -I. -I$(srcdir) -I$(libpq_srcdir) $(CPPFLAGS)
@@ -26,8 +27,14 @@ all: pgbench
pgbench: $(OBJS) | submake-libpq submake-libpgport submake-libpgfeutils
$(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X)
-# exprscan is compiled as part of exprparse
-exprparse.o: exprscan.c
+# See notes in src/backend/parser/Makefile about the following two rules
+exprparse.h: exprparse.c
+ touch $@
+
+exprparse.c: BISONFLAGS += -d
+
+# Force these dependencies to be known even without dependency info built:
+exprparse.o exprscan.o: exprparse.h
distprep: exprparse.c exprscan.c
@@ -45,7 +52,7 @@ clean distclean:
rm -rf tmp_check
maintainer-clean: distclean
- rm -f exprparse.c exprscan.c
+ rm -f exprparse.h exprparse.c exprscan.c
check:
$(prove_check)
diff --git a/src/bin/pgbench/exprparse.y b/src/bin/pgbench/exprparse.y
index b5592d4b97..ade2ecdaab 100644
--- a/src/bin/pgbench/exprparse.y
+++ b/src/bin/pgbench/exprparse.y
@@ -526,18 +526,3 @@ make_case(yyscan_t yyscanner, PgBenchExprList *when_then_list, PgBenchExpr *else
find_func(yyscanner, "!case_end"),
make_elist(else_part, when_then_list));
}
-
-/*
- * exprscan.l is compiled as part of exprparse.y. Currently, this is
- * unavoidable because exprparse does not create a .h file to export
- * its token symbols. If these files ever grow large enough to be
- * worth compiling separately, that could be fixed; but for now it
- * seems like useless complication.
- */
-
-/* First, get rid of "#define yyscan_t" from pgbench.h */
-#undef yyscan_t
-/* ... and the yylval macro, which flex will have its own definition for */
-#undef yylval
-
-#include "exprscan.c"
diff --git a/src/bin/pgbench/exprscan.l b/src/bin/pgbench/exprscan.l
index 4f63818606..6e9d949dcf 100644
--- a/src/bin/pgbench/exprscan.l
+++ b/src/bin/pgbench/exprscan.l
@@ -1,4 +1,4 @@
-%{
+%top{
/*-------------------------------------------------------------------------
*
* exprscan.l
@@ -23,8 +23,15 @@
*-------------------------------------------------------------------------
*/
+#include "postgres_fe.h"
+
#include "fe_utils/psqlscan_int.h"
+#include "pgbench.h"
+#include "exprparse.h"
+}
+
+%{
/* context information for reporting errors in expressions */
static const char *expr_source = NULL;
static int expr_lineno = 0;
--
2.36.1