【问题标题】:Cannot execute synonym stored procedure with SQL Server ODBC Driver; works with OLEDB无法使用 SQL Server ODBC 驱动程序执行同义词存储过程;与 OLEDB 一起使用
【发布时间】:2016-02-09 15:44:47
【问题描述】:

这个问题是 Microsoft ODBC 中一系列错误的一部分 司机:

微软已经表示他们不会在他们的 ODBC 中修复这些错误 司机。


我有(许多)实际上是同义词的存储过程。存储过程规范地存在于一个数据库中,但在其他数据库中可见。

存储过程可以在 SQL Server Management Studio 中正常执行:

EXECUTE Report_ThirdParty @ContosoGUID = '{CC0ECA32-BEFA-11E5-8E2A-C86000D0B92A}'

如果我使用任何 OLEDB 提供程序连接到 SQL Server:

  • SQL Server Native Client 10.0 OLE DB 提供程序Provider=SQLNCLI10;Data Source=contoso.stackoverflow.com;User ID=ContosoManager;Password=correct horse battery staple;
  • 用于 SQL Server 的 Microsoft OLE DB 提供程序Provider=SQLOLEDB;Data Source=contoso.stackoverflow.com;User ID=contoso.stackoverflow.com;Password=correct horse battery staple;

然后存储过程执行良好。我得到结果。每个人都很开心。

但不使用 ODBC 驱动程序

对于the announcement of the deprecation of OleDb drivers,我想使用 SQL Server 的 ODBC 驱动程序进行测试。当我更改连接以使用 SQL Server ODBC 驱动程序之一(例如 "{SQL Server}")并执行相同的 SQL 语句

EXECUTE Report_ThirdParty @ContosoGUID = '{CC0ECA32-BEFA-11E5-8E2A-C86000D0B92A}'

我得到错误:

对过程“Report_ThirdParty”的请求失败,因为“Report_ThirdParty”是同义词对象

无论我使用 SQL Server 的原始 ODBC 驱动程序还是本机客户端,这都是正确的:

  • SQL ServerProvider=MSDASQL;Driver={SQL Server};Server={contoso.stackoverflow.com};UID={contosoManager};PWD={correct horse battery staple};

  • SQL Server Native Client 11.0Provider=MSDASQL;Driver={SQL Server Native Client 11.0};Server={contoso.stackoverflow.com};UID={ContosoManager};PWD={correct horse battery staple};

在这两种变体中,我得到相同的错误:

[Microsoft][SQL Server Native Client 11.0][SQL Server]对过程“Report_ThirdParty”的请求失败,因为“Report_ThirdParty”是同义词对象

或者对于旧的 ODBC 驱动程序:

[Microsoft][ODBC SQL Server Driver][SQL Server]对过程“Report_ThirdParty”的请求失败,因为“Report_ThirdParty”是同义词对象

换句话说:

对过程“%s”的请求失败,因为“%s”是同义词对象

ConnectionErrors 集合提供了更多信息:

  • 错误#1

    • 编号: 0x80040E14
    • 来源: Microsoft OLE DB Provider for ODBC Drivers
    • 说明: [Microsoft][ODBC SQL Server Driver][SQL Server]对过程“Report_ThirdParty”的请求失败,因为“Report_ThirdParty”是同义词对象。
    • SQLState: 37000
    • NativeError:2809
  • 错误#2

    • 编号: 0x80040E14
    • 来源: Microsoft OLE DB Provider for ODBC Drivers
    • 说明: [Microsoft][ODBC SQL Server Driver][SQL Server]游标未声明。
    • SQLState: 37000
    • NativeError: 16945

放弃切换到 ODBC 并没有什么坏处。而且我不会停止使用同义词。

但是出了什么问题,我该如何告诉 SQL Server 的 ODBC 驱动程序工作?

SQL Profiler 结果

  • RPC:Starting: declare @p1 int set @p1=0 declare @p3 int set @p3=16388 declare @p4 int set @p4=8193 declare @p5 int set @p5=0 exec sp_cursoropen @p1 输出,N'EXECUTE Report_ThirdParty @ContosoGUID = ''{3492C4E6-D500-4A23-9CAB-CB6582C27ABD}''',@p3 输出,@p4 输出,@p5 输出选择@p1,@p3,@p4 , @p5
  • 异常错误:2809,严重性:18,状态:1
  • 用户错误消息对过程“Report_ThirdParty”的请求失败,因为“Report_ThirdParty”是同义词对象。
  • 异常错误:16945,严重性:16,状态:2
  • 用户错误消息光标未声明。
  • RPC:Completed: : 声明@p1 int set @p1=0 声明@p3 int set @p3=16388 声明@p4 int set @p4=8193 声明@p5 int set @p5= 0 exec sp_cursoropen @p1 输出,N'EXECUTE Report_ThirdParty @ContosoGUID = ''{3492C4E6-D500-4A23-9CAB-CB6582C27ABD}''',@p3 输出,@p4 输出,@p5 输出选择@p1,@p3,@ p4,@p5

