/* TODO return just constant buffer */ char * odbc_server_to_sql_typename(TDSCOLINFO * col, int odbc_ver) { /* FIXME finish */ switch (tds_get_conversion_type(col->column_type, col->column_size)) { case XSYBCHAR: case SYBCHAR: return (strdup("char")); case XSYBVARCHAR: case SYBVARCHAR: return (strdup("varchar")); case SYBTEXT: return (strdup("text")); case SYBBIT: case SYBBITN: return (strdup("bit")); #if (ODBCVER >= 0x0300) case SYBINT8: /* TODO return numeric for odbc2 and convert bigint to numeric */ return (strdup("bigint")); #endif case SYBINT4: return (strdup("int")); case SYBINT2: return (strdup("smallint")); case SYBINT1: return (strdup("tinyint")); case SYBREAL: return (strdup("real")); case SYBFLT8: return (strdup("float")); case SYBMONEY: case SYBMONEY4: return (strdup("money")); break; case SYBDATETIME: case SYBDATETIME4: return (strdup("datetime")); case SYBBINARY: /* handle TIMESTAMP using usertype */ if (col->column_usertype == 80) return strdup("timestamp"); return (strdup("binary")); case SYBIMAGE: return (strdup("image")); case SYBVARBINARY: return (strdup("varbinary")); case SYBNUMERIC: return (strdup("numeric")); case SYBDECIMAL: return (strdup("decimal")); case SYBINTN: case SYBDATETIMN: case SYBFLTN: case SYBMONEYN: assert(0); case SYBNTEXT: case SYBVOID: case SYBNVARCHAR: case XSYBNVARCHAR: case XSYBNCHAR: break; #if (ODBCVER >= 0x0300) case SYBUNIQUE: /* FIXME for Sybase ?? */ return (strdup("uniqueidentifier")); case SYBVARIANT: /* return (strdup("sql_variant")); */ break; #endif } return (strdup("")); }
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; }
void odbc_set_return_params(struct _hstmt *stmt) { TDSSOCKET *tds = stmt->dbc->tds_socket; TDSPARAMINFO *info = tds->curr_resinfo; TDSCONTEXT *context = stmt->dbc->env->tds_ctx; int i_begin = stmt->prepared_query_is_func ? 1 : 0; int i; int nparam = i_begin; /* I don't understand why but this happen -- freddy77 */ /* TODO check why, put an assert ? */ if (!info) return; for (i = 0; i < info->num_cols; ++i) { struct _drecord *drec_apd, *drec_ipd; TDSCOLINFO *colinfo = info->columns[i]; TDS_CHAR *src; int srclen; SQLINTEGER len; int c_type; /* find next output parameter */ for (;;) { drec_apd = NULL; /* TODO best way to stop */ if (nparam >= stmt->apd->header.sql_desc_count || nparam >= stmt->ipd->header.sql_desc_count) return; drec_apd = &stmt->apd->records[nparam]; drec_ipd = &stmt->ipd->records[nparam]; if (stmt->ipd->records[nparam++].sql_desc_parameter_type != SQL_PARAM_INPUT) break; } /* null parameter ? */ if (tds_get_null(info->current_row, i)) { /* FIXME error if NULL */ if (drec_apd->sql_desc_indicator_ptr) *drec_apd->sql_desc_indicator_ptr = SQL_NULL_DATA; continue; } src = (TDS_CHAR *) & info->current_row[colinfo->column_offset]; if (is_blob_type(colinfo->column_type)) src = ((TDSBLOBINFO *) src)->textvalue; srclen = colinfo->column_cur_size; c_type = drec_apd->sql_desc_concise_type; if (c_type == SQL_C_DEFAULT) c_type = odbc_sql_to_c_type_default(drec_ipd->sql_desc_concise_type); len = convert_tds2sql(context, tds_get_conversion_type(colinfo->column_type, colinfo->column_size), src, srclen, c_type, drec_apd->sql_desc_data_ptr, drec_apd->sql_desc_octet_length); /* TODO error handling */ if (len < 0) return /* SQL_ERROR */ ; if (drec_apd->sql_desc_indicator_ptr) *drec_apd->sql_desc_indicator_ptr = 0; *drec_apd->sql_desc_octet_length_ptr = len; } }
SQLerror Query(SQLrequest* req) { if (!sock) return SQLerror(SQL_BAD_CONN, "Socket was NULL, check if SQL server is running."); /* Pointer to the buffer we screw around with substitution in */ char* query; /* Pointer to the current end of query, where we append new stuff */ char* queryend; /* Total length of the unescaped parameters */ unsigned long maxparamlen, paramcount; /* The length of the longest parameter */ maxparamlen = 0; for(ParamL::iterator i = req->query.p.begin(); i != req->query.p.end(); i++) { if (i->size() > maxparamlen) maxparamlen = i->size(); } /* How many params are there in the query? */ paramcount = count(req->query.q.c_str(), '?'); /* This stores copy of params to be inserted with using numbered params 1;3B*/ ParamL paramscopy(req->query.p); /* To avoid a lot of allocations, allocate enough memory for the biggest the escaped query could possibly be. * sizeofquery + (maxtotalparamlength*2) + 1 * * The +1 is for null-terminating the string */ query = new char[req->query.q.length() + (maxparamlen*paramcount*2) + 1]; queryend = query; for(unsigned long i = 0; i < req->query.q.length(); i++) { if(req->query.q[i] == '?') { /* We found a place to substitute..what fun. * use mssql calls to escape and write the * escaped string onto the end of our query buffer, * then we "just" need to make sure queryend is * pointing at the right place. */ /* Is it numbered parameter? */ bool numbered; numbered = false; /* Numbered parameter number :| */ unsigned int paramnum; paramnum = 0; /* Let's check if it's a numbered param. And also calculate it's number. */ while ((i < req->query.q.length() - 1) && (req->query.q[i+1] >= '0') && (req->query.q[i+1] <= '9')) { numbered = true; ++i; paramnum = paramnum * 10 + req->query.q[i] - '0'; } if (paramnum > paramscopy.size() - 1) { /* index is out of range! */ numbered = false; } if (numbered) { /* Custom escaping for this one. converting ' to '' should make SQL Server happy. Ugly but fast :] */ char* escaped = new char[(paramscopy[paramnum].length() * 2) + 1]; char* escend = escaped; for (std::string::iterator p = paramscopy[paramnum].begin(); p < paramscopy[paramnum].end(); p++) { if (*p == '\'') { *escend = *p; escend++; *escend = *p; } *escend = *p; escend++; } *escend = 0; for (char* n = escaped; *n; n++) { *queryend = *n; queryend++; } delete[] escaped; } else if (req->query.p.size()) { /* Custom escaping for this one. converting ' to '' should make SQL Server happy. Ugly but fast :] */ char* escaped = new char[(req->query.p.front().length() * 2) + 1]; char* escend = escaped; for (std::string::iterator p = req->query.p.front().begin(); p < req->query.p.front().end(); p++) { if (*p == '\'') { *escend = *p; escend++; *escend = *p; } *escend = *p; escend++; } *escend = 0; for (char* n = escaped; *n; n++) { *queryend = *n; queryend++; } delete[] escaped; req->query.p.pop_front(); } else break; } else { *queryend = req->query.q[i]; queryend++; } } *queryend = 0; req->query.q = query; MsSQLResult* res = new MsSQLResult((Module*)mod, req->source, req->id); res->dbid = host.id; res->query = req->query.q; char* msquery = strdup(req->query.q.data()); LoggingMutex->Lock(); ServerInstance->Logs->Log("m_mssql",LOG_DEBUG,"doing Query: %s",msquery); LoggingMutex->Unlock(); if (tds_submit_query(sock, msquery) != TDS_SUCCEED) { std::string error("failed to execute: "+std::string(req->query.q.data())); delete[] query; delete res; free(msquery); return SQLerror(SQL_QSEND_FAIL, error); } delete[] query; free(msquery); int tds_res; while (tds_process_tokens(sock, &tds_res, NULL, TDS_TOKEN_RESULTS) == TDS_SUCCEED) { //ServerInstance->Logs->Log("m_mssql",LOG_DEBUG,"<******> result type: %d", tds_res); //ServerInstance->Logs->Log("m_mssql",LOG_DEBUG,"AFFECTED ROWS: %d", sock->rows_affected); switch (tds_res) { case TDS_ROWFMT_RESULT: break; case TDS_DONE_RESULT: if (sock->rows_affected > -1) { for (int c = 0; c < sock->rows_affected; c++) res->UpdateAffectedCount(); continue; } break; case TDS_ROW_RESULT: while (tds_process_tokens(sock, &tds_res, NULL, TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW) == TDS_SUCCEED) { if (tds_res != TDS_ROW_RESULT) break; if (!sock->current_results) continue; if (sock->res_info->row_count > 0) { int cols = sock->res_info->num_cols; char** name = new char*[MAXBUF]; char** data = new char*[MAXBUF]; for (int j=0; j<cols; j++) { TDSCOLUMN* col = sock->current_results->columns[j]; name[j] = col->column_name; int ctype; int srclen; unsigned char* src; CONV_RESULT dres; ctype = tds_get_conversion_type(col->column_type, col->column_size); #if _TDSVER >= 82 src = col->column_data; #else src = &(sock->current_results->current_row[col->column_offset]); #endif srclen = col->column_cur_size; tds_convert(sock->tds_ctx, ctype, (TDS_CHAR *) src, srclen, SYBCHAR, &dres); data[j] = (char*)dres.ib; } ResultReady(res, cols, data, name); } } break; default: break; } } ResultsMutex->Lock(); results.push_back(res); ResultsMutex->Unlock(); return SQLerror(); }
void tds_check_column_extra(const TDSCOLUMN * column) { int size; TDSCONNECTION conn; int varint_ok; int column_varint_size; assert(column); column_varint_size = column->column_varint_size; /* 8 is for varchar(max) or similar */ assert(column_varint_size == 8 || (column_varint_size <= 5 && column_varint_size != 3)); assert(column->column_scale <= column->column_prec); assert(column->column_prec <= MAXPRECISION); /* I don't like this that much... freddy77 */ if (column->column_type == 0) return; assert(column->funcs); assert(column->column_type > 0); /* check type and server type same or SQLNCHAR -> SQLCHAR */ #define SPECIAL(ttype, server_type, varint) \ if (column->column_type == ttype && column->on_server.column_type == server_type && column_varint_size == varint) {} else SPECIAL(SYBTEXT, XSYBVARCHAR, 8) SPECIAL(SYBTEXT, XSYBNVARCHAR, 8) SPECIAL(SYBIMAGE, XSYBVARBINARY, 8) assert(tds_get_cardinal_type(column->on_server.column_type, column->column_usertype) == column->column_type || (tds_get_conversion_type(column->on_server.column_type, column->column_size) == column->column_type && column_varint_size == 1 && is_fixed_type(column->column_type))); varint_ok = 0; if (column_varint_size == 8) { assert(column->on_server.column_type == XSYBVARCHAR || column->on_server.column_type == XSYBVARBINARY || column->on_server.column_type == XSYBNVARCHAR || column->on_server.column_type == SYBMSXML || column->on_server.column_type == SYBMSUDT); varint_ok = 1; } else if (is_blob_type(column->column_type)) { assert(column_varint_size >= 4); } else if (column->column_type == SYBVARIANT) { assert(column_varint_size == 4); } conn.tds_version = 0x500; varint_ok = varint_ok || tds_get_varint_size(&conn, column->on_server.column_type) == column_varint_size; conn.tds_version = 0x700; varint_ok = varint_ok || tds_get_varint_size(&conn, column->on_server.column_type) == column_varint_size; assert(varint_ok); /* check current size <= size */ if (is_numeric_type(column->column_type)) { /* I don't like that much this difference between numeric and not numeric - freddy77 */ /* TODO what should be the size ?? */ assert(column->column_prec >= 1 && column->column_prec <= MAXPRECISION); assert(column->column_scale <= column->column_prec); /* assert(column->column_cur_size == tds_numeric_bytes_per_prec[column->column_prec] + 2 || column->column_cur_size == -1); */ } else { assert(column->column_cur_size <= column->column_size); } /* check size of fixed type correct */ size = tds_get_size_by_type(column->column_type); assert(size != 0 || column->column_type == SYBVOID); if (size >= 0 && column->column_type != SYBBITN) { /* check macro */ assert(is_fixed_type(column->column_type)); /* check current size */ if (column->column_type != SYBMSDATE) assert(size == column->column_size); /* check cases where server need nullable types */ if (column->column_type != column->on_server.column_type && (column->column_type != SYBINT8 || column->on_server.column_type != SYB5INT8)) { assert(!is_fixed_type(column->on_server.column_type)); assert(column_varint_size == 1); assert(column->column_size == column->column_cur_size || column->column_cur_size == -1); } else { assert(column_varint_size == 0 || (column->column_type == SYBUNIQUE && column_varint_size == 1) || (column->column_type == SYBMSDATE && column_varint_size == 1)); assert(column->column_size == column->column_cur_size || (column->column_type == SYBUNIQUE && column->column_cur_size == -1) || (column->column_type == SYBMSDATE && column->column_cur_size == -1)); } assert(column->column_size == column->on_server.column_size); } else { assert(!is_fixed_type(column->column_type)); assert(is_char_type(column->column_type) || (column->on_server.column_size == column->column_size || column->on_server.column_size == 0)); assert(column_varint_size != 0); } /* check size of nullable types (ie intN) it's supported */ if (tds_get_conversion_type(column->column_type, 4) != column->column_type) { /* check macro */ assert(is_nullable_type(column->column_type)); /* check that size it's correct for this type of nullable */ assert(tds_get_conversion_type(column->column_type, column->column_size) != column->column_type); /* check current size */ assert(column->column_size >= column->column_cur_size || column->column_cur_size == -1); /* check same type and size on server */ assert(column->column_type == column->on_server.column_type); assert(column->column_size == column->on_server.column_size); } }
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 }
/** * Convert type from database to ODBC */ static SQLSMALLINT data_generic_server_to_sql_type(TDSCOLUMN *col) { int col_type = col->on_server.column_type; int col_size = col->on_server.column_size; /* FIXME finish */ switch (tds_get_conversion_type(col_type, col_size)) { case XSYBCHAR: case SYBCHAR: return SQL_CHAR; case XSYBVARCHAR: case SYBVARCHAR: return SQL_VARCHAR; case SYBTEXT: return SQL_LONGVARCHAR; case XSYBNCHAR: return SQL_WCHAR; /* TODO really sure ?? SYBNVARCHAR sybase only ?? */ case SYBNVARCHAR: case XSYBNVARCHAR: return SQL_WVARCHAR; case SYBNTEXT: return SQL_WLONGVARCHAR; case SYBBIT: return SQL_BIT; #if (ODBCVER >= 0x0300) case SYBUINT8: case SYB5INT8: case SYBINT8: /* TODO return numeric for odbc2 and convert bigint to numeric */ return SQL_BIGINT; #endif case SYBINT4: case SYBUINT4: return SQL_INTEGER; case SYBUINT2: case SYBINT2: return SQL_SMALLINT; case SYBUINT1: case SYBSINT1: case SYBINT1: return SQL_TINYINT; case SYBREAL: return SQL_REAL; case SYBFLT8: return SQL_DOUBLE; case SYBMONEY: case SYBMONEY4: return SQL_DECIMAL; case SYBDATETIME: case SYBDATETIME4: #if (ODBCVER >= 0x0300) return SQL_TYPE_TIMESTAMP; #else return SQL_TIMESTAMP; #endif case XSYBBINARY: case SYBBINARY: return SQL_BINARY; case SYBLONGBINARY: case SYBIMAGE: return SQL_LONGVARBINARY; case XSYBVARBINARY: case SYBVARBINARY: return SQL_VARBINARY; #if (ODBCVER >= 0x0300) case SYBUNIQUE: #ifdef SQL_GUID return SQL_GUID; #else return SQL_CHAR; #endif #endif case SYBMSXML: return SQL_CHAR; /* * TODO what should I do with these types ?? * return other types can cause additional problems */ case SYBVOID: case SYBDATE: case SYBDATEN: case SYBINTERVAL: case SYBTIME: case SYBTIMEN: case SYBUNITEXT: case SYBXML: case SYBMSUDT: /* these types are handled by tds_get_conversion_type */ case SYBINTN: case SYBBITN: case SYBFLTN: case SYBMONEYN: case SYBDATETIMN: case SYBUINTN: break; } return SQL_UNKNOWN_TYPE; }
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("", "", ""); }
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 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_SUCCEED) { fprintf(stderr, "tds_submit_query() failed\n"); exit(1); } if (tds_process_tokens(tds, &result_type, NULL, TDS_TOKEN_RESULTS) != TDS_SUCCEED) { 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_SUCCEED) { 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_SUCCEED && (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_type(curcol->column_type)) { 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_SUCCEED) { fprintf(stderr, "tds_process_tokens() unexpected return\n"); exit(1); } while ((rc = tds_process_tokens(tds, &result_type, &done_flags, TDS_TOKEN_RESULTS)) == TDS_SUCCEED) { 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"); }
void tds_check_column_extra(const TDSCOLUMN * column) { int size; TDSCONNECTION conn; int varint_ok; int column_varint_size; assert(column); column_varint_size = column->column_varint_size; /* 8 is for varchar(max) or similar */ assert(column_varint_size == 8 || (column_varint_size <= 5 && column_varint_size != 3)); assert(column->column_scale <= column->column_prec); assert(column->column_prec <= MAXPRECISION); /* I don't like this that much... freddy77 */ if (column->column_type == 0) return; assert(column->funcs); assert(column->column_type > 0); /* specific checks, if true fully checked */ if (column->funcs->check(column)) return; /* check type and server type same or SQLNCHAR -> SQLCHAR */ #define SPECIAL(ttype, server_type, varint) \ if (column->column_type == ttype && column->on_server.column_type == server_type && column_varint_size == varint) {} else SPECIAL(SYBTEXT, XSYBVARCHAR, 8) SPECIAL(SYBTEXT, XSYBNVARCHAR, 8) SPECIAL(SYBIMAGE, XSYBVARBINARY, 8) assert(tds_get_cardinal_type(column->on_server.column_type, column->column_usertype) == column->column_type || (tds_get_conversion_type(column->on_server.column_type, column->column_size) == column->column_type && column_varint_size == 1 && is_fixed_type(column->column_type))); varint_ok = 0; if (column_varint_size == 8) { if (column->on_server.column_type == XSYBVARCHAR || column->on_server.column_type == XSYBVARBINARY || column->on_server.column_type == XSYBNVARCHAR) varint_ok = 1; } else if (is_blob_type(column->column_type)) { assert(column_varint_size >= 4); } else if (column->column_type == SYBVARIANT) { assert(column_varint_size == 4); } conn.tds_version = 0x500; varint_ok = varint_ok || tds_get_varint_size(&conn, column->on_server.column_type) == column_varint_size; conn.tds_version = 0x700; varint_ok = varint_ok || tds_get_varint_size(&conn, column->on_server.column_type) == column_varint_size; assert(varint_ok); assert(!is_numeric_type(column->column_type)); assert(column->column_cur_size <= column->column_size); /* check size of fixed type correct */ size = tds_get_size_by_type(column->column_type); assert(size != 0 || column->column_type == SYBVOID); /* these peculiar types are variable but have only a possible size */ if (size >= 0 && (column->column_type != SYBBITN && column->column_type != SYBDATEN && column->column_type != SYBTIMEN)) { /* check macro */ assert(is_fixed_type(column->column_type)); /* check current size */ assert(size == column->column_size); /* check cases where server need nullable types */ if (column->column_type != column->on_server.column_type && (column->column_type != SYBINT8 || column->on_server.column_type != SYB5INT8)) { assert(!is_fixed_type(column->on_server.column_type)); assert(column_varint_size == 1); assert(column->column_size == column->column_cur_size || column->column_cur_size == -1); } else { assert(column_varint_size == 0 || (column->column_type == SYBUNIQUE && column_varint_size == 1)); assert(column->column_size == column->column_cur_size || (column->column_type == SYBUNIQUE && column->column_cur_size == -1)); } assert(column->column_size == column->on_server.column_size); } else { assert(!is_fixed_type(column->column_type)); assert(is_char_type(column->column_type) || (column->on_server.column_size == column->column_size || column->on_server.column_size == 0)); assert(column_varint_size != 0); } /* check size of nullable types (ie intN) it's supported */ if (tds_get_conversion_type(column->column_type, 4) != column->column_type) { /* check macro */ assert(is_nullable_type(column->column_type)); /* check that size it's correct for this type of nullable */ assert(tds_get_conversion_type(column->column_type, column->column_size) != column->column_type); /* check current size */ assert(column->column_size >= column->column_cur_size || column->column_cur_size == -1); /* check same type and size on server */ assert(column->column_type == column->on_server.column_type); assert(column->column_size == column->on_server.column_size); } }
static TDSRET _bcp_get_col_data(TDSBCPINFO *bcpinfo, TDSCOLUMN *bindcol, int offset) { TDS_TINYINT ti; TDS_SMALLINT si; TDS_INT li; tds_sysdep_int64_type lli; TDS_INT desttype; int coltype; SQLLEN col_len; int data_is_null; SQLLEN bytes_read; int converted_data_size; TDS_CHAR *dataptr; TDS_DBC *dbc = (TDS_DBC *) bcpinfo->parent; tdsdump_log(TDS_DBG_FUNC, "_bcp_get_col_data(%p, %p)\n", bcpinfo, bindcol); dataptr = bindcol->column_varaddr; data_is_null = 0; col_len = SQL_NULL_DATA; /* If a prefix length specified, read the correct amount of data. */ if (bindcol->bcp_prefix_len > 0) { switch (bindcol->bcp_prefix_len) { case 1: memcpy(&ti, dataptr, 1); dataptr += 1; col_len = ti; break; case 2: memcpy(&si, dataptr, 2); dataptr += 2; col_len = si; break; case 4: memcpy(&li, dataptr, 4); dataptr += 4; col_len = li; break; case 8: memcpy(&lli, dataptr, 8); dataptr += 8; col_len = lli; if (lli != col_len) return TDS_FAIL; break; } if (col_len == SQL_NULL_DATA) data_is_null = 1; } /* if (Max) column length specified take that into consideration. */ if (bindcol->column_bindlen == SQL_NULL_DATA) data_is_null = 1; else if (!data_is_null && bindcol->column_bindlen != SQL_VARLEN_DATA) { if (col_len != SQL_NULL_DATA) col_len = ((tds_sysdep_int64_type)bindcol->column_bindlen < col_len) ? bindcol->column_bindlen : col_len; else col_len = bindcol->column_bindlen; } desttype = tds_get_conversion_type(bindcol->column_type, bindcol->column_size); /* Fixed Length data - this overrides anything else specified */ coltype = bindcol->column_bindtype == 0 ? desttype : bindcol->column_bindtype; if (is_fixed_type(coltype)) { col_len = tds_get_size_by_type(coltype); } /* read the data, finally */ if (!data_is_null && bindcol->bcp_term_len > 0) { /* terminated field */ bytes_read = _bcp_get_term_var(dataptr, bindcol->bcp_terminator, bindcol->bcp_term_len); if (col_len != SQL_NULL_DATA) col_len = (bytes_read < col_len) ? bytes_read : col_len; else col_len = bytes_read; } if (data_is_null) { bindcol->bcp_column_data->datalen = 0; bindcol->bcp_column_data->is_null = 1; } else { if ((converted_data_size = _tdsodbc_dbconvert(dbc, coltype, dataptr, col_len, desttype, bindcol->bcp_column_data->data, bindcol)) == -1) { return TDS_FAIL; } bindcol->bcp_column_data->datalen = converted_data_size; bindcol->bcp_column_data->is_null = 0; assert(converted_data_size > 0); } return TDS_SUCCESS; }