// Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, version 2.0, as
// published by the Free Software Foundation.
//
// This program is also distributed with certain software (including
// but not limited to OpenSSL) that is licensed under separate terms,
// as designated in a particular file or component or in included license
// documentation. The authors of MySQL hereby grant you an
// additional permission to link the program and your derivative works
// with the separately licensed software that they have included with
// MySQL.
//
// Without limiting anything contained in the foregoing, this file,
// which is part of <MySQL Product>, is also subject to the
// Universal FOSS Exception, version 1.0, a copy of which can be found at
// http://oss.oracle.com/licenses/universal-foss-exception.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License, version 2.0, for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

/**
@file  info.c
@brief Driver information functions.
*/

#include "driver.h"

#define MYINFO_SET_ULONG(val) \
do { \
  *((SQLUINTEGER *)num_info)= (val); \
  *value_len= sizeof(SQLUINTEGER); \
  return SQL_SUCCESS; \
} while(0)

#define MYINFO_SET_USHORT(val) \
do { \
  *((SQLUSMALLINT *)num_info)= (SQLUSMALLINT)(val); \
  *value_len= sizeof(SQLUSMALLINT); \
  return SQL_SUCCESS; \
} while(0)

#define MYINFO_SET_STR(val) \
do { \
  *char_info= (SQLCHAR *)(val); \
  return SQL_SUCCESS; \
} while(0)

static my_bool myodbc_ov2_inited = 0;


