SQL Server Service Broker 在另一个数据库上执行的 DSQL 操作导致了 Broker 失败。

huangapple go评论83阅读模式
英文:

SQL Server Service Broker DSQL operation on another database causing Broker failure

问题

我已根据Eitan Blumin的优秀示例建立了一个基于Service Broker的配置。

链接:https://eitanblumin.com/2018/10/31/advanced-service-broker-sample-asynchronous-triggers/

我已编写了自己的存储过程。当我在同一数据库内执行操作时,示例可以正常工作。但如果我尝试在当前数据库之外操作,则会出现错误。

这段代码有效:

DELETE FROM CurrentDB.dbo.[gis_pt_im_map_auth_label] 
WHERE plotnumber IN (@ids)

这段代码失败:

DELETE FROM OtherDB.OtherSchema.[gis_pt_im_map_auth_label] 
WHERE plotnumber IN (@ids)

以下是涉及的存储过程。我怀疑这是某种用户权限问题,但我已尝试了一些基于用户的更改,例如将执行用户设置为两个数据库的DBO,但没有结果。

为了澄清,如果我手动执行存储过程,它将正常运行。但是Service Broker会改变执行的方式。

欢迎任何想法或意见。

CREATE PROCEDURE [dbo].[sp_Location_UPDATE_TEST3] 
    @inserted XML,
    @deleted  XML = NULL
AS
    SET NOCOUNT ON;

    BEGIN TRY
    DECLARE @SQL NVARCHAR(max)
    DECLARE @ids NVARCHAR(max)

    IF EXISTS (SELECT NULL FROM @inserted.nodes('inserted/row') AS T(X))
    BEGIN
        INSERT INTO [dbo].[LocationViewLog] ([lo_location], [lo_Location_Code])
            SELECT 
                inserted.[lo_location],
                inserted.lo_location_Code             
            FROM
                (SELECT
                     X.query('.').value('(row/lo_location)[1]', 'int') AS lo_location,
                     X.query('.').value('(row/lo_Location_Code)[1]', 'nvarchar(100)') AS lo_location_Code
                 FROM 
                     @inserted.nodes('inserted/row') AS T(X)) AS inserted 

        SELECT @ids = String_agg(inserted.lo_location, ',')
                    FROM 
                    (SELECT
                         X.query('.').value('(row/lo_location)[1]', 'int') AS lo_location
                    FROM @inserted.nodes('inserted/row') AS T(X)
                     ) AS inserted 

        --以下块是有问题的部分。
        --如果我尝试在另一个数据库中执行此删除操作,Service Broker会因错误而中断。
        --如果我创建相同的命令以在当前数据库中运行,就不会有问题
        SET @SQL = 'DELETE FROM Test.dbo.[gis_pt_im_map_auth_label] WHERE plotnumber IN ('+@ids+')';
        INSERT INTO [dbo].[LocationViewLog]
            (lo_Location_Code) VALUES (@SQL);
        EXEC(@SQL) 
        --结束问题块
        END
    END TRY
    BEGIN CATCH
        --由于我们在异步触发器中,回滚更新操作比常规触发器要复杂得多。
        --目前,对于这种情况,我们将冒有部分数据丢失的风险。

        EXECUTE [dbo].[uspLogError];
    END CATCH;
英文:

I have established a Service Broker configuration based on Eitan Blumin's excellent example.

https://eitanblumin.com/2018/10/31/advanced-service-broker-sample-asynchronous-triggers/

I have crafted my own stored procedure. I can get the example to work when I'm performing activities within the same database. If I try to operate outside the current database, I get errors.

I'm new to Service Brokers and I'm having trouble capturing the error but I suspect this is all part of the same problem.

This works

DELETE FROM CurrentDB.dbo.[gis_pt_im_map_auth_label] 
WHERE plotnumber IN (' + @ids + ')

This fails

DELETE FROM OtherDB.OtherSchema.[gis_pt_im_map_auth_label] 
WHERE plotnumber IN (' + @ids + ')

Below is the stored procedure in question. I suspect it is some kind of user permission but I have tried a couple of user based changes like making the executing user the DBO of both databases and no result.

