diff --git a/include/cspublic.h b/include/cspublic.h index 38d6fb7fa5..f0d3278cba 100644 --- a/include/cspublic.h +++ b/include/cspublic.h @@ -613,9 +613,11 @@ enum #define CS_IMAGELOCATOR_TYPE TDS_STATIC_CAST(CS_INT, 38) #define CS_UNITEXTLOCATOR_TYPE TDS_STATIC_CAST(CS_INT, 39) #define CS_UNIQUE_TYPE TDS_STATIC_CAST(CS_INT, 40) +#define CS_NVARCHAR_TYPE TDS_STATIC_CAST(CS_INT, 41) +#define CS_NLONGCHAR_TYPE TDS_STATIC_CAST(CS_INT, 42) #define CS_MIN_SYBTYPE CS_CHAR_TYPE -#define CS_MAX_SYBTYPE CS_UNIQUE_TYPE +#define CS_MAX_SYBTYPE CS_NLONGCHAR_TYPE #define CS_USER_TYPE TDS_STATIC_CAST(CS_INT, 100) /* cs_dt_info type values */ diff --git a/src/ctlib/cs.c b/src/ctlib/cs.c index ad89d6130d..1fd48e9d60 100644 --- a/src/ctlib/cs.c +++ b/src/ctlib/cs.c @@ -549,7 +549,9 @@ _cs_convert(CS_CONTEXT * ctx, const CS_DATAFMT_COMMON * srcfmt, CS_VOID * srcdat return CS_FAIL; } src_len = srcfmt->maxlength; - if (datatype == CS_VARCHAR_TYPE || datatype == CS_VARBINARY_TYPE) { + if (datatype == CS_VARCHAR_TYPE || + datatype == CS_NVARCHAR_TYPE || + datatype == CS_VARBINARY_TYPE) { CS_VARCHAR *vc = (CS_VARCHAR *) srcdata; src_len = vc->len; srcdata = vc->str; @@ -562,7 +564,9 @@ _cs_convert(CS_CONTEXT * ctx, const CS_DATAFMT_COMMON * srcfmt, CS_VOID * srcdat return CS_FAIL; } destlen = destfmt->maxlength; - if (datatype == CS_VARCHAR_TYPE || datatype == CS_VARBINARY_TYPE) { + if (datatype == CS_VARCHAR_TYPE || + datatype == CS_NVARCHAR_TYPE || + datatype == CS_VARBINARY_TYPE) { destvc = (CS_VARCHAR *) destdata; destlen = sizeof(destvc->str); destdata = destvc->str; @@ -622,6 +626,8 @@ _cs_convert(CS_CONTEXT * ctx, const CS_DATAFMT_COMMON * srcfmt, CS_VOID * srcdat case SYBCHAR: case SYBVARCHAR: case SYBTEXT: + case SYBNVARCHAR: + case SYBNTEXT: tdsdump_log(TDS_DBG_FUNC, "cs_convert() desttype = character\n"); memcpy(dest, srcdata, minlen); @@ -824,6 +830,8 @@ _cs_convert(CS_CONTEXT * ctx, const CS_DATAFMT_COMMON * srcfmt, CS_VOID * srcdat case SYBCHAR: case SYBVARCHAR: case SYBTEXT: + case SYBNVARCHAR: + case SYBNTEXT: ret = CS_SUCCEED; if (len > destlen) { tdsdump_log(TDS_DBG_FUNC, "Data-conversion resulted in overflow\n"); diff --git a/src/ctlib/ct.c b/src/ctlib/ct.c index 396048e849..3d965f1e37 100644 --- a/src/ctlib/ct.c +++ b/src/ctlib/ct.c @@ -2179,6 +2179,7 @@ _ct_get_server_type(TDSSOCKET *tds, int datatype) case CS_BIT_TYPE: return SYBBIT; case CS_CHAR_TYPE: return SYBCHAR; case CS_VARCHAR_TYPE: return SYBVARCHAR; + case CS_NVARCHAR_TYPE: return SYBNVARCHAR; case CS_LONG_TYPE: case CS_UBIGINT_TYPE: if (!tds || tds_capability_has_req(tds->conn, TDS_REQ_DATA_UINT8)) @@ -2208,11 +2209,12 @@ _ct_get_server_type(TDSSOCKET *tds, int datatype) case CS_TEXT_TYPE: return SYBTEXT; case CS_UNIQUE_TYPE: return SYBUNIQUE; case CS_LONGBINARY_TYPE: return SYBLONGBINARY; - case CS_UNICHAR_TYPE: return SYBVARCHAR; + case CS_UNICHAR_TYPE: return SYBNVARCHAR; case CS_LONGCHAR_TYPE: if (!tds || IS_TDS7_PLUS(tds->conn)) return SYBVARCHAR; return SYBLONGCHAR; + case CS_NLONGCHAR_TYPE: return SYBNVARCHAR; case CS_DATE_TYPE: if (!tds || tds_capability_has_req(tds->conn, TDS_REQ_DATA_DATE)) return SYBDATE; @@ -3383,6 +3385,8 @@ ct_param(CS_COMMAND * cmd, CS_DATAFMT * datafmt_arg, CS_VOID * data, CS_INT data CS_PARAM *param; const CS_DATAFMT_LARGE *datafmt; CS_DATAFMT_LARGE datafmt_buf; + bool promote = (datafmt_arg->datatype == CS_VARCHAR_TYPE || + datafmt_arg->datatype == CS_LONGCHAR_TYPE); tdsdump_log(TDS_DBG_FUNC, "ct_param(%p, %p, %p, %d, %hd)\n", cmd, datafmt_arg, data, datalen, indicator); @@ -3391,6 +3395,30 @@ ct_param(CS_COMMAND * cmd, CS_DATAFMT * datafmt_arg, CS_VOID * data, CS_INT data datafmt = _ct_datafmt_conv_in(cmd->con->ctx, datafmt_arg, &datafmt_buf); + if (promote && IS_TDS7_PLUS(cmd->con->tds_socket->conn)) { + /* Actually promote only if non-ASCII characters are present */ + CS_INT i; + + promote = 0; + for (i = 0; i < datalen; ++i) { + if (((const char*)data)[i] & 0x80) { + promote = 1; + break; + } + } + + if ( !promote ) { + /* pure ASCII, leave as is */ + } else if (datafmt->datatype == CS_VARCHAR_TYPE) { + datafmt_arg->datatype = CS_NVARCHAR_TYPE; + datafmt_buf.datatype = CS_NVARCHAR_TYPE; + } else if (datafmt->datatype == CS_LONGCHAR_TYPE) { + datafmt_arg->datatype = CS_NLONGCHAR_TYPE; + datafmt_buf.datatype = CS_NLONGCHAR_TYPE; + } + } + + switch (cmd->command_type) { case CS_RPC_CMD: if (!cmd->rpc) { @@ -4203,7 +4231,9 @@ paraminfoalloc(TDSSOCKET * tds, CS_PARAM * first_param) temp_datalen = *(p->datalen); } - if (temp_type == CS_VARCHAR_TYPE || temp_type == CS_VARBINARY_TYPE) { + if (temp_type == CS_VARCHAR_TYPE + || temp_type == CS_NVARCHAR_TYPE + || temp_type == CS_VARBINARY_TYPE) { CS_VARCHAR *vc = (CS_VARCHAR *) temp_value; if (vc) { @@ -4230,6 +4260,13 @@ paraminfoalloc(TDSSOCKET * tds, CS_PARAM * first_param) if (p->maxlen < 0) { tds_free_param_results(params); return NULL; + } else if (p->maxlen == 0 + && is_unicode_type(tds_type)) { + /* + * Auto-detect; account for possible + * conversion to UCS-2. + */ + p->maxlen = temp_datalen * 2; } pcol->on_server.column_size = pcol->column_size = p->maxlen; pcol->column_cur_size = temp_value ? temp_datalen : -1; diff --git a/src/tds/convert.c b/src/tds/convert.c index 320c5efdab..9ca8b42c5c 100644 --- a/src/tds/convert.c +++ b/src/tds/convert.c @@ -200,8 +200,11 @@ binary_to_result(int desttype, const void *data, size_t len, CONV_RESULT * cr) return (TDS_INT)len; } +/* "N" versions are safe to list due to iconv calls elsewhere. */ #define CASE_ALL_CHAR \ - SYBCHAR: case SYBVARCHAR: case SYBTEXT: case XSYBCHAR: case XSYBVARCHAR + SYBCHAR: case SYBVARCHAR: case SYBTEXT: case XSYBCHAR: \ + case XSYBVARCHAR: case SYBNVARCHAR: case SYBNTEXT: case XSYBNCHAR: \ + case XSYBNVARCHAR #define CASE_ALL_BINARY \ SYBBINARY: case SYBVARBINARY: case SYBIMAGE: case XSYBBINARY: case XSYBVARBINARY: \ case SYBLONGBINARY: case TDS_CONVERT_BINARY @@ -1986,8 +1989,6 @@ tds_convert(const TDSCONTEXT *tds_ctx, int srctype, const void *src, TDS_UINT sr case SYBUNIQUE: length = tds_convert_unique(src, desttype, cr); break; - case SYBNVARCHAR: - case SYBNTEXT: case SYBMSTABLE: default: return TDS_CONVERT_NOAVAIL; diff --git a/src/tds/data.c b/src/tds/data.c index aaab739db3..27637c8773 100644 --- a/src/tds/data.c +++ b/src/tds/data.c @@ -247,6 +247,9 @@ tds_set_param_type(TDSCONNECTION * conn, TDSCOLUMN * curcol, TDS_SERVER_TYPE typ { if (IS_TDS7_PLUS(conn)) { switch (type) { + case SYBNVARCHAR: + type = XSYBNVARCHAR; + break; case SYBVARCHAR: type = XSYBVARCHAR; break; diff --git a/src/tds/tds_willconvert.pl b/src/tds/tds_willconvert.pl index acf1299410..f29f212c11 100755 --- a/src/tds/tds_willconvert.pl +++ b/src/tds/tds_willconvert.pl @@ -36,7 +36,8 @@ ($) return qw(DATETIME DATETIME4 DATE TIME MSDATE MSTIME MSDATETIME2 MSDATETIMEOFFSET 5BIGDATETIME 5BIGTIME) if $_ eq 'DATETIMEx'; return qw(BIT BITN) if $_ eq 'BITx'; return qw(BINARY VARBINARY IMAGE LONGBINARY XBINARY XVARBINARY) if $_ eq 'BINARYx'; - return qw(CHAR VARCHAR XCHAR XVARCHAR) if $_ eq 'CHARx'; + return qw(CHAR VARCHAR XCHAR XNCHAR XNVARCHAR XVARCHAR) if $_ eq 'CHARx'; + return qw(TEXT NTEXT) if $_ eq 'TEXT'; return ($_); } @@ -129,21 +130,23 @@ ($) } print "};\n"; +# NB: SYBSENSITIVITY (Sybase) == 103 (0x67) == SYBNVARCHAR (MS) + __DATA__ To From CHARx TEXT BINARYx INTx FLTx NUMERIC DECIMAL BITx MONEYx DATETIMEx BOUNDARY UNIQUE SENSITIVITY MSTABLE -CHARx T T T T T T T T T T T T t F -TEXT T T T T T T T T T T T T t F -BINARYx T T T T T F F F T F F F F F -INTx T T T T T T T T T F F F F F -FLTx T T T T T T T T T F F F F F -NUMERIC T T T T T T T T T F F F F F -DECIMAL T T T T T T T T T F F F F F -BITx T T T T T T T T T F F F F F -MONEYx T T T T T T T T T F F F F F -DATETIMEx T T T F F F F F F T F F F F -BOUNDARY T T F F F F F F F F T F F F -UNIQUE T T T F F F F F F F F T F F -SENSITIVITY t t F F F F F F F F F F t F +CHARx T T T T T T T T T T T T T F +TEXT T T T T T T T T T T T T T F +BINARYx T T T T T F F F T F F F T F +INTx T T T T T T T T T F F F T F +FLTx T T T T T T T T T F F F T F +NUMERIC T T T T T T T T T F F F T F +DECIMAL T T T T T T T T T F F F T F +BITx T T T T T T T T T F F F T F +MONEYx T T T T T T T T T F F F T F +DATETIMEx T T T F F F F F F T F F T F +BOUNDARY T T F F F F F F F F T F T F +UNIQUE T T T F F F F F F F F T T F +SENSITIVITY T T T T T T T T T T T T T F MSTABLE F F F F F F F F F F F F F T diff --git a/src/tds/unittests/convert.c b/src/tds/unittests/convert.c index 6e8482706d..4e8533b09a 100644 --- a/src/tds/unittests/convert.c +++ b/src/tds/unittests/convert.c @@ -38,6 +38,7 @@ free_convert(int type, CONV_RESULT *cr) { switch (type) { case SYBCHAR: case SYBVARCHAR: case SYBTEXT: case XSYBCHAR: case XSYBVARCHAR: + case SYBNVARCHAR: case SYBNTEXT: case XSYBNCHAR: case XSYBNVARCHAR: case SYBBINARY: case SYBVARBINARY: case SYBIMAGE: case XSYBBINARY: case XSYBVARBINARY: case SYBLONGBINARY: free(cr->c); @@ -157,10 +158,18 @@ main(int argc, char **argv) case XSYBVARBINARY: case XSYBCHAR: case XSYBVARCHAR: + case SYBNTEXT: + case SYBNVARCHAR: + case XSYBNCHAR: + case XSYBNVARCHAR: switch (desttype) { case SYBCHAR: case SYBVARCHAR: case SYBTEXT: + case SYBNTEXT: + case SYBNVARCHAR: + case XSYBNCHAR: + case XSYBNVARCHAR: case SYBDATETIME: case SYBDATETIME4: src = "Jan 1, 1999";