Is it possible for a SQL CLR User-Defined Function to return the data type varbinary(MAX)?
In the documentation it mentions:
"The input parameters and the type returned from a scalar-valued function can be any of the scalar data types supported by SQL Server, except rowversion, text, ntext, image, timestamp, table, or cursor." - they don't mention varbinary, but I'm not sure...
I have some byte-array data from the .NET side that I need to return to SQL Server from the CLR, and I'm trying to avoid having to do it with an output parameter from a stored procedure (this is how I have it working in test now).
Thanks!
If you define it as returning a SqlBytes data type, this should correctly map to varbinary(MAX)
in SQL Server.
[SqlFunction]
public static SqlBytes Function1()
{
return new SqlBytes(Encoding.UTF8.GetBytes("Hello world."));
}
Whilst you can also use the SqlBinary data type, if you deploy via Visual Studio, it will be mapped onto varbinary(8000)
rather than varbinary(MAX)
.
Technically there is no 8000 byte maximum in the interface from SQL Server to CLR code. It is mainly a difference of how the T-SQL wrapper Stored Proc or Function is defined. Meaning, if the T-SQL Proc or Function that calls the CLR code defines the RETURNS
as VARBINARY(MAX)
, then it shall be VARBINARY(MAX)
, whether or not you specified SqlBytes
or SqlBinary
as the return type of the CLR code.
Both SqlBytes
and SqlBinary
can handle the 2 GB limit, BUT the difference is in how the CLR code accepts the data. SqlBinary
(just like SqlString
) takes the parameter value all at once while SqlBytes
(just like SqlChars
) provides a streaming interface so it might be more efficient for very large values.
Going back to the issue that you are seeing with the pre-defined SQL Function wrapper, that is was a matter of how Visual Studio (technically SSDT) auto-generatesd the T-SQL. The default for SqlBinary
is was VARBINARY(8000)
while the default for SqlBytes
is was VARBINARY(MAX)
. In the same manner, the default for SqlString
is was NVARCHAR(4000)
while the default for SqlChars
is was NVARCHAR(MAX)
. Those were the defaults when this question was asked. Starting in perhaps Visual Studio 2012, the default was changed to use MAX
for all 4 of these datatypes. This is not necessarily a good thing since there is a definite performance hit for using the MAX
types vs the non-MAX
types. So, if you don't need more than 8000 bytes of VARBINARY
or 4000 bytes of NVARCHAR
, then you will want to override the default using one of the following methods:
You can ALTER
the Function or Proc definition after it is generated by Visual Studio, and in this case you can even change the datatypes (of either input parameters or return values) to be any size such as VARBINARY(100)
or NVARCHAR(50)
.
You can use the SqlFacet
decorator to tell Visual Studio / SSDT to auto-generate the Function or Proc definitions with the size option that you prefer as opposed to the default. The following example shows specifying the size for both input parameters and the return value (note that -1
= MAX
):
[return: SqlFacet(MaxSize = -1)]
[Microsoft.SqlServer.Server.SqlFunction(Name = "FunctionName")]
public static SqlBinary FuncName([SqlFacet(MaxSize = 50)] SqlString InputParam)
Using either of these two methods you can make either SqlBinary
or SqlBytes
map to either VARBINARY(1 - 8000)
or VARBINARY(MAX)
. Likewise, you can make either SqlString
or SqlChars
map to eithert NVARCHAR(1 - 4000)
or NVARCHAR(MAX)
.
It appears that the answer is yes - you can use both varbinary(MAX) by returning "SqlBinary" or you can use SqlBytes as recommended above.