Utilisation de SQLBindCol

L’application lie des colonnes en appelant SQLBindCol. Cette fonction lie une colonne à la fois. Avec elle, l’application spécifie les éléments suivants :

  • Numéro de la colonne. La colonne 0 est la colonne signet ; cette colonne n’est pas incluse dans certains jeux de résultats. Toutes les autres colonnes sont numérotées à compter du nombre 1. Il s’agit d’une erreur de liaison d’une colonne numérotée plus élevée qu’il y a des colonnes dans le jeu de résultats ; cette erreur ne peut pas être détectée tant que le jeu de résultats n’a pas été créé, il est donc retourné par SQLFetch, et non PAR SQLBindCol.

  • Type de données C, adresse et longueur d’octet de la variable liée à la colonne. Il s’agit d’une erreur pour spécifier un type de données C vers lequel le type de données SQL de la colonne ne peut pas être converti ; cette erreur peut ne pas être détectée tant que le jeu de résultats n’a pas été créé. Il est donc retourné par SQLFetch, et non PAR SQLBindCol. Pour obtenir la liste des conversions prises en charge, consultez Conversion de données de SQL en types de données C dans l’annexe D : Types de données. Pour plus d’informations sur la longueur d’octet, consultez Longueur du tampon de données.

  • Adresse d’une mémoire tampon de longueur/indicateur. La mémoire tampon de longueur/indicateur est facultative. Il est utilisé pour renvoyer la longueur d’octet des données binaires ou caractères ou retourner SQL_NULL_DATA si les données sont NULL. Pour plus d’informations, consultez Utilisation des valeurs de longueur/indicateur.

Quand SQLBindCol est appelé, le pilote associe ces informations à l’instruction. Lorsque chaque ligne de données est extraite, elle utilise les informations pour placer les données de chaque colonne dans les variables d’application liées.

Par exemple, le code suivant lie des variables aux colonnes SalesPerson et CustID. Les données des colonnes sont retournées dans SalesPerson et CustID. Étant donné que SalesPerson est une mémoire tampon de caractères, l’application spécifie sa longueur d’octet (11) afin que le pilote puisse déterminer s’il faut tronquer les données. La longueur d’octet du titre retourné, ou s’il s’agit de NULL, est retournée dans SalesPersonLenOrInd.

Étant donné que CustID est une variable entière et a une longueur fixe, il n’est pas nécessaire de spécifier sa longueur d’octet ; le pilote suppose qu’il est sizeof(SQLUINTEGER). La longueur d’octet des données d’ID client retournées, ou s’il s’agit de NULL, est retournée dans CustIDInd. Notez que l’application s’intéresse uniquement si le salaire est NULL, car la longueur d’octet est toujours sizeof(SQLUINTEGER).

SQLCHAR       SalesPerson[11];  
SQLUINTEGER   CustID;  
SQLINTEGER    SalesPersonLenOrInd, CustIDInd;  
SQLRETURN     rc;  
SQLHSTMT      hstmt;  
  
// Bind SalesPerson to the SalesPerson column and CustID to the   
// CustID column.  
SQLBindCol(hstmt, 1, SQL_C_CHAR, SalesPerson, sizeof(SalesPerson),  
            &SalesPersonLenOrInd);  
SQLBindCol(hstmt, 2, SQL_C_ULONG, &CustID, 0, &CustIDInd);  
  
// Execute a statement to get the sales person/customer of all orders.  
SQLExecDirect(hstmt, "SELECT SalesPerson, CustID FROM Orders ORDER BY SalesPerson",  
               SQL_NTS);  
  
// Fetch and print the data. Print "NULL" if the data is NULL. Code to   
// check if rc equals SQL_ERROR or SQL_SUCCESS_WITH_INFO not shown.  
while ((rc = SQLFetch(hstmt)) != SQL_NO_DATA) {  
   if (SalesPersonLenOrInd == SQL_NULL_DATA)   
            printf("NULL                     ");  
   else   
            printf("%10s   ", SalesPerson);  
   if (CustIDInd == SQL_NULL_DATA)   
         printf("NULL\n");  
   else   
            printf("%d\n", CustID);  
}  
  
// Close the cursor.  
SQLCloseCursor(hstmt);  

Le code suivant exécute une instruction SELECT entrée par l’utilisateur et imprime chaque ligne de données dans le jeu de résultats. Étant donné que l’application ne peut pas prédire la forme du jeu de résultats créé par l’instruction SELECT , elle ne peut pas lier des variables codées en dur au jeu de résultats comme dans l’exemple précédent. Au lieu de cela, l’application alloue une mémoire tampon qui contient les données et une mémoire tampon de longueur/indicateur pour chaque colonne de cette ligne. Pour chaque colonne, elle calcule le décalage au début de la mémoire de la colonne et ajuste ce décalage afin que les mémoires tampons de données et de longueur/indicateur pour la colonne commencent sur les limites d’alignement. Il lie ensuite la mémoire en commençant à l’offset à la colonne. Du point de vue du pilote, l’adresse de cette mémoire est indistinguishable de l’adresse d’une variable liée dans l’exemple précédent. Pour plus d’informations sur l’alignement, consultez Alignement.

