Envoi de données en tant que paramètre table à l'aide de données en cours d'exécution (ODBC)
Cette approche est semblable à la procédure Tout en mémoire, mais utilise des données en cours d'exécution pour le paramètre table.
Dans cet exemple, lorsque SQLExecute ou SQLExecDirect est appelé, le pilote retourne SQL_NEED_DATA. L'application appelle ensuite SQLParamData à plusieurs reprises jusqu'à ce que le pilote retourne une valeur autre que SQL_NEED_DATA. Le pilote retourne ParameterValuePtr pour indiquer à l'application le paramètre pour lequel il demande des données. L'application appelle SQLPutData pour fournir des données de paramètre avant le prochain appel à SQLParamData. Pour un paramètre table, l'appel à SQLPutData indique le nombre de lignes qu'il a préparé pour le pilote (dans cet exemple, toujours 1). Lorsque toutes les lignes de la table ont été passées au pilote, SQLPutData est appelé pour indiquer qu'aucune ligne n'est disponible.
Il est possible d'utiliser les valeurs de données en cours d'exécution dans les lignes d'une valeur de table. La valeur retournée par SQLParamData indique à l'application la valeur requise par le pilote. Comme pour les valeurs de paramètre normales, SQLPutData peut être appelé une ou plusieurs fois pour une valeur de colonne de table caractère ou binaire. Cela permet à une application de passer de grandes valeurs en plusieurs parties.
Lorsque SQLPutData est appelé pour une valeur de table, DataPtr est utilisé pour le nombre de lignes disponibles (dans cet exemple, toujours 1). StrLen_or_IndPtr doit toujours être 0. Lorsque toutes les lignes de la valeur de table ont été passées, SQLPutData est appelé avec une valeur DataPtr de 0.
Élément requis
Cette procédure suppose que la commande Transact-SQL suivante a été exécutée sur le serveur :
create type TVParam as table(ProdCode integer, Qty integer)
create procedure TVPOrderEntry(@CustCode varchar(5), @Items TVPParam,
@OrdNo integer output, @OrdDate datetime output)
as
set @OrdDate = GETDATE();
insert into TVPOrd (OrdDate, CustCode) values (@OrdDate, @CustCode) output OrdNo);
select @OrdNo = SCOPE_IDENTITY();
insert into TVPItem (OrdNo, ProdCode, Qty)
select @OrdNo, @Items.ProdCode, @Items.Qty
from @Items
Pour envoyer les données
Déclarez les variables pour les paramètres SQL. Les mémoires tampon pour les paramètres table ne doivent pas forcément être des tableaux dans cet exemple ; l'exemple passe une ligne à la fois.
SQLRETURN r; // Variables for SQL parameters: SQLCHAR CustCode[6]; SQLCHAR *TVP = (SQLCHAR *) "TVPInParam"; SQLINTEGER ProdCode, Qty; SQLINTEGER OrdNo; char *OrdDate[23]; SQLCHAR *TVP = (SQLCHAR *) "TVParam"; SQLINTEGER ItemNo; // Variables for indicator/length variables associated with parameters: SQLLEN cbCustCode, cbTVP, cbProdCode, cbQty, cbOrdNo, cbOrdDate, cbItemNo; // Token returned by SQLParamData to indicate which param data is needed for: SQLPOINTER ParamId;
Liez les paramètres. ColumnSize est égal à 1, ce qui signifie qu'une ligne au plus est passée à la fois.
// Bind parameters for call to TVPOrderEntryByRow. r = SQLBindParameter(hstmt, 1, SQL_C_CHAR, SQL_PARAM_INPUT,SQL_VARCHAR, 5, 0, CustCode, sizeof(CustCode), &cbCustCode); // 2 - Items TVP r = SQLBindParameter(hstmt, 2, // ParameterNumber SQL_C_DEFAULT, // InputOutputType SQL_PARAM_INPUT, // ValueType SQL_SS_TABLE, // Parametertype 1, // ColumnSize: For a table-valued parameter this the row array size. 0, // DecimalDigits: For a table-valued parameter this is always 0. TVP, // ParameterValuePtr: For a table-valued parameter this is the type name of the TVP, // and also a token returned by SQLParamData. SQL_NTS, // BufferLength: For a table-valued parameter this is the length of the type name or SQL_NTS. &cbTVP); // StrLen_or_IndPtr: For a table-valued parameter this is the number of rows input and output. // 3 - OrdNo output r = SQLBindParameter(hstmt, 3, SQL_PARAM_OUTPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &OrdNo, sizeof(SQLINTEGER), &cbOrdNo); // 4- OrdDate output r = SQLBindParameter(hstmt, 4, SQL_PARAM_OUTPUT, SQL_C_CHAR, SQL_TYPE_TIMESTAMP, 23, 3, &OrdDate, sizeof(OrdDate), &cbOrdDate);
Liez les colonnes pour le paramètre table.
// Bind the table-valued parameter columns. // First set focus on param 2 r = SQLSetStmtAttr(hstmt, SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 2, SQL_IS_INTEGER); // ProdCode r = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &ProdCode, sizeof(SQLINTEGER), &cbProdCode); // Qty r = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, &Qty, sizeof(SQLINTEGER), &cbQty); // Reset param focus r = SQLSetStmtAttr(hstmt, SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 0, SQL_IS_INTEGER);
Initialisez les paramètres. Cet exemple attribue SQL_DATA_AT_EXEC à la taille du paramètre table, et non un nombre de lignes.
// Initialze the TVP for row streaming. cbTVP = SQL_DATA_AT_EXEC; // Populate non-data-at-exec parameters. strcpy_s((char *) CustCode ,sizeof(CustCode), "CUST1"); cbCustCode = SQL_NTS;
Appelez la procédure. SQLExecDirect retourne SQL_NEED_DATA parce que le paramètre table est un paramètre de données en cours d'exécution.
// Call the procedure r = SQLExecDirect(hstmt, (SQLCHAR *) "{call TVPOrderEntry(?, ?, ?, ?)}",SQL_NTS);
Fournissez des données de paramètre de données en cours d'exécution. Lorsque SQLParamData retourne le ParameterValuePtr pour un paramètre table, l'application doit préparer les colonnes pour la ou les lignes suivantes de la valeur de table. L'application appelle ensuite SQLPutData avec DataPtr ayant pour valeur le nombre de lignes disponibles (dans cet exemple 1) et StrLen_or_IndPtr ayant pour valeur 0.
// Check if parameter data is required, and get the first parameter ID token if (r == SQL_NEED_DATA) { r = SQLParamData(hstmt, &ParamId); } // Supply parameter row data. int rowNum = 0; while (r == SQL_NEED_DATA) { if (ParamId == TVP) { switch (rowNum) { case 0: // Supply data for 1st row // Populate input table-valued parameter row constituent columns. ProdCode = 1215; cbProdCode = sizeof(SQLINTEGER); Qty = 5; cbQty = sizeof(SQLINTEGER); // Returning 1 for StrLenOrIndPtr indicates that a row is available. r = SQLPutData(hstmt, (SQLPOINTER) 1, 1); rowNum++; break; case 1: // Supply data for the second row. // Populate another table-valued parameter row as above. ProdCode = 1017; cbProdCode = sizeof(SQLINTEGER); // This time supply Qty through SQLPutData. Qty = 0; cbQty = SQL_DATA_AT_EXEC; r = SQLPutData(hstmt, (SQLPOINTER) 1, 1); rowNum++; break; default: // Passing 0 in StrLenOrIndPtr indicates that no more table-valued parameter rows are available. r = SQLPutData(hstmt, (SQLPOINTER) 1, 0); break; } } else { if (ParamId == &Qty) { Qty = 2; // For a character or binary parameter, SQLPutData could be called // multiple times to pass the value in pieces. SQLPutData(hstmt, &Qty, sizeof(SQLINTEGER)); } } // Signal that parameter data is available, and get the token for // the next parameter. r = SQLParamData(hstmt, &ParamId); } }
Exemple
Description
Cet exemple montre que vous pouvez utiliser la diffusion de lignes en continu, à raison d'une ligne par appel à SQLPutData, avec ODBC TVP, de la même façon que vous pouvez utiliser BCP.exe pour charger des données dans une base de données.
Avant de générer l'exemple, remplacez le nom du serveur dans la chaîne de connexion.
Vous avez besoin de la dernière mise à jour de SQL Server 2008 pour exécuter cet exemple. Si vous n'êtes pas en mesure d'exécuter cet exemple, il est possible que vous ne disposiez pas des dernières mises à jour. La mise à jour requise pour l'exécuter figure sur le site (en anglais) Cumulative update package 2 for SQL Server 2008. Choisissez le fichier nommé SQL_Server_2008_RTM_CU2_SNAC.
Cet exemple utilise la base de données par défaut. Avant de l'exécuter, exécutez les commandes suivantes dans la base de données que vous utiliserez :
create table MCLOG (
biSeqNo bigint,
iSeries int,
bmRestData varbinary(max)
)
go
-- Table type definition
create type MCLOGType
as table(biSeqNo bigint, iSeries int, bmRestData varbinary(max) )
go
-- Insert procedure
create procedure MCLOGInsert (@TableVariable MCLOGType READONLY)
as
insert into MCLog(biSeqNo, iSeries, bmRestData)
select biSeqNo, iSeries, bmRestData from @TableVariable
go
Code
#define UNICODE
#define _UNICODE
#define _SQLNCLI_ODBC_
#include <windows.h>
#include <tchar.h>
#include <sqlext.h>
#include "sqlncli.h"
// link to sqlncli10.lib
#define SUCCESS(x) ( \
!((x) & 0xFFFE) \
)
#define CHKRC(stmt) { \
rc = (stmt); \
if (!SUCCESS(rc)) { \
_tprintf(_T(#stmt) _T(" failed with rc = %ld\r\n"), rc); \
goto EXIT; \
} \
};
void PrintError(SQLSMALLINT HandleType, SQLHANDLE Handle) {
RETCODE rc = SQL_SUCCESS;
SQLTCHAR szSqlState[6];
SQLTCHAR szMessage[1024];
SQLSMALLINT i = 1;
SQLSMALLINT msgLen = 0;
SQLINTEGER NativeError;
i = 1;
while ( (rc = SQLGetDiagRec(HandleType, Handle, i, szSqlState, &NativeError, szMessage, sizeof(szMessage)/sizeof(SQLTCHAR), &msgLen)) != SQL_NO_DATA) {
if (!SUCCESS(rc))
break;
szMessage[msgLen] = 0;
szSqlState[5] = 0;
_tprintf(_T("SQLState=%s, NativeError=%ld, Message=%s\r\n"), szSqlState, NativeError, szMessage);
i++;
}
}
int main() {
RETCODE rc = SQL_SUCCESS;
HENV henv = SQL_NULL_HENV;
HDBC hdbc = SQL_NULL_HDBC;
SQLHSTMT hstmt = SQL_NULL_HSTMT;
SQLTCHAR * pszConnection = _T("DRIVER={SQL Server Native Client 10.0};Server=your_servername;Trusted_Connection=Yes;");
// insert one TVP parameter
SQLTCHAR * pszInsertStmt = _T("{call MCLOGInsert(?)}");
SQLLEN cbParamLength;
SQLULEN cMaxRows = 3;
CHKRC(SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HENV, &henv));
CHKRC(SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0));
CHKRC(SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc));
CHKRC(SQLSetConnectAttr(hdbc, SQL_ATTR_LOGIN_TIMEOUT,reinterpret_cast<SQLPOINTER>(60),SQL_IS_UINTEGER));
CHKRC(SQLDriverConnect(hdbc, NULL, pszConnection, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT));
CHKRC(SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt));
CHKRC(SQLPrepare(hstmt, pszInsertStmt, SQL_NTS));
// Bind the first parameter
CHKRC(SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, cMaxRows, 0, (SQLPOINTER)1, 0, &cbParamLength));
// If the stored procedure is executed as T-SQL ("exec sp_insert ?, ?"), you will supply the type name.
// CHKRC(SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, cMaxRows, 0, (SQLPOINTER)lpszTVPParamType, SQL_NTS, &cbParamLengths));
// bind TVP columns
CHKRC(SQLSetStmtAttr(hstmt, SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER)1, SQL_IS_INTEGER));
// for each TVP column, you can define an array to send more than one row for each SQLPutData call.
LONGLONG llSeqNo;
SQLLEN cbSeqNo = sizeof(LONGLONG);
LONG lSeries;
SQLLEN cbSeries = sizeof(LONG);
BYTE rgbRestData[2048];
SQLLEN cbRestData = SQL_DATA_AT_EXEC;
SQLUSMALLINT iColumn = 1;
// Bind biSeqNo
CHKRC(SQLBindParameter(hstmt, iColumn, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, sizeof(LONGLONG), 0, (SQLPOINTER)&llSeqNo, sizeof(llSeqNo), &cbSeqNo));
// Bind iSeries
iColumn++;
CHKRC(SQLBindParameter(hstmt, iColumn, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, sizeof(LONG), 0, (SQLPOINTER)&lSeries, sizeof(lSeries), &cbSeries));
// Bind bmRestData
iColumn++;
CHKRC(SQLBindParameter(hstmt, iColumn, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_VARBINARY, 0, 0, (SQLPOINTER)rgbRestData, 0, &cbRestData));
CHKRC(SQLSetStmtAttr(hstmt, SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER)0, SQL_IS_INTEGER));
// Set cbParamLength to SQL_DATA_AT_EXEC to indicate the TVP parameter is bound as DAE.
cbParamLength = SQL_DATA_AT_EXEC;
rc = SQLExecute(hstmt);
if (rc == SQL_NEED_DATA) {
SQLPOINTER ptr = NULL;
SQLULEN cRows = 0;
rc = ::SQLParamData(hstmt, &ptr);
while (rc == SQL_NEED_DATA) {
if (ptr == (SQLPOINTER)1) {
// it is the TVP parameter
if (cRows == cMaxRows) {
// We finish sending the last row already.
CHKRC(::SQLPutData(hstmt, NULL, 0));
}
else {
// StrLen_or_IndPtr can be changed to SQL_DATA_AT_EXEC or to a byte length before sending
// the actual TVP rows. SQL_DATA_AT_EXEC means send DAE data.
llSeqNo = cRows;
cbSeqNo = sizeof(LONGLONG); // send as bound TVP column
lSeries = cRows + 100;
cbSeries = sizeof(LONG); // send as bound TVP column
cbRestData = SQL_DATA_AT_EXEC; // send as DAE TVP column
CHKRC(::SQLPutData(hstmt, (SQLPOINTER)1, 1));
cRows++;
}
}
else if (ptr == (SQLPOINTER)rgbRestData)
// varbinary(max) column. Send data in parts.
for ( int i = 0 ; i < 3 ; i++ ) {
// Obtain the data in part from somewhere, here we just set all bytes to 'a'.
::memset(rgbRestData, 'a', sizeof(rgbRestData));
CHKRC(::SQLPutData(hstmt, (SQLPOINTER)rgbRestData, sizeof(rgbRestData)));
}
else
// handling other DAE parameters, but in our case, we don't have other DAE parameters.
goto EXIT;
rc = ::SQLParamData(hstmt, &ptr);
}
}
if (hstmt)
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
if (hdbc) {
SQLDisconnect(hdbc);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
}
if (henv)
SQLFreeHandle(SQL_HANDLE_ENV, henv);
EXIT:
if (!SUCCESS(rc)) {
if (hstmt)
PrintError(SQL_HANDLE_STMT, hstmt);
if (hdbc)
PrintError(SQL_HANDLE_DBC, hdbc);
if(henv)
PrintError(SQL_HANDLE_ENV, henv);
}
}
Exemple
Description
Cet exemple montre que vous pouvez utiliser la diffusion de lignes en continu, à raison de plusieurs lignes par appel à SQLPutData, avec ODBC TVP, de la même façon que vous pouvez utiliser BCP.exe pour charger des données dans une base de données.
Avant de générer l'exemple, remplacez le nom du serveur dans la chaîne de connexion.
Vous devez disposer de SQL Server 2008 SP1 pour exécuter cet exemple.
Cet exemple utilise la base de données par défaut. Avant de l'exécuter, exécutez les commandes suivantes dans la base de données que vous utiliserez :
create table MCLOG (
biSeqNo bigint,
iSeries int,
bmRestData varbinary(max)
)
go
-- Table type definition
create type MCLOGType
as table(biSeqNo bigint, iSeries int, bmRestData varbinary(max) )
go
-- Insert procedure
create procedure MCLOGInsert (@TableVariable MCLOGType READONLY)
as
insert into MCLog(biSeqNo, iSeries, bmRestData)
select biSeqNo, iSeries, bmRestData from @TableVariable
go
Code
#define UNICODE
#define _UNICODE
#define _SQLNCLI_ODBC_
#include <windows.h>
#include <tchar.h>
#include <sqlext.h>
#include "sqlncli.h"
// link to sqlncli10.lib
#define SUCCESS(x) ( \
!((x) & 0xFFFE) \
)
#define CHKRC(stmt) { \
rc = (stmt); \
if (!SUCCESS(rc)) { \
_tprintf(_T(#stmt) _T(" failed with rc = %ld\r\n"), rc); \
goto EXIT; \
} \
};
void PrintError(SQLSMALLINT HandleType, SQLHANDLE Handle) {
RETCODE rc = SQL_SUCCESS;
SQLTCHAR szSqlState[6];
SQLTCHAR szMessage[1024];
SQLSMALLINT i = 1;
SQLSMALLINT msgLen = 0;
SQLINTEGER NativeError;
i = 1;
while ( (rc = SQLGetDiagRec(HandleType, Handle, i, szSqlState, &NativeError, szMessage, sizeof(szMessage)/sizeof(SQLTCHAR), &msgLen)) != SQL_NO_DATA) {
if (!SUCCESS(rc))
break;
szMessage[msgLen] = 0;
szSqlState[5] = 0;
_tprintf(_T("SQLState=%s, NativeError=%ld, Message=%s\r\n"), szSqlState, NativeError, szMessage);
i++;
}
}
int main() {
RETCODE rc = SQL_SUCCESS;
HENV henv = SQL_NULL_HENV;
HDBC hdbc = SQL_NULL_HDBC;
SQLHSTMT hstmt = SQL_NULL_HSTMT;
SQLTCHAR * pszConnection = _T("DRIVER={SQL Server Native Client 10.0};Server=MyServer;Trusted_Connection=Yes;");
// insert one TVP parameter
SQLTCHAR * pszInsertStmt = _T("{call MCLOGInsert(?)}");
SQLLEN cbParamLength;
SQLULEN cMaxRows = 9;
CHKRC(SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HENV, &henv));
CHKRC(SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0));
CHKRC(SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc));
CHKRC(SQLSetConnectAttr( hdbc, SQL_ATTR_LOGIN_TIMEOUT, reinterpret_cast<SQLPOINTER>(60), SQL_IS_UINTEGER));
CHKRC(SQLDriverConnect( hdbc, NULL, pszConnection, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT));
CHKRC(SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt));
CHKRC(SQLPrepare(hstmt, pszInsertStmt, SQL_NTS));
// Bind the first parameter
CHKRC(SQLBindParameter( hstmt, 1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, cMaxRows, 0, (SQLPOINTER)1, 0, &cbParamLength));
/*
// If the stored procedure is executed as T-SQL ("exec sp_insert ?, ?"), then, supply the type name.
CHKRC(SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_SS_TABLE, cMaxRows, 0, (SQLPOINTER)lpszTVPParamType, SQL_NTS, &cbParamLengths));
*/
// bind TVP columns.
CHKRC(SQLSetStmtAttr( hstmt, SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER)1, SQL_IS_INTEGER));
// For the first and the second TVP columns (bigint, int), always send them as bound.
// For the third column varbinary(max), either send them as bound or DAE.
const size_t ARRAY_SIZE = 3;
LONGLONG llSeqNo[ARRAY_SIZE];
SQLLEN cbSeqNo[ARRAY_SIZE] = {sizeof(LONGLONG), sizeof(LONGLONG), sizeof(LONGLONG)};
LONG lSeries[ARRAY_SIZE];
SQLLEN cbSeries[ARRAY_SIZE] = {sizeof(LONG), sizeof(LONG), sizeof(LONG)};
BYTE rgbRestData[ARRAY_SIZE][2048];
SQLLEN cbRestData[ARRAY_SIZE] = {sizeof(rgbRestData[0]), sizeof(rgbRestData[0]), sizeof(rgbRestData[0])};
SQLUSMALLINT iColumn = 1;
// Bind biSeqNo
CHKRC(SQLBindParameter( hstmt, iColumn, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, sizeof(LONGLONG), 0, (SQLPOINTER)&llSeqNo, sizeof(llSeqNo[0]), cbSeqNo));
// Bind iSeries
iColumn++;
CHKRC(SQLBindParameter( hstmt, iColumn, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, sizeof(LONG), 0, (SQLPOINTER)&lSeries, sizeof(lSeries[0]), cbSeries));
// Bind bmRestData
iColumn++;
CHKRC(SQLBindParameter(hstmt, iColumn, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_VARBINARY, 0, 0, (SQLPOINTER)rgbRestData, sizeof(rgbRestData[0]), cbRestData));
CHKRC(SQLSetStmtAttr(hstmt, SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER)0, SQL_IS_INTEGER));
// Set cbParamLength to SQL_DATA_AT_EXEC to indicate the TVP parameter is bound as DAE.
cbParamLength = SQL_DATA_AT_EXEC;
rc = SQLExecute(hstmt);
if (rc == SQL_NEED_DATA) {
SQLPOINTER ptr = NULL;
SQLUINTEGER cRows = 0;
rc = ::SQLParamData(hstmt, &ptr);
while (rc == SQL_NEED_DATA) {
if (ptr == (SQLPOINTER)1) {
// it is the TVP parameter
if (cRows >= cMaxRows) {
// We finish sending the last row already.
CHKRC(::SQLPutData(hstmt, NULL, 0));
}
else {
// Obtaining row data from somewhere. In this case we will fill 3 rows.
for (size_t i = 0; i < ARRAY_SIZE; i++) {
llSeqNo[i] = cRows + i + 1;
lSeries[i] = llSeqNo[i] * 10;
// Now fill the varbinary(max) column. Assume that the even row can't be fit into
// the buffer provided as send them as DAE.
if (!((cRows + i) % 2)) {
// SQL_DATA_AT_EXEC means send DAE data.
cbRestData[i] = SQL_DATA_AT_EXEC;
}
else {
// data can fit into the buffer, then copy the data to the buffer directly.
cbRestData[i] = 100;
::memset(&rgbRestData[i], 'b', cbRestData[i]);
}
}
CHKRC(::SQLPutData(hstmt, (SQLPOINTER)1, ARRAY_SIZE));
cRows += ARRAY_SIZE;
}
}
else if ((SQLPOINTER)&rgbRestData[0] <= ptr && ptr <= (SQLPOINTER)&rgbRestData[ARRAY_SIZE-1]) {
// it is varbinary(max) column
// Send data in parts.
for (int i = 0; i < 3; i++) {
// Obtain the data in part from somewhere, here we just set all bytes to 'a'.
::memset(ptr, 'a', sizeof(rgbRestData[0]));
CHKRC(::SQLPutData(hstmt, (SQLPOINTER)ptr, sizeof(rgbRestData[0])));
}
}
else {
// handling other DAE parameters, but in our case, we don't have other DAE parameters.
goto EXIT;
}
rc = ::SQLParamData(hstmt, &ptr);
}
}
EXIT:
if (!SUCCESS(rc)) {
if (hstmt)
PrintError(SQL_HANDLE_STMT, hstmt);
if (hdbc)
PrintError(SQL_HANDLE_DBC, hdbc);
if(henv)
PrintError(SQL_HANDLE_ENV, henv);
}
if (hstmt)
SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
if (hdbc) {
SQLDisconnect(hdbc);
SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
}
if (henv)
SQLFreeHandle(SQL_HANDLE_ENV, henv);
}
Historique des modifications
Mise à jour du contenu |
---|
Ajout d'un exemple. |