/**
Return general information about the driver and data source
associated with a connection.

@param[in]  hdbc            Handle of database connection
@param[in]  fInfoType       Type of information to retrieve
@param[out] char_info       Pointer to buffer for returning string
@param[out] num_info        Pointer to buffer for returning numeric info
@param[out] value_len       Pointer to buffer for returning length (only
used for numeric data)
*/
SQLRETURN SQL_API
MySQLGetInfo(SQLHDBC hdbc, SQLUSMALLINT fInfoType,
             SQLCHAR **char_info, SQLPOINTER num_info, SQLSMALLINT *value_len)
{
  DBC *dbc = (DBC *)hdbc;
  SQLSMALLINT dummy;
  SQLINTEGER dummy_value;

  if (!value_len)
    value_len = &dummy;
  if (!num_info)
    num_info = &dummy_value;

  switch (fInfoType) {
  case SQL_ACTIVE_ENVIRONMENTS:
    MYINFO_SET_USHORT(0);

  case SQL_AGGREGATE_FUNCTIONS:
    MYINFO_SET_ULONG(SQL_AF_ALL | SQL_AF_AVG | SQL_AF_COUNT | SQL_AF_DISTINCT |
                     SQL_AF_MAX | SQL_AF_MIN | SQL_AF_SUM);

  case SQL_ALTER_DOMAIN:
    MYINFO_SET_ULONG(0);

  case SQL_ALTER_TABLE:
    /** @todo check if we should report more */
    MYINFO_SET_ULONG(SQL_AT_ADD_COLUMN | SQL_AT_DROP_COLUMN);

#ifndef USE_IODBC
  case SQL_ASYNC_DBC_FUNCTIONS:
    MYINFO_SET_ULONG(SQL_ASYNC_DBC_NOT_CAPABLE);
#endif

  case SQL_ASYNC_MODE:
    MYINFO_SET_ULONG(SQL_AM_NONE);

  case SQL_BATCH_ROW_COUNT:
    MYINFO_SET_ULONG(SQL_BRC_EXPLICIT);

  case SQL_BATCH_SUPPORT:
    MYINFO_SET_ULONG(SQL_BS_SELECT_EXPLICIT | SQL_BS_ROW_COUNT_EXPLICIT |
                     SQL_BS_SELECT_PROC | SQL_BS_ROW_COUNT_PROC);

  case SQL_BOOKMARK_PERSISTENCE:
    MYINFO_SET_ULONG(SQL_BP_UPDATE | SQL_BP_DELETE);

  case SQL_CATALOG_LOCATION:
    MYINFO_SET_USHORT(SQL_CL_START);

  case SQL_CATALOG_NAME:
    MYINFO_SET_STR((dbc->ds && dbc->ds->no_catalog) ? "" : "Y");

  case SQL_CATALOG_NAME_SEPARATOR:
    MYINFO_SET_STR((dbc->ds && dbc->ds->no_catalog) ? "" : ".");

  case SQL_CATALOG_TERM:
    MYINFO_SET_STR((dbc->ds && dbc->ds->no_catalog) ? "" : "database");

  case SQL_CATALOG_USAGE:
    MYINFO_SET_ULONG((!dbc->ds || !dbc->ds->no_catalog) ?
      (SQL_CU_DML_STATEMENTS | SQL_CU_PROCEDURE_INVOCATION |
       SQL_CU_TABLE_DEFINITION | SQL_CU_INDEX_DEFINITION |
       SQL_CU_PRIVILEGE_DEFINITION) :
                     0);

  case SQL_COLLATION_SEQ:
    MYINFO_SET_STR(dbc->mysql->charset->name);

  case SQL_COLUMN_ALIAS:
    MYINFO_SET_STR("Y");

  case SQL_CONCAT_NULL_BEHAVIOR:
    MYINFO_SET_USHORT(SQL_CB_NULL);

  case SQL_CONVERT_BIGINT:
  case SQL_CONVERT_BIT:
  case SQL_CONVERT_CHAR:
  case SQL_CONVERT_DATE:
  case SQL_CONVERT_DECIMAL:
  case SQL_CONVERT_DOUBLE:
  case SQL_CONVERT_FLOAT:
  case SQL_CONVERT_INTEGER:
  case SQL_CONVERT_LONGVARCHAR:
  case SQL_CONVERT_NUMERIC:
  case SQL_CONVERT_REAL:
  case SQL_CONVERT_SMALLINT:
  case SQL_CONVERT_TIME:
  case SQL_CONVERT_TIMESTAMP:
  case SQL_CONVERT_TINYINT:
  case SQL_CONVERT_VARCHAR:
  case SQL_CONVERT_WCHAR:
  case SQL_CONVERT_WVARCHAR:
  case SQL_CONVERT_WLONGVARCHAR:
    MYINFO_SET_ULONG(SQL_CVT_CHAR | SQL_CVT_NUMERIC | SQL_CVT_DECIMAL |
                     SQL_CVT_INTEGER | SQL_CVT_SMALLINT | SQL_CVT_FLOAT |
                     SQL_CVT_REAL | SQL_CVT_DOUBLE | SQL_CVT_VARCHAR |
                     SQL_CVT_LONGVARCHAR | SQL_CVT_BIT | SQL_CVT_TINYINT |
                     SQL_CVT_BIGINT | SQL_CVT_DATE | SQL_CVT_TIME |
                     SQL_CVT_TIMESTAMP | SQL_CVT_WCHAR | SQL_CVT_WVARCHAR |
                     SQL_CVT_WLONGVARCHAR);

  case SQL_CONVERT_BINARY:
  case SQL_CONVERT_VARBINARY:
  case SQL_CONVERT_LONGVARBINARY:
  case SQL_CONVERT_INTERVAL_DAY_TIME:
  case SQL_CONVERT_INTERVAL_YEAR_MONTH:
    MYINFO_SET_ULONG(0);

  case SQL_CONVERT_FUNCTIONS:
    /* MySQL's CONVERT() and CAST() functions aren't SQL compliant yet. */
    MYINFO_SET_ULONG(0);

  case SQL_CORRELATION_NAME:
    MYINFO_SET_USHORT(SQL_CN_DIFFERENT);

  case SQL_CREATE_ASSERTION:
  case SQL_CREATE_CHARACTER_SET:
  case SQL_CREATE_COLLATION:
  case SQL_CREATE_DOMAIN:
  case SQL_CREATE_SCHEMA:
    MYINFO_SET_ULONG(0);

  case SQL_CREATE_TABLE:
    MYINFO_SET_ULONG(SQL_CT_CREATE_TABLE | SQL_CT_COMMIT_DELETE |
                     SQL_CT_LOCAL_TEMPORARY | SQL_CT_COLUMN_DEFAULT |
                     SQL_CT_COLUMN_COLLATION);

  case SQL_CREATE_TRANSLATION:
    MYINFO_SET_ULONG(0);

  case SQL_CREATE_VIEW:
    /** @todo SQL_CV_LOCAL ? */
    if (is_minimum_version(dbc->mysql->server_version, "5.0"))
      MYINFO_SET_ULONG(SQL_CV_CREATE_VIEW | SQL_CV_CHECK_OPTION |
                       SQL_CV_CASCADED);
    else
      MYINFO_SET_ULONG(0);

  case SQL_CURSOR_COMMIT_BEHAVIOR:
  case SQL_CURSOR_ROLLBACK_BEHAVIOR:
    MYINFO_SET_USHORT(SQL_CB_PRESERVE);

#ifdef SQL_CURSOR_SENSITIVITY
  case SQL_CURSOR_SENSITIVITY:
    MYINFO_SET_ULONG(SQL_UNSPECIFIED);
#endif

#ifdef SQL_CURSOR_ROLLBACK_SQL_CURSOR_SENSITIVITY
  case SQL_CURSOR_ROLLBACK_SQL_CURSOR_SENSITIVITY:
    MYINFO_SET_ULONG(SQL_UNSPECIFIED);
#endif

  case SQL_DATA_SOURCE_NAME:
    MYINFO_SET_STR(dbc->ds ? dbc->ds->name8 : NULL);

  case SQL_DATA_SOURCE_READ_ONLY:
    MYINFO_SET_STR("N");

  case SQL_DATABASE_NAME:
    if (is_connected(dbc) && reget_current_catalog(dbc))
      return dbc->set_error("HY000",
                           "SQLGetInfo() failed to return current catalog.",
                           0);
    MYINFO_SET_STR(!dbc->database.empty() ? dbc->database.c_str() : "null");

  case SQL_DATETIME_LITERALS:
    MYINFO_SET_ULONG(SQL_DL_SQL92_DATE | SQL_DL_SQL92_TIME |
                     SQL_DL_SQL92_TIMESTAMP);

  case SQL_DBMS_NAME:
    MYINFO_SET_STR("MySQL");

  case SQL_DBMS_VER:
    /** @todo technically this is not right: should be ##.##.#### */
    MYINFO_SET_STR(dbc->mysql->server_version);

  case SQL_DDL_INDEX:
    MYINFO_SET_ULONG(SQL_DI_CREATE_INDEX | SQL_DI_DROP_INDEX);

  case SQL_DEFAULT_TXN_ISOLATION:
    MYINFO_SET_ULONG(DEFAULT_TXN_ISOLATION);

  case SQL_DESCRIBE_PARAMETER:
    MYINFO_SET_STR("N");

  case SQL_DRIVER_NAME:
#ifdef MYODBC_UNICODEDRIVER
# ifdef WIN32
    MYINFO_SET_STR("myodbc8w.dll");
# else
    MYINFO_SET_STR("libmyodbc8w.so");
# endif
#else
# ifdef WIN32
    MYINFO_SET_STR("myodbc8a.dll");
# else
    MYINFO_SET_STR("libmyodbc8a.so");
# endif
#endif
  case SQL_DRIVER_ODBC_VER:
    MYINFO_SET_STR("03.80");               /* What standard we implement */

  case SQL_DRIVER_VER:
    MYINFO_SET_STR(DRIVER_VERSION);

  case SQL_DROP_ASSERTION:
  case SQL_DROP_CHARACTER_SET:
  case SQL_DROP_COLLATION:
  case SQL_DROP_DOMAIN:
  case SQL_DROP_SCHEMA:
  case SQL_DROP_TRANSLATION:
    MYINFO_SET_ULONG(0);

  case SQL_DROP_TABLE:
    MYINFO_SET_ULONG(SQL_DT_DROP_TABLE | SQL_DT_CASCADE | SQL_DT_RESTRICT);

  case SQL_DROP_VIEW:
    if (is_minimum_version(dbc->mysql->server_version, "5.0"))
      MYINFO_SET_ULONG(SQL_DV_DROP_VIEW | SQL_DV_CASCADE | SQL_DV_RESTRICT);
    else
      MYINFO_SET_ULONG(0);

  case SQL_DYNAMIC_CURSOR_ATTRIBUTES1:
    if (dbc->ds && !dbc->ds->force_use_of_forward_only_cursors &&
        dbc->ds->dynamic_cursor)
      MYINFO_SET_ULONG(SQL_CA1_NEXT | SQL_CA1_ABSOLUTE | SQL_CA1_RELATIVE |
                       SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION |
                       SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE |
                       SQL_CA1_POS_REFRESH | SQL_CA1_POSITIONED_UPDATE |
                       SQL_CA1_POSITIONED_DELETE | SQL_CA1_BULK_ADD);
    else
      MYINFO_SET_ULONG(0);

  case SQL_DYNAMIC_CURSOR_ATTRIBUTES2:
    if (dbc->ds && !dbc->ds->force_use_of_forward_only_cursors &&
        dbc->ds->dynamic_cursor)
      MYINFO_SET_ULONG(SQL_CA2_SENSITIVITY_ADDITIONS |
                       SQL_CA2_SENSITIVITY_DELETIONS |
                       SQL_CA2_SENSITIVITY_UPDATES |
                       SQL_CA2_MAX_ROWS_SELECT | SQL_CA2_MAX_ROWS_INSERT |
                       SQL_CA2_MAX_ROWS_DELETE | SQL_CA2_MAX_ROWS_UPDATE |
                       SQL_CA2_CRC_EXACT | SQL_CA2_SIMULATE_TRY_UNIQUE);
    else
      MYINFO_SET_ULONG(0);

  case SQL_EXPRESSIONS_IN_ORDERBY:
    MYINFO_SET_STR("Y");

  case SQL_FILE_USAGE:
    MYINFO_SET_USHORT(SQL_FILE_NOT_SUPPORTED);

  case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1:
    MYINFO_SET_ULONG(dbc->ds && dbc->ds->force_use_of_forward_only_cursors ?
                     SQL_CA1_NEXT :
                     SQL_CA1_NEXT | SQL_CA1_ABSOLUTE | SQL_CA1_RELATIVE |
                     SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION |
                     SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE |
                     SQL_CA1_POS_REFRESH | SQL_CA1_POSITIONED_UPDATE |
                     SQL_CA1_POSITIONED_DELETE | SQL_CA1_BULK_ADD);

  case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2:
    MYINFO_SET_ULONG(SQL_CA2_MAX_ROWS_SELECT | SQL_CA2_MAX_ROWS_INSERT |
                     SQL_CA2_MAX_ROWS_DELETE | SQL_CA2_MAX_ROWS_UPDATE |
                     (dbc->ds && dbc->ds->force_use_of_forward_only_cursors ?
                      0 : SQL_CA2_CRC_EXACT));

  case SQL_GETDATA_EXTENSIONS:
#ifndef USE_IODBC
    MYINFO_SET_ULONG(SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER | SQL_GD_BLOCK |
                     SQL_GD_BOUND | SQL_GD_OUTPUT_PARAMS);
#else
    MYINFO_SET_ULONG(SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER | SQL_GD_BLOCK |
                     SQL_GD_BOUND);
#endif

  case SQL_GROUP_BY:
    MYINFO_SET_USHORT(SQL_GB_NO_RELATION);

  case SQL_IDENTIFIER_CASE:
    MYINFO_SET_USHORT(SQL_IC_MIXED);

  case SQL_IDENTIFIER_QUOTE_CHAR:
    MYINFO_SET_STR("`");

  case SQL_INDEX_KEYWORDS:
    MYINFO_SET_ULONG(SQL_IK_ALL);

  case SQL_INFO_SCHEMA_VIEWS:
    /*
    We have INFORMATION_SCHEMA.SCHEMATA, but we don't report it
    because the driver exposes databases (schema) as catalogs.
    */
    if (is_minimum_version(dbc->mysql->server_version, "5.1"))
      MYINFO_SET_ULONG(SQL_ISV_CHARACTER_SETS | SQL_ISV_COLLATIONS |
                       SQL_ISV_COLUMN_PRIVILEGES | SQL_ISV_COLUMNS |
                       SQL_ISV_KEY_COLUMN_USAGE |
                       SQL_ISV_REFERENTIAL_CONSTRAINTS |
                       /* SQL_ISV_SCHEMATA | */ SQL_ISV_TABLE_CONSTRAINTS |
                       SQL_ISV_TABLE_PRIVILEGES | SQL_ISV_TABLES |
                       SQL_ISV_VIEWS);
    else if (is_minimum_version(dbc->mysql->server_version, "5.0"))
      MYINFO_SET_ULONG(SQL_ISV_CHARACTER_SETS | SQL_ISV_COLLATIONS |
                       SQL_ISV_COLUMN_PRIVILEGES | SQL_ISV_COLUMNS |
                       SQL_ISV_KEY_COLUMN_USAGE | /* SQL_ISV_SCHEMATA | */
                       SQL_ISV_TABLE_CONSTRAINTS | SQL_ISV_TABLE_PRIVILEGES |
                       SQL_ISV_TABLES | SQL_ISV_VIEWS);
    else
      MYINFO_SET_ULONG(0);

  case SQL_INSERT_STATEMENT:
    MYINFO_SET_ULONG(SQL_IS_INSERT_LITERALS | SQL_IS_INSERT_SEARCHED |
                     SQL_IS_SELECT_INTO);

  case SQL_INTEGRITY:
    MYINFO_SET_STR("N");

  case SQL_KEYSET_CURSOR_ATTRIBUTES1:
  case SQL_KEYSET_CURSOR_ATTRIBUTES2:
    MYINFO_SET_ULONG(0);

  case SQL_KEYWORDS:
    /*
    These lists were generated by taking the list of reserved words from
    the MySQL Reference Manual (which is, in turn, generated from the source)
    with the pre-reserved ODBC keywords removed.
    */
    if (is_minimum_version(dbc->mysql->server_version, "8.0.22"))
      MYINFO_SET_STR("ACCESSIBLE,ANALYZE,ASENSITIVE,BEFORE,BIGINT,BINARY,BLOB,"
                     "CALL,CHANGE,CONDITION,DATABASE,DATABASES,DAY_HOUR,"
                     "DAY_MICROSECOND,DAY_MINUTE,DAY_SECOND,DELAYED,"
                     "DETERMINISTIC,DISTINCTROW,DIV,DUAL,EACH,ELSEIF,ENCLOSED,"
                     "ESCAPED,EXIT,EXPLAIN,FLOAT4,FLOAT8,FORCE,FULLTEXT,GENERAL,"
                     "GET,HIGH_PRIORITY,HOUR_MICROSECOND,HOUR_MINUTE,"
                     "HOUR_SECOND,IF,IGNORE,IGNORE_SERVER_IDS,INFILE,INOUT,INT1,"
                     "INT2,INT3,INT4,INT8,IO_AFTER_GTIDS,IO_BEFORE_GTIDS,"
                     "ITERATE,KEYS,KILL,LEAVE,LIMIT,LINEAR,LINES,LOAD,LOCALTIME,"
                     "LOCALTIMESTAMP,LOCK,LONG,LONGBLOB,LONGTEXT,LOOP,"
                     "LOW_PRIORITY,SOURCE_BIND,SOURCE_HEARTBEAT_PERIOD,"
                     "SOURCE_SSL_VERIFY_SERVER_CERT,MAXVALUE,MEDIUMBLOB,"
                     "MEDIUMINT,MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND,"
                     "MINUTE_SECOND,MOD,MODIFIES,NO_WRITE_TO_BINLOG,NONBLOCKING,ONE_SHOT,"
                     "OPTIMIZE,OPTIONALLY,OUT,OUTFILE,PARTITION,PURGE,RANGE,"
                     "READ_ONLY,READS,READ_WRITE,REGEXP,RELEASE,RENAME,REPEAT,"
                     "REPLACE,REQUIRE,RESIGNAL,RETURN,RLIKE,SCHEMAS,"
                     "SECOND_MICROSECOND,SENSITIVE,SEPARATOR,SHOW,SIGNAL,SLOW,"
                     "SPATIAL,SPECIFIC,SQL_AFTER_GTIDS,SQL_BEFORE_GTIDS,"
                     "SQL_BIG_RESULT,SQL_CALC_FOUND_ROWS,SQLEXCEPTION,"
                     "SQL_SMALL_RESULT,SSL,STARTING,STRAIGHT_JOIN,TERMINATED,"
                     "TINYBLOB,TINYINT,TINYTEXT,TRIGGER,UNDO,UNLOCK,UNSIGNED,"
                     "USE,UTC_DATE,UTC_TIME,UTC_TIMESTAMP,VARBINARY,"
                     "VARCHARACTER,WHILE,X509,XOR,YEAR_MONTH,ZEROFILL");
    else if (is_minimum_version(dbc->mysql->server_version, "5.7"))
      MYINFO_SET_STR("ACCESSIBLE,ANALYZE,ASENSITIVE,BEFORE,BIGINT,BINARY,BLOB,"
                     "CALL,CHANGE,CONDITION,DATABASE,DATABASES,DAY_HOUR,"
                     "DAY_MICROSECOND,DAY_MINUTE,DAY_SECOND,DELAYED,"
                     "DETERMINISTIC,DISTINCTROW,DIV,DUAL,EACH,ELSEIF,ENCLOSED,"
                     "ESCAPED,EXIT,EXPLAIN,FLOAT4,FLOAT8,FORCE,FULLTEXT,GENERAL,"
                     "GET,HIGH_PRIORITY,HOUR_MICROSECOND,HOUR_MINUTE,"
                     "HOUR_SECOND,IF,IGNORE,IGNORE_SERVER_IDS,INFILE,INOUT,INT1,"
                     "INT2,INT3,INT4,INT8,IO_AFTER_GTIDS,IO_BEFORE_GTIDS,"
                     "ITERATE,KEYS,KILL,LEAVE,LIMIT,LINEAR,LINES,LOAD,LOCALTIME,"
                     "LOCALTIMESTAMP,LOCK,LONG,LONGBLOB,LONGTEXT,LOOP,"
                     "LOW_PRIORITY,\x4D\x41\x53\x54\x45\x52_BIND,\x4D\x41\x53\x54\x45\x52_HEARTBEAT_PERIOD,"
                     "\x4D\x41\x53\x54\x45\x52_SSL_VERIFY_SERVER_CERT,MAXVALUE,MEDIUMBLOB,"
                     "MEDIUMINT,MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND,"
                     "MINUTE_SECOND,MOD,MODIFIES,NO_WRITE_TO_BINLOG,NONBLOCKING,ONE_SHOT,"
                     "OPTIMIZE,OPTIONALLY,OUT,OUTFILE,PARTITION,PURGE,RANGE,"
                     "READ_ONLY,READS,READ_WRITE,REGEXP,RELEASE,RENAME,REPEAT,"
                     "REPLACE,REQUIRE,RESIGNAL,RETURN,RLIKE,SCHEMAS,"
                     "SECOND_MICROSECOND,SENSITIVE,SEPARATOR,SHOW,SIGNAL,SLOW,"
                     "SPATIAL,SPECIFIC,SQL_AFTER_GTIDS,SQL_BEFORE_GTIDS,"
                     "SQL_BIG_RESULT,SQL_CALC_FOUND_ROWS,SQLEXCEPTION,"
                     "SQL_SMALL_RESULT,SSL,STARTING,STRAIGHT_JOIN,TERMINATED,"
                     "TINYBLOB,TINYINT,TINYTEXT,TRIGGER,UNDO,UNLOCK,UNSIGNED,"
                     "USE,UTC_DATE,UTC_TIME,UTC_TIMESTAMP,VARBINARY,"
                     "VARCHARACTER,WHILE,X509,XOR,YEAR_MONTH,ZEROFILL");
    else if (is_minimum_version(dbc->mysql->server_version, "5.6"))
      MYINFO_SET_STR("ACCESSIBLE,ANALYZE,ASENSITIVE,BEFORE,BIGINT,BINARY,BLOB,"
                     "CALL,CHANGE,CONDITION,DATABASE,DATABASES,DAY_HOUR,"
                     "DAY_MICROSECOND,DAY_MINUTE,DAY_SECOND,DELAYED,"
                     "DETERMINISTIC,DISTINCTROW,DIV,DUAL,EACH,ELSEIF,ENCLOSED,"
                     "ESCAPED,EXIT,EXPLAIN,FLOAT4,FLOAT8,FORCE,FULLTEXT,GENERAL,"
                     "GET,HIGH_PRIORITY,HOUR_MICROSECOND,HOUR_MINUTE,"
                     "HOUR_SECOND,IF,IGNORE,IGNORE_SERVER_IDS,INFILE,INOUT,INT1,"
                     "INT2,INT3,INT4,INT8,IO_AFTER_GTIDS,IO_BEFORE_GTIDS,"
                     "ITERATE,KEYS,KILL,LEAVE,LIMIT,LINEAR,LINES,LOAD,LOCALTIME,"
                     "LOCALTIMESTAMP,LOCK,LONG,LONGBLOB,LONGTEXT,LOOP,"
                     "LOW_PRIORITY,\x4D\x41\x53\x54\x45\x52_BIND,\x4D\x41\x53\x54\x45\x52_HEARTBEAT_PERIOD,"
                     "\x4D\x41\x53\x54\x45\x52_SSL_VERIFY_SERVER_CERT,MAXVALUE,MEDIUMBLOB,"
                     "MEDIUMINT,MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND,"
                     "MINUTE_SECOND,MOD,MODIFIES,NO_WRITE_TO_BINLOG,ONE_SHOT,"
                     "OPTIMIZE,OPTIONALLY,OUT,OUTFILE,PARTITION,PURGE,RANGE,"
                     "READ_ONLY,READS,READ_WRITE,REGEXP,RELEASE,RENAME,REPEAT,"
                     "REPLACE,REQUIRE,RESIGNAL,RETURN,RLIKE,SCHEMAS,"
                     "SECOND_MICROSECOND,SENSITIVE,SEPARATOR,SHOW,SIGNAL,SLOW,"
                     "SPATIAL,SPECIFIC,SQL_AFTER_GTIDS,SQL_BEFORE_GTIDS,"
                     "SQL_BIG_RESULT,SQL_CALC_FOUND_ROWS,SQLEXCEPTION,"
                     "SQL_SMALL_RESULT,SSL,STARTING,STRAIGHT_JOIN,TERMINATED,"
                     "TINYBLOB,TINYINT,TINYTEXT,TRIGGER,UNDO,UNLOCK,UNSIGNED,"
                     "USE,UTC_DATE,UTC_TIME,UTC_TIMESTAMP,VARBINARY,"
                     "VARCHARACTER,WHILE,X509,XOR,YEAR_MONTH,ZEROFILL");
    else if (is_minimum_version(dbc->mysql->server_version, "5.5"))
      MYINFO_SET_STR("ACCESSIBLE,ANALYZE,ASENSITIVE,BEFORE,BIGINT,BINARY,BLOB,"
                     "CALL,CHANGE,CONDITION,DATABASE,DATABASES,DAY_HOUR,"
                     "DAY_MICROSECOND,DAY_MINUTE,DAY_SECOND,DELAYED,"
                     "DETERMINISTIC,DISTINCTROW,DIV,DUAL,EACH,ELSEIF,ENCLOSED,"
                     "ESCAPED,EXIT,EXPLAIN,FLOAT4,FLOAT8,FORCE,FULLTEXT,GENERAL,"
                     "HIGH_PRIORITY,HOUR_MICROSECOND,HOUR_MINUTE,HOUR_SECOND,"
                     "IF,IGNORE,IGNORE_SERVER_IDS,INFILE,INOUT,INT1,INT2,INT3,"
                     "INT4,INT8,ITERATE,KEYS,KILL,LEAVE,LIMIT,LINEAR,LINES,"
                     "LOAD,LOCALTIME,LOCALTIMESTAMP,LOCK,LONG,LONGBLOB,"
                     "LONGTEXT,LOOP,LOW_PRIORITY,\x4D\x41\x53\x54\x45\x52_HEARTBEAT_PERIOD,"
                     "\x4D\x41\x53\x54\x45\x52_SSL_VERIFY_SERVER_CERT,MAXVALUE,MEDIUMBLOB,"
                     "MEDIUMINT,MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND,"
                     "MINUTE_SECOND,MOD,MODIFIES,NO_WRITE_TO_BINLOG,OPTIMIZE,"
                     "OPTIONALLY,OUT,OUTFILE,PURGE,RANGE,READ_ONLY,READS,"
                     "READ_WRITE,REGEXP,RELEASE,RENAME,REPEAT,REPLACE,REQUIRE,"
                     "RESIGNAL,RETURN,RLIKE,SCHEMAS,SECOND_MICROSECOND,"
                     "SENSITIVE,SEPARATOR,SHOW,SIGNAL,SLOW,SPATIAL,SPECIFIC,"
                     "SQL_BIG_RESULT,SQL_CALC_FOUND_ROWS,SQLEXCEPTION,"
                     "SQL_SMALL_RESULT,SSL,STARTING,STRAIGHT_JOIN,TERMINATED,"
                     "TINYBLOB,TINYINT,TINYTEXT,TRIGGER,UNDO,UNLOCK,UNSIGNED,"
                     "USE,UTC_DATE,UTC_TIME,UTC_TIMESTAMP,VARBINARY,"
                     "VARCHARACTER,WHILE,X509,XOR,YEAR_MONTH,ZEROFILL");
    else if (is_minimum_version(dbc->mysql->server_version, "5.1"))
      MYINFO_SET_STR("ACCESSIBLE,ANALYZE,ASENSITIVE,BEFORE,BIGINT,BINARY,BLOB,"
                     "CALL,CHANGE,CONDITION,DATABASE,DATABASES,DAY_HOUR,"
                     "DAY_MICROSECOND,DAY_MINUTE,DAY_SECOND,DELAYED,"
                     "DETERMINISTIC,DISTINCTROW,DIV,DUAL,EACH,ELSEIF,ENCLOSED,"
                     "ESCAPED,EXIT,EXPLAIN,FLOAT4,FLOAT8,FORCE,FULLTEXT,"
                     "HIGH_PRIORITY,HOUR_MICROSECOND,HOUR_MINUTE,HOUR_SECOND,"
                     "IF,IGNORE,INFILE,INOUT,INT1,INT2,INT3,INT4,INT8,ITERATE,"
                     "KEYS,KILL,LEAVE,LIMIT,LINEAR,LINES,LOAD,LOCALTIME,"
                     "LOCALTIMESTAMP,LOCK,LONG,LONGBLOB,LONGTEXT,LOOP,"
                     "LOW_PRIORITY,\x4D\x41\x53\x54\x45\x52_SSL_VERIFY_SERVER_CERT,MEDIUMBLOB,"
                     "MEDIUMINT,MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND,"
                     "MINUTE_SECOND,MOD,MODIFIES,NO_WRITE_TO_BINLOG,OPTIMIZE,"
                     "OPTIONALLY,OUT,OUTFILE,PURGE,RANGE,READ_ONLY,READS,"
                     "READ_WRITE,REGEXP,RELEASE,RENAME,REPEAT,REPLACE,REQUIRE,"
                     "RETURN,RLIKE,SCHEMAS,SECOND_MICROSECOND,SENSITIVE,"
                     "SEPARATOR,SHOW,SPATIAL,SPECIFIC,SQL_BIG_RESULT,"
                     "SQL_CALC_FOUND_ROWS,SQLEXCEPTION,SQL_SMALL_RESULT,SSL,"
                     "STARTING,STRAIGHT_JOIN,TERMINATED,TINYBLOB,TINYINT,"
                     "TINYTEXT,TRIGGER,UNDO,UNLOCK,UNSIGNED,USE,UTC_DATE,"
                     "UTC_TIME,UTC_TIMESTAMP,VARBINARY,VARCHARACTER,WHILE,X509,"
                     "XOR,YEAR_MONTH,ZEROFILL");
    else if (is_minimum_version(dbc->mysql->server_version, "5.0"))
      MYINFO_SET_STR("ANALYZE,ASENSITIVE,BEFORE,BIGINT,BINARY,BLOB,CALL,CHANGE,"
                     "CONDITION,DATABASE,DATABASES,DAY_HOUR,DAY_MICROSECOND,"
                     "DAY_MINUTE,DAY_SECOND,DELAYED,DETERMINISTIC,DISTINCTROW,"
                     "DIV,DUAL,EACH,ELSEIF,ENCLOSED,ESCAPED,EXIT,EXPLAIN,"
                     "FLOAT4,FLOAT8,FORCE,FULLTEXT,HIGH_PRIORITY,"
                     "HOUR_MICROSECOND,HOUR_MINUTE,HOUR_SECOND,IF,IGNORE,"
                     "INFILE,INOUT,INT1,INT2,INT3,INT4,INT8,ITERATE,KEYS,KILL,"
                     "LEAVE,LIMIT,LINES,LOAD,LOCALTIME,LOCALTIMESTAMP,LOCK,"
                     "LONG,LONGBLOB,LONGTEXT,LOOP,LOW_PRIORITY,MEDIUMBLOB,"
                     "MEDIUMINT,MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND,"
                     "MINUTE_SECOND,MOD,MODIFIES,NO_WRITE_TO_BINLOG,OPTIMIZE,"
                     "OPTIONALLY,OUT,OUTFILE,PURGE,RAID0,READS,REGEXP,RELEASE,"
                     "RENAME,REPEAT,REPLACE,REQUIRE,RETURN,RLIKE,SCHEMAS,"
                     "SECOND_MICROSECOND,SENSITIVE,SEPARATOR,SHOW,SONAME,"
                     "SPATIAL,SPECIFIC,SQL_BIG_RESULT,SQL_CALC_FOUND_ROWS,"
                     "SQLEXCEPTION,SQL_SMALL_RESULT,SSL,STARTING,STRAIGHT_JOIN,"
                     "TERMINATED,TINYBLOB,TINYINT,TINYTEXT,TRIGGER,UNDO,UNLOCK,"
                     "UNSIGNED,USE,UTC_DATE,UTC_TIME,UTC_TIMESTAMP,VARBINARY,"
                     "VARCHARACTER,WHILE,X509,XOR,YEAR_MONTH,ZEROFILL");
    else
      MYINFO_SET_STR("ANALYZE,BEFORE,BIGINT,BINARY,BLOB,CHANGE,COLUMNS,"
                     "DATABASE,DATABASES,DAY_HOUR,DAY_MICROSECOND,DAY_MINUTE,"
                     "DAY_SECOND,DELAYED,DISTINCTROW,DIV,DUAL,ENCLOSED,ESCAPED,"
                     "EXPLAIN,FIELDS,FLOAT4,FLOAT8,FORCE,FULLTEXT,"
                     "HIGH_PRIORITY,HOUR_MICROSECOND,HOUR_MINUTE,HOUR_SECOND,"
                     "IF,IGNORE,INFILE,INT1,INT2,INT3,INT4,INT8,KEYS,KILL,"
                     "LIMIT,LINES,LOAD,LOCALTIME,LOCALTIMESTAMP,LOCK,LONG,"
                     "LONGBLOB,LONGTEXT,LOW_PRIORITY,MEDIUMBLOB,MEDIUMINT,"
                     "MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND,MINUTE_SECOND,"
                     "MOD,NO_WRITE_TO_BINLOG,OPTIMIZE,OPTIONALLY,OUTFILE,PURGE,"
                     "RAID0,REGEXP,RENAME,REPLACE,REQUIRE,RLIKE,"
                     "SECOND_MICROSECOND,SEPARATOR,SHOW,SONAME,SPATIAL,"
                     "SQL_BIG_RESULT,SQL_CALC_FOUND_ROWS,SQL_SMALL_RESULT,SSL,"
                     "STARTING,STRAIGHT_JOIN,TABLES,TERMINATED,TINYBLOB,"
                     "TINYINT,TINYTEXT,UNLOCK,UNSIGNED,USE,UTC_DATE,UTC_TIME,"
                     "UTC_TIMESTAMP,VARBINARY,VARCHARACTER,X509,XOR,YEAR_MONTH,"
                     "ZEROFILL");

  case SQL_LIKE_ESCAPE_CLAUSE:
    MYINFO_SET_STR("Y");

  case SQL_MAX_ASYNC_CONCURRENT_STATEMENTS:
    MYINFO_SET_ULONG(0);

  case SQL_MAX_BINARY_LITERAL_LEN:
    MYINFO_SET_ULONG(0);

  // SQL_MAX_QUALIFIER_NAME_LEN is also defined as SQL_MAX_CATALOG_NAME_LEN
  case SQL_MAX_CATALOG_NAME_LEN:
    if (!dbc->ds->no_catalog)
      MYINFO_SET_USHORT(64);
    else
      MYINFO_SET_USHORT(0);

  case SQL_MAX_CHAR_LITERAL_LEN:
    MYINFO_SET_ULONG(0);

  case SQL_MAX_COLUMN_NAME_LEN:
    MYINFO_SET_USHORT(NAME_LEN);

  case SQL_MAX_COLUMNS_IN_GROUP_BY:
    MYINFO_SET_USHORT(0); /* No specific limit */

  case SQL_MAX_COLUMNS_IN_INDEX:
    MYINFO_SET_USHORT(32);

  case SQL_MAX_COLUMNS_IN_ORDER_BY:
    MYINFO_SET_USHORT(0); /* No specific limit */

  case SQL_MAX_COLUMNS_IN_SELECT:
    MYINFO_SET_USHORT(0); /* No specific limit */

  case SQL_MAX_COLUMNS_IN_TABLE:
    MYINFO_SET_USHORT(0); /* No specific limit */

  case SQL_MAX_CONCURRENT_ACTIVITIES:
    MYINFO_SET_USHORT(0); /* No specific limit */

  case SQL_MAX_CURSOR_NAME_LEN:
    MYINFO_SET_USHORT(MYSQL_MAX_CURSOR_LEN);

  case SQL_MAX_DRIVER_CONNECTIONS:
    MYINFO_SET_USHORT(0); /* No specific limit */

  case SQL_MAX_IDENTIFIER_LEN:
    MYINFO_SET_USHORT(NAME_LEN);

  case SQL_MAX_INDEX_SIZE:
    if (is_minimum_version(dbc->mysql->server_version, "5.0"))
      MYINFO_SET_USHORT(3072);
    else
      MYINFO_SET_USHORT(1024);

  case SQL_MAX_PROCEDURE_NAME_LEN:
    MYINFO_SET_USHORT(NAME_LEN);

  case SQL_MAX_ROW_SIZE:
    MYINFO_SET_ULONG(0); /* No specific limit */

  case SQL_MAX_ROW_SIZE_INCLUDES_LONG:
    MYINFO_SET_STR("Y");

  // SQL_MAX_OWNER_NAME_LEN is also defined as SQL_MAX_SCHEMA_NAME_LEN
  case SQL_MAX_SCHEMA_NAME_LEN:
    if (!dbc->ds->no_schema)
      MYINFO_SET_USHORT(64);
    else
      MYINFO_SET_USHORT(0);

  case SQL_MAX_STATEMENT_LEN:
    MYINFO_SET_ULONG(dbc->net_buffer_len);

  case SQL_MAX_TABLE_NAME_LEN:
    MYINFO_SET_USHORT(NAME_LEN);

  case SQL_MAX_TABLES_IN_SELECT:
    if (is_minimum_version(dbc->mysql->server_version, "5.0"))
      MYINFO_SET_USHORT(63);
    else
      MYINFO_SET_USHORT(31);

  case SQL_MAX_USER_NAME_LEN:
    MYINFO_SET_USHORT(USERNAME_LENGTH);

  case SQL_MULT_RESULT_SETS:
    MYINFO_SET_STR("Y");

  case SQL_MULTIPLE_ACTIVE_TXN:
    MYINFO_SET_STR("Y");

  case SQL_NEED_LONG_DATA_LEN:
    MYINFO_SET_STR("N");

  case SQL_NON_NULLABLE_COLUMNS:
    MYINFO_SET_USHORT(SQL_NNC_NON_NULL);

  case SQL_NULL_COLLATION:
    MYINFO_SET_USHORT(SQL_NC_LOW);

  case SQL_NUMERIC_FUNCTIONS:
    MYINFO_SET_ULONG(SQL_FN_NUM_ABS | SQL_FN_NUM_ACOS | SQL_FN_NUM_ASIN |
                     SQL_FN_NUM_ATAN | SQL_FN_NUM_ATAN2 | SQL_FN_NUM_CEILING |
                     SQL_FN_NUM_COS | SQL_FN_NUM_COT | SQL_FN_NUM_EXP |
                     SQL_FN_NUM_FLOOR | SQL_FN_NUM_LOG | SQL_FN_NUM_MOD |
                     SQL_FN_NUM_SIGN | SQL_FN_NUM_SIN | SQL_FN_NUM_SQRT |
                     SQL_FN_NUM_TAN | SQL_FN_NUM_PI | SQL_FN_NUM_RAND |
                     SQL_FN_NUM_DEGREES | SQL_FN_NUM_LOG10 | SQL_FN_NUM_POWER |
                     SQL_FN_NUM_RADIANS | SQL_FN_NUM_ROUND |
                     SQL_FN_NUM_TRUNCATE);

  case SQL_ODBC_API_CONFORMANCE:
    MYINFO_SET_USHORT(SQL_OAC_LEVEL1);

  case SQL_ODBC_INTERFACE_CONFORMANCE:
    MYINFO_SET_ULONG(SQL_OIC_LEVEL1);

  case SQL_ODBC_SQL_CONFORMANCE:
    MYINFO_SET_USHORT(SQL_OSC_CORE);

  case SQL_OJ_CAPABILITIES:
    MYINFO_SET_ULONG(SQL_OJ_LEFT | SQL_OJ_RIGHT | SQL_OJ_NESTED |
                     SQL_OJ_NOT_ORDERED | SQL_OJ_INNER |
                     SQL_OJ_ALL_COMPARISON_OPS);

  case SQL_ORDER_BY_COLUMNS_IN_SELECT:
    MYINFO_SET_STR("N");

  case SQL_PARAM_ARRAY_ROW_COUNTS:
    MYINFO_SET_ULONG(SQL_PARC_NO_BATCH);

  case SQL_PARAM_ARRAY_SELECTS:
    MYINFO_SET_ULONG(SQL_PAS_NO_BATCH);

  case SQL_PROCEDURE_TERM:
    if (is_minimum_version(dbc->mysql->server_version, "5.0"))
      MYINFO_SET_STR("stored procedure");
    else
      MYINFO_SET_STR("");

  case SQL_PROCEDURES:
    if (is_minimum_version(dbc->mysql->server_version, "5.0"))
      MYINFO_SET_STR("Y");
    else
      MYINFO_SET_STR("N");

  case SQL_POS_OPERATIONS:
    if (dbc->ds && dbc->ds->force_use_of_forward_only_cursors)
      MYINFO_SET_ULONG(0);
    else
      MYINFO_SET_ULONG(SQL_POS_POSITION | SQL_POS_UPDATE | SQL_POS_DELETE |
                       SQL_POS_ADD | SQL_POS_REFRESH);

  case SQL_QUOTED_IDENTIFIER_CASE:
    MYINFO_SET_USHORT(SQL_IC_SENSITIVE);

  case SQL_ROW_UPDATES:
    MYINFO_SET_STR("N");

  case SQL_SCHEMA_TERM:
    MYINFO_SET_STR((dbc->ds && dbc->ds->no_schema) ? "" : "database");

  case SQL_SCHEMA_USAGE:
    if (!dbc->ds->no_schema)
      MYINFO_SET_ULONG(SQL_SU_DML_STATEMENTS | SQL_SU_PROCEDURE_INVOCATION |
                       SQL_SU_TABLE_DEFINITION | SQL_SU_INDEX_DEFINITION |
                       SQL_SU_PRIVILEGE_DEFINITION);
    else
      MYINFO_SET_ULONG(0);

  case SQL_SCROLL_OPTIONS:
    MYINFO_SET_ULONG(SQL_SO_FORWARD_ONLY |
      (dbc->ds && dbc->ds->force_use_of_forward_only_cursors ?
       0 : SQL_SO_STATIC |
       (dbc->ds && dbc->ds->dynamic_cursor ? SQL_SO_DYNAMIC : 0)));

  case SQL_SEARCH_PATTERN_ESCAPE:
    MYINFO_SET_STR("\\");

  case SQL_SERVER_NAME:
    MYINFO_SET_STR(dbc->mysql->host_info);

  case SQL_SPECIAL_CHARACTERS:
    /* We can handle anything but / and \xff. */
    MYINFO_SET_STR(" !\"#%&'()*+,-.:;<=>?@[\\]^`{|}~");

  case SQL_SQL_CONFORMANCE:
    MYINFO_SET_ULONG(SQL_SC_SQL92_INTERMEDIATE);

  case SQL_SQL92_DATETIME_FUNCTIONS:
    MYINFO_SET_ULONG(SQL_SDF_CURRENT_DATE | SQL_SDF_CURRENT_TIME |
                     SQL_SDF_CURRENT_TIMESTAMP);

  case SQL_SQL92_FOREIGN_KEY_DELETE_RULE:
  case SQL_SQL92_FOREIGN_KEY_UPDATE_RULE:
    MYINFO_SET_ULONG(0);

  case SQL_SQL92_GRANT:
    MYINFO_SET_ULONG(SQL_SG_DELETE_TABLE | SQL_SG_INSERT_COLUMN |
                     SQL_SG_INSERT_TABLE | SQL_SG_REFERENCES_TABLE |
                     SQL_SG_REFERENCES_COLUMN | SQL_SG_SELECT_TABLE |
                     SQL_SG_UPDATE_COLUMN | SQL_SG_UPDATE_TABLE |
                     SQL_SG_WITH_GRANT_OPTION);

  case SQL_SQL92_NUMERIC_VALUE_FUNCTIONS:
    MYINFO_SET_ULONG(SQL_SNVF_BIT_LENGTH | SQL_SNVF_CHAR_LENGTH |
                     SQL_SNVF_CHARACTER_LENGTH | SQL_SNVF_EXTRACT |
                     SQL_SNVF_OCTET_LENGTH | SQL_SNVF_POSITION);

  case SQL_SQL92_PREDICATES:
    MYINFO_SET_ULONG(SQL_SP_BETWEEN | SQL_SP_COMPARISON | SQL_SP_EXISTS |
                     SQL_SP_IN | SQL_SP_ISNOTNULL | SQL_SP_ISNULL |
                     SQL_SP_LIKE /*| SQL_SP_MATCH_FULL  |SQL_SP_MATCH_PARTIAL |
                                 SQL_SP_MATCH_UNIQUE_FULL | SQL_SP_MATCH_UNIQUE_PARTIAL |
                                 SQL_SP_OVERLAPS */ | SQL_SP_QUANTIFIED_COMPARISON /*|
                                 SQL_SP_UNIQUE */);

  case SQL_SQL92_RELATIONAL_JOIN_OPERATORS:
    MYINFO_SET_ULONG(SQL_SRJO_CROSS_JOIN | SQL_SRJO_INNER_JOIN |
                     SQL_SRJO_LEFT_OUTER_JOIN | SQL_SRJO_NATURAL_JOIN |
                     SQL_SRJO_RIGHT_OUTER_JOIN);

  case SQL_SQL92_REVOKE:
    MYINFO_SET_ULONG(SQL_SR_DELETE_TABLE | SQL_SR_INSERT_COLUMN |
                     SQL_SR_INSERT_TABLE | SQL_SR_REFERENCES_TABLE |
                     SQL_SR_REFERENCES_COLUMN | SQL_SR_SELECT_TABLE |
                     SQL_SR_UPDATE_COLUMN | SQL_SR_UPDATE_TABLE);

  case SQL_SQL92_ROW_VALUE_CONSTRUCTOR:
    MYINFO_SET_ULONG(SQL_SRVC_VALUE_EXPRESSION | SQL_SRVC_NULL |
                     SQL_SRVC_DEFAULT | SQL_SRVC_ROW_SUBQUERY);

  case SQL_SQL92_STRING_FUNCTIONS:
    MYINFO_SET_ULONG(SQL_SSF_CONVERT | SQL_SSF_LOWER | SQL_SSF_UPPER |
                     SQL_SSF_SUBSTRING | SQL_SSF_TRANSLATE | SQL_SSF_TRIM_BOTH |
                     SQL_SSF_TRIM_LEADING | SQL_SSF_TRIM_TRAILING);

  case SQL_SQL92_VALUE_EXPRESSIONS:
    MYINFO_SET_ULONG(SQL_SVE_CASE | SQL_SVE_CAST | SQL_SVE_COALESCE |
                     SQL_SVE_NULLIF);

  case SQL_STANDARD_CLI_CONFORMANCE:
    MYINFO_SET_ULONG(SQL_SCC_ISO92_CLI);

  case SQL_STATIC_CURSOR_ATTRIBUTES1:
    MYINFO_SET_ULONG(SQL_CA1_NEXT | SQL_CA1_ABSOLUTE | SQL_CA1_RELATIVE |
                     SQL_CA1_LOCK_NO_CHANGE | SQL_CA1_POS_POSITION |
                     SQL_CA1_POS_UPDATE | SQL_CA1_POS_DELETE |
                     SQL_CA1_POS_REFRESH | SQL_CA1_POSITIONED_UPDATE |
                     SQL_CA1_POSITIONED_DELETE | SQL_CA1_BULK_ADD);

  case SQL_STATIC_CURSOR_ATTRIBUTES2:
    MYINFO_SET_ULONG(SQL_CA2_MAX_ROWS_SELECT | SQL_CA2_MAX_ROWS_INSERT |
                     SQL_CA2_MAX_ROWS_DELETE | SQL_CA2_MAX_ROWS_UPDATE |
                     SQL_CA2_CRC_EXACT);

  case SQL_STRING_FUNCTIONS:
    MYINFO_SET_ULONG(SQL_FN_STR_ASCII | SQL_FN_STR_BIT_LENGTH |
                     SQL_FN_STR_CHAR | SQL_FN_STR_CHAR_LENGTH |
                     SQL_FN_STR_CONCAT | SQL_FN_STR_INSERT | SQL_FN_STR_LCASE |
                     SQL_FN_STR_LEFT | SQL_FN_STR_LENGTH | SQL_FN_STR_LOCATE |
                     SQL_FN_STR_LOCATE_2 | SQL_FN_STR_LTRIM |
                     SQL_FN_STR_OCTET_LENGTH | SQL_FN_STR_POSITION |
                     SQL_FN_STR_REPEAT | SQL_FN_STR_REPLACE | SQL_FN_STR_RIGHT |
                     SQL_FN_STR_RTRIM | SQL_FN_STR_SOUNDEX | SQL_FN_STR_SPACE |
                     SQL_FN_STR_SUBSTRING | SQL_FN_STR_UCASE);

  case SQL_SUBQUERIES:
    MYINFO_SET_ULONG(SQL_SQ_CORRELATED_SUBQUERIES | SQL_SQ_COMPARISON |
                     SQL_SQ_EXISTS | SQL_SQ_IN | SQL_SQ_QUANTIFIED);

  case SQL_SYSTEM_FUNCTIONS:
    MYINFO_SET_ULONG(SQL_FN_SYS_DBNAME | SQL_FN_SYS_IFNULL |
                     SQL_FN_SYS_USERNAME);

  case SQL_TABLE_TERM:
    MYINFO_SET_STR("table");

  case SQL_TIMEDATE_ADD_INTERVALS:
  case SQL_TIMEDATE_DIFF_INTERVALS:
    MYINFO_SET_ULONG(0);

  case SQL_TIMEDATE_FUNCTIONS:
    MYINFO_SET_ULONG(SQL_FN_TD_CURRENT_DATE | SQL_FN_TD_CURRENT_TIME |
                     SQL_FN_TD_CURRENT_TIMESTAMP | SQL_FN_TD_CURDATE |
                     SQL_FN_TD_CURTIME | SQL_FN_TD_DAYNAME |
                     SQL_FN_TD_DAYOFMONTH | SQL_FN_TD_DAYOFWEEK |
                     SQL_FN_TD_DAYOFYEAR | SQL_FN_TD_EXTRACT | SQL_FN_TD_HOUR |
                     /* SQL_FN_TD_JULIAN_DAY | */ SQL_FN_TD_MINUTE |
                     SQL_FN_TD_MONTH | SQL_FN_TD_MONTHNAME | SQL_FN_TD_NOW |
                     SQL_FN_TD_QUARTER | SQL_FN_TD_SECOND |
                     /*SQL_FN_TD_SECONDS_SINCE_MIDNIGHT | */
                     SQL_FN_TD_TIMESTAMPADD | SQL_FN_TD_TIMESTAMPDIFF |
                     SQL_FN_TD_WEEK | SQL_FN_TD_YEAR);

  case SQL_TXN_CAPABLE:
    if (trans_supported(dbc) && (!dbc->ds || !dbc->ds->disable_transactions))
      MYINFO_SET_USHORT(SQL_TC_DDL_COMMIT);
    else
      MYINFO_SET_USHORT(SQL_TC_NONE);

  case SQL_TXN_ISOLATION_OPTION:
    if (!trans_supported(dbc) || (dbc->ds && dbc->ds->disable_transactions))
      MYINFO_SET_ULONG(SQL_TXN_READ_COMMITTED);
    else
      MYINFO_SET_ULONG(SQL_TXN_READ_COMMITTED | SQL_TXN_READ_UNCOMMITTED |
                       SQL_TXN_REPEATABLE_READ | SQL_TXN_SERIALIZABLE);

  case SQL_UNION:
    MYINFO_SET_ULONG(SQL_U_UNION | SQL_U_UNION_ALL);

  case SQL_USER_NAME:
    MYINFO_SET_STR(dbc->ds ? dbc->ds->uid8 : NULL);

  case SQL_XOPEN_CLI_YEAR:
    MYINFO_SET_STR("1992");

    /* The following aren't listed in the MSDN documentation. */

  case SQL_ACCESSIBLE_PROCEDURES:
  case SQL_ACCESSIBLE_TABLES:
    MYINFO_SET_STR("N");

  case SQL_LOCK_TYPES:
    MYINFO_SET_ULONG(0);

  case SQL_OUTER_JOINS:
    MYINFO_SET_STR("Y");

  case SQL_POSITIONED_STATEMENTS:
    if (dbc->ds && dbc->ds->force_use_of_forward_only_cursors)
      MYINFO_SET_ULONG(0);
    else
      MYINFO_SET_ULONG(SQL_PS_POSITIONED_DELETE | SQL_PS_POSITIONED_UPDATE);

  case SQL_SCROLL_CONCURRENCY:
    /** @todo this is wrong. */
    MYINFO_SET_ULONG(SQL_SS_ADDITIONS | SQL_SS_DELETIONS | SQL_SS_UPDATES);

  case SQL_STATIC_SENSITIVITY:
    MYINFO_SET_ULONG(SQL_SS_ADDITIONS | SQL_SS_DELETIONS | SQL_SS_UPDATES);

  case SQL_FETCH_DIRECTION:
    if (dbc->ds && dbc->ds->force_use_of_forward_only_cursors)
      MYINFO_SET_ULONG(SQL_FD_FETCH_NEXT);
    else
      MYINFO_SET_ULONG(SQL_FD_FETCH_NEXT | SQL_FD_FETCH_FIRST |
                       SQL_FD_FETCH_LAST |
                       (dbc->ds->user_manager_cursor ? 0 : SQL_FD_FETCH_PRIOR) |
                       SQL_FD_FETCH_ABSOLUTE | SQL_FD_FETCH_RELATIVE);

  case SQL_ODBC_SAG_CLI_CONFORMANCE:
    MYINFO_SET_USHORT(SQL_OSCC_COMPLIANT);

  default:
  {
    char buff[80];
    sprintf(buff, "Unsupported option: %d to SQLGetInfo", fInfoType);
    return ((DBC*)hdbc)->set_error(MYERR_S1C00, buff, 4000);
  }
  }

  return SQL_SUCCESS;
}


