Prise en charge de l’API de pilote ODBC

Le pilote Snowflake ODBC prend en charge la version 3.52 de l’ODBC API. Ce chapitre répertorie les routines ODBC pertinentes pour Snowflake, et indique si elles sont prises en charge. Les routines sont organisées en catégories en fonction de la fonction qu’elles remplissent.

Pour la référence API complète , voir Microsoft ODBC Programmer’s Reference.

Dans ce chapitre :

Connexion à une source de données

Nom de la fonction

Pris en charge

Remarques

SQLAllocHandle

SQLConnect

SQLDriverConnect

SQLAllocEnv

Pris en charge par le pilote Snowflake, mais obsolète dans la version 3.x de l’API ODBC.

SQLAllocConnect

Pris en charge par le pilote Snowflake, mais obsolète dans la version 3.x de l’API ODBC.

SQLBrowseConnect

Obtention d’informations sur un pilote et une source de données

Nom de la fonction

Pris en charge

Remarques

SQLDataSources

SQLDrivers

SQLGetInfo

SQLGetFunctions

SQLGetTypeInfo

Définition et récupération des attributs du pilote

Nom de la fonction

Pris en charge

Remarques

SQLSetConnectAttr

Le paramètre SQL_ATTR_METADATA_ID affecte uniquement les fonctions SQLTables et SQLColumns (et non les autres fonctions de catalogue prises en charge).

SQLGetConnectAttr

Le mode lecture seule n’est pas pris en charge. SQL_MODE_READ_ONLY est transmis au pilote, mais Snowflake écrit toujours dans la base de données. . . De plus, certains attributs ont été introduits après la version 3.52 de l’API : 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

Pris en charge par le pilote Snowflake, mais obsolète dans la version 3.x de l’API ODBC.

SQLGetConnectOption

Pris en charge par le pilote Snowflake, mais obsolète dans la version 3.x de l’API ODBC.

SQLSetEnvAttr

SQLGetEnvAttr

L’attribut SQL_ATTR_CONNECTION_POOLING a été introduit après la version 3.52 de l’API ODBC et n’est pas pris en charge.

SQLSetStmtAttr

SQL_ATTR_CURSOR_SCROLLABLE prend uniquement en charge une valeur SQL_NONSCROLLABLE. . SQL_ATTR_USE_BOOKMARKS prend uniquement en charge une valeur SQL_UB_OFF. . . Pour des raisons de compatibilité avec les outils tiers, SQL_ATTR_ENABLE_AUTO_IPD est défini par défaut sur true, même si la norme ODBC indique qu’il devrait par défaut être false. Pour changer la valeur par défaut en false, réglez le paramètre EnableAutoIpdByDefault sur false. . . Le paramètre SQL_ATTR_METADATA_ID n’affecte que les fonctions SQLTables et SQLColumns (et non les autres fonctions de catalogue prises en charge). . . Attributs non pris en charge : SQL_ATTR_SIMULATE_CURSOR, SQL_ATTR_FETCH_BOOKMARK_PTR, SQL_ATTR_KEYSET_SIZE.

SQLGetStmtAttr

Outre les attributs standard, l’implémentation Snowflake prend en charge l’attribut SQL_SF_STMT_ATTR_LAST_QUERY_ID, qui permet à l’utilisateur de récupérer l’ID de requête le plus récent associé au descripteur d’instructions spécifié. Un exemple partiel est présenté dans la section Exemples ci-dessous.

SQLSetStmtOption

Pris en charge par le pilote Snowflake, mais obsolète dans la version 3.x de l’API ODBC. Remplacé par SQLSetStmtAttr.

SQLGetStmtOption

Pris en charge par le pilote Snowflake, mais obsolète dans la version 3.x de l’API ODBC. Remplacé par SQLGetStmtAttr.

SQLParamOptions

Pris en charge par le pilote Snowflake, mais obsolète dans la version 3.x de l’API ODBC. Remplacé par SQLSetStmtAttr.

Chacune des fonctions précédentes a une fonction correspondante qui accepte les caractères larges (unicode). Chacune de ces fonctions unicode porte le nom indiqué ci-dessus, suivi de « W ». Par exemple, la fonction SQLGetStmtAttr, qui accepte un tableau de caractères en tant que troisième paramètre, a une fonction correspondante appelée SQLGetStmtAttrW, qui accepte un tableau wchar en tant que troisième paramètre.

