in sql-odbc/src/sqlodbc/results.c [314:757]
RETCODE SQL_API OPENSEARCHAPI_ColAttributes(HSTMT hstmt, SQLUSMALLINT icol,
SQLUSMALLINT fDescType, PTR rgbDesc,
SQLSMALLINT cbDescMax, SQLSMALLINT *pcbDesc,
SQLLEN *pfDesc) {
CSTR func = "OPENSEARCHAPI_ColAttributes";
StatementClass *stmt = (StatementClass *)hstmt;
IRDFields *irdflds;
OID field_type = 0;
Int2 col_idx;
ConnectionClass *conn;
ConnInfo *ci;
int column_size, unknown_sizes;
int cols = 0;
RETCODE result;
const char *p = NULL;
SQLLEN value = 0;
const FIELD_INFO *fi = NULL;
const TABLE_INFO *ti = NULL;
QResultClass *res;
BOOL stmt_updatable;
MYLOG(OPENSEARCH_TRACE, "entering..col=%d %d len=%d.\n", icol, fDescType,
cbDescMax);
if (!stmt) {
SC_log_error(func, NULL_STRING, NULL);
return SQL_INVALID_HANDLE;
}
stmt_updatable = SC_is_updatable(stmt)
/* The following doesn't seem appropriate for client side cursors
&& stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY
*/
;
if (pcbDesc)
*pcbDesc = 0;
irdflds = SC_get_IRDF(stmt);
conn = SC_get_conn(stmt);
ci = &(conn->connInfo);
/*
* Dont check for bookmark column. This is the responsibility of the
* driver manager. For certain types of arguments, the column number
* is ignored anyway, so it may be 0.
*/
res = SC_get_Curres(stmt);
if (0 == icol && SQL_DESC_COUNT != fDescType) /* bookmark column */
{
MYLOG(OPENSEARCH_ALL, "answering bookmark info\n");
switch (fDescType) {
case SQL_DESC_OCTET_LENGTH:
if (pfDesc)
*pfDesc = 4;
break;
case SQL_DESC_TYPE:
if (pfDesc)
*pfDesc = stmt->options.use_bookmarks == SQL_UB_VARIABLE
? SQL_BINARY
: SQL_INTEGER;
break;
}
return SQL_SUCCESS;
}
col_idx = icol - 1;
unknown_sizes = DEFAULT_UNKNOWNSIZES;
/* not appropriate for SQLColAttributes() */
if (stmt->catalog_result)
unknown_sizes = UNKNOWNS_AS_LONGEST;
else if (unknown_sizes == UNKNOWNS_AS_DONTKNOW)
unknown_sizes = UNKNOWNS_AS_MAX;
if (!stmt->catalog_result && SC_is_parse_forced(stmt)
&& SC_can_parse_statement(stmt)) {
cols = irdflds->nfields;
/*
* Column Count is a special case. The Column number is ignored
* in this case.
*/
if (fDescType == SQL_DESC_COUNT) {
if (pfDesc)
*pfDesc = cols;
return SQL_SUCCESS;
}
if (SC_parsed_status(stmt) != STMT_PARSE_FATAL && irdflds->fi) {
if (col_idx >= cols) {
SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR,
"Invalid column number in ColAttributes.", func);
return SQL_ERROR;
}
}
}
if ((unsigned int)col_idx < irdflds->nfields && irdflds->fi)
fi = irdflds->fi[col_idx];
if (FI_is_applicable(fi))
field_type = getEffectiveOid(conn, fi);
else {
BOOL build_fi = FALSE;
fi = NULL;
switch (fDescType) {
case SQL_COLUMN_OWNER_NAME:
case SQL_COLUMN_TABLE_NAME:
case SQL_COLUMN_TYPE:
case SQL_COLUMN_TYPE_NAME:
case SQL_COLUMN_AUTO_INCREMENT:
case SQL_DESC_NULLABLE:
case SQL_DESC_BASE_TABLE_NAME:
case SQL_DESC_BASE_COLUMN_NAME:
case SQL_COLUMN_UPDATABLE:
case 1212: /* SQL_CA_SS_COLUMN_KEY ? */
build_fi = TRUE;
break;
}
res = SC_get_Curres(stmt);
cols = QR_NumPublicResultCols(res);
/*
* Column Count is a special case. The Column number is ignored
* in this case.
*/
if (fDescType == SQL_DESC_COUNT) {
if (pfDesc)
*pfDesc = cols;
return SQL_SUCCESS;
}
if (col_idx >= cols) {
SC_set_error(stmt, STMT_INVALID_COLUMN_NUMBER_ERROR,
"Invalid column number in ColAttributes.", func);
return SQL_ERROR;
}
field_type = QR_get_field_type(res, col_idx);
if ((unsigned int)col_idx < irdflds->nfields && irdflds->fi)
fi = irdflds->fi[col_idx];
}
if (FI_is_applicable(fi)) {
ti = fi->ti;
field_type = getEffectiveOid(conn, fi);
}
MYLOG(OPENSEARCH_DEBUG, "col %d field_type=%d fi,ti=%p,%p\n", col_idx, field_type,
fi, ti);
#ifdef SUPPRESS_LONGEST_ON_CURSORS
if (UNKNOWNS_AS_LONGEST == unknown_sizes) {
if (QR_once_reached_eof(res))
unknown_sizes = UNKNOWNS_AS_LONGEST;
else
unknown_sizes = UNKNOWNS_AS_MAX;
}
#endif /* SUPPRESS_LONGEST_ON_CURSORS */
/* handle constants */
if (res && -2 == QR_get_fieldsize(res, col_idx))
unknown_sizes = UNKNOWNS_AS_LONGEST;
column_size =
(USE_FI(fi, unknown_sizes) && fi->column_size > 0)
? fi->column_size
: opensearchtype_column_size(stmt, field_type, col_idx,
unknown_sizes);
switch (fDescType) {
case SQL_COLUMN_AUTO_INCREMENT: /* == SQL_DESC_AUTO_UNIQUE_VALUE */
if (fi && fi->auto_increment)
value = TRUE;
else
value = opensearchtype_auto_increment(conn, field_type);
if (value == -1) /* non-numeric becomes FALSE (ODBC Doc) */
value = FALSE;
MYLOG(OPENSEARCH_DEBUG, "AUTO_INCREMENT=" FORMAT_LEN "\n", value);
break;
case SQL_COLUMN_CASE_SENSITIVE: /* == SQL_DESC_CASE_SENSITIVE */
value = opensearchtype_case_sensitive(conn, field_type);
break;
/*
* This special case is handled above.
*
* case SQL_COLUMN_COUNT:
*/
case SQL_COLUMN_DISPLAY_SIZE: /* == SQL_DESC_DISPLAY_SIZE */
value = (USE_FI(fi, unknown_sizes) && 0 != fi->display_size)
? fi->display_size
: opensearchtype_display_size(stmt, field_type, col_idx,
unknown_sizes);
MYLOG(OPENSEARCH_DEBUG, "col %d, display_size= " FORMAT_LEN "\n", col_idx,
value);
break;
case SQL_COLUMN_LABEL: /* == SQL_DESC_LABEL */
if (fi && (NAME_IS_VALID(fi->column_alias))) {
p = GET_NAME(fi->column_alias);
MYLOG(OPENSEARCH_DEBUG, "COLUMN_LABEL = '%s'\n", p);
break;
}
/* otherwise same as column name -- FALL THROUGH!!! */
case SQL_DESC_NAME:
MYLOG(OPENSEARCH_ALL, "fi=%p (alias, name)=", fi);
if (fi)
MYPRINTF(OPENSEARCH_DEBUG, "(%s,%s)\n", PRINT_NAME(fi->column_alias),
PRINT_NAME(fi->column_name));
else
MYPRINTF(OPENSEARCH_DEBUG, "NULL\n");
p = fi ? (NAME_IS_NULL(fi->column_alias)
? SAFE_NAME(fi->column_name)
: GET_NAME(fi->column_alias))
: QR_get_fieldname(res, col_idx);
MYLOG(OPENSEARCH_DEBUG, "COLUMN_NAME = '%s'\n", p);
break;
case SQL_COLUMN_LENGTH:
value = (USE_FI(fi, unknown_sizes) && fi->length > 0)
? fi->length
: opensearchtype_buffer_length(stmt, field_type,
col_idx, unknown_sizes);
if (0 > value)
/* if (-1 == value) I'm not sure which is right */
value = 0;
MYLOG(OPENSEARCH_DEBUG, "col %d, column_length = " FORMAT_LEN "\n", col_idx,
value);
break;
case SQL_COLUMN_MONEY: /* == SQL_DESC_FIXED_PREC_SCALE */
value = opensearchtype_money(conn, field_type);
MYLOG(OPENSEARCH_ALL, "COLUMN_MONEY=" FORMAT_LEN "\n", value);
break;
case SQL_DESC_NULLABLE:
if (SC_has_outer_join(stmt))
value = TRUE;
else
value = fi ? fi->nullable : opensearchtype_nullable(conn, field_type);
MYLOG(OPENSEARCH_ALL, "COLUMN_NULLABLE=" FORMAT_LEN "\n", value);
break;
case SQL_COLUMN_OWNER_NAME: /* == SQL_DESC_SCHEMA_NAME */
p = ti ? SAFE_NAME(ti->schema_name) : NULL_STRING;
MYLOG(OPENSEARCH_DEBUG, "SCHEMA_NAME = '%s'\n", p);
break;
case SQL_COLUMN_PRECISION: /* in 2.x */
value = column_size;
if (value < 0)
value = 0;
MYLOG(OPENSEARCH_DEBUG, "col %d, column_size = " FORMAT_LEN "\n", col_idx,
value);
break;
case SQL_COLUMN_QUALIFIER_NAME: /* == SQL_DESC_CATALOG_NAME */
p = ti ? CurrCatString(conn)
: NULL_STRING; /* empty string means *not supported* */
break;
case SQL_COLUMN_SCALE: /* in 2.x */
value = opensearchtype_decimal_digits(stmt, field_type, col_idx);
MYLOG(OPENSEARCH_ALL, "COLUMN_SCALE=" FORMAT_LEN "\n", value);
if (value < 0)
value = 0;
break;
case SQL_COLUMN_SEARCHABLE: /* == SQL_DESC_SEARCHABLE */
value = opensearchtype_searchable(conn, field_type);
break;
case SQL_COLUMN_TABLE_NAME: /* == SQL_DESC_TABLE_NAME */
p = ti ? SAFE_NAME(ti->table_name) : NULL_STRING;
MYLOG(OPENSEARCH_DEBUG, "TABLE_NAME = '%s'\n", p);
break;
case SQL_COLUMN_TYPE: /* == SQL_DESC_CONCISE_TYPE */
value = opensearchtype_to_concise_type(stmt, field_type, col_idx,
unknown_sizes);
MYLOG(OPENSEARCH_DEBUG, "COLUMN_TYPE=" FORMAT_LEN "\n", value);
break;
case SQL_COLUMN_TYPE_NAME: /* == SQL_DESC_TYPE_NAME */
p = opensearchtype_to_name(stmt, field_type, col_idx,
fi && fi->auto_increment);
break;
case SQL_COLUMN_UNSIGNED: /* == SQL_DESC_UNSINGED */
value = opensearchtype_unsigned(conn, field_type);
if (value == -1) /* non-numeric becomes TRUE (ODBC Doc) */
value = SQL_TRUE;
break;
case SQL_COLUMN_UPDATABLE: /* == SQL_DESC_UPDATABLE */
/*
* Neither Access or Borland care about this.
*
* if (field_type == OPENSEARCH_TYPE_OID) pfDesc = SQL_ATTR_READONLY;
* else
*/
if (!stmt_updatable)
value = SQL_ATTR_READONLY;
else
value =
fi ? (fi->updatable ? SQL_ATTR_WRITE : SQL_ATTR_READONLY)
: (QR_get_attid(res, col_idx) > 0 ? SQL_ATTR_WRITE
: SQL_ATTR_READONLY);
if (SQL_ATTR_READONLY != value) {
const char *name = fi ? SAFE_NAME(fi->column_name)
: QR_get_fieldname(res, col_idx);
if (stricmp(name, OID_NAME) == 0 || stricmp(name, "ctid") == 0
|| stricmp(name, XMIN_NAME) == 0)
value = SQL_ATTR_READONLY;
else if (conn->ms_jet && fi && fi->auto_increment)
value = SQL_ATTR_READONLY;
}
MYLOG(OPENSEARCH_DEBUG, "%s: UPDATEABLE = " FORMAT_LEN "\n", func, value);
break;
case SQL_DESC_BASE_COLUMN_NAME:
p = fi ? SAFE_NAME(fi->column_name)
: QR_get_fieldname(res, col_idx);
MYLOG(OPENSEARCH_DEBUG, "BASE_COLUMN_NAME = '%s'\n", p);
break;
case SQL_DESC_BASE_TABLE_NAME: /* the same as TABLE_NAME ok ? */
p = ti ? SAFE_NAME(ti->table_name) : NULL_STRING;
MYLOG(OPENSEARCH_DEBUG, "BASE_TABLE_NAME = '%s'\n", p);
break;
case SQL_DESC_LENGTH: /* different from SQL_COLUMN_LENGTH */
value = (fi && column_size > 0)
? column_size
: opensearchtype_desclength(stmt, field_type, col_idx,
unknown_sizes);
if (-1 == value)
value = 0;
MYLOG(OPENSEARCH_DEBUG, "col %d, desc_length = " FORMAT_LEN "\n", col_idx,
value);
break;
case SQL_DESC_OCTET_LENGTH:
value = (USE_FI(fi, unknown_sizes) && fi->length > 0)
? fi->length
: opensearchtype_attr_transfer_octet_length(
conn, field_type, column_size, unknown_sizes);
if (-1 == value)
value = 0;
MYLOG(OPENSEARCH_DEBUG, "col %d, octet_length = " FORMAT_LEN "\n", col_idx,
value);
break;
case SQL_DESC_PRECISION: /* different from SQL_COLUMN_PRECISION */
if (value = FI_precision(fi), value <= 0)
value = opensearchtype_precision(stmt, field_type, col_idx,
unknown_sizes);
if (value < 0)
value = 0;
MYLOG(OPENSEARCH_DEBUG, "col %d, desc_precision = " FORMAT_LEN "\n",
col_idx, value);
break;
case SQL_DESC_SCALE: /* different from SQL_COLUMN_SCALE */
value = opensearchtype_scale(stmt, field_type, col_idx);
if (value < 0)
value = 0;
break;
case SQL_DESC_LOCAL_TYPE_NAME:
p = opensearchtype_to_name(stmt, field_type, col_idx,
fi && fi->auto_increment);
break;
case SQL_DESC_TYPE:
value = opensearchtype_to_sqldesctype(stmt, field_type, col_idx,
unknown_sizes);
break;
case SQL_DESC_NUM_PREC_RADIX:
value = opensearchtype_radix(conn, field_type);
break;
case SQL_DESC_LITERAL_PREFIX:
p = opensearchtype_literal_prefix(conn, field_type);
break;
case SQL_DESC_LITERAL_SUFFIX:
p = opensearchtype_literal_suffix(conn, field_type);
break;
case SQL_DESC_UNNAMED:
value = (fi && NAME_IS_NULL(fi->column_name)
&& NAME_IS_NULL(fi->column_alias))
? SQL_UNNAMED
: SQL_NAMED;
break;
case 1211: /* SQL_CA_SS_COLUMN_HIDDEN ? */
value = 0;
break;
case 1212: /* SQL_CA_SS_COLUMN_KEY ? */
SC_set_error(stmt, STMT_OPTION_NOT_FOR_THE_DRIVER,
"this request may be for MS SQL Server", func);
return SQL_ERROR;
default:
SC_set_error(stmt, STMT_INVALID_OPTION_IDENTIFIER,
"ColAttribute for this type not implemented yet",
func);
return SQL_ERROR;
}
result = SQL_SUCCESS;
if (p) { /* char/binary data */
size_t len = strlen(p);
if (rgbDesc) {
strncpy_null((char *)rgbDesc, p, (size_t)cbDescMax);
if (len >= (size_t)cbDescMax) {
result = SQL_SUCCESS_WITH_INFO;
SC_set_error(stmt, STMT_TRUNCATED,
"The buffer was too small for the rgbDesc.", func);
}
}
if (pcbDesc)
*pcbDesc = (SQLSMALLINT)len;
} else {
/* numeric data */
if (pfDesc)
*pfDesc = value;
}
return result;
}