Search code examples
c#.netsql-serversqlclr

SQLCLR assembly registration failed (Type load failed)


I'm trying to register SQLCLR assembly in SqlServer

CREATE ASSEMBLY [DatabaseCLR]
FROM 'T:\DatabaseCLR.dll'
WITH PERMISSION_SET = SAFE
GO

but during registration I get error message

Msg 6218, Level 16, State 2, Line 1 CREATE ASSEMBLY for assembly 'DatabaseCLR' failed because assembly 'DatabaseCLR' 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
[ : DatabaseCLR.BinaryUtils::HasSetBits][mdToken=0x6000039] Type load failed.
[token 0x02000008] Type load failed.

which is similar to one described in this question. However the situation is a bit different. In my assembly I do not use User-Defined Types.

If I usePERMISSION_SET = UNSAFE the assembly registers successfully. It doesn't seem that I use unsafe code though ("Allow unsafe code" checkbox is not checked in project properties) (or do I?).

The assembly code (simplified) is:

using System;
using System.Data.SqlTypes;
using System.Runtime.InteropServices;
using Microsoft.SqlServer.Server;

namespace DatabaseCLR
{
    public class BinaryUtils
    {
        [SqlFunction(Name = "BinaryHasSetBits", IsDeterministic = true, IsPrecise = true)]
        public static SqlBoolean HasSetBits(SqlBytes data)
        {
            if (data.IsNull)
                return SqlBoolean.Null;

            if (data.Storage != StorageState.Buffer)
                throw new NotSupportedException(string.Format("Storage type {0} is not supported.", data.Storage));

            long
                len = data.Length,
                ulen = len / sizeof(ulong),
                tail = len % sizeof(ulong);

            ByteToUlongConverter conv = new ByteToUlongConverter(data.Buffer);
            for (long i = 0; i < ulen; i++)
                if (conv.ulongs[i] != 0)
                    return SqlBoolean.True;

            for (long i = len - tail; i < len; i++)
                if (data.Buffer[i] != 0)
                    return SqlBoolean.True;

            return SqlBoolean.False;
        }
    }

    [StructLayout(LayoutKind.Explicit)]
    internal struct ByteToUlongConverter
    {
        [FieldOffset(0)]
        public byte[] bytes;

        [FieldOffset(0)]
        public ulong[] ulongs;

        public ByteToUlongConverter(byte[] bytes)
        {
            this.ulongs = null;
            this.bytes = bytes;
        }
    }
}

The assembly provides functions for bitwise operations on binary types. I'm using struct with [StructLayout(LayoutKind.Explicit)] attribute for casting byte[] array to ulong[] array (to speed up processing). I guess that use of StructLayout causes error as in the related question. However it is not on UDT, and I do not see how I can fix it in this case.

Are there any chances to register assembly with PERMISSION_SET = SAFE ?


I do register my sample function as

CREATE FUNCTION dbo.BinaryHasSetBits
(
    @data varbinary(8000)
)
RETURNS BIT
AS EXTERNAL NAME [DatabaseCLR].[DatabaseCLR.BinaryUtils].[HasSetBits]
GO

I'm using x64 editions of

  • SqlServer 2014 (assembly compiled for .Net 4.0)
  • SqlServer 2008 (assembly compiled for .Net 2.0)

Solution

  • The error is due to use of LayoutKind.Explicit.

    Changing it to LayoutKind.Sequential

    [StructLayout(LayoutKind.Sequential)]
    internal struct ByteToUlongConverter
    {
        ...
    }
    

    makes possible registering assembly with PERMISSION_SET = SAFE.

    But using LayoutKind.Sequential instead of LayoutKind.Explicit breaks semantics in this case.

    So, PERMISSION_SET = SAFE or LayoutKind.Explicit, not both.