如何避免将SQL_二进制数组数据作为参数传递时以零结尾?(ODBC驱动程序)
我刚刚了解了Windows ODBC驱动程序API需要一个如何避免将SQL_二进制数组数据作为参数传递时以零结尾?(ODBC驱动程序),c,sql-server,windows,odbc,C,Sql Server,Windows,Odbc,我刚刚了解了Windows ODBC驱动程序API需要一个SQL\u二进制数据数组作为输入参数,以零字节终止。尽管我在文档中没有找到这样的语句,但我通过使用以下代码执行存储过程发现了这一点: 最小示例 //参数绑定 字节数据[10]={15,3,54,144,34,211,200,147,15,74}; SQLBindParameter(hstmt,1,SQL参数输入,SQL参数二进制,SQL参数二进制,10,0,数据,0,NULL); //程序执行 SQLRETURN res=SQLExecD
SQL\u二进制
数据数组作为输入参数,以零字节终止。尽管我在文档中没有找到这样的语句,但我通过使用以下代码执行存储过程发现了这一点:
最小示例
//参数绑定
字节数据[10]={15,3,54,144,34,211,200,147,15,74};
SQLBindParameter(hstmt,1,SQL参数输入,SQL参数二进制,SQL参数二进制,10,0,数据,0,NULL);
//程序执行
SQLRETURN res=SQLExecDirect(hstmt,(SQLCHAR*)“{call dbo.Update_Data(?)}”,SQL\NTS);
它导致SQLExecDirect
因SQL\u NULL\u数据
而失败。使用SQLGetDiagRec
查询诊断记录时,我已收到该记录:
SQL State=22001,错误消息:“[Microsoft][ODBC SQL Server驱动程序]
字符串或二进制数据将被截断
虽然这通常表示插入或更新到列中的数据比列本身大,但这里的情况并非如此。在尝试了4个小时的不同参数和语句后,我终于发现解决方案非常简单,只需在最后一个位置用零终止字节数组:
//参数绑定
字节数据[11]={15,3,54,144,34,211,200,147,15,74,0};// 二进制数据必须以null结尾不是真的(如果是真的,则不能插入任何包含0值的数据,如{100,0,100,0,100}
)
您需要为缓冲区长度(缓冲区大小)设置正确的值李>
您需要正确设置并初始化StrLen\u或\u IndPtr参数。对于二进制缓冲区,StrLen_或_IndPtr的值必须是缓冲区中保存的数据的长度。请注意,这不能与实际缓冲区大小相同(但必须是二进制数据必须以null结尾不是真的(如果是真的,则不能插入任何包含0值的数据,如{100,0,100,0,100}
)
您需要为缓冲区长度(缓冲区大小)设置正确的值
您需要正确设置并初始化StrLen_或_IndPtr参数。对于二进制缓冲区,StrLen_或_IndPtr的值必须是缓冲区中保存的数据的长度。请注意,这不得与实际缓冲区大小相同(但必须为“我刚刚了解到,Windows ODBC驱动程序API需要一个SQL_二进制数据数组作为输入参数,以零字节结尾。“-根据我的经验,这肯定不是真的。如果您认为是这样,请发布一些可编译的代码来演示它,并/或将其作为错误报告给ODBC驱动程序的供应商。@NeilButterworth您可能很幸运,在您绑定为参数的二进制数据之后,您的内存为零(在它工作时发生在我身上)。包含整个ODBC句柄分配和驱动程序连接将使此问题至少扩展50行代码,并且在我看来不再显示问题的最小示例。我通过直接从management studio执行来确保查询运行时没有错误。这肯定是ODBC驱动程序API的问题。您如果您设置了最新的参数(StrLen\u或\u IndPtr
),则需要指定函数,即SQLBindParameter(…,data,10,NULL);
)不是NULL
你得到了什么?它应该返回二进制数据的长度,对吗?@Vinzenz:如下所述,你必须同时设置。Buffer length和StrLen_或_IndPtr。StrLen_或_IndPtr不能总是与Buffer length相同的值。例如,考虑VARBINARY列。。“我刚刚了解到,Windows ODBC驱动程序API需要一个SQL_二进制数据数组作为输入参数,以零字节结尾。“-根据我的经验,这肯定不是真的。如果您认为是这样,请发布一些可编译的代码来演示它,并/或将其作为错误报告给ODBC驱动程序的供应商。@NeilButterworth您可能很幸运,在您绑定为参数的二进制数据之后,您的内存为零(在它工作时发生在我身上)。包含整个ODBC句柄分配和驱动程序连接将使此问题至少扩展50行代码,并且在我看来不再显示问题的最小示例。我通过直接从management studio执行来确保查询运行时没有错误。这肯定是ODBC驱动程序API的问题。您如果您设置了最新的参数(StrLen\u或\u IndPtr
),则需要指定函数,即SQLBindParameter(…,data,10,NULL);
)你得到的不是NULL
而是什么?它应该返回二进制数据的长度,对吗?@Vinzenz:如下所述,你必须同时设置。Buffer length和StrLen_或_IndPtr。StrLen_或_IndPtr不能总是与Buffer length相同的值。例如,考虑一个VARBINARY列..你对StrL绝对正确en_或_IndPtr
参数!我刚刚调整了单元测试,将BufferLength
和StrLen或_IndPtr
参数都设置为10,它起了作用!现在好奇的是,我测试了同样的事情,将BufferLength
保留为0
。甚至这也起到了作用。因此看起来BufferLength
实际上正在被激活对于SQL\u C\u BINARY
或与StrLen\u或\u IndPtr
缓冲区相关的or。如果将BufferLength
参数保留在0
,是否会得到相同的结果?如果是,您可以调整您的答案吗?除此之外,回答很好,感谢您花费时间!!它实际上可以用作if-else系统。如果如果给定StrLen_或_IndPtr
参数,它将被解释为len
#include <windows.h>
#include <tchar.h>
#include <iostream>
#include <sql.h>
#include <sqlext.h>
#include <sqlucode.h>
void printErr(SQLHANDLE handle, SQLSMALLINT handleType)
{
SQLSMALLINT recNr = 1;
SQLRETURN ret = SQL_SUCCESS;
while (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
{
SQLWCHAR errMsg[SQL_MAX_MESSAGE_LENGTH + 1];
SQLWCHAR sqlState[5 + 1];
errMsg[0] = 0;
SQLINTEGER nativeError;
SQLSMALLINT cb = 0;
ret = SQLGetDiagRec(handleType, handle, recNr, sqlState, &nativeError, errMsg, SQL_MAX_MESSAGE_LENGTH + 1, &cb);
if (ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO)
{
std::wcerr << L"ERROR; native: " << nativeError << L"; state: " << sqlState << L"; msg: " << errMsg << std::endl;
}
++recNr;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
// connect to db
SQLRETURN nResult = 0;
SQLHANDLE handleEnv = 0;
nResult = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, (SQLHANDLE*)&handleEnv);
nResult = SQLSetEnvAttr(handleEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3_80, SQL_IS_INTEGER);
SQLHANDLE handleDBC = 0;
nResult = SQLAllocHandle(SQL_HANDLE_DBC, handleEnv, (SQLHANDLE*)&handleDBC);
SQLWCHAR strConnect[256] = L"Driver={SQL Server Native Client 11.0};Server=.\\TEST;Database=Foobar;Uid=Secret;Pwd=Secret";
SQLWCHAR strConnectOut[1024] = { 0 };
SQLSMALLINT nNumOut = 0;
nResult = SQLDriverConnect(handleDBC, NULL, (SQLWCHAR*)strConnect, SQL_NTS, (SQLWCHAR*)strConnectOut, sizeof(strConnectOut), &nNumOut, SQL_DRIVER_NOPROMPT);
if (!SQL_SUCCEEDED(nResult))
printErr(handleDBC, SQL_HANDLE_DBC);
// Allocate a statement
SQLHSTMT handleStatement = SQL_NULL_HSTMT;
nResult = SQLAllocHandle(SQL_HANDLE_STMT, handleDBC, (SQLHANDLE*)&handleStatement);
if (!SQL_SUCCEEDED(nResult))
printErr(handleDBC, SQL_HANDLE_DBC);
int col = 0;
SQLRETURN res = SQL_NTS;
// Params
SQLBIGINT id = 2;
SQLCHAR data[10] = { 15, 3, 54, 144, 34, 211, 200, 147, 15, 74 };
SQLLEN cbId = 0;
SQLLEN cbData = 10;
res = SQLBindParameter(handleStatement, 1, SQL_PARAM_INPUT, SQL_C_SBIGINT, SQL_BIGINT, 0, 0, &id, sizeof(id), &cbId);
if (!SQL_SUCCEEDED(res))
printErr(handleStatement, SQL_HANDLE_STMT);
res = SQLBindParameter(handleStatement, 2, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, 10, 0, data, sizeof(data), &cbData);
if (!SQL_SUCCEEDED(res))
printErr(handleStatement, SQL_HANDLE_STMT);
// Execution
res = SQLExecDirect(handleStatement, (SQLWCHAR*)L"{call dbo.Update_Store_Data(?,?)}", SQL_NTS);
if (!SQL_SUCCEEDED(res))
{
printErr(handleStatement, SQL_HANDLE_STMT);
}
return 0;
}