postgres/src/backend/parser/keywords.c
Tom Lane 5a7471c307 Add COST and ROWS options to CREATE/ALTER FUNCTION, plus underlying pg_proc
columns procost and prorows, to allow simple user adjustment of the estimated
cost of a function call, as well as control of the estimated number of rows
returned by a set-returning function.  We might eventually wish to extend this
to allow function-specific estimation routines, but there seems to be
consensus that we should try a simple constant estimate first.  In particular
this provides a relatively simple way to control the order in which different
WHERE clauses are applied in a plan node, which is a Good Thing in view of the
fact that the recent EquivalenceClass planner rewrite made that much less
predictable than before.
2007-01-22 01:35:23 +00:00

457 lines
10 KiB
C

/*-------------------------------------------------------------------------
*
* keywords.c
* lexical token lookup for reserved words in PostgreSQL
*
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.182 2007/01/22 01:35:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <ctype.h>
#include "nodes/parsenodes.h"
#include "parser/gramparse.h" /* required before parser/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 const ScanKeyword ScanKeywords[] = {
/* name, value */
{"abort", ABORT_P},
{"absolute", ABSOLUTE_P},
{"access", ACCESS},
{"action", ACTION},
{"add", ADD_P},
{"admin", ADMIN},
{"after", AFTER},
{"aggregate", AGGREGATE},
{"all", ALL},
{"also", ALSO},
{"alter", ALTER},
{"analyse", ANALYSE}, /* British spelling */
{"analyze", ANALYZE},
{"and", AND},
{"any", ANY},
{"array", ARRAY},
{"as", AS},
{"asc", ASC},
{"assertion", ASSERTION},
{"assignment", ASSIGNMENT},
{"asymmetric", ASYMMETRIC},
{"at", AT},
{"authorization", AUTHORIZATION},
{"backward", BACKWARD},
{"before", BEFORE},
{"begin", BEGIN_P},
{"between", BETWEEN},
{"bigint", BIGINT},
{"binary", BINARY},
{"bit", BIT},
{"boolean", BOOLEAN_P},
{"both", BOTH},
{"by", BY},
{"cache", CACHE},
{"called", CALLED},
{"cascade", CASCADE},
{"cascaded", CASCADED},
{"case", CASE},
{"cast", CAST},
{"chain", CHAIN},
{"char", CHAR_P},
{"character", CHARACTER},
{"characteristics", CHARACTERISTICS},
{"check", CHECK},
{"checkpoint", CHECKPOINT},
{"class", CLASS},
{"close", CLOSE},
{"cluster", CLUSTER},
{"coalesce", COALESCE},
{"collate", COLLATE},
{"column", COLUMN},
{"comment", COMMENT},
{"commit", COMMIT},
{"committed", COMMITTED},
{"concurrently", CONCURRENTLY},
{"connection", CONNECTION},
{"constraint", CONSTRAINT},
{"constraints", CONSTRAINTS},
{"content", CONTENT_P},
{"conversion", CONVERSION_P},
{"convert", CONVERT},
{"copy", COPY},
{"cost", COST},
{"create", CREATE},
{"createdb", CREATEDB},
{"createrole", CREATEROLE},
{"createuser", CREATEUSER},
{"cross", CROSS},
{"csv", CSV},
{"current_date", CURRENT_DATE},
{"current_role", CURRENT_ROLE},
{"current_time", CURRENT_TIME},
{"current_timestamp", CURRENT_TIMESTAMP},
{"current_user", CURRENT_USER},
{"cursor", CURSOR},
{"cycle", CYCLE},
{"database", DATABASE},
{"day", DAY_P},
{"deallocate", DEALLOCATE},
{"dec", DEC},
{"decimal", DECIMAL_P},
{"declare", DECLARE},
{"default", DEFAULT},
{"defaults", DEFAULTS},
{"deferrable", DEFERRABLE},
{"deferred", DEFERRED},
{"definer", DEFINER},
{"delete", DELETE_P},
{"delimiter", DELIMITER},
{"delimiters", DELIMITERS},
{"desc", DESC},
{"disable", DISABLE_P},
{"distinct", DISTINCT},
{"do", DO},
{"document", DOCUMENT_P},
{"domain", DOMAIN_P},
{"double", DOUBLE_P},
{"drop", DROP},
{"each", EACH},
{"else", ELSE},
{"enable", ENABLE_P},
{"encoding", ENCODING},
{"encrypted", ENCRYPTED},
{"end", END_P},
{"escape", ESCAPE},
{"except", EXCEPT},
{"excluding", EXCLUDING},
{"exclusive", EXCLUSIVE},
{"execute", EXECUTE},
{"exists", EXISTS},
{"explain", EXPLAIN},
{"external", EXTERNAL},
{"extract", EXTRACT},
{"false", FALSE_P},
{"fetch", FETCH},
{"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},
{"granted", GRANTED},
{"greatest", GREATEST},
{"group", GROUP_P},
{"handler", HANDLER},
{"having", HAVING},
{"header", HEADER_P},
{"hold", HOLD},
{"hour", HOUR_P},
{"if", IF_P},
{"ilike", ILIKE},
{"immediate", IMMEDIATE},
{"immutable", IMMUTABLE},
{"implicit", IMPLICIT_P},
{"in", IN_P},
{"including", INCLUDING},
{"increment", INCREMENT},
{"index", INDEX},
{"indexes", INDEXES},
{"inherit", INHERIT},
{"inherits", INHERITS},
{"initially", INITIALLY},
{"inner", INNER_P},
{"inout", INOUT},
{"input", INPUT_P},
{"insensitive", INSENSITIVE},
{"insert", INSERT},
{"instead", INSTEAD},
{"int", INT_P},
{"integer", INTEGER},
{"intersect", INTERSECT},
{"interval", INTERVAL},
{"into", INTO},
{"invoker", INVOKER},
{"is", IS},
{"isnull", ISNULL},
{"isolation", ISOLATION},
{"join", JOIN},
{"key", KEY},
{"lancompiler", LANCOMPILER},
{"language", LANGUAGE},
{"large", LARGE_P},
{"last", LAST_P},
{"leading", LEADING},
{"least", LEAST},
{"left", LEFT},
{"level", LEVEL},
{"like", LIKE},
{"limit", LIMIT},
{"listen", LISTEN},
{"load", LOAD},
{"local", LOCAL},
{"localtime", LOCALTIME},
{"localtimestamp", LOCALTIMESTAMP},
{"location", LOCATION},
{"lock", LOCK_P},
{"login", LOGIN_P},
{"match", MATCH},
{"maxvalue", MAXVALUE},
{"minute", MINUTE_P},
{"minvalue", MINVALUE},
{"mode", MODE},
{"month", MONTH_P},
{"move", MOVE},
{"name", NAME_P},
{"names", NAMES},
{"national", NATIONAL},
{"natural", NATURAL},
{"nchar", NCHAR},
{"new", NEW},
{"next", NEXT},
{"no", NO},
{"nocreatedb", NOCREATEDB},
{"nocreaterole", NOCREATEROLE},
{"nocreateuser", NOCREATEUSER},
{"noinherit", NOINHERIT},
{"nologin", NOLOGIN_P},
{"none", NONE},
{"nosuperuser", NOSUPERUSER},
{"not", NOT},
{"nothing", NOTHING},
{"notify", NOTIFY},
{"notnull", NOTNULL},
{"nowait", NOWAIT},
{"null", NULL_P},
{"nullif", NULLIF},
{"nulls", NULLS_P},
{"numeric", NUMERIC},
{"object", OBJECT_P},
{"of", OF},
{"off", OFF},
{"offset", OFFSET},
{"oids", OIDS},
{"old", OLD},
{"on", ON},
{"only", ONLY},
{"operator", OPERATOR},
{"option", OPTION},
{"or", OR},
{"order", ORDER},
{"out", OUT_P},
{"outer", OUTER_P},
{"overlaps", OVERLAPS},
{"overlay", OVERLAY},
{"owned", OWNED},
{"owner", OWNER},
{"partial", PARTIAL},
{"password", PASSWORD},
{"placing", PLACING},
{"position", POSITION},
{"precision", PRECISION},
{"prepare", PREPARE},
{"prepared", PREPARED},
{"preserve", PRESERVE},
{"primary", PRIMARY},
{"prior", PRIOR},
{"privileges", PRIVILEGES},
{"procedural", PROCEDURAL},
{"procedure", PROCEDURE},
{"quote", QUOTE},
{"read", READ},
{"real", REAL},
{"reassign", REASSIGN},
{"recheck", RECHECK},
{"references", REFERENCES},
{"reindex", REINDEX},
{"relative", RELATIVE_P},
{"release", RELEASE},
{"rename", RENAME},
{"repeatable", REPEATABLE},
{"replace", REPLACE},
{"reset", RESET},
{"restart", RESTART},
{"restrict", RESTRICT},
{"returning", RETURNING},
{"returns", RETURNS},
{"revoke", REVOKE},
{"right", RIGHT},
{"role", ROLE},
{"rollback", ROLLBACK},
{"row", ROW},
{"rows", ROWS},
{"rule", RULE},
{"savepoint", SAVEPOINT},
{"schema", SCHEMA},
{"scroll", SCROLL},
{"second", SECOND_P},
{"security", SECURITY},
{"select", SELECT},
{"sequence", SEQUENCE},
{"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},
{"standalone", STANDALONE_P},
{"start", START},
{"statement", STATEMENT},
{"statistics", STATISTICS},
{"stdin", STDIN},
{"stdout", STDOUT},
{"storage", STORAGE},
{"strict", STRICT_P},
{"strip", STRIP_P},
{"substring", SUBSTRING},
{"superuser", SUPERUSER_P},
{"symmetric", SYMMETRIC},
{"sysid", SYSID},
{"system", SYSTEM_P},
{"table", TABLE},
{"tablespace", TABLESPACE},
{"temp", TEMP},
{"template", TEMPLATE},
{"temporary", TEMPORARY},
{"then", THEN},
{"time", TIME},
{"timestamp", TIMESTAMP},
{"to", TO},
{"trailing", TRAILING},
{"transaction", TRANSACTION},
{"treat", TREAT},
{"trigger", TRIGGER},
{"trim", TRIM},
{"true", TRUE_P},
{"truncate", TRUNCATE},
{"trusted", TRUSTED},
{"type", TYPE_P},
{"uncommitted", UNCOMMITTED},
{"unencrypted", UNENCRYPTED},
{"union", UNION},
{"unique", UNIQUE},
{"unknown", UNKNOWN},
{"unlisten", UNLISTEN},
{"until", UNTIL},
{"update", UPDATE},
{"user", USER},
{"using", USING},
{"vacuum", VACUUM},
{"valid", VALID},
{"validator", VALIDATOR},
{"value", VALUE_P},
{"values", VALUES},
{"varchar", VARCHAR},
{"varying", VARYING},
{"verbose", VERBOSE},
{"version", VERSION_P},
{"view", VIEW},
{"volatile", VOLATILE},
{"when", WHEN},
{"where", WHERE},
{"whitespace", WHITESPACE_P},
{"with", WITH},
{"without", WITHOUT},
{"work", WORK},
{"write", WRITE},
{"xmlattributes", XMLATTRIBUTES},
{"xmlconcat", XMLCONCAT},
{"xmlelement", XMLELEMENT},
{"xmlforest", XMLFOREST},
{"xmlparse", XMLPARSE},
{"xmlpi", XMLPI},
{"xmlroot", XMLROOT},
{"xmlserialize", XMLSERIALIZE},
{"year", YEAR_P},
{"yes", YES_P},
{"zone", ZONE},
};
/*
* 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)
{
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).
*/
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 = &ScanKeywords[0];
high = endof(ScanKeywords) - 1;
while (low <= high)
{
const ScanKeyword *middle;
int difference;
middle = low + (high - low) / 2;
difference = strcmp(middle->name, word);
if (difference == 0)
return middle;
else if (difference < 0)
low = middle + 1;
else
high = middle - 1;
}
return NULL;
}