Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ let release =

let version = release.AssemblyVersion
let releaseNotes = release.Notes |> String.concat "\n"
let testDir = "bin"
let testDir = "Build/Tests"

// --------------------------------------------------------------------------------------
// Generate assembly info files with the right version & up-to-date information
Expand Down
7 changes: 4 additions & 3 deletions src/SqlClient.Tests/Lib/Lib.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,19 @@
<Compile Include="Library1.fs" />
<None Include="Script.fsx" />
<Content Include="App.config" />
<Content Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Reference Include="FSharp.Core">
<HintPath>..\..\..\packages\FSharp.Core.4.2.3\lib\net45\FSharp.Core.dll</HintPath>
</Reference>
<Reference Include="FSharp.Data.SqlClient">
<HintPath>..\..\..\bin\FSharp.Data.SqlClient.dll</HintPath>
</Reference>
<Reference Include="Microsoft.SqlServer.Types">
<HintPath>..\..\..\..\..\..\..\..\Program Files (x86)\Microsoft SQL Server\120\SDK\Assemblies\Microsoft.SqlServer.Types.dll</HintPath>
</Reference>
<Reference Include="mscorlib" />
<Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
Expand Down
4 changes: 4 additions & 0 deletions src/SqlClient.Tests/Lib/packages.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FSharp.Core" version="4.2.3" targetFramework="net452" />
</packages>
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,16 @@
<ItemGroup>
<Compile Include="Program.fs" />
<Content Include="Uncomment.App.config" />
<Content Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Reference Include="FSharp.Core">
<HintPath>..\..\..\packages\FSharp.Core.4.0.0.1\lib\net40\FSharp.Core.dll</HintPath>
</Reference>
<Reference Include="FSharp.Data.SqlClient">
<HintPath>..\..\..\bin\FSharp.Data.SqlClient.dll</HintPath>
</Reference>
<Reference Include="mscorlib" />
<Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
Expand Down
4 changes: 4 additions & 0 deletions src/SqlClient.Tests/SqlClient.Tests.NET40/packages.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FSharp.Core" version="4.0.0.1" targetFramework="net40" />
</packages>
10 changes: 5 additions & 5 deletions src/SqlClient.Tests/SqlClient.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<Tailcalls>false</Tailcalls>
<OutputPath>..\..\bin\</OutputPath>
<OutputPath>..\..\Build\Tests</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>
Expand All @@ -39,10 +39,10 @@
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<Tailcalls>true</Tailcalls>
<OutputPath>..\..\bin\</OutputPath>
<OutputPath>..\..\Build\Tests</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<WarningLevel>3</WarningLevel>
<DocumentationFile>..\..\bin\SqlClient.Tests.XML</DocumentationFile>
<DocumentationFile>..\..\Build\Tests\SqlClient.Tests.XML</DocumentationFile>
<OtherFlags>--warnon:1182</OtherFlags>
<NoWarn>101</NoWarn>
</PropertyGroup>
Expand Down Expand Up @@ -101,8 +101,8 @@
<HintPath>..\..\packages\FSharp.Configuration.0.5.3\lib\net40\FSharp.Configuration.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Private>True</Private>
<Reference Include="FSharp.Core">
<HintPath>..\..\packages\FSharp.Core.4.2.1\lib\net45\FSharp.Core.dll</HintPath>
</Reference>
<Reference Include="FSharp.Data.SqlClient">
<HintPath>..\..\bin\FSharp.Data.SqlClient.dll</HintPath>
Expand Down
51 changes: 51 additions & 0 deletions src/SqlClient.Tests/TVPTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,54 @@ let UsingTVPInQuery() =
|> Seq.toList

Assert.Equal<_ list>(expected, actual)