/*
Function sets up a result set containing details of the types
supported by mysql.
*/
MYSQL_FIELD SQL_GET_TYPE_INFO_fields[] =
{
  MYODBC_FIELD_STRING("TYPE_NAME", 32, NOT_NULL_FLAG),
  MYODBC_FIELD_SHORT("DATA_TYPE", NOT_NULL_FLAG),
  MYODBC_FIELD_LONG("COLUMN_SIZE", 0),
  MYODBC_FIELD_STRING("LITERAL_PREFIX", 2, 0),
  MYODBC_FIELD_STRING("LITERAL_SUFFIX", 1, 0),
  MYODBC_FIELD_STRING("CREATE_PARAMS", 15, 0),
  MYODBC_FIELD_SHORT("NULLABLE", NOT_NULL_FLAG),
  MYODBC_FIELD_SHORT("CASE_SENSITIVE", NOT_NULL_FLAG),
  MYODBC_FIELD_SHORT("SEARCHABLE", NOT_NULL_FLAG),
  MYODBC_FIELD_SHORT("UNSIGNED_ATTRIBUTE", 0),
  MYODBC_FIELD_SHORT("FIXED_PREC_SCALE", NOT_NULL_FLAG),
  MYODBC_FIELD_SHORT("AUTO_UNIQUE_VALUE", 0),
  MYODBC_FIELD_STRING("LOCAL_TYPE_NAME", 60, 0),
  MYODBC_FIELD_SHORT("MINIMUM_SCALE", 0),
  MYODBC_FIELD_SHORT("MAXIMUM_SCALE", 0),
  MYODBC_FIELD_SHORT("SQL_DATATYPE", NOT_NULL_FLAG),
  MYODBC_FIELD_SHORT("SQL_DATETIME_SUB", 0),
  MYODBC_FIELD_LONG("NUM_PREC_RADIX", 0),
  MYODBC_FIELD_SHORT("INTERVAL_PRECISION", 0),
};

