Skip to content

Commit

Permalink
Match Sybase ctlib's handling of overlong or malformatted BCP input.
Browse files Browse the repository at this point in the history
Adjust existing failure modes' boundaries and introduce the
possibility of proceeding with truncated input with or without a
message (with the latter occurring in some cases that require no
conversion).  As for messages, arrange to emit them via _ctclient_msg
rather than _csclient_msg with the same (English) wording and context
as Sybase yields, modulo formal punctuation differences and the usual
discrepancies in layer and origin numbering.

To that end:
* Add an internal BLK_CONV_STATUS enum type whose values double as
  (new) message numbers.
* Cover those numbers in _ct_get_user_api_layer_error.
* Have _cs_convert additionally take a (quite possibly NULL) pointer
  to a bulk-conversion-status value to populate as appropriate.
* Keep a running count of rows sent for reporting purposes
  (determining the column number to cite by a linear scan of the
  bindinfo's columns array).

Signed-off-by: Aaron M. Ucko <[email protected]>
  • Loading branch information
ucko committed Jul 3, 2024
1 parent 270b244 commit 3176598
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 17 deletions.
12 changes: 11 additions & 1 deletion include/ctlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,15 @@ typedef union
CS_DATAFMT user;
} CS_DATAFMT_INTERNAL;

typedef enum
{
/* These values double as message numbers. */
BLK_CONV_OK = 0,
BLK_CONV_OVERFLOW = 25,
BLK_CONV_SYNTAX_ERROR = 26,
BLK_CONV_TRUNCATION = 42
} BLK_CONV_STATUS;

/*
* internal prototypes
*/
Expand All @@ -396,7 +405,8 @@ int _cs_convert_not_client(CS_CONTEXT *ctx, const TDSCOLUMN *curcol, CONV_RESULT
CS_RETCODE _cs_convert(CS_CONTEXT * ctx, const CS_DATAFMT_COMMON * srcfmt,
CS_VOID * srcdata, const CS_DATAFMT_COMMON * destfmt,
CS_VOID * destdata, CS_INT * resultlen,
TDS_SERVER_TYPE desttype, CS_VOID ** handle);
TDS_SERVER_TYPE desttype, CS_VOID ** handle,
BLK_CONV_STATUS * blk_status);
bool _ct_is_large_identifiers_version(CS_INT version);
const CS_DATAFMT_COMMON * _ct_datafmt_common(CS_CONTEXT * ctx, const CS_DATAFMT * datafmt);
const CS_DATAFMT_LARGE *_ct_datafmt_conv_in(CS_CONTEXT * ctx, const CS_DATAFMT * datafmt, CS_DATAFMT_LARGE * fmtbuf);
Expand Down
1 change: 1 addition & 0 deletions include/freetds/tds.h
Original file line number Diff line number Diff line change
Expand Up @@ -1715,6 +1715,7 @@ struct tds_bcpinfo
TDS_INT text_sent;
TDS_INT next_col;
TDS_INT blob_cols;
TDS_INT rows_sent;
};

TDSRET tds_bcp_init(TDSSOCKET *tds, TDSBCPINFO *bcpinfo);
Expand Down
22 changes: 17 additions & 5 deletions src/ctlib/blk.c
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,7 @@ _blk_get_col_data(TDSBCPINFO *bulk, TDSCOLUMN *bindcol, int offset)
CS_INT desttype;
TDS_SERVER_TYPE tds_desttype = TDS_INVALID_TYPE;
TDSSOCKET * tds = CONN(blkdesc)->tds_socket;
BLK_CONV_STATUS conv_status = BLK_CONV_OK;

srcfmt.datatype = srctype;
srcfmt.maxlength = srclen;
Expand All @@ -805,11 +806,22 @@ _blk_get_col_data(TDSBCPINFO *bulk, TDSCOLUMN *bindcol, int offset)
destfmt.format = CS_FMT_UNUSED;

