2010-11-05 22 views
15

Bir SQL CLR ilgili sorun ile kişisel bir arkadaşa (aynı zamanda bir istemci de) yardımcı olmaya çalışıyorum. İçinde 3 .NET derleme yerleşik bir veritabanı ile SQL Server vardır. veritabanından derlemeleri çıkarmasına yardım etmemi ve diskte .dll dosyaları olarak kaydetmememi istedi. Bu mümkün mü?Bir SQL derlemesi SQL Server 2005'ten ayıklanıyor

cevap

12

Evet, bu mümkün. Derlemelerin gerçek ikili gösterimi, sunucunuz için SQL kataloğunda yaşar. Yani, sys.assembly_files ve sys.assemblies arasında bir birleşimi çalıştırırsanız, gereksiniminiz olan tüm bilgilere ulaşabilirsiniz. Derlemeler ikili, sys.assembly_files görünümünün içerik sütununda bulunur.

Fakat SQL Server ve montajlar şimdi bulunduğu ifade aynı veritabanı üzerinde çalıştırmak için gereken bazı .NET kodu yazmak zorunda kalacak diskte dosyasına ikili gösterimini elde etmek için

. Görsel Studio'da SQL CLR proje başlatmak ve aşağıdaki kodla kendisine bir sınıf ekleyin:

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(); 
       } 
      } 
     } 
    } 
} 

Ardından projeyi oluşturmak ve veritabanına dağıtın. SQL Server'da CLR Etkin yapılandırma seçeneğinin etkinleştirildiğinden emin olun. Bu, muhtemelen meclisleriniz olduğundan, zaten etkin.

sp_configure 'clr enabled', 1 
go 

reconfigure 
go 

bilmeniz gereken bir şey daha var varsayılan SQL sunucusu tarafından ise yazmayı izin vermeyebilir: durumunda clr yürütme değil Bunu etkinleştirmek için SSMS üzerinde aşağıdaki kodu çalıştırabilir etkin olduğunu .NET kodundan diske. Yukarıdaki kodu, SSMS'de saklı yordamı çağırarak çalıştırdığınızda bir FileIO güvenlik hatası alırsanız, derlemesi için uygun izin kümesini yapılandırmanız gerekir. Bunu SSMS ile yapabilirsiniz: Yeni düzeneği sağ tıklayın ve Özellikler iletişim kutusundaki İzin Kümesi'ne 'a bakın. Harici Erişim olarak ayarlayın. Şimdi SSMS aşağıdaki kodu çalıştırarak meclisleri dışa gerekir: Bu sizin için çalışan

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

Umut ...

+0

Bu yaklaşım, ext yürütüldüğünde düzgün çalışır son derece - SQLCLR yoluna gitmeniz gerekmez. Benim cevabımın – piers7

8

Evet.

Eğer

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

müthiş altında görün. Basit ve temiz. Teşekkür ederim. –

3

preet çözümü benim için çalıştı istediğiniz toplanma id bulmak için bir select * from sys.assembly_files, ama ben SQL Server 2008 R2 üzerinde çalışmak için Ole Otomasyon yapılandırmak zorunda kaldı. Ayrıca, SQL Server'ın bu dizine izinleri olmadıkça, SaveToFile çalışmaz - bir hata iletisi de verir. Benim durumumda, iyi çalışan SQL Server örneğinin veri klasörünü kullandım.

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

Jonas'ın yaklaşım aynı zamanda bir konsol uygulaması veya LINQPad komut dosyası gibi çalışıyor - o da anlaşılacağı gibi kod SQL süreci içinde yerel olarak yürütülecek gerek yoktur.örneğin bir veritabanından tSQLt takımını (bir test aracı) ayıklanması:

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

Preet ve Nate çözümlerini alarak ve bir gösterici kullanarak TÜM clr procs ihraç edecek bir komut dosyası dönüştürerek:

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

ben Bu soruna daha basit bir çözüm bulmuştur, çünkü gerekli olan sp_OACreate'un SQL Server 2017 için mevcut olmadığı (en azından Linux sürümü).

/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 

Ve bu biçim dosyası (BCP.FMT) kullanın:

Sadece çok gibi diskteki bir dosyaya takımını yazmak için BCP yardımcı programını kullanabilirsiniz

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

sonuçtaki dosya (/tmp/my_assembly.so), aşağıdaki gibi bir derlemenin oluşturulmasında kullanılabilir:

CREATE ASSEMBLY [MyAssembly] AUTHORIZATION [dbo] 
FROM '/tmp/my_assembly.so' WITH PERMISSION_SET = SAFE;