我们在 SQL Server 2000 中使用“未记录”的 xp_fileexist 存储过程多年,并且没有遇到任何问题。在 2005 年,如果执行用户帐户不是系统管理员,他们似乎稍微修改了行为以始终返回 0。如果 SQL Server 服务在 LocalSystem 帐户下运行并且您尝试检查网络上的文件,它似乎也返回零。
我想摆脱 xp_fileexist。有没有人有更好的方法来从存储过程内部检查网络位置是否存在文件?
我们在 SQL Server 2000 中使用“未记录”的 xp_fileexist 存储过程多年,并且没有遇到任何问题。在 2005 年,如果执行用户帐户不是系统管理员,他们似乎稍微修改了行为以始终返回 0。如果 SQL Server 服务在 LocalSystem 帐户下运行并且您尝试检查网络上的文件,它似乎也返回零。
我想摆脱 xp_fileexist。有没有人有更好的方法来从存储过程内部检查网络位置是否存在文件?
您必须将 CLR 标记为 EXTERNAL_ACCESS 才能访问 System.IO 命名空间,但是随着事情的发展,这并不是一个糟糕的方法。
SAFE 是默认权限集,但限制性很强。使用 SAFE 设置,您只能访问本地数据库中的数据以对该数据执行计算逻辑。EXTERNAL_ACCESS 是权限层次结构的下一步。此设置允许您访问外部资源,例如文件系统、Windows 事件查看器和 Web 服务。这种类型的资源访问在 SQL Server 2000 和更早版本中是不可能的。此权限集还限制诸如指针访问等影响程序集稳健性的操作。UNSAFE 权限集假定完全信任程序集,因此没有“代码访问安全”限制。此设置类似于扩展存储过程的运行方式——您假设所有代码都是安全的。然而,此设置确实将不安全程序集的创建限制为具有系统管理员权限的用户。Microsoft 建议您尽可能避免创建不安全的程序集。
也许您正在寻找一个 CLR 存储过程。这些通常在您需要以某种方式与系统交互时使用。
我仍然相信 CLR 程序可能是最好的选择。所以,我接受这个答案。但是,要么我不够聪明,要么实现起来非常困难。我们的 SQL Server 服务在本地帐户下运行,因为根据 Mircosoft 的说法,这是让 iSeries 链接服务器从 64 位 SQL Server 2005 实例工作的唯一方法。当我们将 SQL Server 服务更改为使用域帐户运行时,xp_fileexist 命令对于位于网络上的文件可以正常工作。
我创建了这个 CLR 存储过程并使用设置为 External 的权限级别构建它并对其进行了签名:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Security.Principal;
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void FileExists(SqlString fileName, out SqlInt32 returnValue)
{
WindowsImpersonationContext originalContext = null;
try
{
WindowsIdentity callerIdentity = SqlContext.WindowsIdentity;
originalContext = callerIdentity.Impersonate();
if (System.IO.File.Exists(Convert.ToString(fileName)))
{
returnValue = 1;
}
else
{
returnValue = 0;
}
}
catch (Exception)
{
returnValue = -1;
}
finally
{
if (originalContext != null)
{
originalContext.Undo();
}
}
}
}
然后我运行了这些 TSQL 命令:
USE master
GO
CREATE ASYMMETRIC KEY FileUtilitiesKey FROM EXECUTABLE FILE = 'J:\FileUtilities.dll'
CREATE LOGIN CLRLogin FROM ASYMMETRIC KEY FileUtilitiesKey
GRANT EXTERNAL ACCESS ASSEMBLY TO CLRLogin
ALTER DATABASE database SET TRUSTWORTHY ON;
然后我从 Visual Studio 将 CLR 存储过程部署到我的目标数据库,并使用此 TSQL 从使用 Windows 身份验证登录的 SSMS 执行:
DECLARE @i INT
--EXEC FileExists '\\\\server\\share\\folder\\file.dat', @i OUT
EXEC FileExists 'j:\\file.dat', @i OUT
SELECT @i
Whether I try a local file or a network file, I always get a 0. I may try again later, but for now, I'm going to try to go down a different road. If anyone has some light to shed, it would be much appreciated.