On 12/30/18, Andres Freund <and...@anarazel.de> wrote: > I tried to take this for a spin, an for me the build fails because various > frontend programs don't have KeywordOffsets/Strings defined, but reference > it > through various functions exposed to the frontend (like fmtId()). That I > see > that error but you don't is probably related to me using -fuse-ld=gold in > CFLAGS. > > I can "fix" this by including kwlist_d.h in common/keywords.c > regardless of FRONTEND. That also lead me to discover that the build > dependencies somewhere aren't correctly set-up, because I need to > force a clean rebuild to trigger the problem again, just changing > keywords.c back doesn't trigger the problem.
Hmm, that was a typo, and I didn't notice even when I found I had to include kwlist_d.h in ecpg/keywords.c. :-( I've fixed both of those in the attached v6. As far as dependencies, I'm far from sure I have it up to par. That piece could use some discussion. On 1/4/19, Tom Lane <t...@sss.pgh.pa.us> wrote: > Aside from the possible linkage problem, this will need a minor rebase > over 4879a5172, which rearranged some of plpgsql's calls of > ScanKeywordLookup. > > While I don't think it's going to be hard to resolve these issues, > I'm wondering where we want to go with this. Is anyone excited > about pursuing the perfect-hash-function idea? (Joerg's example > function looked pretty neat to me.) If we are going to do that, > does it make sense to push this version beforehand? If it does, for v6 I've also done the rebase, updated the copyright year, and fixed an error in MSVC. -John Naylor
From fb846eeabd450b6932e57f7e8d4f0aebc125d178 Mon Sep 17 00:00:00 2001 From: John Naylor <jcnay...@gmail.com> Date: Fri, 4 Jan 2019 14:35:40 -0500 Subject: [PATCH v6] Use offset-based keyword lookup. Replace binary search over an array of ScanKeyword structs with a binary search over an array of offsets into a keyword string. Access auxillary data only after a keyword hit. This has better locality of reference and a smaller memory footprint. --- .../pg_stat_statements/pg_stat_statements.c | 2 + src/backend/parser/parser.c | 4 +- src/backend/parser/scan.l | 38 ++-- src/backend/utils/adt/misc.c | 2 +- src/backend/utils/adt/ruleutils.c | 9 +- src/common/.gitignore | 1 + src/common/Makefile | 25 ++- src/common/keywords.c | 42 +++-- src/fe_utils/string_utils.c | 9 +- src/include/common/keywords.h | 19 +- src/include/parser/kwlist.h | 2 +- src/include/parser/scanner.h | 8 +- src/interfaces/ecpg/preproc/.gitignore | 2 + src/interfaces/ecpg/preproc/Makefile | 20 ++- src/interfaces/ecpg/preproc/c_keywords.c | 65 ++----- src/interfaces/ecpg/preproc/c_kwlist.h | 53 ++++++ src/interfaces/ecpg/preproc/ecpg_keywords.c | 81 ++------- src/interfaces/ecpg/preproc/ecpg_kwlist.h | 68 +++++++ src/interfaces/ecpg/preproc/keywords.c | 6 +- src/interfaces/ecpg/preproc/pgc.l | 22 +-- src/interfaces/ecpg/preproc/preproc_extern.h | 8 +- src/pl/plpgsql/src/.gitignore | 2 + src/pl/plpgsql/src/Makefile | 16 +- src/pl/plpgsql/src/pl_reserved_kwlist.h | 55 ++++++ src/pl/plpgsql/src/pl_scanner.c | 167 ++++-------------- src/pl/plpgsql/src/pl_unreserved_kwlist.h | 111 ++++++++++++ src/tools/gen_keywords.pl | 136 ++++++++++++++ src/tools/msvc/Solution.pm | 44 +++++ src/tools/msvc/clean.bat | 6 + 29 files changed, 696 insertions(+), 327 deletions(-) create mode 100644 src/common/.gitignore create mode 100644 src/interfaces/ecpg/preproc/c_kwlist.h create mode 100644 src/interfaces/ecpg/preproc/ecpg_kwlist.h create mode 100644 src/pl/plpgsql/src/pl_reserved_kwlist.h create mode 100644 src/pl/plpgsql/src/pl_unreserved_kwlist.h create mode 100644 src/tools/gen_keywords.pl diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c index e8ef966bb5..4776de7595 100644 --- a/contrib/pg_stat_statements/pg_stat_statements.c +++ b/contrib/pg_stat_statements/pg_stat_statements.c @@ -3076,6 +3076,8 @@ fill_in_constant_lengths(pgssJumbleState *jstate, const char *query, yyscanner = scanner_init(query, &yyextra, ScanKeywords, + KeywordString, + KeywordOffsets, NumScanKeywords); /* we don't want to re-emit any escape string warnings */ diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c index 7e9b1222fd..19fa7bb8ae 100644 --- a/src/backend/parser/parser.c +++ b/src/backend/parser/parser.c @@ -40,8 +40,8 @@ raw_parser(const char *str) int yyresult; /* initialize the flex scanner */ - yyscanner = scanner_init(str, &yyextra.core_yy_extra, - ScanKeywords, NumScanKeywords); + yyscanner = scanner_init(str, &yyextra.core_yy_extra, ScanKeywords, + KeywordString, KeywordOffsets, NumScanKeywords); /* base_yylex() only needs this much initialization */ yyextra.have_lookahead = false; diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l index fbeb86f890..fdc8ef3e9a 100644 --- a/src/backend/parser/scan.l +++ b/src/backend/parser/scan.l @@ -504,18 +504,20 @@ other . * We will pass this along as a normal character string, * but preceded with an internally-generated "NCHAR". */ - const ScanKeyword *keyword; + int kwnum; SET_YYLLOC(); yyless(1); /* eat only 'n' this time */ - keyword = ScanKeywordLookup("nchar", - yyextra->keywords, - yyextra->num_keywords); - if (keyword != NULL) + kwnum = ScanKeywordLookup("nchar", + yyextra->kw_string, + yyextra->kw_offsets, + yyextra->num_keywords); + if (kwnum >= 0) { - yylval->keyword = keyword->name; - return keyword->value; + yylval->keyword = yyextra->kw_string + + yyextra->kw_offsets[kwnum]; + return yyextra->keywords[kwnum].value; } else { @@ -1021,19 +1023,21 @@ other . {identifier} { - const ScanKeyword *keyword; + int kwnum; char *ident; SET_YYLLOC(); /* Is it a keyword? */ - keyword = ScanKeywordLookup(yytext, - yyextra->keywords, - yyextra->num_keywords); - if (keyword != NULL) + kwnum = ScanKeywordLookup(yytext, + yyextra->kw_string, + yyextra->kw_offsets, + yyextra->num_keywords); + if (kwnum >= 0) { - yylval->keyword = keyword->name; - return keyword->value; + yylval->keyword = yyextra->kw_string + + yyextra->kw_offsets[kwnum]; + return yyextra->keywords[kwnum].value; } /* @@ -1142,7 +1146,9 @@ scanner_yyerror(const char *message, core_yyscan_t yyscanner) core_yyscan_t scanner_init(const char *str, core_yy_extra_type *yyext, - const ScanKeyword *keywords, + const ScanKeywordAux *keywords, + const char *kw_string, + const uint16 *kw_offsets, int num_keywords) { Size slen = strlen(str); @@ -1154,6 +1160,8 @@ scanner_init(const char *str, core_yyset_extra(yyext, scanner); yyext->keywords = keywords; + yyext->kw_string = kw_string; + yyext->kw_offsets = kw_offsets; yyext->num_keywords = num_keywords; yyext->backslash_quote = backslash_quote; diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c index 7b69b824e1..773dce234e 100644 --- a/src/backend/utils/adt/misc.c +++ b/src/backend/utils/adt/misc.c @@ -423,7 +423,7 @@ pg_get_keywords(PG_FUNCTION_ARGS) HeapTuple tuple; /* cast-away-const is ugly but alternatives aren't much better */ - values[0] = unconstify(char *, ScanKeywords[funcctx->call_cntr].name); + values[0] = unconstify(char *, KeywordString + KeywordOffsets[funcctx->call_cntr]); switch (ScanKeywords[funcctx->call_cntr].category) { diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 368eacf68e..e59ac4757a 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -10601,11 +10601,12 @@ quote_identifier(const char *ident) * Note: ScanKeywordLookup() does case-insensitive comparison, but * that's fine, since we already know we have all-lower-case. */ - const ScanKeyword *keyword = ScanKeywordLookup(ident, - ScanKeywords, - NumScanKeywords); + int kwnum = ScanKeywordLookup(ident, + KeywordString, + KeywordOffsets, + NumScanKeywords); - if (keyword != NULL && keyword->category != UNRESERVED_KEYWORD) + if (kwnum >= 0 && ScanKeywords[kwnum].category != UNRESERVED_KEYWORD) safe = false; } diff --git a/src/common/.gitignore b/src/common/.gitignore new file mode 100644 index 0000000000..ffa3284fbf --- /dev/null +++ b/src/common/.gitignore @@ -0,0 +1 @@ +/kwlist_d.h diff --git a/src/common/Makefile b/src/common/Makefile index ec8139f014..fbd4933554 100644 --- a/src/common/Makefile +++ b/src/common/Makefile @@ -63,7 +63,18 @@ OBJS_FRONTEND = $(OBJS_COMMON) fe_memutils.o file_utils.o restricted_token.o OBJS_SHLIB = $(OBJS_FRONTEND:%.o=%_shlib.o) OBJS_SRV = $(OBJS_COMMON:%.o=%_srv.o) -all: libpgcommon.a libpgcommon_shlib.a libpgcommon_srv.a +all: libpgcommon.a libpgcommon_shlib.a libpgcommon_srv.a kwlist_d.h + +distprep: kwlist_d.h + +# generate keyword header for the core and frontend scanners +kwlist_d.h: $(top_srcdir)/src/include/parser/kwlist.h $(top_srcdir)/src/tools/gen_keywords.pl + $(PERL) $(top_srcdir)/src/tools/gen_keywords.pl --extern $< + +$(top_builddir)/src/include/common/kwlist_d.h: kwlist_d.h + prereqdir=`cd '$(dir $<)' >/dev/null && pwd` && \ + cd '$(dir $@)' && rm -f $(notdir $@) && \ + $(LN_S) "$$prereqdir/$(notdir $<)" . # libpgcommon is needed by some contrib install: all installdirs @@ -121,10 +132,14 @@ libpgcommon_srv.a: $(OBJS_SRV) $(top_builddir)/src/include/parser/gram.h: $(top_srcdir)/src/backend/parser/gram.y $(MAKE) -C $(top_builddir)/src/backend $(top_builddir)/src/include/parser/gram.h -keywords.o: $(top_srcdir)/src/include/parser/kwlist.h -keywords_shlib.o: $(top_srcdir)/src/include/parser/kwlist.h -keywords_srv.o: $(top_builddir)/src/include/parser/gram.h $(top_srcdir)/src/include/parser/kwlist.h +keywords.o: $(top_builddir)/src/include/common/kwlist_d.h $(top_srcdir)/src/include/parser/kwlist.h +keywords_shlib.o: $(top_builddir)/src/include/common/kwlist_d.h $(top_srcdir)/src/include/parser/kwlist.h +keywords_srv.o: $(top_builddir)/src/include/common/kwlist_d.h $(top_builddir)/src/include/parser/gram.h $(top_srcdir)/src/include/parser/kwlist.h -clean distclean maintainer-clean: +# kwlist_d.h is in the distribution tarball, so it is not cleaned here. +clean distclean: rm -f libpgcommon.a libpgcommon_shlib.a libpgcommon_srv.a rm -f $(OBJS_FRONTEND) $(OBJS_SHLIB) $(OBJS_SRV) + +maintainer-clean: distclean + rm -f kwlist_d.h diff --git a/src/common/keywords.c b/src/common/keywords.c index 6f99090a29..76838be4c8 100644 --- a/src/common/keywords.c +++ b/src/common/keywords.c @@ -19,11 +19,14 @@ #include "postgres_fe.h" #endif +/* String lookup table for keywords */ +#include "common/kwlist_d.h" + #ifndef FRONTEND #include "parser/gramparse.h" -#define PG_KEYWORD(a,b,c) {a,b,c}, +#define PG_KEYWORD(kwname, value, category) {value, category}, #else @@ -33,12 +36,11 @@ * We don't need the token number for frontend uses, so leave it out to avoid * requiring backend headers that won't compile cleanly here. */ -#define PG_KEYWORD(a,b,c) {a,0,c}, +#define PG_KEYWORD(kwname, value, category) {0, category}, #endif /* FRONTEND */ - -const ScanKeyword ScanKeywords[] = { +const ScanKeywordAux ScanKeywords[] = { #include "parser/kwlist.h" }; @@ -48,10 +50,13 @@ const int NumScanKeywords = lengthof(ScanKeywords); /* * ScanKeywordLookup - see if a given word is a keyword * - * The table to be searched is passed explicitly, so that this can be used - * to search keyword lists other than the standard list appearing above. + * The keyword string along with an array of offsets into it are passed + * explicitly, so that callers can use different keyword lists. These are + * generated from the appropriate "kwlist" header at compile time. * - * Returns a pointer to the ScanKeyword table entry, or NULL if no match. + * Returns an array index, or -1 if no match. The caller can use this array + * index to retrieve the keyword string as well as any needed auxiliary data + * such as token value or category. * * The match is done case-insensitively. Note that we deliberately use a * dumbed-down case conversion that will only translate 'A'-'Z' into 'a'-'z', @@ -60,21 +65,22 @@ const int NumScanKeywords = lengthof(ScanKeywords); * keywords are to be matched in this way even though non-keyword identifiers * receive a different case-normalization mapping. */ -const ScanKeyword * +int ScanKeywordLookup(const char *text, - const ScanKeyword *keywords, + const char *kw_string, + const uint16 *kw_offsets, int num_keywords) { int len, i; char word[NAMEDATALEN]; - const ScanKeyword *low; - const ScanKeyword *high; + const uint16 *low; + const uint16 *high; len = strlen(text); /* We assume all keywords are shorter than NAMEDATALEN. */ if (len >= NAMEDATALEN) - return NULL; + return -1; /* * Apply an ASCII-only downcasing. We must not use tolower() since it may @@ -93,22 +99,22 @@ ScanKeywordLookup(const char *text, /* * Now do a binary search using plain strcmp() comparison. */ - low = keywords; - high = keywords + (num_keywords - 1); + low = kw_offsets; + high = kw_offsets + (num_keywords - 1); while (low <= high) { - const ScanKeyword *middle; + const uint16 *middle; int difference; middle = low + (high - low) / 2; - difference = strcmp(middle->name, word); + difference = strcmp(kw_string + *middle, word); if (difference == 0) - return middle; + return middle - kw_offsets; else if (difference < 0) low = middle + 1; else high = middle - 1; } - return NULL; + return -1; } diff --git a/src/fe_utils/string_utils.c b/src/fe_utils/string_utils.c index 9b47b62f41..41bec23a13 100644 --- a/src/fe_utils/string_utils.c +++ b/src/fe_utils/string_utils.c @@ -104,11 +104,12 @@ fmtId(const char *rawid) * Note: ScanKeywordLookup() does case-insensitive comparison, but * that's fine, since we already know we have all-lower-case. */ - const ScanKeyword *keyword = ScanKeywordLookup(rawid, - ScanKeywords, - NumScanKeywords); + int kwnum = ScanKeywordLookup(rawid, + KeywordString, + KeywordOffsets, + NumScanKeywords); - if (keyword != NULL && keyword->category != UNRESERVED_KEYWORD) + if (kwnum >= 0 && ScanKeywords[kwnum].category != UNRESERVED_KEYWORD) need_quotes = true; } diff --git a/src/include/common/keywords.h b/src/include/common/keywords.h index 8f22f32548..309830664b 100644 --- a/src/include/common/keywords.h +++ b/src/include/common/keywords.h @@ -21,24 +21,29 @@ #define RESERVED_KEYWORD 3 -typedef struct ScanKeyword +/* Auxiliary data for keywords */ +typedef struct ScanKeywordAux { - const char *name; /* in lower case */ int16 value; /* grammar's token code */ int16 category; /* see codes above */ -} ScanKeyword; +} ScanKeywordAux; #ifndef FRONTEND -extern PGDLLIMPORT const ScanKeyword ScanKeywords[]; +extern PGDLLIMPORT const ScanKeywordAux ScanKeywords[]; +extern PGDLLIMPORT const char *KeywordString; +extern PGDLLIMPORT const uint16 KeywordOffsets[]; extern PGDLLIMPORT const int NumScanKeywords; #else -extern const ScanKeyword ScanKeywords[]; +extern const ScanKeywordAux ScanKeywords[]; +extern const char *KeywordString; +extern const uint16 KeywordOffsets[]; extern const int NumScanKeywords; #endif -extern const ScanKeyword *ScanKeywordLookup(const char *text, - const ScanKeyword *keywords, +extern int ScanKeywordLookup(const char *text, + const char *kw_string, + const uint16 *kw_offsets, int num_keywords); #endif /* KEYWORDS_H */ diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index 0256d53998..b8902d3403 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -2,7 +2,7 @@ * * kwlist.h * - * The keyword list is kept in its own source file for possible use by + * The keyword lists are kept in their own source files for use by * automatic tools. The exact representation of a keyword is determined * by the PG_KEYWORD macro, which is not defined in this file; it can * be defined by the caller for special purposes. diff --git a/src/include/parser/scanner.h b/src/include/parser/scanner.h index 009550f424..0678c9a697 100644 --- a/src/include/parser/scanner.h +++ b/src/include/parser/scanner.h @@ -75,7 +75,9 @@ typedef struct core_yy_extra_type /* * The keyword list to use. */ - const ScanKeyword *keywords; + const ScanKeywordAux *keywords; + const char *kw_string; + const uint16 *kw_offsets; int num_keywords; /* @@ -119,7 +121,9 @@ typedef void *core_yyscan_t; /* Entry points in parser/scan.l */ extern core_yyscan_t scanner_init(const char *str, core_yy_extra_type *yyext, - const ScanKeyword *keywords, + const ScanKeywordAux *keywords, + const char *kw_string, + const uint16 *kw_offsets, int num_keywords); extern void scanner_finish(core_yyscan_t yyscanner); extern int core_yylex(core_YYSTYPE *lvalp, YYLTYPE *llocp, diff --git a/src/interfaces/ecpg/preproc/.gitignore b/src/interfaces/ecpg/preproc/.gitignore index 38ae2fe4d9..685be1c4c7 100644 --- a/src/interfaces/ecpg/preproc/.gitignore +++ b/src/interfaces/ecpg/preproc/.gitignore @@ -1,3 +1,5 @@ +/c_kwlist_d.h +/ecpg_kwlist_d.h /preproc.y /preproc.c /preproc.h diff --git a/src/interfaces/ecpg/preproc/Makefile b/src/interfaces/ecpg/preproc/Makefile index 69ddd8e9f7..07a6783646 100644 --- a/src/interfaces/ecpg/preproc/Makefile +++ b/src/interfaces/ecpg/preproc/Makefile @@ -53,9 +53,20 @@ preproc.y: ../../../backend/parser/gram.y parse.pl ecpg.addons ecpg.header ecpg. $(PERL) $(srcdir)/parse.pl $(srcdir) < $< > $@ $(PERL) $(srcdir)/check_rules.pl $(srcdir) $< +# generate keyword headers +c_kwlist_d.h: c_kwlist.h $(top_srcdir)/src/tools/gen_keywords.pl + $(PERL) $(top_srcdir)/src/tools/gen_keywords.pl --prefix c_ $< + +ecpg_kwlist_d.h: ecpg_kwlist.h $(top_srcdir)/src/tools/gen_keywords.pl + $(PERL) $(top_srcdir)/src/tools/gen_keywords.pl --prefix ecpg_ $< + +# Force these dependencies to be known even without dependency info built: ecpg_keywords.o c_keywords.o keywords.o preproc.o pgc.o parser.o: preproc.h +ecpg_keywords.o: ecpg_kwlist_d.h +c_keywords.o: c_kwlist_d.h +keywords.o: $(top_builddir)/src/include/common/kwlist_d.h -distprep: preproc.y preproc.c preproc.h pgc.c +distprep: preproc.y preproc.c preproc.h pgc.c c_kwlist_d.h ecpg_kwlist_d.h install: all installdirs $(INSTALL_PROGRAM) ecpg$(X) '$(DESTDIR)$(bindir)' @@ -66,12 +77,11 @@ installdirs: uninstall: rm -f '$(DESTDIR)$(bindir)/ecpg$(X)' +# preproc.y, preproc.c, preproc.h, pgc.c, c_kwlist_d.h, and ecpg_kwlist_d.h +# are in the distribution tarball, so they are not cleaned here. clean distclean: rm -f *.o ecpg$(X) rm -f typename.c -# `make distclean' must not remove preproc.y, preproc.c, preproc.h, or pgc.c -# since we want to ship those files in the distribution for people with -# inadequate tools. Instead, `make maintainer-clean' will remove them. maintainer-clean: distclean - rm -f preproc.y preproc.c preproc.h pgc.c + rm -f preproc.y preproc.c preproc.h pgc.c c_kwlist_d.h ecpg_kwlist_d.h diff --git a/src/interfaces/ecpg/preproc/c_keywords.c b/src/interfaces/ecpg/preproc/c_keywords.c index c367dbfc20..989db3a5a5 100644 --- a/src/interfaces/ecpg/preproc/c_keywords.c +++ b/src/interfaces/ecpg/preproc/c_keywords.c @@ -14,72 +14,45 @@ #include "preproc_extern.h" #include "preproc.h" -/* - * List of (keyword-name, keyword-token-value) pairs. - * - * !!WARNING!!: This list must be sorted, because binary - * search is used to locate entries. - */ -static const ScanKeyword ScanCKeywords[] = { - /* name, value, category */ +/* String lookup table for C keywords */ +#include "c_kwlist_d.h" - /* - * category is not needed in ecpg, it is only here so we can share the - * data structure with the backend - */ - {"VARCHAR", VARCHAR, 0}, - {"auto", S_AUTO, 0}, - {"bool", SQL_BOOL, 0}, - {"char", CHAR_P, 0}, - {"const", S_CONST, 0}, - {"enum", ENUM_P, 0}, - {"extern", S_EXTERN, 0}, - {"float", FLOAT_P, 0}, - {"hour", HOUR_P, 0}, - {"int", INT_P, 0}, - {"long", SQL_LONG, 0}, - {"minute", MINUTE_P, 0}, - {"month", MONTH_P, 0}, - {"register", S_REGISTER, 0}, - {"second", SECOND_P, 0}, - {"short", SQL_SHORT, 0}, - {"signed", SQL_SIGNED, 0}, - {"static", S_STATIC, 0}, - {"struct", SQL_STRUCT, 0}, - {"to", TO, 0}, - {"typedef", S_TYPEDEF, 0}, - {"union", UNION, 0}, - {"unsigned", SQL_UNSIGNED, 0}, - {"varchar", VARCHAR, 0}, - {"volatile", S_VOLATILE, 0}, - {"year", YEAR_P, 0}, -}; +#define PG_KEYWORD(kwname, value) value, +static const int16 ScanCKeywords[] = { +#include "c_kwlist.h" +}; /* + * ScanCKeywordLookup - see if a given word is a keyword + * + * Returns the token value of the keyword or -1 if no match. + * * Do a binary search using plain strcmp() comparison. This is much like * ScanKeywordLookup(), except we want case-sensitive matching. */ -const ScanKeyword * +int16 ScanCKeywordLookup(const char *text) { - const ScanKeyword *low = &ScanCKeywords[0]; - const ScanKeyword *high = &ScanCKeywords[lengthof(ScanCKeywords) - 1]; + const uint16 *low = &c_kw_offsets[0]; + const uint16 *high = &c_kw_offsets[lengthof(c_kw_offsets) - 1]; while (low <= high) { - const ScanKeyword *middle; + const uint16 *middle; int difference; middle = low + (high - low) / 2; - difference = strcmp(middle->name, text); + difference = strcmp(c_kw_string + *middle, text); if (difference == 0) - return middle; + { + return ScanCKeywords[middle - c_kw_offsets]; + } else if (difference < 0) low = middle + 1; else high = middle - 1; } - return NULL; + return -1; } diff --git a/src/interfaces/ecpg/preproc/c_kwlist.h b/src/interfaces/ecpg/preproc/c_kwlist.h new file mode 100644 index 0000000000..05834328d8 --- /dev/null +++ b/src/interfaces/ecpg/preproc/c_kwlist.h @@ -0,0 +1,53 @@ +/*------------------------------------------------------------------------- + * + * c_kwlist.h + * + * The keyword lists are kept in their own source files for use by + * automatic tools. The exact representation of a keyword is determined + * by the PG_KEYWORD macro, which is not defined in this file; it can + * be defined by the caller for special purposes. + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/ecpg/preproc/c_kwlist.h + * + *------------------------------------------------------------------------- + */ + +/* There is deliberately not an #ifndef C_KWLIST_H here. */ + +/* + * List of (keyword-name, keyword-token-value) pairs. + * + * !!WARNING!!: This list must be sorted, because binary + * search is used to locate entries. + */ + +/* name, value */ +PG_KEYWORD("VARCHAR", VARCHAR) +PG_KEYWORD("auto", S_AUTO) +PG_KEYWORD("bool", SQL_BOOL) +PG_KEYWORD("char", CHAR_P) +PG_KEYWORD("const", S_CONST) +PG_KEYWORD("enum", ENUM_P) +PG_KEYWORD("extern", S_EXTERN) +PG_KEYWORD("float", FLOAT_P) +PG_KEYWORD("hour", HOUR_P) +PG_KEYWORD("int", INT_P) +PG_KEYWORD("long", SQL_LONG) +PG_KEYWORD("minute", MINUTE_P) +PG_KEYWORD("month", MONTH_P) +PG_KEYWORD("register", S_REGISTER) +PG_KEYWORD("second", SECOND_P) +PG_KEYWORD("short", SQL_SHORT) +PG_KEYWORD("signed", SQL_SIGNED) +PG_KEYWORD("static", S_STATIC) +PG_KEYWORD("struct", SQL_STRUCT) +PG_KEYWORD("to", TO) +PG_KEYWORD("typedef", S_TYPEDEF) +PG_KEYWORD("union", UNION) +PG_KEYWORD("unsigned", SQL_UNSIGNED) +PG_KEYWORD("varchar", VARCHAR) +PG_KEYWORD("volatile", S_VOLATILE) +PG_KEYWORD("year", YEAR_P) diff --git a/src/interfaces/ecpg/preproc/ecpg_keywords.c b/src/interfaces/ecpg/preproc/ecpg_keywords.c index 37c97e162d..3255060976 100644 --- a/src/interfaces/ecpg/preproc/ecpg_keywords.c +++ b/src/interfaces/ecpg/preproc/ecpg_keywords.c @@ -16,82 +16,37 @@ #include "preproc_extern.h" #include "preproc.h" -/* - * List of (keyword-name, keyword-token-value) pairs. - * - * !!WARNING!!: This list must be sorted, because binary - * search is used to locate entries. - */ -static const ScanKeyword ECPGScanKeywords[] = { - /* name, value, category */ +/* String lookup table for ECPG keywords */ +#include "ecpg_kwlist_d.h" + +#define PG_KEYWORD(kwname, value) value, - /* - * category is not needed in ecpg, it is only here so we can share the - * data structure with the backend - */ - {"allocate", SQL_ALLOCATE, 0}, - {"autocommit", SQL_AUTOCOMMIT, 0}, - {"bool", SQL_BOOL, 0}, - {"break", SQL_BREAK, 0}, - {"cardinality", SQL_CARDINALITY, 0}, - {"connect", SQL_CONNECT, 0}, - {"count", SQL_COUNT, 0}, - {"datetime_interval_code", SQL_DATETIME_INTERVAL_CODE, 0}, - {"datetime_interval_precision", SQL_DATETIME_INTERVAL_PRECISION, 0}, - {"describe", SQL_DESCRIBE, 0}, - {"descriptor", SQL_DESCRIPTOR, 0}, - {"disconnect", SQL_DISCONNECT, 0}, - {"found", SQL_FOUND, 0}, - {"free", SQL_FREE, 0}, - {"get", SQL_GET, 0}, - {"go", SQL_GO, 0}, - {"goto", SQL_GOTO, 0}, - {"identified", SQL_IDENTIFIED, 0}, - {"indicator", SQL_INDICATOR, 0}, - {"key_member", SQL_KEY_MEMBER, 0}, - {"length", SQL_LENGTH, 0}, - {"long", SQL_LONG, 0}, - {"nullable", SQL_NULLABLE, 0}, - {"octet_length", SQL_OCTET_LENGTH, 0}, - {"open", SQL_OPEN, 0}, - {"output", SQL_OUTPUT, 0}, - {"reference", SQL_REFERENCE, 0}, - {"returned_length", SQL_RETURNED_LENGTH, 0}, - {"returned_octet_length", SQL_RETURNED_OCTET_LENGTH, 0}, - {"scale", SQL_SCALE, 0}, - {"section", SQL_SECTION, 0}, - {"short", SQL_SHORT, 0}, - {"signed", SQL_SIGNED, 0}, - {"sqlerror", SQL_SQLERROR, 0}, - {"sqlprint", SQL_SQLPRINT, 0}, - {"sqlwarning", SQL_SQLWARNING, 0}, - {"stop", SQL_STOP, 0}, - {"struct", SQL_STRUCT, 0}, - {"unsigned", SQL_UNSIGNED, 0}, - {"var", SQL_VAR, 0}, - {"whenever", SQL_WHENEVER, 0}, +static const int16 ECPGScanKeywords[] = { +#include "ecpg_kwlist.h" }; /* * ScanECPGKeywordLookup - see if a given word is a keyword * - * Returns a pointer to the ScanKeyword table entry, or NULL if no match. + * Returns the token value of the keyword or -1 if no match. * Keywords are matched using the same case-folding rules as in the backend. */ -const ScanKeyword * +int16 ScanECPGKeywordLookup(const char *text) { - const ScanKeyword *res; + int kwnum; /* First check SQL symbols defined by the backend. */ - res = ScanKeywordLookup(text, SQLScanKeywords, NumSQLScanKeywords); - if (res) - return res; + kwnum = ScanKeywordLookup(text, KeywordString, KeywordOffsets, + NumSQLScanKeywords); + if (kwnum >= 0) + return SQLScanKeywords[kwnum]; /* Try ECPG-specific keywords. */ - res = ScanKeywordLookup(text, ECPGScanKeywords, lengthof(ECPGScanKeywords)); - if (res) - return res; + kwnum = ScanKeywordLookup(text, ecpg_kw_string, ecpg_kw_offsets, + lengthof(ECPGScanKeywords)); + if (kwnum >= 0) + return ECPGScanKeywords[kwnum]; - return NULL; + return -1; } diff --git a/src/interfaces/ecpg/preproc/ecpg_kwlist.h b/src/interfaces/ecpg/preproc/ecpg_kwlist.h new file mode 100644 index 0000000000..5a6fbd3041 --- /dev/null +++ b/src/interfaces/ecpg/preproc/ecpg_kwlist.h @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------- + * + * ecpg_kwlist.h + * + * The keyword lists are kept in their own source files for use by + * automatic tools. The exact representation of a keyword is determined + * by the PG_KEYWORD macro, which is not defined in this file; it can + * be defined by the caller for special purposes. + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/interfaces/ecpg/preproc/ecpg_kwlist.h + * + *------------------------------------------------------------------------- + */ + +/* There is deliberately not an #ifndef ECPG_KWLIST_H here. */ + +/* + * List of (keyword-name, keyword-token-value) pairs. + * + * !!WARNING!!: This list must be sorted, because binary + * search is used to locate entries. + */ + +/* name, value */ +PG_KEYWORD("allocate", SQL_ALLOCATE) +PG_KEYWORD("autocommit", SQL_AUTOCOMMIT) +PG_KEYWORD("bool", SQL_BOOL) +PG_KEYWORD("break", SQL_BREAK) +PG_KEYWORD("cardinality", SQL_CARDINALITY) +PG_KEYWORD("connect", SQL_CONNECT) +PG_KEYWORD("count", SQL_COUNT) +PG_KEYWORD("datetime_interval_code", SQL_DATETIME_INTERVAL_CODE) +PG_KEYWORD("datetime_interval_precision", SQL_DATETIME_INTERVAL_PRECISION) +PG_KEYWORD("describe", SQL_DESCRIBE) +PG_KEYWORD("descriptor", SQL_DESCRIPTOR) +PG_KEYWORD("disconnect", SQL_DISCONNECT) +PG_KEYWORD("found", SQL_FOUND) +PG_KEYWORD("free", SQL_FREE) +PG_KEYWORD("get", SQL_GET) +PG_KEYWORD("go", SQL_GO) +PG_KEYWORD("goto", SQL_GOTO) +PG_KEYWORD("identified", SQL_IDENTIFIED) +PG_KEYWORD("indicator", SQL_INDICATOR) +PG_KEYWORD("key_member", SQL_KEY_MEMBER) +PG_KEYWORD("length", SQL_LENGTH) +PG_KEYWORD("long", SQL_LONG) +PG_KEYWORD("nullable", SQL_NULLABLE) +PG_KEYWORD("octet_length", SQL_OCTET_LENGTH) +PG_KEYWORD("open", SQL_OPEN) +PG_KEYWORD("output", SQL_OUTPUT) +PG_KEYWORD("reference", SQL_REFERENCE) +PG_KEYWORD("returned_length", SQL_RETURNED_LENGTH) +PG_KEYWORD("returned_octet_length", SQL_RETURNED_OCTET_LENGTH) +PG_KEYWORD("scale", SQL_SCALE) +PG_KEYWORD("section", SQL_SECTION) +PG_KEYWORD("short", SQL_SHORT) +PG_KEYWORD("signed", SQL_SIGNED) +PG_KEYWORD("sqlerror", SQL_SQLERROR) +PG_KEYWORD("sqlprint", SQL_SQLPRINT) +PG_KEYWORD("sqlwarning", SQL_SQLWARNING) +PG_KEYWORD("stop", SQL_STOP) +PG_KEYWORD("struct", SQL_STRUCT) +PG_KEYWORD("unsigned", SQL_UNSIGNED) +PG_KEYWORD("var", SQL_VAR) +PG_KEYWORD("whenever", SQL_WHENEVER) diff --git a/src/interfaces/ecpg/preproc/keywords.c b/src/interfaces/ecpg/preproc/keywords.c index 12409e9805..a1283793af 100644 --- a/src/interfaces/ecpg/preproc/keywords.c +++ b/src/interfaces/ecpg/preproc/keywords.c @@ -13,6 +13,7 @@ * *------------------------------------------------------------------------- */ + #include "postgres_fe.h" /* @@ -30,10 +31,9 @@ #include "preproc_extern.h" #include "preproc.h" +#define PG_KEYWORD(kwname, value, category) value, -#define PG_KEYWORD(a,b,c) {a,b,c}, - -const ScanKeyword SQLScanKeywords[] = { +const int16 SQLScanKeywords[] = { #include "parser/kwlist.h" }; diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l index a60564c690..10f504596a 100644 --- a/src/interfaces/ecpg/preproc/pgc.l +++ b/src/interfaces/ecpg/preproc/pgc.l @@ -920,19 +920,19 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+ } {identifier} { - const ScanKeyword *keyword; + int16 kwvalue; if (!isdefine()) { /* Is it an SQL/ECPG keyword? */ - keyword = ScanECPGKeywordLookup(yytext); - if (keyword != NULL) - return keyword->value; + kwvalue = ScanECPGKeywordLookup(yytext); + if (kwvalue >= 0) + return kwvalue; /* Is it a C keyword? */ - keyword = ScanCKeywordLookup(yytext); - if (keyword != NULL) - return keyword->value; + kwvalue = ScanCKeywordLookup(yytext); + if (kwvalue >= 0) + return kwvalue; /* * None of the above. Return it as an identifier. @@ -1010,7 +1010,7 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+ return CPP_LINE; } <C>{identifier} { - const ScanKeyword *keyword; + int16 kwvalue; /* * Try to detect a function name: @@ -1026,9 +1026,9 @@ cppline {space}*#([^i][A-Za-z]*|{if}|{ifdef}|{ifndef}|{import})((\/\*[^*/]*\*+ /* however, some defines have to be taken care of for compatibility */ if ((!INFORMIX_MODE || !isinformixdefine()) && !isdefine()) { - keyword = ScanCKeywordLookup(yytext); - if (keyword != NULL) - return keyword->value; + kwvalue = ScanCKeywordLookup(yytext); + if (kwvalue >= 0) + return kwvalue; else { base_yylval.str = mm_strdup(yytext); diff --git a/src/interfaces/ecpg/preproc/preproc_extern.h b/src/interfaces/ecpg/preproc/preproc_extern.h index 13eda670ff..0d7c63cc6c 100644 --- a/src/interfaces/ecpg/preproc/preproc_extern.h +++ b/src/interfaces/ecpg/preproc/preproc_extern.h @@ -59,7 +59,9 @@ extern struct when when_error, extern struct ECPGstruct_member *struct_member_list[STRUCT_DEPTH]; /* Globals from keywords.c */ -extern const ScanKeyword SQLScanKeywords[]; +extern const int16 SQLScanKeywords[]; +extern const char *KeywordString; +extern const uint16 KeywordOffsets[]; extern const int NumSQLScanKeywords; /* functions */ @@ -102,8 +104,8 @@ extern void check_indicator(struct ECPGtype *); extern void remove_typedefs(int); extern void remove_variables(int); extern struct variable *new_variable(const char *, struct ECPGtype *, int); -extern const ScanKeyword *ScanCKeywordLookup(const char *); -extern const ScanKeyword *ScanECPGKeywordLookup(const char *text); +extern int16 ScanCKeywordLookup(const char *text); +extern int16 ScanECPGKeywordLookup(const char *text); extern void parser_init(void); extern int filtered_base_yylex(void); diff --git a/src/pl/plpgsql/src/.gitignore b/src/pl/plpgsql/src/.gitignore index ff6ac965fd..3ab9a2243c 100644 --- a/src/pl/plpgsql/src/.gitignore +++ b/src/pl/plpgsql/src/.gitignore @@ -1,5 +1,7 @@ /pl_gram.c /pl_gram.h +/pl_reserved_kwlist_d.h +/pl_unreserved_kwlist_d.h /plerrcodes.h /log/ /results/ diff --git a/src/pl/plpgsql/src/Makefile b/src/pl/plpgsql/src/Makefile index 25a5a9d448..bdc28c5f01 100644 --- a/src/pl/plpgsql/src/Makefile +++ b/src/pl/plpgsql/src/Makefile @@ -61,6 +61,7 @@ uninstall-headers: # Force these dependencies to be known even without dependency info built: pl_gram.o pl_handler.o pl_comp.o pl_exec.o pl_funcs.o pl_scanner.o: plpgsql.h pl_gram.h plerrcodes.h +pl_scanner.o: pl_reserved_kwlist_d.h pl_unreserved_kwlist_d.h # See notes in src/backend/parser/Makefile about the following two rules pl_gram.h: pl_gram.c @@ -72,6 +73,12 @@ pl_gram.c: BISONFLAGS += -d plerrcodes.h: $(top_srcdir)/src/backend/utils/errcodes.txt generate-plerrcodes.pl $(PERL) $(srcdir)/generate-plerrcodes.pl $< > $@ +# generate keyword headers for the scanner +pl_reserved_kwlist_d.h: pl_reserved_kwlist.h $(top_srcdir)/src/tools/gen_keywords.pl + $(PERL) $(top_srcdir)/src/tools/gen_keywords.pl --prefix pl_reserved_ $< + +pl_unreserved_kwlist_d.h: pl_unreserved_kwlist.h $(top_srcdir)/src/tools/gen_keywords.pl + $(PERL) $(top_srcdir)/src/tools/gen_keywords.pl --prefix pl_unreserved_ $< check: submake $(pg_regress_check) $(REGRESS_OPTS) $(REGRESS) @@ -84,13 +91,14 @@ submake: $(MAKE) -C $(top_builddir)/src/test/regress pg_regress$(X) -distprep: pl_gram.h pl_gram.c plerrcodes.h +distprep: pl_gram.h pl_gram.c plerrcodes.h pl_reserved_kwlist_d.h pl_unreserved_kwlist_d.h -# pl_gram.c, pl_gram.h and plerrcodes.h are in the distribution tarball, -# so they are not cleaned here. +# pl_gram.c, pl_gram.h, plerrcodes.h, pl_reserved_kwlist_d.h, and +# pl_unreserved_kwlist_d.h are in the distribution tarball, so they +# are not cleaned here. clean distclean: clean-lib rm -f $(OBJS) rm -rf $(pg_regress_clean_files) maintainer-clean: distclean - rm -f pl_gram.c pl_gram.h plerrcodes.h + rm -f pl_gram.c pl_gram.h plerrcodes.h pl_reserved_kwlist_d.h pl_unreserved_kwlist_d.h diff --git a/src/pl/plpgsql/src/pl_reserved_kwlist.h b/src/pl/plpgsql/src/pl_reserved_kwlist.h new file mode 100644 index 0000000000..4b94b753f2 --- /dev/null +++ b/src/pl/plpgsql/src/pl_reserved_kwlist.h @@ -0,0 +1,55 @@ +/*------------------------------------------------------------------------- + * + * pl_reserved_kwlist.h + * + * The keyword lists are kept in their own source files for use by + * automatic tools. The exact representation of a keyword is determined + * by the PG_KEYWORD macro, which is not defined in this file; it can + * be defined by the caller for special purposes. + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/pl/plpgsql/src/pl_reserved_kwlist.h + * + *------------------------------------------------------------------------- + */ + +/* There is deliberately not an #ifndef PL_RESERVED_KWLIST_H here. */ + +/* + * List of (keyword-name, keyword-token-value, category) pairs. + * Category is only here to share the same auxiliary data structure + * with the backend. + * + * Be careful not to put the same word in both lists. + * + * !!WARNING!!: This list must be sorted by ASCII name, because binary + * search is used to locate entries. + */ + +/* name, value, category */ +PG_KEYWORD("all", K_ALL, RESERVED_KEYWORD) +PG_KEYWORD("begin", K_BEGIN, RESERVED_KEYWORD) +PG_KEYWORD("by", K_BY, RESERVED_KEYWORD) +PG_KEYWORD("case", K_CASE, RESERVED_KEYWORD) +PG_KEYWORD("declare", K_DECLARE, RESERVED_KEYWORD) +PG_KEYWORD("else", K_ELSE, RESERVED_KEYWORD) +PG_KEYWORD("end", K_END, RESERVED_KEYWORD) +PG_KEYWORD("execute", K_EXECUTE, RESERVED_KEYWORD) +PG_KEYWORD("for", K_FOR, RESERVED_KEYWORD) +PG_KEYWORD("foreach", K_FOREACH, RESERVED_KEYWORD) +PG_KEYWORD("from", K_FROM, RESERVED_KEYWORD) +PG_KEYWORD("if", K_IF, RESERVED_KEYWORD) +PG_KEYWORD("in", K_IN, RESERVED_KEYWORD) +PG_KEYWORD("into", K_INTO, RESERVED_KEYWORD) +PG_KEYWORD("loop", K_LOOP, RESERVED_KEYWORD) +PG_KEYWORD("not", K_NOT, RESERVED_KEYWORD) +PG_KEYWORD("null", K_NULL, RESERVED_KEYWORD) +PG_KEYWORD("or", K_OR, RESERVED_KEYWORD) +PG_KEYWORD("strict", K_STRICT, RESERVED_KEYWORD) +PG_KEYWORD("then", K_THEN, RESERVED_KEYWORD) +PG_KEYWORD("to", K_TO, RESERVED_KEYWORD) +PG_KEYWORD("using", K_USING, RESERVED_KEYWORD) +PG_KEYWORD("when", K_WHEN, RESERVED_KEYWORD) +PG_KEYWORD("while", K_WHILE, RESERVED_KEYWORD) diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c index 8340628de3..07a2a95c2d 100644 --- a/src/pl/plpgsql/src/pl_scanner.c +++ b/src/pl/plpgsql/src/pl_scanner.c @@ -21,8 +21,9 @@ #include "plpgsql.h" #include "pl_gram.h" /* must be after parser/scanner.h */ - -#define PG_KEYWORD(a,b,c) {a,b,c}, +/* String lookup tables for keywords */ +#include "pl_reserved_kwlist_d.h" +#include "pl_unreserved_kwlist_d.h" /* Klugy flag to tell scanner how to look up identifiers */ @@ -31,7 +32,9 @@ IdentifierLookup plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL; /* * A word about keywords: * - * We keep reserved and unreserved keywords in separate arrays. The + * We keep reserved and unreserved keywords in separate headers. Be careful + * not to put the same word in both headers. Also be sure that pl_gram.y's + * unreserved_keyword production agrees with the unreserved header. The * reserved keywords are passed to the core scanner, so they will be * recognized before (and instead of) any variable name. Unreserved words * are checked for separately, usually after determining that the identifier @@ -57,127 +60,19 @@ IdentifierLookup plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL; * BEGIN BY DECLARE EXECUTE FOREACH IF LOOP STRICT WHILE */ -/* - * Lists of keyword (name, token-value, category) entries. - * - * !!WARNING!!: These lists must be sorted by ASCII name, because binary - * search is used to locate entries. - * - * Be careful not to put the same word in both lists. Also be sure that - * pl_gram.y's unreserved_keyword production agrees with the second list. - */ +#define PG_KEYWORD(kwname, value, category) {value, category}, -static const ScanKeyword reserved_keywords[] = { - PG_KEYWORD("all", K_ALL, RESERVED_KEYWORD) - PG_KEYWORD("begin", K_BEGIN, RESERVED_KEYWORD) - PG_KEYWORD("by", K_BY, RESERVED_KEYWORD) - PG_KEYWORD("case", K_CASE, RESERVED_KEYWORD) - PG_KEYWORD("declare", K_DECLARE, RESERVED_KEYWORD) - PG_KEYWORD("else", K_ELSE, RESERVED_KEYWORD) - PG_KEYWORD("end", K_END, RESERVED_KEYWORD) - PG_KEYWORD("execute", K_EXECUTE, RESERVED_KEYWORD) - PG_KEYWORD("for", K_FOR, RESERVED_KEYWORD) - PG_KEYWORD("foreach", K_FOREACH, RESERVED_KEYWORD) - PG_KEYWORD("from", K_FROM, RESERVED_KEYWORD) - PG_KEYWORD("if", K_IF, RESERVED_KEYWORD) - PG_KEYWORD("in", K_IN, RESERVED_KEYWORD) - PG_KEYWORD("into", K_INTO, RESERVED_KEYWORD) - PG_KEYWORD("loop", K_LOOP, RESERVED_KEYWORD) - PG_KEYWORD("not", K_NOT, RESERVED_KEYWORD) - PG_KEYWORD("null", K_NULL, RESERVED_KEYWORD) - PG_KEYWORD("or", K_OR, RESERVED_KEYWORD) - PG_KEYWORD("strict", K_STRICT, RESERVED_KEYWORD) - PG_KEYWORD("then", K_THEN, RESERVED_KEYWORD) - PG_KEYWORD("to", K_TO, RESERVED_KEYWORD) - PG_KEYWORD("using", K_USING, RESERVED_KEYWORD) - PG_KEYWORD("when", K_WHEN, RESERVED_KEYWORD) - PG_KEYWORD("while", K_WHILE, RESERVED_KEYWORD) +static const ScanKeywordAux reserved_keywords[] = { +#include "pl_reserved_kwlist.h" }; static const int num_reserved_keywords = lengthof(reserved_keywords); -static const ScanKeyword unreserved_keywords[] = { - PG_KEYWORD("absolute", K_ABSOLUTE, UNRESERVED_KEYWORD) - PG_KEYWORD("alias", K_ALIAS, UNRESERVED_KEYWORD) - PG_KEYWORD("array", K_ARRAY, UNRESERVED_KEYWORD) - PG_KEYWORD("assert", K_ASSERT, UNRESERVED_KEYWORD) - PG_KEYWORD("backward", K_BACKWARD, UNRESERVED_KEYWORD) - PG_KEYWORD("call", K_CALL, UNRESERVED_KEYWORD) - PG_KEYWORD("close", K_CLOSE, UNRESERVED_KEYWORD) - PG_KEYWORD("collate", K_COLLATE, UNRESERVED_KEYWORD) - PG_KEYWORD("column", K_COLUMN, UNRESERVED_KEYWORD) - PG_KEYWORD("column_name", K_COLUMN_NAME, UNRESERVED_KEYWORD) - PG_KEYWORD("commit", K_COMMIT, UNRESERVED_KEYWORD) - PG_KEYWORD("constant", K_CONSTANT, UNRESERVED_KEYWORD) - PG_KEYWORD("constraint", K_CONSTRAINT, UNRESERVED_KEYWORD) - PG_KEYWORD("constraint_name", K_CONSTRAINT_NAME, UNRESERVED_KEYWORD) - PG_KEYWORD("continue", K_CONTINUE, UNRESERVED_KEYWORD) - PG_KEYWORD("current", K_CURRENT, UNRESERVED_KEYWORD) - PG_KEYWORD("cursor", K_CURSOR, UNRESERVED_KEYWORD) - PG_KEYWORD("datatype", K_DATATYPE, UNRESERVED_KEYWORD) - PG_KEYWORD("debug", K_DEBUG, UNRESERVED_KEYWORD) - PG_KEYWORD("default", K_DEFAULT, UNRESERVED_KEYWORD) - PG_KEYWORD("detail", K_DETAIL, UNRESERVED_KEYWORD) - PG_KEYWORD("diagnostics", K_DIAGNOSTICS, UNRESERVED_KEYWORD) - PG_KEYWORD("do", K_DO, UNRESERVED_KEYWORD) - PG_KEYWORD("dump", K_DUMP, UNRESERVED_KEYWORD) - PG_KEYWORD("elseif", K_ELSIF, UNRESERVED_KEYWORD) - PG_KEYWORD("elsif", K_ELSIF, UNRESERVED_KEYWORD) - PG_KEYWORD("errcode", K_ERRCODE, UNRESERVED_KEYWORD) - PG_KEYWORD("error", K_ERROR, UNRESERVED_KEYWORD) - PG_KEYWORD("exception", K_EXCEPTION, UNRESERVED_KEYWORD) - PG_KEYWORD("exit", K_EXIT, UNRESERVED_KEYWORD) - PG_KEYWORD("fetch", K_FETCH, UNRESERVED_KEYWORD) - PG_KEYWORD("first", K_FIRST, UNRESERVED_KEYWORD) - PG_KEYWORD("forward", K_FORWARD, UNRESERVED_KEYWORD) - PG_KEYWORD("get", K_GET, UNRESERVED_KEYWORD) - PG_KEYWORD("hint", K_HINT, UNRESERVED_KEYWORD) - PG_KEYWORD("import", K_IMPORT, UNRESERVED_KEYWORD) - PG_KEYWORD("info", K_INFO, UNRESERVED_KEYWORD) - PG_KEYWORD("insert", K_INSERT, UNRESERVED_KEYWORD) - PG_KEYWORD("is", K_IS, UNRESERVED_KEYWORD) - PG_KEYWORD("last", K_LAST, UNRESERVED_KEYWORD) - PG_KEYWORD("log", K_LOG, UNRESERVED_KEYWORD) - PG_KEYWORD("message", K_MESSAGE, UNRESERVED_KEYWORD) - PG_KEYWORD("message_text", K_MESSAGE_TEXT, UNRESERVED_KEYWORD) - PG_KEYWORD("move", K_MOVE, UNRESERVED_KEYWORD) - PG_KEYWORD("next", K_NEXT, UNRESERVED_KEYWORD) - PG_KEYWORD("no", K_NO, UNRESERVED_KEYWORD) - PG_KEYWORD("notice", K_NOTICE, UNRESERVED_KEYWORD) - PG_KEYWORD("open", K_OPEN, UNRESERVED_KEYWORD) - PG_KEYWORD("option", K_OPTION, UNRESERVED_KEYWORD) - PG_KEYWORD("perform", K_PERFORM, UNRESERVED_KEYWORD) - PG_KEYWORD("pg_context", K_PG_CONTEXT, UNRESERVED_KEYWORD) - PG_KEYWORD("pg_datatype_name", K_PG_DATATYPE_NAME, UNRESERVED_KEYWORD) - PG_KEYWORD("pg_exception_context", K_PG_EXCEPTION_CONTEXT, UNRESERVED_KEYWORD) - PG_KEYWORD("pg_exception_detail", K_PG_EXCEPTION_DETAIL, UNRESERVED_KEYWORD) - PG_KEYWORD("pg_exception_hint", K_PG_EXCEPTION_HINT, UNRESERVED_KEYWORD) - PG_KEYWORD("print_strict_params", K_PRINT_STRICT_PARAMS, UNRESERVED_KEYWORD) - PG_KEYWORD("prior", K_PRIOR, UNRESERVED_KEYWORD) - PG_KEYWORD("query", K_QUERY, UNRESERVED_KEYWORD) - PG_KEYWORD("raise", K_RAISE, UNRESERVED_KEYWORD) - PG_KEYWORD("relative", K_RELATIVE, UNRESERVED_KEYWORD) - PG_KEYWORD("reset", K_RESET, UNRESERVED_KEYWORD) - PG_KEYWORD("return", K_RETURN, UNRESERVED_KEYWORD) - PG_KEYWORD("returned_sqlstate", K_RETURNED_SQLSTATE, UNRESERVED_KEYWORD) - PG_KEYWORD("reverse", K_REVERSE, UNRESERVED_KEYWORD) - PG_KEYWORD("rollback", K_ROLLBACK, UNRESERVED_KEYWORD) - PG_KEYWORD("row_count", K_ROW_COUNT, UNRESERVED_KEYWORD) - PG_KEYWORD("rowtype", K_ROWTYPE, UNRESERVED_KEYWORD) - PG_KEYWORD("schema", K_SCHEMA, UNRESERVED_KEYWORD) - PG_KEYWORD("schema_name", K_SCHEMA_NAME, UNRESERVED_KEYWORD) - PG_KEYWORD("scroll", K_SCROLL, UNRESERVED_KEYWORD) - PG_KEYWORD("set", K_SET, UNRESERVED_KEYWORD) - PG_KEYWORD("slice", K_SLICE, UNRESERVED_KEYWORD) - PG_KEYWORD("sqlstate", K_SQLSTATE, UNRESERVED_KEYWORD) - PG_KEYWORD("stacked", K_STACKED, UNRESERVED_KEYWORD) - PG_KEYWORD("table", K_TABLE, UNRESERVED_KEYWORD) - PG_KEYWORD("table_name", K_TABLE_NAME, UNRESERVED_KEYWORD) - PG_KEYWORD("type", K_TYPE, UNRESERVED_KEYWORD) - PG_KEYWORD("use_column", K_USE_COLUMN, UNRESERVED_KEYWORD) - PG_KEYWORD("use_variable", K_USE_VARIABLE, UNRESERVED_KEYWORD) - PG_KEYWORD("variable_conflict", K_VARIABLE_CONFLICT, UNRESERVED_KEYWORD) - PG_KEYWORD("warning", K_WARNING, UNRESERVED_KEYWORD) +#undef PG_KEYWORD +#define PG_KEYWORD(kwname, value) value, + +static const int16 unreserved_keywords[] = { +#include "pl_unreserved_kwlist.h" }; static const int num_unreserved_keywords = lengthof(unreserved_keywords); @@ -256,7 +151,7 @@ plpgsql_yylex(void) { int tok1; TokenAuxData aux1; - const ScanKeyword *kw; + int kwnum; tok1 = internal_yylex(&aux1); if (tok1 == IDENT || tok1 == PARAM) @@ -333,12 +228,14 @@ plpgsql_yylex(void) &aux1.lval.word)) tok1 = T_DATUM; else if (!aux1.lval.word.quoted && - (kw = ScanKeywordLookup(aux1.lval.word.ident, - unreserved_keywords, - num_unreserved_keywords))) + (kwnum = ScanKeywordLookup(aux1.lval.word.ident, + pl_unreserved_kw_string, + pl_unreserved_kw_offsets, + num_unreserved_keywords)) >= 0) { - aux1.lval.keyword = kw->name; - tok1 = kw->value; + aux1.lval.keyword = pl_unreserved_kw_string + + pl_unreserved_kw_offsets[kwnum]; + tok1 = unreserved_keywords[kwnum]; } else tok1 = T_WORD; @@ -375,12 +272,14 @@ plpgsql_yylex(void) &aux1.lval.word)) tok1 = T_DATUM; else if (!aux1.lval.word.quoted && - (kw = ScanKeywordLookup(aux1.lval.word.ident, - unreserved_keywords, - num_unreserved_keywords))) + (kwnum = ScanKeywordLookup(aux1.lval.word.ident, + pl_unreserved_kw_string, + pl_unreserved_kw_offsets, + num_unreserved_keywords)) >= 0) { - aux1.lval.keyword = kw->name; - tok1 = kw->value; + aux1.lval.keyword = pl_unreserved_kw_string + + pl_unreserved_kw_offsets[kwnum]; + tok1 = unreserved_keywords[kwnum]; } else tok1 = T_WORD; @@ -499,7 +398,7 @@ plpgsql_token_is_unreserved_keyword(int token) for (i = 0; i < num_unreserved_keywords; i++) { - if (unreserved_keywords[i].value == token) + if (unreserved_keywords[i] == token) return true; } return false; @@ -695,8 +594,10 @@ void plpgsql_scanner_init(const char *str) { /* Start up the core scanner */ - yyscanner = scanner_init(str, &core_yy, - reserved_keywords, num_reserved_keywords); + yyscanner = scanner_init(str, &core_yy, reserved_keywords, + pl_reserved_kw_string, + pl_reserved_kw_offsets, + num_reserved_keywords); /* * scanorig points to the original string, which unlike the scanner's diff --git a/src/pl/plpgsql/src/pl_unreserved_kwlist.h b/src/pl/plpgsql/src/pl_unreserved_kwlist.h new file mode 100644 index 0000000000..4d30456869 --- /dev/null +++ b/src/pl/plpgsql/src/pl_unreserved_kwlist.h @@ -0,0 +1,111 @@ +/*------------------------------------------------------------------------- + * + * pl_unreserved_kwlist.h + * + * The keyword lists are kept in their own source files for use by + * automatic tools. The exact representation of a keyword is determined + * by the PG_KEYWORD macro, which is not defined in this file; it can + * be defined by the caller for special purposes. + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/pl/plpgsql/src/pl_unreserved_kwlist.h + * + *------------------------------------------------------------------------- + */ + +/* There is deliberately not an #ifndef PL_UNRESERVED_KWLIST_H here. */ + +/* + * List of (keyword-name, keyword-token-value) pairs. + * + * Be careful not to put the same word in both lists. Also be sure that + * pl_gram.y's unreserved_keyword production agrees with this list. + * + * !!WARNING!!: This list must be sorted by ASCII name, because binary + * search is used to locate entries. + */ + +/* name, value */ +PG_KEYWORD("absolute", K_ABSOLUTE) +PG_KEYWORD("alias", K_ALIAS) +PG_KEYWORD("array", K_ARRAY) +PG_KEYWORD("assert", K_ASSERT) +PG_KEYWORD("backward", K_BACKWARD) +PG_KEYWORD("call", K_CALL) +PG_KEYWORD("close", K_CLOSE) +PG_KEYWORD("collate", K_COLLATE) +PG_KEYWORD("column", K_COLUMN) +PG_KEYWORD("column_name", K_COLUMN_NAME) +PG_KEYWORD("commit", K_COMMIT) +PG_KEYWORD("constant", K_CONSTANT) +PG_KEYWORD("constraint", K_CONSTRAINT) +PG_KEYWORD("constraint_name", K_CONSTRAINT_NAME) +PG_KEYWORD("continue", K_CONTINUE) +PG_KEYWORD("current", K_CURRENT) +PG_KEYWORD("cursor", K_CURSOR) +PG_KEYWORD("datatype", K_DATATYPE) +PG_KEYWORD("debug", K_DEBUG) +PG_KEYWORD("default", K_DEFAULT) +PG_KEYWORD("detail", K_DETAIL) +PG_KEYWORD("diagnostics", K_DIAGNOSTICS) +PG_KEYWORD("do", K_DO) +PG_KEYWORD("dump", K_DUMP) +PG_KEYWORD("elseif", K_ELSIF) +PG_KEYWORD("elsif", K_ELSIF) +PG_KEYWORD("errcode", K_ERRCODE) +PG_KEYWORD("error", K_ERROR) +PG_KEYWORD("exception", K_EXCEPTION) +PG_KEYWORD("exit", K_EXIT) +PG_KEYWORD("fetch", K_FETCH) +PG_KEYWORD("first", K_FIRST) +PG_KEYWORD("forward", K_FORWARD) +PG_KEYWORD("get", K_GET) +PG_KEYWORD("hint", K_HINT) +PG_KEYWORD("import", K_IMPORT) +PG_KEYWORD("info", K_INFO) +PG_KEYWORD("insert", K_INSERT) +PG_KEYWORD("is", K_IS) +PG_KEYWORD("last", K_LAST) +PG_KEYWORD("log", K_LOG) +PG_KEYWORD("message", K_MESSAGE) +PG_KEYWORD("message_text", K_MESSAGE_TEXT) +PG_KEYWORD("move", K_MOVE) +PG_KEYWORD("next", K_NEXT) +PG_KEYWORD("no", K_NO) +PG_KEYWORD("notice", K_NOTICE) +PG_KEYWORD("open", K_OPEN) +PG_KEYWORD("option", K_OPTION) +PG_KEYWORD("perform", K_PERFORM) +PG_KEYWORD("pg_context", K_PG_CONTEXT) +PG_KEYWORD("pg_datatype_name", K_PG_DATATYPE_NAME) +PG_KEYWORD("pg_exception_context", K_PG_EXCEPTION_CONTEXT) +PG_KEYWORD("pg_exception_detail", K_PG_EXCEPTION_DETAIL) +PG_KEYWORD("pg_exception_hint", K_PG_EXCEPTION_HINT) +PG_KEYWORD("print_strict_params", K_PRINT_STRICT_PARAMS) +PG_KEYWORD("prior", K_PRIOR) +PG_KEYWORD("query", K_QUERY) +PG_KEYWORD("raise", K_RAISE) +PG_KEYWORD("relative", K_RELATIVE) +PG_KEYWORD("reset", K_RESET) +PG_KEYWORD("return", K_RETURN) +PG_KEYWORD("returned_sqlstate", K_RETURNED_SQLSTATE) +PG_KEYWORD("reverse", K_REVERSE) +PG_KEYWORD("rollback", K_ROLLBACK) +PG_KEYWORD("row_count", K_ROW_COUNT) +PG_KEYWORD("rowtype", K_ROWTYPE) +PG_KEYWORD("schema", K_SCHEMA) +PG_KEYWORD("schema_name", K_SCHEMA_NAME) +PG_KEYWORD("scroll", K_SCROLL) +PG_KEYWORD("set", K_SET) +PG_KEYWORD("slice", K_SLICE) +PG_KEYWORD("sqlstate", K_SQLSTATE) +PG_KEYWORD("stacked", K_STACKED) +PG_KEYWORD("table", K_TABLE) +PG_KEYWORD("table_name", K_TABLE_NAME) +PG_KEYWORD("type", K_TYPE) +PG_KEYWORD("use_column", K_USE_COLUMN) +PG_KEYWORD("use_variable", K_USE_VARIABLE) +PG_KEYWORD("variable_conflict", K_VARIABLE_CONFLICT) +PG_KEYWORD("warning", K_WARNING) diff --git a/src/tools/gen_keywords.pl b/src/tools/gen_keywords.pl new file mode 100644 index 0000000000..9d66adab74 --- /dev/null +++ b/src/tools/gen_keywords.pl @@ -0,0 +1,136 @@ +#---------------------------------------------------------------------- +# +# gen_keywords.pl +# Perl script that transforms a list of keywords into a single string +# and an array of offsets into it. These are emitted into a header so +# they can be passed to ScanKeywordLookup(). +# +# Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group +# Portions Copyright (c) 1994, Regents of the University of California +# +# src/tools/gen_keywords.pl +# +#---------------------------------------------------------------------- + +use strict; +use warnings; +use Getopt::Long; + +my $output_path = ''; +my $extern = 0; +my $prefix = ''; + +GetOptions( + 'output:s' => \$output_path, + 'extern' => \$extern, + 'prefix:s' => \$prefix) || usage(); + +my $kw_input_file = shift @ARGV || die "No input file.\n"; + +# Make sure output_path ends in a slash. +if ($output_path ne '' && substr($output_path, -1) ne '/') +{ + $output_path .= '/'; +} + +$kw_input_file =~ /(\w+)\.h$/; +my $base_filename = $1 . '_d'; +my $kw_def_file = $output_path . $base_filename . '.h'; + +open(my $kif, '<', $kw_input_file) || die "$kw_input_file: $!"; +open(my $kwdef, '>', $kw_def_file) || die "$kw_def_file: $!"; + +# Opening boilerplate for keyword definition header. +printf $kwdef <<EOM, $base_filename, uc $base_filename, uc $base_filename; +/*------------------------------------------------------------------------- + * + * %s.h + * List of keywords represented as a keyword string and offsets into it. + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * NOTES + * ****************************** + * *** DO NOT EDIT THIS FILE! *** + * ****************************** + * + * It has been GENERATED by src/tools/gen_keywords.pl + * + *------------------------------------------------------------------------- + */ + +#ifndef %s_H +#define %s_H + +EOM + +# Parse keyword header for names. +my @keywords; +while (<$kif>) +{ + if (/^PG_KEYWORD\("(\w+)",/) + { + push @keywords, $1; + } +} + +# Error out if the keyword names are not in ASCII order. +for my $i (0..$#keywords - 1) +{ + die qq|The keyword "$keywords[$i + 1]" is out of order in $kw_input_file| + if ($keywords[$i] cmp $keywords[$i + 1]) >= 0; +} + +# Emit the keyword string. + +if ($extern) +{ + printf $kwdef qq|const char *%sKeywordString =\n\t"|, $prefix; +} +else +{ + printf $kwdef qq|static const char *%skw_string =\n\t"|, $prefix; +} + +print $kwdef join qq|\\0"\n\t"|, @keywords; +print $kwdef qq|";\n\n|; +printf $kwdef "#endif\t\t\t\t\t\t\t/* %s_H */\n\n", uc $base_filename; + +# Emit an array of numerical offsets which will be used to index into the +# keyword string. + +if ($extern) +{ + printf $kwdef "const uint16 %sKeywordOffsets[] = {\n\t", $prefix; +} +else +{ + printf $kwdef "static const uint16 %skw_offsets[] = {\n\t", $prefix; +} + +my $offset = 0; +foreach my $name (@keywords) +{ + print $kwdef "$offset,\n\t"; + + # Calculate the cumulative offset of the next keyword, + # taking into account the null terminator. + $offset += length($name) + 1; +} + +print $kwdef "};\n"; + + +sub usage +{ + die <<EOM; +Usage: gen_keywords.pl [--output/-o <path>] [--prefix/-p <prefix>] input_file + --output Output directory + --prefix String prepended to var names in the output file + +gen_keywords.pl transforms a list of keywords into a single string and +an array of offsets into it. + +EOM +} diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm index eb2346b8d3..b82c0179d5 100644 --- a/src/tools/msvc/Solution.pm +++ b/src/tools/msvc/Solution.pm @@ -268,6 +268,22 @@ sub GenerateFiles "src/interfaces/ecpg/pgtypeslib/exports.txt", "LIBPGTYPES"); + if (IsNewer( + 'src/common/kwlist_d.h', + 'src/include/parser/kwlist.h')) + { + print "Generating kwlist_d.h...\n"; + system('perl src/tools/gen_keywords.pl --extern -o src/common src/include/parser/kwlist.h'); + } + + if (IsNewer( + 'src/include/common/kwlist_d.h', + 'src/common/kwlist_d.h')) + { + copyFile('src/common/kwlist_d.h', + 'src/include/common/kwlist_d.h'); + } + chdir('src/backend/utils'); my $pg_proc_dat = '../../../src/include/catalog/pg_proc.dat'; if ( IsNewer('fmgr-stamp', 'Gen_fmgrtab.pl') @@ -409,6 +425,34 @@ sub GenerateFiles chdir('../../..'); } + if (IsNewer( + 'src/pl/plpgsql/src/pl_reserved_kwlist_d.h', + 'src/pl/plpgsql/src/pl_reserved_kwlist.h') + || IsNewer( + 'src/pl/plpgsql/src/pl_unreserved_kwlist_d.h', + 'src/pl/plpgsql/src/pl_unreserved_kwlist.h')) + { + print "Generating pl_reserved_kwlist_d.h and pl_unreserved_kwlist_d.h...\n"; + chdir('src/pl/plpgsql/src'); + system('perl ../../../tools/gen_keywords.pl --prefix pl_reserved_ pl_reserved_kwlist.h'); + system('perl ../../../tools/gen_keywords.pl --prefix pl_unreserved_ pl_unreserved_kwlist.h'); + chdir('../../../..'); + } + + if (IsNewer( + 'src/interfaces/ecpg/preproc/c_kwlist_d.h', + 'src/interfaces/ecpg/preproc/c_kwlist.h') + || IsNewer( + 'src/interfaces/ecpg/preproc/ecpg_kwlist_d.h', + 'src/interfaces/ecpg/preproc/ecpg_kwlist.h')) + { + print "Generating c_kwlist_d.h and ecpg_kwlist_d.h...\n"; + chdir('src/interfaces/ecpg/preproc'); + system('perl ../../../tools/gen_keywords.pl --prefix c_ c_kwlist.h'); + system('perl ../../../tools/gen_keywords.pl --prefix ecpg_ ecpg_kwlist.h'); + chdir('../../../..'); + } + if (IsNewer( 'src/interfaces/ecpg/preproc/preproc.y', 'src/backend/parser/gram.y')) diff --git a/src/tools/msvc/clean.bat b/src/tools/msvc/clean.bat index 7a23a2b55f..82619ce3d0 100755 --- a/src/tools/msvc/clean.bat +++ b/src/tools/msvc/clean.bat @@ -41,6 +41,7 @@ if exist src\include\pg_config.h del /q src\include\pg_config.h if exist src\include\pg_config_ext.h del /q src\include\pg_config_ext.h if exist src\include\pg_config_os.h del /q src\include\pg_config_os.h if %DIST%==1 if exist src\backend\parser\gram.h del /q src\backend\parser\gram.h +if exist src\include\common/kwlist_d.h del /q src\include\common/kwlist_d.h if exist src\include\utils\errcodes.h del /q src\include\utils\errcodes.h if exist src\include\utils\fmgroids.h del /q src\include\utils\fmgroids.h if exist src\include\utils\fmgrprotos.h del /q src\include\utils\fmgrprotos.h @@ -51,6 +52,7 @@ if exist src\include\catalog\pg_*_d.h del /q src\include\catalog\pg_*_d.h if exist src\include\catalog\header-stamp del /q src\include\catalog\header-stamp if exist doc\src\sgml\version.sgml del /q doc\src\sgml\version.sgml +if %DIST%==1 if exist src\common\kwlist_d.h del /q src\common\kwlist_d.h if %DIST%==1 if exist src\backend\utils\fmgroids.h del /q src\backend\utils\fmgroids.h if %DIST%==1 if exist src\backend\utils\fmgrprotos.h del /q src\backend\utils\fmgrprotos.h if %DIST%==1 if exist src\backend\utils\fmgrtab.c del /q src\backend\utils\fmgrtab.c @@ -59,11 +61,15 @@ if %DIST%==1 if exist src\backend\utils\errcodes.h del /q src\backend\utils\errc if %DIST%==1 if exist src\backend\storage\lmgr\lwlocknames.c del /q src\backend\storage\lmgr\lwlocknames.c if %DIST%==1 if exist src\backend\storage\lmgr\lwlocknames.h del /q src\backend\storage\lmgr\lwlocknames.h if %DIST%==1 if exist src\pl\plpython\spiexceptions.h del /q src\pl\plpython\spiexceptions.h +if %DIST%==1 if exist src\pl\plpgsql\src\pl_reserved_kwlist_d.h del /q src\pl\plpgsql\src\pl_reserved_kwlist_d.h +if %DIST%==1 if exist src\pl\plpgsql\src\pl_unreserved_kwlist_d.h del /q src\pl\plpgsql\src\pl_unreserved_kwlist_d.h if %DIST%==1 if exist src\pl\plpgsql\src\plerrcodes.h del /q src\pl\plpgsql\src\plerrcodes.h if %DIST%==1 if exist src\pl\tcl\pltclerrcodes.h del /q src\pl\tcl\pltclerrcodes.h if %DIST%==1 if exist src\backend\utils\sort\qsort_tuple.c del /q src\backend\utils\sort\qsort_tuple.c if %DIST%==1 if exist src\bin\psql\sql_help.c del /q src\bin\psql\sql_help.c if %DIST%==1 if exist src\bin\psql\sql_help.h del /q src\bin\psql\sql_help.h +if %DIST%==1 if exist src\interfaces\ecpg\preproc\c_kwlist_d.h del /q src\interfaces\ecpg\preproc\c_kwlist_d.h +if %DIST%==1 if exist src\interfaces\ecpg\preproc\ecpg_kwlist_d.h del /q src\interfaces\ecpg\preproc\ecpg_kwlist_d.h if %DIST%==1 if exist src\interfaces\ecpg\preproc\preproc.y del /q src\interfaces\ecpg\preproc\preproc.y if %DIST%==1 if exist src\backend\catalog\postgres.bki del /q src\backend\catalog\postgres.bki if %DIST%==1 if exist src\backend\catalog\postgres.description del /q src\backend\catalog\postgres.description -- 2.17.1