У меня проблемы с получением этого кода для заполнения выходных параметров клиентским драйвером MSSQL ODBC 10.0 и 9.0.Я могу подтвердить, что мое направление привязки установлено правильно.
Я также вызываю SQLMoreResults, моя хранимая процедура имеет установленный NOCOUNT и отлично работает в другой библиотеке на основе ODBC.В моем операторе execute он успешно выполняется, но SQLMoreResults всегда говорит, что результатов больше нет, и я понимаю, что эти параметры не заполняются, пока не пройдут все наборы результатов.Один выходной параметр, который я использую - это bigint.
К сожалению, я не знаю всех тонкостей разработки ODBC, и должно быть что-то важное, что я упускаю.Я пытаюсь повторно использовать мой дескриптор оператора, но я сбрасываю его после первого вызова SQLProcedureColumns и удаляю связанные переменные.Затем я перепривязываю.
Есть идеи, где я сбился с пути?
bool ODBCConnection::Execute()
{
LLOG("Execute " << (void *)this << " " << (void *)session);
if(session->hstmt == SQL_NULL_HANDLE)
return false;
if(IsCurrent())
session->current = NULL;
session->FlushConnections();
last_insert_table.Clear();
number.Clear();
text.Clear();
time.Clear();
CParser p(statement);
/* parse for stored procedure */
bool isStoredProcedure = false;
if (p.Char('{'))
{
p.Spaces();
String procedure_name;
p.Id("?");
p.Id("=");
if (p.Id("call") || p.Id("CALL")) {
procedure_name = p.ReadId();
isStoredProcedure = true;
//Cout() << "Proc name: " << procedure_name << "\n";
}
SQLSetEnvAttr(session->henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_INTEGER);
SDWORD cbValue5;
SDWORD cbValue4;
SQLSMALLINT ParameterType = SQL_PARAM_INPUT;
if (!IsOk(SQLProcedureColumns (
session->hstmt,
NULL,
0,
NULL,
0,
(SQLCHAR *)~procedure_name,
procedure_name.GetLength(),
NULL,
0
))) {
SQLFreeStmt(session->hstmt, SQL_CLOSE);
return false;
}
char parameter_name [20];
if (!IsOk(SQLBindCol(
session->hstmt,
4, // Column 4 returns column name
SQL_C_CHAR,
parameter_name,
sizeof(parameter_name),
&cbValue4
))) {
}
if (!IsOk(SQLBindCol(
session->hstmt,
5, // Column 5 returns whether parameter is input or output
SQL_C_SHORT,
&ParameterType,
0,
&cbValue5
))) {
}
int i = 0;
while (SQLFetch(session->hstmt) == SQL_SUCCESS) {
Param& p = param[i];
Cout() << ParameterType << "\n";
/*switch (ParameterType) {
case SQL_PARAM_INPUT:
case SQL_PARAM_OUTPUT:
case SQL_PARAM_INPUT_OUTPUT:
p.direction = ParameterType;
break;
case 5:
p.direction = SQL_PARAM_OUTPUT;
break;
default:
break;
}*/
if(ParameterType == 5)
p.direction = SQL_PARAM_OUTPUT;
else
p.direction = ParameterType;
i++;
}
SQLFreeStmt(session->hstmt, SQL_CLOSE);
SQLFreeStmt(session->hstmt, SQL_RESET_PARAMS);
SQLFreeStmt(session->hstmt, SQL_UNBIND);
}
if((p.Id("insert") || p.Id("INSERT")) && (p.Id("into") || p.Id("INTO")) && p.IsId())
last_insert_table = p.ReadId();
if(!IsOk(SQLPrepare(session->hstmt, (SQLCHAR *)~statement, statement.GetCount())))
return false;
parse = false;
bparam = param;
param.Clear();
for(int i = 0; i < bparam.GetCount(); i++) {
Param& p = bparam[i];
SQLSMALLINT DataType;
SQLULEN ParameterSize;
SQLSMALLINT DecimalDigits;
SQLSMALLINT Nullable;
Cout() << "Direction: " << p.direction << "\n";
Cout() << "Length: " << p.li << "\n";
if(!IsOk(SQLDescribeParam(session->hstmt, i + 1, &DataType, &ParameterSize, &DecimalDigits, &Nullable)))
return false;
if(!IsOk(SQLBindParameter(session->hstmt, i + 1, p.direction, p.ctype, DataType,
ParameterSize, DecimalDigits, (SQLPOINTER)~p.data, p.data.GetLength(),
&p.li)))
return false;
}
SQLSMALLINT ncol;
if(!isStoredProcedure)
{
if(!IsOk(SQLExecute(session->hstmt)) || !IsOk(SQLNumResultCols(session->hstmt, &ncol))) {
Cout() << "SQLExecute crashed\n";
SQLFreeStmt(session->hstmt, SQL_CLOSE);
return false;
}
}
else
{
Cout() << "statement: " << statement << "\n";
if(!IsOk(SQLExecute(session->hstmt))) {
Cout() << "SQLExecute crashed\n";
SQLFreeStmt(session->hstmt, SQL_CLOSE);
return false;
}
Cout() << "Calling SQLMoreResults...\n";
//SQLFreeStmt(session->hstmt, SQL_CLOSE);
int iReturn = SQLMoreResults(session->hstmt);
Cout() << "SQLMoreResults return code: " << iReturn << "\n";
while (iReturn == SQL_SUCCESS || iReturn == SQL_SUCCESS_WITH_INFO)
{
iReturn = SQLMoreResults(session->hstmt);
} ;
//SQLFreeStmt(session->hstmt, SQL_RESET_PARAMS);
//SQLFreeStmt(session->hstmt, SQL_UNBIND);
ncol = 0;
}
session->current = this;
info.Clear();
binary.Clear();
for(int i = 1; i <= ncol; i++) {
SQLCHAR ColumnName[256];
SQLSMALLINT NameLength;
SQLSMALLINT DataType;
SQLULEN ColumnSize;
SQLSMALLINT DecimalDigits;
SQLSMALLINT Nullable;
if(!IsOk(SQLDescribeCol(session->hstmt, i, ColumnName, 255, &NameLength, &DataType,
&ColumnSize, &DecimalDigits, &Nullable)))
return false;
binary.Add(false);
SqlColumnInfo& f = info.Add();
f.nullable = Nullable != SQL_NO_NULLS;
f.binary = false;
f.precision = DecimalDigits;
f.scale = 0;
f.width = ColumnSize;
f.name = (char *)ColumnName;
switch(DataType) {
case SQL_DECIMAL:
case SQL_NUMERIC:
case SQL_SMALLINT:
case SQL_INTEGER:
case SQL_REAL:
case SQL_FLOAT:
case SQL_DOUBLE:
case SQL_BIT:
case SQL_TINYINT:
f.type = DOUBLE_V;
break;
case SQL_BIGINT:
f.type = INT64_V;
break;
case SQL_TYPE_DATE:
case SQL_TYPE_TIMESTAMP:
f.type = TIME_V;
break;
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
f.type = STRING_V;
f.binary = true;
binary.Top() = true;
break;
default:
f.type = STRING_V;
break;
}
}
SQLLEN rc;
SQLRowCount(session->hstmt, &rc);
rowsprocessed = rc;
return true;
}