Skip to content

SqlFunctionAttribute in Microsoft.SqlServer.Server doesn't work for table-valued UDFs #3234

@gfody

Description

@gfody

Describe the bug

After porting a SQLCLR assembly to netstandard2.0, scalar-valued UDFs work but table-valued UDFs do not.

The Init method for a CLR table-valued function must be annotated with SqlFunctionAttribute.

To reproduce

create a test SQLCLR assembly targeting netstandard2.0 using the Microsoft.SqlServer.Server package:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.SqlServer.Server" Version="1.0.0" />
    <PackageReference Include="ILRepack.Lib.MSBuild.Task" Version="2.0.40" PrivateAssets="All" />
  </ItemGroup>
  <Target Name="ILRepacker" AfterTargets="Build">
    <ItemGroup>
      <InputAssemblies Include="$(OutputPath)Microsoft.SqlServer.Server.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)netstandard.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.ComponentModel.Composition.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.Data.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.Data.Common.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.Diagnostics.StackTrace.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.Diagnostics.Tracing.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.IO.Compression.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.IO.Compression.Filesystem.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.Net.Http.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.Net.Sockets.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.Resources.ResourceManager.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.Runtime.InteropServices.RuntimeInformation.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.Runtime.Serialization.Primitives.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.Runtime.Serialization.Xml.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.Security.Cryptography.Algorithms.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.Security.SecureString.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.Threading.Overlapped.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.Globalization.Extensions.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.ValueTuple.dll" />
      <InputAssemblies Include="$(MSBuildFrameworkToolsPath64)System.Xml.XPath.XDocument.dll" />
      <InputAssemblies Include="$(OutputPath)*.dll" Exclude="$(OutputPath)Microsoft.SqlServer.Server.dll" />
    </ItemGroup>
    <ILRepack InputAssemblies="@(InputAssemblies)" OutputFile="$(OutputPath)$(AssemblyName)$(TargetExt)" />
  </Target>
</Project>

class w/a scalar-valued udf and a table-valued udf to test:

using Microsoft.SqlServer.Server;
using System.Collections;

public static class SqlClr
{
    [SqlFunction]
    public static string ScalarTest() => "testing";

    [SqlFunction(FillRowMethodName = nameof(TableTestFillRow), TableDefinition = "test nvarchar(max)")]
    public static IEnumerable TableTest()
    {
        yield return "test1";
        yield return "test2";
        yield return "test3";
    }

    public static void TableTestFillRow(object o, out string test) => test = (string)o;
}

deploy the assembly:

create assembly [System.EnterpriseServices]
from 'C:\Windows\Microsoft.NET\Framework64\v4.0.30319\System.EnterpriseServices.dll'
with permission_set = unsafe

create assembly TestClr from 'TestClr.dll' with permission_set = unsafe

scalar-valued UDF works as expected:

create function ScalarTest() returns nvarchar(max) as external name TestClr.SqlClr.ScalarTest

select dbo.ScalarTest()

> testing

table-valued UDF throws:

create function TableTest() returns table (test nvarchar(max)) as external name TestClr.SqlClr.TableTest

> The Init method for a CLR table-valued function must be annotated with SqlFunctionAttribute.

Further technical details

Microsoft.SqlServer.Server version: 1.0.0
.NET target: netstandard2.0
SQL Server version: SQL Server 2022
Operating system: Windows Server 2022

Metadata

Metadata

Assignees

Labels

Bug! 🐛Issues that are bugs in the drivers we maintain.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions