in sql-odbc/src/sqlodbc/convert.c [973:1652]
int copy_and_convert_field(StatementClass *stmt, OID field_type, int atttypmod,
void *valuei, SQLSMALLINT fCType, int precision,
PTR rgbValue, SQLLEN cbValueMax, SQLLEN *pcbValue,
SQLLEN *pIndicator) {
CSTR func = "copy_and_convert_field";
const char *value = valuei;
ARDFields *opts = SC_get_ARDF(stmt);
GetDataInfo *gdata = SC_get_GDTI(stmt);
SQLLEN len = 0;
SIMPLE_TIME std_time;
#ifdef HAVE_LOCALTIME_R
struct tm tm;
#endif /* HAVE_LOCALTIME_R */
SQLLEN pcbValueOffset, rgbValueOffset;
char *rgbValueBindRow = NULL;
SQLLEN *pcbValueBindRow = NULL, *pIndicatorBindRow = NULL;
SQLSETPOSIROW bind_row = stmt->bind_row;
int bind_size = opts->bind_size;
int result = COPY_OK;
const ConnectionClass *conn = SC_get_conn(stmt);
BOOL text_bin_handling;
const char *neut_str = value;
char booltemp[3];
char midtemp[64];
GetDataClass *esdc;
if (stmt->current_col >= 0) {
if (stmt->current_col >= opts->allocated) {
return SQL_ERROR;
}
if (gdata->allocated != opts->allocated)
extend_getdata_info(gdata, opts->allocated, TRUE);
esdc = &gdata->gdata[stmt->current_col];
if (esdc->data_left == -2)
esdc->data_left = (cbValueMax > 0) ? 0 : -1; /* This seems to be *
* needed by ADO ? */
if (esdc->data_left == 0) {
if (esdc->ttlbuf != NULL) {
free(esdc->ttlbuf);
esdc->ttlbuf = NULL;
esdc->ttlbuflen = 0;
}
esdc->data_left = -2; /* needed by ADO ? */
return COPY_NO_DATA_FOUND;
}
}
/*---------
* rgbValueOffset is *ONLY* for character and binary data.
* pcbValueOffset is for computing any pcbValue location
*---------
*/
if (bind_size > 0)
pcbValueOffset = rgbValueOffset = (bind_size * bind_row);
else {
pcbValueOffset = bind_row * sizeof(SQLLEN);
rgbValueOffset = bind_row * cbValueMax;
}
/*
* The following is applicable in case bind_size > 0
* or the fCType is of variable length.
*/
if (rgbValue)
rgbValueBindRow = (char *)rgbValue + rgbValueOffset;
if (pcbValue)
pcbValueBindRow = LENADDR_SHIFT(pcbValue, pcbValueOffset);
if (pIndicator) {
pIndicatorBindRow = (SQLLEN *)((char *)pIndicator + pcbValueOffset);
*pIndicatorBindRow = 0;
}
memset(&std_time, 0, sizeof(SIMPLE_TIME));
MYLOG(OPENSEARCH_DEBUG,
"field_type = %d, fctype = %d, value = '%s', cbValueMax=" FORMAT_LEN
"\n",
field_type, fCType, (value == NULL) ? "<NULL>" : value, cbValueMax);
if (!value) {
/*
* handle a null just by returning SQL_NULL_DATA in pcbValue, and
* doing nothing to the buffer.
*/
if (pIndicator) {
*pIndicatorBindRow = SQL_NULL_DATA;
return COPY_OK;
} else {
SC_set_error(stmt, STMT_RETURN_NULL_WITHOUT_INDICATOR,
"StrLen_or_IndPtr was a null pointer and NULL data "
"was retrieved",
func);
return SQL_ERROR;
}
}
if (stmt->hdbc->DataSourceToDriver != NULL) {
size_t length = strlen(value);
stmt->hdbc->DataSourceToDriver(stmt->hdbc->translation_option, SQL_CHAR,
valuei, (SDWORD)length, valuei,
(SDWORD)length, NULL, NULL, 0, NULL);
}
/*
* First convert any specific OpenSearch types into more useable data.
*
* NOTE: Conversions from ES char/varchar of a date/time/timestamp value
* to SQL_C_DATE,SQL_C_TIME, SQL_C_TIMESTAMP not supported
*/
switch (field_type) {
/*
* $$$ need to add parsing for date/time/timestamp strings in
* OPENSEARCH_TYPE_CHAR,VARCHAR $$$
*/
case OPENSEARCH_TYPE_DATE:
sscanf(value, "%4d-%2d-%2d", &std_time.y, &std_time.m, &std_time.d);
break;
case OPENSEARCH_TYPE_TIME: {
BOOL bZone = FALSE; /* time zone stuff is unreliable */
int zone;
timestamp2stime(value, &std_time, &bZone, &zone);
} break;
case OPENSEARCH_TYPE_ABSTIME:
case OPENSEARCH_TYPE_DATETIME:
case OPENSEARCH_TYPE_TIMESTAMP_NO_TMZONE:
case OPENSEARCH_TYPE_TIMESTAMP:
std_time.fr = 0;
std_time.infinity = 0;
if (strnicmp(value, INFINITY_STRING, 8) == 0) {
std_time.infinity = 1;
std_time.m = 12;
std_time.d = 31;
std_time.y = 9999;
std_time.hh = 23;
std_time.mm = 59;
std_time.ss = 59;
}
if (strnicmp(value, MINFINITY_STRING, 9) == 0) {
std_time.infinity = -1;
std_time.m = 1;
std_time.d = 1;
// std_time.y = -4713;
std_time.y = -9999;
std_time.hh = 0;
std_time.mm = 0;
std_time.ss = 0;
}
if (strnicmp(value, "invalid", 7) != 0) {
BOOL bZone = field_type != OPENSEARCH_TYPE_TIMESTAMP_NO_TMZONE;
int zone;
/*
* sscanf(value, "%4d-%2d-%2d %2d:%2d:%2d", &std_time.y,
* &std_time.m, &std_time.d, &std_time.hh, &std_time.mm,
* &std_time.ss);
*/
bZone = FALSE; /* time zone stuff is unreliable */
timestamp2stime(value, &std_time, &bZone, &zone);
MYLOG(OPENSEARCH_ALL, "2stime fr=%d\n", std_time.fr);
} else {
/*
* The timestamp is invalid so set something conspicuous,
* like the epoch
*/
struct tm *tim;
time_t t = 0;
#ifdef HAVE_LOCALTIME_R
tim = localtime_r(&t, &tm);
#else
tim = localtime(&t);
#endif /* HAVE_LOCALTIME_R */
std_time.m = tim->tm_mon + 1;
std_time.d = tim->tm_mday;
std_time.y = tim->tm_year + 1900;
std_time.hh = tim->tm_hour;
std_time.mm = tim->tm_min;
std_time.ss = tim->tm_sec;
}
break;
case OPENSEARCH_TYPE_BOOL: { /* change T/F to 1/0 */
switch (((char *)value)[0]) {
case 'f':
case 'F':
case 'n':
case 'N':
case '0':
STRCPY_FIXED(booltemp, "0");
break;
default:
STRCPY_FIXED(booltemp, "1");
}
neut_str = booltemp;
} break;
/* This is for internal use by SQLStatistics() */
case OPENSEARCH_TYPE_INT2VECTOR:
if (SQL_C_DEFAULT == fCType) {
int i, nval, maxc;
const char *vp;
/* this is an array of eight integers */
short *short_array = (short *)rgbValueBindRow, shortv;
maxc = 0;
if (NULL != short_array)
maxc = (int)cbValueMax / sizeof(short);
vp = value;
nval = 0;
MYLOG(OPENSEARCH_DEBUG, "index=(");
for (i = 0;; i++) {
if (sscanf(vp, "%hi", &shortv) != 1)
break;
MYPRINTF(0, " %hi", shortv);
nval++;
if (nval < maxc)
short_array[i + 1] = shortv;
/* skip the current token */
while (IS_NOT_SPACE(*vp))
vp++;
/* and skip the space to the next token */
while ((*vp != '\0') && (isspace(*vp)))
vp++;
if (*vp == '\0')
break;
}
MYPRINTF(0, ") nval = %i\n", nval);
if (maxc > 0)
short_array[0] = (short)nval;
/* There is no corresponding fCType for this. */
len = (nval + 1) * sizeof(short);
if (pcbValue)
*pcbValueBindRow = len;
if (len <= cbValueMax)
return COPY_OK; /* dont go any further or the data will be
* trashed */
else
return COPY_RESULT_TRUNCATED;
}
break;
/*
* This is a large object OID, which is used to store
* LONGVARBINARY objects.
*/
case OPENSEARCH_TYPE_LO_UNDEFINED:
return convert_lo(stmt, value, fCType, rgbValueBindRow, cbValueMax,
pcbValueBindRow);
case 0:
break;
default:
if (field_type
== (OID)stmt->hdbc
->lobj_type /* hack until permanent type available */
|| (OPENSEARCH_TYPE_OID == field_type && SQL_C_BINARY == fCType
&& conn->lo_is_domain))
return convert_lo(stmt, value, fCType, rgbValueBindRow,
cbValueMax, pcbValueBindRow);
}
/* Change default into something useable */
if (fCType == SQL_C_DEFAULT) {
fCType = opensearchtype_attr_to_ctype(conn, field_type, atttypmod);
#ifdef UNICODE_SUPPORT
if (fCType == SQL_C_WCHAR && CC_default_is_c(conn))
fCType = SQL_C_CHAR;
#endif
MYLOG(OPENSEARCH_DEBUG, ", SQL_C_DEFAULT: fCType = %d\n", fCType);
}
text_bin_handling = FALSE;
switch (fCType) {
case INTERNAL_ASIS_TYPE:
#ifdef UNICODE_SUPPORT
case SQL_C_WCHAR:
#endif /* UNICODE_SUPPORT */
case SQL_C_CHAR:
text_bin_handling = TRUE;
break;
case SQL_C_BINARY:
switch (field_type) {
case OPENSEARCH_TYPE_UNKNOWN:
case OPENSEARCH_TYPE_BPCHAR:
case OPENSEARCH_TYPE_VARCHAR:
case OPENSEARCH_TYPE_TEXT:
case OPENSEARCH_TYPE_XML:
case OPENSEARCH_TYPE_BPCHARARRAY:
case OPENSEARCH_TYPE_VARCHARARRAY:
case OPENSEARCH_TYPE_TEXTARRAY:
case OPENSEARCH_TYPE_XMLARRAY:
case OPENSEARCH_TYPE_BYTEA:
text_bin_handling = TRUE;
break;
}
break;
}
if (text_bin_handling) {
BOOL pre_convert = TRUE;
int midsize = sizeof(midtemp);
int i;
/* Special character formatting as required */
/*
* These really should return error if cbValueMax is not big
* enough.
*/
switch (field_type) {
case OPENSEARCH_TYPE_DATE:
len = SPRINTF_FIXED(midtemp, "%.4d-%.2d-%.2d", std_time.y,
std_time.m, std_time.d);
break;
case OPENSEARCH_TYPE_TIME:
len = SPRINTF_FIXED(midtemp, "%.2d:%.2d:%.2d", std_time.hh,
std_time.mm, std_time.ss);
if (std_time.fr > 0) {
int wdt;
int fr = effective_fraction(std_time.fr, &wdt);
char *fraction = NULL;
len = sprintf(fraction, ".%0*d", wdt, fr);
strcat(midtemp, fraction);
}
break;
case OPENSEARCH_TYPE_ABSTIME:
case OPENSEARCH_TYPE_DATETIME:
case OPENSEARCH_TYPE_TIMESTAMP_NO_TMZONE:
case OPENSEARCH_TYPE_TIMESTAMP:
len = stime2timestamp(&std_time, midtemp, midsize, FALSE,
(int)(midsize - 19 - 2));
break;
case OPENSEARCH_TYPE_UUID:
len = strlen(neut_str);
for (i = 0; i < len && i < midsize - 2; i++)
midtemp[i] = (char)toupper((UCHAR)neut_str[i]);
midtemp[i] = '\0';
MYLOG(OPENSEARCH_DEBUG, "OPENSEARCH_TYPE_UUID: rgbValueBindRow = '%s'\n",
rgbValueBindRow);
break;
/*
* Currently, data is SILENTLY TRUNCATED for BYTEA and
* character data types if there is not enough room in
* cbValueMax because the driver can't handle multiple
* calls to SQLGetData for these, yet. Most likely, the
* buffer passed in will be big enough to handle the
* maximum limit of OpenSearch, anyway.
*
* LongVarBinary types are handled correctly above, observing
* truncation and all that stuff since there is
* essentially no limit on the large object used to store
* those.
*/
case OPENSEARCH_TYPE_BYTEA: /* convert binary data to hex strings
* (i.e, 255 = "FF") */
default:
pre_convert = FALSE;
}
if (pre_convert)
neut_str = midtemp;
result = convert_text_field_to_sql_c(
gdata, stmt->current_col, neut_str, field_type, fCType,
rgbValueBindRow, cbValueMax, conn, &len);
} else {
SQLGUID g;
/*
* for SQL_C_CHAR, it's probably ok to leave currency symbols in.
* But to convert to numeric types, it is necessary to get rid of
* those.
*/
if (field_type == OPENSEARCH_TYPE_MONEY) {
if (convert_money(neut_str, midtemp, sizeof(midtemp)))
neut_str = midtemp;
else {
MYLOG(OPENSEARCH_DEBUG, "couldn't convert money type to %d\n", fCType);
return COPY_UNSUPPORTED_TYPE;
}
}
switch (fCType) {
case SQL_C_DATE:
case SQL_C_TYPE_DATE: /* 91 */
len = 6;
{
DATE_STRUCT *ds;
struct tm *tim;
if (bind_size > 0)
ds = (DATE_STRUCT *)rgbValueBindRow;
else
ds = (DATE_STRUCT *)rgbValue + bind_row;
/*
* Initialize date in case conversion destination
* expects date part from this source time data.
* A value may be partially set here, so do some
* sanity checks on the existing values before
* setting them.
*/
tim = SC_get_localtime(stmt);
if (std_time.m == 0)
std_time.m = tim->tm_mon + 1;
if (std_time.d == 0)
std_time.d = tim->tm_mday;
if (std_time.y == 0)
std_time.y = tim->tm_year + 1900;
ds->year = (SQLSMALLINT)std_time.y;
ds->month = (SQLUSMALLINT)std_time.m;
ds->day = (SQLUSMALLINT)std_time.d;
}
break;
case SQL_C_TIME:
case SQL_C_TYPE_TIME: /* 92 */
len = 6;
{
TIME_STRUCT *ts;
if (bind_size > 0)
ts = (TIME_STRUCT *)rgbValueBindRow;
else
ts = (TIME_STRUCT *)rgbValue + bind_row;
ts->hour = (SQLUSMALLINT)std_time.hh;
ts->minute = (SQLUSMALLINT)std_time.mm;
ts->second = (SQLUSMALLINT)std_time.ss;
}
break;
case SQL_C_TIMESTAMP:
case SQL_C_TYPE_TIMESTAMP: /* 93 */
len = 16;
{
struct tm *tim;
TIMESTAMP_STRUCT *ts;
if (bind_size > 0)
ts = (TIMESTAMP_STRUCT *)rgbValueBindRow;
else
ts = (TIMESTAMP_STRUCT *)rgbValue + bind_row;
/*
* Initialize date in case conversion destination
* expects date part from this source time data.
* A value may be partially set here, so do some
* sanity checks on the existing values before
* setting them.
*/
tim = SC_get_localtime(stmt);
if (std_time.m == 0)
std_time.m = tim->tm_mon + 1;
if (std_time.d == 0)
std_time.d = tim->tm_mday;
if (std_time.y == 0)
std_time.y = tim->tm_year + 1900;
ts->year = (SQLSMALLINT)std_time.y;
ts->month = (SQLUSMALLINT)std_time.m;
ts->day = (SQLUSMALLINT)std_time.d;
ts->hour = (SQLUSMALLINT)std_time.hh;
ts->minute = (SQLUSMALLINT)std_time.mm;
ts->second = (SQLUSMALLINT)std_time.ss;
ts->fraction = (SQLUINTEGER)std_time.fr;
}
break;
case SQL_C_BIT:
len = 1;
if (bind_size > 0)
*((UCHAR *)rgbValueBindRow) = (UCHAR)atoi(neut_str);
else
*((UCHAR *)rgbValue + bind_row) = (UCHAR)atoi(neut_str);
MYLOG(99,
"SQL_C_BIT: bind_row = " FORMAT_POSIROW
" val = %d, cb = " FORMAT_LEN ", rgb=%d\n",
bind_row, atoi(neut_str), cbValueMax,
*((UCHAR *)rgbValue));
break;
case SQL_C_STINYINT:
case SQL_C_TINYINT:
len = 1;
if (bind_size > 0)
*((SCHAR *)rgbValueBindRow) = (SCHAR)atoi(neut_str);
else
*((SCHAR *)rgbValue + bind_row) = (SCHAR)atoi(neut_str);
break;
case SQL_C_UTINYINT:
len = 1;
if (bind_size > 0)
*((UCHAR *)rgbValueBindRow) = (UCHAR)atoi(neut_str);
else
*((UCHAR *)rgbValue + bind_row) = (UCHAR)atoi(neut_str);
break;
case SQL_C_FLOAT:
set_client_decimal_point((char *)neut_str);
len = 4;
if (bind_size > 0)
*((SFLOAT *)rgbValueBindRow) =
(SFLOAT)get_double_value(neut_str);
else
*((SFLOAT *)rgbValue + bind_row) =
(SFLOAT)get_double_value(neut_str);
break;
case SQL_C_DOUBLE:
set_client_decimal_point((char *)neut_str);
len = 8;
if (bind_size > 0)
*((SDOUBLE *)rgbValueBindRow) =
(SDOUBLE)get_double_value(neut_str);
else
*((SDOUBLE *)rgbValue + bind_row) =
(SDOUBLE)get_double_value(neut_str);
break;
case SQL_C_NUMERIC: {
SQL_NUMERIC_STRUCT *ns;
BOOL overflowed;
if (bind_size > 0)
ns = (SQL_NUMERIC_STRUCT *)rgbValueBindRow;
else
ns = (SQL_NUMERIC_STRUCT *)rgbValue + bind_row;
parse_to_numeric_struct(neut_str, ns, &overflowed);
if (overflowed)
result = COPY_RESULT_TRUNCATED;
} break;
case SQL_C_SSHORT:
case SQL_C_SHORT:
len = 2;
if (bind_size > 0)
*((SQLSMALLINT *)rgbValueBindRow) =
(SQLSMALLINT)atoi(neut_str);
else
*((SQLSMALLINT *)rgbValue + bind_row) =
(SQLSMALLINT)atoi(neut_str);
break;
case SQL_C_USHORT:
len = 2;
if (bind_size > 0)
*((SQLUSMALLINT *)rgbValueBindRow) =
(SQLUSMALLINT)atoi(neut_str);
else
*((SQLUSMALLINT *)rgbValue + bind_row) =
(SQLUSMALLINT)atoi(neut_str);
break;
case SQL_C_SLONG:
case SQL_C_LONG:
len = 4;
if (bind_size > 0)
*((SQLINTEGER *)rgbValueBindRow) = atol(neut_str);
else
*((SQLINTEGER *)rgbValue + bind_row) = atol(neut_str);
break;
case SQL_C_ULONG:
len = 4;
if (bind_size > 0)
*((SQLUINTEGER *)rgbValueBindRow) = ATOI32U(neut_str);
else
*((SQLUINTEGER *)rgbValue + bind_row) = ATOI32U(neut_str);
break;
#ifdef ODBCINT64
case SQL_C_SBIGINT:
len = 8;
if (bind_size > 0)
*((SQLBIGINT *)rgbValueBindRow) = ATOI64(neut_str);
else
*((SQLBIGINT *)rgbValue + bind_row) = ATOI64(neut_str);
break;
case SQL_C_UBIGINT:
len = 8;
if (bind_size > 0)
*((SQLUBIGINT *)rgbValueBindRow) = ATOI64U(neut_str);
else
*((SQLUBIGINT *)rgbValue + bind_row) = ATOI64U(neut_str);
break;
#endif /* ODBCINT64 */
case SQL_C_BINARY:
/* The following is for SQL_C_VARBOOKMARK */
if (OPENSEARCH_TYPE_INT4 == field_type) {
UInt4 ival = ATOI32U(neut_str);
MYLOG(OPENSEARCH_ALL, "SQL_C_VARBOOKMARK value=%d\n", ival);
if (pcbValue)
*pcbValueBindRow = sizeof(ival);
if (cbValueMax >= (SQLLEN)sizeof(ival)) {
memcpy(rgbValueBindRow, &ival, sizeof(ival));
return COPY_OK;
} else
return COPY_RESULT_TRUNCATED;
} else if (OPENSEARCH_TYPE_UUID == field_type) {
int rtn = char2guid(neut_str, &g);
if (COPY_OK != rtn)
return rtn;
if (pcbValue)
*pcbValueBindRow = sizeof(g);
if (cbValueMax >= (SQLLEN)sizeof(g)) {
memcpy(rgbValueBindRow, &g, sizeof(g));
return COPY_OK;
} else
return COPY_RESULT_TRUNCATED;
} else {
MYLOG(OPENSEARCH_DEBUG,
"couldn't convert the type %d to SQL_C_BINARY\n",
field_type);
return COPY_UNSUPPORTED_TYPE;
}
break;
case SQL_C_GUID:
result = char2guid(neut_str, &g);
if (COPY_OK != result) {
MYLOG(OPENSEARCH_DEBUG, "Could not convert to SQL_C_GUID\n");
return COPY_UNSUPPORTED_TYPE;
}
len = sizeof(g);
if (bind_size > 0)
*((SQLGUID *)rgbValueBindRow) = g;
else
*((SQLGUID *)rgbValue + bind_row) = g;
break;
case SQL_C_INTERVAL_YEAR:
case SQL_C_INTERVAL_MONTH:
case SQL_C_INTERVAL_YEAR_TO_MONTH:
case SQL_C_INTERVAL_DAY:
case SQL_C_INTERVAL_HOUR:
case SQL_C_INTERVAL_DAY_TO_HOUR:
case SQL_C_INTERVAL_MINUTE:
case SQL_C_INTERVAL_HOUR_TO_MINUTE:
case SQL_C_INTERVAL_SECOND:
case SQL_C_INTERVAL_DAY_TO_SECOND:
case SQL_C_INTERVAL_HOUR_TO_SECOND:
case SQL_C_INTERVAL_MINUTE_TO_SECOND:
interval2istruct(
fCType, precision, neut_str,
bind_size > 0 ? (SQL_INTERVAL_STRUCT *)rgbValueBindRow
: (SQL_INTERVAL_STRUCT *)rgbValue + bind_row);
break;
default:
MYLOG(OPENSEARCH_DEBUG, "conversion to the type %d isn't supported\n",
fCType);
return COPY_UNSUPPORTED_TYPE;
}
}
/* store the length of what was copied, if there's a place for it */
if (pcbValue)
*pcbValueBindRow = len;
if (result == COPY_OK && stmt->current_col >= 0)
gdata->gdata[stmt->current_col].data_left = 0;
return result;
}