SQL 使用 table 值参数时本机客户端界面错误 8058,无法诊断原因

SQL Native client interface error 8058 when using table-valued parameter, cannot diagnose cause

我编写了一个函数,它使用 table 值参数将数据发送到 T-SQL 存储过程。这是我第一次这样做,我被这个错误困住了:

The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Table-valued parameter 1, to a parameterized string has no table type defined.

不幸的是,我在网上找不到任何资源来提供解决此类错误的方法。从消息中我不知道这里出了什么问题。

在下面的代码中调用 SQLExecDirectA 时发生错误。

ErrorCode SQLif::InsertObjectBatch(ObjTypeKey* pObjectTypeKeys, ObjectId* pObjectIds, unsigned int numObjects)
{
    ErrorCode ec = ACK;
    EnterCriticalSection(m_pcs);

    SQLRETURN ret;
    SQLHSTMT hStmt;

    StorageClass* pStorageClasses = new StorageClass[numObjects];
    RevisionId* pRevisionIds = new RevisionId[numObjects];

    if (!pStorageClasses || !pRevisionIds) 
    {
        if (pStorageClasses) delete pStorageClasses;
        if (pRevisionIds) delete pRevisionIds;
        LeaveCriticalSection(m_pcs);
        return NACK("SQLif::InsertObjectBack: Allocation failure allocating arrays");
    }

    for (unsigned int i = 0; i < numObjects; i++)
        pStorageClasses[i] = PERSIST;
    for (unsigned int i = 0; i < numObjects; i++)
        pRevisionIds[i] = 0;


    // Variable for TVP row count;
    SQLINTEGER cbTVP;

    unsigned long long objectIdParam = 0;
    SQLLEN objectIdParamLen = sizeof(objectIdParam);

    // Allocate a statement handle
    if ((ec = AllocStatementHandle(&hStmt)) != ACK)
    {
        LeaveCriticalSection(m_pcs);
        return ec;
    }

    // Bind paramters for call  
    if (ec == ACK)
    {
        ret = SQLBindParameter( hStmt,  // Statement handle
                                1,      // ParameterNumber
                                SQL_PARAM_INPUT,    // InputOutputType
                                SQL_C_DEFAULT,      // ValueType
                                SQL_SS_TABLE,       // ParameterType
                                (SQLINTEGER) numObjects, // Number of rows in TVP
                                0,                  // Number of columns in TVP
                                NULL,               // not needed
                                NULL,               // not needed
                                &cbTVP );
        if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO)) ec = NACK("SQLif::InsertObjectBatch: Binding to parameter 1 failed");
    }

    // Bind colums for the TVP (param 1)
    if (ec == ACK)
    {
        ret = SQLSetStmtAttr(hStmt, SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 1, SQL_IS_INTEGER);
        if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO)) ec = NACK("SQLif::InsertObjectBatch: Setting Focus to TVP failed");
    }

    // Bind column 1
    if (ec == ACK)
    {
        ret = SQLBindParameter(hStmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, pStorageClasses, sizeof(SQLINTEGER), NULL);
        if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO)) ec = NACK("SQLif::InsertObjectBach: Binding to column 1 of TVP failed");
    }
    // Bind column 2
    if (ec == ACK)
    {
        ret = SQLBindParameter(hStmt, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, pObjectTypeKeys, sizeof(SQLUINTEGER), NULL);
        if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO)) ec = NACK("SQLif::InsertObjectBach: Binding to column 2 of TVP failed");
    }
    // Bind column 3
    if (ec == ACK)
    {
        ret = SQLBindParameter(hStmt, 3, SQL_PARAM_INPUT, SQL_C_UBIGINT, SQL_BIGINT, 0, 0, pRevisionIds, sizeof(SQLUBIGINT), NULL);
        if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO)) ec = NACK("SQLif::InsertObjectBach: Binding to column 3 of TVP failed");
    }
    // Release focus
    if (ec == ACK)
    {
        ret = SQLSetStmtAttr(hStmt, SQL_SOPT_SS_PARAM_FOCUS, (SQLPOINTER) 0, SQL_IS_INTEGER);
        if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO)) ec = NACK("SQLif::InsertObjectBatch: Resetting Focus failed");
    }

    // Initialize row count
    cbTVP = numObjects;

    if (ec == ACK)
    {
        ret =SQLExecDirectA(hStmt, (SQLCHAR*)"EXEC Proc_InsertObjectBatch ?", SQL_NTS);     
        if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO)) ec = GetErrorMessage(hStmt);
    }

    if (ec == ACK)
    {   
        ret = SQLBindCol(hStmt, 1, SQL_C_UBIGINT, &objectIdParam, sizeof(objectIdParam), &objectIdParamLen);
        if (ret != SQL_SUCCESS) ec = NACK("SQLif::InsertObjectBatch: Binding to column 1 failed");
    }

    bool bHasData = false;
    unsigned int i = 0;
    if (ec == ACK)
    {
        while ((ret = SQLFetch(hStmt)) != SQL_NO_DATA)
        {
            if ((ret != SQL_SUCCESS) && ( ret != SQL_SUCCESS_WITH_INFO)) ec = NACK("SQLif::RetrieveAttribute: Unable to fetch rows");

            bHasData = true;
            pObjectIds[i] = objectIdParam;
        }
    }
    FreeStatementHandle(hStmt);

    LeaveCriticalSection(m_pcs);

    return ACK;
}

这是对应的存储过程

-- Create a procedure to insert batch of objects
CREATE PROCEDURE Proc_InsertObjectBatch @InsertObjects_TVP ObjectTableType READONLY AS
-- Declare a variable holding return value
DECLARE @return_value integer;
-- Declare a table to hold oid of created objects
DECLARE @OutputTable TABLE (oid bigint);

-- Insert objects
INSERT INTO dbo.object_table(stkey, otkey, orev)
OUTPUT INSERTED.oid INTO @OutputTable
SELECT * from @InsertObjects_TVP;

-- Return oid's
SELECT oid FROM @OutputTable;

SET @return_value = 0;

RETURN @return_value
-- End of stored procedure Proc_InsertObjectBatch

这是table类型。

-- Create ObjectTableType
CREATE TYPE ObjectTableType AS TABLE
(
    stkey integer,
    otkey integer,
    orev bigint
)

如能提供解决此问题的任何帮助,我们将不胜感激。

这次修改成功了。

SQLWCHAR* TVP = (SQLWCHAR*) L"ObjectTableType";
// Bind paramters for call  
if (ec == ACK)
{
    ret = SQLBindParameter( hStmt,  // Statement handle
                            1,      // ParameterNumber
                            SQL_PARAM_INPUT,    // InputOutputType
                            SQL_C_DEFAULT,      // ValueType
                            SQL_SS_TABLE,       // ParameterType
                            (SQLINTEGER) numObjects, // Number of rows in TVP
                            0,                  // Number of columns in TVP
                            TVP,               // Specify Table type
                            SQL_NTS,               // Termination method for table type
                            &cbTVP );
    if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO)) ec = NACK("SQLif::InsertObjectBatch: Binding to parameter 1 failed");
}