RETCODE SQL_API OPENSEARCHAPI_ColAttributes()

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;
}