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