type MappedTVP =
SqlCommandProvider<"
SELECT myId, myName from @input
", ConnectionStrings.AdventureWorksLiteral, TableVarMapping = "@input=dbo.MyTableType">
[<Fact>]
let UsingMappedTVPInQuery() =
printfn "%s" ConnectionStrings.AdventureWorksLiteral
use cmd = new MappedTVP(ConnectionStrings.AdventureWorksLiteral)
let expected = [
1, Some "monkey"
2, Some "donkey"
]

let actual =
cmd.Execute(input = [ for id, name in expected -> MappedTVP.MyTableType(id, name) ])
|> Seq.map(fun x -> x.myId, x.myName)
|> Seq.toList

Assert.Equal<_ list>(expected, actual)


[<Fact>]
let UsingTempTable() =
use conn = new SqlConnection(ConnectionStrings.AdventureWorksLiteral)
conn.Open()
use create = new SqlCommand("
CREATE TABLE #Temp(Id INT NOT NULL, Name NVARCHAR(100) NULL);
INSERT #Temp(Id, Name)
VALUES (1, 'monkey'),
(2, 'donkey')
", conn)

create.ExecuteScalar() |> ignore

use cmd = new SqlCommandProvider<"
SELECT Id, Name from #Temp
", ConnectionStrings.AdventureWorksLiteral, TempTableDefinitions = "CREATE TABLE #Temp(Id INT NOT NULL, Name NVARCHAR(100) NULL)">(conn)

let expected = [
1, Some "monkey"
2, Some "donkey"
]

let actual =
cmd.Execute()
|> Seq.map(fun x -> x.Id, x.Name)
|> Seq.toList

Assert.Equal<_ list>(expected, actual)
2 changes: 1 addition & 1 deletion src/SqlClient.Tests/app.config
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="FSharp.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.4.0.0" newVersion="4.3.1.0" />
<bindingRedirect oldVersion="0.0.0.0-4.4.1.0" newVersion="4.4.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.SqlServer.Types" publicKeyToken="89845dcd8080cc91" culture="neutral" />
Expand Down
5 changes: 5 additions & 0 deletions src/SqlClient.Tests/connectionStrings.Azure.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<connectionStrings>
<add name="AdventureWorks" connectionString="Data Source=.;Initial Catalog=AdventureWorks2012;Integrated Security=True" />
<add name="AdventureWorksDesignOnly" connectionString="Data Source=.;Initial Catalog=AdventureWorks2012;Integrated Security=True" />
<add name="MasterDb" connectionString="Data Source=.;Initial Catalog=master;Integrated Security=True" />
</connectionStrings>
1 change: 1 addition & 0 deletions src/SqlClient.Tests/packages.config
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FSharp.Configuration" version="0.5.3" targetFramework="net451" />
<package id="FSharp.Core" version="4.2.1" targetFramework="net451" />
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net451" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
<package id="xunit.runner.visualstudio" version="2.1.0" targetFramework="net451" />
Expand Down
53 changes: 53 additions & 0 deletions src/SqlClient/DesignTime.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ open System.Diagnostics
open Microsoft.FSharp.Quotations
open ProviderImplementation.ProvidedTypes
open FSharp.Data
open System.Text.RegularExpressions

type internal RowType = {
Provided: Type
Expand Down Expand Up @@ -40,6 +41,10 @@ module internal SharedLogic =
// add .Table
returnType.Single |> cmdProvidedType.AddMember

module Prefixes =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to keep new members in this file internal.

let tempTable = "##SQLCOMMANDPROVIDER_"
let tableVar = "@SQLCOMMANDPROVIDER_"

type DesignTime private() =
static member internal AddGeneratedMethod
(sqlParameters: Parameter list, hasOutputParameters, executeArgs: ProvidedParameter list, erasedType, providedOutputType, name) =
Expand Down Expand Up @@ -600,3 +605,51 @@ type DesignTime private() =
then
yield upcast ProvidedMethod(factoryMethodName.Value, parameters2, returnType = cmdProvidedType, IsStaticMethod = true, InvokeCode = body2)
]

static member internal SubstituteTempTables(connection, commandText: string, tempTableDefinitions : string) =
let tempTableRegex = Regex("#([a-z0-9\-_]+)", RegexOptions.IgnoreCase)

let tempTableNames =
tempTableRegex.Matches(tempTableDefinitions)
|> Seq.cast<Match>
|> Seq.map (fun m -> m.Groups.[1].Value)
|> Seq.toList

match tempTableNames with
| [] -> commandText, []
| _ ->
use cmd = new SqlCommand(tempTableRegex.Replace(tempTableDefinitions, Prefixes.tempTable+"$1"), connection)
cmd.ExecuteScalar() |> ignore

// Only replace temp tables we find in our list.
tempTableRegex.Replace(commandText, MatchEvaluator(fun m ->
match tempTableNames |> List.tryFind((=) m.Groups.[1].Value) with
| Some name -> Prefixes.tempTable + name
| None -> m.Groups.[0].Value)),

tempTableNames

static member internal RemoveSubstitutedTempTables(connection, tempTableNames : string list) =
if not tempTableNames.IsEmpty then
use cmd = new SqlCommand(tempTableNames |> List.map(fun name -> sprintf "DROP TABLE [%s%s]" Prefixes.tempTable name) |> String.concat ";", connection)
cmd.ExecuteScalar() |> ignore

static member internal SubstituteTableVar(commandText: string, tableVarMapping : string) =
let varRegex = Regex("@([a-z0-9_]+)", RegexOptions.IgnoreCase)

let vars =
tableVarMapping.Split([|';'|], System.StringSplitOptions.RemoveEmptyEntries)
|> Array.choose(fun (x : string) ->
match x.Split([|'='|]) with
| [|name;typ|] -> Some(name.TrimStart('@'), typ)
| _ -> None)

// Only replace table vars we find in our list.
let commandText =
varRegex.Replace(commandText, MatchEvaluator(fun m ->
match vars |> Array.tryFind(fun (n,_) -> n = m.Groups.[1].Value) with
| Some (name, _) -> Prefixes.tableVar + name
| None -> m.Groups.[0].Value))

(vars |> Array.map(fun (name,typ) -> sprintf "DECLARE %s%s %s = @%s" Prefixes.tableVar name typ name) |> String.concat "; ") + "; " + commandText

4 changes: 2 additions & 2 deletions src/SqlClient/SqlClient.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@
<OtherFlags>--warnon:1182</OtherFlags>
</PropertyGroup>
<ItemGroup>
<Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Private>True</Private>
<Reference Include="FSharp.Core">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for now we should revert changes to project files, you can keep them local and eventually we will make the solution work better outside of VS2015, but it is easier to know we don't break things without those changes.

<HintPath>..\..\packages\FSharp.Core.4.0.0.1\lib\net40\FSharp.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.SqlServer.TransactSql.ScriptDom">
<HintPath>..\..\lib\Microsoft.SqlServer.TransactSql.ScriptDom.dll</HintPath>
Expand Down
19 changes: 15 additions & 4 deletions src/SqlClient/SqlCommandProvider.fs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ open Microsoft.FSharp.Quotations
open FSharp.Data.SqlClient

open ProviderImplementation.ProvidedTypes
open System.Text.RegularExpressions

[<assembly:TypeProviderAssembly()>]
#if DEBUG
Expand Down Expand Up @@ -53,9 +54,11 @@ type SqlCommandProvider(config : TypeProviderConfig) as this =
ProvidedStaticParameter("ConfigFile", typeof<string>, "")
ProvidedStaticParameter("AllParametersOptional", typeof<bool>, false)
ProvidedStaticParameter("DataDirectory", typeof<string>, "")
ProvidedStaticParameter("TempTableDefinitions", typeof<string>, "")
ProvidedStaticParameter("TableVarMapping", typeof<string>, "")
],
instantiationFunction = (fun typeName args ->
let value = lazy this.CreateRootType(typeName, unbox args.[0], unbox args.[1], unbox args.[2], unbox args.[3], unbox args.[4], unbox args.[5], unbox args.[6])
let value = lazy this.CreateRootType(typeName, unbox args.[0], unbox args.[1], unbox args.[2], unbox args.[3], unbox args.[4], unbox args.[5], unbox args.[6], unbox args.[7], unbox args.[8])
cache.GetOrAdd(typeName, value)
)
)
Expand All @@ -70,6 +73,8 @@ type SqlCommandProvider(config : TypeProviderConfig) as this =
<param name='AllParametersOptional'>If set all parameters become optional. NULL input values must be handled inside T-SQL.</param>
<param name='ResolutionFolder'>A folder to be used to resolve relative file paths to *.sql script files at compile time. The default value is the folder that contains the project or script.</param>
<param name='DataDirectory'>The name of the data directory that replaces |DataDirectory| in connection strings. The default value is the project or script directory.</param>
<param name='TempTableDefinitions'>Temp tables create command.</param>
<param name='TableVarMapping'>List table-valued parameters in the format of "@tvp1=[dbo].[TVP_IDs]; @tvp2=[dbo].[TVP_IDs]"</param>
"""

this.AddNamespace(nameSpace, [ providerType ])
Expand All @@ -81,7 +86,7 @@ type SqlCommandProvider(config : TypeProviderConfig) as this =
|> defaultArg
<| base.ResolveAssembly args

member internal this.CreateRootType(typeName, sqlStatement, connectionStringOrName: string, resultType, singleRow, configFile, allParametersOptional, dataDirectory) =
member internal this.CreateRootType(typeName, sqlStatement, connectionStringOrName: string, resultType, singleRow, configFile, allParametersOptional, dataDirectory, tempTableDefinitions, tableVarMapping) =

if singleRow && not (resultType = ResultType.Records || resultType = ResultType.Tuples)
then
Expand All @@ -104,13 +109,19 @@ type SqlCommandProvider(config : TypeProviderConfig) as this =
conn.CheckVersion()
conn.LoadDataTypesMap()

let parameters = DesignTime.ExtractParameters(conn, sqlStatement, allParametersOptional)
let designTimeSqlStatement, tempTableNames =
let sql, tempTableNames = DesignTime.SubstituteTempTables(conn, sqlStatement, tempTableDefinitions)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidtme we need a conditional to not go in that codepath if tempTableDefinitions is null or empty, because without that conditional, we are at risk of not having a workaround if SubstituteTempTables alters the statement in unexpected ways (SQL statements can have # in them, you can have that frequently in strings and dynamical statement).

I'd like the new codepaths to only be active when the parameter is actually defined by the user.

DesignTime.SubstituteTableVar(sql, tableVarMapping), tempTableNames

let parameters = DesignTime.ExtractParameters(conn, designTimeSqlStatement, allParametersOptional)

let outputColumns =
if resultType <> ResultType.DataReader
then DesignTime.GetOutputColumns(conn, sqlStatement, parameters, isStoredProcedure = false)
then DesignTime.GetOutputColumns(conn, designTimeSqlStatement, parameters, isStoredProcedure = false)
else []

DesignTime.RemoveSubstitutedTempTables(conn, tempTableNames)

let rank = if singleRow then ResultRank.SingleRow else ResultRank.Sequence
let returnType = DesignTime.GetOutputTypes(outputColumns, resultType, rank, hasOutputParameters = false)

Expand Down
1 change: 1 addition & 0 deletions src/SqlClient/packages.config
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FSharp.Core" version="4.0.0.1" targetFramework="net40" />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, let's put that aside for now, you can keep it in your local repository.

<package id="FSharp.TypeProviders.StarterPack" version="1.1.3.72" targetFramework="net40" />
</packages>