On 12/20/18, Tom Lane <[email protected]> wrote:
> I'd be inclined to put the script in src/tools, I think. IMO src/common
> is for code that actually gets built into our executables.
Done.
>> which takes
>> pl_unreserved_kwlist.h as input and outputs
>> pl_unreserved_kwlist_offset.h and pl_unreserved_kwlist_string.h.
>
> I wonder whether we'd not be better off producing just one output
> file, in which we have the offsets emitted as PG_KEYWORD macros
> and then the giant string emitted as a macro definition, ie
> something like
>
> #define PG_KEYWORD_STRING \
> "absolute\0" \
> "alias\0" \
> ...
>
> That simplifies the Makefile-hacking, at least, and it possibly gives
> callers more flexibility about what they actually want to do with the
> string.
Okay, I tried that. Since the script is turning one header into
another, I borrowed the "*_d.h" nomenclature from the catalogs. Using
a single file required some #ifdef hacks in the output file. Maybe
there's a cleaner way to do this, but I don't know what it is.
Using a single file also gave me another idea: Take value and category
out of ScanKeyword, and replace them with an index into another array
containing those, which will only be accessed in the event of a hit.
That would shrink ScanKeyword to 4 bytes (offset, index), further
increasing locality of reference. Might not be worth it, but I can try
it after moving on to the core scanner.
> I'm for "not change things unnecessarily". People might well be
> scraping the keyword list out of parser/kwlist.h for other purposes
> right now --- indeed, it's defined the way it is exactly to let
> people do that. I don't see a good reason to force them to redo
> whatever tooling they have that depends on that. So let's build
> kwlist_offsets.h alongside that, but not change kwlist.h itself.
Done.
>> I used the global .gitignore, but maybe that's an abuse of it.
>
> Yeah, I'd say it is.
Moved.
>> +# TODO: Error out if the keyword names are not in ASCII order.
>
> +many for including such a check.
Done.
> Also note that we don't require people to have Perl installed when
> building from a tarball. Therefore, these derived headers must get
> built during "make distprep" and removed by maintainer-clean but
> not distclean. I think this also has some implications for VPATH
> builds, but as long as you follow the pattern used for other
> derived header files (e.g. fmgroids.h), you should be fine.
Done. I also blindly added support for MSVC.
-John Naylor
src/common/keywords.c | 55 ++++++++++++
src/include/common/keywords.h | 14 +++
src/pl/plpgsql/src/.gitignore | 1 +
src/pl/plpgsql/src/Makefile | 9 +-
src/pl/plpgsql/src/pl_scanner.c | 119 ++++++--------------------
src/pl/plpgsql/src/pl_unreserved_kwlist.h | 107 +++++++++++++++++++++++
src/tools/gen_keywords.pl | 136 ++++++++++++++++++++++++++++++
src/tools/msvc/Solution.pm | 10 +++
8 files changed, 356 insertions(+), 95 deletions(-)
diff --git a/src/common/keywords.c b/src/common/keywords.c
index 0c0c794c68..0fb14a0372 100644
--- a/src/common/keywords.c
+++ b/src/common/keywords.c
@@ -112,3 +112,58 @@ ScanKeywordLookup(const char *text,
return NULL;
}
+
+/* Like ScanKeywordLookup, but uses offsets into a keyword string. */
+const ScanKeywordOffset *
+ScanKeywordLookupOffset(const char *text,
+ const ScanKeywordOffset *keywords,
+ int num_keywords,
+ const char *keyword_string)
+{
+ int len,
+ i;
+ char word[NAMEDATALEN];
+ const ScanKeywordOffset *low;
+ const ScanKeywordOffset *high;
+
+ len = strlen(text);
+ /* We assume all keywords are shorter than NAMEDATALEN. */
+ if (len >= NAMEDATALEN)
+ return NULL;
+
+ /*
+ * Apply an ASCII-only downcasing. We must not use tolower() since it may
+ * produce the wrong translation in some locales (eg, Turkish).
+ */
+ for (i = 0; i < len; i++)
+ {
+ char ch = text[i];
+
+ if (ch >= 'A' && ch <= 'Z')
+ ch += 'a' - 'A';
+ word[i] = ch;
+ }
+ word[len] = '\0';
+
+ /*
+ * Now do a binary search using plain strcmp() comparison.
+ */
+ low = keywords;
+ high = keywords + (num_keywords - 1);
+ while (low <= high)
+ {
+ const ScanKeywordOffset *middle;
+ int difference;
+
+ middle = low + (high - low) / 2;
+ difference = strcmp(keyword_string + middle->offset, word);
+ if (difference == 0)
+ return middle;
+ else if (difference < 0)
+ low = middle + 1;
+ else
+ high = middle - 1;
+ }
+
+ return NULL;
+}
diff --git a/src/include/common/keywords.h b/src/include/common/keywords.h
index 0b31505b66..7cd7bf4461 100644
--- a/src/include/common/keywords.h
+++ b/src/include/common/keywords.h
@@ -28,11 +28,20 @@ typedef struct ScanKeyword
int16 category; /* see codes above */
} ScanKeyword;
+typedef struct ScanKeywordOffset
+{
+ int32 offset; /* offset into a keyword string */
+ int16 value; /* grammar's token code */
+ int16 category; /* see codes above */
+} ScanKeywordOffset;
+
#ifndef FRONTEND
extern PGDLLIMPORT const ScanKeyword ScanKeywords[];
+extern PGDLLIMPORT const ScanKeywordOffset ScanKeywordsOffset[];
extern PGDLLIMPORT const int NumScanKeywords;
#else
extern const ScanKeyword ScanKeywords[];
+extern const ScanKeywordOffset ScanKeywordsOffset[];
extern const int NumScanKeywords;
#endif
@@ -41,4 +50,9 @@ extern const ScanKeyword *ScanKeywordLookup(const char *text,
const ScanKeyword *keywords,
int num_keywords);
+const ScanKeywordOffset *ScanKeywordLookupOffset(const char *text,
+ const ScanKeywordOffset *keywords,
+ int num_keywords,
+ const char *keywords_as_string);
+
#endif /* KEYWORDS_H */
diff --git a/src/pl/plpgsql/src/.gitignore b/src/pl/plpgsql/src/.gitignore
index ff6ac965fd..a649302fdb 100644
--- a/src/pl/plpgsql/src/.gitignore
+++ b/src/pl/plpgsql/src/.gitignore
@@ -1,3 +1,4 @@
+/*kwlist_d.h
/pl_gram.c
/pl_gram.h
/plerrcodes.h
diff --git a/src/pl/plpgsql/src/Makefile b/src/pl/plpgsql/src/Makefile
index 25a5a9d448..742e79bc66 100644
--- a/src/pl/plpgsql/src/Makefile
+++ b/src/pl/plpgsql/src/Makefile
@@ -60,7 +60,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_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_unreserved_kwlist_d.h
# See notes in src/backend/parser/Makefile about the following two rules
pl_gram.h: pl_gram.c
@@ -72,6 +72,9 @@ 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_unreserved_kwlist_d.h: pl_unreserved_kwlist.h $(top_srcdir)/src/tools/gen_keywords.pl
+ $(PERL) $(top_srcdir)/src/tools/gen_keywords.pl $<
check: submake
$(pg_regress_check) $(REGRESS_OPTS) $(REGRESS)
@@ -84,7 +87,7 @@ 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_unreserved_kwlist_d.h
# pl_gram.c, pl_gram.h and plerrcodes.h are in the distribution tarball,
# so they are not cleaned here.
@@ -93,4 +96,4 @@ clean distclean: clean-lib
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_unreserved_kwlist_d.h
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
index ab18946847..048a6ff05b 100644
--- a/src/pl/plpgsql/src/pl_scanner.c
+++ b/src/pl/plpgsql/src/pl_scanner.c
@@ -21,7 +21,19 @@
#include "plpgsql.h"
#include "pl_gram.h" /* must be after parser/scanner.h */
+/*
+ * We include the keyword definition files twice, once for the keyword
+ * string, and once for the ScanKeyword array.
+ * TODO: This comment will move to common/keywords.c and all other files
+ * will refer to that.
+ */
+#include "pl_unreserved_kwlist_d.h"
+static const char *pl_unreserved_keyword_string = PL_UNRESERVED_KWLIST_STRING;
+/*
+ * We must define PG_KEYWORD after assigning the strings and before assigning
+ * the ScanKeyword arrays.
+ */
#define PG_KEYWORD(a,b,c) {a,b,c},
@@ -96,88 +108,8 @@ static const ScanKeyword reserved_keywords[] = {
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)
+static const ScanKeywordOffset unreserved_keywords[] = {
+#include "pl_unreserved_kwlist_d.h"
};
static const int num_unreserved_keywords = lengthof(unreserved_keywords);
@@ -256,7 +188,7 @@ plpgsql_yylex(void)
{
int tok1;
TokenAuxData aux1;
- const ScanKeyword *kw;
+ const ScanKeywordOffset *kw;
tok1 = internal_yylex(&aux1);
if (tok1 == IDENT || tok1 == PARAM)
@@ -332,11 +264,12 @@ plpgsql_yylex(void)
&aux1.lval.word))
tok1 = T_DATUM;
else if (!aux1.lval.word.quoted &&
- (kw = ScanKeywordLookup(aux1.lval.word.ident,
+ (kw = ScanKeywordLookupOffset(aux1.lval.word.ident,
unreserved_keywords,
- num_unreserved_keywords)))
+ num_unreserved_keywords,
+ pl_unreserved_keyword_string)))
{
- aux1.lval.keyword = kw->name;
+ aux1.lval.keyword = pl_unreserved_keyword_string + kw->offset;
tok1 = kw->value;
}
else
@@ -362,11 +295,12 @@ plpgsql_yylex(void)
{
/* try for unreserved keyword, then for variable name */
if (core_yy.scanbuf[aux1.lloc] != '"' &&
- (kw = ScanKeywordLookup(aux1.lval.str,
+ (kw = ScanKeywordLookupOffset(aux1.lval.str,
unreserved_keywords,
- num_unreserved_keywords)))
+ num_unreserved_keywords,
+ pl_unreserved_keyword_string)))
{
- aux1.lval.keyword = kw->name;
+ aux1.lval.keyword = pl_unreserved_keyword_string + kw->offset;
tok1 = kw->value;
}
else if (plpgsql_parse_word(aux1.lval.str,
@@ -386,11 +320,12 @@ plpgsql_yylex(void)
&aux1.lval.word))
tok1 = T_DATUM;
else if (!aux1.lval.word.quoted &&
- (kw = ScanKeywordLookup(aux1.lval.word.ident,
+ (kw = ScanKeywordLookupOffset(aux1.lval.word.ident,
unreserved_keywords,
- num_unreserved_keywords)))
+ num_unreserved_keywords,
+ pl_unreserved_keyword_string)))
{
- aux1.lval.keyword = kw->name;
+ aux1.lval.keyword = pl_unreserved_keyword_string + kw->offset;
tok1 = kw->value;
}
else
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..5ad464b196
--- /dev/null
+++ b/src/pl/plpgsql/src/pl_unreserved_kwlist.h
@@ -0,0 +1,107 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/pl/plpgsql/src/pl_unreserved_kwlist.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+
+/*
+ * List of (keyword-name, keyword-token-value) pairs.
+ *
+ * !!WARNING!!: This list must be sorted, because binary
+ * search is used to locate entries.
+ */
+
+/* name, value, category */
+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)
diff --git a/src/tools/gen_keywords.pl b/src/tools/gen_keywords.pl
new file mode 100644
index 0000000000..f00cda100b
--- /dev/null
+++ b/src/tools/gen_keywords.pl
@@ -0,0 +1,136 @@
+#----------------------------------------------------------------------
+#
+# gen_keywords.pl
+# Perl script that generates *kwlist_d.h from a given *kwlist.h
+# keyword list file. These headers are then included into files that
+# call ScanKeywordLookup() on that keyword list. The keyword name is
+# is represented as an offset into a single string.
+#
+# Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/tools/gen_keywords.pl
+#
+#----------------------------------------------------------------------
+
+
+my $output_path = '';
+my $kw_input_file;
+
+# Process command line switches.
+while (@ARGV)
+{
+ my $arg = shift @ARGV;
+ if ($arg !~ /^-/)
+ {
+ $kw_input_file = $arg;
+ }
+ elsif ($arg =~ /^-o/)
+ {
+ $output_path = length($arg) > 2 ? substr($arg, 2) : shift @ARGV;
+ }
+ else
+ {
+ usage();
+ }
+}
+
+
+# Make sure output_path ends in a slash.
+if ($output_path ne '' && substr($output_path, -1) ne '/')
+{
+ $output_path .= '/';
+}
+
+$kw_input_file =~ /(\w*kwlist)\.h/;
+my $base_filename = $1;
+
+my $kw_def_file = $output_path . $base_filename . '_d.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;
+/*-------------------------------------------------------------------------
+ *
+ * %s_d.h
+ * List of keywords represented as a keyword string and offsets into it.
+ *
+ * Portions Copyright (c) 1996-2018, 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
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/*
+ * There is deliberately not an #ifndef %s_D_H here.
+ * See parser/kwlist.h
+ */
+
+EOM
+
+
+my $name;
+my $value;
+my $category;
+my @keywords;
+my $offset = 0;
+
+print $kwdef "#ifdef PG_KEYWORD\n\n";
+while (<$kif>)
+{
+ if (/^PG_KEYWORD\("(\w+)",\s+(\w+),\s+(\w+)\)/)
+ {
+ $name = $1;
+ $value = $2;
+ $category = $3;
+
+ push @keywords, $name;
+
+ # Emit ScanKeyword macros with numerical offsets instead of text.
+ print $kwdef "PG_KEYWORD($offset, $value, $category)\n";
+
+ # Calculate the cumulative offset of the next keyword,
+ # taking into account the null terminator.
+ $offset += length($name) + 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;
+}
+
+print $kwdef "\n#endif\t\t\t\t\t\t\t/* PG_KEYWORD */\n\n";
+
+# Now generate the keyword string.
+print $kwdef "#ifndef PG_KEYWORD\n";
+printf $kwdef qq|#define %s_STRING \\\n\t"|, uc $base_filename;
+print $kwdef join qq|\\0" \\\n\t"|, @keywords;
+print $kwdef qq|";\n\n|;
+printf $kwdef "#endif\t\t\t\t\t\t\t/* %s_STRING */\n", uc $base_filename;
+
+
+sub usage
+{
+ die <<EOM;
+Usage: gen_keywords.pl [options] header...
+
+Options:
+ -o output path
+
+gen_keywords.pl transforms a list of keywords into a list of offsets
+into a single string.
+
+EOM
+}
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index 0b7cdf8dd5..981f3ad97c 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -411,6 +411,16 @@ sub GenerateFiles
chdir('../../..');
}
+ if (IsNewer(
+ 'src/pl/plpgsql/src/pl_unreserved_kwlist_d.h',
+ 'src/pl/plpgsql/src/pl_unreserved_kwlist.h'))
+ {
+ print "Generating pl_unreserved_kwlist_d.h...\n";
+ chdir('src/pl/plpgsql/src/');
+ system('perl ../../../../src/tools/gen_keywords.pl pl_unreserved_kwlist.h -o pl_unreserved_kwlist_d.h');
+ chdir('../../../..');
+ }
+
if (IsNewer(
'src/interfaces/ecpg/preproc/preproc.y',
'src/backend/parser/gram.y'))