开发者

How to detect interface break between stored procedure

开发者 https://www.devze.com 2023-02-13 03:12 出处:网络
I am working on a large project with a lot of stored procedures. I c开发者_JAVA技巧ame into the following situation where a developer modified the arguments of a stored procedure which was called by a

I am working on a large project with a lot of stored procedures. I c开发者_JAVA技巧ame into the following situation where a developer modified the arguments of a stored procedure which was called by another stored procedure. Unfortunately, nothing prevents the ALTER PROC to complete.

Is there a way to perform those checks afterwards ? What would be the guidelines to avoid getting into that kind of problems ?

Here is a sample code to reproduce this behavior :

CREATE PROC Test1 @arg1 int
AS
BEGIN
PRINT CONVERT(varchar(32), @arg1)
END
GO

CREATE PROC Test2 @arg1 int
AS
BEGIN
DECLARE @arg int;
SET @arg = @arg1+1;
EXEC Test1 @arg;
END
GO

EXEC Test2 1;
GO

ALTER PROC Test1 @arg1 int, @arg2 int AS
BEGIN
PRINT CONVERT(varchar(32), @arg1)
PRINT CONVERT(varchar(32), @arg2)
END
GO

EXEC Test2 1;
GO

DROP PROC Test2
DROP PROC Test1
GO


Sql server 2005 has a system view sys.sql_dependencies that tracks dependencies. Unfortunately, it's not all that reliable (For more info, see this answer). Oracle, however, is much better in that regard. So you could switch. There's also a 3rd party vendor, Redgate, who has Sql Dependency Tracker. Never tested it myself but there is a trial version available.

I have the same problem so I implemented my poor man's solution by creating a stored procedure that can search for strings in all the stored procedures and views in the current database. By searching on the name of the changed stored procedure I can (hopefully) find EXEC calls.

I used this on sql server 2000 and 2008 so it probably also works on 2005. (Note : @word1, @word2, etc must all be present but that can easily be changed in the last SELECT if you have different needs.)

CREATE PROCEDURE [dbo].[findWordsInStoredProceduresViews]
@word1 nvarchar(4000) = null,
@word2 nvarchar(4000) = null,
@word3 nvarchar(4000) = null,
@word4 nvarchar(4000) = null,
@word5 nvarchar(4000) = null
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- create temp table 
    create table #temp
    ( 
        id int identity(1,1),
        Proc_id INT, 
        Proc_Name SYSNAME, 
        Definition NTEXT 
    ) 

    -- get the names of the procedures that meet our criteria 
    INSERT #temp(Proc_id, Proc_Name) 
        SELECT id, OBJECT_NAME(id) 
            FROM syscomments 
            WHERE OBJECTPROPERTY(id, 'IsProcedure') = 1 or
                  OBJECTPROPERTY(id, 'IsView') = 1 
            GROUP BY id, OBJECT_NAME(id) 

    -- initialize the NTEXT column so there is a pointer 
    UPDATE #temp SET Definition = '' 

    -- declare local variables 
    DECLARE  
        @txtPval binary(16),  
        @txtPidx INT, 
        @curText NVARCHAR(4000), 
        @counterId int,
        @maxCounterId int,
        @counterIdInner int,
        @maxCounterIdInner int

    -- set up a double while loop to get the data from syscomments

    select @maxCounterId = max(id)
    from #temp t

    create table #tempInner  
    (
        id int identity(1,1),
        curName SYSNAME, 
        curtext ntext
    ) 


    set @counterId = 0

    WHILE (@counterId < @maxCounterId) 
    BEGIN 
        set @counterId = @counterId + 1

        insert into #tempInner(curName, curtext)
        SELECT OBJECT_NAME(s.id), text 
        FROM syscomments s 
        INNER JOIN #temp t 
        ON s.id = t.Proc_id 
        WHERE t.id = @counterid
        ORDER BY s.id, colid

        select @maxCounterIdInner = max(id)
        from #tempInner t

        set @counterIdInner = 0
        while (@counterIdInner < @maxCounterIdInner)
        begin
            set @counterIdInner = @counterIdInner + 1

            -- get the pointer for the current procedure name / colid 
            SELECT @txtPval = TEXTPTR(Definition) 
                FROM #temp
                WHERE id = @counterId

            -- find out where to append the #temp table's value 
            SELECT @txtPidx = DATALENGTH(Definition)/2 
                FROM #temp 
                WHERE id = @counterId

            select @curText = curtext
            from #tempInner
            where id = @counterIdInner

            -- apply the append of the current 8KB chunk 
            UPDATETEXT #temp.definition @txtPval @txtPidx 0 @curtext 
        end

        truncate table #tempInner
    END 

    -- check our filter 
    SELECT Proc_Name, Definition 
        FROM #temp t
        WHERE (@word1 is null or definition LIKE '%' + @word1 + '%') AND
              (@word2 is null or definition LIKE '%' + @word2 + '%') AND
              (@word3 is null or definition LIKE '%' + @word3 + '%') AND
              (@word4 is null or definition LIKE '%' + @word4 + '%') AND
              (@word5 is null or definition LIKE '%' + @word5 + '%') 
        ORDER BY Proc_Name

    -- clean up 
    DROP TABLE #temp 
    DROP TABLE #tempInner
END


You can use sp_refreshsqlmodule to attempt to re-validate SPs (this also updates dependencies), but it won't validate this particular scenario with parameters at the caller level (it will validate things like invalid columns in tables and views).

http://www.mssqltips.com/tip.asp?tip=1294 has a number of techniques, including sp_depends

Dependency information is stored in the SQL Server metadata, including parameter columns/types for each SP and function, but it isn't obvious how to validate all the calls, but it is possible to locate them and inspect them.

0

精彩评论

暂无评论...
验证码 换一张
取 消