Is there a way to have SQL server validate object references in Stored Procs?

后端 未结 7 2091
半阙折子戏
半阙折子戏 2021-01-13 20:12

The following code executes fine in SQL Server

create proc IamBrokenAndDontKnowIt as
select * from tablewhichdoesnotexist

Of course if I tr

7条回答
  •  臣服心动
    2021-01-13 21:08

    You used to get a warning message when you tried to create a stored procedure like that. It would say:

    Cannot add rows to sysdepends for the current stored procedure because it depends on the missing object 'dbo.nonexistenttable'. The stored procedure will still be created.

    For some reason I'm not getting it now, I'm not sure if it's been changed or if there's just some setting that turns the warning on or off. Regardless, this should give you a hint as to what's happening here.

    SQL Server does track dependencies, but only dependencies which actually exist. Unfortunately, none of the dependency tricks like sp_depends or sp_MSdependencies will work here, because you're looking for missing dependencies.

    Even if we could hypothetically come up with a way to check for these missing dependencies, it would still be trivial to concoct something to defeat the check:

    CREATE PROCEDURE usp_Broken
    AS
    
    DECLARE @sql nvarchar(4000)
    SET @sql = N'SELECT * FROM NonExistentTable'
    EXEC sp_executesql @sql
    

    You could also try parsing for expressions like "FROM xxx", but it's easy to defeat that too:

    CREATE PROCEDURE usp_Broken2
    AS
    
    SELECT *
    FROM
        NonExistentTable
    

    There really isn't any reliable way to examine a stored procedure and check for missing dependencies without actually running it.

    You can use SET FMTONLY ON as Tom H mentions, but be aware that this changes the way that the procedure is "run". It won't catch some things. For example, there's nothing stopping you from writing a procedure like this:

    CREATE PROCEDURE usp_Broken3
    AS
    
    DECLARE @TableName sysname
    
    SELECT @TableName = Name
    FROM SomeTable
    WHERE ID = 1
    
    DECLARE @sql nvarchar(4000)
    SET @sql = N'SELECT * FROM ' + @TableName
    EXEC sp_executesql @sql
    

    Let's assume you have a real table named SomeTable and a real row with ID = 1, but with a Name that doesn't refer to any table. You won't get any errors from this if you wrap it inside a SET FMTONLY ON/OFF block.

    That may be a contrived problem, but FMTONLY ON does other weird things like executing every branch of an IF/THEN/ELSE block, which can cause other unexpected errors, so you have to be very specific with your error-handling.

    The only truly reliable way to test a procedure is to actually run it, like so:

    BEGIN TRAN
    
    BEGIN TRY
        EXEC usp_Broken
    END TRY
    BEGIN CATCH
        PRINT 'Error'
    END CATCH
    
    ROLLBACK
    

    This script will run the procedure in a transaction, take some action on error (in the CATCH), and immediately roll back the transaction. Of course, even this may have some side-effects, like changing the IDENTITY seed if it inserts into a table (successfully). Just something to be aware of.

    To be honest, I wouldn't touch this problem with a 50-foot pole.

提交回复
热议问题