const uint SQL_GET_TYPE_INFO_FIELDS = array_elements(SQL_GET_TYPE_INFO_fields);
#define MYSQL_DATA_TYPES 59

char sql_searchable[6], sql_unsearchable[6], sql_nullable[6], sql_no_nulls[6],
sql_bit[6], sql_tinyint[6], sql_smallint[6], sql_integer[6], sql_bigint[6],
sql_float[6], sql_real[6], sql_double[6], sql_char[6], sql_varchar[6],
sql_longvarchar[6], sql_timestamp[6], sql_decimal[6], sql_numeric[6],
sql_varbinary[6], sql_time[6], sql_date[6], sql_binary[6],
sql_longvarbinary[6], sql_datetime[6], sql_wchar[6], sql_wvarchar[6],
sql_wlongvarchar[6];

char *SQL_GET_TYPE_INFO_values[MYSQL_DATA_TYPES][19] =
{
  /* SQL_BIT= -7 */
  { "bit",sql_bit,"1","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"bit",NULL,NULL,sql_bit,NULL,NULL,NULL },

  /* SQL_TINY= -6 */
  { "tinyint",sql_tinyint,"3","'","'",NULL,sql_nullable,"0",sql_searchable,"0","0","0","tinyint",NULL,NULL,sql_tinyint,NULL,"10",NULL },
  { "tinyint unsigned",sql_tinyint,"3","'","'",NULL,sql_nullable,"0",sql_searchable,"1","0","0","tinyint unsigned",NULL,NULL,sql_tinyint,NULL,"10",NULL },
  { "tinyint auto_increment",sql_tinyint,"3","'","'",NULL,sql_no_nulls,"0",sql_searchable,"0","0","1","tinyint auto_increment",NULL,NULL,sql_tinyint, NULL,"10",NULL },
  { "tinyint unsigned auto_increment",sql_tinyint,"3","'","'",NULL,sql_no_nulls, "0",sql_searchable,"1","0","1","tinyint unsigned auto_increment",NULL,NULL, sql_tinyint,NULL,"10",NULL },

  /* SQL_BIGINT= -5 */
  { "bigint",sql_bigint,"19","'","'",NULL,sql_nullable,"0",sql_searchable,"0","0","0","bigint",NULL,NULL,sql_bigint,NULL,"10",NULL },
  { "bigint unsigned",sql_bigint,"20","'","'",NULL,sql_nullable,"0",sql_searchable,"1","0","0","bigint unsigned",NULL,NULL,sql_bigint,NULL,"10",NULL },
  { "bigint auto_increment",sql_bigint,"19","'","'",NULL,sql_no_nulls,"0",sql_searchable,"0","0","1","bigint auto_increment",NULL,NULL,sql_bigint,NULL,"10",NULL },
  { "bigint unsigned auto_increment",sql_bigint,"20","'","'",NULL,sql_no_nulls, "0",sql_searchable,"1","0","1","bigint unsigned auto_increment",NULL,NULL,sql_bigint, NULL,"10",NULL },

  /* SQL_LONGVARBINARY= -4 */
  { "long varbinary",sql_longvarbinary,"16777215","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"long varbinary",NULL,NULL,sql_longvarbinary,NULL,NULL,NULL },
  { "blob",sql_longvarbinary,"65535","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"blob",NULL,NULL,sql_longvarbinary,NULL,NULL,NULL },
  { "longblob",sql_longvarbinary,"2147483647","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"longblob",NULL,NULL,sql_longvarbinary,NULL,NULL,NULL },
  { "tinyblob",sql_longvarbinary,"255","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"tinyblob",NULL,NULL,sql_longvarbinary,NULL,NULL,NULL },
  { "mediumblob",sql_longvarbinary,"16777215","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"mediumblob",NULL,NULL,sql_longvarbinary,NULL,NULL,NULL },

  /* SQL_VARBINARY= -3 */
  { "varbinary",sql_varbinary,"255","'","'","length",sql_nullable,"0",sql_searchable,NULL,"0",NULL,"varbinary",NULL,NULL,sql_varbinary,NULL,NULL,NULL },

  /* SQL_BINARY= -2 */
  { "binary",sql_binary,"255","'","'","length",sql_nullable,"0",sql_searchable,NULL,"0",NULL,"binary",NULL,NULL,sql_binary,NULL,NULL,NULL },

  /* SQL_LONGVARCHAR= -1 */
  { "long varchar",sql_longvarchar,"16777215","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"mediumtext",NULL,NULL,sql_longvarchar,NULL,NULL,NULL },
  { "text",sql_longvarchar,"65535","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"text",NULL,NULL,sql_longvarchar,NULL,NULL,NULL },
  { "mediumtext",sql_longvarchar,"16777215","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"mediumtext",NULL,NULL,sql_longvarchar,NULL,NULL,NULL },
  { "longtext",sql_longvarchar,"2147483647","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"longtext",NULL,NULL,sql_longvarchar,NULL,NULL,NULL },
  { "tinytext",sql_longvarchar,"255","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"tinytext",NULL,NULL,sql_longvarchar,NULL,NULL,NULL },

  { "long varchar",sql_wlongvarchar,"16777215","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"mediumtext",NULL,NULL,sql_longvarchar,NULL,NULL,NULL },
  { "text",sql_wlongvarchar,"65535","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"text",NULL,NULL,sql_longvarchar,NULL,NULL,NULL },
  { "mediumtext",sql_wlongvarchar,"16777215","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"mediumtext",NULL,NULL,sql_longvarchar,NULL,NULL,NULL },
  { "longtext",sql_wlongvarchar,"2147483647","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"longtext",NULL,NULL,sql_longvarchar,NULL,NULL,NULL },
  { "tinytext",sql_wlongvarchar,"255","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"tinytext",NULL,NULL,sql_longvarchar,NULL,NULL,NULL },

  /* SQL_CHAR= 1 */
  { "char",sql_char, "255","'","'","length",sql_nullable,"0",sql_searchable,NULL,"0",NULL,"char",NULL,NULL,sql_char,"0",NULL,NULL },
  { "char",sql_wchar,"255","'","'","length",sql_nullable,"0",sql_searchable,NULL,"0",NULL,"char",NULL,NULL,sql_wchar,"0",NULL,NULL },

  /* SQL_NUMERIC= 2 */
  { "numeric",sql_numeric,"19","'","'","precision,scale",sql_nullable,"0",sql_searchable,"0","0","0","numeric","0","19",sql_numeric,NULL,"10",NULL },

  /* SQL_DECIMAL= 3 */
  { "decimal",sql_decimal,"19","'","'","precision,scale",sql_nullable,"0",sql_searchable,"0","0","0","decimal","0","19",sql_decimal,NULL,"10",NULL },

  /* SQL_INTEGER= 4 */
  { "integer",sql_integer,"10","'","'",NULL,sql_nullable,"0",sql_searchable,"0","0","0","integer",NULL,NULL,sql_integer,NULL,"10",NULL },
  { "integer unsigned",sql_integer,"10","'","'",NULL,sql_nullable,"0",sql_searchable,"1","0","0","integer unsigned",NULL,NULL,sql_integer,NULL,"10",NULL },
  { "int",sql_integer,"10","'","'",NULL,sql_nullable,"0",sql_searchable,"0","0","0","integer",NULL,NULL,sql_integer,NULL,"10",NULL },
  { "int unsigned",sql_integer,"10","'","'",NULL,sql_nullable,"0",sql_searchable,"1","0","0","integer unsigned",NULL,NULL,sql_integer,NULL,"10",NULL },
  { "mediumint",sql_integer,"7","'","'",NULL,sql_nullable,"0",sql_searchable,"0","0","0","Medium integer",NULL,NULL,sql_integer,NULL,"10",NULL },
  { "mediumint unsigned",sql_integer,"8","'","'",NULL,sql_nullable,"0",sql_searchable,"1","0","0","Medium integer unsigned",NULL,NULL,sql_integer,NULL,"10",NULL },
  { "integer auto_increment",sql_integer,"10","'","'",NULL,sql_no_nulls,"0",sql_searchable,"0","0","1","integer auto_increment",NULL,NULL,sql_integer, NULL,"10",NULL },
  { "integer unsigned auto_increment",sql_integer,"10","'","'",NULL,sql_no_nulls, "0",sql_searchable,"1","0","1","integer unsigned auto_increment",NULL,NULL, sql_integer,NULL,"10",NULL },
  { "int auto_increment",sql_integer,"10","'","'",NULL,sql_no_nulls,"0",sql_searchable,"0","0","1","integer auto_increment",NULL,NULL,sql_integer, NULL,"10",NULL },
  { "int unsigned auto_increment",sql_integer,"10","'","'",NULL,sql_no_nulls,"0",sql_searchable,"1","0","1","integer unsigned auto_increment",NULL,NULL,sql_integer,NULL,"10",NULL },
  { "mediumint auto_increment",sql_integer,"7","'","'",NULL,sql_no_nulls,"0",sql_searchable,"0","0","1","Medium integer auto_increment",NULL,NULL,sql_integer,NULL,"10",NULL },
  { "mediumint unsigned auto_increment",sql_integer,"8","'","'",NULL,sql_no_nulls, "0",sql_searchable,"1","0","1","Medium integer unsigned auto_increment",NULL,NULL, sql_integer,NULL,"10",NULL },

  /* SQL_SMALLINT= 5 */
  { "smallint",sql_smallint,"5","'","'",NULL,sql_nullable,"0",sql_searchable,"0","0","0","smallint",NULL,NULL,sql_smallint,NULL,"10",NULL },
  { "smallint unsigned",sql_smallint,"5","'","'",NULL,sql_nullable,"0",sql_searchable,"1","0","0","smallint unsigned",NULL,NULL,sql_smallint,NULL,"10",NULL },
  { "smallint auto_increment",sql_smallint,"5","'","'",NULL,sql_no_nulls,"0",sql_searchable,"0","0","1","smallint auto_increment",NULL,NULL,sql_smallint,NULL,"10",NULL },
  { "smallint unsigned auto_increment",sql_smallint,"5","'","'",NULL,sql_no_nulls, "0",sql_searchable,"1","0","1","smallint unsigned auto_increment",NULL,NULL, sql_smallint,NULL,"10",NULL },

  /* SQL_FLOAT= 6 */
  { "double",sql_float,"15","'","'",NULL,sql_nullable,"0",sql_searchable,"0","0","0","double","0","4",sql_float, NULL,"2",NULL },
  { "double auto_increment",sql_float,"15","'","'",NULL,sql_no_nulls,"0",sql_searchable,"0","0","1","double auto_increment","0","4",sql_float,NULL,"2",NULL },

  /* SQL_REAL= 7 */
  { "float",sql_real,"7","'","'",NULL,sql_nullable, "0",sql_unsearchable,"0","0","0","float","0","2",sql_real, NULL,"2",NULL },
  { "float auto_increment",sql_real,"7","'","'",NULL,sql_no_nulls,"0",sql_unsearchable,"0","0","1","float auto_increment","0","2",sql_real,NULL,"2",NULL },

  /* SQL_DOUBLE= 8 */
  { "double",sql_double,"15","'","'",NULL,sql_nullable,"0",sql_searchable,"0","0","0","double","0","4",sql_double,NULL,"2",NULL },
  { "double auto_increment",sql_double,"15","'","'",NULL,sql_no_nulls,"0",sql_searchable,"0","0","1","double auto_increment","0","4",sql_double,NULL,"2",NULL },

  /* SQL_TYPE_DATE= 91 */
  { "date",sql_date,"10","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"date",NULL,NULL,sql_datetime,sql_date,NULL,NULL },

  /* SQL_TYPE_TIME= 92 */
  { "time",sql_time,"8","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"time",NULL,NULL,sql_datetime,sql_time,NULL,NULL },

  /* YEAR - SQL_SMALLINT */
  { "year",sql_smallint,"4","'","'",NULL,sql_nullable,"0",sql_searchable,"0","0","0","year",NULL,NULL,sql_smallint,NULL,"10",NULL },

  /* SQL_TYPE_TIMESTAMP= 93 */
  { "datetime",sql_timestamp,"21","'","'",NULL,sql_nullable,"0",sql_searchable,NULL,"0",NULL,"datetime","0","0",sql_datetime,sql_timestamp,NULL,NULL },
  { "timestamp",sql_timestamp,"14","'","'",NULL,sql_no_nulls,"0",sql_searchable,NULL,"0",NULL,"timestamp","0","0",sql_datetime,sql_timestamp,NULL,NULL },

  /* SQL_VARCHAR= 12 */
  { "varchar",sql_varchar,"255","'","'","length",sql_nullable,"0",sql_searchable,NULL,"0",NULL,"varchar","0","0",sql_varchar,NULL,"10",NULL },
  { "varchar",sql_wvarchar,"255","'","'","length",sql_nullable,"0",sql_searchable,NULL,"0",NULL,"varchar","0","0",sql_wvarchar,NULL,"10",NULL },

  /* ENUM and SET are not included -- it confuses some applications. */
};


/**
Return information about data types supported by the server.

@param[in] hstmt     Handle of statement
@param[in] fSqlType  SQL data type or @c SQL_ALL_TYPES

@since ODBC 1.0
@since ISO SQL 92
*/
SQLRETURN SQL_API MySQLGetTypeInfo(SQLHSTMT hstmt, SQLSMALLINT fSqlType)
{
  STMT *stmt = (STMT *)hstmt;
  uint i;

  my_SQLFreeStmt(hstmt, FREE_STMT_RESET);

  /* use ODBC2 types if called with ODBC3 types on an ODBC2 handle */
  if (stmt->dbc->env->odbc_ver == SQL_OV_ODBC2)
  {
    switch (fSqlType)
    {
    case SQL_TYPE_DATE:
      fSqlType = SQL_DATE;
      break;
    case SQL_TYPE_TIME:
      fSqlType = SQL_TIME;
      break;
    case SQL_TYPE_TIMESTAMP:
      fSqlType = SQL_TIMESTAMP;
      break;
    }
  }

  /* Set up result Data dictionary. */
  stmt->result = (MYSQL_RES *)myodbc_malloc(sizeof(MYSQL_RES), MYF(MY_ZEROFILL));
  stmt->fake_result = 1;
  x_free(stmt->result_array);
  stmt->result_array = (char **)myodbc_malloc(sizeof(SQL_GET_TYPE_INFO_values),
                                              MYF(MY_FAE | MY_ZEROFILL));
  if (!stmt->result || !stmt->result_array)
  {
    stmt_result_free(stmt);
    x_free(stmt->result_array);
    return stmt->set_error("S1001", "Not enough memory", 4001);
  }

  if (fSqlType == SQL_ALL_TYPES)
  {
    memcpy(stmt->result_array,
           SQL_GET_TYPE_INFO_values,
           sizeof(SQL_GET_TYPE_INFO_values));
    stmt->result->row_count = MYSQL_DATA_TYPES;
  }
  else
  {
    stmt->result->row_count= 0;
    for (i = 0; i < MYSQL_DATA_TYPES; ++i)
    {
      if (atoi(SQL_GET_TYPE_INFO_values[i][1]) == fSqlType ||
          atoi(SQL_GET_TYPE_INFO_values[i][15]) == fSqlType)
      {
        memcpy(&stmt->result_array[stmt->result->row_count++ *
               SQL_GET_TYPE_INFO_FIELDS],
               &SQL_GET_TYPE_INFO_values[i][0],
               sizeof(char *) * SQL_GET_TYPE_INFO_FIELDS);
      }
    }
  }
  myodbc_link_fields(stmt, SQL_GET_TYPE_INFO_fields, SQL_GET_TYPE_INFO_FIELDS);

  return SQL_SUCCESS;
}


/**
Create strings from some integers for easy initialization of string arrays.

@todo get rid of this. it is evil.
*/
void init_getfunctions(void)
{
  my_int2str(SQL_SEARCHABLE, sql_searchable, -10, 0);
  my_int2str(SQL_UNSEARCHABLE, sql_unsearchable, -10, 0);
  my_int2str(SQL_NULLABLE, sql_nullable, -10, 0);
  my_int2str(SQL_NO_NULLS, sql_no_nulls, -10, 0);
  my_int2str(SQL_BIT, sql_bit, -10, 0);
  my_int2str(SQL_TINYINT, sql_tinyint, -10, 0);
  my_int2str(SQL_SMALLINT, sql_smallint, -10, 0);
  my_int2str(SQL_INTEGER, sql_integer, -10, 0);
  my_int2str(SQL_BIGINT, sql_bigint, -10, 0);
  my_int2str(SQL_FLOAT, sql_float, -10, 0);
  my_int2str(SQL_REAL, sql_real, -10, 0);
  my_int2str(SQL_DOUBLE, sql_double, -10, 0);
  my_int2str(SQL_CHAR, sql_char, -10, 0);
  my_int2str(SQL_VARCHAR, sql_varchar, -10, 0);
  my_int2str(SQL_LONGVARCHAR, sql_longvarchar, -10, 0);
  my_int2str(SQL_TYPE_TIMESTAMP, sql_timestamp, -10, 0);
  my_int2str(SQL_DECIMAL, sql_decimal, -10, 0);
  my_int2str(SQL_NUMERIC, sql_numeric, -10, 0);
  my_int2str(SQL_VARBINARY, sql_varbinary, -10, 0);
  my_int2str(SQL_TYPE_TIME, sql_time, -10, 0);
  my_int2str(SQL_TYPE_DATE, sql_date, -10, 0);
  my_int2str(SQL_LONGVARBINARY, sql_longvarbinary, -10, 0);
  my_int2str(SQL_BINARY, sql_binary, -10, 0);
  my_int2str(SQL_DATETIME, sql_datetime, -10, 0);
  my_int2str(SQL_WCHAR, sql_wchar, -10, 0);
  my_int2str(SQL_WVARCHAR, sql_wvarchar, -10, 0);
  my_int2str(SQL_WLONGVARCHAR, sql_wlongvarchar, -10, 0);
# if (ODBCVER < 0x0300)
  myodbc_sqlstate2_init();
  myodbc_ov2_inited = 1;
# endif
}

/**
Fix some initializations based on the ODBC version.
*/
void myodbc_ov_init(SQLINTEGER odbc_version)
{
  if (odbc_version == SQL_OV_ODBC2)
  {
    my_int2str(SQL_TIMESTAMP, sql_timestamp, -10, 0);
    my_int2str(SQL_DATE, sql_date, -10, 0);
    my_int2str(SQL_TIME, sql_time, -10, 0);
    myodbc_sqlstate2_init();
    myodbc_ov2_inited = 1;
  }
  else
  {
    if (!myodbc_ov2_inited)
      return;
    myodbc_ov2_inited = 0;

    my_int2str(SQL_TYPE_TIMESTAMP, sql_timestamp, -10, 0);
    my_int2str(SQL_TYPE_DATE, sql_date, -10, 0);
    my_int2str(SQL_TYPE_TIME, sql_time, -10, 0);
    myodbc_sqlstate3_init();
  }
}


/**
List of functions supported in the driver.
*/
SQLUSMALLINT myodbc3_functions[] =
{
  SQL_API_SQLALLOCCONNECT,
  SQL_API_SQLALLOCENV,
  SQL_API_SQLALLOCHANDLE,
  SQL_API_SQLALLOCSTMT,
  SQL_API_SQLBINDCOL,
  /* SQL_API_SQLBINDPARAM */
  SQL_API_SQLCANCEL,
#ifndef USE_IODBC
  SQL_API_SQLCANCELHANDLE,
#endif
  SQL_API_SQLCLOSECURSOR,
  SQL_API_SQLCOLATTRIBUTE,
  SQL_API_SQLCOLUMNS,
  SQL_API_SQLCONNECT,
  SQL_API_SQLCOPYDESC,
  SQL_API_SQLDATASOURCES,
  SQL_API_SQLDESCRIBECOL,
  SQL_API_SQLDISCONNECT,
  SQL_API_SQLENDTRAN,
  SQL_API_SQLERROR,
  SQL_API_SQLEXECDIRECT,
  SQL_API_SQLEXECUTE,
  SQL_API_SQLFETCH,
  SQL_API_SQLFETCHSCROLL,
  SQL_API_SQLFREECONNECT,
  SQL_API_SQLFREEENV,
  SQL_API_SQLFREEHANDLE,
  SQL_API_SQLFREESTMT,
  SQL_API_SQLGETCONNECTATTR,
  SQL_API_SQLGETCONNECTOPTION,
  SQL_API_SQLGETCURSORNAME,
  SQL_API_SQLGETDATA,
  SQL_API_SQLGETDESCFIELD,
  SQL_API_SQLGETDESCREC,
  SQL_API_SQLGETDIAGFIELD,
  SQL_API_SQLGETDIAGREC,
  SQL_API_SQLGETENVATTR,
  SQL_API_SQLGETFUNCTIONS,
  SQL_API_SQLGETINFO,
  SQL_API_SQLGETSTMTATTR,
  SQL_API_SQLGETSTMTOPTION,
  SQL_API_SQLGETTYPEINFO,
  SQL_API_SQLNUMRESULTCOLS,
  SQL_API_SQLPARAMDATA,
  SQL_API_SQLPREPARE,
  SQL_API_SQLPUTDATA,
  SQL_API_SQLROWCOUNT,
  SQL_API_SQLSETCONNECTATTR,
  SQL_API_SQLSETCONNECTOPTION,
  SQL_API_SQLSETCURSORNAME,
  SQL_API_SQLSETDESCFIELD,
  SQL_API_SQLSETDESCREC,
  SQL_API_SQLSETENVATTR,
  SQL_API_SQLSETPARAM,
  SQL_API_SQLSETSTMTATTR,
  SQL_API_SQLSETSTMTOPTION,
  SQL_API_SQLSPECIALCOLUMNS,
  SQL_API_SQLSTATISTICS,
  SQL_API_SQLTABLES,
  SQL_API_SQLTRANSACT,
  /* SQL_API_SQLALLOCHANDLESTD */
  SQL_API_SQLBULKOPERATIONS,
  SQL_API_SQLBINDPARAMETER,
  SQL_API_SQLBROWSECONNECT,
  SQL_API_SQLCOLATTRIBUTES,
  SQL_API_SQLCOLUMNPRIVILEGES ,
  SQL_API_SQLDESCRIBEPARAM,
  SQL_API_SQLDRIVERCONNECT,
  SQL_API_SQLDRIVERS,
  SQL_API_SQLEXTENDEDFETCH,
  SQL_API_SQLFOREIGNKEYS,
  SQL_API_SQLMORERESULTS,
  SQL_API_SQLNATIVESQL,
  SQL_API_SQLNUMPARAMS,
  SQL_API_SQLPARAMOPTIONS,
  SQL_API_SQLPRIMARYKEYS,
  SQL_API_SQLPROCEDURECOLUMNS,
  SQL_API_SQLPROCEDURES,
  SQL_API_SQLSETPOS,
  SQL_API_SQLSETSCROLLOPTIONS,
  SQL_API_SQLTABLEPRIVILEGES
};


/**
Get information on which functions are supported by the driver.

@param[in]  hdbc      Handle of database connection
@param[in]  fFunction Function to check, @c SQL_API_ODBC3_ALL_FUNCTIONS,
or @c SQL_API_ALL_FUNCTIONS
@param[out] pfExists  Pointer to either one @c SQLUSMALLINT or an array
of SQLUSMALLINT for returning results

@since ODBC 1.0
@since ISO SQL 92
*/
SQLRETURN SQL_API SQLGetFunctions(SQLHDBC hdbc __attribute__((unused)),
                                  SQLUSMALLINT fFunction,
                                  SQLUSMALLINT *pfExists)
{
  SQLUSMALLINT index, myodbc_func_size;

  myodbc_func_size = sizeof(myodbc3_functions) / sizeof(myodbc3_functions[0]);

  if (fFunction == SQL_API_ODBC3_ALL_FUNCTIONS)
  {
    /* Clear and set bits in the 4000 bit vector */
    memset(pfExists, 0,
           sizeof(SQLUSMALLINT) * SQL_API_ODBC3_ALL_FUNCTIONS_SIZE);
    for (index = 0; index < myodbc_func_size; ++index)
    {
      SQLUSMALLINT id = myodbc3_functions[index];
      pfExists[id >> 4] |= (1 << (id & 0x000F));
    }
    return SQL_SUCCESS;
  }

  if (fFunction == SQL_API_ALL_FUNCTIONS)
  {
    /* Clear and set elements in the SQLUSMALLINT 100 element array */
    memset(pfExists, 0, sizeof(SQLUSMALLINT) * 100);
    for (index = 0; index < myodbc_func_size; ++index)
    {
      if (myodbc3_functions[index] < 100)
        pfExists[myodbc3_functions[index]] = SQL_TRUE;
    }
    return SQL_SUCCESS;
  }

  *pfExists = SQL_FALSE;
  for (index = 0; index < myodbc_func_size; ++index)
  {
    if (myodbc3_functions[index] == fFunction)
    {
      *pfExists = SQL_TRUE;
      break;
    }
  }

  return SQL_SUCCESS;
}