Можете ли вы обнаружить сценарий INSERT-EXEC? - PullRequest
0 голосов
/ 07 июля 2011

Возможно ли ОБНАРУЖИТЬ, вызывается ли текущая хранимая процедура с помощью оператора INSERT-EXEC?

Да, я понимаю, что мы можем больше не использовать INSERT-EXEC заявления ... это НЕ вопрос, который я задаю.

ПРИЧИНА, которую я использую INSERT-EXEC, заключается в том, что я надеюсь способствовать повторному использованию хранимых процедур, а не переписывать один и тот же SQL все время.

Вот почему я забочусь:
В сценарии INSERT-EXEC исходное сообщение об ошибке будет потеряно при запросе ROLLBACK. Таким образом, все созданные записи теперь будут осиротевшими.

Пример:

ALTER PROCEDURE [dbo].[spa_DoSomething]
(
    @SomeKey    INT,
    @CreatedBy  NVARCHAR(50)
)
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRY
        BEGIN TRANSACTION

        -- SQL runs and throws an error of some kind.

        COMMIT TRAN
    END TRY
    BEGIN CATCH

        IF @@TRANCOUNT > 0
            ROLLBACK TRAN

        -- If this procedure is called using an INSERT-EXEC
        -- then the original error will be lost at this point because
        -- "Cannot use the ROLLBACK statement within an INSERT-EXEC statement." 
        -- will come-up instead of the original error.

        SET @ErrorMessage = ERROR_MESSAGE();
        SET @ErrorSeverity =    ERROR_SEVERITY();
        SET @ErrorState =       ERROR_STATE();

        RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState)
    END CATCH

    RETURN @@Error
END

Ответы [ 2 ]

1 голос
/ 07 июля 2011

Я придумал немного клуджа (хорошо, это большой клудж), основанный на том факте, что вы не можете иметь вложенные операторы INSERT--EXECUTE .... По сути, если ваша «проблемная» процедура является целью INSERT--EXECUTE, а сама содержит INSERT--EXECUTE, то возникнет ошибка. Чтобы это работало, вам нужно было бы (вполне возможно, бессмысленно) вызвать INSERT--EXECUTE в процедуре и обернуть его в блок TRY--CATCH с соответствующей обработкой. Неуклюжий и тупой, но если ничего не подойдет, стоит попробовать.

Используйте следующее, чтобы проверить это. Это создаст три процедуры:

IF objectproperty(object_id('dbo.Foo1'), 'isProcedure') = 1
    DROP PROCEDURE dbo.Foo1
IF objectproperty(object_id('dbo.Foo2'), 'isProcedure') = 1
    DROP PROCEDURE dbo.Foo2
IF objectproperty(object_id('dbo.Foo3'), 'isProcedure') = 1
    DROP PROCEDURE dbo.Foo3

GO
--  Returns a simple data set
CREATE PROCEDURE Foo1

AS

    SET NOCOUNT on

    SELECT name
     from sys.databases

GO
--  Calls Foo1, loads data into a local temp table, then returns those contents
CREATE PROCEDURE Foo2

AS

    SET NOCOUNT on

    CREATE TABLE #Temp (DBName sysname not null)

    BEGIN TRY
        INSERT #Temp (DBName)
         EXECUTE Foo1
    END TRY

    BEGIN CATCH
        IF ERROR_NUMBER() = 8164
            PRINT 'Nested INSERT EXECUTE'
        ELSE
            PRINT 'Unanticipated err: ' + cast(ERROR_NUMBER() as varchar(10))
    END CATCH


    SELECT *
     from #Temp

GO
--  Calls Foo2, loads data into a local temp table, then returns those contents
CREATE PROCEDURE Foo3

AS

    SET NOCOUNT on

    CREATE TABLE #Temp2 (DBName sysname not null)

    INSERT #Temp2 (DBName)
     EXECUTE Foo2

    SELECT *
     from #Temp2
GO

EXECUTE Foo1 вернет «базовый» набор данных.

EXECUTE Foo2 вызовет Foo1, загрузит данные во временную таблицу и затем вернет содержимое этой таблицы.

EXECUTE Foo3 пытается сделать то же самое, что и Foo2, но вызывает Foo2. Это приводит к вложенной ошибке INSERT--EXECUTE, которая обнаруживается и обрабатывается TRY--CATCH.

в Foo2.
0 голосов
/ 07 июля 2011

Может быть, @@ NESTLEVEL может помочь:

http://msdn.microsoft.com/en-us/library/ms187371.aspx

...