数据库引擎扩展存储过程编程

适用范围:SQL Server

重要

将在 SQL Server 的未来版本中删除此功能。 避免在新开发工作中使用此功能,并计划修改当前使用此功能的应用程序。 改用 CLR集成

扩展存储过程的工作原理

扩展存储过程的工作过程是:

  1. 当客户端执行扩展存储过程时,请求会以表格数据流(TDS)或简单对象访问协议(SOAP)格式从客户端应用程序传输到SQL Server。

  2. SQL Server 会搜索与扩展存储过程关联的 DLL,如果还没加载该 DLL,就会加载。

  3. SQL Server 调用请求的扩展存储过程(作为 DLL 内的一个函数实现)。

  4. 扩展存储过程通过扩展存储过程API将结果集和参数返回给服务器。

过去,开放数据服务被用于编写服务器应用程序,例如访问非SQL Server数据库环境的网关。 SQL Server 不支持开放数据服务 API 中已过时的部分。 原始开放数据服务 API 中唯一仍被 SQL Server 支持的是扩展存储过程函数,因此该 API 被更名为扩展存储过程 API。

随着分布式查询和 CLR 集成的出现,扩展存储过程 API 应用的需求基本被取代。

如果你已有网关应用,就不能用 opends60.dll SQL Server 自带的网关来运行这些应用。 网关应用已不再支持。

扩展存储过程与 CLR 集成

CLR集成为编写服务器端逻辑提供了更稳健的替代方案,而这些逻辑在Transact-SQL中要么难以表达,要么无法写成。 在早期的SQL Server版本中,扩展存储过程(XPs)是数据库应用开发者编写此类代码的唯一机制。

通过 CLR 集成,原本以存储过程形式书写的逻辑通常更适合用表值函数表达,这些函数通过将结果 SELECT 嵌入 FROM 子句中来查询。

欲了解更多信息,请参见 CLR集成概览

扩展存储过程的执行特性

扩展存储过程的执行具有以下特性:

  • 扩展存储过程函数在 SQL Server 的安全上下文下执行。

  • 扩展存储过程函数运行在 SQL Server 的进程空间中。

  • 与扩展存储过程执行相关的线程与客户端连接所使用的线程相同。

重要

在向服务器添加扩展存储过程并授予其他用户执行权限之前,系统管理员应彻底审查每个扩展存储过程,确保其不含有害或恶意代码。

在加载扩展存储过程 DLL 后,DLL 会一直加载在服务器的地址空间中,直到 SQL Server 停止运行或管理员通过 明确卸载 。DBCC <DLL_name> (FREE)

扩展存储过程可以通过以下 EXECUTE 语句从 Transact-SQL 以存储过程的形式执行:

EXECUTE @retval = xp_extendedProcName @param1, @param2 OUTPUT;

参数

@ 复刻

一个回报价值。

@ 第1段

输入参数。

@ 第2段

一个输入/输出参数。

注意

扩展存储过程提供性能提升和SQL Server功能扩展。 然而,由于扩展存储过程 DLL 和 SQL Server 共享相同的地址空间,问题过程可能会对 SQL Server 的功能产生负面影响。 虽然扩展存储过程 DLL 抛出的异常由 SQL Server 处理,但也可能损坏 SQL Server 的数据区域。 作为安全预防措施,只有 SQL Server 系统管理员可以向 SQL Server 添加扩展存储过程。 这些程序在安装前应经过彻底测试。

通过扩展存储过程 API 向服务器发送结果集

当将结果集发送到 SQL Server,扩展存储过程应调用相应的 API,具体如下:

  • srv_sendmsg该函数可以在所有行srv_sendrow(如有)为 的之前或之后任意顺序调用。 所有消息必须在完成状态以 发送 srv_senddone之前发送给客户端。

  • srv_sendrow该函数对每发送给客户端的行调用一次。 所有行必须先发送给客户端,然后发送任何消息、状态值或完成状态,且参数srv_sendmsgsrv_statussrv_pfieldsrv_senddone或 。

  • 发送未定义所有列 srv_describe 的行会导致应用程序发出信息错误消息并返回 FAIL 客户端。 在这种情况下,行并未被发送。

