Lewis Bruck's musings on SQL Server and .NET

  • F# and SQL Server CLR integration (Part 1)

    Please read my first post to understand the goals and security implications of the samples I am presenting. 

    I will assume that you are in the sysadmin role on your SQL2005 server, you are using a program that allows you to execute TSQL commands (like Management Studio), and that you are running it on the same machine as your F# installation.  Change any paths to be appropriate to your machine.

    First of all, you need to turn on CLR support in SQL2005:

    sp_configure 'clr enabled', 1
    go
    reconfigure
    go

    Next, you need to create a database and allow the execution of "unsafe" assemblies in it.  There is a more sophisticated (and complicated) method involving signing the unsafe assemblies with a key and then granting the appropriate permissions to that key, but for testing purposes I will go the simple route:

    create database fsharp
    go
    alter database fsharp set trustworthy on
    go
    use fsharp
    go

    Now you need to load the two utility libraries used by most F# programs into the database:

    create assembly fslib from 'c:\fsharp\fsharp-1.1.11.7\bin\fslib.dll' with permission_set = unsafe
    go
    create assembly mllib from 'c:\fsharp\fsharp-1.1.11.7\bin\mllib.dll' with permission_set = unsafe
    go

    Now you are ready to write your first F# stored procedure.  Create a file called sqlclr.fs (the name is important) with following code:

    open System
    open System.Data
    open System.Data.Sql
    open System.Data.SqlTypes
    open Microsoft.SqlServer.Server

    [<SqlProcedure>]
    let printToday() =
       SqlContext.Pipe.Send(DateTime.Now.ToString())

    Compile this into a DLL.  To load and test out the procedure, issue the following commands:

    create assembly sqlclr from 'c:\fsharp\sqlclr\sqlclr.dll' with permission_set = unsafe
    go
    create procedure PrintToday external name sqlclr.sqlclr.PrintToday
    go
    exec PrintToday
    go

    I won't go into all of the details of SQL CLR integration at this point, I want to point out some important facts.  CLR stored procedures have to be static functions; this works well with normal F# functions since they are defined this way.  By default, they get created in a class with the same name as the source file;  the name of that class must be used as the second part of the external name of the stored procedure.

    This has been a lot of setup work for very little immediate reward, but it lays the foundation for what I'll present in future posts.  Next, I will demonstrate less trivial (and more useful) user-defined functions in F#.

     

     

  • F# and SQL Server CLR integration (Part 0)

    One of the many features in SQL2005 is the ability to create stored procedures, functions (UDFs), and user-defined data types (UDTs) in any .NET language.  As might be expected, most of the examples available are in C# and Visual Basic.  However, I think that F# should get equal representation, especially since I think that certain features of F# can make it a great language for this:

    • Object expressions to make it easier to re-use "boiler-plate" code for UDTs
    • Easy IEnumerable<> functions for table-valued functions
    • Compositional pickling for simpler binary serialization
    • Integration with LINQ for more sophisticated query processing in UDFs

    There are still some technical challenges that need to be addressed before F# can be used  as a general purpose language with SQL2005 CLR.  The biggest problem is that SQL2005 is very strict about the IL it allows to run since it runs "in-process" on the server and the classes and assemblies used can be shared between multiple users.  Currently, F# runs afoul of one of the checks:  all static fields must be marked as readonly.  This restriction is to make sure that one user can't change the contents of a shared class being used by other users.  Don Syme is aware of the problem and working on a solution (no release date, yet).

    However, with the release of 1.1.11.7, it is possible to start experimenting with F# and SQL2005 by disabling or bypassing the CLR security checks.  By disabling the checks, we can use F#-generated assemblies at the risk of compromising the server.  Therefore, I can't recommend this in a production environment unless you have a very clear understanding of the security and stability implications and consequences of the various methods of disabling the checks.  See the SQL2005 documentation for more details.

    With these caveats, I'm going to present some sample F# programs which implement SQL2005 CLR objects.  These can be run on any SQL2005 installion (including the free Express Edition).  They require the use of .NET 2.0 (aka Whidbey) (another free download).

    My plan is to have four more posts over the next week:

    1. Setup and a simple stored procedure
    2. User defined functions (both scalar- and table-valued)
    3. User defined data types
    4. User defined aggregate functions

    I hope that this alternative look at F#/.NET interoperability will be interesting.

     

  • Generating a table of integers

    Generating a sequence of integers as a table is a useful utility to have.  Historically, this was done either by having a temporary table or falling back to a TSQL while loop.  However, the UDT feature in SQL2000 and the CTE (common table expression) feature in SQL2005 make it easy to write a function that does this without explicitly looping or materializing the sequence.

    Here is the code:

    alter function Ints(@n int)
    returns table as
    return
    with
    Digits as
    (
    select  0 as num union all select  1 union all select  2 union all select  3 union all
    select  4        union all select  5 union all select  6 union all select  7 union all
    select  8        union all select  9 union all select 10 union all select 11 union all
    select 12        union all select 13 union all select 14 union all select 15
    ),
    Numbers as
    (
    select 
       
    d7
    .num * 16 * 16 * 16 * 16 * 16 * 16 * 16
       
    + d6.num * 16 * 16 * 16 * 16 * 16 * 16
       
    + d5.num * 16 * 16 * 16 * 16 * 16
       
    + d4.num * 16 * 16 * 16 * 16
       
    + d3.num * 16 * 16 * 16
       
    + d2.num * 16 * 16 
       
    + d1.num * 16 
       
    + d0.num as num
    from Digits d0 cross join Digits d1 cross join Digits d2 cross join Digits d3 cross join 
         Digits d4
    cross join Digits d5 cross join Digits d6 cross join Digits d7
    where 
           
    d7.num <= @n / (4096 * 65536)
       
    and
    d6.num <= @n / (256 * 65536)
       
    and
    d5.num <= @n / (16 * 65536)
       
    and
    d4.num <= @n / (65536)
       
    and
    d3.num <= @n / (4096)
       
    and
    d2.num <= @n / (256)
       
    and
    d1.num <= @n / (16)
       
    and
    d0.num <= @n
    )
    select * from Numbers where num < @n

    For instance, if you really wanted to know what the harmonic series sum(1/n) from 1 to 1,000,000, then you could run the following query:

    select sum(1.0 / (1.0 + num)) from Ints(1000000)

    It comes back with the answer (14.39272671788580) in under two seconds on AMD 4200 X2.  It automatically parallelized the query since two cores are available.

    I am going to use this function in further notes on using SQL queries for unorthodox purposes.

     

  • Welcome to my blog

    I am an SDET (aka tester) in the SQL Server Storage Engine group.  I want to use the blog to share what I find interesting, useful, or exciting about SQL2005, .NET, C#, or databases in general.

© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker