2010-11-05 41 views
15

Tôi đang cố gắng giúp đỡ một người bạn cá nhân (hiện giờ cũng là khách hàng) với một vấn đề liên quan đến SQL CLR . Ông có một máy chủ SQL với một cơ sở dữ liệu có 3 hội đồng NET được nhúng trong đó. Anh ấy yêu cầu tôi giúp anh ta trích xuất các hội đồng từ bên trong cơ sở dữ liệu và lưu chúng dưới dạng tệp .dll trên đĩa. Điều này thậm chí có thể?Trích xuất một Hội đồng .NET từ SQL Server 2005

Trả lời

12

Có, điều này là có thể. Biểu diễn nhị phân thực tế của các assembly trực tiếp trong danh mục SQL cho máy chủ của bạn. Cụ thể là, nếu bạn điều hành một sự tham gia giữa sys.assembly_files và sys.assemblies, bạn có thể truy cập tất cả thông tin mà bạn cần . Tập hợp nhị phân nằm trong cột nội dung của khung nhìn sys.assembly_files .

Nhưng để trích xuất biểu diễn nhị phân từ SQL Server và thành tệp trên đĩa, bạn sẽ phải viết một số mã .NET cần chạy trên cơ sở dữ liệu tương tự tại nơi bạn đang tham chiếu. Trong Visual Studio bắt đầu một dự án CLR SQL và thêm một lớp học để nó với đoạn mã sau:

using System; 
using System.IO; 
using System.Data; 
using System.Data.SqlClient; 
using System.Data.SqlTypes; 
using Microsoft.SqlServer.Server; 
using System.Security.Permissions; 

namespace ExtractSqlAssembly { 
    [PermissionSet(SecurityAction.Demand, Unrestricted = true, Name = "FullTrust")] 
    public partial class SaveSqlAssembly { 

     [SqlProcedure] 
     public static void SaveAssembly(string assemblyName, string path) { 
      string sql = @"SELECT AF.content FROM sys.assembly_files AF JOIN sys.assemblies A ON AF.assembly_id = A.assembly_id where AF.file_id = 1 AND A.name = @assemblyname"; 
      using (SqlConnection conn = new SqlConnection("context connection=true")) { 
       using (SqlCommand cmd = new SqlCommand(sql, conn)) { 
        SqlParameter param = new SqlParameter("@assemblyname", SqlDbType.VarChar); 
        param.Value = assemblyName; 
        cmd.Parameters.Add(param); 

        cmd.Connection.Open(); // Read in the assembly byte stream 
        SqlDataReader reader = cmd.ExecuteReader(); 
        reader.Read(); 
        SqlBytes bytes = reader.GetSqlBytes(0); 

        // write the byte stream out to disk 
        FileStream bytestream = new FileStream(path, FileMode.CreateNew); 
        bytestream.Write(bytes.Value, 0, (int)bytes.Length); 
        bytestream.Close(); 
       } 
      } 
     } 
    } 
} 

Sau đó xây dựng dự án và triển khai nó đến cơ sở dữ liệu của bạn. Đảm bảo rằng tùy chọn cấu hình CLR Bật được bật trên Máy chủ SQL. Đây có lẽ là đã được bật, vì bạn có các hội đồng trên đó. Trong trường hợp thực hiện clr không kích hoạt bạn có thể chạy đoạn mã sau vào SSMS để kích hoạt nó:

sp_configure 'clr enabled', 1 
go 

reconfigure 
go 

Một điều nữa mà bạn cần phải nhận thức được là bởi SQL mặc định máy chủ có thể không cho phép bạn viết vào đĩa từ mã .NET. Nếu bạn gặp lỗi bảo mật FileIO khi bạn chạy mã ở trên bằng cách gọi thủ tục được lưu trữ trong SSMS, bạn sẽ cần phải định cấu hình bộ quyền thích hợp cho cụm . Bạn có thể thực hiện điều này thông qua SSMS: nhấp chuột phải vào hội đồng mới và xem Tập hợp quyền trong hộp thoại Thuộc tính. Đặt nó vào Truy cập bên ngoài. Bây giờ bạn sẽ có thể xuất lắp ráp của bạn bằng cách chạy đoạn mã sau trong SSMS:

exec SaveAssembly 'AssemblyName', 'f:\path\to\assemblyname.dll' 