/* if convert return FAIL mark error but process other columns */
if ((result = _cs_convert(ctx, &srcfmt, (CS_VOID *) src,
&destfmt, (CS_VOID *) coldata->data,
&destlen, tds_desttype,
(CS_VOID **) &coldata->data))
!= CS_SUCCEED) {
result = _cs_convert(ctx, &srcfmt, (CS_VOID *) src, &destfmt,
(CS_VOID *) coldata->data, &destlen,
tds_desttype, (CS_VOID **) &coldata->data,
&conv_status);
if (conv_status != BLK_CONV_OK) {
const TDSRESULTINFO * info = bulk->bindinfo;
TDS_USMALLINT colnum;
for (colnum = 0; colnum < info->num_cols; ++colnum) {
if (info->columns[colnum] == bindcol)
break;
}
_ctclient_msg(ctx, CONN(blkdesc), "blk_rowxfer",
2, 7, 1, conv_status, "%d,%hu",
bulk->rows_sent + 1, colnum + 1);
}
if (result != CS_SUCCEED) {
tdsdump_log(TDS_DBG_INFO1, "convert failed for %d \n", srctype);
return TDS_FAIL;
}
Expand Down
101 changes: 91 additions & 10 deletions src/ctlib/cs.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#include <stdio.h>
#include <assert.h>
#include <ctype.h>

#if HAVE_STDLIB_H
#include <stdlib.h>
Expand Down Expand Up @@ -507,7 +508,8 @@ CS_RETCODE
_cs_convert(CS_CONTEXT * ctx, const CS_DATAFMT_COMMON * srcfmt,
CS_VOID * srcdata, const CS_DATAFMT_COMMON * destfmt,
CS_VOID * destdata, CS_INT * resultlen,
TDS_SERVER_TYPE desttype, CS_VOID ** handle)
TDS_SERVER_TYPE desttype, CS_VOID ** handle,
BLK_CONV_STATUS * blk_status)
{
TDS_SERVER_TYPE src_type;
int src_len, destlen, len;
Expand All @@ -518,9 +520,9 @@ _cs_convert(CS_CONTEXT * ctx, const CS_DATAFMT_COMMON * srcfmt,
CS_VARCHAR *destvc = NULL;

tdsdump_log(TDS_DBG_FUNC,
"cs_convert(%p, %p, %p, %p, %p, %p, %d, %p)\n",
"cs_convert(%p, %p, %p, %p, %p, %p, %d, %p, %p)\n",
ctx, srcfmt, srcdata, destfmt, destdata, resultlen,
desttype, handle);
desttype, handle, blk_status);

/* If destfmt is NULL we have a problem */
if (destfmt == NULL) {
Expand Down Expand Up @@ -613,8 +615,17 @@ _cs_convert(CS_CONTEXT * ctx, const CS_DATAFMT_COMMON * srcfmt,

if (src_len > destlen) {
tdsdump_log(TDS_DBG_FUNC, "error: src_len > destlen\n");
_csclient_msg(ctx, "cs_convert", 2, 4, 1, 36, "");
ret = CS_FAIL;
if (blk_status == NULL) {
_csclient_msg(ctx, "cs_convert",
2, 4, 1, 36, "");
ret = CS_FAIL;
} else if (srcfmt->datatype
== CS_VARBINARY_TYPE) {
*blk_status = BLK_CONV_OVERFLOW;
ret = CS_FAIL;
} else {
ret = CS_SUCCEED;
}
} else {
switch (destfmt->format) {
case CS_FMT_PADNULL:
Expand Down Expand Up @@ -643,6 +654,12 @@ _cs_convert(CS_CONTEXT * ctx, const CS_DATAFMT_COMMON * srcfmt,
memcpy(dest, srcdata, minlen);
*resultlen = minlen;

if (src_len > destlen && blk_status != NULL) {
src_len = destlen;
if (srcfmt->datatype == CS_VARCHAR_TYPE)
*blk_status = BLK_CONV_TRUNCATION;
}

if (src_len > destlen) {
tdsdump_log(TDS_DBG_FUNC, "error: src_len > destlen\n");
_csclient_msg(ctx, "cs_convert", 2, 4, 1, 36, "");
Expand Down Expand Up @@ -754,6 +771,41 @@ _cs_convert(CS_CONTEXT * ctx, const CS_DATAFMT_COMMON * srcfmt,
len = tds_convert(ctx->tds_ctx, src_type, srcdata, src_len, desttype, &cres);

tdsdump_log(TDS_DBG_FUNC, "cs_convert() tds_convert returned %d\n", len);
if (len == TDS_CONVERT_SYNTAX && blk_status != NULL
&& is_binary_type(desttype)) {
char * s = (char *) srcdata;
int src_len2 = destlen * 2;
if (src_len > 1 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
src_len2 += 2;
/* Match full input's low order bit to get correct phase. */
if (src_len & 1)
--src_len2;
if (src_len2 < src_len) {
len = tds_convert(ctx->tds_ctx, src_type, srcdata,
src_len2, desttype, &cres);
}
tdsdump_log(TDS_DBG_FUNC,
"cs_convert(): retry returned %d\n", len);
/*
* Check the last character to avoid interference from
* space and NUL stripping.
*/
if (len == destlen && isxdigit(s[src_len2 - 1])) {
if (srcfmt->datatype == CS_VARCHAR_TYPE) {
free(cres.ib);
*blk_status = BLK_CONV_OVERFLOW;
return CS_FAIL;
} else {
*blk_status = BLK_CONV_TRUNCATION;
/* Proceed anyway. */
}
} else {
if (len >= 0)
free(cres.ib);
*blk_status = BLK_CONV_SYNTAX_ERROR;
return CS_FAIL;
}
}

switch (len) {
case TDS_CONVERT_NOAVAIL:
Expand Down Expand Up @@ -789,8 +841,18 @@ _cs_convert(CS_CONTEXT * ctx, const CS_DATAFMT_COMMON * srcfmt,
ret = CS_SUCCEED;
if (len > destlen) {
tdsdump_log(TDS_DBG_FUNC, "error_handler: Data-conversion resulted in overflow\n");
_csclient_msg(ctx, "cs_convert", 2, 4, 1, 36, "");
ret = CS_FAIL;
if (blk_status == NULL) {
_csclient_msg(ctx, "cs_convert", 2, 4, 1, 36,
"");
ret = CS_FAIL;
} else if (srcfmt->datatype == CS_VARBINARY_TYPE
|| srcfmt->datatype == CS_VARCHAR_TYPE) {
*blk_status = BLK_CONV_OVERFLOW;
ret = CS_FAIL;
} else {
*blk_status = BLK_CONV_TRUNCATION;
/* Proceed anyway. */
}
len = destlen;
}
if (handle == NULL) {
Expand Down Expand Up @@ -857,9 +919,28 @@ _cs_convert(CS_CONTEXT * ctx, const CS_DATAFMT_COMMON * srcfmt,
ret = CS_SUCCEED;
if (len > destlen) {
tdsdump_log(TDS_DBG_FUNC, "Data-conversion resulted in overflow\n");
_csclient_msg(ctx, "cs_convert", 2, 4, 1, 36, "");
len = destlen;
ret = CS_FAIL;
if (blk_status == NULL) {
_csclient_msg(ctx, "cs_convert", 2, 4, 1, 36,
"");
ret = CS_FAIL;
} else if (is_char_type(src_type)) {
if (srcfmt->datatype == CS_VARCHAR_TYPE)
*blk_status = BLK_CONV_TRUNCATION;
} else if (is_binary_type(src_type)) {
if (srcfmt->datatype == CS_VARBINARY_TYPE) {
*blk_status = BLK_CONV_OVERFLOW;
ret = CS_FAIL;
} else {
*blk_status = BLK_CONV_TRUNCATION;
len &= ~1;
}
} else if (is_datetime_type(src_type)) {
*blk_status = BLK_CONV_TRUNCATION;
} else {
*blk_status = BLK_CONV_OVERFLOW;
ret = CS_FAIL;
}
}
switch (destfmt->format) {

Expand Down Expand Up @@ -943,7 +1024,7 @@ cs_convert(CS_CONTEXT * ctx, CS_DATAFMT * srcfmt, CS_VOID * srcdata, CS_DATAFMT
{
return _cs_convert(ctx, _ct_datafmt_common(ctx, srcfmt), srcdata,
_ct_datafmt_common(ctx, destfmt), destdata,
resultlen, TDS_INVALID_TYPE, NULL);
resultlen, TDS_INVALID_TYPE, NULL, NULL);
}

CS_RETCODE
Expand Down
11 changes: 10 additions & 1 deletion src/ctlib/ct.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,15 @@ _ct_get_user_api_layer_error(int error)
case 15:
return "Use direction CS_BLK_IN or CS_BLK_OUT for a bulk copy operation.";
break;
case 25:
return "Failed in conversion routine - condition overflow."
" col = %1! row = %2!.";
case 26:
return "Failed in conversion routine - syntax error."
" col = %1! row = %2!.";
case 42:
return "Data truncated while doing local character set"
" conversion. col = %1! row = %2!.";
case 51:
return "Exactly one of context and connection must be non-NULL.";
break;
Expand Down Expand Up @@ -1955,7 +1964,7 @@ _ct_bind_data(CS_CONTEXT *ctx, TDSRESULTINFO * resinfo, TDSRESULTINFO *bindinfo,

/* if convert return FAIL mark error but process other columns */
if ((ret = _cs_convert(ctx, &srcfmt, src, &destfmt, dest,
pdatalen, TDS_INVALID_TYPE, NULL)
pdatalen, TDS_INVALID_TYPE, NULL, NULL)
!= CS_SUCCEED)) {
tdsdump_log(TDS_DBG_FUNC, "cs_convert-result = %d\n", ret);
result = 1;
Expand Down
1 change: 1 addition & 0 deletions src/tds/bulk.c
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,7 @@ tds_bcp_send_record(TDSSOCKET *tds, TDSBCPINFO *bcpinfo,
return TDS_SUCCESS;
tds_set_state(tds, TDS_SENDING);
bcpinfo->next_col = 0;
bcpinfo->rows_sent++;
return rc;
}

Expand Down

0 comments on commit 3176598

Please sign in to comment.