Как лучше всего проверить, существует ли файл из хранимой процедуры SQL Server 2005?

Мы годами использовали «недокументированную» хранимую процедуру xp_fileexist в SQL Server 2000 и не испытывали с ней никаких проблем. В 2005 году кажется, что они немного изменили поведение, чтобы всегда возвращать 0, если выполняющая учетная запись пользователя не является системным администратором. Также кажется, что он возвращает ноль, если служба SQL Server работает под учетной записью LocalSystem, а вы пытаетесь проверить файл в сети.

Я бы хотел уйти от xp_fileexist. Есть ли у кого-нибудь лучший способ проверить наличие файла в сетевом расположении изнутри хранимой процедуры?

Ответов (3)

Решение

Возможно, вам нужна хранимая процедура CLR. Обычно они используются, когда вам нужно каким-то образом взаимодействовать с системой.

Вам нужно будет пометить CLR как EXTERNAL_ACCESS, чтобы получить доступ к пространству имен System.IO, однако, по мере развития событий, это неплохой способ сделать это.

БЕЗОПАСНЫЙ - это набор разрешений по умолчанию, но он очень ограничен. С настройкой SAFE вы можете получить доступ только к данным из локальной базы данных для выполнения вычислительной логики над этими данными. EXTERNAL_ACCESS - следующий шаг в иерархии разрешений. Этот параметр позволяет получить доступ к внешним ресурсам, таким как файловая система, средство просмотра событий Windows и веб-службы. Этот тип доступа к ресурсам невозможен в SQL Server 2000 и более ранних версиях. Этот набор разрешений также ограничивает такие операции, как доступ к указателям, которые влияют на надежность сборки. Набор разрешений UNSAFE предполагает полное доверие к сборке и, таким образом, не накладывает никаких ограничений «Безопасность доступа для кода». Этот параметр сравним со способом работы расширенных хранимых процедур - вы предполагаете, что весь код безопасен. Тем не мение, этот параметр ограничивает создание небезопасных сборок пользователями, имеющими разрешения системного администратора. Microsoft рекомендует по возможности избегать создания небезопасных сборок.

Я все еще считаю, что процедура 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;

Затем я развернул хранимую процедуру CLR в моей целевой базе данных из Visual Studio и использовал этот TSQL для выполнения из SSMS, вошедшего в систему с проверкой подлинности Windows:

DECLARE @i INT
--EXEC FileExists '\\\\server\\share\\folder\\file.dat', @i OUT
EXEC FileExists 'j:\\file.dat', @i OUT
SELECT @i

Независимо от того, пытаюсь ли я использовать локальный или сетевой файл, я всегда получаю 0. Я могу повторить попытку позже, но сейчас я попытаюсь пойти другим путем. Если у кого-то есть свет, который нужно пролить, это было бы очень полезно.