Hope làm việc này cho bạn ...

+0

Cách tiếp cận này hoạt động tốt khi thực hiện ext ernally quá - bạn không cần phải đi theo con đường SQLCLR. Xem câu trả lời của tôi bên dưới – piers7

8

Yes.

làm một select * from sys.assembly_files để tìm id của assembly mà bạn muốn

DECLARE @IMG_PATH VARBINARY(MAX) 
DECLARE @ObjectToken INT 

SELECT @IMG_PATH = content FROM sys.assembly_files WHERE assembly_id = 65536 

EXEC sp_OACreate 'ADODB.Stream', @ObjectToken OUTPUT 
     EXEC sp_OASetProperty @ObjectToken, 'Type', 1 
     EXEC sp_OAMethod @ObjectToken, 'Open' 
     EXEC sp_OAMethod @ObjectToken, 'Write', NULL, @IMG_PATH 
     EXEC sp_OAMethod @ObjectToken, 'SaveToFile', NULL, 'c:\temp\myassembly.dll', 2 
     EXEC sp_OAMethod @ObjectToken, 'Close' 
     EXEC sp_OADestroy @ObjectToken 
+0

tuyệt vời. Đơn giản và sạch sẽ. Cảm ơn bạn. –

3

giải pháp Preet của làm việc cho tôi, nhưng tôi đã phải cấu hình Ole Tự động hóa để làm việc trên SQL Server 2008 R2. Cũng lưu ý rằng SaveToFile không hoạt động - nó cũng không cung cấp thông báo lỗi - trừ khi SQL Server có quyền đối với thư mục đó. Trong trường hợp của tôi, tôi đã sử dụng thư mục dữ liệu của cá thể SQL Server hoạt động tốt.

EXECUTE SP_CONFIGURE 'show advanced options', 1 
RECONFIGURE WITH OVERRIDE 
GO 

EXEC sp_configure 'Ole Automation Procedures', 1; 
RECONFIGURE WITH OVERRIDE 
GO 

DECLARE @IMG_PATH VARBINARY(MAX) 
DECLARE @ObjectToken INT 

SELECT @IMG_PATH = content FROM sys.assembly_files WHERE assembly_id = 65546 

EXEC sp_OACreate 'ADODB.Stream', @ObjectToken OUTPUT 
     EXEC sp_OASetProperty @ObjectToken, 'Type', 1 
     EXEC sp_OAMethod @ObjectToken, 'Open' 
     EXEC sp_OAMethod @ObjectToken, 'Write', NULL, @IMG_PATH 
     EXEC sp_OAMethod @ObjectToken, 'SaveToFile', NULL, 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\myassembly.dll', 2 
     EXEC sp_OAMethod @ObjectToken, 'Close' 
     EXEC sp_OADestroy @ObjectToken 

EXEC sp_configure 'Ole Automation Procedures', 0; 
RECONFIGURE WITH OVERRIDE 
GO 

EXECUTE SP_CONFIGURE 'show advanced options', 0 
RECONFIGURE WITH OVERRIDE 
GO 
6

Cách tiếp cận Jonas cũng hoạt động tốt như ứng dụng Console hoặc tập lệnh Linqpad - không cần mã được thực thi cục bộ trong quá trình SQL, như ông ngụ ý.ví dụ như chiết xuất lắp ráp tSQLt (một công cụ kiểm tra) từ một cơ sở dữ liệu:

void Main() 
{ 
    var assemblyName = "tSQLtCLR"; 
    var serverName = "localhost"; 
    var databaseName = "MyDb"; 
    var targetDir = Environment.ExpandEnvironmentVariables("%TEMP%"); 
    var targetFile = Path.Combine(targetDir, assemblyName) + ".dll"; 

    var sql = @"SELECT AF.content FROM sys.assembly_files AF JOIN sys.assemblies A ON AF.assembly_id = A.assembly_id where AF.file_id = 1 AND A.name = @assemblyName"; 

    var connectionString = string.Format("Data Source={0};Initial Catalog={1};Integrated Security=true", serverName, databaseName); 
    using(var connection = new System.Data.SqlClient.SqlConnection(connectionString)){ 
     connection.Open(); 

     var command = connection.CreateCommand(); 
     command.CommandText = sql; 
     command.Parameters.Add("@assemblyName", assemblyName); 

     using(var reader = command.ExecuteReader()){ 
      if(reader.Read()){ 
       var bytes = reader.GetSqlBytes(0); 

       File.WriteAllBytes(targetFile, bytes.Value); 
       Console.WriteLine(targetFile); 
      }else{ 
       throw new Exception("No rows returned"); 
      } 
     } 
    } 
} 
0

