Improve @@identty handling.
authorHiroshi Inoue <h-inoue@dream.email.ne.jp>
Mon, 18 Sep 2017 06:09:35 +0000 (15:09 +0900)
committerHiroshi Inoue <h-inoue@dream.email.ne.jp>
Mon, 18 Sep 2017 07:35:04 +0000 (16:35 +0900)
Use multibyte-aware eatTableIdentifiers() instead of next_name_token().
Use identifierEscape() to escape single quotes or double quotes.

connection.h
convert.c
execute.c
info.c
multibyte.h
parse.c

index 0ccd44219f60b97816d3f2924060cafd1598cfd7..d620b422e610e816490a116d63c718689c71395e 100644 (file)
@@ -450,7 +450,10 @@ int             CC_discard_marked_objects(ConnectionClass *conn);
 
 int        CC_get_max_idlen(ConnectionClass *self);
 char   CC_get_escape(const ConnectionClass *self);
-char *     identifierEscape(const SQLCHAR *src, SQLLEN srclen, const ConnectionClass *conn, char *buf, size_t bufsize);
+char *     identifierEscape(const SQLCHAR *src, SQLLEN srclen, const ConnectionClass *conn, char *buf, size_t bufsize, BOOL double_quote);
+int        findIdentifier(const UCHAR *str, int ccsc, const UCHAR **next_token);
+int        eatTableIdentifiers(const UCHAR *str, int ccsc, pgNAME *table, pgNAME *schema);
+
 
 const      char *CurrCat(const ConnectionClass *self);
 const      char *CurrCatString(const ConnectionClass *self);
index 5444e31dbf345a12823e59ffdcdc6f92e8f5b337..e0efebc9313eac44dd0ba16aec93ba10b2a590f2 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -3189,23 +3189,21 @@ findTag(const char *tag, int ccsc)
    return taglen;
 }
 
