static int odbc_set_stmt(TDS_STMT * stmt, char **dest, const ODBC_CHAR *sql, int sql_len _WIDE) { char *p; assert(dest == &stmt->prepared_query || dest == &stmt->query); if (sql_len == SQL_NTS) #ifdef ENABLE_ODBC_WIDE sql_len = wide ? sqlwcslen(sql->wide) : strlen(sql->mb); #else sql_len = strlen((const char*) sql); #endif else if (sql_len <= 0)
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; }
/** * 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; }