]> granicus.if.org Git - postgresql/blobdiff - src/backend/parser/keywords.c
Implement SQL92-compatible FIRST, LAST, ABSOLUTE n, RELATIVE n options
[postgresql] / src / backend / parser / keywords.c
index 05035f2344f0a9113965f628ba27c8ee25f7b240..49432cb957a83f0a44dc0843d6356037bc253ca1 100644 (file)
@@ -1,24 +1,26 @@
 /*-------------------------------------------------------------------------
  *
  * 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.
@@ -26,9 +28,9 @@
  * !!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},
@@ -37,25 +39,38 @@ static ScanKeyword ScanKeywords[] = {
        {"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},
@@ -66,6 +81,8 @@ static ScanKeyword ScanKeywords[] = {
        {"committed", COMMITTED},
        {"constraint", CONSTRAINT},
        {"constraints", CONSTRAINTS},
+       {"conversion", CONVERSION_P},
+       {"convert", CONVERT},
        {"copy", COPY},
        {"create", CREATE},
        {"createdb", CREATEDB},
@@ -79,57 +96,75 @@ static ScanKeyword ScanKeywords[] = {
        {"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},
@@ -137,6 +172,7 @@ static ScanKeyword ScanKeywords[] = {
        {"key", KEY},
        {"lancompiler", LANCOMPILER},
        {"language", LANGUAGE},
+       {"last", LAST_P},
        {"leading", LEADING},
        {"left", LEFT},
        {"level", LEVEL},
@@ -145,6 +181,8 @@ static ScanKeyword ScanKeywords[] = {
        {"listen", LISTEN},
        {"load", LOAD},
        {"local", LOCAL},
+       {"localtime", LOCALTIME},
+       {"localtimestamp", LOCALTIMESTAMP},
        {"location", LOCATION},
        {"lock", LOCK_P},
        {"match", MATCH},
@@ -172,31 +210,43 @@ static ScanKeyword ScanKeywords[] = {
        {"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},
@@ -204,75 +254,139 @@ static ScanKeyword ScanKeywords[] = {
        {"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)