Beispiel #1
0
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;
}
Beispiel #2
0
/**
 * Convert parameters to libtds format
 * @return SQL_SUCCESS, SQL_ERROR or SQL_NEED_DATA
 */
SQLRETURN
sql2tds(TDS_DBC * dbc, struct _drecord *drec_ipd, struct _drecord *drec_apd, TDSPARAMINFO * info, int nparam)
{
	int dest_type, src_type, res;
	CONV_RESULT ores;
	TDSBLOBINFO *blob_info;
	char *src;
	unsigned char *dest;
	TDSCOLINFO *curcol = info->columns[nparam];
	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, "%s:%d type=%d\n", __FILE__, __LINE__, 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, "%s:%d\n", __FILE__, __LINE__);

	/* 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
		 */
		curcol->column_cur_size = 0;
		curcol->column_size = drec_ipd->sql_desc_length;
		if (curcol->column_size < 0)
			curcol->column_size = 0x7FFFFFFFl;
	} else {
		/* TODO only a trick... */
		if (curcol->column_varint_size == 0)
			tds_set_param_type(dbc->tds_socket, curcol, tds_get_null_type(dest_type));
	}

	/* get C type */
	src_type = drec_apd->sql_desc_concise_type;
	if (src_type == SQL_C_DEFAULT)
		src_type = odbc_sql_to_c_type_default(drec_ipd->sql_desc_concise_type);

	/* 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(dbc->tds_socket, drec_apd, drec_ipd);

	/* compute source length */
	switch (sql_len) {
	case SQL_NULL_DATA:
		len = 0;
		break;
	case SQL_NTS:
		len = strlen(drec_apd->sql_desc_data_ptr);
		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 (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;

			/* other trick, set length of destination */
			switch (odbc_sql_to_c_type_default(drec_ipd->sql_desc_concise_type)) {
			case SQL_C_CHAR:
			case SQL_C_BINARY:
				curcol->column_size = len;
				break;
			}
		}
	}

	/* allocate given space */
	if (!tds_alloc_param_row(info, curcol))
		return SQL_ERROR;

	/* test source type */
	/* TODO test intervals */
	src_type = odbc_c_to_server_type(src_type);
	if (src_type == TDS_FAIL)
		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 = 0;
		tds_set_null(info->current_row, nparam);
		return TDS_SUCCEED;
	}

	/* convert special parameters (not libTDS compatible) */
	src = drec_apd->sql_desc_data_ptr;
	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;
		/* TODO test precision so client do not crash our library ?? */
		i = tds_numeric_bytes_per_prec[num.precision];
		memcpy(num.array + 1, sql_num->val, i);
		tds_swap_bytes(num.array + 1, i);
		++i;
		if (i < sizeof(num.array))
			memset(num.array + i, 0, sizeof(num.array) - i);
		src = (char *) &num;

		/* set output */
		/* TODO use descriptors informations ?? */
		ores.n.precision = 18;
		ores.n.scale = 2;
		break;
		/* TODO intervals */
	}

	res = tds_convert(dbc->env->tds_ctx, src_type, src, len, dest_type, &ores);
	if (res < 0)
		return SQL_ERROR;
	tdsdump_log(TDS_DBG_INFO2, "%s:%d\n", __FILE__, __LINE__);

	/* truncate ?? */
	/* TODO what happen for blobs ?? */
	if (res > curcol->column_size)
		res = curcol->column_size;
	curcol->column_cur_size = res;

	/* free allocated memory */
	dest = &info->current_row[curcol->column_offset];
	switch ((TDS_SERVER_TYPE) dest_type) {
	case SYBCHAR:
	case SYBVARCHAR:
	case XSYBCHAR:
	case XSYBVARCHAR:
		memcpy(&info->current_row[curcol->column_offset], ores.c, res);
		free(ores.c);
		break;
	case SYBTEXT:
		blob_info = (TDSBLOBINFO *) dest;
		if (blob_info->textvalue)
			free(blob_info->textvalue);
		blob_info->textvalue = ores.c;
		break;
	case SYBBINARY:
	case SYBVARBINARY:
	case XSYBBINARY:
	case XSYBVARBINARY:
		memcpy(&info->current_row[curcol->column_offset], ores.ib, res);
		free(ores.ib);
		break;
	case SYBLONGBINARY:
	case SYBIMAGE:
		blob_info = (TDSBLOBINFO *) dest;
		if (blob_info->textvalue)
			free(blob_info->textvalue);
		blob_info->textvalue = ores.ib;
		break;
	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 SYBNUMERIC:
	case SYBDECIMAL:
	case SYBFLTN:
	case SYBMONEYN:
	case SYBDATETIMN:
	case SYBSINT1:
	case SYBUINT2:
	case SYBUINT4:
	case SYBUINT8:
	case SYBUNIQUE:
		memcpy(&info->current_row[curcol->column_offset], &ores, res);
		break;
	case XSYBNVARCHAR:
	case XSYBNCHAR:
	case SYBNVARCHAR:
	case SYBNTEXT:
	case SYBVOID:
	case SYBVARIANT:
		/* TODO ODBC 3.5 */
		assert(0);
		break;
	}

	return SQL_SUCCESS;
}
Beispiel #3
0
/**
 * 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 *) &num;
		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;
}
Beispiel #4
0
/**
 * 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 *) &num;
		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;
}
Beispiel #5
0
static SQLLEN
_tdsodbc_dbconvert(TDS_DBC *dbc, int srctype, const TDS_CHAR * src, SQLLEN src_len, int desttype, BYTE * dest, TDSCOLUMN *bindcol)
{
    CONV_RESULT dres;
    SQLLEN ret;
    SQLLEN len;
    SQLLEN destlen = bindcol->column_size;
    TDS_DATETIMEALL dta;
    TDS_NUMERIC num;
    SQL_NUMERIC_STRUCT * sql_num;
    int always_convert = 0;

    assert(src_len >= 0);
    assert(src != NULL);
    assert(dest != NULL);
    assert(destlen > 0);

    tdsdump_log(TDS_DBG_FUNC, "tdsodbc_dbconvert(%p, %d, %p, %d, %d, %p, %d)\n",
                dbc, srctype, src, (int)src_len, desttype, dest, (int)destlen);

    switch (srctype) {
    case SYBMSDATETIME2:
        convert_datetime2server(SQL_C_TYPE_TIMESTAMP, src, &dta);
        dta.time_prec = (destlen - 40) / 2;
        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 -1;
        len = tds_numeric_bytes_per_prec[num.precision];
        memcpy(num.array + 1, sql_num->val, len - 1);
        tds_swap_bytes(num.array + 1, len - 1);
        if (len < sizeof(num.array))
            memset(num.array + len, 0, sizeof(num.array) - len);
        src = (char *) &num;
        always_convert = num.scale != bindcol->column_scale;
        break;
        /* TODO intervals */
    }

    /* oft times we are asked to convert a data type to itself */
    if ((srctype == desttype || is_similar_type(srctype, desttype)) && !always_convert) {
        if (is_char_type(desttype)) {
            ret = _bcp_iconv_helper(dbc, bindcol, src, src_len, (char *)dest, destlen);
        }
        else {
            ret = destlen < src_len ? destlen : src_len;
            memcpy(dest, src, ret);
        }
        return ret;
    }

    tdsdump_log(TDS_DBG_INFO1, "dbconvert() calling tds_convert\n");

    if (is_numeric_type(desttype)) {
        dres.n.precision = bindcol->column_prec;
        dres.n.scale = bindcol->column_scale;
    }
    len = tds_convert(dbc->env->tds_ctx, srctype, src, src_len, desttype, &dres);
    tdsdump_log(TDS_DBG_INFO1, "dbconvert() called tds_convert returned %d\n", (int)len);

    if (len < 0) {
        odbc_convert_err_set(&dbc->errs, len);
        return -1;
    }

    switch (desttype) {
    case SYBBINARY:
    case SYBVARBINARY:
    case SYBIMAGE:
        ret = destlen < len ? destlen : len;
        memcpy(dest, dres.ib, ret);
        free(dres.ib);
        break;
    case SYBINT1:
    case SYBINT2:
    case SYBINT4:
    case SYBINT8:
    case SYBFLT8:
    case SYBREAL:
    case SYBBIT:
    case SYBBITN:
    case SYBMONEY:
    case SYBMONEY4:
    case SYBDATETIME:
    case SYBDATETIME4:
    case SYBNUMERIC:
    case SYBDECIMAL:
    case SYBUNIQUE:
    case SYBMSDATE:
    case SYBMSTIME:
    case SYBMSDATETIME2:
    case SYBMSDATETIMEOFFSET:
        memcpy(dest, &dres, len);
        ret = len;
        break;
    case SYBCHAR:
    case SYBVARCHAR:
    case SYBTEXT:
        ret = _bcp_iconv_helper(dbc, bindcol, dres.c, len, (char *)dest, destlen);
        free(dres.c);
        break;
    default:
        tdsdump_log(TDS_DBG_INFO1, "error: dbconvert(): unrecognized desttype %d \n", desttype);
        ret = -1;
        break;

    }
    return (ret);
}