注意事项

  • 本机(即非 .NET)代码。您可以假装它是 C、C++、汇编或 Delphi。
  • Microsoft SQL Server 2008 R2 (SP2) - 10.50.4000.0 (X64)

ODBC 的其他意想不到的功能(在 OLEDB 中工作)

阅读奖励

【问题讨论】:

  • 您是否运行过跟踪并捕获了 ODBC 正在发送的命令文本?
  • ODBC 驱动程序总是会调用 sp_cursoropen,对我来说,我看到与您在带有 SP 同义词的 SSMS 中使用相同的错误,因此它可能与该系统 SP 的实现有关,还有其他问题研究
  • 您可能已经看过这些,但是,FWIW,this 来自似乎是 MSFT 员工的帖子表明支持有限但没有太多详细说明。 This 条目表明这是一个正在调查的已知限制。
  • 我无法使用 C++ 和 Sql Server 本机客户端 11 在 Sql Server 2014 中重现此问题。但在我的简单测试用例中,存储过程和同义词都在同一个数据库中。

标签: sql-server winapi odbc


【解决方案1】:

作为对我的评论的跟进,这里是我的(有点不那么最小的)例子:

一个非常简单的存储过程:

USE [Test]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[foo] 
@p1 int
AS
RETURN 13 + @p1

并且已创建(使用 gui 工具)该过程的同义词,名为 dbo.fooSyn

我可以在 SQL Server 2014 Management Studio 中执行这两项操作:

execute foo @p1 = 2
GO
execute fooSyn @p1 = 3
GO

两个语句都完成且没有错误。

这是我使用名称和同义词执行一次的测试代码:

#include <windows.h>
#include <tchar.h>
#include <iostream>
#include <sql.h>
#include <sqlext.h>
#include <sqlucode.h>   

void printErrDbc(SQLHDBC hDbc)
{
    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(SQL_HANDLE_DBC, hDbc, 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;
    }
}

void printErrStmt(SQLHSTMT hStmt)
{
    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(SQL_HANDLE_STMT, hStmt, 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[])
{
    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=.\\INSTANCE;Database=Test;Trusted_Connection=yes;";
    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))
    {
        printErrDbc(handleDBC);
    }

    nResult = SQLSetConnectAttr(handleDBC, SQL_ATTR_AUTOCOMMIT, (SQLUINTEGER)SQL_AUTOCOMMIT_OFF, NULL);
    if (!SQL_SUCCEEDED(nResult))
    {
        printErrDbc(handleDBC);
    }

    SQLHSTMT    handleStatement = 0;
    nResult = SQLAllocHandle(SQL_HANDLE_STMT, handleDBC, (SQLHANDLE*)&handleStatement);
    if (!SQL_SUCCEEDED(nResult))
    {
        printErrDbc(handleDBC);
    }

    // Bind return code
    SQLINTEGER res = 0;
    SQLLEN cb = 0;
    SWORD sParm1 = 0;
    SQLLEN cbParm1 = SQL_NTS;

    nResult = SQLBindParameter(handleStatement, 1, SQL_PARAM_OUTPUT, SQL_C_SSHORT, SQL_INTEGER, 0, 0, &sParm1, 0, &cbParm1);
    if (!SQL_SUCCEEDED(nResult))
    {
        printErrStmt(handleStatement);
    }

    // And call using synonym name
    nResult = SQLExecDirect(handleStatement, L"{? = call fooSyn(3)}", SQL_NTS);
    if (!SQL_SUCCEEDED(nResult))
    {
        printErrStmt(handleStatement);
    }

    nResult = SQLFetch(handleStatement);
    std::wcout << L"Result is: " << sParm1 << std::endl;

    // Note: It also works using EXECUTE - but I dont remember how to read return value like that.
    nResult = SQLExecDirect(handleStatement, L"execute foo @p1 = 2", SQL_NTS);
    if (!SQL_SUCCEEDED(nResult))
    {
        printErrStmt(handleStatement);
    }
    else
    {
        std::wcout << L"Working using name" << std::endl;
    }
    nResult = SQLExecDirect(handleStatement, L"execute fooSyn @p1 = 2", SQL_NTS);
    if (!SQL_SUCCEEDED(nResult))
    {
        printErrStmt(handleStatement);
    }
    else
    {
        std::wcout << L"Working using synonym" << std::endl;
    }

    return 0;
}

这会打印出预期的输出:

Result is: 16
Working using name
Working using synonym

那么,你的设置有什么不同?

总结一下我的设置:

  • SQL Server 2014 速成版
  • SQL Server native Client 11.0 版本 2011.110.3000.00(但它也可以仅使用 {SQL Server} 作为驱动程序)
  • Windows 7 教授
  • 使用 Visual Studio 2013 编译。
  • 使用 odbc 3.8 版。
  • 同义词和存储过程在同一个数据库中,甚至在这个数据库的同一个模式中也是一样的。

【讨论】:

    猜你喜欢
    • 2020-06-01
    • 2010-10-04
    • 2011-02-11
    • 2014-11-04
    • 2020-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多