Lấy Preet và Nate giải pháp và biến chúng thành một kịch bản mà sẽ xuất khẩu TẤT CẢ procs clr sử dụng một con trỏ:

EXECUTE SP_CONFIGURE 'show advanced options', 1 
RECONFIGURE WITH OVERRIDE 
GO 

EXEC sp_configure 'Ole Automation Procedures', 1; 
RECONFIGURE WITH OVERRIDE 
GO 

RAISERROR ('Starting...', 0, 1) WITH NOWAIT 

DECLARE @ObjectToken INT 
DECLARE @AssemblyLocation VARCHAR(MAX) 
DECLARE @Msg VARCHAR(MAX) 
DECLARE @Content VARBINARY(MAX) 
DECLARE @Count AS INT = (SELECT COUNT(name) FROM sys.assembly_files) 

DECLARE AssemblyFiles CURSOR FAST_FORWARD 
FOR 
    SELECT 
    CAST(ROW_NUMBER() OVER (ORDER BY name) AS VARCHAR(10)) + ' of ' + CAST(@Count AS VARCHAR(10)) + ' - ' + name AS Msg, 
    '[a location the server can write to]' + name + '.dll' AS AssemblyLocation, 
    content  
    FROM 
    sys.assembly_files 
    ORDER BY 
    name 

OPEN AssemblyFiles 
FETCH NEXT FROM AssemblyFiles 
INTO @Msg, @AssemblyLocation, @Content 

WHILE @@FETCH_STATUS = 0 
    BEGIN 
    EXEC sp_OACreate 'ADODB.Stream', @ObjectToken OUTPUT 
    EXEC sp_OASetProperty @ObjectToken, 'Type', 1 
    EXEC sp_OAMethod @ObjectToken, 'Open' 
    EXEC sp_OAMethod @ObjectToken, 'Write', NULL, @Content 
    EXEC sp_OAMethod @ObjectToken, 'SaveToFile', NULL, @AssemblyLocation, 2 
    EXEC sp_OAMethod @ObjectToken, 'Close' 
    EXEC sp_OADestroy @ObjectToken 

    RAISERROR (@Msg, 0, 1) WITH NOWAIT 

    FETCH NEXT FROM AssemblyFiles 
    INTO @Msg, @AssemblyLocation, @Content 
    END 

CLOSE AssemblyFiles 
DEALLOCATE AssemblyFiles 

RAISERROR ('Done', 0, 1) WITH NOWAIT 

EXEC sp_configure 'Ole Automation Procedures', 0; 
RECONFIGURE WITH OVERRIDE 
GO 

EXECUTE SP_CONFIGURE 'show advanced options', 0 
RECONFIGURE WITH OVERRIDE 
GO 
0

tôi đã tìm thấy một giải pháp đơn giản cho vấn đề này, điều này là cần thiết vì sp_OACreate dường như không có sẵn cho SQL Server 2017 (ít nhất, không phải là phiên bản Linux).

Bạn có thể chỉ cần sử dụng các tiện ích BCP để viết lắp ráp vào một tập tin trên đĩa, như vậy:

/opt/mssql-tools/bin/bcp "SELECT content FROM sys.assembly_files WHERE name = '${ASSEMBLY_NAME}'" \ 
    queryout /tmp/my_assembly.so -f bcp.fmt \ 
    -S localhost -U sa -P "${SA_PASSWORD}" -d master 

Và sử dụng tập tin định dạng này (bcp.fmt):

13.0 
1 
1  SQLBINARY   0  0  "" 1  content       "" 

Các tập tin kết quả (/tmp/my_assembly.so) có thể được sử dụng trong việc tạo ra một hội đồng, như vậy:

CREATE ASSEMBLY [MyAssembly] AUTHORIZATION [dbo] 
FROM '/tmp/my_assembly.so' WITH PERMISSION_SET = SAFE; 
Các vấn đề liên quan