Comportement spécifique à Snowflake

  • SQLSetConnectAttr

    Cette méthode prend en charge deux attributs spécifiques à Snowflake :

    Nom d’attribut

    Description

    SQL_SF_CONN_ATTR_APPLICATION

    Cela remplace la valeur spécifiée par le paramètre APPLICATION dans le registre ou le fichier .ini.

    SQL_SF_CONN_ATTR_PRIV_KEY

    Il s’agit d’un pointeur EVP_PKEY* qui pointe vers une copie en mémoire de la clé privée. Cela remplace les paramètres PRIV_KEY_FILE et PRIV_KEY_PWD du registre ou du fichier .ini. Snowflake recommande d’utiliser cet attribut pour définir la clé privée.

Définition et récupération de champs descripteurs

Nom de la fonction

Pris en charge

Remarques

SQLGetDescField

SQLGetDescRec

SQLSetDescField

SQLSetDescRec

Préparation de requêtes SQL

Nom de la fonction

Pris en charge

Remarques

SQLAllocStmt

Pris en charge par le pilote Snowflake, mais obsolète dans la version 3.x de l’API ODBC.

SQLBindParameter

SQLPrepare

SQLGetCursorName

SQLSetCursorName

SQLSetScrollOptions

Pris en charge par le pilote Snowflake, mais obsolète dans l’API ODBC.

SQLSetParam

Pris en charge par le pilote Snowflake, mais obsolète dans la version 2.x de l’API ODBC. Remplacé par SQLBindParameter.

Note

Soumission de requêtes

Nom de la fonction

Pris en charge

Remarques

SQLExecute

SQLExecDirect

SQLNativeSql

SQLDescribeParam

Quel que soit le type de données lié au paramètre, Snowflake effectue une conversion côté serveur et retourne un VARCHAR avec une longueur maximale de 16777216.

SQLNumParams

SQLParamData

La prise en charge de cette fonction a été ajoutée dans la version 2.23.3 du pilote ODBC.

SQLPutData

La prise en charge de cette fonction a été ajoutée dans la version 2.23.3 du pilote ODBC.

Récupération de résultats et d’informations sur les résultats

Nom de la fonction

Pris en charge

Remarques

SQLBindCol

Le pilote ODBC ne prend pas actuellement en charge les données semi-structurées, notamment les types de données VARIANT, OBJECT et ARRAY.

SQLError

Pris en charge par le pilote Snowflake, mais obsolète dans la version 3.x de l’API ODBC. Remplacé par SQLGetDiagRec.

SQLGetData

SQLGetDiagField

SQLGetDiagRec

SQLRowCount

SQLNumResultCols

SQLDescribeCol

SQLColAttribute

Pour les colonnes GEOGRAPHY , SQL_DESC_TYPE_NAME renvoie GEOGRAPHY. Notez que d’autres descripteurs (par exemple SQL_DESC_CONCISE_TYPE) n’indiquent pas que le type de colonne est GEOGRAPHY.

SQLColAttributes

Pris en charge par le pilote Snowflake, mais obsolète dans la version 2.x de l’API ODBC. Remplacé par SQLColAttribute.

SQLFetch

SQLFetchScroll

L’argument FetchOrientation prend uniquement en charge la valeur SQL_FETCH_NEXT. Tous les autres types d’extraction échouent.

SQLExtendedFetch

Remplacé par SQLFetchScroll dans le pilote version 3.x de l’API.

SQLSetPos

Snowflake ne prend pas en charge cette fonctionnalité.

SQLBulkOperations

Snowflake ne prend pas en charge cette fonctionnalité.

Obtention d’informations sur les tables système de la source de données (fonctions du catalogue)

Nom de la fonction

Pris en charge

Remarques

SQLColumnPrivileges

Renvoie un ensemble de résultats vide.

SQLColumns

SQLForeignKeys

SQLPrimaryKeys

SQLProcedureColumns

SQLProcedures

Dans le jeu de résultats, la colonne NUM_INPUT_PARAMS contient le nombre d’arguments pour la procédure (la valeur de la colonne max_num_arguments dans la sortie de la commande SHOW PROCEDURES). . . La colonne NUM_OUTPUT_PARAMS contient des valeurs NULL, car les procédures stockées dans Snowflake ne prennent pas en charge les paramètres de sortie. . . La colonne NUM_RESULT_SETS contient également des valeurs NULL, car les procédures stockées dans Snowflake ne renvoient pas de jeux de résultats. . . La colonne PROCEDURE_TYPE contient toujours SQL_PT_FUNCTION car les procédures stockées dans Snowflake renvoient toujours une valeur.

