/* * ScanECPGKeywordLookup - see if a given word is a keyword * * Returns a pointer to the ScanKeyword table entry, or NULL if no match. * Keywords are matched using the same case-folding rules as in the backend. */ const ScanKeyword * ScanECPGKeywordLookup(const char *text) { const ScanKeyword *res; /* First check SQL symbols defined by the backend. */ res = ScanKeywordLookup(text, SQLScanKeywords, NumSQLScanKeywords); if (res) return res; /* Try ECPG-specific keywords. */ res = ScanKeywordLookup(text, ECPGScanKeywords, lengthof(ECPGScanKeywords)); if (res) return res; return NULL; }
/* check if ident is keyword that needs quoting */ static bool is_keyword(const char *ident) { const ScanKeyword *kw; /* do the lookup */ #if PG_VERSION_NUM >= 80500 kw = ScanKeywordLookup(ident, ScanKeywords, NumScanKeywords); #else kw = ScanKeywordLookup(ident); #endif /* unreserved? */ #if PG_VERSION_NUM >= 80300 if (kw && kw->category == UNRESERVED_KEYWORD) return false; #endif /* found anything? */ return kw != NULL; }
static bool needsQuoting(wxString &value, bool forTypes) { // Is it a number? if (value.IsNumber()) return true; else { // certain types should not be quoted even though it contains a space. Evilness. wxString valNoArray; if (forTypes && value.Right(2) == wxT("[]")) valNoArray = value.Mid(0, value.Len() - 2); else valNoArray = value; if (forTypes && (!valNoArray.CmpNoCase(wxT("character varying")) || !valNoArray.CmpNoCase(wxT("\"char\"")) || !valNoArray.CmpNoCase(wxT("bit varying")) || !valNoArray.CmpNoCase(wxT("double precision")) || !valNoArray.CmpNoCase(wxT("timestamp without time zone")) || !valNoArray.CmpNoCase(wxT("timestamp with time zone")) || !valNoArray.CmpNoCase(wxT("time without time zone")) || !valNoArray.CmpNoCase(wxT("time with time zone")) || !valNoArray.CmpNoCase(wxT("\"trigger\"")) || !valNoArray.CmpNoCase(wxT("\"unknown\"")))) return false; int pos = 0; while (pos < (int)valNoArray.length()) { wxChar c = valNoArray.GetChar(pos); if (!((pos > 0) && (c >= '0' && c <= '9')) && !(c >= 'a' && c <= 'z') && !(c == '_')) { return true; } pos++; } } // is it a keyword? const ScanKeyword *sk = ScanKeywordLookup(value.ToAscii()); if (!sk) return false; if (sk->category == UNRESERVED_KEYWORD) return false; if (forTypes && sk->category == COL_NAME_KEYWORD) return false; return true; }
/* * This is the yylex routine called from the PL/pgSQL grammar. * It is a wrapper around the core lexer, with the ability to recognize * PL/pgSQL variables and return them as special T_DATUM tokens. If a * word or compound word does not match any variable name, or if matching * is turned off by plpgsql_IdentifierLookup, it is returned as * T_WORD or T_CWORD respectively, or as an unreserved keyword if it * matches one of those. */ int plpgsql_yylex(void) { int tok1; TokenAuxData aux1; const ScanKeyword *kw; tok1 = internal_yylex(&aux1); if (tok1 == IDENT || tok1 == PARAM) { int tok2; TokenAuxData aux2; tok2 = internal_yylex(&aux2); if (tok2 == '.') { int tok3; TokenAuxData aux3; tok3 = internal_yylex(&aux3); if (tok3 == IDENT) { int tok4; TokenAuxData aux4; tok4 = internal_yylex(&aux4); if (tok4 == '.') { int tok5; TokenAuxData aux5; tok5 = internal_yylex(&aux5); if (tok5 == IDENT) { if (plpgsql_parse_tripword(aux1.lval.str, aux3.lval.str, aux5.lval.str, &aux1.lval.wdatum, &aux1.lval.cword)) tok1 = T_DATUM; else tok1 = T_CWORD; } else { /* not A.B.C, so just process A.B */ push_back_token(tok5, &aux5); push_back_token(tok4, &aux4); if (plpgsql_parse_dblword(aux1.lval.str, aux3.lval.str, &aux1.lval.wdatum, &aux1.lval.cword)) tok1 = T_DATUM; else tok1 = T_CWORD; } } else { /* not A.B.C, so just process A.B */ push_back_token(tok4, &aux4); if (plpgsql_parse_dblword(aux1.lval.str, aux3.lval.str, &aux1.lval.wdatum, &aux1.lval.cword)) tok1 = T_DATUM; else tok1 = T_CWORD; } } else { /* not A.B, so just process A */ push_back_token(tok3, &aux3); push_back_token(tok2, &aux2); if (plpgsql_parse_word(aux1.lval.str, core_yy.scanbuf + aux1.lloc, &aux1.lval.wdatum, &aux1.lval.word)) tok1 = T_DATUM; else if (!aux1.lval.word.quoted && (kw = ScanKeywordLookup(aux1.lval.word.ident, unreserved_keywords, num_unreserved_keywords))) { aux1.lval.keyword = kw->name; tok1 = kw->value; } else tok1 = T_WORD; } } else { /* not A.B, so just process A */ push_back_token(tok2, &aux2); /* * If we are at start of statement, prefer unreserved keywords * over variable names, unless the next token is assignment or * '[', in which case prefer variable names. (Note we need not * consider '.' as the next token; that case was handled above, * and we always prefer variable names in that case.) If we are * not at start of statement, always prefer variable names over * unreserved keywords. */ if (AT_STMT_START(plpgsql_yytoken) && !(tok2 == '=' || tok2 == COLON_EQUALS || tok2 == '[')) { /* try for unreserved keyword, then for variable name */ if (core_yy.scanbuf[aux1.lloc] != '"' && (kw = ScanKeywordLookup(aux1.lval.str, unreserved_keywords, num_unreserved_keywords))) { aux1.lval.keyword = kw->name; tok1 = kw->value; } else if (plpgsql_parse_word(aux1.lval.str, core_yy.scanbuf + aux1.lloc, &aux1.lval.wdatum, &aux1.lval.word)) tok1 = T_DATUM; else tok1 = T_WORD; } else { /* try for variable name, then for unreserved keyword */ if (plpgsql_parse_word(aux1.lval.str, core_yy.scanbuf + aux1.lloc, &aux1.lval.wdatum, &aux1.lval.word)) tok1 = T_DATUM; else if (!aux1.lval.word.quoted && (kw = ScanKeywordLookup(aux1.lval.word.ident, unreserved_keywords, num_unreserved_keywords))) { aux1.lval.keyword = kw->name; tok1 = kw->value; } else tok1 = T_WORD; } } } else { /* * Not a potential plpgsql variable name, just return the data. * * Note that we also come through here if the grammar pushed back a * T_DATUM, T_CWORD, T_WORD, or unreserved-keyword token returned by a * previous lookup cycle; thus, pushbacks do not incur extra lookup * work, since we'll never do the above code twice for the same token. * This property also makes it safe to rely on the old value of * plpgsql_yytoken in the is-this-start-of-statement test above. */ } plpgsql_yylval = aux1.lval; plpgsql_yylloc = aux1.lloc; plpgsql_yyleng = aux1.leng; plpgsql_yytoken = tok1; return tok1; }
/* * Quotes input string if it's not a legitimate SQL identifier as-is. * * Note that the returned string must be used before calling fmtId again, * since we re-use the same return buffer each time. Non-reentrant but * reduces memory leakage. (On Windows the memory leakage will be one buffer * per thread, which is at least better than one per call). */ const char * fmtId(const char *rawid) { /* * The Tls code goes awry if we use a static var, so we provide for both * static and auto, and omit any use of the static var when using Tls. */ static PQExpBuffer s_id_return = NULL; PQExpBuffer id_return; const char *cp; bool need_quotes = false; #ifdef WIN32 if (parallel_init_done) id_return = (PQExpBuffer) TlsGetValue(tls_index); /* 0 when not set */ else id_return = s_id_return; #else id_return = s_id_return; #endif if (id_return) /* first time through? */ { /* same buffer, just wipe contents */ resetPQExpBuffer(id_return); } else { /* new buffer */ id_return = createPQExpBuffer(); #ifdef WIN32 if (parallel_init_done) TlsSetValue(tls_index, id_return); else s_id_return = id_return; #else s_id_return = id_return; #endif } /* * These checks need to match the identifier production in scan.l. Don't * use islower() etc. */ if (quote_all_identifiers) need_quotes = true; /* slightly different rules for first character */ else if (!((rawid[0] >= 'a' && rawid[0] <= 'z') || rawid[0] == '_')) need_quotes = true; else { /* otherwise check the entire string */ for (cp = rawid; *cp; cp++) { if (!((*cp >= 'a' && *cp <= 'z') || (*cp >= '0' && *cp <= '9') || (*cp == '_'))) { need_quotes = true; break; } } } if (!need_quotes) { /* * Check for keyword. We quote keywords except for unreserved ones. * (In some cases we could avoid quoting a col_name or type_func_name * keyword, but it seems much harder than it's worth to tell that.) * * Note: ScanKeywordLookup() does case-insensitive comparison, but * that's fine, since we already know we have all-lower-case. */ const ScanKeyword *keyword = ScanKeywordLookup(rawid, ScanKeywords, NumScanKeywords); if (keyword != NULL && keyword->category != UNRESERVED_KEYWORD) need_quotes = true; } if (!need_quotes) { /* no quoting needed */ appendPQExpBufferStr(id_return, rawid); } else { appendPQExpBufferChar(id_return, '\"'); for (cp = rawid; *cp; cp++) { /* * Did we find a double-quote in the string? Then make this a * double double-quote per SQL99. Before, we put in a * backslash/double-quote pair. - thomas 2000-08-05 */ if (*cp == '\"') appendPQExpBufferChar(id_return, '\"'); appendPQExpBufferChar(id_return, *cp); } appendPQExpBufferChar(id_return, '\"'); } return id_return->data; }
/* * Quotes input string if it's not a legitimate SQL identifier as-is. * * Note that the returned string must be used before calling fmtId again, * since we re-use the same return buffer each time. */ const char * fmtId(const char *rawid) { PQExpBuffer id_return = getLocalPQExpBuffer(); const char *cp; bool need_quotes = false; /* * These checks need to match the identifier production in scan.l. Don't * use islower() etc. */ if (quote_all_identifiers) need_quotes = true; /* slightly different rules for first character */ else if (!((rawid[0] >= 'a' && rawid[0] <= 'z') || rawid[0] == '_')) need_quotes = true; else { /* otherwise check the entire string */ for (cp = rawid; *cp; cp++) { if (!((*cp >= 'a' && *cp <= 'z') || (*cp >= '0' && *cp <= '9') || (*cp == '_'))) { need_quotes = true; break; } } } if (!need_quotes) { /* * Check for keyword. We quote keywords except for unreserved ones. * (In some cases we could avoid quoting a col_name or type_func_name * keyword, but it seems much harder than it's worth to tell that.) * * Note: ScanKeywordLookup() does case-insensitive comparison, but * that's fine, since we already know we have all-lower-case. */ const ScanKeyword *keyword = ScanKeywordLookup(rawid, FEScanKeywords, NumFEScanKeywords); if (keyword != NULL && keyword->category != UNRESERVED_KEYWORD) need_quotes = true; } if (!need_quotes) { /* no quoting needed */ appendPQExpBufferStr(id_return, rawid); } else { appendPQExpBufferChar(id_return, '\"'); for (cp = rawid; *cp; cp++) { /* * Did we find a double-quote in the string? Then make this a * double double-quote per SQL99. Before, we put in a * backslash/double-quote pair. - thomas 2000-08-05 */ if (*cp == '\"') appendPQExpBufferChar(id_return, '\"'); appendPQExpBufferChar(id_return, *cp); } appendPQExpBufferChar(id_return, '\"'); } return id_return->data; }
/* * slon_quote_identifier - Quote an identifier only if needed */ static int pgq_quote_ident(char *dst, const uint8 *src, int srclen) { /* * Can avoid quoting if ident starts with a lowercase letter or * underscore and contains only lowercase letters, digits, and * underscores, *and* is not any SQL keyword. Otherwise, supply * quotes. */ int nquotes = 0; bool safe; const char *ptr; char *optr; char ident[NAMEDATALEN + 1]; /* expect idents be not bigger than NAMEDATALEN */ if (srclen > NAMEDATALEN) srclen = NAMEDATALEN; memcpy(ident, src, srclen); ident[srclen] = 0; /* * would like to use <ctype.h> macros here, but they might yield * unwanted locale-specific results... */ safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_'); for (ptr = ident; *ptr; ptr++) { char ch = *ptr; if ((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || (ch == '_')) continue; /* okay */ safe = false; if (ch == '"') nquotes++; } if (safe) { /* * Check for keyword. This test is overly strong, since many of * the "keywords" known to the parser are usable as column names, * but the parser doesn't provide any easy way to test for whether * an identifier is safe or not... so be safe not sorry. * * Note: ScanKeywordLookup() does case-insensitive comparison, but * that's fine, since we already know we have all-lower-case. */ if (ScanKeywordLookup(ident) != NULL) safe = false; } optr = dst; if (!safe) *optr++ = '"'; for (ptr = ident; *ptr; ptr++) { char ch = *ptr; if (ch == '"') *optr++ = '"'; *optr++ = ch; } if (!safe) *optr++ = '"'; return optr - dst; }
/* * Quotes input string if it's not a legitimate SQL identifier as-is. * * Note that the returned string must be used before calling fmtId again, * since we re-use the same return buffer each time. Non-reentrant but * avoids memory leakage. */ const char * fmtId(const char *rawid) { static PQExpBuffer id_return = NULL; const char *cp; bool need_quotes = false; if (id_return) /* first time through? */ resetPQExpBuffer(id_return); else id_return = createPQExpBuffer(); /* * These checks need to match the identifier production in scan.l. Don't * use islower() etc. */ if (ScanKeywordLookup(rawid)) need_quotes = true; /* slightly different rules for first character */ else if (!((rawid[0] >= 'a' && rawid[0] <= 'z') || rawid[0] == '_')) need_quotes = true; else { /* otherwise check the entire string */ for (cp = rawid; *cp; cp++) { if (!((*cp >= 'a' && *cp <= 'z') || (*cp >= '0' && *cp <= '9') || (*cp == '_'))) { need_quotes = true; break; } } } if (!need_quotes) { /* no quoting needed */ appendPQExpBufferStr(id_return, rawid); } else { appendPQExpBufferChar(id_return, '\"'); for (cp = rawid; *cp; cp++) { /* * Did we find a double-quote in the string? Then make this a * double double-quote per SQL99. Before, we put in a * backslash/double-quote pair. - thomas 2000-08-05 */ if (*cp == '\"') appendPQExpBufferChar(id_return, '\"'); appendPQExpBufferChar(id_return, *cp); } appendPQExpBufferChar(id_return, '\"'); } return id_return->data; }
/* * This is the yylex routine called from the PL/pgSQL grammar. * It is a wrapper around the core lexer, with the ability to recognize * PL/pgSQL variables and return them as special T_DATUM tokens. If a * word or compound word does not match any variable name, or if matching * is turned off by plpgsql_IdentifierLookup, it is returned as * T_WORD or T_CWORD respectively, or as an unreserved keyword if it * matches one of those. */ int plpgsql_yylex(void) { int tok1; TokenAuxData aux1; const ScanKeyword *kw; tok1 = internal_yylex(&aux1); if (tok1 == IDENT || tok1 == PARAM) { int tok2; TokenAuxData aux2; tok2 = internal_yylex(&aux2); if (tok2 == '.') { int tok3; TokenAuxData aux3; tok3 = internal_yylex(&aux3); if (tok3 == IDENT) { int tok4; TokenAuxData aux4; tok4 = internal_yylex(&aux4); if (tok4 == '.') { int tok5; TokenAuxData aux5; tok5 = internal_yylex(&aux5); if (tok5 == IDENT) { if (plpgsql_parse_tripword(aux1.lval.str, aux3.lval.str, aux5.lval.str, &aux1.lval.wdatum, &aux1.lval.cword)) tok1 = T_DATUM; else tok1 = T_CWORD; } else { /* not A.B.C, so just process A.B */ push_back_token(tok5, &aux5); push_back_token(tok4, &aux4); if (plpgsql_parse_dblword(aux1.lval.str, aux3.lval.str, &aux1.lval.wdatum, &aux1.lval.cword)) tok1 = T_DATUM; else tok1 = T_CWORD; } } else { /* not A.B.C, so just process A.B */ push_back_token(tok4, &aux4); if (plpgsql_parse_dblword(aux1.lval.str, aux3.lval.str, &aux1.lval.wdatum, &aux1.lval.cword)) tok1 = T_DATUM; else tok1 = T_CWORD; } } else { /* not A.B, so just process A */ push_back_token(tok3, &aux3); push_back_token(tok2, &aux2); if (plpgsql_parse_word(aux1.lval.str, core_yy.scanbuf + aux1.lloc, &aux1.lval.wdatum, &aux1.lval.word)) tok1 = T_DATUM; else if (!aux1.lval.word.quoted && (kw = ScanKeywordLookup(aux1.lval.word.ident, unreserved_keywords, num_unreserved_keywords))) { aux1.lval.keyword = kw->name; tok1 = kw->value; } else tok1 = T_WORD; } } else { /* not A.B, so just process A */ push_back_token(tok2, &aux2); if (plpgsql_parse_word(aux1.lval.str, core_yy.scanbuf + aux1.lloc, &aux1.lval.wdatum, &aux1.lval.word)) tok1 = T_DATUM; else if (!aux1.lval.word.quoted && (kw = ScanKeywordLookup(aux1.lval.word.ident, unreserved_keywords, num_unreserved_keywords))) { aux1.lval.keyword = kw->name; tok1 = kw->value; } else tok1 = T_WORD; } } else { /* Not a potential plpgsql variable name, just return the data */ } plpgsql_yylval = aux1.lval; plpgsql_yylloc = aux1.lloc; plpgsql_yyleng = aux1.leng; return tok1; }
/* * Quote an identifier iif necessary. For quoting, consider the SQL rules and * also the list of PostgreSQL keywords. Those keywords come from PostgreSQL * version that pgquarrel was compiled with. Ensure that pgquarrel is compiled * with the latest PostgreSQL version to ensure that we don't have problems * with strings that become keywords. Returns an allocated string. */ char * formatObjectIdentifier(char *s) { bool need_quotes = false; char *p; char *ret; int i = 0; /* different rule for first character */ if (!((s[0] >= 'a' && s[0] <= 'z') || s[0] == '_')) need_quotes = true; else { /* otherwise check the entire string */ for (p = s; *p; p++) { if (!((*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || (*p == '_'))) { need_quotes = true; break; } } } if (!need_quotes) { /* ScanKeywordLookup() was copied from postgres source code */ const ScanKeyword *keyword = ScanKeywordLookup(s, PQLScanKeywords, NumPQLScanKeywords); if (keyword != NULL && keyword->category != UNRESERVED_KEYWORD) need_quotes = true; } if (!need_quotes) { /* no quotes needed */ ret = strdup(s); } else { /* * Maximum length for identifiers is NAMEDATALEN (64 bytes by default * including trailing zero byte). * */ ret = malloc(NAMEDATALEN * sizeof(char)); if (ret == NULL) { logError("could not allocate memory"); exit(EXIT_FAILURE); } ret[i++] = '\"'; for (p = s; *p; p++) { /* per SQL99, if quote is found, add another quote */ if (*p == '\"') ret[i++] = '\"'; ret[i++] = *p; } ret[i++] = '\"'; ret[i] = '\0'; } return ret; }
/* * slon_quote_identifier - Quote an identifier only if needed * * When quotes are needed, we palloc the required space; slightly * space-wasteful but well worth it for notational simplicity. * * Version: pgsql/src/backend/utils/adt/ruleutils.c,v 1.188 2005/01/13 17:19:10 */ static const char * slon_quote_identifier(const char *ident) { /* * Can avoid quoting if ident starts with a lowercase letter or underscore * and contains only lowercase letters, digits, and underscores, *and* is * not any SQL keyword. Otherwise, supply quotes. */ int nquotes = 0; bool safe; const char *ptr; char *result; char *optr; /* * would like to use <ctype.h> macros here, but they might yield unwanted * locale-specific results... */ safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_'); for (ptr = ident; *ptr; ptr++) { char ch = *ptr; if ((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || (ch == '_')) { /* okay */ } else { safe = false; if (ch == '"') nquotes++; } } if (safe) { /* * Check for keyword. This test is overly strong, since many of the * "keywords" known to the parser are usable as column names, but the * parser doesn't provide any easy way to test for whether an * identifier is safe or not... so be safe not sorry. * * Note: ScanKeywordLookup() does case-insensitive comparison, but * that's fine, since we already know we have all-lower-case. */ #ifdef SCANKEYWORDLOOKUP_1 if (ScanKeywordLookup(ident) != NULL) #endif #ifdef SCANKEYWORDLOOKUP_3 if (ScanKeywordLookup(ident,ScanKeywords,NumScanKeywords) != NULL) #endif safe = false; } if (safe) return ident; /* no change needed */ result = (char *) palloc(strlen(ident) + nquotes + 2 + 1); optr = result; *optr++ = '"'; for (ptr = ident; *ptr; ptr++) { char ch = *ptr; if (ch == '"') *optr++ = '"'; *optr++ = ch; } *optr++ = '"'; *optr = '\0'; return result; }