aboutsummaryrefslogtreecommitdiff
path: root/src/interfaces/ecpg/preproc/ecpg_keywords.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/interfaces/ecpg/preproc/ecpg_keywords.c')
-rw-r--r--src/interfaces/ecpg/preproc/ecpg_keywords.c178
1 files changed, 132 insertions, 46 deletions
diff --git a/src/interfaces/ecpg/preproc/ecpg_keywords.c b/src/interfaces/ecpg/preproc/ecpg_keywords.c
index 98179fe39fc..3d8db405379 100644
--- a/src/interfaces/ecpg/preproc/ecpg_keywords.c
+++ b/src/interfaces/ecpg/preproc/ecpg_keywords.c
@@ -4,11 +4,18 @@
* lexical token lookup for reserved words in postgres embedded SQL
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/ecpg_keywords.c,v 1.37 2007/11/15 21:14:45 momjian Exp $
+ * $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/ecpg_keywords.c,v 1.38 2008/05/20 23:17:32 meskes Exp $
*
*-------------------------------------------------------------------------
*/
+#include "postgres_fe.h"
+
+#include <ctype.h>
+
+#include "extern.h"
+#include "preproc.h"
+
/*
* List of (keyword-name, keyword-token-value) pairs.
*
@@ -16,50 +23,129 @@
* search is used to locate entries.
*/
static const ScanKeyword ScanECPGKeywords[] = {
- /* name value */
- {"allocate", SQL_ALLOCATE},
- {"autocommit", SQL_AUTOCOMMIT},
- {"bool", SQL_BOOL},
- {"break", SQL_BREAK},
- {"call", SQL_CALL},
- {"cardinality", SQL_CARDINALITY},
- {"connect", SQL_CONNECT},
- {"continue", SQL_CONTINUE},
- {"count", SQL_COUNT},
- {"data", SQL_DATA},
- {"datetime_interval_code", SQL_DATETIME_INTERVAL_CODE},
- {"datetime_interval_precision", SQL_DATETIME_INTERVAL_PRECISION},
- {"describe", SQL_DESCRIBE},
- {"descriptor", SQL_DESCRIPTOR},
- {"disconnect", SQL_DISCONNECT},
- {"found", SQL_FOUND},
- {"free", SQL_FREE},
- {"go", SQL_GO},
- {"goto", SQL_GOTO},
- {"identified", SQL_IDENTIFIED},
- {"indicator", SQL_INDICATOR},
- {"key_member", SQL_KEY_MEMBER},
- {"length", SQL_LENGTH},
- {"long", SQL_LONG},
- {"nullable", SQL_NULLABLE},
- {"octet_length", SQL_OCTET_LENGTH},
- {"open", SQL_OPEN},
- {"output", SQL_OUTPUT},
- {"reference", SQL_REFERENCE},
- {"returned_length", SQL_RETURNED_LENGTH},
- {"returned_octet_length", SQL_RETURNED_OCTET_LENGTH},
- {"scale", SQL_SCALE},
- {"section", SQL_SECTION},
- {"short", SQL_SHORT},
- {"signed", SQL_SIGNED},
- {"sql", SQL_SQL}, /* strange thing, used for into sql descriptor
+ /* name, value, category */
+ /* category is not needed in ecpg, it is only here so we can share
+ * the data structure with the backend */
+ {"allocate", SQL_ALLOCATE, 0},
+ {"autocommit", SQL_AUTOCOMMIT, 0},
+ {"bool", SQL_BOOL, 0},
+ {"break", SQL_BREAK, 0},
+ {"call", SQL_CALL, 0},
+ {"cardinality", SQL_CARDINALITY, 0},
+ {"connect", SQL_CONNECT, 0},
+ {"count", SQL_COUNT, 0},
+ {"data", SQL_DATA, 0},
+ {"datetime_interval_code", SQL_DATETIME_INTERVAL_CODE, 0},
+ {"datetime_interval_precision", SQL_DATETIME_INTERVAL_PRECISION, 0},
+ {"describe", SQL_DESCRIBE, 0},
+ {"descriptor", SQL_DESCRIPTOR, 0},
+ {"disconnect", SQL_DISCONNECT, 0},
+ {"found", SQL_FOUND, 0},
+ {"free", SQL_FREE, 0},
+ {"get", SQL_GET, 0},
+ {"go", SQL_GO, 0},
+ {"goto", SQL_GOTO, 0},
+ {"identified", SQL_IDENTIFIED, 0},
+ {"indicator", SQL_INDICATOR, 0},
+ {"key_member", SQL_KEY_MEMBER, 0},
+ {"length", SQL_LENGTH, 0},
+ {"long", SQL_LONG, 0},
+ {"nullable", SQL_NULLABLE, 0},
+ {"octet_length", SQL_OCTET_LENGTH, 0},
+ {"open", SQL_OPEN, 0},
+ {"output", SQL_OUTPUT, 0},
+ {"reference", SQL_REFERENCE, 0},
+ {"returned_length", SQL_RETURNED_LENGTH, 0},
+ {"returned_octet_length", SQL_RETURNED_OCTET_LENGTH, 0},
+ {"scale", SQL_SCALE, 0},
+ {"section", SQL_SECTION, 0},
+ {"short", SQL_SHORT, 0},
+ {"signed", SQL_SIGNED, 0},
+ {"sql", SQL_SQL, 0}, /* strange thing, used for into sql descriptor
* MYDESC; */
- {"sqlerror", SQL_SQLERROR},
- {"sqlprint", SQL_SQLPRINT},
- {"sqlwarning", SQL_SQLWARNING},
- {"stop", SQL_STOP},
- {"struct", SQL_STRUCT},
- {"unsigned", SQL_UNSIGNED},
- {"var", SQL_VAR},
- {"whenever", SQL_WHENEVER},
+ {"sqlerror", SQL_SQLERROR, 0},
+ {"sqlprint", SQL_SQLPRINT, 0},
+ {"sqlwarning", SQL_SQLWARNING, 0},
+ {"stop", SQL_STOP, 0},
+ {"struct", SQL_STRUCT, 0},
+ {"unsigned", SQL_UNSIGNED, 0},
+ {"var", SQL_VAR, 0},
+ {"whenever", SQL_WHENEVER, 0},
};
+
+/* This is all taken from src/backend/parser/keyword.c and adjusted for our needs. */
+/*
+ * Do a binary search using plain strcmp() comparison.
+ */
+const ScanKeyword *
+DoLookup(const char *word, const ScanKeyword *low, const ScanKeyword *high)
+{
+ 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;
+}
+
+/*
+ * ScanECPGKeywordLookup - 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 *
+ScanECPGKeywordLookup(const char *text)
+{
+ int len,
+ i;
+ char word[NAMEDATALEN];
+ const ScanKeyword *res;
+
+ /* First check SQL symbols defined by the backend. */
+
+ res = ScanKeywordLookup(text);
+ if (res)
+ return res;
+
+ 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.
+ */
+
+ return DoLookup(word, &ScanECPGKeywords[0], endof(ScanECPGKeywords) - 1);
+}