ODBC Driver API support¶
This topic lists the ODBC routines relevant to Snowflake and indicates whether they are supported. The routines are organized into categories based on the function they perform.
ODBC 3.x advertises conformance with version 3.52 of the ODBC API, and ODBC 4.x advertises version 3.80. Where the two versions behave differently, the Notes column says so. For the full list of changes, see Migrating from ODBC Driver 3.x to 4.x.
For the complete API reference, see the Microsoft ODBC Programmer’s Reference.
Connecting to a data source¶
| Function Name | Supported | Notes |
|---|---|---|
SQLAllocHandle | ✔ | |
SQLConnect | ✔ | |
SQLDriverConnect | ✔ | |
SQLAllocEnv | ✔ | Supported by the Snowflake driver, but deprecated in ODBC API version 3.x. |
SQLAllocConnect | ✔ | Supported by the Snowflake driver, but deprecated in ODBC API version 3.x. |
SQLBrowseConnect | ✔ |
Obtaining information about a driver and data source¶
| Function Name | Supported | Notes |
|---|---|---|
SQLDataSources | ✔ | |
SQLDrivers | ✔ | |
SQLGetInfo | ✔ | |
SQLGetFunctions | ✔ | |
SQLGetTypeInfo | ✔ | ODBC 4.x always reports COLUMN_SIZE 29 for SQL_TYPE_TIMESTAMP, and doesn’t accept the ODBC_USE_STANDARD_TIMESTAMP_COLUMNSIZE parameter. |
Setting and retrieving driver attributes¶
| Function Name | Supported | Notes |
|---|---|---|
SQLSetConnectAttr | ✔ | Setting SQL_ATTR_METADATA_ID puts catalog function arguments into identifier mode, and the two versions fold unquoted identifiers differently. See catalog functions. |
SQLGetConnectAttr | ✔ | Read-only mode is not supported. SQL_MODE_READ_ONLY is passed to the driver, but Snowflake still writes to the database. Also, some attributes were introduced post API version 3.52: SQL_ATTR_ASYNC_DBC_EVENT, SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, SQL_ATTR_ASYNC_DBC_PCALLBACK, SQL_ATTR_ASYNC_DBC_PCONTEXT, SQL_ATTR_DBC_INFO_TOKEN. |
SQLSetConnectOption | ✔ | Supported by the Snowflake driver, but deprecated in ODBC API version 3.x. |
SQLGetConnectOption | ✔ | Supported by the Snowflake driver, but deprecated in ODBC API version 3.x. |
SQLSetEnvAttr | ✔ | |
SQLGetEnvAttr | ✔ | The SQL_ATTR_CONNECTION_POOLING attribute was introduced after ODBC API version 3.52 and is not supported. |
SQLSetStmtAttr | ✔ | SQL_ATTR_CURSOR_SCROLLABLE only supports a SQL_NONSCROLLABLE value. SQL_ATTR_USE_BOOKMARKS only supports a SQL_UB_OFF value. SQL_ATTR_CURSOR_TYPE only supports a SQL_CURSOR_FORWARD_ONLY value. In ODBC 4.x, any other value is replaced with SQL_CURSOR_FORWARD_ONLY and the function returns SQL_SUCCESS_WITH_INFO with SQLSTATE 01S02. ODBC 3.x returns SQL_SUCCESS. In ODBC 3.x, SQL_ATTR_ENABLE_AUTO_IPD defaults to true for compatibility with third-party tools, even though the ODBC standard says it should default to false. To change the default to false, set the EnableAutoIpdByDefault parameter to false. ODBC 4.x doesn’t populate the implementation parameter descriptor automatically. SQL_ATTR_ENABLE_AUTO_IPD always reads SQL_FALSE, setting it to SQL_FALSE is accepted as a no-op, and setting it to SQL_TRUE returns SQL_ERROR with SQLSTATE HYC00. The EnableAutoIpdByDefault parameter doesn’t apply. Setting SQL_ATTR_METADATA_ID puts catalog function arguments into identifier mode, and the two versions fold unquoted identifiers differently. See catalog functions. Unsupported attributes: SQL_ATTR_SIMULATE_CURSOR, SQL_ATTR_FETCH_BOOKMARK_PTR, SQL_ATTR_KEYSET_SIZE. |
SQLGetStmtAttr | ✔ | In addition to the standard attributes, the Snowflake implementation supports SQL_SF_STMT_ATTR_LAST_QUERY_ID and SQL_SF_STMT_ATTR_MULTI_STATEMENT_COUNT. See Snowflake-specific behavior. |
SQLSetStmtOption | ✔ | Supported by the Snowflake driver, but deprecated in ODBC API version 3.x. Replaced by SQLSetStmtAttr. |
SQLGetStmtOption | ✔ | Supported by the Snowflake driver, but deprecated in ODBC API version 3.x. Replaced by SQLGetStmtAttr. |
SQLParamOptions | ✔ | Supported by the Snowflake driver, but deprecated in ODBC API version 3.x. Replaced by SQLSetStmtAttr. |
Each of the preceding functions has a corresponding function that accepts wide characters (unicode). Each such
unicode function has the name shown above, followed by “W”. For example, the function SQLGetStmtAttr, which
accepts a char array as the third parameter, has a corresponding function named SQLGetStmtAttrW, which accepts a
wchar array as the third parameter.
Snowflake-specific behavior¶
-
SQLSetConnectAttrThis method supports two Snowflake-specific attributes:
Attribute Name Description SQL_SF_CONN_ATTR_APPLICATION This overrides the value specified by the APPLICATION setting in the registry or .ini file. SQL_SF_CONN_ATTR_PRIV_KEY ODBC 3.x only. An EVP_PKEY*pointer to an in-memory copy of the private key. This overrides thePRIV_KEY_FILEandPRIV_KEY_PWDsettings in the registry or .ini file. ODBC 4.x does not support this attribute. SetSQL_SF_CONN_ATTR_PRIV_KEY_CONTENTorSQL_SF_CONN_ATTR_PRIV_KEY_BASE64withSQLSetConnectAttr, or use thePRIV_KEY_FILEDSN/connection-string keyword. See Configuration differences.In Snowflake ODBC driver version 3.4.0 and up, you can use the following additional attributes in
SQLSetConnectAttr:Attribute name Description SQL_SF_CONN_ATTR_PRIV_KEY_CONTENTLets you pass the contents of a private key directly into the connection. Make sure to pass the full key contents, including the header and footer. SQL_SF_CONN_ATTR_PRIV_KEY_BASE64Lets you pass a base64-encoded private key directly into the connection. This attribute was introduced in version 3.11.0 of the ODBC driver. SQL_SF_CONN_ATTR_PRIV_KEY_PASSWORDIf you’re passing an encrypted private key in the
SQL_SF_CONN_ATTR_PRIV_KEY_CONTENT, this attribute lets you specify the password.Using
SQL_SF_CONN_ATTR_PRIV_KEY_CONTENTmight be necessary, if your application and the ODBC driver are linked to incompatible versions of OpenSSL, and you’re seeing crashes coming from the ODBC driver when key-pair authentication is used.The following C++ code illustrates the implementation:
-
SQLSetStmtAttrandSQLGetStmtAttrThese methods support two Snowflake-specific attributes:
Attribute name Description SQL_SF_STMT_ATTR_LAST_QUERY_IDRead-only. Returns the query ID of the most recent statement run on the statement handle, or an empty string before the first execution. Both versions reject attempts to set it: ODBC 4.x returns SQL_ERROR with SQLSTATE HY092, and ODBC 3.x returns an error saying the attribute isn’t settable. In ODBC 4.x, the query ID is populated after SQLExecDirectand afterSQLPreparefollowed bySQLExecute. A partial example is in the Examples section below.SQL_SF_STMT_ATTR_MULTI_STATEMENT_COUNTSets the number of statements in a multi-statement request, which enables multi-statement result sets through SQLMoreResults. Both driver versions support setting it. The default is-1, which leaves the count to the server. A value of 0 or greater is sent to the server as theMULTI_STATEMENT_COUNTparameter.
ODBC 4.x accepts-1through32767and returns SQL_ERROR with SQLSTATE HY024 for a value outside that range, andSQLGetStmtAttrreturns the value you set. ODBC 3.x accepts any value without validating the range, and reading the attribute back doesn’t reflect a value you set.
Setting and retrieving descriptor fields¶
| Function Name | Supported | Notes |
|---|---|---|
SQLGetDescField | ✔ | |
SQLGetDescRec | ✔ | |
SQLSetDescField | ✔ | |
SQLSetDescRec | ✔ |
Preparing SQL requests¶
| Function Name | Supported | Notes |
|---|---|---|
SQLAllocStmt | ✔ | Supported by the Snowflake driver, but deprecated in ODBC API version 3.x. |
SQLBindParameter | ✔ | |
SQLPrepare | ✔ | |
SQLGetCursorName | ✔ | |
SQLSetCursorName | ✔ | |
SQLSetScrollOptions | ✔ | Supported by the Snowflake driver, but deprecated ODBC API. |
SQLSetParam | ✔ | Supported by the Snowflake driver, but deprecated in ODBC API version 2.x. Replaced by SQLBindParameter. |
Note
- There is an upper limit to the size of data that you can bind. For details, see Limits on Query Text Size.
- SQL Statements Supported for Preparation lists the types of SQL statements that are supported for preparation.
Submitting requests¶
| Function Name | Supported | Notes |
|---|---|---|
SQLExecute | ✔ | |
SQLExecDirect | ✔ | |
SQLNativeSql | ✔ | |
SQLDescribeParam | ✔ | Regardless of the data type bound to the parameter, Snowflake performs a server-side conversion and returns a VARCHAR with a maximum length of 134217728. |
SQLNumParams | ✔ | |
SQLParamData | ✔ | Support for this function was added in version 2.23.3 of the ODBC Driver. |
SQLPutData | ✔ | Support for this function was added in version 2.23.3 of the ODBC Driver. |
Retrieving results and information about results¶
| Function Name | Supported | Notes |
|---|---|---|
SQLBindCol | ✔ | The ODBC driver does not currently support semi-structured data, including VARIANT, OBJECT and ARRAY data types. |
SQLError | ✔ | Supported by the Snowflake driver, but deprecated in ODBC API version 3.x. Replaced by SQLGetDiagRec. |
SQLGetData | ✔ | |
SQLGetDiagField | ✔ | |
SQLGetDiagRec | ✔ | |
SQLRowCount | ✔ | |
SQLNumResultCols | ✔ | |
SQLDescribeCol | ✔ | |
SQLColAttribute | ✔ | For GEOGRAPHY columns, SQL_DESC_TYPE_NAME returns GEOGRAPHY. Note that other descriptors (e.g. SQL_DESC_CONCISE_TYPE) do not indicate that the column type is GEOGRAPHY. |
SQLColAttributes | ✔ | Supported by the Snowflake driver, but deprecated in ODBC API version 2.x. Replaced by SQLColAttribute. |
SQLFetch | ✔ | |
SQLFetchScroll | ✔ | The FetchOrientation argument supports the SQL_FETCH_NEXT value only. All other types of fetch fail. |
SQLExtendedFetch | Replaced by SQLFetchScroll in API version 3.x driver. | |
SQLSetPos | Snowflake does not support the functionality. | |
SQLBulkOperations | Snowflake does not support the functionality. |
Obtaining information about the data source’s system tables (catalog functions)¶
| Function Name | Supported | Notes |
|---|---|---|
SQLColumnPrivileges | Returns an empty results set. | |
SQLColumns | ✔ | In ODBC 4.x, several catalog metadata columns follow the ODBC definitions of COLUMN_SIZE and BUFFER_LENGTH instead of the Snowflake storage widths that ODBC 3.x reported: REMARKS and COLUMN_DEF return SQL_NULL_DATA when the value is absent, instead of an empty string. BUFFER_LENGTH for NUMBER and DECIMAL is precision + 2. For example, NUMBER(38,0) reports 40. COLUMN_SIZE for FLOAT, DOUBLE, and REAL is 15, the number of significant decimal digits. COLUMN_SIZE for TIMESTAMP types is 20 + scale (19 when the scale is 0), and BUFFER_LENGTH is 16. BUFFER_LENGTH for DATE and TIME is 6. For DATE, TIME, and TIMESTAMP columns, SQL_DATA_TYPE returns the verbose type SQL_DATETIME and SQL_DATETIME_SUB returns the subtype. DATA_TYPE still returns the concise type. COLUMN_SIZE, BUFFER_LENGTH, and CHAR_OCTET_LENGTH for VARIANT, OBJECT, ARRAY, GEOGRAPHY, and GEOMETRY follow the session VARCHAR_AND_BINARY_MAX_SIZE_IN_RESULT value. For the full list, see Behavior differences. |
SQLForeignKeys | ✔ | In ODBC 4.x, when SQL_ATTR_METADATA_ID is SQL_TRUE, an unquoted catalog, schema, or table name is folded to uppercase before the lookup, so a lowercase name matches the stored name. ODBC 3.x compared the name case-sensitively and returned an empty result set. ODBC 4.x also treats an empty string table name the same as an omitted one when it chooses between SHOW EXPORTED KEYS and SHOW IMPORTED KEYS. If you pass an empty PKTableName with a populated FKTableName, ODBC 4.x scopes the query to the foreign key side and returns the relationship. ODBC 3.x returned an empty result set. |
SQLPrimaryKeys | ✔ | In ODBC 4.x, when SQL_ATTR_METADATA_ID is SQL_TRUE, an unquoted catalog, schema, or table name is folded to uppercase before the lookup, so a lowercase name matches the stored name. ODBC 3.x compared the name case-sensitively and returned an empty result set. |
SQLProcedureColumns | ✔ | In ODBC 4.x, BUFFER_LENGTH for NUMBER and DECIMAL columns is the ODBC transfer octet length, precision + 2, instead of the Snowflake storage width that ODBC 3.x returned. |
SQLProcedures | ✔ | In the result set, the NUM_INPUT_PARAMS column contains the number of arguments for the procedure (the value of the max_num_arguments column in the output of the SHOW PROCEDURES command). The NUM_OUTPUT_PARAMS column contains NULL values because stored procedures in Snowflake don’t support output parameters. The NUM_RESULT_SETS column also contains NULL values because stored procedures in Snowflake don’t return result sets. The PROCEDURE_TYPE column always contains SQL_PT_FUNCTION because stored procedures in Snowflake always return a value. In ODBC 4.x, the REMARKS column returns SQL_NULL_DATA when a description is absent, instead of an empty string. |
SQLSpecialColumns | Returns an empty results set. | |
SQLStatistics | Returns an empty results set. | |
SQLTablePrivileges | Returns an empty results set. | |
SQLTables | ✔ | If the parameter passed to the function is “TABLE”, the function returns all types of tables, including transient tables and temporary tables. If the parameter passed to the function is “VIEW”, the function returns all types of views, including materialized views. If the parameter passed to the function is “TABLE, VIEW” or “%”, the function returns information about all types of tables and all types of views. In ODBC 4.x, the REMARKS column returns SQL_NULL_DATA when a comment is absent, instead of an empty string. |
If the name passed to the catalog function has an invalid character, or if the name does not match any database object, the function returns an empty result set.
Setting SQL_ATTR_METADATA_ID to SQL_TRUE puts the catalog function arguments into identifier mode. In ODBC 4.x, SQLTables, SQLColumns, SQLPrimaryKeys, SQLForeignKeys, SQLProcedures, and SQLProcedureColumns all honor the attribute.
In ODBC 4.x identifier mode, an unquoted name is folded to uppercase before the lookup, which matches how Snowflake stores unquoted identifiers, and a double-quoted name stays case-sensitive. ODBC 3.x compares an unquoted name case-sensitively against the stored uppercase name, so a lowercase name returns an empty result set. The default pattern mode (SQL_FALSE) is case-sensitive in both versions.
Terminating a statement¶
| Function Name | Supported | Notes |
|---|---|---|
SQLFreeStmt | ✔ | |
SQLCloseCursor | ✔ | |
SQLCancel | ✔ | When cancelling during a data-at-execution sequence, ODBC 4.x discards all of the data accumulated by SQLPutData, so a retry starts from a clean parameter. ODBC 3.x retained the accumulated data, and a retry concatenated the old and new chunks. |
SQLEndTran | ✔ | |
SQLTransact | ✔ | Supported by the Snowflake driver, but deprecated in ODBC API version 3.x. Replaced by SQLEndTran. |
Terminating a connection¶
| Function Name | Supported | Notes |
|---|---|---|
SQLCancelHandle | ✔ | With SQL_HANDLE_STMT, this function behaves the same as SQLCancel. With SQL_HANDLE_DBC, driver doesn’t cancel anything, because asynchronous connection-level operations are not supported. ODBC 4.x returns SQL_ERROR with SQLSTATE HY010 if a statement on the connection is executing asynchronously or is waiting for data-at-execution, and otherwise returns SQL_SUCCESS without doing anything. ODBC 3.x always returns SQL_SUCCESS without doing anything. In ODBC 4.x, SQL_HANDLE_ENV and SQL_HANDLE_DESC return SQL_ERROR with SQLSTATE HY092. |
SQLDisconnect | ✔ | |
SQLFreeHandle | ✔ | |
SQLFreeConnect | ✔ | Supported by the Snowflake driver, but deprecated in ODBC API version 3.x. |
SQLFreeEnv | ✔ | Supported by the Snowflake driver, but deprecated in ODBC API version 3.x. |
Custom SQL data types¶
Some SQL data types supported by Snowflake have no direct mapping in ODBC (e.g. TIMESTAMP_*tz, VARIANT). To enable the ODBC driver to work with the unsupported data types, the header file shipped with the driver includes definitions for the following custom data types:
In ODBC 3.x, result set metadata reports these codes only when the ODBC_USE_CUSTOM_SQL_DATA_TYPES session parameter is TRUE, and the parameter defaults to FALSE. With the default, SQLDescribeCol and the SQL_DESC_CONCISE_TYPE descriptor field report SQL_TYPE_TIMESTAMP for all three TIMESTAMP variants (SQL_TIMESTAMP for an ODBC 2.x application) and SQL_VARCHAR for ARRAY, OBJECT, VARIANT, GEOGRAPHY, and GEOMETRY. Setting the parameter to TRUE reports the custom codes instead, with GEOGRAPHY and GEOMETRY both reporting SQL_SF_OBJECT, and changes the SQL_DESC_TYPE_NAME of the TIMESTAMP variants from TIMESTAMP to TIMESTAMP_LTZ, TIMESTAMP_NTZ, or TIMESTAMP_TZ. The parameter doesn’t affect parameter binding, so you can pass these codes to SQLBindParameter whatever its value.
ODBC 4.x never reports these codes in result set metadata. There’s no equivalent of the ODBC_USE_CUSTOM_SQL_DATA_TYPES parameter, so the types always report the way ODBC 3.x reports them by default. SQLGetTypeInfo does publish all of them, along with VECTOR as code 2006.
ODBC 4.x accepts SQL_SF_TIMESTAMP_LTZ, SQL_SF_TIMESTAMP_TZ, and SQL_SF_TIMESTAMP_NTZ as the ParameterType argument of SQLBindParameter. It can’t bind semi-structured or vector values, so SQL_SF_ARRAY, SQL_SF_OBJECT, SQL_SF_VARIANT, and VECTOR return SQL_ERROR with SQLSTATE HYC00 at bind time. A vendor code that SQLGetTypeInfo doesn’t publish at all, such as 2007, returns SQLSTATE HY004 instead, which distinguishes a type the driver knows but can’t bind from one it doesn’t recognize. ODBC 3.x accepts the semi-structured codes at bind time and fails later during execution with SQLSTATE HY000. To insert a semi-structured value in either version, bind it as SQL_VARCHAR and convert it in the statement with PARSE_JSON(?), TO_ARRAY(?), or TO_OBJECT(?).
The following code demonstrates sample usage of the custom data types:
Examples¶
This section provides examples of using the API.
Retrieving the last query ID¶
Retrieving the last query ID is a Snowflake extension to the ODBC standard.
To retrieve the last query ID, call the function SQLGetStmtAttr (or SQLGetStmtAttrW), passing the attribute
SQL_SF_STMT_ATTR_LAST_QUERY_ID and a character array large enough to hold the query ID.
The example below shows how to retrieve the query ID for a query:
If you are executing on Linux or macOS, call SQLGetStmtAttrW and pass parameters
of the appropriate data type (for example, “wchar” rather than “char”).
Best practices to improve performance when retrieving data¶
When retrieving data with SQLFetch, you can use the SQLGetData or SQLBindCol functions to access
the contents of the cells. In most cases, using SQLBindCol provides better performance because it reduces the number
of ODBC calls you need to make to retrieve data and because it lets you take advantage of copying data in-memory.
Using SQLGetData to retrieve cell data¶
The following example uses the SQLGetData function to retrieve cell values from the data buffer returned
by SQLFetch. Notice that you need to call SQLGetData once for each cell in the row.
Using SQLBindCol to bind the columns for one row of data¶
The following example uses the SQLBindCol function to retrieve cell values from the data buffer returned by
SQLFetch. It creates an in-memory buffer for the number of columns in a row and then makes a single
SQLBindCol call to bind the application buffers to the result set. Finally, it calls SQLFetch once per row and
loads the cell values into the buffer. This approach can significantly increase the speed and efficiency of retrieving data.
Using SQLBindCol to bind the columns for multiple rows of data¶
You can improve performance even more by fetching multiple rows in a single SQLFetch call, which reduces
the number of ODBC SQLFetch calls needed to process all the rows of a query table.
The following example:
- Determines the number of columns in the result set.
- Creates an in-memory array to store the data from multiple columns.
- Calls
SQLBindColfor each column to bind the application buffers to the result set. - Calls
SQLFetchto get the specified number of rows (100) and processes the data in the in-memory buffer without making ODBC calls, until the end of the query table is reached.
This approach can significantly increase the speed and efficiency of retrieving data. For a query table with 20 columns and 1000 rows, this example would make only 20 SQLBindCol and 10 SQLFetch calls instead of 20000 SQLGetData calls to load all of the table data.