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 中使用函数调用?