static int parse_server(TDS_ERRS *errs, char *server, TDSLOGIN * login) { char *p = (char *) strchr(server, '\\'); if (p) { if (!tds_dstr_copy(&login->instance_name, p+1)) { odbc_errs_add(errs, "HY001", NULL); return 0; } *p = 0; } else { p = (char *) strchr(server, ','); if (p && atoi(p+1) > 0) { login->port = atoi(p+1); *p = 0; } } if (TDS_SUCCEED(tds_lookup_host_set(server, &login->ip_addrs))) if (!tds_dstr_copy(&login->server_host_name, server)) { odbc_errs_add(errs, "HY001", NULL); return 0; } return 1; }
static char* odbc_wstr2str(TDS_STMT * stmt, const char *src, int* len) { int srclen = (*len) / sizeof(SQLWCHAR); char *out = (char *) malloc(srclen + 1), *p; const SQLWCHAR *wp = (const SQLWCHAR *) src; if (!out) { odbc_errs_add(&stmt->errs, "HY001", NULL); return NULL; } /* convert */ p = out; for (; srclen && *wp < 256; --srclen) *p++ = (char) *wp++; /* still characters, wrong format */ if (srclen) { free(out); /* TODO correct error ?? */ odbc_errs_add(&stmt->errs, "07006", NULL); return NULL; } *len = p - out; return out; }
static SQLLEN odbc_convert_to_binary(TDS_STMT * stmt, TDSCOLUMN *curcol, int srctype, TDS_CHAR * src, TDS_UINT srclen, TDS_CHAR * dest, SQLULEN destlen) { SQLLEN ret = srclen; /* special case for MS date/time */ switch (srctype) { case SYBMSTIME: case SYBMSDATE: case SYBMSDATETIME2: case SYBMSDATETIMEOFFSET: return odbc_convert_msdatetime_to_binary(stmt, curcol, srctype, (TDS_DATETIMEALL *) src, dest, destlen); } if (destlen > 0) { size_t cplen = (destlen > srclen) ? srclen : destlen; /* do not NULL terminate binary buffer */ memcpy(dest, src, cplen); if (curcol) curcol->column_text_sqlgetdatapos += cplen; } else { /* if destlen == 0 we return only length */ if (destlen != 0) { odbc_errs_add(&stmt->errs, "07006", NULL); return SQL_NULL_DATA; } } return ret; }
int odbc_build_connect_string(TDS_ERRS *errs, TDS_PARSED_PARAM *params, char **out) { unsigned n; size_t len = 1; char *p; /* compute string size */ for (n = 0; n < ODBC_PARAM_SIZE; ++n) { if (params[n].p) len += strlen(odbc_param_names[n]) + params[n].len + 2; } /* allocate */ p = (char*) malloc(len); if (!p) { odbc_errs_add(errs, "HY001", NULL); return 0; } *out = p; /* build it */ for (n = 0; n < ODBC_PARAM_SIZE; ++n) { if (params[n].p) p += sprintf(p, "%s=%.*s;", odbc_param_names[n], (int) params[n].len, params[n].p); } *p = 0; return 1; }
int parse_prepared_query(struct _hstmt *stmt, int compute_row) { /* try setting this parameter */ TDSPARAMINFO *temp_params; int nparam = stmt->params ? stmt->params->num_cols : 0; if (stmt->prepared_pos) return prepared_rpc(stmt, compute_row); tdsdump_log(TDS_DBG_FUNC, "parsing %d parameters\n", nparam); for (; stmt->param_num <= stmt->param_count; ++nparam, ++stmt->param_num) { /* find bound parameter */ if (stmt->param_num > stmt->apd->header.sql_desc_count || stmt->param_num > stmt->ipd->header.sql_desc_count) { tdsdump_log(TDS_DBG_FUNC, "parse_prepared_query: logic_error: parameter out of bounds: " "%d > %d || %d > %d\n", stmt->param_num, stmt->apd->header.sql_desc_count, stmt->param_num, stmt->ipd->header.sql_desc_count); return SQL_ERROR; } /* add a column to parameters */ if (!(temp_params = tds_alloc_param_result(stmt->params))) { odbc_errs_add(&stmt->errs, "HY001", NULL); return SQL_ERROR; } stmt->params = temp_params; switch (odbc_sql2tds (stmt, &stmt->ipd->records[stmt->param_num - 1], &stmt->apd->records[stmt->param_num - 1], stmt->params->columns[nparam], compute_row, stmt->apd, stmt->curr_param_row)) { case SQL_ERROR: return SQL_ERROR; case SQL_NEED_DATA: return SQL_NEED_DATA; } } return SQL_SUCCESS; }
static int prepared_rpc(struct _hstmt *stmt, int compute_row) { int nparam = stmt->params ? stmt->params->num_cols : 0; const char *p = stmt->prepared_pos - 1; TDSCONNECTION *conn = stmt->dbc->tds_socket->conn; for (;;) { TDSPARAMINFO *temp_params; TDSCOLUMN *curcol; TDS_SERVER_TYPE type; const char *start; while (TDS_ISSPACE(*++p)); if (!*p) return SQL_SUCCESS; /* we have certainly a parameter */ if (!(temp_params = tds_alloc_param_result(stmt->params))) { odbc_errs_add(&stmt->errs, "HY001", NULL); return SQL_ERROR; } stmt->params = temp_params; curcol = temp_params->columns[nparam]; switch (*p) { case ',': if (IS_TDS7_PLUS(conn)) { tds_set_param_type(conn, curcol, SYBVOID); curcol->column_size = curcol->column_cur_size = 0; } else { /* TODO is there a better type ? */ tds_set_param_type(conn, curcol, SYBINTN); curcol->column_size = curcol->on_server.column_size = 4; curcol->column_cur_size = -1; } if (compute_row) if (!tds_alloc_param_data(curcol)) { tds_free_param_result(temp_params); return SQL_ERROR; } --p; break; default: /* add next parameter to list */ start = p; if (!(p = parse_const_param(p, &type))) { tds_free_param_result(temp_params); return SQL_ERROR; } tds_set_param_type(conn, curcol, type); switch (type) { case SYBVARCHAR: curcol->column_size = p - start; break; case SYBVARBINARY: curcol->column_size = (p - start) / 2 -1; break; default: assert(0); case SYBINT4: case SYBFLT8: curcol->column_cur_size = curcol->column_size; break; } curcol->on_server.column_size = curcol->column_size; /* TODO support other type other than VARCHAR, do not strip escape in prepare_call */ if (compute_row) { char *dest; int len; CONV_RESULT cr; if (!tds_alloc_param_data(curcol)) { tds_free_param_result(temp_params); return SQL_ERROR; } dest = (char *) curcol->column_data; switch (type) { case SYBVARCHAR: if (*start != '\'') { memcpy(dest, start, p - start); curcol->column_cur_size = p - start; } else { ++start; for (;;) { if (*start == '\'') ++start; if (start >= p) break; *dest++ = *start++; } curcol->column_cur_size = dest - (char *) curcol->column_data; } break; case SYBVARBINARY: cr.cb.len = curcol->column_size; cr.cb.ib = dest; len = tds_convert(NULL, SYBVARCHAR, start, p - start, TDS_CONVERT_BINARY, &cr); if (len >= 0 && len <= curcol->column_size) curcol->column_cur_size = len; break; case SYBINT4: *((TDS_INT *) dest) = strtol(start, NULL, 10); break; case SYBFLT8: *((TDS_FLOAT *) dest) = strtod(start, NULL); break; default: break; } } --p; break; case '?': /* find binded parameter */ if (stmt->param_num > stmt->apd->header.sql_desc_count || stmt->param_num > stmt->ipd->header.sql_desc_count) { tds_free_param_result(temp_params); /* TODO set error */ return SQL_ERROR; } switch (odbc_sql2tds (stmt, &stmt->ipd->records[stmt->param_num - 1], &stmt->apd->records[stmt->param_num - 1], curcol, compute_row, stmt->apd, stmt->curr_param_row)) { case SQL_ERROR: return SQL_ERROR; case SQL_NEED_DATA: return SQL_NEED_DATA; } ++stmt->param_num; break; } ++nparam; while (TDS_ISSPACE(*++p)); if (!*p || *p != ',') return SQL_SUCCESS; stmt->prepared_pos = (char *) p + 1; } }
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; }
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; }
/** * Convert parameters to libtds format * @return SQL_SUCCESS, SQL_ERROR or SQL_NEED_DATA */ SQLRETURN 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; int dest_type, src_type, sql_src_type, res; CONV_RESULT ores; TDSBLOB *blob; char *src; unsigned char *dest; int len; TDS_DATETIME dt; 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(dbc->tds_socket, drec_ipd->sql_desc_concise_type); if (dest_type == TDS_FAIL) return SQL_ERROR; tdsdump_log(TDS_DBG_INFO2, "trace\n"); /* TODO what happen for unicode types ?? */ tds_set_param_type(dbc->tds_socket, curcol, dest_type); 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->column_size = 0x7FFFFFFFl; } } else if (dest_type != SYBBIT) { /* TODO only a trick... */ tds_set_param_type(dbc->tds_socket, curcol, tds_get_null_type(dest_type)); } /* 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); /* test source type */ /* TODO test intervals */ src_type = odbc_c_to_server_type(sql_src_type); if (src_type == TDS_FAIL) return SQL_ERROR; /* we have no data to convert, just return */ if (!compute_row) return TDS_SUCCEED; 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 ?? */ 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; } len = strlen(src); break; case SQL_DEFAULT_PARAM: case SQL_DATA_AT_EXEC: /* TODO */ return SQL_ERROR; break; 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_BINARY: break; default: 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_LONGVARBINARY: break; default: return SQL_ERROR; } } } /* allocate given space */ if (!tds_alloc_param_data(curcol)) return SQL_ERROR; if (need_data) { curcol->column_cur_size = 0; return SQL_NEED_DATA; } /* set null */ 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 TDS_SUCCEED; } if (!src) { odbc_errs_add(&stmt->errs, "HY090", NULL); return SQL_ERROR; } /* convert special parameters (not libTDS compatible) */ switch (src_type) { case SYBDATETIME: convert_datetime2server(drec_apd->sql_desc_concise_type, src, &dt); src = (char *) &dt; 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 */ } dest = curcol->column_data; switch ((TDS_SERVER_TYPE) dest_type) { case SYBCHAR: case SYBVARCHAR: case XSYBCHAR: case XSYBVARCHAR: 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 SYBTEXT: case SYBLONGBINARY: case SYBIMAGE: res = tds_convert(dbc->env->tds_ctx, src_type, src, len, dest_type, &ores); if (res < 0) return SQL_ERROR; 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: res = tds_convert(dbc->env->tds_ctx, src_type, src, len, dest_type, (CONV_RESULT*) dest); break; default: case XSYBNVARCHAR: case XSYBNCHAR: case SYBNVARCHAR: case SYBNTEXT: case SYBVOID: case SYBVARIANT: /* TODO ODBC 3.5 */ assert(0); res = -1; break; } if (res < 0) return SQL_ERROR; curcol->column_cur_size = res; return SQL_SUCCESS; }
/** * Parse connection string and fill login according * @param connect_string connect string * @param connect_string_end connect string end (pointer to char past last) * @param login where to store connection info * @return 1 if success 0 otherwhise */ int odbc_parse_connect_string(TDS_ERRS *errs, const char *connect_string, const char *connect_string_end, TDSLOGIN * login, TDS_PARSED_PARAM *parsed_params) { const char *p, *end; DSTR *dest_s, value = DSTR_INITIALIZER; enum { CFG_DSN = 1, CFG_SERVER = 2, CFG_SERVERNAME = 4 }; unsigned int cfgs = 0; /* flags for indicate second parse of string */ char option[24]; int trusted = 0; if (parsed_params) memset(parsed_params, 0, sizeof(*parsed_params)*ODBC_PARAM_SIZE); for (p = connect_string; p < connect_string_end && *p;) { int num_param = -1; dest_s = NULL; /* handle empty options */ while (p < connect_string_end && *p == ';') ++p; /* parse option */ end = (const char *) memchr(p, '=', connect_string_end - p); if (!end) break; /* account for spaces between ;'s. */ while (p < end && *p == ' ') ++p; if ((end - p) >= (int) sizeof(option)) option[0] = 0; else { memcpy(option, p, end - p); option[end - p] = 0; } /* parse value */ p = end + 1; if (*p == '{') { ++p; /* search "};" */ end = p; while ((end = (const char *) memchr(end, '}', connect_string_end - end)) != NULL) { if ((end + 1) != connect_string_end && end[1] == ';') break; ++end; } } else { end = (const char *) memchr(p, ';', connect_string_end - p); } if (!end) end = connect_string_end; if (!tds_dstr_copyn(&value, p, end - p)) { odbc_errs_add(errs, "HY001", NULL); return 0; } #define CHK_PARAM(p) (strcasecmp(option, odbc_param_##p) == 0 && (num_param=ODBC_PARAM_##p) >= 0) if (CHK_PARAM(Server)) { /* error if servername or DSN specified */ if ((cfgs & (CFG_DSN|CFG_SERVERNAME)) != 0) { tds_dstr_free(&value); odbc_errs_add(errs, "HY000", "Only one between SERVER, SERVERNAME and DSN can be specified"); return 0; } if (!cfgs) { dest_s = &login->server_name; /* not that safe cast but works -- freddy77 */ if (!parse_server(errs, (char *) tds_dstr_cstr(&value), login)) { tds_dstr_free(&value); return 0; } cfgs = CFG_SERVER; } } else if (CHK_PARAM(Servername)) { if ((cfgs & (CFG_DSN|CFG_SERVER)) != 0) { tds_dstr_free(&value); odbc_errs_add(errs, "HY000", "Only one between SERVER, SERVERNAME and DSN can be specified"); return 0; } if (!cfgs) { odbc_dstr_swap(&login->server_name, &value); tds_read_conf_file(login, tds_dstr_cstr(&login->server_name)); cfgs = CFG_SERVERNAME; p = connect_string; continue; } } else if (CHK_PARAM(DSN)) { if ((cfgs & (CFG_SERVER|CFG_SERVERNAME)) != 0) { tds_dstr_free(&value); odbc_errs_add(errs, "HY000", "Only one between SERVER, SERVERNAME and DSN can be specified"); return 0; } if (!cfgs) { if (!odbc_get_dsn_info(errs, tds_dstr_cstr(&value), login)) { tds_dstr_free(&value); return 0; } cfgs = CFG_DSN; p = connect_string; continue; } } else if (CHK_PARAM(Database)) { dest_s = &login->database; } else if (CHK_PARAM(UID)) { dest_s = &login->user_name; } else if (CHK_PARAM(PWD)) { dest_s = &login->password; } else if (CHK_PARAM(APP)) { dest_s = &login->app_name; } else if (CHK_PARAM(WSID)) { dest_s = &login->client_host_name; } else if (CHK_PARAM(Language)) { tds_parse_conf_section(TDS_STR_LANGUAGE, tds_dstr_cstr(&value), login); } else if (CHK_PARAM(Port)) { tds_parse_conf_section(TDS_STR_PORT, tds_dstr_cstr(&value), login); } else if (CHK_PARAM(TDS_Version)) { tds_parse_conf_section(TDS_STR_VERSION, tds_dstr_cstr(&value), login); } else if (CHK_PARAM(TextSize)) { tds_parse_conf_section(TDS_STR_TEXTSZ, tds_dstr_cstr(&value), login); } else if (CHK_PARAM(PacketSize)) { tds_parse_conf_section(TDS_STR_BLKSZ, tds_dstr_cstr(&value), login); } else if (CHK_PARAM(ClientCharset)) { tds_parse_conf_section(TDS_STR_CLCHARSET, tds_dstr_cstr(&value), login); } else if (CHK_PARAM(DumpFile)) { tds_parse_conf_section(TDS_STR_DUMPFILE, tds_dstr_cstr(&value), login); } else if (CHK_PARAM(DumpFileAppend)) { tds_parse_conf_section(TDS_STR_APPENDMODE, tds_dstr_cstr(&value), login); } else if (CHK_PARAM(DebugFlags)) { tds_parse_conf_section(TDS_STR_DEBUGFLAGS, tds_dstr_cstr(&value), login); } else if (CHK_PARAM(Encryption)) { tds_parse_conf_section(TDS_STR_ENCRYPTION, tds_dstr_cstr(&value), login); } else if (CHK_PARAM(UseNTLMv2)) { tds_parse_conf_section(TDS_STR_USENTLMV2, tds_dstr_cstr(&value), login); } else if (CHK_PARAM(REALM)) { tds_parse_conf_section(TDS_STR_REALM, tds_dstr_cstr(&value), login); } else if (CHK_PARAM(ServerSPN)) { tds_parse_conf_section(TDS_STR_SPN, tds_dstr_cstr(&value), login); } else if (CHK_PARAM(Trusted_Connection)) { trusted = tds_config_boolean(option, tds_dstr_cstr(&value), login); tdsdump_log(TDS_DBG_INFO1, "trusted %s -> %d\n", tds_dstr_cstr(&value), trusted); num_param = -1; /* TODO odbc_param_Address field */ } else if (CHK_PARAM(MARS_Connection)) { if (tds_config_boolean(option, tds_dstr_cstr(&value), login)) login->mars = 1; } else if (CHK_PARAM(AttachDbFilename)) { dest_s = &login->db_filename; } else if (CHK_PARAM(ApplicationIntent)) { const char *readonly_intent; if (strcasecmp(tds_dstr_cstr(&value), "ReadOnly") == 0) { readonly_intent = "yes"; } else if (strcasecmp(tds_dstr_cstr(&value), "ReadWrite") == 0) { readonly_intent = "no"; } else { tdsdump_log(TDS_DBG_ERROR, "Invalid ApplicationIntent %s\n", tds_dstr_cstr(&value)); return 0; } tds_parse_conf_section(TDS_STR_READONLY_INTENT, readonly_intent, login); tdsdump_log(TDS_DBG_INFO1, "Application Intent %s\n", readonly_intent); } if (num_param >= 0 && parsed_params) { parsed_params[num_param].p = p; parsed_params[num_param].len = end - p; } /* copy to destination */ if (dest_s) odbc_dstr_swap(dest_s, &value); p = end; /* handle "" ";.." "};.." cases */ if (p >= connect_string_end) break; if (*p == '}') ++p; ++p; } if (trusted) { if (parsed_params) { parsed_params[ODBC_PARAM_Trusted_Connection].p = "Yes"; parsed_params[ODBC_PARAM_Trusted_Connection].len = 3; parsed_params[ODBC_PARAM_UID].p = NULL; parsed_params[ODBC_PARAM_PWD].p = NULL; } tds_dstr_empty(&login->user_name); tds_dstr_empty(&login->password); } tds_dstr_free(&value); return 1; }
/** * Read connection information from given DSN * @param DSN DSN name * @param login where to store connection info * @return 1 if success 0 otherwhise */ int odbc_get_dsn_info(TDS_ERRS *errs, const char *DSN, TDSLOGIN * login) { char tmp[FILENAME_MAX]; int freetds_conf_less = 1; /* use old servername */ if (myGetPrivateProfileString(DSN, odbc_param_Servername, tmp) > 0) { freetds_conf_less = 0; if (!tds_dstr_copy(&login->server_name, tmp)) { odbc_errs_add(errs, "HY001", NULL); return 0; } tds_read_conf_file(login, tmp); if (myGetPrivateProfileString(DSN, odbc_param_Server, tmp) > 0) { odbc_errs_add(errs, "HY000", "You cannot specify both SERVERNAME and SERVER"); return 0; } if (myGetPrivateProfileString(DSN, odbc_param_Address, tmp) > 0) { odbc_errs_add(errs, "HY000", "You cannot specify both SERVERNAME and ADDRESS"); return 0; } } /* search for server (compatible with ms one) */ if (freetds_conf_less) { int address_specified = 0; if (myGetPrivateProfileString(DSN, odbc_param_Address, tmp) > 0) { address_specified = 1; /* TODO parse like MS */ if (TDS_FAILED(tds_lookup_host_set(tmp, &login->ip_addrs))) { odbc_errs_add(errs, "HY000", "Error parsing ADDRESS attribute"); return 0; } } if (myGetPrivateProfileString(DSN, odbc_param_Server, tmp) > 0) { if (!tds_dstr_copy(&login->server_name, tmp)) { odbc_errs_add(errs, "HY001", NULL); return 0; } if (!address_specified) { if (!parse_server(errs, tmp, login)) return 0; } } } if (myGetPrivateProfileString(DSN, odbc_param_Port, tmp) > 0) tds_parse_conf_section(TDS_STR_PORT, tmp, login); if (myGetPrivateProfileString(DSN, odbc_param_TDS_Version, tmp) > 0) tds_parse_conf_section(TDS_STR_VERSION, tmp, login); if (myGetPrivateProfileString(DSN, odbc_param_Language, tmp) > 0) tds_parse_conf_section(TDS_STR_LANGUAGE, tmp, login); if (tds_dstr_isempty(&login->database) && myGetPrivateProfileString(DSN, odbc_param_Database, tmp) > 0) if (!tds_dstr_copy(&login->database, tmp)) { odbc_errs_add(errs, "HY001", NULL); return 0; } if (myGetPrivateProfileString(DSN, odbc_param_TextSize, tmp) > 0) tds_parse_conf_section(TDS_STR_TEXTSZ, tmp, login); if (myGetPrivateProfileString(DSN, odbc_param_PacketSize, tmp) > 0) tds_parse_conf_section(TDS_STR_BLKSZ, tmp, login); if (myGetPrivateProfileString(DSN, odbc_param_ClientCharset, tmp) > 0) tds_parse_conf_section(TDS_STR_CLCHARSET, tmp, login); if (myGetPrivateProfileString(DSN, odbc_param_DumpFile, tmp) > 0) tds_parse_conf_section(TDS_STR_DUMPFILE, tmp, login); if (myGetPrivateProfileString(DSN, odbc_param_DumpFileAppend, tmp) > 0) tds_parse_conf_section(TDS_STR_APPENDMODE, tmp, login); if (myGetPrivateProfileString(DSN, odbc_param_DebugFlags, tmp) > 0) tds_parse_conf_section(TDS_STR_DEBUGFLAGS, tmp, login); if (myGetPrivateProfileString(DSN, odbc_param_Encryption, tmp) > 0) tds_parse_conf_section(TDS_STR_ENCRYPTION, tmp, login); if (myGetPrivateProfileString(DSN, odbc_param_UseNTLMv2, tmp) > 0) tds_parse_conf_section(TDS_STR_USENTLMV2, tmp, login); if (myGetPrivateProfileString(DSN, odbc_param_REALM, tmp) > 0) tds_parse_conf_section(TDS_STR_REALM, tmp, login); if (myGetPrivateProfileString(DSN, odbc_param_ServerSPN, tmp) > 0) tds_parse_conf_section(TDS_STR_SPN, tmp, login); if (myGetPrivateProfileString(DSN, odbc_param_Trusted_Connection, tmp) > 0 && tds_config_boolean(odbc_param_Trusted_Connection, tmp, login)) { tds_dstr_empty(&login->user_name); tds_dstr_empty(&login->password); } if (myGetPrivateProfileString(DSN, odbc_param_MARS_Connection, tmp) > 0 && tds_config_boolean(odbc_param_MARS_Connection, tmp, login)) { login->mars = 1; } if (myGetPrivateProfileString(DSN, odbc_param_AttachDbFilename, tmp) > 0) tds_parse_conf_section(TDS_STR_DBFILENAME, tmp, login); return 1; }
/** * 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; }