I have a written a number of SQL CLR functions (UDF) that reads data from an external DB2 database hosted on an IBM iSeries (using IBM DB2 .Net Provider). So that the function has the necessary permissions to read this data, I need to decorate the function with the SqlFunction attribute having the DataAccess property set to DataAccessKind.Read. I also deploy the assembly as UNSAFE.
The time taken to read data from the DB2 database is relatively slow (eg, 3ms for the simplest ExecuteScalar).
I use these UDFs to effectively merge data from the DB2 database into Sql Server views.
For example, suppose my UDF is defined as
[SqlFunction(DataAccess = DataAccessKind.Read, IsDeterministic = true)]
public static SqlMoney GetCostPrice(SqlString partNumber)
{
decimal costPrice;
// open DB2 connection and retrieve cost price for part
return new SqlMoney(costPrice);
}
and then use this in my SQL View as:
select Parts.PartNumber,
dbo.GetCostPrice(Parts.PartNumber) as CostPrice
from Parts
The poor performance problems could be impacted significantly if I could run my SQL Views with a parallel query plan.
There are documented techniques on how to force a query plan to run in parallel rather than serial but these techniques have limitations imposed by SQL Server, one of which is that a SQL CLR defined function MUST have DataAccess = DataAccessKind.None.
But if I set DataAcessKind to None then I get an exception when attempting to open any DbConnection within the function.
And that is my problem! How can I run my UDF in a parallel query plan while still allowing it to read data from the external database?
The best idea I have to address this is to hard-code DataAccess = DataAccessKind.None in my SqlFunction attribute and then, at runtime, within the body of the function elevate permissions with Code Access Security so that subsequent code has permissions to open DbConnection objects.
But I can't figure out how to do it? As an experiment, I have tried the following
[SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true)]
public static SqlMoney TestFunction()
{
var sqlPerm = new SqlClientPermission(PermissionState.Unrestricted);
sqlPerm.Assert();
using (var conn = new SqlConnection("context connection=true"))
{
conn.Open();
}
return new SqlMoney();
}
and invoke from Sql Server Management Studio with:
select dbo.TestFunction()
but I continue to get a security exception...
A .NET Framework error occurred during execution of user-defined routine or aggregate "TestFunction": System.InvalidOperationException: Data access is not allowed in this context. Either the context is a function or method not marked with DataAccessKind.Read or SystemDataAccessKind.Read, is a callback to obtain data from FillRow method of a Table Valued Function, or is a UDT validation method. System.InvalidOperationException: at System.Data.SqlServer.Internal.ClrLevelContext.CheckSqlAccessReturnCode(SqlAccessApiReturnCode eRc) at System.Data.SqlServer.Internal.ClrLevelContext.GetCurrentContext(SmiEventSink sink, Boolean throwIfNotASqlClrThread, Boolean fAllowImpersonation) at Microsoft.SqlServer.Server.InProcLink.GetCurrentContext(SmiEventSink eventSink) at Microsoft.SqlServer.Server.SmiContextFactory.GetCurrentContext() at System.Data.SqlClient.SqlConnectionFactory.GetContextConnection(SqlConnectionString options, Object providerInfo, DbConnection owningConnection) at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection) at System.Data.ProviderBase.DbConnectionFactory.CreateNonPooledConnection(DbConnection owningConnection, DbConnectionPoolGroup poolGroup) at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection) at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) at System.Data.SqlClient.SqlConnection.Open() at UserDefinedFunctions.UserDefinedFunctions.TestFunction()
Anyone got any ideas?
Thanks in advance.
(btw, I am running on SQL 2008 using .Net 3.5)