driver/info.c (1,045 lines of code) (raw):

/* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ #include <string.h> #include <assert.h> #include <stdio.h> #include "log.h" #include "handles.h" #include "queries.h" #include "connect.h" #include "info.h" #include "catalogue.h" #define ORIG_DISCRIM "IM" #define ORIG_CLASS_ISO "ISO 9075" #define ORIG_CLASS_ODBC "ODBC 3.0" #if defined(_WIN32) || defined (WIN32) /* DRV_NAME defined in CMakeLists.txt */ #define DRIVER_NAME STR(DRV_NAME) ".dll" #else /* win32 */ #endif /* win32 */ /* List of supported functions in the driver. * Advertize them as being all implemented and fail at call time, to prevent * an early failure in the client application. */ static SQLUSMALLINT esodbc_functions[] = { SQL_API_SQLALLOCHANDLE, SQL_API_SQLBINDCOL, SQL_API_SQLCANCEL, SQL_API_SQLCLOSECURSOR, SQL_API_SQLCOLATTRIBUTE, SQL_API_SQLCONNECT, SQL_API_SQLCOPYDESC, SQL_API_SQLDATASOURCES, SQL_API_SQLDESCRIBECOL, SQL_API_SQLDISCONNECT, SQL_API_SQLDRIVERS, SQL_API_SQLENDTRAN, SQL_API_SQLEXECDIRECT, SQL_API_SQLEXECUTE, SQL_API_SQLFETCH, SQL_API_SQLFETCHSCROLL, SQL_API_SQLFREEHANDLE, SQL_API_SQLFREESTMT, SQL_API_SQLGETCONNECTATTR, 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_SQLGETTYPEINFO, SQL_API_SQLNUMRESULTCOLS, SQL_API_SQLPARAMDATA, SQL_API_SQLPREPARE, SQL_API_SQLPUTDATA, SQL_API_SQLROWCOUNT, SQL_API_SQLSETCONNECTATTR, SQL_API_SQLSETCURSORNAME, SQL_API_SQLSETDESCFIELD, SQL_API_SQLSETDESCREC, SQL_API_SQLSETENVATTR, SQL_API_SQLSETSTMTATTR, SQL_API_SQLCOLUMNS, SQL_API_SQLSPECIALCOLUMNS, SQL_API_SQLSTATISTICS, SQL_API_SQLTABLES, SQL_API_SQLBINDPARAMETER, /* SQL_API_SQLBROWSECONNECT, */ /* SQL_API_SQLBULKOPERATIONS, */ SQL_API_SQLCOLUMNPRIVILEGES, SQL_API_SQLDESCRIBEPARAM, SQL_API_SQLDRIVERCONNECT, SQL_API_SQLFOREIGNKEYS, SQL_API_SQLMORERESULTS, SQL_API_SQLNATIVESQL, SQL_API_SQLNUMPARAMS, SQL_API_SQLPRIMARYKEYS, SQL_API_SQLPROCEDURECOLUMNS, SQL_API_SQLPROCEDURES, SQL_API_SQLSETPOS, SQL_API_SQLTABLEPRIVILEGES, }; #define ESODBC_FUNC_SIZE \ (sizeof(esodbc_functions)/sizeof(esodbc_functions[0])) #define SQL_FUNC_SET(pfExists, uwAPI) \ *(((UWORD*) (pfExists)) + ((uwAPI) >> 4)) |= (1 << ((uwAPI) & 0x000F)) #define SQL_API_ODBC2_ALL_FUNCTIONS_SIZE 100 /* requires following local vals defined: dbc, InfoValue, StringLengthPtr */ #define RET_INFO(_type, _val, _name) \ do { \ size_t _sz; \ switch (_type) { \ case SQL_C_USHORT: \ *(SQLUSMALLINT *)InfoValue = (SQLUSMALLINT)_val; \ _sz = sizeof(SQLUSMALLINT); \ DBGH(dbc, "requested: %s: %hu", _name, _val); \ break; \ case SQL_C_SHORT: \ case SQL_C_SSHORT: \ *(SQLSMALLINT *)InfoValue = (SQLSMALLINT)_val; \ _sz = sizeof(SQLSMALLINT); \ DBGH(dbc, "requested: %s: %hd", _name, _val); \ break; \ case SQL_C_ULONG: \ *(SQLUINTEGER *)InfoValue = (SQLUINTEGER)_val; \ _sz = sizeof(SQLUINTEGER); \ DBGH(dbc, "requested: %s: %lu", _name, _val); \ break; \ case SQL_C_LONG: \ case SQL_C_SLONG: \ *(SQLINTEGER *)InfoValue = (SQLINTEGER)_val; \ _sz = sizeof(SQLINTEGER); \ DBGH(dbc, "requested: %s: %ld", _name, _val); \ break; \ } \ if (StringLengthPtr) { \ /* not standard enforced, but MS Access required */ \ *StringLengthPtr = (SQLSMALLINT)_sz; \ } \ return SQL_SUCCESS; \ } while (0) #define RET_INF(_type, _val) \ RET_INFO(_type, _val, STR(# _val)) static SQLRETURN getinfo_driver( BOOL *handled, SQLHDBC ConnectionHandle, SQLUSMALLINT InfoType, _Out_writes_bytes_opt_(BufferLength) SQLPOINTER InfoValue, SQLSMALLINT BufferLength, _Out_opt_ SQLSMALLINT *StringLengthPtr) { esodbc_dbc_st *dbc = DBCH(ConnectionHandle); *handled = TRUE; switch(InfoType) { case SQL_ACTIVE_ENVIRONMENTS: RET_INFO(SQL_C_USHORT, 0, "max active environments"); /* "if the driver can execute functions asynchronously on the * connection handle" */ case SQL_ASYNC_DBC_FUNCTIONS: RET_INFO(SQL_C_ULONG, SQL_ASYNC_DBC_NOT_CAPABLE, "async DBC functions"); case SQL_ASYNC_MODE: RET_INFO(SQL_C_ULONG, SQL_AM_NONE, "async mode"); /* "if the driver supports asynchronous notification" */ case SQL_ASYNC_NOTIFICATION: RET_INFO(SQL_C_ULONG, SQL_ASYNC_NOTIFICATION_NOT_CAPABLE, "async notification"); case SQL_BATCH_ROW_COUNT: RET_INFO(SQL_C_ULONG, SQL_BRC_ROLLED_UP, "batch row count"); case SQL_BATCH_SUPPORT: RET_INFO(SQL_C_ULONG, 0, "batch support"); case SQL_DATA_SOURCE_NAME: DBGH(dbc, "requested: data source name: `" LWPDL "`.", LWSTR(&dbc->dsn)); return write_wstr(dbc, InfoValue, /* request may occur before connection */ dbc->dsn.str ? &dbc->dsn : &MK_WSTR(""), BufferLength, StringLengthPtr); case SQL_DRIVER_AWARE_POOLING_SUPPORTED: RET_INFO(SQL_C_ULONG, SQL_DRIVER_AWARE_POOLING_NOT_CAPABLE, "driver aware pooling"); /* * DM-only: case SQL_DRIVER_HDBC: case SQL_DRIVER_HDESC: case SQL_DRIVER_HENV: case SQL_DRIVER_HLIB: case SQL_DRIVER_HSTMT: */ case SQL_DRIVER_NAME: DBGH(dbc, "requested: driver (file) name: %s.", DRIVER_NAME); return write_wstr(dbc, InfoValue, &MK_WSTR(DRIVER_NAME), BufferLength, StringLengthPtr); /* Driver Information */ /* "what version of odbc a driver complies with" */ case SQL_DRIVER_ODBC_VER: return write_wstr(dbc, InfoValue, &MK_WSTR(ESODBC_SQL_SPEC_STRING), BufferLength, StringLengthPtr); case SQL_DRIVER_VER: DBGH(dbc, "requested: driver version (`%s`).", ESODBC_DRIVER_VER); return write_wstr(dbc, InfoValue, &MK_WSTR(ESODBC_DRIVER_VER), BufferLength, StringLengthPtr); case SQL_DTC_TRANSITION_COST: INFOH(dbc, "no connection pooling / DTC support."); RET_INFO(SQL_C_ULONG, 0, "DTC transition cost"); /* what Operations are supported by SQLSetPos */ // FIXME: review@alpha case SQL_DYNAMIC_CURSOR_ATTRIBUTES1: case SQL_DYNAMIC_CURSOR_ATTRIBUTES2: RET_INFO(SQL_C_ULONG, 0, "[dynamic cursor attributes]"); case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1: RET_INF(SQL_C_ULONG, ESODBC_FORWARD_ONLY_CURSOR_ATTRIBUTES1); case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2: RET_INF(SQL_C_ULONG, ESODBC_FORWARD_ONLY_CURSOR_ATTRIBUTES2); case SQL_FILE_USAGE: /* JDBC[0]: usesLocalFilePerTable() -- not ODBC applicable */ RET_INFO(SQL_C_USHORT, SQL_FILE_NOT_SUPPORTED, "file usage"); case SQL_GETDATA_EXTENSIONS: RET_INF(SQL_C_ULONG, ESODBC_GETDATA_EXTENSIONS); case SQL_INFO_SCHEMA_VIEWS: WARNH(dbc, "schema not yet supported by data source."); RET_INFO(SQL_C_ULONG, 0, "schema views"); case SQL_KEYSET_CURSOR_ATTRIBUTES1: case SQL_KEYSET_CURSOR_ATTRIBUTES2: RET_INFO(SQL_C_ULONG, 0, "[keyset cursor attributes]"); case SQL_MAX_ASYNC_CONCURRENT_STATEMENTS: RET_INFO(SQL_C_ULONG, 0, "async concurrent statements"); /* "the maximum number of active statements that the driver can * support for a connection" */ //case SQL_ACTIVE_STATEMENTS: case SQL_MAX_CONCURRENT_ACTIVITIES: RET_INFO(SQL_C_USHORT, 0, "max concurrent activities"); case SQL_MAX_DRIVER_CONNECTIONS: RET_INFO(SQL_C_USHORT, 0, "max driver connections"); case SQL_ODBC_INTERFACE_CONFORMANCE: RET_INF(SQL_C_ULONG, ESODBC_ODBC_INTERFACE_CONFORMANCE); // case SQL_ODBC_STANDARD_CLI_CONFORMANCE // undef'd: // case SQL_ODBC_VER: DM-only case SQL_PARAM_ARRAY_ROW_COUNTS: RET_INFO(SQL_C_ULONG, SQL_PARC_NO_BATCH, "param array row counts"); case SQL_PARAM_ARRAY_SELECTS: RET_INFO(SQL_C_ULONG, SQL_PAS_NO_SELECT, "result set availability " "with parameterized execution"); #if (ODBCVER < 0x0400) /* this is an ODBC 4.0, but Excel seems to asks for it anyways in * certain cases with a 3.80 driver */ case 180: /* = SQL_RETURN_ESCAPE_CLAUSE */ #else case SQL_RETURN_ESCAPE_CLAUSE: #endif /* SQL_RETURN_ESCAPE_CLAUSE */ /* actually an error, but can continue */ INFOH(dbc, "SQL_RETURN_ESCAPE_CLAUSE not supported."); RET_INFO(SQL_C_ULONG, /*SQL_RC_NONE*/0, "return escape clause"); case SQL_ROW_UPDATES: DBGH(dbc, "requested: row updates detection (N)."); WARNH(dbc, "no keyset-driven or mixed cursor support."); return write_wstr(dbc, InfoValue, &MK_WSTR("N"), BufferLength, StringLengthPtr); case SQL_SEARCH_PATTERN_ESCAPE: DBGH(dbc, "requested: escape character (`%s`).", ESODBC_PATTERN_ESCAPE); return write_wstr(dbc, InfoValue, &MK_WSTR(ESODBC_PATTERN_ESCAPE), BufferLength, StringLengthPtr); case SQL_SERVER_NAME: DBGH(dbc, "requested: server name: `" LWPDL "`.", LWSTR(&dbc->server)); return write_wstr(dbc, InfoValue, /* request may occur before connection */ dbc->server.str ? &dbc->server : &MK_WSTR(""), BufferLength, StringLengthPtr); case SQL_STATIC_CURSOR_ATTRIBUTES1: case SQL_STATIC_CURSOR_ATTRIBUTES2: WARNH(dbc, "no static cursor support."); RET_INFO(SQL_C_ULONG, 0, "[static cursor attributes]"); } *handled = FALSE; return SQL_ERROR; } static SQLRETURN getinfo_dbms_product( BOOL *handled, SQLHDBC ConnectionHandle, SQLUSMALLINT InfoType, _Out_writes_bytes_opt_(BufferLength) SQLPOINTER InfoValue, SQLSMALLINT BufferLength, _Out_opt_ SQLSMALLINT *StringLengthPtr) { esodbc_dbc_st *dbc = DBCH(ConnectionHandle); SQLINTEGER string_len; SQLRETURN ret; *handled = TRUE; switch(InfoType) { case SQL_DATABASE_NAME: DBGH(dbc, "requested database name."); ret = EsSQLGetConnectAttrW(ConnectionHandle, SQL_ATTR_CURRENT_CATALOG, InfoValue, (SQLINTEGER)BufferLength, &string_len); if (StringLengthPtr) { *StringLengthPtr = (SQLSMALLINT)string_len; } return ret; case SQL_DBMS_NAME: DBGH(dbc, "requested: DBMS name (`%s`).", ESODBC_ELASTICSEARCH_NAME); return write_wstr(dbc, InfoValue, &MK_WSTR(ESODBC_ELASTICSEARCH_NAME), BufferLength, StringLengthPtr); case SQL_DBMS_VER: DBGH(dbc, "requested: DBMS version (`" LWPDL "`).", LWSTR(&dbc->srv_ver)); return write_wstr(dbc, InfoValue, &dbc->srv_ver, BufferLength, StringLengthPtr); } *handled = FALSE; return SQL_ERROR; } static SQLRETURN getinfo_data_source( BOOL *handled, SQLHDBC ConnectionHandle, SQLUSMALLINT InfoType, _Out_writes_bytes_opt_(BufferLength) SQLPOINTER InfoValue, SQLSMALLINT BufferLength, _Out_opt_ SQLSMALLINT *StringLengthPtr) { esodbc_dbc_st *dbc = DBCH(ConnectionHandle); SQLSMALLINT used; *handled = TRUE; switch(InfoType) { case SQL_ACCESSIBLE_PROCEDURES: DBGH(dbc, "requested: accessible procedures (N)."); return write_wstr(dbc, InfoValue, &MK_WSTR("N"), BufferLength, StringLengthPtr); case SQL_ACCESSIBLE_TABLES: DBGH(dbc, "requested: accessible tables (`Y`)."); return write_wstr(dbc, InfoValue, &MK_WSTR("Y"), BufferLength, StringLengthPtr); case SQL_BOOKMARK_PERSISTENCE: RET_INFO(SQL_C_ULONG, 0, "bookmark persistence"); case SQL_CATALOG_TERM: /* SQL_QUALIFIER_TERM */ /* JDBC[0]: getCatalogSeparator() */ DBGH(dbc, "requested: catalog term (`%s`).", ESODBC_CATALOG_TERM); return write_wstr(dbc, InfoValue, &MK_WSTR(ESODBC_CATALOG_TERM), BufferLength, StringLengthPtr); case SQL_COLLATION_SEQ: DBGH(dbc, "requested: collation seq (`UTF8`)."); return write_wstr(dbc, InfoValue, &MK_WSTR("UTF8"), BufferLength, StringLengthPtr); case SQL_CONCAT_NULL_BEHAVIOR: RET_INFO(SQL_C_USHORT, SQL_CB_NULL, "concat NULL behavior"); case SQL_CURSOR_COMMIT_BEHAVIOR: case SQL_CURSOR_ROLLBACK_BEHAVIOR: DBGH(dbc, "requested: cursor %s behavior.", InfoType == SQL_CURSOR_COMMIT_BEHAVIOR ? "commit" : "rollback"); /* assume it's the equivalent of JDBC's HOLD_CURSORS_OVER_COMMIT */ RET_INFO(SQL_C_USHORT, SQL_CB_PRESERVE, "[cursor behavior]"); case SQL_CURSOR_SENSITIVITY: RET_INFO(SQL_C_ULONG, SQL_INSENSITIVE, "cursor sensitivity"); case SQL_DATA_SOURCE_READ_ONLY: DBGH(dbc, "requested: if data source is read only (Y)."); return write_wstr(dbc, InfoValue, &MK_WSTR("Y"), BufferLength, StringLengthPtr); case SQL_DEFAULT_TXN_ISOLATION: WARNH(dbc, "no support for transactions available."); RET_INFO(SQL_C_ULONG, 0, "def txn isolation"); case SQL_DESCRIBE_PARAMETER: DBGH(dbc, "requested: describe param (`N`)."); return write_wstr(dbc, InfoValue, &MK_WSTR("N"), BufferLength, StringLengthPtr); case SQL_MULT_RESULT_SETS: DBGH(dbc, "requested: multiple result set support (N)."); return write_wstr(dbc, InfoValue, &MK_WSTR("N"), BufferLength, StringLengthPtr); case SQL_MULTIPLE_ACTIVE_TXN: INFOH(dbc, "no transactions support."); DBGH(dbc, "requested: multiple active transactions (`Y`)."); /* returning Y in the spirit of concurrency */ return write_wstr(dbc, InfoValue, &MK_WSTR("Y"), BufferLength, StringLengthPtr); case SQL_NEED_LONG_DATA_LEN: DBGH(dbc, "requested: long data len (`N`)."); return write_wstr(dbc, InfoValue, &MK_WSTR("N"), BufferLength, StringLengthPtr); case SQL_NULL_COLLATION: RET_INFO(SQL_C_USHORT, SQL_NC_END, "null collation"); case SQL_PROCEDURE_TERM: DBGH(dbc, "requested: procedure term (``)."); return write_wstr(dbc, InfoValue, &MK_WSTR(""), BufferLength, StringLengthPtr); case SQL_SCHEMA_TERM: DBGH(dbc, "requested schema term (`%s`).", ESODBC_SCHEMA_TERM); return write_wstr(dbc, InfoValue, &MK_WSTR(ESODBC_SCHEMA_TERM), BufferLength, StringLengthPtr); case SQL_SCROLL_OPTIONS: RET_INFO(SQL_C_ULONG, SQL_SO_FORWARD_ONLY, "scroll options"); case SQL_TABLE_TERM: DBGH(dbc, "requested table term (`%s`).", ESODBC_TABLE_TERM); return write_wstr(dbc, InfoValue, &MK_WSTR(ESODBC_TABLE_TERM), BufferLength, StringLengthPtr); case SQL_TXN_CAPABLE: /* SQL_TRANSACTION_CAPABLE */ RET_INFO(SQL_C_USHORT, SQL_TC_NONE, "transaction capable"); case SQL_TXN_ISOLATION_OPTION: WARNH(dbc, "transactions not supported."); RET_INFO(SQL_C_ULONG, 0, "txn isolation options"); case SQL_USER_NAME: if (! dbc->es_types) { ERRH(dbc, "no connection active."); RET_HDIAGS(dbc, SQL_STATE_08003); } used = fetch_server_attr(dbc, (SQLINTEGER)SQL_USER_NAME, (SQLWCHAR *)InfoValue, BufferLength); if (used < 0) { ERRH(dbc, "failed to get current user."); RET_STATE(dbc->hdr.diag.state); } if (StringLengthPtr) { *StringLengthPtr = (SQLINTEGER)used; } return SQL_SUCCESS; } *handled = FALSE; return SQL_ERROR; } static SQLRETURN getinfo_sql( BOOL *handled, SQLHDBC ConnectionHandle, SQLUSMALLINT InfoType, _Out_writes_bytes_opt_(BufferLength) SQLPOINTER InfoValue, SQLSMALLINT BufferLength, _Out_opt_ SQLSMALLINT *StringLengthPtr) { esodbc_dbc_st *dbc = DBCH(ConnectionHandle); *handled = TRUE; switch(InfoType) { case SQL_AGGREGATE_FUNCTIONS: RET_INF(SQL_C_ULONG, ESODBC_AGGREGATE_FUNCTIONS); case SQL_ALTER_DOMAIN: RET_INFO(SQL_C_ULONG, 0, "alter domain"); // case SQL_ALTER_SCHEMA: // undef'd case SQL_ALTER_TABLE: RET_INFO(SQL_C_ULONG, 0, "alter table"); // case SQL_ANSI_SQL_DATETIME_LITERALS: // undef'd case SQL_CATALOG_LOCATION: RET_INFO(SQL_C_USHORT, SQL_CL_START, "catalog location"); case SQL_CATALOG_NAME: DBGH(dbc, "requested: catalog name support (Y)."); return write_wstr(dbc, InfoValue, &MK_WSTR("Y"), BufferLength, StringLengthPtr); case SQL_CATALOG_NAME_SEPARATOR: /* SQL_QUALIFIER_NAME_SEPARATOR */ /* JDBC[0]: getCatalogSeparator() */ DBGH(dbc, "requested: catalogue separator (`%s`).", ESODBC_CATALOG_SEPARATOR); return write_wstr(dbc, InfoValue, &MK_WSTR(ESODBC_CATALOG_SEPARATOR), BufferLength, StringLengthPtr); case SQL_CATALOG_USAGE: RET_INF(SQL_C_ULONG, ESODBC_CATALOG_USAGE); case SQL_COLUMN_ALIAS: DBGH(dbc, "requested: column alias (Y)."); return write_wstr(dbc, InfoValue, &MK_WSTR("Y"), BufferLength, StringLengthPtr); case SQL_CORRELATION_NAME: // JDBC[0]: supportsDifferentTableCorrelationNames() /* TODO: JDBC returns true for correlation, but false for * difference. How to signal that in ODBC?? (with no bit mask) */ RET_INFO(SQL_C_USHORT, SQL_CN_ANY, "table correlation names"); case SQL_CREATE_ASSERTION: case SQL_CREATE_CHARACTER_SET: case SQL_CREATE_COLLATION: case SQL_CREATE_DOMAIN: case SQL_CREATE_SCHEMA: case SQL_CREATE_TABLE: case SQL_CREATE_TRANSLATION: RET_INFO(SQL_C_ULONG, 0, "[create statement]"); case SQL_DATETIME_LITERALS: RET_INF(SQL_C_ULONG, ESODBC_DATETIME_LITERALS); case SQL_DDL_INDEX: RET_INFO(SQL_C_ULONG, 0, "index managment"); case SQL_DROP_ASSERTION: case SQL_DROP_CHARACTER_SET: case SQL_DROP_COLLATION: case SQL_DROP_DOMAIN: case SQL_DROP_SCHEMA: case SQL_DROP_TABLE: case SQL_DROP_TRANSLATION: case SQL_DROP_VIEW: RET_INFO(SQL_C_ULONG, 0, "[drop statement]"); case SQL_EXPRESSIONS_IN_ORDERBY: DBGH(dbc, "requested: expressions in order by (Y)."); return write_wstr(dbc, InfoValue, &MK_WSTR("Y"), BufferLength, StringLengthPtr); case SQL_GROUP_BY: RET_INFO(SQL_C_USHORT, SQL_GB_NO_RELATION, "group by"); case SQL_IDENTIFIER_CASE: RET_INFO(SQL_C_USHORT, SQL_IC_MIXED, "identifier case"); case SQL_IDENTIFIER_QUOTE_CHAR: /* JDBC[0]: getIdentifierQuoteString() */ DBGH(dbc, "requested: quoting char (`%s`).", ESODBC_QUOTE_CHAR); return write_wstr(dbc, InfoValue, &MK_WSTR(ESODBC_QUOTE_CHAR), BufferLength, StringLengthPtr); case SQL_INDEX_KEYWORDS: RET_INFO(SQL_C_USHORT, SQL_IK_NONE, "identifier case"); case SQL_INSERT_STATEMENT: RET_INFO(SQL_C_ULONG, 0, "insert support"); case SQL_INTEGRITY: DBGH(dbc, "requested: Integrity Enhancement Facility (N)."); return write_wstr(dbc, InfoValue, &MK_WSTR("N"), BufferLength, StringLengthPtr); case SQL_KEYWORDS: ERRH(dbc, "attribute 'KEYWORDS' not yet implemented."); break; // TODO case SQL_LIKE_ESCAPE_CLAUSE: DBGH(dbc, "requested: like escape clause (Y)."); return write_wstr(dbc, InfoValue, &MK_WSTR("Y"), BufferLength, StringLengthPtr); case SQL_NON_NULLABLE_COLUMNS: /* JDBC[0]: supportsNonNullableColumns() */ RET_INFO(SQL_C_USHORT, SQL_NNC_NULL, "nullable columns"); case SQL_SQL_CONFORMANCE: RET_INF(SQL_C_ULONG, ESODBC_SQL_CONFORMANCE); case SQL_OJ_CAPABILITIES: /* SQL_OUTER_JOIN_CAPABILITIES */ RET_INFO(SQL_C_ULONG, 0, "outer joins capabilities"); case SQL_ORDER_BY_COLUMNS_IN_SELECT: DBGH(dbc, "requested: order by columns in select (N)."); return write_wstr(dbc, InfoValue, &MK_WSTR("N"), BufferLength, StringLengthPtr); case SQL_OUTER_JOINS: DBGH(dbc, "requested: outer join support (Y)."); return write_wstr(dbc, InfoValue, &MK_WSTR("Y"), BufferLength, StringLengthPtr); case SQL_PROCEDURES: DBGH(dbc, "requested: procedures (N)."); return write_wstr(dbc, InfoValue, &MK_WSTR("N"), BufferLength, StringLengthPtr); case SQL_QUOTED_IDENTIFIER_CASE: RET_INFO(SQL_C_USHORT, SQL_IC_SENSITIVE, "quoted identifier case"); case SQL_SCHEMA_USAGE: /* no schema support, but accepted (=currently ignored) by drv */ RET_INFO(SQL_C_ULONG, SQL_SU_PROCEDURE_INVOCATION, "schema usage"); case SQL_SPECIAL_CHARACTERS: DBGH(dbc, "requested: special characters (`%s`).", ESODBC_SPECIAL_CHARACTERS); return write_wstr(dbc, InfoValue, &MK_WSTR(ESODBC_SPECIAL_CHARACTERS), BufferLength, StringLengthPtr); case SQL_SQL92_DATETIME_FUNCTIONS: RET_INF(SQL_C_ULONG, ESODBC_SQL92_DATETIME_FUNCTIONS); case SQL_SQL92_NUMERIC_VALUE_FUNCTIONS: RET_INF(SQL_C_ULONG, ESODBC_SQL92_NUMERIC_VALUE_FUNCTIONS); case SQL_SQL92_PREDICATES: RET_INF(SQL_C_ULONG, ESODBC_SQL92_PREDICATES); case SQL_SQL92_RELATIONAL_JOIN_OPERATORS: RET_INF(SQL_C_ULONG, ESODBC_SQL92_RELATIONAL_JOIN_OPERATORS); //case SQL_STRING_FUNCTIONS: case SQL_SQL92_STRING_FUNCTIONS: RET_INF(SQL_C_ULONG, ESODBC_SQL92_STRING_FUNCTIONS); case SQL_SQL92_VALUE_EXPRESSIONS: RET_INF(SQL_C_ULONG, ODBC_SQL92_VALUE_EXPRESSIONS); case SQL_SUBQUERIES: RET_INFO(SQL_C_ULONG, 0, "subqueries support"); case SQL_UNION: RET_INFO(SQL_C_ULONG, SQL_U_UNION_ALL, "union support"); } *handled = FALSE; return SQL_ERROR; } static SQLRETURN getinfo_sql_limits( BOOL *handled, SQLHDBC ConnectionHandle, SQLUSMALLINT InfoType, _Out_writes_bytes_opt_(BufferLength) SQLPOINTER InfoValue, SQLSMALLINT BufferLength, _Out_opt_ SQLSMALLINT *StringLengthPtr) { esodbc_dbc_st *dbc = DBCH(ConnectionHandle); size_t len; *handled = TRUE; /*INDENT-OFF*/ switch(InfoType) { do { /* spec (alphabetically) ordered */ case SQL_MAX_BINARY_LITERAL_LEN: len = sizeof(SQLUINTEGER); break; case SQL_MAX_CATALOG_NAME_LEN: len = sizeof(SQLUSMALLINT); break; case SQL_MAX_CHAR_LITERAL_LEN: len = sizeof(SQLUINTEGER); break; case SQL_MAX_COLUMN_NAME_LEN: len = sizeof(SQLUSMALLINT); break; case SQL_MAX_COLUMNS_IN_GROUP_BY: len = sizeof(SQLUSMALLINT); break; case SQL_MAX_COLUMNS_IN_INDEX: len = sizeof(SQLUSMALLINT); break; case SQL_MAX_COLUMNS_IN_ORDER_BY: len = sizeof(SQLUSMALLINT); break; case SQL_MAX_COLUMNS_IN_SELECT: len = sizeof(SQLUSMALLINT); break; case SQL_MAX_COLUMNS_IN_TABLE: len = sizeof(SQLUSMALLINT); break; case SQL_MAX_CURSOR_NAME_LEN: len = sizeof(SQLUSMALLINT); break; case SQL_MAX_IDENTIFIER_LEN: DBGH(dbc, "requested: max identifier len (%u).", ESODBC_MAX_IDENTIFIER_LEN); *(SQLUSMALLINT *)InfoValue = ESODBC_MAX_IDENTIFIER_LEN; return SQL_SUCCESS; case SQL_MAX_INDEX_SIZE: len = sizeof(SQLUINTEGER); break; case SQL_MAX_PROCEDURE_NAME_LEN: len = sizeof(SQLUSMALLINT); break; case SQL_MAX_ROW_SIZE: len = sizeof(SQLUINTEGER); break; case SQL_MAX_ROW_SIZE_INCLUDES_LONG: DBGH(dbc, "requested: max row size includes longs (Y)."); return write_wstr(dbc, InfoValue, &MK_WSTR("Y"), BufferLength, StringLengthPtr); case SQL_MAX_SCHEMA_NAME_LEN: len = sizeof(SQLUSMALLINT); break; case SQL_MAX_STATEMENT_LEN: len = sizeof(SQLUINTEGER); break; case SQL_MAX_TABLE_NAME_LEN: len = sizeof(SQLUSMALLINT); break; case SQL_MAX_TABLES_IN_SELECT: len = sizeof(SQLUSMALLINT); break; case SQL_MAX_USER_NAME_LEN: len = sizeof(SQLUSMALLINT); break; } while (0); DBGH(dbc, "requested: max %hu (0).", InfoType); memset(InfoValue, 0, len); if (StringLengthPtr) { *StringLengthPtr = (SQLSMALLINT)len; } return SQL_SUCCESS; } /*INDENT-ON*/ *handled = FALSE; return SQL_ERROR; } static SQLRETURN getinfo_scalars( BOOL *handled, SQLHDBC ConnectionHandle, SQLUSMALLINT InfoType, _Out_writes_bytes_opt_(BufferLength) SQLPOINTER InfoValue, SQLSMALLINT BufferLength, _Out_opt_ SQLSMALLINT *StringLengthPtr) { esodbc_dbc_st *dbc = DBCH(ConnectionHandle); *handled = TRUE; switch(InfoType) { case SQL_CONVERT_FUNCTIONS: RET_INF(SQL_C_ULONG, ESODBC_CONVERT_FUNCTIONS); case SQL_NUMERIC_FUNCTIONS: RET_INF(SQL_C_ULONG, ESODBC_NUMERIC_FUNCTIONS); case SQL_STRING_FUNCTIONS: RET_INF(SQL_C_ULONG, ESODBC_STRING_FUNCTIONS); case SQL_SYSTEM_FUNCTIONS: RET_INF(SQL_C_ULONG, ESODBC_SYSTEM_FUNCTIONS); case SQL_TIMEDATE_ADD_INTERVALS: RET_INF(SQL_C_ULONG, ESODBC_TIMEDATE_ADD_INTERVALS); case SQL_TIMEDATE_DIFF_INTERVALS: RET_INF(SQL_C_ULONG, ESODBC_TIMEDATE_DIFF_INTERVALS); case SQL_TIMEDATE_FUNCTIONS: RET_INF(SQL_C_ULONG, ESODBC_TIMEDATE_FUNCTIONS); } *handled = FALSE; return SQL_ERROR; } /* accepted conversion destination types */ static inline SQLUINTEGER conv_exclude(SQLUINTEGER exclude) { static SQLUINTEGER all_types = 0 | 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_BINARY | SQL_CVT_VARBINARY | SQL_CVT_BIT | SQL_CVT_TINYINT | SQL_CVT_BIGINT | SQL_CVT_DATE | SQL_CVT_TIME | SQL_CVT_TIMESTAMP | SQL_CVT_LONGVARBINARY | SQL_CVT_INTERVAL_YEAR_MONTH | SQL_CVT_INTERVAL_DAY_TIME | SQL_CVT_WCHAR | SQL_CVT_WLONGVARCHAR | SQL_CVT_WVARCHAR | SQL_CVT_GUID; /* intervals not yet supported */ exclude |= SQL_CVT_INTERVAL_YEAR_MONTH | SQL_CVT_INTERVAL_DAY_TIME; /* GUID not convertible */ exclude |= SQL_CVT_GUID; /* binary not convertible */ exclude |= SQL_CVT_BINARY | SQL_CVT_VARBINARY | SQL_CVT_LONGVARBINARY; return all_types & ~exclude; } static SQLRETURN getinfo_conversion( BOOL *handled, SQLHDBC ConnectionHandle, SQLUSMALLINT InfoType, _Out_writes_bytes_opt_(BufferLength) SQLPOINTER InfoValue, SQLSMALLINT BufferLength, _Out_opt_ SQLSMALLINT *StringLengthPtr) { esodbc_dbc_st *dbc = DBCH(ConnectionHandle); *handled = TRUE; switch(InfoType) { /* source type */ case SQL_CONVERT_BIGINT: case SQL_CONVERT_INTEGER: case SQL_CONVERT_SMALLINT: case SQL_CONVERT_TINYINT: case SQL_CONVERT_BIT: case SQL_CONVERT_CHAR: case SQL_CONVERT_LONGVARCHAR: case SQL_CONVERT_VARCHAR: case SQL_CONVERT_WCHAR: case SQL_CONVERT_WLONGVARCHAR: case SQL_CONVERT_WVARCHAR: case SQL_CONVERT_DATE: case SQL_CONVERT_TIME: case SQL_CONVERT_TIMESTAMP: case SQL_CONVERT_DECIMAL: case SQL_CONVERT_DOUBLE: case SQL_CONVERT_FLOAT: case SQL_CONVERT_NUMERIC: case SQL_CONVERT_REAL: DBGH(dbc, "convert from %lu type: 0x%lx.", InfoType, conv_exclude(0LU)); RET_INFO(SQL_C_ULONG, conv_exclude(0LU), "[convert supported]"); case SQL_CONVERT_BINARY: case SQL_CONVERT_VARBINARY: case SQL_CONVERT_LONGVARBINARY: case SQL_CONVERT_INTERVAL_YEAR_MONTH: case SQL_CONVERT_INTERVAL_DAY_TIME: case SQL_CONVERT_GUID: DBGH(dbc, "convert from type %lu: 0 " "(source type not convertible).", InfoType); RET_INFO(SQL_C_ULONG, 0, "[convert unsupported]"); } *handled = FALSE; return SQL_ERROR; } static SQLRETURN getinfo_deprecated( BOOL *handled, SQLHDBC ConnectionHandle, SQLUSMALLINT InfoType, _Out_writes_bytes_opt_(BufferLength) SQLPOINTER InfoValue, SQLSMALLINT BufferLength, _Out_opt_ SQLSMALLINT *StringLengthPtr) { esodbc_dbc_st *dbc = DBCH(ConnectionHandle); *handled = TRUE; switch(InfoType) { case SQL_FETCH_DIRECTION: RET_INF(SQL_C_LONG, ESODBC_FETCH_DIRECTION); case SQL_POS_OPERATIONS: RET_INF(SQL_C_LONG, ESODBC_POS_OPERATIONS); case SQL_LOCK_TYPES: RET_INF(SQL_C_LONG, ESODBC_LOCK_TYPES); case SQL_POSITIONED_STATEMENTS: RET_INF(SQL_C_LONG, ESODBC_POSITIONED_STATEMENTS); case SQL_ODBC_API_CONFORMANCE: RET_INF(SQL_C_SHORT, ESODBC_ODBC_API_CONFORMANCE); case SQL_SCROLL_CONCURRENCY: RET_INF(SQL_C_LONG, ESODBC_SCROLL_CONCURRENCY); case SQL_ODBC_SQL_CONFORMANCE: RET_INF(SQL_C_SHORT, ESODBC_ODBC_SQL_CONFORMANCE); case SQL_STATIC_SENSITIVITY: RET_INF(SQL_C_LONG, ESODBC_STATIC_SENSITIVITY); } *handled = FALSE; return SQL_ERROR; } #undef RET_INF #undef RET_INFO // [0] x-p-es/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcDatabaseMetaData.java : DatabaseMetaData /* * """ * The SQL_MAX_DRIVER_CONNECTIONS option in SQLGetInfo specifies how many * active connections a particular driver supports. * """ */ SQLRETURN EsSQLGetInfoW( SQLHDBC ConnectionHandle, SQLUSMALLINT InfoType, _Out_writes_bytes_opt_(BufferLength) SQLPOINTER InfoValue, SQLSMALLINT BufferLength, _Out_opt_ SQLSMALLINT *StringLengthPtr) { esodbc_dbc_st *dbc = DBCH(ConnectionHandle); BOOL handled; SQLRETURN ret; ret = getinfo_driver(&handled, ConnectionHandle, InfoType, InfoValue, BufferLength, StringLengthPtr); if (handled) { return ret; } ret = getinfo_dbms_product(&handled, ConnectionHandle, InfoType, InfoValue, BufferLength, StringLengthPtr); if (handled) { return ret; } ret = getinfo_data_source(&handled, ConnectionHandle, InfoType, InfoValue, BufferLength, StringLengthPtr); if (handled) { return ret; } ret = getinfo_sql(&handled, ConnectionHandle, InfoType, InfoValue, BufferLength, StringLengthPtr); if (handled) { return ret; } ret = getinfo_sql_limits(&handled, ConnectionHandle, InfoType, InfoValue, BufferLength, StringLengthPtr); if (handled) { return ret; } ret = getinfo_scalars(&handled, ConnectionHandle, InfoType, InfoValue, BufferLength, StringLengthPtr); if (handled) { return ret; } ret = getinfo_conversion(&handled, ConnectionHandle, InfoType, InfoValue, BufferLength, StringLengthPtr); if (handled) { return ret; } ret = getinfo_deprecated(&handled, ConnectionHandle, InfoType, InfoValue, BufferLength, StringLengthPtr); if (handled) { return ret; } ERRH(dbc, "unknown InfoType: %u.", InfoType); RET_HDIAGS(dbc, SQL_STATE_HY096); } /* TODO: see error.h: esodbc_errors definition note on 2.x apps support */ /* Note: with SQL_DIAG_SQLSTATE DM provides a NULL StringLengthPtr */ SQLRETURN EsSQLGetDiagFieldW( SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, SQLSMALLINT DiagIdentifier, _Out_writes_opt_(_Inexpressible_(BufferLength)) SQLPOINTER DiagInfoPtr, SQLSMALLINT BufferLength, _Out_opt_ SQLSMALLINT *StringLengthPtr) { esodbc_dbc_st *dbc; esodbc_diag_st *diag, bak; esodbc_env_st dummy; SQLSMALLINT used; size_t len; void *srcptr; wstr_st *wstrp, wstr; SQLRETURN ret; if (RecNumber <= 0) { ERRH(Handle, "record number must be >=1; received: %d.", RecNumber); return SQL_ERROR; } else if (1 < RecNumber) { return SQL_NO_DATA; } if (! Handle) { ERR("null handle provided."); return SQL_INVALID_HANDLE; } diag = &HDRH(Handle)->diag; /* GetDiagField can't set diagnostics itself, so use a dummy */ *HDRH(&dummy) = *HDRH(Handle); /* need a valid hhdr struct */ /*INDENT-OFF*/ switch (DiagIdentifier) { /* Header Fields */ case SQL_DIAG_NUMBER: if (! DiagInfoPtr) { ERRH(Handle, "NULL DiagInfo with SQL_DIAG_NUMBER"); return SQL_ERROR; } *(SQLINTEGER *)DiagInfoPtr = (diag->state != SQL_STATE_00000) ? 1 : 0; DBGH(Handle, "available diagnostics count: %ld.", *(SQLINTEGER *)DiagInfoPtr); return SQL_SUCCESS; case SQL_DIAG_CURSOR_ROW_COUNT: case SQL_DIAG_DYNAMIC_FUNCTION: case SQL_DIAG_DYNAMIC_FUNCTION_CODE: case SQL_DIAG_ROW_COUNT: /* should be handled by DM */ if (HandleType != SQL_HANDLE_STMT) { ERRH(Handle, "DiagIdentifier %d called with non-statement " "handle type %d.", DiagIdentifier, HandleType); return SQL_ERROR; } ERRH(Handle, "DiagIdentifier %hd is not supported."); return SQL_ERROR; /* case SQL_DIAG_RETURNCODE: break; -- DM only */ /* Record Fields */ do { case SQL_DIAG_CLASS_ORIGIN: len = (sizeof(ORIG_DISCRIM) - 1) * sizeof (SQLWCHAR); assert(len <= sizeof(esodbc_errors[diag->state].code)); if (memcmp(esodbc_errors[diag->state].code, MK_WPTR(ORIG_DISCRIM), len) == 0) { wstrp = &MK_WSTR(ORIG_CLASS_ODBC); } else { wstrp = &MK_WSTR(ORIG_CLASS_ISO); } break; case SQL_DIAG_SUBCLASS_ORIGIN: switch (diag->state) { case SQL_STATE_01S00: case SQL_STATE_01S01: case SQL_STATE_01S02: case SQL_STATE_01S06: case SQL_STATE_01S07: case SQL_STATE_07S01: case SQL_STATE_08S01: case SQL_STATE_21S01: case SQL_STATE_21S02: case SQL_STATE_25S01: case SQL_STATE_25S02: case SQL_STATE_25S03: case SQL_STATE_42S01: case SQL_STATE_42S02: case SQL_STATE_42S11: case SQL_STATE_42S12: case SQL_STATE_42S21: case SQL_STATE_42S22: case SQL_STATE_HY095: case SQL_STATE_HY097: case SQL_STATE_HY098: case SQL_STATE_HY099: case SQL_STATE_HY100: case SQL_STATE_HY101: case SQL_STATE_HY105: case SQL_STATE_HY107: case SQL_STATE_HY109: case SQL_STATE_HY110: case SQL_STATE_HY111: case SQL_STATE_HYT00: case SQL_STATE_HYT01: case SQL_STATE_IM001: case SQL_STATE_IM002: case SQL_STATE_IM003: case SQL_STATE_IM004: case SQL_STATE_IM005: case SQL_STATE_IM006: case SQL_STATE_IM007: case SQL_STATE_IM008: case SQL_STATE_IM010: case SQL_STATE_IM011: case SQL_STATE_IM012: wstrp = &MK_WSTR(ORIG_CLASS_ODBC); break; default: wstrp = &MK_WSTR(ORIG_CLASS_ISO); } break; } while (0); DBGH(Handle, "diagnostic code '"LWPD"' is of class '" LWPDL "'.", esodbc_errors[diag->state].code, LWSTR(wstrp)); return write_wstr(&dummy, DiagInfoPtr, wstrp, BufferLength, StringLengthPtr); do { case SQL_DIAG_CONNECTION_NAME: case SQL_DIAG_SERVER_NAME: switch (HandleType) { case SQL_HANDLE_DBC: dbc = DBCH(Handle); break; case SQL_HANDLE_STMT: dbc = STMH(Handle)->hdr.dbc; break; case SQL_HANDLE_DESC: dbc = DSCH(Handle)->hdr.stmt->hdr.dbc; break; default: ERR("unknown handle type %hd, @0x%p.", HandleType, Handle); return SQL_ERROR; // SQL_INVALID_HANDLE? } } while (0); /* save (and then restore) the diag state, since this call mustn't * change it */ bak = dbc->hdr.diag; ret = EsSQLGetInfoW(dbc, DiagIdentifier == SQL_DIAG_CONNECTION_NAME ? SQL_DATA_SOURCE_NAME : SQL_SERVER_NAME, DiagInfoPtr, BufferLength, StringLengthPtr); dbc->hdr.diag = bak; return ret; case SQL_DIAG_MESSAGE_TEXT: wstr.str = diag->text; wstr.cnt = diag->text_len; return write_wstr(Handle, DiagInfoPtr, &wstr, BufferLength * sizeof(*diag->text), StringLengthPtr); do { case SQL_DIAG_NATIVE: len = sizeof(diag->native_code); srcptr = &diag->native_code; break; case SQL_DIAG_COLUMN_NUMBER: len = sizeof(diag->column_number); srcptr = &diag->column_number; break; case SQL_DIAG_ROW_NUMBER: len = sizeof(diag->row_number); srcptr = &diag->row_number; break; } while (0); if (BufferLength != SQL_IS_POINTER) { WARNH(Handle, "BufferLength param not indicating a ptr type."); } if (! DiagInfoPtr) { ERRH(Handle, "integer diagnostic field %hd asked for, but " "NULL destination provided."); RET_HDIAGS(Handle, SQL_STATE_HY009); } else { memcpy(DiagInfoPtr, srcptr, len); } return SQL_SUCCESS; case SQL_DIAG_SQLSTATE: if (diag->state == SQL_STATE_00000) { DBGH(Handle, "no diagnostic available for handle type %d.", HandleType); /* "The function also returns SQL_NO_DATA for any positive * RecNumber if there are no diagnostic records for Handle" */ return SQL_NO_DATA; } wstr.str = esodbc_errors[diag->state].code; wstr.cnt = wcslen(wstr.str); ret = write_wstr(&dummy, DiagInfoPtr, &wstr, BufferLength, &used); if (StringLengthPtr) { *StringLengthPtr = used; } else { /* SQLSTATE is always on 5 chars, but this Identifier sticks * out, by not being given a buffer to write this into */ WARNH(Handle, "SQLSTATE writen on %uB, but no output buffer " "provided.", used); } return ret; default: ERRH(Handle, "unknown DiagIdentifier: %d.", DiagIdentifier); return SQL_ERROR; } /*INDENT-ON*/ assert(0); // FIXME: shouldn't get here return SQL_ERROR; } /* * https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/appendix-a-odbc-error-codes : * """ * SQLGetDiagRec or SQLGetDiagField returns SQLSTATE values as defined by Open * Group Data Management: Structured Query Language (SQL), Version 2 (March * 1995). SQLSTATE values are strings that contain five characters. * """ * https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/implementing-sqlgetdiagrec-and-sqlgetdiagfield : */ /* TODO: see error.h: esodbc_errors definition note on 2.x apps support */ SQLRETURN EsSQLGetDiagRecW ( SQLSMALLINT HandleType, SQLHANDLE Handle, SQLSMALLINT RecNumber, _Out_writes_opt_(6) SQLWCHAR *Sqlstate, SQLINTEGER *NativeError, _Out_writes_opt_(BufferLength) SQLWCHAR *MessageText, SQLSMALLINT BufferLength, _Out_opt_ SQLSMALLINT *TextLength ) { esodbc_diag_st *diag; esodbc_env_st dummy; SQLRETURN ret; SQLSMALLINT used; wstr_st wstr; if (RecNumber <= 0) { ERRH(Handle, "record number must be >=1; received: %d.", RecNumber); return SQL_ERROR; } if (1 < RecNumber) { /* XXX: does it make sense to have error FIFOs? maybe, for one * diagnostic per failed fetched row */ // WARN("no error lists supported (yet)."); return SQL_NO_DATA; } if (! Handle) { ERRH(Handle, "NULL handle provided."); return SQL_ERROR; } else if (HandleType == SQL_HANDLE_SENV) { /* * https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgetdiagrec-function : * """ * A call to SQLGetDiagRec will return SQL_INVALID_HANDLE if * HandleType is SQL_HANDLE_SENV. * """ */ ERRH(Handle, "shared environment handle type not allowed."); return SQL_INVALID_HANDLE; } else { diag = &HDRH(Handle)->diag; } if (diag->state == SQL_STATE_00000) { INFOH(Handle, "no diagnostic record available for handle type %d.", HandleType); return SQL_NO_DATA; } /* not documented in API, but both below can be null. */ /* API assumes there's always enough buffer here.. */ /* no err indicator */ if (Sqlstate) { wcscpy(Sqlstate, esodbc_errors[diag->state].code); } if (NativeError) { *NativeError = diag->native_code; } wstr.str = diag->text; wstr.cnt = diag->text_len; *HDRH(&dummy) = *HDRH(Handle); /* need a valid hhdr struct */ ret = write_wstr(&dummy, MessageText, &wstr, BufferLength * sizeof(*MessageText), &used); if (TextLength) { *TextLength = used / sizeof(*MessageText); } return ret; } SQLRETURN EsSQLGetFunctions(SQLHDBC ConnectionHandle, SQLUSMALLINT FunctionId, _Out_writes_opt_ (_Inexpressible_("Buffer length pfExists points to depends on fFunction value.")) SQLUSMALLINT *Supported) { int i; if (FunctionId == SQL_API_ODBC3_ALL_FUNCTIONS) { DBGH(ConnectionHandle, "ODBC 3.x application asking for supported " "function set."); memset(Supported, 0, SQL_API_ODBC3_ALL_FUNCTIONS_SIZE * sizeof(SQLSMALLINT)); for (i = 0; i < ESODBC_FUNC_SIZE; i++) { SQL_FUNC_SET(Supported, esodbc_functions[i]); } } else if (FunctionId == SQL_API_ALL_FUNCTIONS) { DBGH(ConnectionHandle, "ODBC 2.x application asking for supported " "function set."); memset(Supported, SQL_FALSE, SQL_API_ODBC2_ALL_FUNCTIONS_SIZE * sizeof(SQLUSMALLINT)); for (i = 0; i < ESODBC_FUNC_SIZE; i++) if (esodbc_functions[i] < SQL_API_ODBC2_ALL_FUNCTIONS_SIZE) { Supported[esodbc_functions[i]] = SQL_TRUE; } } else { DBGH(ConnectionHandle, "application asking for support of function " "#%d.", FunctionId); *Supported = SQL_FALSE; for (i = 0; i < ESODBC_FUNC_SIZE; i++) if (esodbc_functions[i] == FunctionId) { *Supported = SQL_TRUE; break; } } return SQL_SUCCESS; } /* "If the DataType argument specifies a data type which is valid for the * version of ODBC supported by the driver, but is not supported by the * driver, then it will return an empty result set." */ SQLRETURN EsSQLGetTypeInfoW(SQLHSTMT StatementHandle, SQLSMALLINT DataType) { #define SQL_TYPES_STMT "SYS TYPES" esodbc_stmt_st *stmt = STMH(StatementHandle); SQLWCHAR wbuff[sizeof(SQL_TYPES_STMT " 32767")]; int cnt; DBGH(stmt, "requested type description for type %hd.", DataType); cnt = swprintf(wbuff, sizeof(wbuff)/sizeof(*wbuff), MK_WPTR(SQL_TYPES_STMT " %hd"), DataType); if (cnt <= 0) { ERRNH(stmt, "failed to print catalog query."); RET_HDIAGS(stmt, SQL_STATE_HY000); } return EsSQLExecDirectW(stmt, wbuff, cnt); # undef SQL_TYPES_STMT } /* vim: set noet fenc=utf-8 ff=dos sts=0 sw=4 ts=4 tw=78 : */