SQL Server provides a very powerful feature called CLR Integration that gives database developers the ability to let SQL Server execute .NET code from T-SQL procedures or functions. To begin with, this feature must be enabled on the database:
sp_configure'clr enabled', 1;
reconfigurewithoverride;
SQL Server CLR Functions or Procedures are created with reference to methods in a .NET assembly added to SQL Server. That .NET assembly may execute unmanaged code as well (see security note about permission_set below). .NET results can be obtained from out parameters or function return values, and this provides for an infinite toolset extension as one can do anything* in SQL code by calling .NET. I had to use it to draw QR codes on a Crystal Report as I explain in this post.
The first step is to create the .NET assembly containing the method that you want to run. Using SQL Server 2008, only .NET 2.0 assemblies are supported. Fortunately, I was able to limit my solution to use .NET 2.0 but I had to switch out one 3rd party library. This is a considerable limitation, and I hope in future SQL Server versions, assemblies of higher .NET versions would be supported.
The .NET method that you want to import must be a static one because T-SQL does not have the notion of instantiating a class and calling an object's method. The inputs and outputs must be compatible with SQL Server and the T-SQL declaration of the procedure must match that of the .NET method as illustrated in the next step. The method should be annotated with [Microsoft.SqlServer.Server.SqlProcedure] or [SqlFunction] in the same namespace.
Both attributes exist in the System.Data.dll Framework DLL so I didn't need to add another reference to my application/library.
namespace Example
{
public class SqlFunctionality
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void ClrProc(out SqlInt32 i)
{
i = 123;
Microsoft.SqlServer.Server.SqlContext.Pipe.ExecuteAndSend(new SqlCommand("print 'running sql proc!'"));
}
[Microsoft.SqlServer.Server.SqlFunction, Microsoft.SqlServer.Server.SqlMethod]
public static int ClrFunc()
{
//You cannot use print in SQL Functions...
return 57481;
}
}
}
Let's say the output assembly is Example.dll.
The second step is then to add the .NET assembly to SQL Server. You set the applicable permission_set in this stage. If your functionality does not need external resources, does not use unmanaged calls, then you can use the SAFE permission_set, which is default (you can just omit the permissions clause). If your code needs to access resources outside SQL Server, then you need the External_Access permission. If you use unmanaged code or use .NET framework libraries that haven't been tested with the SQL Server CLR feature, then you still can add them but with the unsafe permission_set.
CREATEASSEMBLYExampleAssemblyFROM'C:\path\on\sqlserver\machine\example.dll';
SQL Server verifies that the assembly can be added and used with the specified permission_set. To see this in action, try to add the System.Drawing.dll .NET framework DLL.
CREATEASSEMBLY[System.Drawing]FROM'c:\windows\Microsoft.NET\Framework\v2.0.50727\System.Drawing.dll';
NOTE: You don't normally add System.Drawing.DLL to use directly from T-SQL, but rather because another library you are using references this Framework library.
SQL Server tries to verify that the assembly is safe, but because we use the default SAFE permission_set which isn't applicable to the System.Drawing.dll assembly, the command causes a warning message to be issues then fails with the subsequent message:
Warning: The Microsoft .Net frameworks assembly 'system.drawing, version=2.0.0.0, culture=neutral, publickeytoken=b03f5f7f11d50a3a, processorarchitecture=msil.' you are registering is not fully tested in SQL Server hosted environment.
CREATE ASSEMBLY for assembly 'System.Drawing' failed because assembly 'System.Drawing' failed verification. Check if the referenced assemblies are up-to-date and trusted (for external_access or unsafe) to execute in the database. CLR Verifier error messages if any will follow this message [...omitted...].
To overcome this, you add the System.Drawing.dll with UNSAFE permission_set. But in order to do this, SQL Server requires that the database is trustworthy which is a property that need to be turned on the database.
ALTERDATABASEMyDatabaseSETTRUSTWORTHYON
The user account must have the privilege to ALTER the database: see this TechNet article for details. If this step is not executed, the next command even with the UNSAFE permission_set will fail. SQL Server issues an error message that explains why.
createassembly[System.Drawing]from'C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Drawing.dll'withpermission_set=unsafe
Msg 10327, Level 14, State 1, Line 1
CREATE ASSEMBLY for assembly 'System.Drawing' failed because assembly 'System.Drawing' is not authorized for PERMISSION_SET = UNSAFE. The assembly is authorized when either of the following is true: the database owner (DBO) has UNSAFE ASSEMBLY permission and the database has the TRUSTWORTHY database property on; or the assembly is signed with a certificate or an asymmetric key that has a corresponding login with UNSAFE ASSEMBLY permission. If you have restored or attached this database, make sure the database owner is mapped to the correct login on this server. If not, use sp_changedbowner to fix the problem.
Turning on the TRUSTWORTHY property causes the command to succeed (the warning that System.Drawing is not fully tested with SQL Server is still issued).
After the assembly is created, a CLR stored proc or function is created with an external name as follows:
CREATEPROCEDUREClrProc(@iintoutput)ASEXTERNALNAMEExampleApp.[Example.SqlFunctionality].ClrProc;
CREATEFUNCTIONClrFunc()RETURNSintASEXTERNALNAMEExampleApp.[Example.SqlFunctionality].ClrFunc;
Note how the external name is fully qualified. The C# static class and the containing namespace are [grouped with square brackets] because they are a single name from SQL Server's perspective (they do not reflect different SQL Server objects).
The procedure and function can be invoked just like any other.
execClrProc1;--prints "running sql proc!" to SQL Server output
selectClrFunc();--returns 57481
Quiet an adventure. Good to know about this feature.