SQLSpecialColumns

Renvoie un ensemble de résultats vide.

SQLStatistics

Renvoie un ensemble de résultats vide.

SQLTablePrivileges

Renvoie un ensemble de résultats vide.

SQLTables

Si le paramètre transmis à la fonction est « TABLE », la fonction renvoie tous les types de tables, y compris les tables transitoires et les tables temporaires. . . Si le paramètre transmis à la fonction est « VIEW », la fonction renvoie tous les types de vues, y compris les vues matérialisées. . . Si le paramètre transmis à la fonction est « TABLE, VIEW » ou « % », la fonction renvoie des informations sur tous les types de tables et tous les types de vues.

Si le nom transmis à la fonction de catalogue a un caractère non valide ou si le nom ne correspond à aucun objet de base de données, la fonction renvoie un jeu de résultats vide.

Le paramètre SQL_ATTR_METADATA_ID affecte uniquement les fonctions SQLTables, SQLColumns, et SQLProcedures.

Mettre fin à une instruction

Nom de la fonction

Pris en charge

Remarques

SQLFreeStmt

SQLCloseCursor

SQLCancel

SQLEndTran

SQLTransact

Pris en charge par le pilote Snowflake, mais obsolète dans la version 3.x de l’API ODBC. Remplacé par SQLEndTran.

Mettre fin à une connexion

Nom de la fonction

Pris en charge

Remarques

SQLCancelHandle

Introduit dans l’API après la version 3.52.

SQLDisconnect

SQLFreeHandle

SQLFreeConnect

Pris en charge par le pilote Snowflake, mais obsolète dans la version 3.x de l’API ODBC.

SQLFreeEnv

Pris en charge par le pilote Snowflake, mais obsolète dans la version 3.x de l’API ODBC.

Types de données SQL personnalisés

Certains types de données SQL pris en charge par Snowflake n’ont pas de mappage direct dans ODBC (par exemple, TIMESTAMP_*tz, VARIANT). Pour permettre au pilote ODBC de fonctionner avec les types de données non pris en charge, le fichier d’en-tête fourni avec le pilote inclut les définitions des types de données personnalisés suivants :

////////////////////////////////////////////////////////////////////////////////////////////////////
/// Custom SQL Data Type Definition
///
///
////////////////////////////////////////////////////////////////////////////////////////////////////

#define SQL_SF_TIMESTAMP_LTZ 2000
#define SQL_SF_TIMESTAMP_TZ  2001
#define SQL_SF_TIMESTAMP_NTZ 2002
#define SQL_SF_ARRAY         2003
#define SQL_SF_OBJECT        2004
#define SQL_SF_VARIANT       2005
Copy

Le code suivant illustre un exemple d’utilisation des types de données personnalisés :

// bind insert as timestamp_ntz
SQLRETURN rc;
rc = SQLPrepare(odbc.StmtHandle,
               (SQLCHAR *) "insert into testtimestampntz values (?)",
               SQL_NTS);

 SQL_TIMESTAMP_STRUCT bindData;
 SQLLEN datalen = sizeof(SQL_TIMESTAMP_STRUCT);
 bindData.year = 2017;
 bindData.month = 11;
 bindData.day = 30;
 bindData.hour = 18;
 bindData.minute = 17;
 bindData.second = 5;
 bindData.fraction = 123456789;

 rc = SQLBindParameter(
   odbc.StmtHandle, 1, SQL_PARAM_INPUT,
   SQL_C_TIMESTAMP, SQL_SF_TIMESTAMP_NTZ,
   100, 0, &bindData, sizeof(bindData), &datalen);

 rc = SQLExecute(odbc.StmtHandle);

 // query table
 rc = SQLExecDirect(odbc.StmtHandle, (SQLCHAR *)"select * from testtimestampntz", SQL_NTS);

 rc = SQLFetch(odbc.StmtHandle);

 // fetch data as timestamp
 SQL_TIMESTAMP_STRUCT ret;
 SQLLEN retLen = (SQLLEN) 0;
 rc = SQLGetData(odbc.StmtHandle, 1, SQL_C_TIMESTAMP, &ret, (SQLLEN)sizeof(ret), &retLen);
Copy

Exemples

Cette section fournit des exemples d’utilisation de l’API.

Récupération de l’ID de la dernière requête

