|
4 | 4 | * lexical token lookup for reserved words in postgres embedded SQL
|
5 | 5 | *
|
6 | 6 | * IDENTIFICATION
|
7 |
| - * $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/ecpg_keywords.c,v 1.37 2007/11/15 21:14:45 momjian Exp $ |
| 7 | + * $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/ecpg_keywords.c,v 1.38 2008/05/20 23:17:32 meskes Exp $ |
8 | 8 | *
|
9 | 9 | *-------------------------------------------------------------------------
|
10 | 10 | */
|
11 | 11 |
|
| 12 | +#include "postgres_fe.h" |
| 13 | + |
| 14 | +#include <ctype.h> |
| 15 | + |
| 16 | +#include "extern.h" |
| 17 | +#include "preproc.h" |
| 18 | + |
12 | 19 | /*
|
13 | 20 | * List of (keyword-name, keyword-token-value) pairs.
|
14 | 21 | *
|
15 | 22 | * !!WARNING!!: This list must be sorted, because binary
|
16 | 23 | * search is used to locate entries.
|
17 | 24 | */
|
18 | 25 | static const ScanKeyword ScanECPGKeywords[] = {
|
19 |
| - /* name value */ |
20 |
| - {"allocate", SQL_ALLOCATE}, |
21 |
| - {"autocommit", SQL_AUTOCOMMIT}, |
22 |
| - {"bool", SQL_BOOL}, |
23 |
| - {"break", SQL_BREAK}, |
24 |
| - {"call", SQL_CALL}, |
25 |
| - {"cardinality", SQL_CARDINALITY}, |
26 |
| - {"connect", SQL_CONNECT}, |
27 |
| - {"continue", SQL_CONTINUE}, |
28 |
| - {"count", SQL_COUNT}, |
29 |
| - {"data", SQL_DATA}, |
30 |
| - {"datetime_interval_code", SQL_DATETIME_INTERVAL_CODE}, |
31 |
| - {"datetime_interval_precision", SQL_DATETIME_INTERVAL_PRECISION}, |
32 |
| - {"describe", SQL_DESCRIBE}, |
33 |
| - {"descriptor", SQL_DESCRIPTOR}, |
34 |
| - {"disconnect", SQL_DISCONNECT}, |
35 |
| - {"found", SQL_FOUND}, |
36 |
| - {"free", SQL_FREE}, |
37 |
| - {"go", SQL_GO}, |
38 |
| - {"goto", SQL_GOTO}, |
39 |
| - {"identified", SQL_IDENTIFIED}, |
40 |
| - {"indicator", SQL_INDICATOR}, |
41 |
| - {"key_member", SQL_KEY_MEMBER}, |
42 |
| - {"length", SQL_LENGTH}, |
43 |
| - {"long", SQL_LONG}, |
44 |
| - {"nullable", SQL_NULLABLE}, |
45 |
| - {"octet_length", SQL_OCTET_LENGTH}, |
46 |
| - {"open", SQL_OPEN}, |
47 |
| - {"output", SQL_OUTPUT}, |
48 |
| - {"reference", SQL_REFERENCE}, |
49 |
| - {"returned_length", SQL_RETURNED_LENGTH}, |
50 |
| - {"returned_octet_length", SQL_RETURNED_OCTET_LENGTH}, |
51 |
| - {"scale", SQL_SCALE}, |
52 |
| - {"section", SQL_SECTION}, |
53 |
| - {"short", SQL_SHORT}, |
54 |
| - {"signed", SQL_SIGNED}, |
55 |
| - {"sql", SQL_SQL}, /* strange thing, used for into sql descriptor |
| 26 | + /* name, value, category */ |
| 27 | + /* category is not needed in ecpg, it is only here so we can share |
| 28 | + * the data structure with the backend */ |
| 29 | + {"allocate", SQL_ALLOCATE, 0}, |
| 30 | + {"autocommit", SQL_AUTOCOMMIT, 0}, |
| 31 | + {"bool", SQL_BOOL, 0}, |
| 32 | + {"break", SQL_BREAK, 0}, |
| 33 | + {"call", SQL_CALL, 0}, |
| 34 | + {"cardinality", SQL_CARDINALITY, 0}, |
| 35 | + {"connect", SQL_CONNECT, 0}, |
| 36 | + {"count", SQL_COUNT, 0}, |
| 37 | + {"data", SQL_DATA, 0}, |
| 38 | + {"datetime_interval_code", SQL_DATETIME_INTERVAL_CODE, 0}, |
| 39 | + {"datetime_interval_precision", SQL_DATETIME_INTERVAL_PRECISION, 0}, |
| 40 | + {"describe", SQL_DESCRIBE, 0}, |
| 41 | + {"descriptor", SQL_DESCRIPTOR, 0}, |
| 42 | + {"disconnect", SQL_DISCONNECT, 0}, |
| 43 | + {"found", SQL_FOUND, 0}, |
| 44 | + {"free", SQL_FREE, 0}, |
| 45 | + {"get", SQL_GET, 0}, |
| 46 | + {"go", SQL_GO, 0}, |
| 47 | + {"goto", SQL_GOTO, 0}, |
| 48 | + {"identified", SQL_IDENTIFIED, 0}, |
| 49 | + {"indicator", SQL_INDICATOR, 0}, |
| 50 | + {"key_member", SQL_KEY_MEMBER, 0}, |
| 51 | + {"length", SQL_LENGTH, 0}, |
| 52 | + {"long", SQL_LONG, 0}, |
| 53 | + {"nullable", SQL_NULLABLE, 0}, |
| 54 | + {"octet_length", SQL_OCTET_LENGTH, 0}, |
| 55 | + {"open", SQL_OPEN, 0}, |
| 56 | + {"output", SQL_OUTPUT, 0}, |
| 57 | + {"reference", SQL_REFERENCE, 0}, |
| 58 | + {"returned_length", SQL_RETURNED_LENGTH, 0}, |
| 59 | + {"returned_octet_length", SQL_RETURNED_OCTET_LENGTH, 0}, |
| 60 | + {"scale", SQL_SCALE, 0}, |
| 61 | + {"section", SQL_SECTION, 0}, |
| 62 | + {"short", SQL_SHORT, 0}, |
| 63 | + {"signed", SQL_SIGNED, 0}, |
| 64 | + {"sql", SQL_SQL, 0}, /* strange thing, used for into sql descriptor |
56 | 65 | * MYDESC; */
|
57 |
| - {"sqlerror", SQL_SQLERROR}, |
58 |
| - {"sqlprint", SQL_SQLPRINT}, |
59 |
| - {"sqlwarning", SQL_SQLWARNING}, |
60 |
| - {"stop", SQL_STOP}, |
61 |
| - {"struct", SQL_STRUCT}, |
62 |
| - {"unsigned", SQL_UNSIGNED}, |
63 |
| - {"var", SQL_VAR}, |
64 |
| - {"whenever", SQL_WHENEVER}, |
| 66 | + {"sqlerror", SQL_SQLERROR, 0}, |
| 67 | + {"sqlprint", SQL_SQLPRINT, 0}, |
| 68 | + {"sqlwarning", SQL_SQLWARNING, 0}, |
| 69 | + {"stop", SQL_STOP, 0}, |
| 70 | + {"struct", SQL_STRUCT, 0}, |
| 71 | + {"unsigned", SQL_UNSIGNED, 0}, |
| 72 | + {"var", SQL_VAR, 0}, |
| 73 | + {"whenever", SQL_WHENEVER, 0}, |
65 | 74 | };
|
| 75 | + |
| 76 | +/* This is all taken from src/backend/parser/keyword.c and adjusted for our needs. */ |
| 77 | +/* |
| 78 | + * Do a binary search using plain strcmp() comparison. |
| 79 | + */ |
| 80 | +const ScanKeyword * |
| 81 | +DoLookup(const char *word, const ScanKeyword *low, const ScanKeyword *high) |
| 82 | +{ |
| 83 | + while (low <= high) |
| 84 | + { |
| 85 | + const ScanKeyword *middle; |
| 86 | + int difference; |
| 87 | + |
| 88 | + middle = low + (high - low) / 2; |
| 89 | + difference = strcmp(middle->name, word); |
| 90 | + if (difference == 0) |
| 91 | + return middle; |
| 92 | + else if (difference < 0) |
| 93 | + low = middle + 1; |
| 94 | + else |
| 95 | + high = middle - 1; |
| 96 | + } |
| 97 | + |
| 98 | + return NULL; |
| 99 | +} |
| 100 | + |
| 101 | +/* |
| 102 | + * ScanECPGKeywordLookup - see if a given word is a keyword |
| 103 | + * |
| 104 | + * Returns a pointer to the ScanKeyword table entry, or NULL if no match. |
| 105 | + * |
| 106 | + * The match is done case-insensitively. Note that we deliberately use a |
| 107 | + * dumbed-down case conversion that will only translate 'A'-'Z' into 'a'-'z', |
| 108 | + * even if we are in a locale where tolower() would produce more or different |
| 109 | + * translations. This is to conform to the SQL99 spec, which says that |
| 110 | + * keywords are to be matched in this way even though non-keyword identifiers |
| 111 | + * receive a different case-normalization mapping. |
| 112 | + */ |
| 113 | +const ScanKeyword * |
| 114 | +ScanECPGKeywordLookup(const char *text) |
| 115 | +{ |
| 116 | + int len, |
| 117 | + i; |
| 118 | + char word[NAMEDATALEN]; |
| 119 | + const ScanKeyword *res; |
| 120 | + |
| 121 | + /* First check SQL symbols defined by the backend. */ |
| 122 | + |
| 123 | + res = ScanKeywordLookup(text); |
| 124 | + if (res) |
| 125 | + return res; |
| 126 | + |
| 127 | + len = strlen(text); |
| 128 | + /* We assume all keywords are shorter than NAMEDATALEN. */ |
| 129 | + if (len >= NAMEDATALEN) |
| 130 | + return NULL; |
| 131 | + |
| 132 | + /* |
| 133 | + * Apply an ASCII-only downcasing. We must not use tolower() since it may |
| 134 | + * produce the wrong translation in some locales (eg, Turkish). |
| 135 | + */ |
| 136 | + for (i = 0; i < len; i++) |
| 137 | + { |
| 138 | + char ch = text[i]; |
| 139 | + |
| 140 | + if (ch >= 'A' && ch <= 'Z') |
| 141 | + ch += 'a' - 'A'; |
| 142 | + word[i] = ch; |
| 143 | + } |
| 144 | + word[len] = '\0'; |
| 145 | + |
| 146 | + /* |
| 147 | + * Now do a binary search using plain strcmp() comparison. |
| 148 | + */ |
| 149 | + |
| 150 | + return DoLookup(word, &ScanECPGKeywords[0], endof(ScanECPGKeywords) - 1); |
| 151 | +} |
0 commit comments