/*-------------------------------------------------------------------------
*
* keywords.c
- * lexical token lookup for reserved words in postgres SQL
+ * lexical token lookup for reserved words in PostgreSQL
*
- * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.65 2000/01/26 05:56:42 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.135 2003/03/11 19:40:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
+#include "postgres.h"
+
#include <ctype.h>
-#include "postgres.h"
#include "nodes/parsenodes.h"
-#include "nodes/pg_list.h"
-#include "parse.h"
#include "parser/keywords.h"
+#include "parser/parse.h"
+
+/* NB: This file is also used by pg_dump. */
/*
* List of (keyword-name, keyword-token-value) pairs.
* !!WARNING!!: This list must be sorted, because binary
* search is used to locate entries.
*/
-static ScanKeyword ScanKeywords[] = {
+static const ScanKeyword ScanKeywords[] = {
/* name, value */
- {"abort", ABORT_TRANS},
+ {"abort", ABORT_P},
{"absolute", ABSOLUTE},
{"access", ACCESS},
{"action", ACTION},
{"aggregate", AGGREGATE},
{"all", ALL},
{"alter", ALTER},
+ {"analyse", ANALYSE}, /* British spelling */
{"analyze", ANALYZE},
{"and", AND},
{"any", ANY},
{"as", AS},
{"asc", ASC},
+ {"assertion", ASSERTION},
+ {"assignment", ASSIGNMENT},
+ {"at", AT},
+ {"authorization", AUTHORIZATION},
{"backward", BACKWARD},
{"before", BEFORE},
- {"begin", BEGIN_TRANS},
+ {"begin", BEGIN_P},
{"between", BETWEEN},
+ {"bigint", BIGINT},
{"binary", BINARY},
+ {"bit", BIT},
+ {"boolean", BOOLEAN},
{"both", BOTH},
{"by", BY},
{"cache", CACHE},
+ {"called", CALLED},
{"cascade", CASCADE},
{"case", CASE},
{"cast", CAST},
- {"char", CHAR},
+ {"chain", CHAIN},
+ {"char", CHAR_P},
{"character", CHARACTER},
+ {"characteristics", CHARACTERISTICS},
{"check", CHECK},
+ {"checkpoint", CHECKPOINT},
+ {"class", CLASS},
{"close", CLOSE},
{"cluster", CLUSTER},
{"coalesce", COALESCE},
{"committed", COMMITTED},
{"constraint", CONSTRAINT},
{"constraints", CONSTRAINTS},
+ {"conversion", CONVERSION_P},
+ {"convert", CONVERT},
{"copy", COPY},
{"create", CREATE},
{"createdb", CREATEDB},
{"cycle", CYCLE},
{"database", DATABASE},
{"day", DAY_P},
+ {"deallocate", DEALLOCATE},
+ {"dec", DEC},
{"decimal", DECIMAL},
{"declare", DECLARE},
{"default", DEFAULT},
{"deferrable", DEFERRABLE},
{"deferred", DEFERRED},
- {"delete", DELETE},
+ {"definer", DEFINER},
+ {"delete", DELETE_P},
+ {"delimiter", DELIMITER},
{"delimiters", DELIMITERS},
{"desc", DESC},
{"distinct", DISTINCT},
{"do", DO},
+ {"domain", DOMAIN_P},
{"double", DOUBLE},
{"drop", DROP},
{"each", EACH},
{"else", ELSE},
{"encoding", ENCODING},
- {"end", END_TRANS},
+ {"encrypted", ENCRYPTED},
+ {"end", END_P},
+ {"escape", ESCAPE},
{"except", EXCEPT},
{"exclusive", EXCLUSIVE},
{"execute", EXECUTE},
{"exists", EXISTS},
{"explain", EXPLAIN},
- {"extend", EXTEND},
+ {"external", EXTERNAL},
{"extract", EXTRACT},
{"false", FALSE_P},
{"fetch", FETCH},
- {"float", FLOAT},
+ {"first", FIRST_P},
+ {"float", FLOAT_P},
{"for", FOR},
+ {"force", FORCE},
{"foreign", FOREIGN},
{"forward", FORWARD},
+ {"freeze", FREEZE},
{"from", FROM},
{"full", FULL},
{"function", FUNCTION},
{"global", GLOBAL},
{"grant", GRANT},
- {"group", GROUP},
+ {"group", GROUP_P},
{"handler", HANDLER},
{"having", HAVING},
{"hour", HOUR_P},
+ {"ilike", ILIKE},
{"immediate", IMMEDIATE},
- {"in", IN},
+ {"immutable", IMMUTABLE},
+ {"implicit", IMPLICIT_P},
+ {"in", IN_P},
{"increment", INCREMENT},
{"index", INDEX},
{"inherits", INHERITS},
{"initially", INITIALLY},
{"inner", INNER_P},
+ {"inout", INOUT},
+ {"input", INPUT},
{"insensitive", INSENSITIVE},
{"insert", INSERT},
{"instead", INSTEAD},
+ {"int", INT},
+ {"integer", INTEGER},
{"intersect", INTERSECT},
{"interval", INTERVAL},
{"into", INTO},
+ {"invoker", INVOKER},
{"is", IS},
{"isnull", ISNULL},
{"isolation", ISOLATION},
{"key", KEY},
{"lancompiler", LANCOMPILER},
{"language", LANGUAGE},
+ {"last", LAST_P},
{"leading", LEADING},
{"left", LEFT},
{"level", LEVEL},
{"listen", LISTEN},
{"load", LOAD},
{"local", LOCAL},
+ {"localtime", LOCALTIME},
+ {"localtimestamp", LOCALTIMESTAMP},
{"location", LOCATION},
{"lock", LOCK_P},
{"match", MATCH},
{"nullif", NULLIF},
{"numeric", NUMERIC},
{"of", OF},
+ {"off", OFF},
{"offset", OFFSET},
{"oids", OIDS},
- {"old", CURRENT},
+ {"old", OLD},
{"on", ON},
{"only", ONLY},
{"operator", OPERATOR},
{"option", OPTION},
{"or", OR},
{"order", ORDER},
+ {"out", OUT_P},
{"outer", OUTER_P},
+ {"overlaps", OVERLAPS},
+ {"overlay", OVERLAY},
+ {"owner", OWNER},
{"partial", PARTIAL},
{"password", PASSWORD},
+ {"path", PATH_P},
{"pendant", PENDANT},
+ {"placing", PLACING},
{"position", POSITION},
{"precision", PRECISION},
+ {"prepare", PREPARE},
+ {"preserve", PRESERVE},
{"primary", PRIMARY},
{"prior", PRIOR},
{"privileges", PRIVILEGES},
{"procedural", PROCEDURAL},
{"procedure", PROCEDURE},
- {"public", PUBLIC},
{"read", READ},
+ {"real", REAL},
+ {"recheck", RECHECK},
{"references", REFERENCES},
+ {"reindex", REINDEX},
{"relative", RELATIVE},
{"rename", RENAME},
+ {"replace", REPLACE},
{"reset", RESET},
{"restrict", RESTRICT},
{"returns", RETURNS},
{"right", RIGHT},
{"rollback", ROLLBACK},
{"row", ROW},
+ {"rows", ROWS},
{"rule", RULE},
+ {"schema", SCHEMA},
{"scroll", SCROLL},
{"second", SECOND_P},
+ {"security", SECURITY},
{"select", SELECT},
{"sequence", SEQUENCE},
- {"serial", SERIAL},
{"serializable", SERIALIZABLE},
+ {"session", SESSION},
+ {"session_user", SESSION_USER},
{"set", SET},
{"setof", SETOF},
{"share", SHARE},
{"show", SHOW},
+ {"similar", SIMILAR},
+ {"simple", SIMPLE},
+ {"smallint", SMALLINT},
+ {"some", SOME},
+ {"stable", STABLE},
{"start", START},
{"statement", STATEMENT},
+ {"statistics", STATISTICS},
{"stdin", STDIN},
{"stdout", STDOUT},
+ {"storage", STORAGE},
+ {"strict", STRICT},
{"substring", SUBSTRING},
- {"sysid", SYSID},
+ {"sysid", SYSID},
{"table", TABLE},
{"temp", TEMP},
+ {"template", TEMPLATE},
{"temporary", TEMPORARY},
{"then", THEN},
{"time", TIME},
{"timestamp", TIMESTAMP},
- {"timezone_hour", TIMEZONE_HOUR},
- {"timezone_minute", TIMEZONE_MINUTE},
{"to", TO},
+ {"toast", TOAST},
{"trailing", TRAILING},
{"transaction", TRANSACTION},
+ {"treat", TREAT},
{"trigger", TRIGGER},
{"trim", TRIM},
{"true", TRUE_P},
{"truncate", TRUNCATE},
{"trusted", TRUSTED},
{"type", TYPE_P},
+ {"unencrypted", UNENCRYPTED},
{"union", UNION},
{"unique", UNIQUE},
+ {"unknown", UNKNOWN},
{"unlisten", UNLISTEN},
{"until", UNTIL},
{"update", UPDATE},
+ {"usage", USAGE},
{"user", USER},
{"using", USING},
{"vacuum", VACUUM},
{"valid", VALID},
+ {"validator", VALIDATOR},
{"values", VALUES},
{"varchar", VARCHAR},
{"varying", VARYING},
{"verbose", VERBOSE},
{"version", VERSION},
{"view", VIEW},
+ {"volatile", VOLATILE},
{"when", WHEN},
{"where", WHERE},
{"with", WITH},
+ {"without", WITHOUT},
{"work", WORK},
+ {"write", WRITE},
{"year", YEAR_P},
{"zone", ZONE},
};
-ScanKeyword *
-ScanKeywordLookup(char *text)
+/*
+ * ScanKeywordLookup - see if a given word is a keyword
+ *
+ * Returns a pointer to the ScanKeyword table entry, or NULL if no match.
+ *
+ * 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',
+ * even if we are in a locale where tolower() would produce more or different
+ * translations. This is to conform to the SQL99 spec, which says that
+ * keywords are to be matched in this way even though non-keyword identifiers
+ * receive a different case-normalization mapping.
+ */
+const ScanKeyword *
+ScanKeywordLookup(const char *text)
{
- ScanKeyword *low = &ScanKeywords[0];
- ScanKeyword *high = endof(ScanKeywords) - 1;
- ScanKeyword *middle;
- int difference;
+ int len,
+ i;
+ char word[NAMEDATALEN];
+ const ScanKeyword *low;
+ const ScanKeyword *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),
+ * and we don't trust isupper() very much either. In an ASCII-based
+ * encoding the tests against A and Z are sufficient, but we also
+ * check isupper() so that we will work correctly under EBCDIC. The
+ * actual case conversion step should work for either ASCII or EBCDIC.
+ */
+ for (i = 0; i < len; i++)
+ {
+ char ch = text[i];
+ if (ch >= 'A' && ch <= 'Z' && isupper((unsigned char) ch))
+ ch += 'a' - 'A';
+ word[i] = ch;
+ }
+ word[len] = '\0';
+
+ /*
+ * Now do a binary search using plain strcmp() comparison.
+ */
+ low = &ScanKeywords[0];
+ high = endof(ScanKeywords) - 1;
while (low <= high)
{
+ const ScanKeyword *middle;
+ int difference;
+
middle = low + (high - low) / 2;
- difference = strcmp(middle->name, text);
+ difference = strcmp(middle->name, word);
if (difference == 0)
return middle;
else if (difference < 0)