/** * Fetch character data the wire. * Output is NOT null terminated. * If \a char_conv is not NULL, convert data accordingly. * \param tds state information for the socket and the TDS protocol * \param row_buffer destination buffer in current_row. Can't be NULL * \param wire_size size to read from wire (in bytes) * \param curcol column information * \return TDS_SUCCEED or TDS_FAIL (probably memory error on text data) * \todo put a TDSICONV structure in every TDSCOLUMN */ int tds_get_char_data(TDSSOCKET * tds, char *row_buffer, size_t wire_size, TDSCOLUMN * curcol) { size_t in_left; TDSBLOB *blob = NULL; char *dest = row_buffer; if (is_blob_col(curcol)) { blob = (TDSBLOB *) row_buffer; dest = blob->textvalue; } /* * dest is usually a column buffer, allocated when the column's metadata are processed * and reused for each row. * For blobs, dest is blob->textvalue, and can be reallocated or freed * TODO: reallocate if blob and no space */ /* silly case, empty string */ if (wire_size == 0) { curcol->column_cur_size = 0; if (blob) TDS_ZERO_FREE(blob->textvalue); return TDS_SUCCEED; } if (curcol->char_conv) { /* * TODO The conversion should be selected from curcol and tds version * TDS8/single -> use curcol collation * TDS7/single -> use server single byte * TDS7+/unicode -> use server (always unicode) * TDS5/4.2 -> use server * TDS5/UTF-8 -> use server * TDS5/UTF-16 -> use UTF-16 */ in_left = blob ? curcol->column_cur_size : curcol->column_size; curcol->column_cur_size = read_and_convert(tds, curcol->char_conv, &wire_size, &dest, &in_left); if (wire_size > 0) { tdsdump_log(TDS_DBG_NETWORK, "error: tds_get_char_data: discarded %u on wire while reading %d into client. \n", (unsigned int) wire_size, curcol->column_cur_size); return TDS_FAIL; } } else { curcol->column_cur_size = (TDS_INT)wire_size; if (tds_get_n(tds, dest, (int)wire_size) == NULL) { tdsdump_log(TDS_DBG_NETWORK, "error: tds_get_char_data: failed to read %u from wire. \n", (unsigned int) wire_size); return TDS_FAIL; } } return TDS_SUCCEED; }
static void test(const char *type, const char *test_name) { char buf[256]; char tmp[256]; int i; const char **s; int rc; TDS_INT result_type; int done_flags; sprintf(buf, "CREATE TABLE #tmp (i INT, t %s)", type); query(buf); /* insert all test strings in table */ for (i = 0, s = strings; *s; ++s, ++i) { sprintf(buf, "insert into #tmp values(%d, N'%s')", i, to_utf8(*s, tmp)); query(buf); } /* do a select and check all results */ rc = tds_submit_query(tds, "select t from #tmp order by i"); if (rc != TDS_SUCCESS) { fprintf(stderr, "tds_submit_query() failed\n"); exit(1); } if (tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS) != TDS_SUCCESS) { fprintf(stderr, "tds_process_tokens() failed\n"); exit(1); } if (result_type != TDS_ROWFMT_RESULT) { fprintf(stderr, "expected row fmt() failed\n"); exit(1); } if (tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS) != TDS_SUCCESS) { fprintf(stderr, "tds_process_tokens() failed\n"); exit(1); } if (result_type != TDS_ROW_RESULT) { fprintf(stderr, "expected row result() failed\n"); exit(1); } i = 0; while ((rc = tds_process_tokens(tds, &result_type, NULL, TDS_RETURN_ROWFMT|TDS_RETURN_ROW|TDS_RETURN_COMPUTE)) == TDS_SUCCESS) { switch (result_type) { case TDS_ROW_RESULT: { TDSCOLUMN *curcol = tds->current_results->columns[0]; char *src = (char *) curcol->column_data; if (is_blob_col(curcol)) { TDSBLOB *blob = (TDSBLOB *) src; src = blob->textvalue; } strcpy(buf, to_utf8(strings[i], tmp)); if (strlen(buf) != curcol->column_cur_size || strncmp(buf, src, curcol->column_cur_size) != 0) { int l = curcol->column_cur_size; if (l > 200) l = 200; strncpy(tmp, src, l); tmp[l] = 0; fprintf(stderr, "Wrong result in test %s\n Got: '%s' len %d\n Expected: '%s' len %u\n", test_name, tmp, curcol->column_cur_size, buf, (unsigned int) strlen(buf)); exit(1); } ++i; } break; default: fprintf(stderr, "Unexpected result\n"); exit(1); break; } } if (rc != TDS_NO_MORE_RESULTS) { fprintf(stderr, "tds_process_tokens() unexpected return\n"); exit(1); } while ((rc = tds_process_tokens(tds, &result_type, &done_flags, TDS_TOKEN_RESULTS)) == TDS_SUCCESS) { switch (result_type) { case TDS_NO_MORE_RESULTS: return; case TDS_DONE_RESULT: case TDS_DONEPROC_RESULT: case TDS_DONEINPROC_RESULT: if (!(done_flags & TDS_DONE_ERROR)) break; default: fprintf(stderr, "tds_process_tokens() unexpected result_type\n"); exit(1); break; } } query("DROP TABLE #tmp"); /* do sone select to test results */ /* * for (s = strings; *s; ++s) { * printf("%s\n", to_utf8(*s, tmp)); * } */ }
int continue_parse_prepared_query(struct _hstmt *stmt, SQLPOINTER DataPtr, SQLLEN StrLen_or_Ind) { struct _drecord *drec_apd, *drec_ipd; SQLLEN len; int need_bytes; TDSCOLUMN *curcol; TDSBLOB *blob; int sql_src_type; assert(stmt); tdsdump_log(TDS_DBG_FUNC, "continue_parse_prepared_query with parameter %d\n", stmt->param_num); if (!stmt->params) { tdsdump_log(TDS_DBG_FUNC, "error? continue_parse_prepared_query: no parameters provided"); return SQL_ERROR; } if (stmt->param_num > stmt->apd->header.sql_desc_count || stmt->param_num > stmt->ipd->header.sql_desc_count) return SQL_ERROR; drec_apd = &stmt->apd->records[stmt->param_num - 1]; drec_ipd = &stmt->ipd->records[stmt->param_num - 1]; curcol = stmt->params->columns[stmt->param_num - (stmt->prepared_query_is_func ? 2 : 1)]; blob = NULL; if (is_blob_col(curcol)) blob = (TDSBLOB *) curcol->column_data; assert(curcol->column_cur_size <= curcol->column_size); need_bytes = curcol->column_size - curcol->column_cur_size; if (DataPtr == NULL) { switch(StrLen_or_Ind) { case SQL_NULL_DATA: case SQL_DEFAULT_PARAM: break; /* OK */ default: odbc_errs_add(&stmt->errs, "HY009", NULL); /* Invalid use of null pointer */ return SQL_ERROR; } } /* get C type */ sql_src_type = drec_apd->sql_desc_concise_type; if (sql_src_type == SQL_C_DEFAULT) sql_src_type = odbc_sql_to_c_type_default(drec_ipd->sql_desc_concise_type); switch(StrLen_or_Ind) { case SQL_NTS: if (sql_src_type == SQL_C_WCHAR) len = sqlwcslen((SQLWCHAR *) DataPtr); else len = strlen((char *) DataPtr); break; case SQL_NULL_DATA: len = 0; break; case SQL_DEFAULT_PARAM: /* FIXME: use the default if the parameter has one. */ odbc_errs_add(&stmt->errs, "07S01", NULL); /* Invalid use of default parameter */ return SQL_ERROR; default: if (DataPtr && StrLen_or_Ind < 0) { /* * "The argument DataPtr was not a null pointer, and * the argument StrLen_or_Ind was less than 0 * but not equal to SQL_NTS or SQL_NULL_DATA." */ odbc_errs_add(&stmt->errs, "HY090", NULL); return SQL_ERROR; } len = StrLen_or_Ind; break; } if (!blob && len > need_bytes) len = need_bytes; /* copy to destination */ if (blob) { TDS_CHAR *p; int binary_convert = 0; SQLLEN orig_len = len; if (sql_src_type == SQL_C_CHAR || sql_src_type == SQL_C_WCHAR) { switch (tds_get_conversion_type(curcol->column_type, curcol->column_size)) { case SYBBINARY: case SYBVARBINARY: case XSYBBINARY: case XSYBVARBINARY: case SYBLONGBINARY: case SYBIMAGE: if (len && sql_src_type == SQL_C_CHAR && !*((char*)DataPtr+len-1)) --len; if (sql_src_type == SQL_C_WCHAR) len /= sizeof(SQLWCHAR); if (!len) return SQL_SUCCESS; binary_convert = 1; orig_len = len; len = len / 2u + 1u; break; } } if (!len) return SQL_SUCCESS; if (blob->textvalue) p = (TDS_CHAR *) realloc(blob->textvalue, len + curcol->column_cur_size); else { assert(curcol->column_cur_size == 0); p = (TDS_CHAR *) malloc(len); } if (!p) { odbc_errs_add(&stmt->errs, "HY001", NULL); /* Memory allocation error */ return SQL_ERROR; } blob->textvalue = p; p += curcol->column_cur_size; if (binary_convert) { int res; len = orig_len; if (curcol->column_cur_size > 0 && curcol->column_text_sqlputdatainfo) { SQLWCHAR data[2]; data[0] = curcol->column_text_sqlputdatainfo; data[1] = (sql_src_type == SQL_C_CHAR) ? *(unsigned char*)DataPtr : *(SQLWCHAR*)DataPtr; res = odbc_wchar2hex(p, 1, data, 2); if (res < 0) { odbc_convert_err_set(&stmt->errs, res); return SQL_ERROR; } p += res; DataPtr = (SQLPOINTER) (((char*)DataPtr) + (sql_src_type == SQL_C_CHAR ? 1 : sizeof(SQLWCHAR))); --len; } if (len&1) { --len; curcol->column_text_sqlputdatainfo = (sql_src_type == SQL_C_CHAR) ? ((char*)DataPtr)[len] : ((SQLWCHAR*)DataPtr)[len]; } res = (sql_src_type == SQL_C_CHAR) ? tds_char2hex(p, len / 2u, (const TDS_CHAR*) DataPtr, len): odbc_wchar2hex(p, len / 2u, (const SQLWCHAR*) DataPtr, len); if (res < 0) { odbc_convert_err_set(&stmt->errs, res); return SQL_ERROR; } p += res; len = p - (blob->textvalue + curcol->column_cur_size); } else { memcpy(blob->textvalue + curcol->column_cur_size, DataPtr, len); } } else { memcpy(curcol->column_data + curcol->column_cur_size, DataPtr, len); } curcol->column_cur_size += len; if (blob && curcol->column_cur_size > curcol->column_size) curcol->column_size = curcol->column_cur_size; return SQL_SUCCESS; }
static void data_set_type_info(TDSCOLUMN * col, struct _drecord *drec, SQLINTEGER odbc_ver) { const char *type; #define SET_INFO(type, prefix, suffix) do { \ drec->sql_desc_literal_prefix = prefix; \ drec->sql_desc_literal_suffix = suffix; \ drec->sql_desc_type_name = type; \ return; \ } while(0) #define SET_INFO2(type, prefix, suffix, len) do { \ drec->sql_desc_length = (len); \ SET_INFO(type, prefix, suffix); \ } while(0) drec->sql_desc_unsigned = SQL_FALSE; drec->sql_desc_octet_length = drec->sql_desc_length = col->on_server.column_size; switch (tds_get_conversion_type(col->column_type, col->column_size)) { case XSYBCHAR: case SYBCHAR: if (col->on_server.column_type == XSYBNCHAR) SET_INFO2("nchar", "'", "'", col->on_server.column_size / 2); SET_INFO("char", "'", "'"); case XSYBVARCHAR: case SYBVARCHAR: type = "varchar"; if (col->on_server.column_type == SYBNVARCHAR || col->on_server.column_type == XSYBNVARCHAR) { drec->sql_desc_length = col->on_server.column_size / 2u; type = "nvarchar"; } if (is_blob_col(col)) drec->sql_desc_octet_length = drec->sql_desc_length = SQL_SS_LENGTH_UNLIMITED; SET_INFO(type, "'", "'"); case SYBTEXT: if (col->on_server.column_type == SYBNTEXT) SET_INFO2("ntext", "'", "'", col->on_server.column_size / 2); SET_INFO("text", "'", "'"); case SYBBIT: case SYBBITN: drec->sql_desc_unsigned = SQL_TRUE; SET_INFO2("bit", "", "", 1); #if (ODBCVER >= 0x0300) case SYBINT8: /* TODO return numeric for odbc2 and convert bigint to numeric */ SET_INFO2("bigint", "", "", 19); #endif case SYBINT4: SET_INFO2("int", "", "", 10); case SYBINT2: SET_INFO2("smallint", "", "", 5); case SYBUINT1: case SYBINT1: drec->sql_desc_unsigned = SQL_TRUE; SET_INFO2("tinyint", "", "", 3); #if (ODBCVER >= 0x0300) case SYBUINT8: drec->sql_desc_unsigned = SQL_TRUE; /* TODO return numeric for odbc2 and convert bigint to numeric */ SET_INFO2("unsigned bigint", "", "", 19); #endif case SYBUINT4: drec->sql_desc_unsigned = SQL_TRUE; SET_INFO2("unsigned int", "", "", 10); case SYBUINT2: drec->sql_desc_unsigned = SQL_TRUE; SET_INFO2("unsigned smallint", "", "", 5); case SYBREAL: SET_INFO2("real", "", "", odbc_ver == SQL_OV_ODBC3 ? 24 : 7); case SYBFLT8: SET_INFO2("float", "", "", odbc_ver == SQL_OV_ODBC3 ? 53 : 15); case SYBMONEY: drec->sql_desc_octet_length = 21; SET_INFO2("money", "$", "", 19); case SYBMONEY4: drec->sql_desc_octet_length = 12; SET_INFO2("money", "$", "", 10); case SYBDATETIME: drec->sql_desc_octet_length = sizeof(TIMESTAMP_STRUCT); SET_INFO2("datetime", "'", "'", 23); case SYBDATETIME4: drec->sql_desc_octet_length = sizeof(TIMESTAMP_STRUCT); SET_INFO2("datetime", "'", "'", 16); case SYBBINARY: /* handle TIMESTAMP using usertype */ if (col->column_usertype == 80) SET_INFO("timestamp", "0x", ""); SET_INFO("binary", "0x", ""); case SYBIMAGE: SET_INFO("image", "0x", ""); case SYBVARBINARY: if (is_blob_col(col)) drec->sql_desc_octet_length = drec->sql_desc_length = SQL_SS_LENGTH_UNLIMITED; SET_INFO("varbinary", "0x", ""); case SYBNUMERIC: drec->sql_desc_octet_length = col->column_prec + 2; SET_INFO2("numeric", "", "", col->column_prec); case SYBDECIMAL: drec->sql_desc_octet_length = col->column_prec + 2; SET_INFO2("decimal", "", "", col->column_prec); case SYBINTN: case SYBDATETIMN: case SYBFLTN: case SYBMONEYN: assert(0); case SYBVOID: case SYBNTEXT: case SYBNVARCHAR: case XSYBNVARCHAR: case XSYBNCHAR: break; #if (ODBCVER >= 0x0300) case SYBUNIQUE: /* FIXME for Sybase ?? */ SET_INFO2("uniqueidentifier", "'", "'", 36); case SYBVARIANT: SET_INFO("sql_variant", "", ""); break; #endif case SYBMSDATETIMEOFFSET: SET_INFO2("datetimeoffset", "'", "'", col->column_prec + 27); case SYBMSDATETIME2: SET_INFO2("datetime2", "'", "'", col->column_prec + 20); case SYBMSTIME: SET_INFO2("time", "'", "'", col->column_prec + 9); case SYBMSDATE: SET_INFO2("date", "'", "'", 10); case SYBMSXML: SET_INFO("xml", "'", "'"); } SET_INFO("", "", ""); #undef SET_INFO #undef SET_INFO2 }
SQLLEN odbc_tds2sql(TDS_STMT * stmt, TDSCOLUMN *curcol, int srctype, TDS_CHAR * src, TDS_UINT srclen, int desttype, TDS_CHAR * dest, SQLULEN destlen, const struct _drecord *drec_ixd) { TDS_INT nDestSybType; TDS_INT nRetVal = TDS_CONVERT_FAIL; TDSCONTEXT *context = stmt->dbc->env->tds_ctx; CONV_RESULT ores; SQLLEN ret = SQL_NULL_DATA; int i, cplen; int binary_conversion = 0; TDS_CHAR conv_buf[256]; tdsdump_log(TDS_DBG_FUNC, "odbc_tds2sql: src is %d dest = %d\n", srctype, desttype); assert(desttype != SQL_C_DEFAULT); if (curcol) { if (is_blob_col(curcol)) { if (srctype == SYBLONGBINARY && ( curcol->column_usertype == USER_UNICHAR_TYPE || curcol->column_usertype == USER_UNIVARCHAR_TYPE)) srctype = SYBTEXT; if (curcol->column_type == SYBVARIANT) srctype = ((TDSVARIANT *) src)->type; src = ((TDSBLOB *) src)->textvalue; } if (is_variable_type(curcol->column_type)) { src += curcol->column_text_sqlgetdatapos; srclen -= curcol->column_text_sqlgetdatapos; } } nDestSybType = odbc_c_to_server_type(desttype); if (!nDestSybType) { odbc_errs_add(&stmt->errs, "HY003", NULL); return SQL_NULL_DATA; } /* special case for binary type */ if (desttype == SQL_C_BINARY) { tdsdump_log(TDS_DBG_FUNC, "odbc_tds2sql: outputting binary data destlen = %lu \n", (unsigned long) destlen); if (is_numeric_type(srctype)) { desttype = SQL_C_NUMERIC; nDestSybType = SYBNUMERIC; /* prevent buffer overflow */ if (destlen < sizeof(SQL_NUMERIC_STRUCT)) { odbc_errs_add(&stmt->errs, "07006", NULL); return SQL_NULL_DATA; } ores.n.precision = ((TDS_NUMERIC *) src)->precision; ores.n.scale = ((TDS_NUMERIC *) src)->scale; } else { return odbc_convert_to_binary(stmt, curcol, srctype, src, srclen, dest, destlen); } } else if (is_numeric_type(nDestSybType)) { /* TODO use descriptor information (APD) ?? However APD can contain SQL_C_DEFAULT... */ if (drec_ixd) ores.n.precision = drec_ixd->sql_desc_precision; else ores.n.precision = 38; ores.n.scale = 0; } if (is_char_type(srctype)) { if (desttype == SQL_C_CHAR || desttype == SQL_C_WCHAR) return odbc_convert_char(stmt, curcol, src, srclen, desttype, dest, destlen); if (is_unicode_type(srctype)) { /* * convert to single and then process normally. * Here we processed SQL_C_BINARY and SQL_C_*CHAR so only fixed types are left */ i = odbc_tds_convert_wide_iso(curcol, src, srclen, conv_buf, sizeof(conv_buf)); if (i < 0) return SQL_NULL_DATA; src = conv_buf; srclen = i; srctype = SYBVARCHAR; } } if (desttype == SQL_C_WCHAR) destlen /= sizeof(SQLWCHAR); if (desttype == SQL_C_CHAR || desttype == SQL_C_WCHAR) { switch (srctype) { case SYBLONGBINARY: case SYBBINARY: case SYBVARBINARY: case SYBIMAGE: case XSYBBINARY: case XSYBVARBINARY: binary_conversion = 1; if (destlen && !(destlen % 2)) --destlen; } nDestSybType = TDS_CONVERT_CHAR; ores.cc.len = destlen; ores.cc.c = dest; } if (desttype == SQL_C_CHAR || desttype == SQL_C_WCHAR) { char buf[48]; TDSDATEREC when; int prec = 3; const char *fmt = NULL; const TDS_DATETIMEALL *dta = (const TDS_DATETIMEALL *) src; switch (srctype) { case SYBMSDATETIMEOFFSET: case SYBMSDATETIME2: prec = dta->time_prec; case SYBDATETIME: fmt = "%Y-%m-%d %H:%M:%S.%z"; if (prec) break; case SYBDATETIME4: fmt = "%Y-%m-%d %H:%M:%S"; break; case SYBMSTIME: prec = dta->time_prec; fmt = prec ? "%H:%M:%S.%z" : "%H:%M:%S"; break; case SYBMSDATE: fmt = "%Y-%m-%d"; break; } if (!fmt) goto normal_conversion; tds_datecrack(srctype, src, &when); tds_strftime(buf, sizeof(buf), fmt, &when, prec); if (srctype == SYBMSDATETIMEOFFSET) { char sign = '+'; int off = dta->offset; if (off < 0) { sign = '-'; off = -off; } sprintf(buf + strlen(buf), " %c%02d:%02d", sign, off / 60, off % 60); } nRetVal = strlen(buf); memcpy(dest, buf, destlen < nRetVal ? destlen : nRetVal); } else { normal_conversion: nRetVal = tds_convert(context, srctype, src, srclen, nDestSybType, &ores); } if (nRetVal < 0) { odbc_convert_err_set(&stmt->errs, nRetVal); return SQL_NULL_DATA; } switch (desttype) { case SQL_C_CHAR: tdsdump_log(TDS_DBG_FUNC, "odbc_tds2sql: outputting character data destlen = %lu \n", (unsigned long) destlen); ret = nRetVal; /* TODO handle not terminated configuration */ if (destlen > 0) { cplen = (destlen - 1) > nRetVal ? nRetVal : (destlen - 1); assert(cplen >= 0); /* * odbc always terminate but do not overwrite * destination buffer more than needed */ /* update datapos only for binary source (char already handled) */ if (curcol && binary_conversion) curcol->column_text_sqlgetdatapos += cplen / 2; dest[cplen] = 0; } else { /* if destlen == 0 we return only length */ } break; case SQL_C_WCHAR: tdsdump_log(TDS_DBG_FUNC, "odbc_tds2sql: outputting character data destlen = %lu \n", (unsigned long) destlen); ret = nRetVal * sizeof(SQLWCHAR); /* TODO handle not terminated configuration */ if (destlen > 0) { SQLWCHAR *wp = (SQLWCHAR *) dest; SQLCHAR *p = (SQLCHAR *) dest; cplen = (destlen - 1) > nRetVal ? nRetVal : (destlen - 1); assert(cplen >= 0); /* * odbc always terminate but do not overwrite * destination buffer more than needed */ /* update datapos only for binary source (char already handled) */ if (curcol && binary_conversion) curcol->column_text_sqlgetdatapos += cplen / 2; /* convert in place and terminate */ wp[cplen] = 0; while (cplen > 0) { --cplen; wp[cplen] = p[cplen]; } } else { /* if destlen == 0 we return only length */ } break; case SQL_C_TYPE_DATE: case SQL_C_DATE: { TDSDATEREC dr; DATE_STRUCT *dsp = (DATE_STRUCT *) dest; /* * we've already converted the returned value to a SYBMSDATETIME2 * now decompose date into constituent parts... */ tds_datecrack(SYBMSDATETIME2, &(ores.dt), &dr); dsp->year = dr.year; dsp->month = dr.month + 1; dsp->day = dr.day; ret = sizeof(DATE_STRUCT); } break; case SQL_C_TYPE_TIME: case SQL_C_TIME: { TDSDATEREC dr; TIME_STRUCT *tsp = (TIME_STRUCT *) dest; /* * we've already converted the returned value to a SYBMSDATETIME2 * now decompose date into constituent parts... */ tds_datecrack(SYBMSDATETIME2, &(ores.dt), &dr); tsp->hour = dr.hour; tsp->minute = dr.minute; tsp->second = dr.second; ret = sizeof(TIME_STRUCT); } break; case SQL_C_TYPE_TIMESTAMP: case SQL_C_TIMESTAMP: { TDSDATEREC dr; TIMESTAMP_STRUCT *tssp = (TIMESTAMP_STRUCT *) dest; /* * we've already converted the returned value to a SYBMSDATETIME2 * now decompose date into constituent parts... */ tds_datecrack(SYBMSDATETIME2, &(ores.dt), &dr); tssp->year = dr.year; tssp->month = dr.month + 1; tssp->day = dr.day; tssp->hour = dr.hour; tssp->minute = dr.minute; tssp->second = dr.second; tssp->fraction = dr.decimicrosecond * 100u; ret = sizeof(TIMESTAMP_STRUCT); } break; #ifdef SQL_C_SBIGINT case SQL_C_SBIGINT: case SQL_C_UBIGINT: *((TDS_INT8 *) dest) = ores.bi; ret = sizeof(TDS_INT8); break; #endif case SQL_C_LONG: case SQL_C_SLONG: case SQL_C_ULONG: *((TDS_INT *) dest) = ores.i; ret = sizeof(TDS_INT); break; case SQL_C_SHORT: case SQL_C_SSHORT: case SQL_C_USHORT: *((TDS_SMALLINT *) dest) = ores.si; ret = sizeof(TDS_SMALLINT); break; case SQL_C_TINYINT: case SQL_C_STINYINT: case SQL_C_UTINYINT: case SQL_C_BIT: *((TDS_TINYINT *) dest) = ores.ti; ret = sizeof(TDS_TINYINT); break; case SQL_C_DOUBLE: *((TDS_FLOAT *) dest) = ores.f; ret = sizeof(TDS_FLOAT); break; case SQL_C_FLOAT: *((TDS_REAL *) dest) = ores.r; ret = sizeof(TDS_REAL); break; case SQL_C_NUMERIC: { /* ODBC numeric is quite different from TDS one ... */ SQL_NUMERIC_STRUCT *num = (SQL_NUMERIC_STRUCT *) dest; num->precision = ores.n.precision; num->scale = ores.n.scale; num->sign = ores.n.array[0] ^ 1; /* * TODO can be greater than SQL_MAX_NUMERIC_LEN ?? * seeing Sybase manual wire support bigger numeric but currently * DBs so not support such precision */ i = ODBC_MIN(tds_numeric_bytes_per_prec[ores.n.precision] - 1, SQL_MAX_NUMERIC_LEN); memcpy(num->val, ores.n.array + 1, i); tds_swap_bytes(num->val, i); if (i < SQL_MAX_NUMERIC_LEN) memset(num->val + i, 0, SQL_MAX_NUMERIC_LEN - i); ret = sizeof(SQL_NUMERIC_STRUCT); } break; #ifdef SQL_C_GUID case SQL_C_GUID: memcpy(dest, &(ores.u), sizeof(TDS_UNIQUE)); ret = sizeof(TDS_UNIQUE); break; #endif case SQL_C_BINARY: /* type already handled */ assert(desttype != SQL_C_BINARY); default: break; } return ret; }
static int do_query(TDSSOCKET * tds, char *buf, int opt_flags) { int rows = 0; TDSRET rc; int i; TDSCOLUMN *col; int ctype; CONV_RESULT dres; unsigned char *src; TDS_INT srclen; TDS_INT resulttype; struct timeval start, stop; int print_rows = 1; char message[128]; rc = tds_submit_query(tds, buf); if (TDS_FAILED(rc)) { fprintf(stderr, "tds_submit_query() failed\n"); return 1; } while ((rc = tds_process_tokens(tds, &resulttype, NULL, TDS_TOKEN_RESULTS)) == TDS_SUCCESS) { const int stop_mask = TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW|TDS_RETURN_COMPUTE; if (opt_flags & OPT_TIMER) { gettimeofday(&start, NULL); print_rows = 0; } switch (resulttype) { case TDS_ROWFMT_RESULT: if ((!(opt_flags & OPT_NOHEADER)) && tds->current_results) { for (i = 0; i < tds->current_results->num_cols; i++) { if (i) fputs(opt_col_term, stdout); fputs(tds_dstr_cstr(&tds->current_results->columns[i]->column_name), stdout); } fputs(opt_row_term, stdout); } break; case TDS_COMPUTE_RESULT: case TDS_ROW_RESULT: rows = 0; while ((rc = tds_process_tokens(tds, &resulttype, NULL, stop_mask)) == TDS_SUCCESS) { if (resulttype != TDS_ROW_RESULT && resulttype != TDS_COMPUTE_RESULT) break; rows++; if (!tds->current_results) continue; for (i = 0; i < tds->current_results->num_cols; i++) { col = tds->current_results->columns[i]; if (col->column_cur_size < 0) { if (print_rows) { if (i) fputs(opt_col_term, stdout); fputs("NULL", stdout); } continue; } ctype = tds_get_conversion_type(col->column_type, col->column_size); src = col->column_data; if (is_blob_col(col) && col->column_type != SYBVARIANT) src = (unsigned char *) ((TDSBLOB *) src)->textvalue; srclen = col->column_cur_size; if (tds_convert(tds_get_ctx(tds), ctype, (TDS_CHAR *) src, srclen, SYBVARCHAR, &dres) < 0) continue; if (print_rows) { if (i) fputs(opt_col_term, stdout); fputs(dres.c, stdout); } free(dres.c); } if (print_rows) fputs(opt_row_term, stdout); } if (!QUIET) fprintf(stdout, "(%d row%s affected)\n", rows, rows == 1 ? "" : "s"); break; case TDS_STATUS_RESULT: if (!QUIET) printf("(return status = %d)\n", tds->ret_status); break; default: break; } if (opt_flags & OPT_VERSION) { char version[64]; int line = 0; line = tds_version(tds->conn, version); if (line) { TDSMESSAGE msg; memset(&msg, 0, sizeof(TDSMESSAGE)); msg.server = "tsql"; sprintf(message, "using TDS version %s", version); msg.message = message; tsql_handle_message(tds_get_ctx(tds), tds, &msg); } } if (opt_flags & OPT_TIMER) { TDSMESSAGE msg; gettimeofday(&stop, NULL); sprintf(message, "Total time for processing %d rows: %ld msecs\n", rows, (long) ((stop.tv_sec - start.tv_sec) * 1000) + ((stop.tv_usec - start.tv_usec) / 1000)); memset(&msg, 0, sizeof(TDSMESSAGE)); msg.server = "tsql"; msg.message = message; tsql_handle_message(tds_get_ctx(tds), tds, &msg); } } return 0; }
static void data_generic_set_type_info(TDSCOLUMN * col, struct _drecord *drec, SQLINTEGER odbc_ver) { TDS_SERVER_TYPE col_type = col->on_server.column_type; int col_size = col->on_server.column_size; switch (tds_get_conversion_type(col_type, col_size)) { case XSYBNCHAR: drec->sql_desc_concise_type = SQL_WCHAR; drec->sql_desc_display_size = col->on_server.column_size / 2; SET_INFO2("nchar", "'", "'", col->on_server.column_size / 2); case XSYBCHAR: case SYBCHAR: drec->sql_desc_concise_type = SQL_CHAR; drec->sql_desc_display_size = col->on_server.column_size; SET_INFO("char", "'", "'"); /* TODO really sure ?? SYBNVARCHAR sybase only ?? */ case SYBNVARCHAR: case XSYBNVARCHAR: drec->sql_desc_concise_type = SQL_WVARCHAR; drec->sql_desc_display_size = col->on_server.column_size / 2; drec->sql_desc_length = col->on_server.column_size / 2u; if (is_blob_col(col)) { drec->sql_desc_display_size = SQL_SS_LENGTH_UNLIMITED; drec->sql_desc_octet_length = drec->sql_desc_length = SQL_SS_LENGTH_UNLIMITED; } SET_INFO("nvarchar", "'", "'"); case XSYBVARCHAR: case SYBVARCHAR: drec->sql_desc_concise_type = SQL_VARCHAR; drec->sql_desc_display_size = col->on_server.column_size; if (is_blob_col(col)) { drec->sql_desc_display_size = SQL_SS_LENGTH_UNLIMITED; drec->sql_desc_octet_length = drec->sql_desc_length = SQL_SS_LENGTH_UNLIMITED; } SET_INFO("varchar", "'", "'"); case SYBNTEXT: drec->sql_desc_concise_type = SQL_WLONGVARCHAR; drec->sql_desc_display_size = col->on_server.column_size / 2; SET_INFO2("ntext", "'", "'", col->on_server.column_size / 2); case SYBTEXT: drec->sql_desc_concise_type = SQL_LONGVARCHAR; drec->sql_desc_display_size = col->on_server.column_size; SET_INFO("text", "'", "'"); case SYBBIT: case SYBBITN: drec->sql_desc_concise_type = SQL_BIT; drec->sql_desc_display_size = 1; drec->sql_desc_unsigned = SQL_TRUE; SET_INFO2("bit", "", "", 1); #if (ODBCVER >= 0x0300) case SYB5INT8: case SYBINT8: /* TODO return numeric for odbc2 and convert bigint to numeric */ drec->sql_desc_concise_type = SQL_BIGINT; drec->sql_desc_display_size = 20; SET_INFO2("bigint", "", "", 19); #endif case SYBINT4: drec->sql_desc_concise_type = SQL_INTEGER; drec->sql_desc_display_size = 11; /* -1000000000 */ SET_INFO2("int", "", "", 10); case SYBINT2: drec->sql_desc_concise_type = SQL_SMALLINT; drec->sql_desc_display_size = 6; /* -10000 */ SET_INFO2("smallint", "", "", 5); case SYBUINT1: case SYBINT1: drec->sql_desc_unsigned = SQL_TRUE; case SYBSINT1: /* TODO not another type_name ?? */ drec->sql_desc_concise_type = SQL_TINYINT; drec->sql_desc_display_size = 3; /* 255 */ SET_INFO2("tinyint", "", "", 3); #if (ODBCVER >= 0x0300) case SYBUINT8: drec->sql_desc_unsigned = SQL_TRUE; drec->sql_desc_concise_type = SQL_BIGINT; drec->sql_desc_display_size = 20; /* TODO return numeric for odbc2 and convert bigint to numeric */ SET_INFO2("unsigned bigint", "", "", 20); #endif case SYBUINT4: drec->sql_desc_unsigned = SQL_TRUE; drec->sql_desc_concise_type = SQL_INTEGER; drec->sql_desc_display_size = 10; SET_INFO2("unsigned int", "", "", 10); case SYBUINT2: drec->sql_desc_unsigned = SQL_TRUE; drec->sql_desc_concise_type = SQL_SMALLINT; drec->sql_desc_display_size = 5; /* 65535 */ SET_INFO2("unsigned smallint", "", "", 5); case SYBREAL: drec->sql_desc_concise_type = SQL_REAL; drec->sql_desc_display_size = 14; SET_INFO2("real", "", "", odbc_ver == SQL_OV_ODBC3 ? 24 : 7); case SYBFLT8: drec->sql_desc_concise_type = SQL_DOUBLE; drec->sql_desc_display_size = 24; /* FIXME -- what should the correct size be? */ SET_INFO2("float", "", "", odbc_ver == SQL_OV_ODBC3 ? 53 : 15); case SYBMONEY: /* TODO check money format returned by propretary ODBC, scale == 4 but we use 2 digits */ drec->sql_desc_concise_type = SQL_DECIMAL; drec->sql_desc_octet_length = 21; drec->sql_desc_display_size = 21; drec->sql_desc_precision = 19; drec->sql_desc_scale = 4; SET_INFO2("money", "$", "", 19); case SYBMONEY4: drec->sql_desc_concise_type = SQL_DECIMAL; drec->sql_desc_octet_length = 12; drec->sql_desc_display_size = 12; drec->sql_desc_precision = 10; drec->sql_desc_scale = 4; SET_INFO2("money", "$", "", 10); case SYBDATETIME: drec->sql_desc_concise_type = SQL_TYPE_TIMESTAMP; drec->sql_desc_display_size = 23; drec->sql_desc_octet_length = sizeof(TIMESTAMP_STRUCT); drec->sql_desc_precision = 3; drec->sql_desc_scale = 3; drec->sql_desc_datetime_interval_code = SQL_CODE_TIMESTAMP; SET_INFO2("datetime", "'", "'", 23); case SYBDATETIME4: drec->sql_desc_concise_type = SQL_TYPE_TIMESTAMP; /* TODO dependent on precision (decimal second digits) */ /* we always format using yyyy-mm-dd hh:mm:ss[.fff], see convert_tds2sql.c */ drec->sql_desc_display_size = 19; drec->sql_desc_octet_length = sizeof(TIMESTAMP_STRUCT); drec->sql_desc_datetime_interval_code = SQL_CODE_TIMESTAMP; SET_INFO2("datetime", "'", "'", 16); /* The following two types are just Sybase types but as mainly our ODBC * driver is much more compatible with Windows use attributes similar * to MS one. For instance Sybase ODBC returns TIME into a TIME_STRUCT * however this truncate the precision to 0 as TIME does not have * fraction of seconds. Also Sybase ODBC have different concepts for * PRECISION for many types and making these 2 types compatibles with * Sybase would break this driver compatibility. */ case SYBTIME: drec->sql_desc_concise_type = SQL_SS_TIME2; drec->sql_desc_octet_length = sizeof(SQL_SS_TIME2_STRUCT); /* we always format using hh:mm:ss[.fff], see convert_tds2sql.c */ drec->sql_desc_display_size = 12; drec->sql_desc_precision = 3; drec->sql_desc_scale = 3; SET_INFO2("time", "'", "'", 12); case SYBDATE: drec->sql_desc_octet_length = sizeof(DATE_STRUCT); drec->sql_desc_concise_type = SQL_TYPE_DATE; /* we always format using yyyy-mm-dd, see convert_tds2sql.c */ drec->sql_desc_display_size = 10; SET_INFO2("date", "'", "'", 10); case XSYBBINARY: case SYBBINARY: drec->sql_desc_concise_type = SQL_BINARY; drec->sql_desc_display_size = col->column_size * 2; /* handle TIMESTAMP using usertype */ if (col->column_usertype == 80) SET_INFO("timestamp", "0x", ""); SET_INFO("binary", "0x", ""); case SYBLONGBINARY: case SYBIMAGE: drec->sql_desc_concise_type = SQL_LONGVARBINARY; drec->sql_desc_display_size = col->column_size * 2; SET_INFO("image", "0x", ""); case XSYBVARBINARY: case SYBVARBINARY: drec->sql_desc_concise_type = SQL_VARBINARY; drec->sql_desc_display_size = col->column_size * 2; if (is_blob_col(col)) { drec->sql_desc_display_size = SQL_SS_LENGTH_UNLIMITED; drec->sql_desc_octet_length = drec->sql_desc_length = SQL_SS_LENGTH_UNLIMITED; } SET_INFO("varbinary", "0x", ""); case SYBINTN: case SYBDATETIMN: case SYBFLTN: case SYBMONEYN: case SYBUINTN: case SYBTIMEN: case SYBDATEN: assert(0); case SYBVOID: case SYBINTERVAL: case SYBUNITEXT: case SYBXML: case SYBMSUDT: break; #if (ODBCVER >= 0x0300) case SYBUNIQUE: #ifdef SQL_GUID drec->sql_desc_concise_type = SQL_GUID; #else drec->sql_desc_concise_type = SQL_CHAR; #endif drec->sql_desc_display_size = 36; /* FIXME for Sybase ?? */ SET_INFO2("uniqueidentifier", "'", "'", 36); #endif case SYBMSXML: drec->sql_desc_concise_type = SQL_SS_XML; drec->sql_desc_display_size = SQL_SS_LENGTH_UNLIMITED; drec->sql_desc_octet_length = drec->sql_desc_length = SQL_SS_LENGTH_UNLIMITED; SET_INFO("xml", "'", "'"); /* types already handled in other types, just to silent warnings */ case SYBNUMERIC: case SYBDECIMAL: case SYBVARIANT: case SYBMSDATE: case SYBMSTIME: case SYBMSDATETIME2: case SYBMSDATETIMEOFFSET: case SYB5BIGDATETIME: case SYB5BIGTIME: break; } SET_INFO("", "", ""); }
/** * Initialize BCP information. * Query structure of the table to server. * \tds * \param bcpinfo BCP information to initialize. Structure should be allocate * and table name and direction should be already set. */ TDSRET tds_bcp_init(TDSSOCKET *tds, TDSBCPINFO *bcpinfo) { TDSRESULTINFO *resinfo; TDSRESULTINFO *bindinfo = NULL; TDSCOLUMN *curcol; TDS_INT result_type; int i; TDSRET rc; const char *fmt; /* FIXME don't leave state in processing state */ /* TODO quote tablename if needed */ if (bcpinfo->direction != TDS_BCP_QUERYOUT) fmt = "SET FMTONLY ON select * from %s SET FMTONLY OFF"; else fmt = "SET FMTONLY ON %s SET FMTONLY OFF"; if (TDS_FAILED(rc=tds_submit_queryf(tds, fmt, bcpinfo->tablename))) /* TODO return an error ?? */ /* Attempt to use Bulk Copy with a non-existent Server table (might be why ...) */ return rc; /* TODO possibly stop at ROWFMT and copy before going to idle */ /* TODO check what happen if table is not present, cleanup on error */ while ((rc = tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS)) == TDS_SUCCESS) continue; if (TDS_FAILED(rc)) return rc; /* copy the results info from the TDS socket */ if (!tds->res_info) return TDS_FAIL; resinfo = tds->res_info; if ((bindinfo = tds_alloc_results(resinfo->num_cols)) == NULL) { rc = TDS_FAIL; goto cleanup; } bindinfo->row_size = resinfo->row_size; /* Copy the column metadata */ rc = TDS_FAIL; for (i = 0; i < bindinfo->num_cols; i++) { curcol = bindinfo->columns[i]; /* * TODO use memcpy ?? * curcol and resinfo->columns[i] are both TDSCOLUMN. * Why not "curcol = resinfo->columns[i];"? Because the rest of TDSCOLUMN (below column_timestamp) * isn't being used. Perhaps this "upper" part of TDSCOLUMN should be a substructure. * Or, see if the "lower" part is unused (and zeroed out) at this point, and just do one assignment. */ curcol->funcs = resinfo->columns[i]->funcs; curcol->column_type = resinfo->columns[i]->column_type; curcol->column_usertype = resinfo->columns[i]->column_usertype; curcol->column_flags = resinfo->columns[i]->column_flags; if (curcol->column_varint_size == 0) curcol->column_cur_size = resinfo->columns[i]->column_cur_size; else curcol->column_cur_size = -1; curcol->column_size = resinfo->columns[i]->column_size; curcol->column_varint_size = resinfo->columns[i]->column_varint_size; curcol->column_prec = resinfo->columns[i]->column_prec; curcol->column_scale = resinfo->columns[i]->column_scale; curcol->on_server.column_type = resinfo->columns[i]->on_server.column_type; curcol->on_server.column_size = resinfo->columns[i]->on_server.column_size; curcol->char_conv = resinfo->columns[i]->char_conv; if (!tds_dstr_dup(&curcol->column_name, &resinfo->columns[i]->column_name)) goto cleanup; if (!tds_dstr_dup(&curcol->table_column_name, &resinfo->columns[i]->table_column_name)) goto cleanup; curcol->column_nullable = resinfo->columns[i]->column_nullable; curcol->column_identity = resinfo->columns[i]->column_identity; curcol->column_timestamp = resinfo->columns[i]->column_timestamp; memcpy(curcol->column_collation, resinfo->columns[i]->column_collation, 5); if (is_numeric_type(curcol->column_type)) { curcol->bcp_column_data = tds_alloc_bcp_column_data(sizeof(TDS_NUMERIC)); ((TDS_NUMERIC *) curcol->bcp_column_data->data)->precision = curcol->column_prec; ((TDS_NUMERIC *) curcol->bcp_column_data->data)->scale = curcol->column_scale; } else if (bcpinfo->bind_count != 0 /* ctlib */ && is_blob_col(curcol)) { curcol->bcp_column_data = tds_alloc_bcp_column_data(0); } else { curcol->bcp_column_data = tds_alloc_bcp_column_data(MAX(curcol->column_size,curcol->on_server.column_size)); } if (!curcol->bcp_column_data) goto cleanup; } if (!IS_TDS7_PLUS(tds->conn)) { bindinfo->current_row = (unsigned char*) malloc(bindinfo->row_size); if (!bindinfo->current_row) goto cleanup; bindinfo->row_free = tds_bcp_row_free; } if (bcpinfo->identity_insert_on) { rc = tds_submit_queryf(tds, "set identity_insert %s on", bcpinfo->tablename); if (TDS_FAILED(rc)) goto cleanup; /* TODO use tds_process_simple_query */ while ((rc = tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS)) == TDS_SUCCESS) { } if (rc != TDS_NO_MORE_RESULTS) goto cleanup; } bcpinfo->bindinfo = bindinfo; bcpinfo->bind_count = 0; return TDS_SUCCESS; cleanup: tds_free_results(bindinfo); return rc; }
/** * Send one row of data to server * \tds * \param bcpinfo BCP information * \param get_col_data function to call to retrieve data to be sent * \param ignored function to call if we try to send NULL if not allowed (not used) * \param offset passed to get_col_data and null_error to specify the row to get * \return TDS_SUCCESS or TDS_FAIL. */ TDSRET tds_bcp_send_record(TDSSOCKET *tds, TDSBCPINFO *bcpinfo, tds_bcp_get_col_data get_col_data, tds_bcp_null_error null_error, int offset) { TDSCOLUMN *bindcol; int i, start_col = bcpinfo->next_col; TDSRET rc; tdsdump_log(TDS_DBG_FUNC, "tds_bcp_send_bcp_record(%p, %p, %p, %p, %d)\n", tds, bcpinfo, get_col_data, null_error, offset); if (tds->out_flag != TDS_BULK || tds_set_state(tds, TDS_WRITING) != TDS_WRITING) return TDS_FAIL; if (start_col > 0) { bindcol = bcpinfo->bindinfo->columns[start_col - 1]; *bindcol->column_lenbind = MIN((TDS_INT) bindcol->column_bindlen - bcpinfo->text_sent, *bindcol->column_lenbind); tds_put_n(tds, bindcol->column_varaddr, *bindcol->column_lenbind); bcpinfo->text_sent += *bindcol->column_lenbind; if ((TDS_UINT) bcpinfo->text_sent < bindcol->column_bindlen) { return TDS_SUCCESS; /* That's all for now. */ } else if (!IS_TDS7_PLUS(tds->conn)) { bcpinfo->blob_cols++; } bcpinfo->next_col = 0; bcpinfo->text_sent = 0; } if (IS_TDS7_PLUS(tds->conn)) { if (start_col == 0) { tds_put_byte(tds, TDS_ROW_TOKEN); /* 0xd1 */ } for (i = start_col; i < bcpinfo->bindinfo->num_cols; i++) { TDS_INT save_size; unsigned char *save_data; TDSBLOB blob; int /* bool */ has_text = 0; bindcol = bcpinfo->bindinfo->columns[i]; /* * Don't send the (meta)data for timestamp columns or * identity columns unless indentity_insert is enabled. */ if ((!bcpinfo->identity_insert_on && bindcol->column_identity) || bindcol->column_timestamp || !tds_bcp_is_bound(bcpinfo, bindcol)) { continue; } rc = get_col_data(bcpinfo, bindcol, offset); if (rc == TDS_FAIL) { tdsdump_log(TDS_DBG_INFO1, "get_col_data (column %d) failed\n", i + 1); goto cleanup; } else if (rc != TDS_SUCCESS) { /* CS_BLK_HAS_TEXT? */ has_text = 1; } tdsdump_log(TDS_DBG_INFO1, "gotten column %d length %d null %d\n", i + 1, bindcol->bcp_column_data->datalen, bindcol->bcp_column_data->is_null); save_size = bindcol->column_cur_size; save_data = bindcol->column_data; assert(bindcol->column_data == NULL); if (bindcol->bcp_column_data->is_null) { if ( !bindcol->column_nullable && !is_nullable_type(bindcol->on_server.column_type) ) { return TDS_FAIL; } bindcol->column_cur_size = -1; } else if (has_text) { bindcol->column_cur_size = bindcol->bcp_column_data->datalen; } else if (is_blob_col(bindcol)) { bindcol->column_cur_size = bindcol->bcp_column_data->datalen; memset(&blob, 0, sizeof(blob)); blob.textvalue = (TDS_CHAR *) bindcol->bcp_column_data->data; bindcol->column_data = (unsigned char *) &blob; } else { bindcol->column_cur_size = bindcol->bcp_column_data->datalen; bindcol->column_data = bindcol->bcp_column_data->data; } rc = bindcol->funcs->put_data(tds, bindcol, 1); bindcol->column_cur_size = save_size; bindcol->column_data = save_data; if (TDS_FAILED(rc)) goto cleanup; else if (has_text) { bcpinfo->next_col = i + 1; /* bcpinfo->text_sent = 0; */ break; } } } /* IS_TDS7_PLUS */ else { if (start_col == 0) { int row_pos; int row_sz_pos; int var_cols_written = 0; TDS_INT old_record_size = bcpinfo->bindinfo->row_size; unsigned char *record = bcpinfo->bindinfo->current_row; memset(record, '\0', old_record_size); /* zero the rowbuffer */ /* * offset 0 = number of var columns * offset 1 = row number. zeroed (datasever assigns) */ row_pos = 2; rc = TDS_FAIL; if ((row_pos = tds_bcp_add_fixed_columns(bcpinfo, get_col_data, null_error, offset, record, row_pos)) < 0) goto cleanup; row_sz_pos = row_pos; /* potential variable columns to write */ if ((row_pos = tds_bcp_add_variable_columns(bcpinfo, get_col_data, null_error, offset, record, row_pos, &var_cols_written)) < 0) goto cleanup; if (var_cols_written) { TDS_PUT_UA2(&record[row_sz_pos], row_pos); record[0] = var_cols_written; } tdsdump_log(TDS_DBG_INFO1, "old_record_size = %d new size = %d \n", old_record_size, row_pos); tds_put_smallint(tds, row_pos); tds_put_n(tds, record, row_pos); /* row is done, now handle any text/image data */ bcpinfo->blob_cols = 0; } for (i = start_col; i < bcpinfo->bindinfo->num_cols; i++) { bindcol = bcpinfo->bindinfo->columns[i]; if (is_blob_type(bindcol->column_type)) { /* Elide trailing NULLs */ if (bindcol->bcp_column_data->is_null) { int j; for (j = i + 1; j < bcpinfo->bindinfo->num_cols; ++j) { TDSCOLUMN *bindcol2 = bcpinfo->bindinfo->columns[j]; if (is_blob_type(bindcol2->column_type) && !bindcol2->bcp_column_data->is_null) { break; } } if (j == bcpinfo->bindinfo->num_cols) { i = j; break; } } rc = get_col_data(bcpinfo, bindcol, offset); if (rc == TDS_FAIL) goto cleanup; /* unknown but zero */ tds_put_smallint(tds, 0); tds_put_byte(tds, (unsigned char) bindcol->column_type); tds_put_byte(tds, 0xff - bcpinfo->blob_cols); /* * offset of txptr we stashed during variable * column processing */ tds_put_smallint(tds, bindcol->column_textpos); tds_put_int(tds, bindcol->bcp_column_data->datalen); if (rc != TDS_SUCCESS) { /* CS_BLK_HAS_TEXT? */ bcpinfo->next_col = i + 1; /* bcpinfo->text_sent = 0; */ break; } tds_put_n(tds, bindcol->bcp_column_data->data, bindcol->bcp_column_data->datalen); bcpinfo->blob_cols++; } } } if (i == bcpinfo->bindinfo->num_cols) { tds_set_state(tds, TDS_SENDING); bcpinfo->next_col = 0; } return TDS_SUCCESS; cleanup: tds_set_state(tds, TDS_SENDING); return rc; }
/** * Convert parameters to libtds format * @return SQL_SUCCESS, SQL_ERROR or SQL_NEED_DATA */ SQLRETURN odbc_sql2tds(TDS_STMT * stmt, const struct _drecord *drec_ipd, const struct _drecord *drec_apd, TDSCOLUMN *curcol, int compute_row, const TDS_DESC* axd, unsigned int n_row) { TDS_DBC * dbc = stmt->dbc; TDSCONNECTION * conn = dbc->tds_socket->conn; int dest_type, src_type, sql_src_type, res; CONV_RESULT ores; TDSBLOB *blob; char *src, *converted_src; unsigned char *dest; int len; TDS_DATETIMEALL dta; TDS_NUMERIC num; SQL_NUMERIC_STRUCT *sql_num; SQLINTEGER sql_len; int need_data = 0, i; /* TODO handle bindings of char like "{d '2002-11-12'}" */ tdsdump_log(TDS_DBG_INFO2, "type=%d\n", drec_ipd->sql_desc_concise_type); /* what type to convert ? */ dest_type = odbc_sql_to_server_type(conn, drec_ipd->sql_desc_concise_type, drec_ipd->sql_desc_unsigned); if (!dest_type) { odbc_errs_add(&stmt->errs, "07006", NULL); /* Restricted data type attribute violation */ return SQL_ERROR; } tdsdump_log(TDS_DBG_INFO2, "trace\n"); /* get C type */ sql_src_type = drec_apd->sql_desc_concise_type; if (sql_src_type == SQL_C_DEFAULT) sql_src_type = odbc_sql_to_c_type_default(drec_ipd->sql_desc_concise_type); /* TODO what happen for unicode types ?? */ if (is_char_type(dest_type) && sql_src_type == SQL_C_WCHAR) { TDSICONV *conv = conn->char_convs[is_unicode_type(dest_type) ? client2ucs2 : client2server_chardata]; tds_set_param_type(conn, curcol, dest_type); curcol->char_conv = tds_iconv_get(conn, odbc_get_wide_name(conn), conv->to.charset.name); memcpy(curcol->column_collation, conn->collation, sizeof(conn->collation)); } else { #ifdef ENABLE_ODBC_WIDE TDSICONV *conv = conn->char_convs[is_unicode_type(dest_type) ? client2ucs2 : client2server_chardata]; tds_set_param_type(conn, curcol, dest_type); /* use binary format for binary to char */ if (is_char_type(dest_type)) curcol->char_conv = sql_src_type == SQL_C_BINARY ? NULL : tds_iconv_get(conn, tds_dstr_cstr(&dbc->original_charset), conv->to.charset.name); #else tds_set_param_type(conn, curcol, dest_type); /* use binary format for binary to char */ if (sql_src_type == SQL_C_BINARY && is_char_type(dest_type)) curcol->char_conv = NULL; #endif } if (is_numeric_type(curcol->column_type)) { curcol->column_prec = drec_ipd->sql_desc_precision; curcol->column_scale = drec_ipd->sql_desc_scale; } if (drec_ipd->sql_desc_parameter_type != SQL_PARAM_INPUT) curcol->column_output = 1; /* compute destination length */ if (curcol->column_varint_size != 0) { /* curcol->column_size = drec_apd->sql_desc_octet_length; */ /* * TODO destination length should come from sql_desc_length, * however there is the encoding problem to take into account * we should fill destination length after conversion keeping * attention to fill correctly blob/fixed type/variable type */ /* TODO location of this test is correct here ?? */ if (dest_type != SYBUNIQUE && dest_type != SYBBITN && !is_fixed_type(dest_type)) { curcol->column_cur_size = 0; curcol->column_size = drec_ipd->sql_desc_length; if (curcol->column_size < 0) { curcol->on_server.column_size = curcol->column_size = 0x7FFFFFFFl; } else { if (is_unicode_type(dest_type)) curcol->on_server.column_size = curcol->column_size * 2; else curcol->on_server.column_size = curcol->column_size; } } } else if (dest_type != SYBBIT) { /* TODO only a trick... */ tds_set_param_type(conn, curcol, tds_get_null_type(dest_type)); } /* test source type */ /* TODO test intervals */ src_type = odbc_c_to_server_type(sql_src_type); if (!src_type) { odbc_errs_add(&stmt->errs, "07006", NULL); /* Restricted data type attribute violation */ return SQL_ERROR; } /* we have no data to convert, just return */ if (!compute_row) return SQL_SUCCESS; src = drec_apd->sql_desc_data_ptr; if (src && n_row) { SQLLEN len; if (axd->header.sql_desc_bind_type != SQL_BIND_BY_COLUMN) { len = axd->header.sql_desc_bind_type; if (axd->header.sql_desc_bind_offset_ptr) src += *axd->header.sql_desc_bind_offset_ptr; } else { len = odbc_get_octet_len(sql_src_type, drec_apd); if (len < 0) /* TODO sure ? what happen to upper layer ?? */ /* TODO fill error */ return SQL_ERROR; } src += len * n_row; } /* if only output assume input is NULL */ if (drec_ipd->sql_desc_parameter_type == SQL_PARAM_OUTPUT) { sql_len = SQL_NULL_DATA; } else { sql_len = odbc_get_param_len(drec_apd, drec_ipd, axd, n_row); /* special case, MS ODBC handle conversion from "\0" to any to NULL, DBD::ODBC require it */ if (src_type == SYBVARCHAR && sql_len == 1 && drec_ipd->sql_desc_parameter_type == SQL_PARAM_INPUT_OUTPUT && src && *src == 0) { sql_len = SQL_NULL_DATA; } } /* compute source length */ switch (sql_len) { case SQL_NULL_DATA: len = 0; break; case SQL_NTS: /* check that SQLBindParameter::ParameterValuePtr is not zero for input parameters */ if (!src) { odbc_errs_add(&stmt->errs, "HY090", NULL); return SQL_ERROR; } if (sql_src_type == SQL_C_WCHAR) len = sqlwcslen((const SQLWCHAR *) src) * sizeof(SQLWCHAR); else len = strlen(src); break; case SQL_DEFAULT_PARAM: odbc_errs_add(&stmt->errs, "07S01", NULL); /* Invalid use of default parameter */ return SQL_ERROR; break; case SQL_DATA_AT_EXEC: default: len = sql_len; if (sql_len < 0) { /* test for SQL_C_CHAR/SQL_C_BINARY */ switch (sql_src_type) { case SQL_C_CHAR: case SQL_C_WCHAR: case SQL_C_BINARY: break; default: odbc_errs_add(&stmt->errs, "HY090", NULL); return SQL_ERROR; } len = SQL_LEN_DATA_AT_EXEC(sql_len); need_data = 1; /* dynamic length allowed only for BLOB fields */ switch (drec_ipd->sql_desc_concise_type) { case SQL_LONGVARCHAR: case SQL_WLONGVARCHAR: case SQL_LONGVARBINARY: break; default: odbc_errs_add(&stmt->errs, "HY090", NULL); return SQL_ERROR; } } } /* set NULL. For NULLs we don't need to allocate row buffer so avoid it */ if (!need_data) { assert(drec_ipd->sql_desc_parameter_type != SQL_PARAM_OUTPUT || sql_len == SQL_NULL_DATA); if (sql_len == SQL_NULL_DATA) { curcol->column_cur_size = -1; return SQL_SUCCESS; } } switch (dest_type) { case SYBCHAR: case SYBVARCHAR: case XSYBCHAR: case XSYBVARCHAR: case XSYBNVARCHAR: case XSYBNCHAR: case SYBNVARCHAR: case SYBNTEXT: case SYBTEXT: if (!need_data && (sql_src_type == SQL_C_CHAR || sql_src_type == SQL_C_WCHAR || sql_src_type == SQL_C_BINARY)) { if (curcol->column_data && curcol->column_data_free) curcol->column_data_free(curcol); curcol->column_data_free = NULL; if (is_blob_col(curcol)) { /* trick to set blob without freeing it, _odbc_blob_free does not free TDSBLOB->textvalue */ TDSBLOB *blob = (TDSBLOB *) calloc(1, sizeof(TDSBLOB)); if (!blob) { odbc_errs_add(&stmt->errs, "HY001", NULL); return SQL_ERROR; } blob->textvalue = src; curcol->column_data = (TDS_UCHAR*) blob; curcol->column_data_free = _odbc_blob_free; } else { curcol->column_data = (TDS_UCHAR*) src; } curcol->column_size = len; curcol->column_cur_size = len; return SQL_SUCCESS; } } /* allocate given space */ if (!tds_alloc_param_data(curcol)) { odbc_errs_add(&stmt->errs, "HY001", NULL); return SQL_ERROR; } /* fill data with SQLPutData */ if (need_data) { curcol->column_cur_size = 0; return SQL_NEED_DATA; } if (!src) { odbc_errs_add(&stmt->errs, "HY090", NULL); return SQL_ERROR; } /* convert special parameters (not libTDS compatible) */ switch (src_type) { case SYBMSDATETIME2: convert_datetime2server(drec_apd->sql_desc_concise_type, src, &dta); src = (char *) &dta; break; case SYBDECIMAL: case SYBNUMERIC: sql_num = (SQL_NUMERIC_STRUCT *) src; num.precision = sql_num->precision; num.scale = sql_num->scale; num.array[0] = sql_num->sign ^ 1; /* test precision so client do not crash our library */ if (num.precision <= 0 || num.precision > 38 || num.scale > num.precision) /* TODO add proper error */ return SQL_ERROR; i = tds_numeric_bytes_per_prec[num.precision]; memcpy(num.array + 1, sql_num->val, i - 1); tds_swap_bytes(num.array + 1, i - 1); if (i < sizeof(num.array)) memset(num.array + i, 0, sizeof(num.array) - i); src = (char *) # break; /* TODO intervals */ } converted_src = NULL; if (sql_src_type == SQL_C_WCHAR) { converted_src = src = odbc_wstr2str(stmt, src, &len); if (!src) return SQL_ERROR; src_type = SYBVARCHAR; } dest = curcol->column_data; switch ((TDS_SERVER_TYPE) dest_type) { case SYBCHAR: case SYBVARCHAR: case XSYBCHAR: case XSYBVARCHAR: case XSYBNVARCHAR: case XSYBNCHAR: case SYBNVARCHAR: ores.cc.c = (TDS_CHAR*) dest; ores.cc.len = curcol->column_size; res = tds_convert(dbc->env->tds_ctx, src_type, src, len, TDS_CONVERT_CHAR, &ores); if (res > curcol->column_size) res = curcol->column_size; break; case SYBBINARY: case SYBVARBINARY: case XSYBBINARY: case XSYBVARBINARY: ores.cb.ib = (TDS_CHAR*) dest; ores.cb.len = curcol->column_size; res = tds_convert(dbc->env->tds_ctx, src_type, src, len, TDS_CONVERT_BINARY, &ores); if (res > curcol->column_size) res = curcol->column_size; break; case SYBNTEXT: dest_type = SYBTEXT; case SYBTEXT: case SYBLONGBINARY: case SYBIMAGE: res = tds_convert(dbc->env->tds_ctx, src_type, src, len, dest_type, &ores); if (res >= 0) { blob = (TDSBLOB *) dest; free(blob->textvalue); blob->textvalue = ores.ib; } break; case SYBNUMERIC: case SYBDECIMAL: ((TDS_NUMERIC *) dest)->precision = drec_ipd->sql_desc_precision; ((TDS_NUMERIC *) dest)->scale = drec_ipd->sql_desc_scale; case SYBINTN: case SYBINT1: case SYBINT2: case SYBINT4: case SYBINT8: case SYBFLT8: case SYBDATETIME: case SYBBIT: case SYBMONEY4: case SYBMONEY: case SYBDATETIME4: case SYBREAL: case SYBBITN: case SYBFLTN: case SYBMONEYN: case SYBDATETIMN: case SYBSINT1: case SYBUINT2: case SYBUINT4: case SYBUINT8: case SYBUNIQUE: case SYBMSTIME: case SYBMSDATE: case SYBMSDATETIME2: case SYBMSDATETIMEOFFSET: res = tds_convert(dbc->env->tds_ctx, src_type, src, len, dest_type, (CONV_RESULT*) dest); break; default: case SYBVOID: case SYBVARIANT: /* TODO ODBC 3.5 */ assert(0); res = -1; break; } free(converted_src); if (res < 0) { odbc_convert_err_set(&stmt->errs, res); return SQL_ERROR; } curcol->column_cur_size = res; return SQL_SUCCESS; }
static void test(int n, int type) { int rc; TDS_INT result_type; char buf[1024], tmp[1024]; TDSCOLUMN *curcol; char *src; int done_flags; int i; char prefix[32], suffix[32]; sprintf(test_name, "test %d len %d", type, n); /* do a select and check all results */ prefix[0] = 0; suffix[0] = 0; tmp[0] = 0; switch (type) { case 0: strcpy(suffix, "C280C290"); break; case 1: /* try two invalid in different part */ strcpy(prefix, "C480C290"); strcpy(suffix, "C480C290"); break; } for (i = 0; i < n; ++i) sprintf(strchr(tmp, 0), "%02X", 0x30 + (i % 10)); sprintf(buf, "select convert(varchar(255), 0x%s%s%s)", prefix, tmp, suffix); rc = tds_submit_query(tds, buf); if (rc != TDS_SUCCESS) { fprintf(stderr, "tds_submit_query() failed\n"); exit(1); } if (tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS) != TDS_SUCCESS) { fprintf(stderr, "tds_process_tokens() failed\n"); exit(1); } if (result_type != TDS_ROWFMT_RESULT) { fprintf(stderr, "expected row fmt() failed\n"); exit(1); } if (tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS) != TDS_SUCCESS) { fprintf(stderr, "tds_process_tokens() failed\n"); exit(1); } if (result_type != TDS_ROW_RESULT) { fprintf(stderr, "expected row result() failed\n"); exit(1); } /* force tds to convert from utf8 to iso8859-1 (even on Sybase) */ tds_srv_charset_changed(tds->conn, "UTF-8"); tds->current_results->columns[0]->char_conv = tds->conn->char_convs[client2server_chardata]; rc = tds_process_tokens(tds, &result_type, NULL, TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW|TDS_RETURN_COMPUTE); if (rc != TDS_SUCCESS) { fprintf(stderr, "tds_process_tokens() failed\n"); exit(1); } curcol = tds->current_results->columns[0]; src = (char*) curcol->column_data; if (is_blob_col(curcol)) { TDSBLOB *blob = (TDSBLOB *) src; src = blob->textvalue; } prefix[0] = 0; suffix[0] = 0; tmp[0] = 0; switch (type) { case 0: strcpy(suffix, "\x80\x90"); break; case 1: /* try two invalid in different part */ strcpy(prefix, "?\x90"); strcpy(suffix, "?\x90"); /* some platforms replace invalid sequence with a fixed char */ if (invalid_char < 0) invalid_char = (unsigned char) src[0]; prefix[0] = (char) invalid_char; suffix[0] = (char) invalid_char; break; } for (i = 0; i < n; ++i) sprintf(strchr(tmp, 0), "%c", "0123456789"[i % 10]); sprintf(buf, "%s%s%s", prefix, tmp, suffix); if (strlen(buf) != curcol->column_cur_size || strncmp(buf, src, curcol->column_cur_size) != 0) { int l = curcol->column_cur_size; if (l > 1000) l = 1000; strncpy(tmp, src, l); tmp[l] = 0; fprintf(stderr, "Wrong result in %s\n Got: '%s' len %d\n Expected: '%s' len %u\n", test_name, tmp, curcol->column_cur_size, buf, (unsigned int) strlen(buf)); exit(1); } rc = tds_process_tokens(tds, &result_type, NULL, TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW|TDS_RETURN_COMPUTE); if (rc != TDS_SUCCESS || result_type == TDS_ROW_RESULT) { fprintf(stderr, "tds_process_tokens() unexpected return\n"); exit(1); } while ((rc = tds_process_tokens(tds, &result_type, &done_flags, TDS_TOKEN_RESULTS)) == TDS_SUCCESS) { switch (result_type) { case TDS_NO_MORE_RESULTS: break; case TDS_DONE_RESULT: case TDS_DONEPROC_RESULT: case TDS_DONEINPROC_RESULT: if (!(done_flags & TDS_DONE_ERROR)) break; default: fprintf(stderr, "tds_process_tokens() unexpected result_type\n"); exit(1); break; } } }
static void test0(const char *type, ...) { char buf[512]; CONV_RESULT cr; int rc; TDS_INT result_type; int done_flags; va_list ap; struct { const char *value; const char *result; } data[10]; int num_data = 0, i_row; sprintf(buf, "CREATE TABLE #tmp(a %s)", type); exec_query(buf); va_start(ap, type); for (;;) { const char * value = va_arg(ap, const char *); const char * result; if (!value) break; result = va_arg(ap, const char *); if (!result) result = value; data[num_data].value = value; data[num_data].result = result; sprintf(buf, "INSERT INTO #tmp VALUES(CONVERT(%s,'%s'))", type, value); exec_query(buf); ++num_data; } va_end(ap); assert(num_data > 0); /* execute it */ rc = tds_submit_query(tds, "SELECT * FROM #tmp"); if (rc != TDS_SUCCESS) { fprintf(stderr, "tds_submit_query() failed\n"); exit(1); } if (tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS) != TDS_SUCCESS) { fprintf(stderr, "tds_process_tokens() failed\n"); exit(1); } if (result_type != TDS_ROWFMT_RESULT) { fprintf(stderr, "expected row fmt() failed\n"); exit(1); } if (tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS) != TDS_SUCCESS) { fprintf(stderr, "tds_process_tokens() failed\n"); exit(1); } if (result_type != TDS_ROW_RESULT) { fprintf(stderr, "expected row result() failed\n"); exit(1); } i_row = 0; while ((rc = tds_process_tokens(tds, &result_type, NULL, TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW|TDS_RETURN_COMPUTE)) == TDS_SUCCESS && (result_type == TDS_ROW_RESULT || result_type == TDS_COMPUTE_RESULT)) { TDSCOLUMN *curcol = tds->current_results->columns[0]; TDS_CHAR *src = (TDS_CHAR *) curcol->column_data; int conv_type = tds_get_conversion_type(curcol->column_type, curcol->column_size); assert(i_row < num_data); if (is_blob_col(curcol)) { TDSBLOB *blob = (TDSBLOB *) src; src = blob->textvalue; } if (tds_convert(test_context, conv_type, src, curcol->column_cur_size, SYBVARCHAR, &cr) < 0) { fprintf(stderr, "Error converting\n"); g_result = 1; } else { if (strcmp(data[i_row].result, cr.c) != 0) { fprintf(stderr, "Failed! Is \n%s\nShould be\n%s\n", cr.c, data[i_row].result); g_result = 1; } free(cr.c); } ++i_row; } if (rc != TDS_NO_MORE_RESULTS && rc != TDS_SUCCESS) { fprintf(stderr, "tds_process_tokens() unexpected return\n"); exit(1); } while ((rc = tds_process_tokens(tds, &result_type, &done_flags, TDS_TOKEN_RESULTS)) == TDS_SUCCESS) { switch (result_type) { case TDS_NO_MORE_RESULTS: return; case TDS_DONE_RESULT: case TDS_DONEPROC_RESULT: case TDS_DONEINPROC_RESULT: if (!(done_flags & TDS_DONE_ERROR)) break; default: fprintf(stderr, "tds_process_tokens() unexpected result_type\n"); exit(1); break; } } exec_query("DROP TABLE #tmp"); }