创建扩展存储过程

扩展存储过程是一种带有原型的C/C++函数:

SRVRETCODE xp_extendedProcName (SRVPROC *);

使用前缀 xp_ 是可选的。 扩展存储过程名称在 Transact-SQL 语句中引用时具有大小写区分,无论服务器上安装的代码页/排序顺序如何。 当你构建DLL时:

  • 如果需要一个入口点,就写一个 DllMain 函数。

    此函数是可选的。 如果你没有在源代码中提供,编译器会链接自己的版本,而它只会返回 TRUE。 如果你提供函数 DllMain ,作系统会在线程或进程连接或脱离DLL时调用该函数。

  • 所有从DLL外部调用的函数(所有扩展存储过程E函数)都必须导出。

    你可以在文件的.def部分中列出函数名称EXPORTS来导出函数,或者在源代码中用 ,一个 __declspec(dllexport)Microsoft编译器扩展名(__declspec()以两个底划线开头)作为前缀。

这些文件是创建扩展存储过程 DLL 所必需的。

文件 Description
srv.h 扩展存储过程 API 头文件
opends60.lib 导入库 opends60.dll

要创建扩展存储过程 DLL,创建一个类型为动态链接库的项目。 有关创建 DLL 的更多信息,请参阅开发环境文档。

所有扩展存储过程 DLL 都应实现并导出以下函数:

__declspec(dllexport) ULONG __GetXpVersion()
{
   return ODS_VERSION;
}

__declspec(dllexport) 是 Microsoft 专用的编译器扩展。 如果你的编译器不支持这个指令,你应该在文件EXPORTS里的章节导出这个函数DEF

当 SQL Server 启动时带有追踪标志 -T260 ,或拥有系统管理员权限的用户运行 DBCC TRACEON (260),且扩展存储过程 DLL 不支持 __GetXpVersion(),错误日志会打印以下警告信息(__GetXpVersion() 以两个下划线开头)。

Error 8131: Extended stored procedure DLL '%' does not export __GetXpVersion().

如果扩展存储过程 DLL 导出 __GetXpVersion(),但函数返回的版本小于服务器要求的版本,则错误日志中会打印出警告信息,说明函数返回的版本与服务器预期的版本。 如果你收到这个消息,说明你返回的值是错误的, __GetXpVersion()或者你编译的是较旧版本 srv.h的。

注释

SetErrorMode,Win32 函数,不应在扩展存储过程中调用。

长期运行的扩展存储过程应定期调用 srv_got_attention ,以便在连接被终止或批处理中止时,过程可以自行终止。

要调试扩展存储过程 DLL,将其复制到 SQL Server \Binn 目录。 要指定调试会话的可执行文件,输入 SQL Server 可执行文件的路径和文件名(例如, C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\Binn\sqlservr.exe)。 有关参数的信息 sqlservr ,请参见 sqlservr 应用

向SQL Server添加扩展存储过程

包含扩展存储过程函数的 DLL 作为 SQL Server 的扩展。 安装 DLL 时,将文件复制到目录中,例如默认包含标准 SQL Server DLL 文件C:\Program Files\Microsoft SQL Server\MSSQL16.0.<x>\MSSQL\Binn 的目录。

在扩展存储过程 DLL 复制到服务器后,SQL Server 系统管理员必须向 SQL Server 注册 DLL 中的每个扩展存储过程函数。 这是通过 sp_addextendedproc 系统存储过程完成的。

重要

系统管理员应彻底审查扩展存储过程,确保不含有害或恶意代码,然后才将其添加到服务器并授予其他用户执行权限。 验证所有用户的输入。 在验证用户输入之前,不要把它串接起来。 绝对不要执行根据尚未验证的用户输入构造的命令。

第一个 sp_addextendedproc 参数指定函数的名称,第二个参数指定该函数所在的 DLL 名称。 你应该指定DLL的完整路径。

注释

未注册完整路径的现有DLL在升级到SQL Server 2005(9.x)或更高版本后无法使用。 要纠正问题,可以先取消 sp_dropextendedproc 注册DLL,然后重新注册并 sp_addextendedproc, 指定完整路径。