La récupération de l’ID de la dernière requête est une extension Snowflake au standard ODBC.

Pour récupérer l’ID de la dernière requête, appelez la fonction SQLGetStmtAttr (ou SQLGetStmtAttrW) en transmettant l’attribut SQL_SF_STMT_ATTR_LAST_QUERY_ID et un tableau de caractères suffisamment grand pour contenir l’ID de requête.

L’exemple ci-dessous montre comment récupérer l’ID de requête pour une requête :

// Space to store the query ID.
// The SQLGetStmtAttr() function fills this in with the actual ID.
char queryId[37];   // Maximum 36 chars plus string terminator.

// The length (in characters) of the query ID. The SQLGetStmtAttr() function fills this in
// with the actual length of the query ID (usually 36).
SQLINTEGER idLen;

// Execute a query.
rc = SQLExecDirect(odbc.StmtHandle, (SQLCHAR *) "select 1", SQL_NTS);

// Retrieve the query ID (queryId) and the length of that query ID (idLen).
SQLGetStmtAttr(odbc.StmtHandle, SQL_SF_STMT_ATTR_LAST_QUERY_ID, queryId, sizeof(queryId), &idLen);
Copy

Si vous travaillez sous Linux ou macOS, appelez SQLGetStmtAttrW et transmettez les paramètres du type de données approprié (par exemple, « wchar » plutôt que « char »).

Meilleures pratiques pour améliorer les performances lors de la récupération de données

Lorsque vous récupérez des données avec SQLFetch, vous pouvez utiliser les fonctions SQLGetData ou SQLBindCol pour accéder au contenu des cellules. Dans la plupart des cas, l’utilisation de SQLBindCol offre de meilleures performances en raison de la réduction du nombre d’appels ODBC que vous devez effectuer pour récupérer des données et parce que cela vous permet de tirer parti de la copie des données en mémoire.

Utilisation de SQLGetData pour récupérer des données de cellule

L’exemple suivant utilise la fonction SQLGetData pour récupérer les valeurs des cellules du tampon de données renvoyé par SQLFetch. Notez que vous devez appeler SQLGetData une fois pour chaque cellule de la ligne.

SQLRETURN rc;
SQLSMALLINT numCols;
const size_t s_MaxDataLen = 300;

// fetch with SQLGetData()
// query table
rc = SQLExecDirect(stmt, (SQLCHAR *)"select * from table", SQL_NTS);

// Find out the number of result set columns.
rc = SQLNumResultCols(stmt, &numCols);

// buffer for one cell
vector<char> dataBuffer(s_MaxDataLen);
SQLLEN dataLen = (SQLLEN)0;

// call SQLFetch() per row and SQLGetData() per column per row
while (true)
{
    rc = SQLFetch(stmt);
    if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO))
    {
        break;
    }
    for (SQLUSMALLINT i = 0; i < numCols; i++)
    {
        rc = SQLGetData(stmt, i + 1, SQL_C_CHAR, dataBuffer.data(), (SQLLEN)s_MaxDataLen, &dataLen);
        std::string data;
        if (SQL_NULL_DATA == dataLen)
            continue;
        if (SQL_NO_TOTAL == dataLen)
            dataLen = s_MaxDataLen;
        data = std::string(dataBuffer.data(), dataLen);
    }
}
rc = SQLCloseCursor(stmt);
Copy

Utilisation de SQLBindCol pour lier les colonnes d’une ligne de données

L’exemple suivant utilise la fonction SQLBindCol pour récupérer les valeurs des cellules du tampon de données renvoyé par SQLFetch. Un tampon est créé en mémoire pour le nombre de colonnes d’une ligne, puis un appel SQLBindCol unique est effectué pour lier les tampons de l’application au jeu de résultats. Enfin, un appel SQLFetch se produit une fois par ligne, avec le chargement des valeurs des cellules dans le tampon. Cette approche peut augmenter considérablement la vitesse et l’efficacité de la récupération des données.

SQLRETURN rc;
SQLSMALLINT numCols;
const size_t s_MaxDataLen = 300;

// fetch with SQLBindCol()
// query table
rc = SQLExecDirect(stmt, (SQLCHAR *)"select * from table", SQL_NTS);

// Find out the number of result set columns.
rc = SQLNumResultCols(stmt, &numCols);

// buffer for one row
vector<char> rowBuffer(s_MaxDataLen * numCols);
vector<SQLLEN> columnLenBuffer(numCols);

