Skip to content

Commit 1882804

Browse files
Issue 199 (#212)
1 parent 7c2df5c commit 1882804

File tree

4 files changed

+134
-51
lines changed

4 files changed

+134
-51
lines changed

src/SqlClient.Tests/ProgrammabilityTests.fs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,34 @@ let ResultSetAndOutParam() =
241241
Assert.Equal<_ list>([ Some "donkey" ], [ for x in result -> x.myName ] )
242242
Assert.Equal(2L, !total)
243243

244+
[<Fact>]
245+
let TVFSynonym() =
246+
let personId = 42
247+
let actual =
248+
use cmd = new AdventureWorks.HumanResources.GetContactInformation()
249+
cmd.ExecuteSingle(personId)
250+
|> Option.map(fun x -> x.FirstName, x.LastName)
251+
252+
let expected =
253+
use cmd = new GetContactInformation()
254+
cmd.ExecuteSingle(personId)
255+
|> Option.map(fun x -> x.FirstName, x.LastName)
256+
257+
Assert.Equal(expected, actual)
258+
259+
[<Fact>]
260+
let SPSynonym() =
261+
let personId = 42
262+
let actual =
263+
use cmd = new AdventureWorks.HumanResources.GetEmployeeManagers()
264+
[ for x in cmd.Execute(personId) -> sprintf "%A.%A" x.FirstName x.LastName ]
265+
266+
let expected =
267+
use cmd = new AdventureWorks.dbo.uspGetEmployeeManagers()
268+
[ for x in cmd.Execute(personId) -> sprintf "%A.%A" x.FirstName x.LastName ]
269+
270+
Assert.Equal<_ list>(expected, actual)
271+
244272
module ReturnValues =
245273
type AdventureWorks = SqlProgrammabilityProvider<ConnectionStrings.AdventureWorksNamed, UseReturnValue = true>
246274

src/SqlClient.Tests/extensions.sql

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ IF OBJECT_ID(N'dbo.TableHavingColumnNamesWithSpaces') IS NOT NULL
6161
DROP TABLE dbo.TableHavingColumnNamesWithSpaces
6262
GO
6363

64+
IF OBJECT_ID(N'HumanResources.GetContactInformation') IS NOT NULL
65+
DROP SYNONYM HumanResources.GetContactInformation
66+
GO
67+
68+
IF OBJECT_ID(N'HumanResources.GetEmployeeManagers') IS NOT NULL
69+
DROP SYNONYM HumanResources.GetEmployeeManagers
70+
GO
6471

6572
CREATE PROCEDURE dbo.AddRef @x AS INT, @y AS INT, @sum AS INT OUTPUT
6673
AS
@@ -193,3 +200,9 @@ END
193200

194201
GO
195202

203+
CREATE SYNONYM HumanResources.GetContactInformation FOR dbo.ufnGetContactInformation;
204+
GO
205+
206+
CREATE SYNONYM HumanResources.GetEmployeeManagers FOR dbo.uspGetEmployeeManagers;
207+
GO
208+

src/SqlClient/SqlClientExtensions.fs

Lines changed: 92 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -176,38 +176,28 @@ let rec parseDefaultValue (definition: string) (expr: Microsoft.SqlServer.Transa
176176
| _ -> None
177177
| _ -> None
178178

179-
type Routine =
180-
| StoredProcedure of schema: string * name: string * definition: string * description: string option
181-
| TableValuedFunction of schema: string * name: string * definition: string * description: string option
182-
| ScalarValuedFunction of schema: string * name: string * definition: string * description: string option
183-
184-
member this.Definition =
185-
match this with
186-
| StoredProcedure(_, _, definition, _)
187-
| TableValuedFunction(_, _, definition, _)
188-
| ScalarValuedFunction(_, _, definition, _) -> definition
189-
190-
member this.Description =
191-
match this with
192-
| StoredProcedure(_, _, _, description)
193-
| TableValuedFunction(_, _, _, description)
194-
| ScalarValuedFunction(_, _, _, description) -> description
195-
196-
member this.TwoPartName =
197-
match this with
198-
| StoredProcedure(schema, name, _, _)
199-
| TableValuedFunction(schema, name, _, _)
200-
| ScalarValuedFunction(schema, name, _, _) -> schema, name
201-
202-
member this.IsStoredProc = match this with StoredProcedure _ -> true | _ -> false
179+
type internal RoutineType = StoredProcedure | TableValuedFunction | ScalarValuedFunction
180+
181+
type internal Routine = {
182+
Type: RoutineType
183+
Schema: string
184+
Name: string
185+
Definition: string
186+
Description: string option
187+
BaseObject: string * string
188+
} with
189+
190+
member this.TwoPartName = this.Schema, this.Name
191+
192+
member this.IsStoredProc = this.Type = StoredProcedure
203193

204194
member this.ToCommantText(parameters: Parameter list) =
205195
let twoPartNameIdentifier = sprintf "%s.%s" <|| this.TwoPartName
206-
match this with
207-
| StoredProcedure _-> twoPartNameIdentifier
208-
| TableValuedFunction _ ->
196+
match this.Type with
197+
| StoredProcedure -> twoPartNameIdentifier
198+
| TableValuedFunction ->
209199
parameters |> List.map (fun p -> p.Name) |> String.concat ", " |> sprintf "SELECT * FROM %s(%s)" twoPartNameIdentifier
210-
| ScalarValuedFunction _ ->
200+
| ScalarValuedFunction ->
211201
parameters |> List.map (fun p -> p.Name) |> String.concat ", " |> sprintf "SELECT %s(%s)" twoPartNameIdentifier
212202

213203
let internal providerTypes =
@@ -305,30 +295,82 @@ type SqlConnection with
305295
then
306296
"(SELECT NULL AS Value)"
307297
else
308-
"fn_listextendedproperty ('MS_Description', 'schema', SPECIFIC_SCHEMA, ROUTINE_TYPE, SPECIFIC_NAME, default, default)"
309-
310-
let getRoutinesQuery = sprintf "
311-
SELECT
312-
SPECIFIC_SCHEMA
313-
,SPECIFIC_NAME
314-
,DATA_TYPE
315-
,DEFINITION = ISNULL( OBJECT_DEFINITION( OBJECT_ID( SPECIFIC_SCHEMA + '.' + SPECIFIC_NAME)), '')
316-
,DESCRIPTION = XProp.Value
317-
FROM
318-
INFORMATION_SCHEMA.ROUTINES
319-
OUTER APPLY %s AS XProp
320-
WHERE
321-
ROUTINE_SCHEMA = '%s'" descriptionSelector schema
298+
"fn_listextendedproperty ('MS_Description', 'schema', BaseObjectSchema, ROUTINE_TYPE, BaseObjectName, default, default)"
299+
300+
let getRoutinesQuery =
301+
sprintf "
302+
WITH ExplicitRoutines AS
303+
(
304+
SELECT
305+
SPECIFIC_SCHEMA AS [Schema]
306+
,SPECIFIC_NAME AS Name
307+
,ROUTINE_TYPE
308+
,DATA_TYPE
309+
,SPECIFIC_SCHEMA AS BaseObjectSchema
310+
,SPECIFIC_NAME AS BaseObjectName
311+
FROM
312+
INFORMATION_SCHEMA.ROUTINES
313+
),
314+
Synonyms AS
315+
(
316+
SELECT
317+
OBJECT_SCHEMA_NAME(object_id) AS [Schema]
318+
,name AS Name
319+
,ROUTINE_TYPE
320+
,DATA_TYPE
321+
,SPECIFIC_SCHEMA AS BaseObjectSchema
322+
,SPECIFIC_NAME AS BaseObjectName
323+
FROM
324+
sys.synonyms
325+
JOIN INFORMATION_SCHEMA.ROUTINES ON
326+
OBJECT_ID(ROUTINES.ROUTINE_SCHEMA + '.' + ROUTINES.ROUTINE_NAME) = OBJECT_ID(base_object_name)
327+
)
328+
SELECT
329+
XS.[Schema]
330+
,XS.Name
331+
,RoutineSubType =
332+
CASE
333+
WHEN XS.DATA_TYPE = 'TABLE' THEN 'TableValuedFunction'
334+
WHEN XS.DATA_TYPE IS NULL THEN 'StoredProcedure'
335+
ELSE 'ScalarValuedFunction'
336+
END
337+
,ISNULL( OBJECT_DEFINITION( OBJECT_ID( XS.BaseObjectSchema + '.' + XS.BaseObjectName)), '') AS [Definition]
338+
,XS.BaseObjectSchema
339+
,XS.BaseObjectName
340+
,[Description] = XProp.Value
341+
FROM
342+
(
343+
SELECT * FROM ExplicitRoutines
344+
UNION ALL
345+
SELECT * FROM Synonyms
346+
) AS XS
347+
OUTER APPLY %s AS XProp
348+
WHERE
349+
[Schema] = @schema
350+
" descriptionSelector
322351

323352
use cmd = new SqlCommand(getRoutinesQuery, this)
353+
cmd.Parameters.AddWithValue("@schema", schema) |> ignore
354+
324355
cmd.ExecuteQuery(fun x ->
325-
let schema, name = unbox x.["SPECIFIC_SCHEMA"], unbox x.["SPECIFIC_NAME"]
326-
let definition = unbox x.["DEFINITION"]
327-
let description = x.TryGetValue( "DESCRIPTION")
328-
match x.["DATA_TYPE"] with
329-
| :? string as x when x = "TABLE" -> TableValuedFunction(schema, name, definition, description)
330-
| :? DBNull -> StoredProcedure(schema, name, definition, description)
331-
| _ -> ScalarValuedFunction(schema, name, definition, description)
356+
let schema, name = unbox x.["Schema"], unbox x.["Name"]
357+
let definition = unbox x.["Definition"]
358+
let description = x.TryGetValue( "Description")
359+
let routineType =
360+
match string x.["RoutineSubType"] with
361+
| "TableValuedFunction" -> TableValuedFunction
362+
| "StoredProcedure" -> StoredProcedure
363+
| "ScalarValuedFunction" -> ScalarValuedFunction
364+
| unexpected -> failwithf "Unexpected database routine type: %s." unexpected
365+
366+
{
367+
Type = routineType
368+
Schema = schema
369+
Name = name
370+
Definition = definition
371+
Description = description
372+
BaseObject = unbox x.["BaseObjectSchema"], unbox x.["BaseObjectName"]
373+
}
332374
)
333375
|> Seq.toArray
334376

@@ -377,7 +419,7 @@ type SqlConnection with
377419
OUTER APPLY %s AS XProp
378420
WHERE
379421
p.Name <> ''
380-
AND OBJECT_ID('%s.%s') = object_id" descriptionSelector <|| routine.TwoPartName
422+
AND OBJECT_ID('%s.%s') = object_id" descriptionSelector <|| routine.BaseObject
381423

382424
[
383425
use cmd = new SqlCommand( query, this)

src/SqlClient/SqlClientProvider.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ type public SqlProgrammabilityProvider(config : TypeProviderConfig) as this =
167167

168168
let commandText = routine.ToCommantText(parameters)
169169
let outputColumns = DesignTime.GetOutputColumns(conn, commandText, parameters, routine.IsStoredProc)
170-
let rank = match routine with ScalarValuedFunction _ -> ResultRank.ScalarValue | _ -> ResultRank.Sequence
170+
let rank = if routine.Type = ScalarValuedFunction then ResultRank.ScalarValue else ResultRank.Sequence
171171

172172
let hasOutputParameters = parameters |> List.exists (fun x -> x.Direction.HasFlag( ParameterDirection.Output))
173173

0 commit comments

Comments
 (0)