SQL Server 2005:“保护”存储过程免受 MS Access 使用的 FMTONLY 模式的影响

Posted

技术标签:

【中文标题】SQL Server 2005:“保护”存储过程免受 MS Access 使用的 FMTONLY 模式的影响【英文标题】:SQL Server 2005: "Protecting" stored procedures from FMTONLY mode used by MS Access 【发布时间】:2008-11-19 19:56:34 【问题描述】:

我们的一些存储过程包含条件逻辑,像这样:

Create Procedure dbo.DoSomething(Some Parameters)
As
    ...
    If (Some Condition) Begin
        Set @SomeVariable = SomeValue                
    End
    ...
    Select ...

当这样的存储过程用作 MS Access 表单的记录源,并且用户尝试使用表单的内置排序/过滤功能时,MS Access 会尝试在 FMTONLY 模式下执行存储过程(显然,狩猎用于存储过程提供的行集的元数据)。

正如大多数人所知(现在包括我们自己 :-),当 FMTONLY 设置为 ON 时,SQL Server 会忽略条件语句。在下例中,不管Some Condition是否为真,都会执行Set @SomeVariable = SomeValue语句,这显然给我们带来了一些麻烦。

-- EXAMPLE
-- -------
Create Procedure dbo.DoSomething(..., @vcSomeDate as VarChar(50), ...)
As
   ...
   Declare @dtSomeDate As Datetime
   If (IsDate(@vcSomeDateOrAgeInDays)) Begin
       -- The next statement fails miserably when FMTONLY=ON
       Set @dtSomeDate = @vcSomeDateOrAgeInDays
   End Else Begin
       ...
   End
   ...

为了规避这个问题,我们像这样“包装”条件逻辑(或任何其他受 FMTONLY 影响的代码片段):

Create Procedure dbo.DoSomething(Some Parameters)
As
    ...

    -- HACK: Protection from unexpected FMTONLY mode
    Declare @wasFmtonlyOn As Bit; If (0 = 1) Set @wasFmtonlyOn = 1; SET FMTONLY OFF
    ...
    If (Some Condition) Begin
        Set @SomeVariable = SomeValue                
    End
    ...
    -- /HACK: Protection from unexpected FMTONLY mode
    If (@wasFmtonlyOn = 1) SET FMTONLY ON

    ...
    Select ...

(“保护代码”的这种丑陋的单行格式是故意的:我们认为解决一些奇怪问题所需的 hack 不值得正确格式化;恰恰相反,我们认为它们应该适合几行代码尽可能。:-)

无论如何,这种“保护”工作正常,但它有点过于冗长,并且没有我们希望的那样封装。例如,我们肯定更喜欢隐藏 hack 的实际逻辑 - 例如在这样的标量 UDF 后面:

Create Procedure dbo.DoSomething(Some Parameters)
As
    ...

    declare @wasFmtonlyOn as bit; set @wasFmtonlyOn = dbo.SetFmtonly(0)
    ...
    If (Some Condition) Begin
        Set @SomeVariable = SomeValue                
    End
    ...
    dbo.SetFmtonly(@wasFmtonlyOn)

    ...
    Select ...

不幸的是,这似乎不起作用 - 既不适用于标量 UDF,也不适用于另一个存储过程。看起来 FMTONLY 阻止从任何地方返回任何数据。所以,主要问题来了:

如果你也必须处理这个问题(SQL Server 在 FMTONLY 模式下忽略条件),你能想出比上面描述的更好的“保护习语”吗? p>

顺便说一句,我仍然不明白一件事:这个问题是 SQL Server 2005 中的错误还是功能?如果它是一个特性,那么它有什么好的理由呢?

谢谢!

【问题讨论】:

@Yarik:你能用问题的形式来表达这个吗? SQL Server 文档明确指出 SET FMTONLY ON 执行所有非条件语句,所以我不确定你的问题是什么? 【参考方案1】:

这个怎么样?

If (Some Condition) Begin
    Set @SomeVariable = SomeValue
ELSE
    Set @SomeVariable = @SomeVariable --or dummy/default value?
End

您的代码是否基于此变量返回 2 个不同的记录集(列和类型)? 如果是这样,您必须将存储的过程拆分为 2

另外,我找到了一个 KB article 来解释原因。

编辑: 把分支改成内联代码...

Set @dtSomeDate = CASE WHEN ISDATE(@vcSomeDateOrAgeInDays) = 1 THEN @vcSomeDateOrAgeInDays ELSE NULL END

【讨论】:

感谢文章参考!虽然我没有看到它解释了为什么在 FMTONLY 模式下会忽略条件语句和分支语句,但它展示了一个很好的例子,说明了如何使用 FMTONLY 模式来破坏安全性。 至于你提供的代码……我想我没有明白我的意思:问题是FMTONLY=ON模式可能会导致执行不应该执行的语句;这可能会导致异常。我将继续在我的问题中插入一个这种情况的具体示例...... 设置@dtSomeDate = CASE WHEN ISDATE(@vcSomeDateOrAgeInDays) = 1 THEN @vcSomeDateOrAgeInDays ELSE NULL END ?

以上是关于SQL Server 2005:“保护”存储过程免受 MS Access 使用的 FMTONLY 模式的影响的主要内容,如果未能解决你的问题,请参考以下文章

使用存储过程将记录从 SQL Server 复制到 SQL Server (2005)

存储过程系列之调试存储过程 SQL Server 2005

在存储过程 sql server 2005 中使用函数调用?

SQL Server 2005 视图与物化视图与存储过程

MS SQL Server 2005 - 存储过程“自发中断”

SQL Server 2005 事务复制无法发布包含索引创建的存储过程