in driver/handles.c [2426:2802]
SQLRETURN EsSQLSetDescFieldW(
SQLHDESC DescriptorHandle,
SQLSMALLINT RecNumber,
SQLSMALLINT FieldIdentifier,
SQLPOINTER ValuePtr,
SQLINTEGER BufferLength)
{
esodbc_desc_st *desc = DSCH(DescriptorHandle);
esodbc_state_et state;
esodbc_rec_st *rec;
wstr_st *wstrp;
SQLSMALLINT *wordp;
SQLINTEGER *intp;
SQLSMALLINT count, type, chk_type, chk_code;
SQLULEN ulen;
SQLLEN slen;
size_t wlen;
if (! check_access(desc, FieldIdentifier, O_RDWR)) {
/* "The SQL_DESC_DATA_PTR field of an IPD is not normally set;
* however, an application can do so to force a consistency check of
* IPD fields."
* TODO: the above won't work with the generic check implementation:
* is it worth hacking an exception here? (since IPD/.data_ptr is
* marked RO) */
ERRH(desc, "field access check failed: not defined or RO for "
"desciptor.");
RET_HDIAGS(desc, SQL_STATE_HY091);
}
state = check_buff(FieldIdentifier, ValuePtr, BufferLength, FALSE);
if (state != SQL_STATE_00000) {
ERRH(desc, "buffer/~ length check failed (%d).", state);
RET_HDIAGS(desc, state);
}
/* header fields */
switch (FieldIdentifier) {
case SQL_DESC_ARRAY_SIZE:
ulen = (SQLULEN)(uintptr_t)ValuePtr;
DBGH(desc, "setting desc array size to: %llu.", (uint64_t)ulen);
if (DESC_TYPE_IS_RECORD(desc->type)) {
if (ESODBC_MAX_ROW_ARRAY_SIZE < ulen) {
WARNH(desc, "provided desc array size (%llu) larger than "
"allowed max (%hu) -- set value adjusted to max.",
(uint64_t)ulen, ESODBC_MAX_ROW_ARRAY_SIZE);
desc->array_size = ESODBC_MAX_ROW_ARRAY_SIZE;
RET_HDIAGS(desc, SQL_STATE_01S02);
} else if (ulen < 1) {
ERRH(desc, "can't set the array size to less than 1.");
RET_HDIAGS(desc, SQL_STATE_HY092);
}
} else { /* IS_PARAMETER */
/* no support for param arrays (yet) TODO */
if (1 < ulen) {
ERRH(desc, "no support for arrays of parameters.");
RET_HDIAG(desc, SQL_STATE_HYC00,
"Parameter arrays not implemented", 0);
}
}
desc->array_size = ulen;
return SQL_SUCCESS;
case SQL_DESC_ARRAY_STATUS_PTR:
DBGH(desc, "setting desc array status ptr to: 0x%p.", ValuePtr);
/* deferred */
desc->array_status_ptr = (SQLUSMALLINT *)ValuePtr;
return SQL_SUCCESS;
case SQL_DESC_BIND_OFFSET_PTR:
DBGH(desc, "setting binding offset ptr to: 0x%p.", ValuePtr);
/* deferred */
desc->bind_offset_ptr = (SQLLEN *)ValuePtr;
return SQL_SUCCESS;
case SQL_DESC_BIND_TYPE:
DBGH(desc, "setting bind type to: %lu.",
(SQLUINTEGER)(uintptr_t)ValuePtr);
desc->bind_type = (SQLUINTEGER)(uintptr_t)ValuePtr;
return SQL_SUCCESS;
/*
* "Specifies the 1-based index of the highest-numbered record that
* contains data."
* "Is not a count of all data columns or of all parameters that are
* bound, but the number of the highest-numbered record."
*
* "If the highest-numbered column or parameter is unbound, then
* SQL_DESC_COUNT is changed to the number of the next
* highest-numbered column or parameter. If a column or a parameter
* with a number that is less than the number of the highest-numbered
* column is unbound, SQL_DESC_COUNT is not changed. If additional
* columns or parameters are bound with numbers greater than the
* highest-numbered record that contains data, the driver
* automatically increases the value in the SQL_DESC_COUNT field."
*
* "If the value in SQL_DESC_COUNT is explicitly decreased, all
* records with numbers greater than the new value in SQL_DESC_COUNT
* are effectively removed. If the value in SQL_DESC_COUNT is
* explicitly set to 0 and the field is in an ARD, all data buffers
* except a bound bookmark column are released."
*/
case SQL_DESC_COUNT:
return update_rec_count(desc, (SQLSMALLINT)(intptr_t)ValuePtr);
case SQL_DESC_ROWS_PROCESSED_PTR:
DBGH(desc, "setting desc rows processed ptr to: 0x%p.", ValuePtr);
desc->rows_processed_ptr = (SQLULEN *)ValuePtr;
return SQL_SUCCESS;
}
/*
* The field is a record field -> get the record to apply the field to.
*/
if (RecNumber < 0) { /* TODO: need to check also if AxD, as per spec?? */
ERRH(desc, "negative record number provided (%d) with record field "
"(%d).", RecNumber, FieldIdentifier);
RET_HDIAG(desc, SQL_STATE_07009,
"Negative record number provided with record field", 0);
} else if (RecNumber == 0) {
ERRH(desc, "unsupported record number 0."); /* TODO: bookmarks? */
RET_HDIAG(desc, SQL_STATE_07009,
"Unsupported record number 0", 0);
} else { /* apparently one can set a record before the count is set */
rec = get_record(desc, RecNumber, TRUE);
if (! rec) {
ERRH(desc, "can't get record with number %d.", RecNumber);
RET_STATE(desc->hdr.diag.state);
}
DBGH(desc, "setting field %d of record #%d @ 0x%p.", FieldIdentifier,
RecNumber, rec);
}
/*
* "If the application changes the data type or attributes after setting
* the SQL_DESC_DATA_PTR field, the driver sets SQL_DESC_DATA_PTR to a
* null pointer, unbinding the record."
*
* NOTE: the record can actually still be bound by the length/indicator
* buffer(s), so the above "binding" definition is incomplete.
*/
if (FieldIdentifier != SQL_DESC_DATA_PTR) {
DBGH(desc, "attribute to set is different than data ptr (%d) => "
"unbinding data buffer (was 0x%p).", SQL_DESC_DATA_PTR,
rec->data_ptr);
rec->data_ptr = NULL;
}
/*INDENT-OFF*/
/* record fields */
switch (FieldIdentifier) {
/* "For datetime and interval data types, however, a verbose type
* (SQL_DATETIME or SQL_INTERVAL) is stored in SQL_DESC_TYPE, a
* concise type is stored in SQL_DESC_CONCISE_TYPE, and a subcode for
* each concise type is stored in SQL_DESC_DATETIME_INTERVAL_CODE." */
case SQL_DESC_TYPE:
type = (SQLSMALLINT)(intptr_t)ValuePtr;
DBGH(desc, "setting type of rec@0x%p to %d.", rec, type);
/* Note: SQL_[C_]DATE == SQL_DATETIME (== 9) =>
* 1. one needs to always use SQL_DESC_CONCISE_TYPE for setting
* the types from within the driver (binding cols, params):
* "SQL_DESC_CONCISE_TYPE can be set by a call to SQLBindCol or
* SQLBindParameter, or SQLSetDescField. SQL_DESC_TYPE can be set
* by a call to SQLSetDescField or SQLSetDescRec."
* 2. SQL_DESC_TYPE can only be used when setting the type record
* fields (.type, .concise_type, datetime_interval_code)
* individually. */
if (type == SQL_DATETIME || type == SQL_INTERVAL) {
/* "When the application sets the SQL_DESC_TYPE field, the
* driver checks that other fields that specify the type are
* valid and consistent." */
/* setting the verbose type only */
concise_to_type_code(rec->concise_type, &chk_type, &chk_code);
if (chk_type != type ||
chk_code != rec->datetime_interval_code ||
(! rec->datetime_interval_code)) {
ERRH(desc, "type fields found inconsistent when setting "
"the type to %hd: concise: %hd, datetime_code: %hd.",
(SQLSMALLINT)(intptr_t)ValuePtr,
rec->concise_type, rec->datetime_interval_code);
RET_HDIAGS(desc, SQL_STATE_HY021);
} else {
rec->type = type;
}
break;
}
/* no break! */
case SQL_DESC_CONCISE_TYPE:
DBGH(desc, "setting concise type of rec 0x%p to %d.", rec,
(SQLSMALLINT)(intptr_t)ValuePtr);
rec->concise_type = (SQLSMALLINT)(intptr_t)ValuePtr;
concise_to_type_code(rec->concise_type, &rec->type,
&rec->datetime_interval_code);
rec->meta_type = concise_to_meta(rec->concise_type, desc->type);
if (rec->meta_type == METATYPE_UNKNOWN) {
ERRH(desc, "REC@0x%p: incorrect concise type %d for rec #%d.",
rec, rec->concise_type, RecNumber);
RET_HDIAGS(desc, DESC_TYPE_IS_APPLICATION(desc->type) ?
SQL_STATE_HY003 : SQL_STATE_HY004);
}
/* "When the SQL_DESC_TYPE or SQL_DESC_CONCISE_TYPE field is set
* for some data types, the SQL_DESC_DATETIME_INTERVAL_PRECISION,
* SQL_DESC_LENGTH, SQL_DESC_PRECISION, and SQL_DESC_SCALE fields
* are automatically set to default values". */
set_defaults_from_meta_type(rec);
DBGH(desc, "REC@0x%p types: concise: %d, verbose: %d, code: %d.",
rec, rec->concise_type, rec->type,
rec->datetime_interval_code);
break;
case SQL_DESC_DATA_PTR:
DBGH(desc, "setting data ptr to 0x%p of type %d.", ValuePtr,
BufferLength);
/* deferred */
rec->data_ptr = ValuePtr;
if (rec->data_ptr) {
/* "A consistency check is performed by the driver
* automatically whenever an application sets the
* SQL_DESC_DATA_PTR field of an APD, ARD, or IPD."
* "The SQL_DESC_DATA_PTR field of an IPD is not normally set;
* however, an application can do so to force a consistency
* check of IPD fields. A consistency check cannot be
* performed on an IRD." */
if ((desc->type != DESC_TYPE_IRD) &&
(! consistency_check(rec))) {
ERRH(desc, "consistency check failed on rec@0x%p.", rec);
RET_HDIAGS(desc, SQL_STATE_HY021);
} else {
DBGH(desc, "rec@0x%p: bound data ptr@0x%p.", rec,
rec->data_ptr);
}
} else {
/* "If the highest-numbered column or parameter is unbound,
* then SQL_DESC_COUNT is changed to the number of the next
* highest-numbered column or parameter. " */
if (DESC_TYPE_IS_APPLICATION(desc->type) &&
/* see function-top comments on when to unbind */
(! REC_IS_BOUND(rec))) {
DBGH(desc, "rec 0x%p of desc type %d unbound.", rec,
desc->type);
if (RecNumber == desc->count) {
count = count_bound(desc);
/* worst case: trying to unbind a not-yet-bound rec */
if (count != desc->count) {
DBGH(desc, "adjusting rec count from %hd to %hd.",
desc->count, count);
return update_rec_count(desc, count);
}
}
}
}
break;
case SQL_DESC_NAME:
WARNH(desc, "stored procedure params (to set to `"LWPD"`) not "
"supported.", ValuePtr ? (SQLWCHAR *)ValuePtr : TWS_NULL);
RET_HDIAG(desc, SQL_STATE_HYC00,
"stored procedure params not supported", 0);
/* <SQLWCHAR *> */
do {
case SQL_DESC_BASE_COLUMN_NAME: wstrp = &rec->base_column_name; break;
case SQL_DESC_BASE_TABLE_NAME: wstrp = &rec->base_table_name; break;
case SQL_DESC_CATALOG_NAME: wstrp = &rec->catalog_name; break;
case SQL_DESC_LABEL: wstrp = &rec->label; break;
/* R/O fields: literal_prefix/_suffix, local_type_name, type_name */
case SQL_DESC_SCHEMA_NAME: wstrp = &rec->schema_name; break;
case SQL_DESC_TABLE_NAME: wstrp = &rec->table_name; break;
} while (0);
if (BufferLength == SQL_NTS) {
wlen = ValuePtr ? wcslen((SQLWCHAR *)ValuePtr) : 0;
} else {
wlen = BufferLength;
}
DBGH(desc, "setting SQLWCHAR field %d to `" LWPDL "`(@0x%p).",
FieldIdentifier, wlen, ValuePtr, wlen, ValuePtr);
if (wstrp->str) {
DBGH(desc, "freeing previously allocated value for field %d "
"(`" LWPDL "`).", FieldIdentifier, LWSTR(wstrp));
free(wstrp->str);
wstrp->str = NULL;
wstrp->cnt = 0;
}
if (! ValuePtr) {
DBGH(desc, "field %d reset to NULL.", FieldIdentifier);
break;
}
if (! (wstrp->str = (SQLWCHAR *)malloc((wlen + /*0-term*/1)
* sizeof(SQLWCHAR)))) {
ERRH(desc, "failed to alloc w-string buffer of len %zd.",
wlen + 1);
RET_HDIAGS(desc, SQL_STATE_HY001);
}
memcpy(wstrp->str, ValuePtr, wlen * sizeof(SQLWCHAR));
wstrp->str[wlen] = 0;
wstrp->cnt = wlen;
break;
/* <SQLLEN *>, deferred */
case SQL_DESC_INDICATOR_PTR:
DBGH(desc, "setting indicator pointer to 0x%p.", ValuePtr);
rec->indicator_ptr = (SQLLEN *)ValuePtr;
break;
case SQL_DESC_OCTET_LENGTH_PTR:
DBGH(desc, "setting octet length pointer to 0x%p.", ValuePtr);
rec->octet_length_ptr = (SQLLEN *)ValuePtr;
break;
/* <SQLLEN> */
/* R/O fields: display_size */
case SQL_DESC_OCTET_LENGTH:
slen = (SQLLEN)ValuePtr;
DBGH(desc, "setting octet length: %lld.", (int64_t)slen);
/* rec field's type is signed; a negative can be dangerous */
if (slen < 0) {
WARNH(desc, "negative octet length provided (%lld)",
(int64_t)slen);
/* no eror returned: in non-str/binary, it is to be ignorred */
}
rec->octet_length = slen;
break;
/* <SQLULEN> */
case SQL_DESC_LENGTH:
DBGH(desc, "setting length: %llu.", (uint64_t)ValuePtr);
rec->length = (SQLULEN)ValuePtr;
break;
/* <SQLSMALLINT> */
do {
case SQL_DESC_DATETIME_INTERVAL_CODE:
wordp = &rec->datetime_interval_code; break;
case SQL_DESC_PARAMETER_TYPE: wordp = &rec->parameter_type; break;
case SQL_DESC_PRECISION: wordp = &rec->precision; break;
case SQL_DESC_ROWVER: wordp = &rec->rowver; break;
case SQL_DESC_SCALE: wordp = &rec->scale; break;
case SQL_DESC_UNNAMED:
/* only driver can set this value */
if ((SQLSMALLINT)(intptr_t)ValuePtr == SQL_NAMED) {
ERRH(desc, "only the driver can set %d field to 'SQL_NAMED'.",
FieldIdentifier);
RET_HDIAGS(desc, SQL_STATE_HY091);
}
wordp = &rec->unnamed;
break;
/* R/O field: fixed_prec_scale, nullable, searchable, unsigned */
case SQL_DESC_UPDATABLE: wordp = &rec->updatable; break;
} while (0);
DBGH(desc, "setting record field %d to %d.", FieldIdentifier,
(SQLSMALLINT)(intptr_t)ValuePtr);
*wordp = (SQLSMALLINT)(intptr_t)ValuePtr;
break;
/* <SQLINTEGER> */
do {
/* R/O field: auto_unique_value, case_sensitive */
case SQL_DESC_DATETIME_INTERVAL_PRECISION:
intp = &rec->datetime_interval_precision;
break;
case SQL_DESC_NUM_PREC_RADIX:
intp = &rec->num_prec_radix;
break;
} while (0);
DBGH(desc, "returning record field %d as %d.", FieldIdentifier,
(SQLINTEGER)(intptr_t)ValuePtr);
*intp = (SQLINTEGER)(intptr_t)ValuePtr;
break;
default:
ERRH(desc, "unknown FieldIdentifier: %d.", FieldIdentifier);
RET_HDIAGS(desc, SQL_STATE_HY091);
}
/*INDENT-ON*/
return SQL_SUCCESS;
}