For clarity if I execute the stored procedure manually, this will run. However Service Brokers changes the nature of the execution.

Any thoughts or inputs appreciated.

CREATE PROCEDURE [dbo].[sp_Location_UPDATE_TEST3] 
	@inserted XML,
	@deleted  XML = NULL
AS
    SET NOCOUNT ON;

    BEGIN TRY
	DECLARE @SQL NVARCHAR(max)
	DECLARE @ids NVARCHAR(max)

    IF EXISTS (SELECT NULL FROM @inserted.nodes('inserted/row') AS T(X))
    BEGIN
        INSERT INTO [dbo].[LocationViewLog] ([lo_location], [lo_Location_Code])
    		SELECT 
                inserted.[lo_location],
                inserted.lo_location_Code			 
		    FROM
                (SELECT
				     --X.query('.').value('(row/PurchaseOrderID)[1]', 'int') AS PurchaseOrderID
					 X.query('.').value('(row/lo_Location)[1]', 'int') AS lo_location,
                     X.query('.').value('(row/lo_Location_Code)[1]', 'nvarchar(100)') AS lo_location_Code
				 FROM 
                     @inserted.nodes('inserted/row') AS T(X)) AS inserted 

		   SELECT @ids = String_agg(inserted.lo_location, ',')
					FROM 
					(SELECT
						 X.query('.').value('(row/lo_Location)[1]', 'int') AS lo_location
					FROM @inserted.nodes('inserted/row') AS T(X)
					 ) AS inserted 

--The following block is what is problematic.
--if I try to execute this delete in another database the Service Broker breaks as a result of an error.
--If I craft this same command to operate in the current database there's no problem
			SET @SQL = 'DELETE FROM Test.dbo.[gis_pt_im_map_auth_label] WHERE  plotnumber IN ('+@ids+')';
					   INSERT INTO [dbo].[LocationViewLog]
				   (lo_Location_Code) VALUES (@SQL);
			EXEC(@SQL) 
--END Problem Block
        END
    END TRY
    BEGIN CATCH
        -- Since we're in an Asynchronous Trigger, rolling back an update operation
        -- is a lot more complicated than in a regular trigger.
        -- For now, for this scenario we'll take the risk of having partial data.

        EXECUTE [dbo].[uspLogError];
    END CATCH;

**EDIT. The answer provided by David Browne was indeed the issue. I found the follwo=ing two links to be exactly what I needed and demonstrated the implementation of Module Signing I needed.
https://sqlundercover.com/2018/05/02/digitally-signing-a-stored-procedure-to-allow-it-to-run-with-elevated-permissions/

https://sqlundercover.com/2018/10/30/signing-stored-procedures-that-access-multiple-databases/

答案1

得分: 3

Service Broker Internal Activation 使用 SQL Server 身份模拟来运行您的激活过程。而 SQL Server 身份模拟仅限于当前数据库。

> 问题的简要解释是,激活执行上下文仅在数据库中受信任,而不在整个服务器中受信任。
> 与整个服务器相关的任何内容,如服务器级别视图、动态管理视图或链接服务器,都会被视为您以 [Public] 身份登录。

> 解决此问题的推荐方法是使用具有所需操作权限的服务器级别证书对存储过程进行签名。

Why does feature … not work under activation?

要对存储过程进行签名,请按照此处的说明操作:
Tutorial: Signing Stored Procedures with a Certificate

英文:

Service Broker Internal Activation runs your activation procedure using SQL Server impersonation. And SQL Server impersonation is limited to the current database.

> A short explanation of the problem is that the activation execution
> context is trusted only in the database, not in the whole server.
> Anything related to the whole server, like a server level view or a
> dynamic management view or a linked server, acts as if you logged in
> as [Public].
>
> The recommended way to solve the issue is to sign the procedure with a
> server level certificate that has the proper rights needed for the
> operation in question

Why does feature … not work under activation?

To sign the stored procedure, follow the instructions here:
Tutorial: Signing Stored Procedures with a Certificate

huangapple
  • 本文由 发表于 2023年5月28日 20:38:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/76351528.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定