-static size_t
-findIdentifier(const char *str, int ccsc, const char **nextdel)
+int
+findIdentifier(const UCHAR *str, int ccsc, const UCHAR **next_token)
 {
-   size_t  slen = 0;
+   int slen = -1;
    encoded_str encstr;
-   unsigned char   tchar;
-   const char  *sptr;
+   UCHAR   tchar;
    BOOL    dquote = FALSE;
 
-   *nextdel = NULL;
-   encoded_str_constr(&encstr, ccsc, str);
-   for (sptr = str; *sptr; sptr++)
+   *next_token = NULL;
+   encoded_str_constr(&encstr, ccsc, (const char *) str);
+   for (tchar = encoded_nextchar(&encstr); tchar; tchar = encoded_nextchar(&encstr))
    {
-       tchar = encoded_nextchar(&encstr);
        if (MBCS_NON_ASCII(encstr))
            continue;
-       if (sptr == str) /* the first character */
+       if (ENCODE_PTR(encstr) == str) /* the first character */
        {
            if (dquote = (IDENTIFIER_QUOTE == tchar), dquote)
                continue;
@@ -3213,7 +3211,7 @@ findIdentifier(const char *str, int ccsc, const char **nextdel)
            {
                slen = 0;
                if (!isspace(tchar))
-                   *nextdel = sptr;
+                   *next_token = ENCODE_PTR(encstr);
                break;
            }
        }
@@ -3221,14 +3219,10 @@ findIdentifier(const char *str, int ccsc, const char **nextdel)
        {
            if (IDENTIFIER_QUOTE == tchar)
            {
-               if (IDENTIFIER_QUOTE == sptr[1])
-               {
-                   encoded_nextchar(&encstr);
-                   sptr++;
+               tchar = encoded_nextchar(&encstr);
+               if (IDENTIFIER_QUOTE == tchar)
                    continue;
-               }
-               slen = sptr - str + 1;
-               sptr++;
+               slen = ENCODE_PTR(encstr) - str;
                break;
            }
        }
@@ -3236,29 +3230,27 @@ findIdentifier(const char *str, int ccsc, const char **nextdel)
        {
            if (isalnum(tchar))
                continue;
-           if (isspace(tchar))
-           {
-               slen = sptr - str;
-               break;
-           }
            switch (tchar)
            {
                case '_':
-               case '$':
+               case DOLLAR_QUOTE:
                    continue;
            }
-           slen = sptr - str;
-           *nextdel = sptr;
+           slen = ENCODE_PTR(encstr) - str;
+           if (!isspace(tchar))
+               *next_token = ENCODE_PTR(encstr);
            break;
        }
    }
-   if (NULL == *nextdel)
+   if (slen < 0 && !dquote)
+       slen = ENCODE_PTR(encstr) - str;
+   if (NULL == *next_token)
    {
-       for (; *sptr; sptr++)
+       for (; tchar; tchar = encoded_nextchar(&encstr))
        {
-           if (!isspace((UCHAR) *sptr))
+           if (!isspace((UCHAR) tchar))
            {
-               *nextdel = sptr;
+               *next_token = ENCODE_PTR(encstr);
                break;
            }
        }
@@ -3266,6 +3258,98 @@ findIdentifier(const char *str, int ccsc, const char **nextdel)
    return slen;
 }
 
+static pgNAME lower_or_remove_dquote(pgNAME nm, const UCHAR *src, int srclen, int ccsc)
+{
+   int i, outlen;
+   char *tc;
+   UCHAR   tchar;
+   BOOL    idQuote;
+   encoded_str encstr;
+
+   if (nm.name)
+       tc = realloc(nm.name, srclen + 1);
+   else
+       tc = malloc(srclen + 1);
+   if (!tc)
+   {
+       NULL_THE_NAME(nm);
+       return nm;
+   }
+   nm.name = tc;
+   idQuote = (src[0] == IDENTIFIER_QUOTE);
+   encoded_str_constr(&encstr, ccsc, (const char *) src);
+   for (i = 0, tchar = encoded_nextchar(&encstr), outlen = 0; i < srclen; i++, tchar = encoded_nextchar(&encstr))
+   {
+       if (MBCS_NON_ASCII(encstr))
+       {
+           tc[outlen++] = tchar;
+           continue;
+       }
+       if (idQuote)
+       {
+           if (IDENTIFIER_QUOTE == tchar)
+           {
+               if (0 == i)
+                   continue;
+               if (i == srclen - 1)
+                   continue;
+               i++;
+               tchar = encoded_nextchar(&encstr);
+           }
+           tc[outlen++] = tchar;
+       }
+       else
+       {
+           tc[outlen++] = tolower(tchar);
+       }
+   }
+   tc[outlen] = '\0';
+   return nm;
+}
+
+int
+eatTableIdentifiers(const UCHAR *str, int ccsc, pgNAME *table, pgNAME *schema)
+{
+   int len;
+   const UCHAR *next_token;
+   const UCHAR *tstr = str;
+
+   while (isspace(*tstr)) tstr++;
+
+   if ((len = findIdentifier(tstr, ccsc, &next_token)) <= 0)
+       return len; /* table name doesn't exist */
+   if (table)
+   {
+       if (IDENTIFIER_QUOTE == *tstr)
+           *table = lower_or_remove_dquote(*table, tstr, len, ccsc);
+       else
+           STRN_TO_NAME(*table, tstr, len);
+   }
+   if (!next_token || '.' != *next_token || (int) (next_token - tstr) != len)
+       return (int) (next_token - str); /* table only */
+   tstr = next_token + 1;
+   if ((len = findIdentifier(tstr, ccsc, &next_token)) <= 0)
+       return -1;
+   if (table)
+   {
+       if (schema)
+           MOVE_NAME(*schema, *table);
+       *table = lower_or_remove_dquote(*table, tstr, len, ccsc);
+   }
+   if (!next_token || '.' != *next_token || (int) (next_token - tstr) != len)
+       return (int) (next_token - str); /* schema.table */
+   tstr = next_token + 1;
+   if ((len = findIdentifier(tstr, ccsc, &next_token)) <= 0)
+       return -1;
+   if (table)
+   {
+       if (schema)
+           MOVE_NAME(*schema, *table);
+       *table = lower_or_remove_dquote(*table, tstr, len, ccsc);
+   }
+   return (int) (next_token - str); /* catalog.schema.table */
+}
+
 static void token_start(QueryParse *qp, char oldchar)
 {
    qp->prev_token_end = FALSE;
@@ -3617,37 +3701,33 @@ inner_process_tokens(QueryParse *qp, QueryBuild *qb)
            if (NULL != coli)
            {
                int i, num_fields = QR_NumResultCols(coli->result);
-               const char *auto_increment, *column_def;
+               const char *auto_increment;
 
                for (i = 0; i < num_fields; i++)
                {
                    auto_increment = (const char *) QR_get_value_backend_text(coli->result, i, COLUMNS_AUTO_INCREMENT);
                    if (auto_increment && auto_increment[0] == '1')
                    {
-                       column_def = (const char *) QR_get_value_backend_text(coli->result, i, COLUMNS_COLUMN_DEF);
-                       if (NULL != column_def)
-                       {
-                           CVT_APPEND_STR(qb, "curr");
-                           CVT_APPEND_STR(qb, column_def + 4);
-                       }
-                       else
+                       char relcnv[128];
+                       const char *column_name = (const char *) QR_get_value_backend_text(coli->result, i, COLUMNS_COLUMN_NAME);
+
+                       CVT_APPEND_STR(qb, "currval('");
+                       if (NAME_IS_VALID(conn->schemaIns))
                        {
-                           const char *column_name = (const char *) QR_get_value_backend_text(coli->result, i, COLUMNS_COLUMN_NAME);
-                           CVT_APPEND_STR(qb, "currval('\"");
-                           if (NAME_IS_VALID(conn->schemaIns))
-                           {
-                               CVT_SPECIAL_CHARS(qb, SAFE_NAME(conn->schemaIns), SQL_NTS);
-                               CVT_APPEND_STR(qb, "\".\"");
-                           }
-                           CVT_SPECIAL_CHARS(qb, SAFE_NAME(conn->tableIns), SQL_NTS);
-                           CVT_APPEND_STR(qb, "_");
-                           if (NULL != column_name)
-                               CVT_SPECIAL_CHARS(qb, column_name, SQL_NTS);
-                           CVT_APPEND_STR(qb, "_seq\"'::regclass)");
+                           CVT_APPEND_STR(qb, identifierEscape((const SQLCHAR *) SAFE_NAME(conn->schemaIns), SQL_NTS, conn, relcnv, sizeof(relcnv), TRUE));
+                           CVT_APPEND_STR(qb, ".");
                        }
-                       converted = TRUE;
-                       break;
+                       identifierEscape((const SQLCHAR *) SAFE_NAME(conn->tableIns), SQL_NTS, conn, relcnv, sizeof(relcnv), TRUE);
+                       /* remove the last " */
+                       relcnv[strlen(relcnv) - 1] = '\0';
+                       CVT_APPEND_STR(qb, relcnv);
+                       CVT_APPEND_STR(qb, "_");
+                       if (NULL != column_name)
+                           CVT_SPECIAL_CHARS(qb, column_name, SQL_NTS);
+                       CVT_APPEND_STR(qb, "_seq\"'::regclass)");
                    }
+                   converted = TRUE;
+                   break;
                }
            }
        }
@@ -5424,7 +5504,7 @@ convert_escape(QueryParse *qp, QueryBuild *qb)
    if (stricmp(key, "call") == 0)
    {
        size_t funclen;
-       const char *nextdel;
+       const UCHAR *next_token;
 
        if (SQL_ERROR == QB_start_brace(qb))
        {
@@ -5436,8 +5516,8 @@ convert_escape(QueryParse *qp, QueryBuild *qb)
            CVT_APPEND_STR(qb, "SELECT * FROM ");
        else
            CVT_APPEND_STR(qb, "SELECT ");
-       funclen = findIdentifier(F_OldPtr(qp), qb->ccsc, &nextdel);
-       if (nextdel && ODBC_ESCAPE_END == *nextdel)
+       funclen = findIdentifier((const UCHAR *) F_OldPtr(qp), qb->ccsc, &next_token);
+       if (next_token && ODBC_ESCAPE_END == *next_token)
        {
            CVT_APPEND_DATA(qb, F_OldPtr(qp), funclen);
            CVT_APPEND_STR(qb, "()");
@@ -5447,7 +5527,7 @@ convert_escape(QueryParse *qp, QueryBuild *qb)
                goto cleanup;
            }
            /* positioned at } */
-           qp->opos += (nextdel - F_OldPtr(qp));
+           qp->opos += ((const char *) next_token - F_OldPtr(qp));
        }
        else
        {
index a9c96cb5dc6f9bfc5f16cb03018761c2c16a9867..24b063a021b91d5bbcefb973950c8e2ecde28065 100644 (file)
--- a/execute.c
+++ b/execute.c
@@ -33,8 +33,6 @@
 #include "lobj.h"
 #include "pgapifunc.h"
 
-static const char *next_name_token(const char *s, size_t *len);
-
 /*     Perform a Prepare on the SQL statement */
 RETCODE        SQL_API
 PGAPI_Prepare(HSTMT hstmt,
@@ -769,7 +767,6 @@ SC_setInsertedTable(StatementClass *stmt, RETCODE retval)
    const char *cmd = stmt->statement;
    ConnectionClass *conn;
    size_t  len;
-   const char *token = NULL;
 
    if (STMT_TYPE_INSERT != stmt->statement_type)
        return;
@@ -811,85 +808,11 @@ SC_setInsertedTable(StatementClass *stmt, RETCODE retval)
    NULL_THE_NAME(conn->schemaIns);
    NULL_THE_NAME(conn->tableIns);
 
-   len = 0;
-   token = next_name_token(cmd, &len);
-   if (token && *token == IDENTIFIER_QUOTE)
-       STRN_TO_NAME(conn->tableIns, token + 1, len - 2);
-   else
-       STRN_TO_NAME(conn->tableIns, token, len);
-   token = next_name_token(token, &len);
-   if (token && *token == '.')
-   {
-       token = next_name_token(token, &len);
-       if (token) {
-           if (NAME_IS_VALID(conn->tableIns))
-               MOVE_NAME(conn->schemaIns, conn->tableIns);
-           if (*token == IDENTIFIER_QUOTE)
-               STRN_TO_NAME(conn->tableIns, token + 1, len - 2);
-           else
-               STRN_TO_NAME(conn->tableIns, token, len);
-       }
-   }
-   if (token && (token = next_name_token(token, &len)) && *token == '.')
-   {
-       token = next_name_token(token, &len);
-       if (token) {
-           if (NAME_IS_VALID(conn->tableIns))
-               MOVE_NAME(conn->schemaIns, conn->tableIns);
-           if (*token == IDENTIFIER_QUOTE)
-               STRN_TO_NAME(conn->tableIns, token + 1, len - 2);
-           else
-               STRN_TO_NAME(conn->tableIns, token, len);
-       }
-   }
-
+   eatTableIdentifiers((const UCHAR *) cmd, conn->ccsc, &conn->tableIns, &conn->schemaIns);
    if (!NAME_IS_VALID(conn->tableIns))
        NULL_THE_NAME(conn->schemaIns);
 }
 
-/*
- * Returns the next token from a qualified or unqualified name.
- *
- * s is the previous token. On entry, *len is the length of the previous
- * token; on return, it is the length of the next one. If the token is
- * quoted, the quotes are included in the result. If no valid token is found,
- * NULL is returned.
- */
-static const char *
-next_name_token(const char *s, size_t *len)
-{
-   const char *p;
-
-   s += *len;
-   while (*s && isspace((UCHAR) *s)) ++s;
-
-   switch (*s) {
-       case '\0':
-           break;
-       case '.':
-           *len = 1;
-           return s;
-       case IDENTIFIER_QUOTE:
-           p = strchr(s + 1, IDENTIFIER_QUOTE);
-           if (p) {
-               *len = p - s + 1;
-               return s;
-           }
-           break;
-       default:
-           p = s;
-           while (*p && !isspace((UCHAR) *p) && *p != '.') ++p;
-           if (p) {
-               *len = p - s;
-               return s;
-           }
-           break;
-   }
-
-   *len = 0;
-   return NULL;
-}
-
 /* Execute a prepared SQL statement */
 RETCODE        SQL_API
 PGAPI_Execute(HSTMT hstmt, UWORD flag)
diff --git a/info.c b/info.c
index a268f35861246ed058bc135ad79e8ccb86f45421..1793ce474c4543b7dff0c885f201da9766d97bd7 100644 (file)
--- a/info.c
+++ b/info.c
@@ -1509,7 +1509,7 @@ PGAPI_GetFunctions(HDBC hdbc,
 
 
 char *
-identifierEscape(const SQLCHAR *src, SQLLEN srclen, const ConnectionClass *conn, char *buf, size_t bufsize)
+identifierEscape(const SQLCHAR *src, SQLLEN srclen, const ConnectionClass *conn, char *buf, size_t bufsize, BOOL double_quote)
 {
    int i, outlen;
    const char *in;
@@ -1532,7 +1532,10 @@ MYLOG(0, "entering in=%s(" FORMAT_LEN ")\n", src, srclen);
        dest = malloc(bufsize);
    }
    if (!dest) return NULL;
-   for (i = 0, in = (char *) src, outlen = 0; i < srclen && outlen < bufsize - 1; i++, in++)
+   outlen = 0;
+   if (double_quote)
+       dest[outlen++] = '"';
+   for (i = 0, in = (char *) src; i < srclen && outlen < bufsize - 1; i++, in++)
    {
                 encoded_nextchar(&encstr);
                 if (MBCS_NON_ASCII(encstr))
@@ -1543,8 +1546,13 @@ MYLOG(0, "entering in=%s(" FORMAT_LEN ")\n", src, srclen);
        if (LITERAL_QUOTE == *in ||
            escape_ch == *in)
            dest[outlen++] = *in;
+       else if (double_quote &&
+            '"' == *in)
+           dest[outlen++] = *in;
        dest[outlen++] = *in;
    }
+   if (double_quote)
+       dest[outlen++] = '"';
    dest[outlen] = '\0';
 MYLOG(0, "leaving output=%s(%d)\n", dest, outlen);
    return dest;
@@ -1553,7 +1561,7 @@ MYLOG(0, "leaving output=%s(%d)\n", dest, outlen);
 static char *
 simpleCatalogEscape(const SQLCHAR *src, SQLLEN srclen, const ConnectionClass *conn)
 {
-   return identifierEscape(src, srclen, conn, NULL, -1);
+   return identifierEscape(src, srclen, conn, NULL, -1, FALSE);
 }
 
 /*
index dd13044591bb88eb0d85dec29e6551f31cc328dc..abe35c8f048c0c100009cabc46d2d584666e1ec7 100644 (file)
@@ -65,6 +65,7 @@ typedef struct
    int ccst;
 } encoded_str;
 #define ENCODE_STATUS(enc) ((enc).ccst)
+#define ENCODE_PTR(enc)    ((enc).encstr + (enc).pos)
 #define MBCS_NON_ASCII(enc)    (0 != (enc).ccst || (enc).encstr[(enc).pos] >= 0x80)
 
 void encoded_str_constr(encoded_str *encstr, int ccsc, const char *str);
diff --git a/parse.c b/parse.c
index 7890d437f39a014aea1ce1a2502974cae76b27d2..ebc884eba5c4831f42812635835054bbde7deaeb 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -770,8 +770,8 @@ COL_INFO **coli)
             */
            SPRINTF_FIXED(token,
                     "select nspname from pg_namespace n, pg_class c"
-                    " where c.relnamespace=n.oid and c.oid='\"%s\"'::regclass",
-                    identifierEscape((const SQLCHAR *) SAFE_NAME(table_name), SQL_NTS, conn, relcnv, sizeof(relcnv)));
+                    " where c.relnamespace=n.oid and c.oid='%s'::regclass",
+                    identifierEscape((const SQLCHAR *) SAFE_NAME(table_name), SQL_NTS, conn, relcnv, sizeof(relcnv), TRUE));
            res = CC_send_query(conn, token, NULL, READ_ONLY_QUERY, NULL);
            if (QR_command_maybe_successful(res))
            {