0

A little context, I need a means to losslessly insert/select/compare single and double precision floating point into a SQL Server database without using parameters, thought I'd be clever and call a DLL as a super-fast function.

I wrote and tested this C# class (which I've well-tested):

using System;
using System.Linq;
using System.Reflection;

namespace SqlServerCast
{
    public class SqlServerCast
    {
        static public char ByteOrder;

        public enum ByteOrderEnum {
            BigEndian = 1,     /*  The most significant byte (highest address) is stored first. */
            LittleEndian = 0   /*  The least significant byte (lowest address) is stored first. */}

        public static void Main()
        {
            Assembly assem = typeof(SqlServerCast).Assembly;
            Object o = assem.CreateInstance("SqlServerCast", false,
                BindingFlags.ExactBinding,
                null, new Object[] { 2 }, null, null); 
        }

        public SqlServerCast(char val)    //  constructor
        {
            if (val != 0) ByteOrder = (char) ByteOrderEnum.BigEndian;
            else ByteOrder = (char) ByteOrderEnum.LittleEndian;
        }

        public static double CastToDBL(string str) {return BitConverter.ToDouble(StringToByteArray(str), 0);}

        public static float CastToSGL(string str)  {return BitConverter.ToSingle(StringToByteArray(str), 0);}

        private static string Dash = "-", NullStr = null;
        public static string CastFromDBL(double dbl) {
            switch (ByteOrder)
            {
                case (char)ByteOrderEnum.BigEndian:   //  actually, network byte order, big endian
                    byte[] bytes = BitConverter.GetBytes(dbl);
                    return BitConverter.ToString(bytes.Reverse().ToArray()).Replace(Dash, NullStr);
                case (char)ByteOrderEnum.LittleEndian:
                    return BitConverter.ToString(BitConverter.GetBytes(dbl)).Replace(Dash, NullStr);
                default:
                    return null;
            }
         }

        public static string CastFromSGL(float sgl)
        {
            switch (ByteOrder)
            {
                case (char)ByteOrderEnum.BigEndian:   //  actually, network byte order, big endian
                    byte[] bytes = BitConverter.GetBytes(sgl);
                    return BitConverter.ToString(bytes.Reverse().ToArray()).Replace(Dash, NullStr);
                case (char)ByteOrderEnum.LittleEndian:
                    return BitConverter.ToString(BitConverter.GetBytes(sgl)).Replace(Dash, NullStr);
                default:
                    return null;
            }
        }

        private static byte[] StringToByteArray(String hex)
        {
            int NumberChars = hex.Length;
            byte[] bytes = new byte[NumberChars / 2];
            for (int i = 0; i < NumberChars; i += 2)
                bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
            switch (ByteOrder) {
                case (char) ByteOrderEnum.BigEndian:   //  actually, network byte order, big endian
                    return bytes.Reverse().ToArray();
                case (char) ByteOrderEnum.LittleEndian:
                    return bytes;
                default:
                    return null;
            }
        }
    }
}

...trying to follow the prescriptions on SO and MSDN for T-SQL -callable DLLs and so far I get this error:

Msg 6544, Level 16, State 1, Line 1
CREATE ASSEMBLY for assembly 'SqlServerCast' failed because assembly 'SqlServerCast' is malformed or not a pure .NET assembly.
Unverifiable PE Header/native stub.

Can someone give me a step-by-step path to success here? I'm stuck...

BTW: I know about creating the hashes, I don't think that's the issue yet.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Danny Holstein
  • 144
  • 1
  • 14
  • 1
    hope this helps https://stackoverflow.com/questions/36653838/sql-server-create-assembly-for-assembly-test-failed-because-assembly-test – Alex Mar 03 '22 at 17:02
  • You are quite clearly missing quite a few things to get that function into a SQLCLR assembly, namely the correct attributes on the functions. Completely confused: what is lossy about floats/doubles? They can have slightly different results on different processors, but the same machine will always get the same result. And why would you not want to use parameters? This honestly strikes me as a [XY problem](https://en.wikipedia.org/wiki/XY_problem), what problem are you *actually* facing? – Charlieface Mar 03 '22 at 20:16
  • Are you trying to use `CREATE ASSEMBLY` with the `SqlServerCast.cs` file directly? You can't do that. You'll need to compile the C# code to a .dll file first then use `CREATE ASSEMBLY` with that. If you've created the correct project type in Visual Studio it also outputs an .sql file containing the `CREATE ASSEMBLY` code you need to execute. – AlwaysLearning Mar 03 '22 at 20:59
  • @AlwaysLearning Of course I had built a DLL from the source file, and executed the CREATE ASSEMBLY command, the results for both the file and the binhex are posted in the question. I've been using VS since 2003. – Danny Holstein Mar 07 '22 at 15:39
  • I had read the information pointed to from the link already, is it stating that I won't be able to create an assembly with my code? I don't want to use parameters because it's not very portable, requiring .NET on those clients. I have to deal with old PCs, including Win 7, and don't have ANY latitude in updating. Yes, floats/doubles are lossy when you convert to text representation and back, that's why I want to have a function that converts the 8 character hex representation of a float into the IEEE-754 representation in the DB. – Danny Holstein Mar 07 '22 at 15:58
  • What is the version of the target SQL Server and what version of the .NET Framework are you targeting? SQL Server 2005-2008 R2 use .NET Framework 2.0, SQL Sever 2012 and later use 4.x. – AlwaysLearning Mar 07 '22 at 21:38
  • My development environment is VS 2019, .NET Framework 4.8 and SQL Server 2019 – Danny Holstein Mar 09 '22 at 13:58

1 Answers1

0

These instructions are for Microsoft SQL Server Management Studio 2014.

Import the Assembly

First of all you need to import that assembly into your database inside SQL Server Management Studio by navigating to the New Assembly dialog window:

DatabaseName -> Programmability -> Assemblies -> (Right Click) 'New Assembly...'

Inside the 'New Assembly' dialog window, chose Browse under the Path to assembly field and select the assembly you want to import. Adjust Permissions and click ok.

Wrap Assembly Methods in a SQL Function

Next you need to create sql function to wrap your assembly method like this:

CREATE FUNCTION [dbo].[fn_funcName](@str [varchar](max))
RETURNS 
   varchar(max) 
WITH EXECUTE AS CALLER
AS 
EXTERNAL NAME [YourSqlAssemblyName].[YourAssemblyName.Class1].[GetName]

If you want to return table from your function read about SqlFunctionAttribute in .NET.

Step by Step Guide to Create Sql server database project and compile dll to be used in Sql Server:

https://dbtut.com/index.php/2019/05/05/what-is-clr-and-how-to-import-dll-in-sql-server/

Gaurav Chaudhary
  • 321
  • 1
  • 3
  • 15