2010-11-05 12 views
15

Estoy tratando de ayudar a un amigo personal (que ahora también es un cliente) con un problema relacionado SQL CLR . Él tiene un servidor SQL con una base de datos que tiene 3 ensamblados .NET embebidos en él. Me pidió que lo ayudara a extraer los ensamblados desde la base de datos y guardarlos como archivos .dll en el disco. ¿Esto es posible?Extrayendo un ensamblado .NET de SQL Server 2005

Respuesta

12

Sí, esto es posible. La representación binaria real de los ensamblados en vivo en el catálogo de SQL para su servidor. A saber, si ejecuta una unión entre sys.assembly_files y sys.assemblies, puede obtener toda la información que necesita. El ensamblado binario se encuentra en la columna de contenido de la vista sys.assembly_files .

Pero con el fin de extraer la representación binaria de SQL Server y en un archivo en el disco tendrá que escribir código .NET que necesita ejecutarse en el misma base de datos, donde los montajes que se refieren a se encuentran ahora. En Visual Studio iniciar un proyecto SQL CLR y añadir una clase a la misma con el siguiente código:

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

luego construir el proyecto e implementarlo en su base de datos. Asegúrese de que la opción de configuración habilitada de CLR esté habilitada en SQL Server. Esto es probablemente ya habilitado, ya que tiene ensamblados en él. En caso de ejecución CLR no es activado, puede ejecutar el siguiente código en SSMS para permitirle:

sp_configure 'clr enabled', 1 
go 

reconfigure 
go 

Una cosa más que usted necesita para tener en cuenta es el servidor SQL predeterminado puede no permitirá escribir en el disco desde el código .NET. Si obtiene un error de seguridad FileIO cuando ejecuta el código anterior llamando al procedimiento almacenado en SSMS, deberá configurar el conjunto de permisos adecuado para el ensamblaje . Puede hacerlo a través de SSMS: haga clic con el botón derecho en el nuevo ensamblaje y consulte el Conjunto de permisos en el cuadro de diálogo Propiedades. Establecerlo en Acceso Externo. Ahora debe ser capaz de exportar sus asambleas ejecutando el código siguiente en SSMS:

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

Espero que esto funcione para usted ...

+0

Este enfoque funciona bien cuando se ejecuta ext también, no es necesario que vaya a la ruta SQLCLR. Ver mi respuesta a continuación – piers7

8

Sí.

hacer un select * from sys.assembly_files para encontrar el id de la Asamblea desea

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

impresionante. Simple y limpio. Gracias. –

3

solución de Preet trabajó para mí, pero tuve que configurar automatización para trabajar en SQL Server 2008 R2. Tenga en cuenta también que SaveToFile no funciona, tampoco da un mensaje de error, a menos que SQL Server tenga permisos para ese directorio. En mi caso, utilicé la carpeta de datos de la instancia de SQL Server que funcionó bien.

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

enfoque Jonas' funciona bien como una aplicación de consola o un script LINQPad también - no hay necesidad de que el código que se ejecuta localmente dentro del proceso de SQL, ya que implica.por ejemplo, la extracción de la Asamblea tSQLt (una herramienta de prueba) a partir de una base de datos:

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

Tomando soluciones Preet y Nate y convertirlos en un script que exportar todos procs clr utilizando un cursor:

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

I han encontrado una solución más simple a este problema, que era necesaria porque sp_OACreate no parece estar disponible para SQL Server 2017 (al menos, no la versión de Linux).

sólo puede utilizar la utilidad BCP para escribir el conjunto a un archivo en el disco, así:

/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 

Y utilizar este archivo de formato (BCP.FMT):

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

El El archivo resultante (/tmp/my_assembly.so) se puede usar en la creación de un ensamblaje, de la siguiente manera:

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