sp_addextendedproc 指定的函数名称必须与DLL中函数名称(包括情况)完全相同。 例如,该命令将位于名为 xp_hello.dll的 dll 中的函数xp_hello,注册为 SQL Server 扩展存储过程:

sp_addextendedproc 'xp_hello', 'c:\Program Files\Microsoft SQL Server\MSSQL13.0.MSSQLSERVER\MSSQL\Binn\xp_hello.dll';

如果 中 sp_addextendedproc 指定的函数名称与 DLL 中的函数名称不完全一致,新名称会注册在 SQL Server 中,但该名称无法使用。 例如,虽然 xp_Hello 注册为位于 xp_hello.dll的 SQL Server 扩展存储过程,但 SQL Server 在 DLL 中找不到该函数,但如果你之后调用 xp_Hello 该函数。

-- Register the function (xp_hello) with an initial upper case
sp_addextendedproc 'xp_Hello', 'c:\xp_hello.dll';

-- Use the newly registered name to call the function
DECLARE @txt VARCHAR(33);
EXEC xp_Hello @txt OUTPUT;

下面是错误消息:

Server: Msg 17750, Level 16, State 1, Procedure xp_Hello, Line 1
Could not load the DLL xp_hello.dll, or one of the DLLs it references. Reason: 127(The specified procedure could not be found.).

如果 中 sp_addextendedproc 指定的函数名称与 DLL 中的函数名完全一致,且 SQL Server 实例的排序不区分大小写,用户可以使用名称中任意大小写字母组合调用扩展存储过程。

-- Register the function (xp_hello)
sp_addextendedproc 'xp_hello', 'c:\xp_hello.dll';

-- The following example succeeds in calling xp_hello
DECLARE @txt VARCHAR(33);
EXEC xp_Hello @txt OUTPUT;

DECLARE @txt VARCHAR(33);
EXEC xp_HelLO @txt OUTPUT;

DECLARE @txt VARCHAR(33);
EXEC xp_HELLO @txt OUTPUT;

当 SQL Server 实例的排序是区分大小写的时,如果过程被调用的是不同的案例,SQL Server 就无法调用该扩展存储过程。 即使它注册的名称和排序与 DLL 中的函数完全相同,这一点依然成立。

-- Register the function (xp_hello)
sp_addextendedproc 'xp_hello', 'c:\xp_hello.dll';

-- The following example results in an error
DECLARE @txt VARCHAR(33);
EXEC xp_HELLO @txt OUTPUT;

下面是错误消息:

Server: Msg 2812, Level 16, State 62, Line 1

你不需要停止再重启SQL Server。

查询安装在SQL Server中的扩展存储过程

经过 SQL Server 认证的用户可以通过运行 sp_helpextendedproc 系统过程,显示当前定义的扩展存储过程及其所属的 DLL 名称。 例如,以下示例返回了所属的 DLL xp_hello

sp_helpextendedproc 'xp_hello';

如果 sp_helpextendedproc 执行时未指定扩展存储过程,则所有扩展存储过程及其 DLL 都会被显示。

从 SQL Server 中移除扩展存储过程

要在用户定义的扩展存储过程 DLL 中丢弃每个扩展存储过程函数,SQL Server 系统管理员必须运行该 sp_dropextendedproc 系统存储过程,指定函数名称及该函数所在的 DLL 名称。 例如,该命令移除了位于名为 xp_hello.dll, SQL Server 的 DLL 中的函数 xp_hello

sp_dropextendedproc 'xp_hello';

sp_dropextendedproc 不会丢弃系统扩展存储过程。 相反,系统管理员应 EXECUTE 拒绝将扩展存储过程授权给 公共 角色。

卸载扩展存储过程 DLL

SQL Server 在调用 DLL 的某个函数时,会加载扩展存储过程 DLL。 DLL 会一直加载,直到服务器关闭或系统管理员使用 DBCC 该语句卸载。 例如,该命令卸载了 xp_hello.dll,允许系统管理员在不关闭服务器的情况下将该文件的更新版本复制到目录:

DBCC xp_hello(FREE);