// This application allocates a buffer at run time. For each column, this   
// buffer contains memory for the column's data and length/indicator.   
// For example:  
//      column 1         column 2      column 3      column 4  
// <------------><---------------><-----><------------>  
//      db1   li1   db2   li2   db3   li3   db4   li4  
//      |      |      |      |      |      |      |         |  
//      _____V_____V________V_______V___V___V______V_____V_  
// |__________|__|_____________|__|___|__|__________|__|  
//  
// dbn = data buffer for column n  
// lin = length/indicator buffer for column n  
  
// Define a macro to increase the size of a buffer so that it is a   
// multiple of the alignment size. Thus, if a buffer starts on an   
// alignment boundary, it will end just before the next alignment   
// boundary. In this example, an alignment size of 4 is used because   
// this is the size of the largest data type used in the application's   
// buffer--the size of an SDWORD and of the largest default C data type   
// are both 4. If a larger data type (such as _int64) was used, it would   
// be necessary to align for that size.  
#define ALIGNSIZE 4  
#define ALIGNBUF(Length) Length % ALIGNSIZE ? \  
                  Length + ALIGNSIZE - (Length % ALIGNSIZE) : Length  
  
SQLCHAR        SelectStmt[100];  
SQLSMALLINT    NumCols, *CTypeArray, i;  
SQLINTEGER *   ColLenArray, *OffsetArray, SQLType, *DataPtr;  
SQLRETURN      rc;   
SQLHSTMT       hstmt;  
  
// Get a SELECT statement from the user and execute it.  
GetSelectStmt(SelectStmt, 100);  
SQLExecDirect(hstmt, SelectStmt, SQL_NTS);  
  
// Determine the number of result set columns. Allocate arrays to hold   
// the C type, byte length, and buffer offset to the data.  
SQLNumResultCols(hstmt, &NumCols);  
CTypeArray = (SQLSMALLINT *) malloc(NumCols * sizeof(SQLSMALLINT));  
ColLenArray = (SQLINTEGER *) malloc(NumCols * sizeof(SQLINTEGER));  
OffsetArray = (SQLINTEGER *) malloc(NumCols * sizeof(SQLINTEGER));  
  
OffsetArray[0] = 0;  
for (i = 0; i < NumCols; i++) {  
   // Determine the column's SQL type. GetDefaultCType contains a switch   
   // statement that returns the default C type for each SQL type.  
   SQLColAttribute(hstmt, ((SQLUSMALLINT) i) + 1, SQL_DESC_TYPE, NULL, 0, NULL, (SQLPOINTER) &SQLType);  
   CTypeArray[i] = GetDefaultCType(SQLType);  
  
   // Determine the column's byte length. Calculate the offset in the   
   // buffer to the data as the offset to the previous column, plus the   
   // byte length of the previous column, plus the byte length of the   
   // previous column's length/indicator buffer. Note that the byte   
   // length of the column and the length/indicator buffer are increased   
   // so that, assuming they start on an alignment boundary, they will  
   // end on the byte before the next alignment boundary. Although this   
   // might leave some holes in the buffer, it is a relatively   
   // inexpensive way to guarantee alignment.  
   SQLColAttribute(hstmt, ((SQLUSMALLINT) i)+1, SQL_DESC_OCTET_LENGTH, NULL, 0, NULL, &ColLenArray[i]);  
   ColLenArray[i] = ALIGNBUF(ColLenArray[i]);  
   if (i)  
      OffsetArray[i] = OffsetArray[i-1]+ColLenArray[i-1]+ALIGNBUF(sizeof(SQLINTEGER));  
}  
  
// Allocate the data buffer. The size of the buffer is equal to the   
// offset to the data buffer for the final column, plus the byte length   
// of the data buffer and length/indicator buffer for the last column.  
void *DataPtr = malloc(OffsetArray[NumCols - 1] +  
               ColLenArray[NumCols - 1] + ALIGNBUF(sizeof(SQLINTEGER)));  
  
// For each column, bind the address in the buffer at the start of the   
// memory allocated for that column's data and the address at the start   
// of the memory allocated for that column's length/indicator buffer.  
for (i = 0; i < NumCols; i++)  
   SQLBindCol(hstmt,  
            ((SQLUSMALLINT) i) + 1,  
            CTypeArray[i],  
            (SQLPOINTER)((SQLCHAR *)DataPtr + OffsetArray[i]),  
            ColLenArray[i],  
            (SQLINTEGER *)((SQLCHAR *)DataPtr + OffsetArray[i] + ColLenArray[i]));  
  
// Retrieve and print each row. PrintData accepts a pointer to the data,   
// its C type, and its byte length/indicator. It contains a switch   
// statement that casts and prints the data according to its type. Code   
// to check if rc equals SQL_ERROR or SQL_SUCCESS_WITH_INFO not shown.  
while ((rc = SQLFetch(hstmt)) != SQL_NO_DATA) {  
   for (i = 0; i < NumCols; i++) {  
      PrintData((SQLCHAR *)DataPtr[OffsetArray[i]], CTypeArray[i],  
               (SQLINTEGER *)((SQLCHAR *)DataPtr[OffsetArray[i] + ColLenArray[i]]));  
   }  
}  
  
// Close the cursor.  
SQLCloseCursor(hstmt);