Changeset: 0b23137c480b for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=0b23137c480b
Modified Files:
        clients/odbc/driver/ODBCConvert.c
        clients/odbc/driver/ODBCUtil.c
        clients/odbc/driver/ODBCUtil.h
        clients/odbc/driver/SQLError.c
        clients/odbc/driver/SQLGetDescRec.c
        clients/odbc/driver/SQLGetDiagField.c
        clients/odbc/driver/SQLGetDiagRec.c
Branch: Aug2018
Log Message:

Fixes for retrieving data in chunks with SQLGetData.
Also, make sure returned strings are NULL-terminated, truncating them
when needed.
This (hopefully) fixes bug 6672.


diffs (298 lines):

diff --git a/clients/odbc/driver/ODBCConvert.c 
b/clients/odbc/driver/ODBCConvert.c
--- a/clients/odbc/driver/ODBCConvert.c
+++ b/clients/odbc/driver/ODBCConvert.c
@@ -1225,7 +1225,7 @@ ODBCFetch(ODBCStmt *stmt,
                        ptr = (SQLPOINTER) ((char *) ptr + row * (bind_type == 
SQL_BIND_BY_COLUMN ? ardrec->sql_desc_octet_length : bind_type));
 
                /* if SQL_C_WCHAR is requested, first convert to UTF-8
-                * (SQL_C_CHAR), and at the end convert to UTF-16 */
+                * (SQL_C_CHAR), and at the end convert to WCHAR */
                origptr = ptr;
 
                origbuflen = buflen;
@@ -1259,25 +1259,20 @@ ODBCFetch(ODBCStmt *stmt,
                case SQL_WVARCHAR:
                case SQL_WLONGVARCHAR:
                case SQL_GUID:
-                       if (irdrec->already_returned > datalen) {
-                               data += datalen;
-                               datalen = 0;
-                       } else {
-                               data += irdrec->already_returned;
-                               datalen -= irdrec->already_returned;
-                       }
-                       if (datalen == 0 && irdrec->already_returned != 0) {
+                       if (irdrec->already_returned >= datalen) {
                                /* no more data to return */
                                if (type == SQL_C_WCHAR)
                                        free(ptr);
                                return SQL_NO_DATA;
                        }
+                       data += irdrec->already_returned;
+                       datalen -= irdrec->already_returned;
                        copyString(data, datalen, ptr, buflen, lenp, SQLLEN,
                                   addStmtError, stmt, return SQL_ERROR);
                        if (datalen < (size_t) buflen)
                                irdrec->already_returned += datalen;
                        else
-                               irdrec->already_returned += buflen;
+                               irdrec->already_returned += buflen - 1;
                        break;
                case SQL_BINARY:
                case SQL_VARBINARY:
@@ -1285,30 +1280,18 @@ ODBCFetch(ODBCStmt *stmt,
                        size_t k;
                        int n;
                        unsigned char c = 0;
-                       SQLLEN j = 0;
+                       SQLLEN j;
                        unsigned char *p = ptr;
 
-                       if (buflen < 0) {
-                               /* Invalid string or buffer length */
-                               addStmtError(stmt, "HY090", NULL, 0);
-                               if (type == SQL_C_WCHAR)
-                                       free(ptr);
-                               return SQL_ERROR;
-                       }
-                       if (irdrec->already_returned > datalen) {
-                               data += datalen;
-                               datalen = 0;
-                       } else {
-                               data += irdrec->already_returned;
-                               datalen -= irdrec->already_returned;
-                       }
-                       if (datalen == 0 && irdrec->already_returned != 0) {
+                       if (irdrec->already_returned >= datalen) {
                                /* no more data to return */
                                if (type == SQL_C_WCHAR)
                                        free(ptr);
                                return SQL_NO_DATA;
                        }
-                       for (k = 0; k < datalen; k++) {
+                       data += irdrec->already_returned;
+                       datalen -= irdrec->already_returned;
+                       for (k = 0, j = 0; k < datalen && j < buflen; k++) {
                                if ('0' <= data[k] && data[k] <= '9')
                                        n = data[k] - '0';
                                else if ('A' <= data[k] && data[k] <= 'F')
@@ -1325,15 +1308,22 @@ ODBCFetch(ODBCStmt *stmt,
                                }
                                if (k & 1) {
                                        c |= n;
-                                       if (j < buflen)
-                                               p[j] = c;
+                                       p[j] = c;
                                        j++;
                                } else
                                        c = n << 4;
                        }
+                       if (k & 1) {
+                               /* should not happen: uneven length */
+                               /* General error */
+                               addStmtError(stmt, "HY000", "Unexpected data 
from server", 0);
+                               if (type == SQL_C_WCHAR)
+                                       free(ptr);
+                               return SQL_ERROR;
+                       }
                        irdrec->already_returned += k;
                        if (lenp)
-                               *lenp = j;
+                               *lenp = datalen / 2;
                        break;
                }
                case SQL_TINYINT:
@@ -1872,9 +1862,11 @@ ODBCFetch(ODBCStmt *stmt,
                }
                if (type == SQL_C_WCHAR) {
                        SQLSMALLINT n;
+                       SQLINTEGER i;
 
                        ODBCutf82wchar((SQLCHAR *) ptr, SQL_NTS,
-                                      (SQLWCHAR *) origptr, origbuflen, &n);
+                                      (SQLWCHAR *) origptr,
+                                      origbuflen / sizeof(SQLWCHAR), &n, &i);
 #ifdef ODBCDEBUG
                        ODBCLOG("Writing %d bytes to %p\n",
                                (int) (n * sizeof(SQLWCHAR)),
@@ -1884,6 +1876,8 @@ ODBCFetch(ODBCStmt *stmt,
                        if (origlenp)
                                *origlenp = n * sizeof(SQLWCHAR); /* # of 
bytes, not chars */
                        free(ptr);
+                       irdrec->already_returned -= datalen;
+                       irdrec->already_returned += i;
                }
 #ifdef ODBCDEBUG
                else
diff --git a/clients/odbc/driver/ODBCUtil.c b/clients/odbc/driver/ODBCUtil.c
--- a/clients/odbc/driver/ODBCUtil.c
+++ b/clients/odbc/driver/ODBCUtil.c
@@ -178,14 +178,17 @@ ODBCwchar2utf8(const SQLWCHAR *src, SQLL
 
 /* Convert a UTF-8 encoded string to UTF-16 (SQLWCHAR).  On success
    returns NULL, on error returns a string with an error message.  The
-   first two arguments describe the input, the last three arguments
-   describe the output, both in the normal ODBC fashion. */
+   first two arguments describe the input, the next three arguments
+   describe the output, both in the normal ODBC fashion.
+   The last argument is the count of the number of input bytes
+   actually converted to the output. */
 const char *
 ODBCutf82wchar(const SQLCHAR *src,
               SQLINTEGER length,
               SQLWCHAR *buf,
               SQLLEN buflen,
-              SQLSMALLINT *buflenout)
+              SQLSMALLINT *buflenout,
+              SQLINTEGER *consumed)
 {
        SQLLEN i = 0;
        SQLINTEGER j = 0;
@@ -201,6 +204,8 @@ ODBCutf82wchar(const SQLCHAR *src,
                        buf[0] = 0;
                if (buflenout)
                        *buflenout = 0;
+               if (consumed)
+                       *consumed = 0;
                return NULL;
        }
        if (length == SQL_NTS)
@@ -257,6 +262,8 @@ ODBCutf82wchar(const SQLCHAR *src,
        }
        if (buflen > 0)
                buf[i] = 0;
+       if (consumed)
+               *consumed = j;
        while (j < length && src[j]) {
                i++;
                if ((src[j+0] & 0x80) == 0) {
diff --git a/clients/odbc/driver/ODBCUtil.h b/clients/odbc/driver/ODBCUtil.h
--- a/clients/odbc/driver/ODBCUtil.h
+++ b/clients/odbc/driver/ODBCUtil.h
@@ -70,30 +70,37 @@ extern char *dupODBCstring(const SQLCHAR
 /* Utility macro to copy a string to an output argument.  In the ODBC
    API there are generally three arguments involved: the pointer to a
    buffer, the length of that buffer, and a pointer to where the
-   actual string length is to be stored. */
-#define copyString(str, strlen, buf, len, lenp, lent, errfunc, hdl, ret) \
+   actual string length is to be stored.
+   The output buffer is always NULL-terminated, so if *lenp == buflen
+   the input string did not fit. */
+#define copyString(str, strlen, buf, buflen, lenp, lent, errfunc, hdl, ret) \
        do {                                                            \
                lent _l;                                                \
-               if ((len) < 0) {                                        \
+               if ((buflen) < 0) {                                     \
                        /* Invalid string or buffer length */           \
                        errfunc((hdl), "HY090", NULL, 0);               \
                        ret;                                            \
                }                                                       \
                _l = (str) ? (lent) (strlen) : 0;                       \
                if (buf) {                                              \
-                       strncpy((char *) (buf), (str) ? (const char *) (str) : 
"", (len)); \
-                       if (_l < (len))                                 \
-                               ((char *)(buf))[_l] = 0;                \
+                       if ((buflen) > 1) {                             \
+                               /* note: if second arg short, rest */   \
+                               /* of buf is cleared (i.e. terminated) */ \
+                               strncpy((char *) (buf), (str) ? (const char *) 
(str) : "", (buflen) - 1); \
+                       }                                               \
+                       if ((buflen) > 0) {                             \
+                               ((char *)(buf))[(buflen) - 1] = 0;      \
+                       }                                               \
                }                                                       \
                if (lenp)                                               \
                        *(lenp) = _l;                                   \
-               if ((buf) == NULL || _l >= (len))                       \
+               if ((buf) == NULL || _l >= (buflen))                    \
                        /* String data, right-truncated */              \
                        errfunc((hdl), "01004", NULL, 0);               \
        } while (0)
 
 extern SQLCHAR *ODBCwchar2utf8(const SQLWCHAR *s, SQLLEN length, const char 
**errmsg);
-extern const char *ODBCutf82wchar(const SQLCHAR *s, SQLINTEGER length, 
SQLWCHAR *buf, SQLLEN buflen, SQLSMALLINT *buflenout);
+extern const char *ODBCutf82wchar(const SQLCHAR *s, SQLINTEGER length, 
SQLWCHAR *buf, SQLLEN buflen, SQLSMALLINT *buflenout, SQLINTEGER *consumed);
 
 #define fixWcharIn(ws, wsl, t, s, errfunc, hdl, exit)                  \
        do {                                                            \
@@ -110,7 +117,8 @@ extern const char *ODBCutf82wchar(const 
 #define fixWcharOut(r, s, sl, ws, wsl, wslp, cw, errfunc, hdl)         \
        do {                                                            \
                const char *e = ODBCutf82wchar((s), (sl), (ws),         \
-                                              (wsl) / (cw), &(sl));    \
+                                              (wsl) / (cw), &(sl),     \
+                                              NULL);                   \
                if (e) {                                                \
                        /* General error */                             \
                        errfunc((hdl), "HY000", e, 0);                  \
diff --git a/clients/odbc/driver/SQLError.c b/clients/odbc/driver/SQLError.c
--- a/clients/odbc/driver/SQLError.c
+++ b/clients/odbc/driver/SQLError.c
@@ -140,7 +140,8 @@ SQLErrorW(SQLHENV EnvironmentHandle,
                return SQL_ERROR;
 
        if (SQL_SUCCEEDED(rc)) {
-               const char *e = ODBCutf82wchar(state, 5, SQLState, 6, NULL);
+               const char *e = ODBCutf82wchar(state, 5, SQLState, 6, NULL,
+                                              NULL);
 
                if (e)
                        rc = SQL_ERROR;
@@ -148,7 +149,8 @@ SQLErrorW(SQLHENV EnvironmentHandle,
 
        if (SQL_SUCCEEDED(rc)) {
                const char *e = ODBCutf82wchar(errmsg, n,
-                                              MessageText, BufferLength, &n);
+                                              MessageText, BufferLength, &n,
+                                              NULL);
 
                if (e)
                        rc = SQL_ERROR;
diff --git a/clients/odbc/driver/SQLGetDescRec.c 
b/clients/odbc/driver/SQLGetDescRec.c
--- a/clients/odbc/driver/SQLGetDescRec.c
+++ b/clients/odbc/driver/SQLGetDescRec.c
@@ -201,7 +201,8 @@ SQLGetDescRecW(SQLHDESC DescriptorHandle
                            NullablePtr);
 
        if (SQL_SUCCEEDED(rc)) {
-               const char *e = ODBCutf82wchar(name, n, Name, BufferLength, &n);
+               const char *e = ODBCutf82wchar(name, n, Name, BufferLength, &n,
+                                              NULL);
 
                if (e)
                        rc = SQL_ERROR;
diff --git a/clients/odbc/driver/SQLGetDiagField.c 
b/clients/odbc/driver/SQLGetDiagField.c
--- a/clients/odbc/driver/SQLGetDiagField.c
+++ b/clients/odbc/driver/SQLGetDiagField.c
@@ -338,7 +338,8 @@ SQLGetDiagFieldW(SQLSMALLINT HandleType,
        if (ptr != DiagInfoPtr) {
                if (SQL_SUCCEEDED(rc)) {
                        const char *e = ODBCutf82wchar(ptr, n, DiagInfoPtr,
-                                                      BufferLength / 2, &n);
+                                                      BufferLength / 2, &n,
+                                                      NULL);
 
                        if (e)
                                rc = SQL_ERROR;
diff --git a/clients/odbc/driver/SQLGetDiagRec.c 
b/clients/odbc/driver/SQLGetDiagRec.c
--- a/clients/odbc/driver/SQLGetDiagRec.c
+++ b/clients/odbc/driver/SQLGetDiagRec.c
@@ -221,7 +221,8 @@ SQLGetDiagRecW(SQLSMALLINT HandleType,
 #endif
 
        if (SQL_SUCCEEDED(rc)) {
-               const char *e = ODBCutf82wchar(state, 5, SQLState, 6, NULL);
+               const char *e = ODBCutf82wchar(state, 5, SQLState, 6, NULL,
+                                              NULL);
 
                if (e)
                        rc = SQL_ERROR;
@@ -229,7 +230,7 @@ SQLGetDiagRecW(SQLSMALLINT HandleType,
 
        if (SQL_SUCCEEDED(rc)) {
                const char *e = ODBCutf82wchar(msg, n, MessageText,
-                                              BufferLength, &n);
+                                              BufferLength, &n, NULL);
 
                if (e)
                        rc = SQL_ERROR;
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to