// call SQLBindCol() per column
for (SQLSMALLINT i = 0; i < numCols; ++i)
{
    SQLBindCol(stmt, i + 1, SQL_C_CHAR, &rowBuffer[s_MaxDataLen * i],
               s_MaxDataLen, &columnLenBuffer[i]);
}

// call SQLFetch() per row
while (true)
{
    rc = SQLFetch(stmt);
    if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO))
    {
         break;
    }
    // go through data for each cell in buffer without ODBC calls
    for (SQLUSMALLINT i = 0; i < numCols; i++)
    {
        std::string data;
        SQLLEN len = columnLenBuffer[i];
        if (SQL_NULL_DATA == len)
            continue;
        if (SQL_NO_TOTAL == len)
            len = s_MaxDataLen;
        data = std::string(&rowBuffer[s_MaxDataLen * i], len);
    }
}
rc = SQLCloseCursor(stmt);
Copy

Utilisation de SQLBindCol pour lier les colonnes de plusieurs lignes de données

Vous pouvez encore améliorer les performances en extrayant plusieurs lignes en un seul appel SQLFetch , ce qui réduit le nombre d’appels ODBC SQLFetch nécessaires pour traiter toutes les lignes d’une table de requête.

L’exemple suivant :

  • Détermine le nombre de colonnes dans le jeu de résultats.

  • Crée un tableau en mémoire pour stocker les données de plusieurs colonnes.

  • Appelle SQLBindCol pour chaque colonne afin de lier les tampons de l’application au jeu de résultats.

  • Appelle SQLFetch pour obtenir le nombre de lignes spécifié (100) et traite les données dans le tampon en mémoire sans faire d’appels ODBC, jusqu’à ce que la fin de la table de requête soit atteinte.

Cette approche peut augmenter considérablement la vitesse et l’efficacité de la récupération des données. Pour une table de requête comportant 20 colonnes et 1 000 lignes, cet exemple n’effectuerait que 20 appels SQLBindCol et 10 appels SQLFetch au lieu de 20 000 appels SQLGetData pour charger toutes les données de la table.

SQLRETURN rc;
SQLSMALLINT numCols;
const size_t s_MaxDataLen = 300;

// fetch with SQLBindCol() and SQL_ATTR_ROW_ARRAY_SIZE > 1
const size_t s_numRowsPerSQLFetch = 100;
SQLULEN numRowsFetched = 0;
rc = SQLSetStmtAttr(stmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)s_numRowsPerSQLFetch, 0);
rc = SQLSetStmtAttr(stmt, SQL_ATTR_ROWS_FETCHED_PTR, (SQLPOINTER)&numRowsFetched, sizeof(SQLULEN));

// query table
rc = SQLExecDirect(stmt, (SQLCHAR *)"select * from table", SQL_NTS);

// Find out the number of result set columns.
rc = SQLNumResultCols(stmt, &numCols);

// buffer for all columns; each column has buffer size of s_numRowsPerSQLFetch
// To retrieve multiple rows per SQLFetch() call, use the default behavior of SQL_BIND_BY_COLUMN
vector<vector<char> > colArray(numCols);
vector<vector<SQLLEN> > colLenArray(numCols);

// call SQLBindCol() per column
for (SQLSMALLINT i = 0; i < numCols; ++i)
{
    // initialize buffer for each column
    colArray[i].resize(s_MaxDataLen * s_numRowsPerSQLFetch);
    colLenArray[i].resize(s_numRowsPerSQLFetch);

    SQLBindCol(stmt, i + 1, SQL_C_CHAR, colArray[i].data(),
                s_MaxDataLen, colLenArray[i].data());
}

// call SQLFetch() per s_numRowsPerSQLFetch rows
while (true)
{
    rc = SQLFetch(stmt);
    if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO))
    {
        break;
    }
    // go through data for each cell in buffer without ODBC calls
    for (SQLULEN rowIndex = 0; rowIndex < numRowsFetched; rowIndex++)
    {
        for (SQLUSMALLINT colIndex = 0; colIndex < colIndex; colIndex++)
        {
            std::string data;
            SQLLEN len = colLenArray[colIndex][rowIndex];
            if (SQL_NULL_DATA == len)
                continue;
            if (SQL_NO_TOTAL == len)
                len = s_MaxDataLen;
            data = std::string(&(colArray[colIndex][s_MaxDataLen * rowIndex]), len);
        }
    }
}
rc = SQLCloseCursor(stmt);
Copy