On 12/18/18, Tom Lane <t...@sss.pgh.pa.us> wrote: > I'd be kind of inclined to convert all uses of ScanKeyword to the new way, > if only for consistency's sake. On the other hand, I'm not the one > volunteering to do the work.
That's reasonable, as long as the design is nailed down first. Along those lines, attached is a heavily WIP patch that only touches plpgsql unreserved keywords, to test out the new methodology in a limited area. After settling APIs and name/directory bikeshedding, I'll move on to the other four keyword types. There's a new Perl script, src/common/gen_keywords.pl, which takes pl_unreserved_kwlist.h as input and outputs pl_unreserved_kwlist_offset.h and pl_unreserved_kwlist_string.h. The output headers are not installed or symlinked anywhere. Since the input keyword lists will never be #included directly, they might be better as .txt files, like errcodes.txt. If we went that far, we might also remove the PG_KEYWORD macros (they'd still be in the output files) and rename parser/kwlist.h to common/core_kwlist.txt. There's also a case for not changing things unnecessarily, especially if there's ever a new reason to include the base keyword list directly. To keep the other keyword types functional, I had to add a separate new struct ScanKeywordOffset and new function ScanKeywordLookupOffset(), so the patch is a bit messier than the final will be. With a 4-byte offset, ScankeyWordOffset is 8 bytes, down from 12, and is now a power of 2. I used the global .gitignore, but maybe that's an abuse of it. Make check passes, but I don't know how well it stresses keyword use. I'll create a commitfest entry soon. -John Naylor
.gitignore | 2 + src/common/gen_keywords.pl | 151 ++++++++++++++++++++++++++++++ src/common/keywords.c | 55 +++++++++++ src/include/common/keywords.h | 14 +++ src/pl/plpgsql/src/Makefile | 13 ++- src/pl/plpgsql/src/pl_scanner.c | 108 ++++----------------- src/pl/plpgsql/src/pl_unreserved_kwlist.h | 107 +++++++++++++++++++++ 7 files changed, 355 insertions(+), 95 deletions(-) diff --git a/.gitignore b/.gitignore index 794e35b73c..8a9a05c20a 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,8 @@ win32ver.rc *.exe lib*dll.def lib*.pc +*kwlist_offset.h +*kwlist_string.h # Local excludes in root directory /GNUmakefile diff --git a/src/common/gen_keywords.pl b/src/common/gen_keywords.pl new file mode 100644 index 0000000000..a8a8576d43 --- /dev/null +++ b/src/common/gen_keywords.pl @@ -0,0 +1,151 @@ +#---------------------------------------------------------------------- +# +# gen_keywords.pl +# Perl script that generates *_offset.h and *_string.h from a given +# 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/commen/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_offset_file = $output_path . $base_filename . '_offset.h'; +my $kw_string_file = $output_path . $base_filename . '_string.h'; + +open(my $kif, '<', $kw_input_file) || die "$kw_input_file: $!"; +open(my $kof, '>', $kw_offset_file) || die "$kw_offset_file: $!"; +open(my $ksf, '>', $kw_string_file) || die "$kw_string_file: $!"; + +# Opening boilerplate for keyword offset header. +printf $kof <<EOM, $base_filename, uc $base_filename; +/*------------------------------------------------------------------------- + * + * %s_offset.h + * List of keywords represented as offsets into a keyword string. + * + * 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/backend/parser/gen_keywords.pl + * + *------------------------------------------------------------------------- + */ + +/* + * There is deliberately not an #ifndef %s_OFFSET_H here. + * See parser/kwlist.h + */ + +EOM + +# Opening boilerplate for keywords-as-string header. +printf $ksf <<EOM, $base_filename, uc $base_filename, uc $base_filename, $base_filename; +/*------------------------------------------------------------------------- + * + * %s_string.h + * List of keywords represented as a single string. + * + * 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/backend/parser/gen_keywords.pl + * + *------------------------------------------------------------------------- + */ +#ifndef %s_STRING_H +#define %s_STRING_H + +EOM + + +my $name; +my $value; +my $category; +my @keywords; +my $offset = 0; + +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 $kof "PG_KEYWORD($offset, $value, $category)\n"; + + # Calculate the cumulative offset of the next keyword, + # taking into account the null terminator. + $offset += length($name) +1; + } +} + +# TODO: Error out if the keyword names are not in ASCII order. + +printf $ksf qq|const char *%s_string =\n"|, $base_filename; +print $ksf join qq|\\0"\n"|, @keywords; +printf $ksf qq|";\n\n#endif\t\t\t\t\t\t\t/* %s_STRING_H */\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/common/keywords.c b/src/common/keywords.c index 0c0c794c68..417c078ffe 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 *keywords_as_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(keywords_as_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/Makefile b/src/pl/plpgsql/src/Makefile index 25a5a9d448..8d49b224f3 100644 --- a/src/pl/plpgsql/src/Makefile +++ b/src/pl/plpgsql/src/Makefile @@ -60,18 +60,25 @@ 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_offset.h pl_unreserved_kwlist_string.h -# See notes in src/backend/parser/Makefile about the following two rules +# See notes in src/backend/parser/Makefile about the following three rules pl_gram.h: pl_gram.c touch $@ +pl_unreserved_kwlist_string.h: pl_unreserved_kwlist_offset.h + touch $@ + pl_gram.c: BISONFLAGS += -d # generate plerrcodes.h from src/backend/utils/errcodes.txt 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_offset.h: pl_unreserved_kwlist.h $(top_srcdir)/src/common/gen_keywords.pl + $(PERL) $(top_srcdir)/src/common/gen_keywords.pl $< + check: submake $(pg_regress_check) $(REGRESS_OPTS) $(REGRESS) @@ -93,4 +100,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_offset.h pl_unreserved_kwlist_string.h diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c index ab18946847..835db36fde 100644 --- a/src/pl/plpgsql/src/pl_scanner.c +++ b/src/pl/plpgsql/src/pl_scanner.c @@ -20,6 +20,7 @@ #include "plpgsql.h" #include "pl_gram.h" /* must be after parser/scanner.h */ +#include "pl_unreserved_kwlist_string.h" #define PG_KEYWORD(a,b,c) {a,b,c}, @@ -96,88 +97,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_offset.h" }; static const int num_unreserved_keywords = lengthof(unreserved_keywords); @@ -256,7 +177,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 +253,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_kwlist_string))) { - aux1.lval.keyword = kw->name; + aux1.lval.keyword = pl_unreserved_kwlist_string + kw->offset; tok1 = kw->value; } else @@ -362,11 +284,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_kwlist_string))) { - aux1.lval.keyword = kw->name; + aux1.lval.keyword = pl_unreserved_kwlist_string + kw->offset; tok1 = kw->value; } else if (plpgsql_parse_word(aux1.lval.str, @@ -386,11 +309,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_kwlist_string))) { - aux1.lval.keyword = kw->name; + aux1.lval.keyword = pl_unreserved_kwlist_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)