From d7565fd5b59e2a400bc731856ddafbe240dac8af Mon Sep 17 00:00:00 2001 From: Stefano Pian Date: Fri, 29 Sep 2017 17:19:38 +0200 Subject: [PATCH 1/4] Add sqlQuery computation expression --- src/SQLProvider/SqlProvider.fsproj | 1 + src/SQLProvider/SqlQueryBuilder.fs | 321 ++++++++++++++++++++++++++ tests/SqlProvider.Tests/CrudTests.fs | 45 ++-- tests/SqlProvider.Tests/QueryTests.fs | 190 +++++++-------- 4 files changed, 446 insertions(+), 111 deletions(-) create mode 100644 src/SQLProvider/SqlQueryBuilder.fs diff --git a/src/SQLProvider/SqlProvider.fsproj b/src/SQLProvider/SqlProvider.fsproj index 2042da17c..3bb3ca52b 100644 --- a/src/SQLProvider/SqlProvider.fsproj +++ b/src/SQLProvider/SqlProvider.fsproj @@ -63,6 +63,7 @@ ExpressionOptimizer.fs + diff --git a/src/SQLProvider/SqlQueryBuilder.fs b/src/SQLProvider/SqlQueryBuilder.fs new file mode 100644 index 000000000..57e1bd270 --- /dev/null +++ b/src/SQLProvider/SqlQueryBuilder.fs @@ -0,0 +1,321 @@ +namespace FSharp.Data.Sql + +open System.Linq +open System.Collections.Generic +open Microsoft.FSharp.Linq +open Microsoft.FSharp.Quotations + +/// The type used to support queries against a database. Use 'sqlQuery { ... }' to use the query syntax. +type SqlQueryBuilder() = + + let qb = QueryBuilder() + + // The user should not have any need to access the underlying builder, + // but it needs to be public so we can inline the numeric operators (sumBy etc.) + + /// + /// The LINQ query builder underlying this SQL query builder. + /// + member __.QueryBuilder = qb + + /// + /// A method used to support queries against a database. Inputs to queries are implicitly wrapped by a call to one of the overloads of this method. + /// + member inline this.Source (source : IQueryable<_>) = this.QueryBuilder.Source source + + /// + /// A method used to support queries against a database. Inputs to queries are implicitly wrapped by a call to one of the overloads of this method. + /// + member inline this.Source (source : IEnumerable<_>) = this.QueryBuilder.Source source + + /// + /// A method used to support queries against a database. Projects each element of a sequence to another sequence and combines the resulting sequences into one sequence. + /// + member inline this.For (source, body) = this.QueryBuilder.For (source, body) + + /// + /// A method used to support queries against a database. Returns an empty sequence that has the specified type argument. + /// + member inline this.Zero () = this.QueryBuilder.Zero () + + /// + /// A method used to support queries against a database. Returns a sequence of length one that contains the specified value. + /// + member inline this.Yield (value) = this.QueryBuilder.Yield (value) + + /// + /// A method used to support queries against a database. Returns a sequence that contains the specified values. + /// + member inline this.YieldFrom (computation) = this.QueryBuilder.YieldFrom (computation) + + /// + /// A method used to support queries against a database. Indicates that the query should be passed as a quotation to the Run method. + /// + member inline this.Quote (query) = this.QueryBuilder.Quote (query) + + /// + /// A method used to support queries against a database. Runs the given quotation as a query using LINQ rules. + /// + member inline internal this.RunQueryAsValue (query: Expr<'T>) : 'T = this.QueryBuilder.Run (query) + + /// + /// A method used to support queries against a database. Runs the given quotation as a query using LINQ IQueryable rules. + /// + member inline internal this.RunQueryAsEnumerable (query: Expr>) : IEnumerable<'T> = this.QueryBuilder.Run (query) + + /// + /// A method used to support queries against a database. Runs the given quotation as a query using LINQ IEnumerable rules. + /// + member inline internal this.RunQueryAsQueryable (expression: Expr>) : IQueryable<'T> = this.QueryBuilder.Run(expression) + + /// + /// A method used to support queries against a database. Runs the given quotation as a query using LINQ IEnumerable rules. + /// + member inline this.Run (expression) = this.RunQueryAsQueryable (expression) + + /// A query operator that determines whether the selected elements contains a specified element. + /// + [] + member inline this.Contains (source, key) = this.QueryBuilder.Contains (source, key) + + /// A query operator that returns the number of selected elements. + /// + [] + member inline this.Count (source) = this.QueryBuilder.Count (source) + + ///// A query operator that selects the last element of those selected so far. + ///// + //[] + //member inline this.Last (source) = qb.Last (source) + + ///// A query operator that selects the last element of those selected so far, or a default value if no element is found. + ///// + //[] + //member inline this.LastOrDefault (source) = qb.LastOrDefault (source) + + /// A query operator that selects the single, specific element selected so far + /// + [] + member inline this.ExactlyOne (source) = this.QueryBuilder.ExactlyOne (source) + + /// A query operator that selects the single, specific element of those selected so far, or a default value if that element is not found. + /// + [] + member inline this.ExactlyOneOrDefault (source) = this.QueryBuilder.ExactlyOneOrDefault (source) + + /// A query operator that selects the first element of those selected so far, or a default value if the sequence contains no elements. + /// + [] + member inline this.HeadOrDefault (source) = this.QueryBuilder.HeadOrDefault (source) + + /// A query operator that projects each of the elements selected so far. + /// + [] + member inline this.Select (source, [] projection) = this.QueryBuilder.Select (source, projection) + + /// A query operator that selects those elements based on a specified predicate. + /// + [] + member inline this.Where (source, [] predicate) = this.QueryBuilder.Where (source, predicate) + + /// A query operator that selects a value for each element selected so far and returns the minimum resulting value. + /// + [] + member inline this.MinBy (source, [] valueSelector) = this.QueryBuilder.MinBy (source, valueSelector) + + /// A query operator that selects a value for each element selected so far and returns the maximum resulting value. + /// + [] + member inline this.MaxBy (source, [] valueSelector) = this.QueryBuilder.MaxBy (source, valueSelector) + + /// A query operator that groups the elements selected so far according to a specified key selector. + /// + [] + member inline this.GroupBy (source, [] keySelector) = this.QueryBuilder.GroupBy (source, keySelector) + + /// A query operator that sorts the elements selected so far in ascending order by the given sorting key. + /// + [] + member inline this.SortBy (source, [] keySelector) = this.QueryBuilder.SortBy (source, keySelector) + + /// A query operator that sorts the elements selected so far in descending order by the given sorting key. + /// + [] + member inline this.SortByDescending (source, [] keySelector) = this.QueryBuilder.SortByDescending (source, keySelector) + + /// A query operator that performs a subsequent ordering of the elements selected so far in ascending order by the given sorting key. + /// This operator may only be used immediately after a 'sortBy', 'sortByDescending', 'thenBy' or 'thenByDescending', or their nullable variants. + /// + [] + member inline this.ThenBy (source, [] keySelector) = this.QueryBuilder.ThenBy (source, keySelector) + + /// A query operator that performs a subsequent ordering of the elements selected so far in descending order by the given sorting key. + /// This operator may only be used immediately after a 'sortBy', 'sortByDescending', 'thenBy' or 'thenByDescending', or their nullable variants. + /// + [] + member inline this.ThenByDescending (source, [] keySelector) = this.QueryBuilder.ThenByDescending (source, keySelector) + + ///// A query operator that selects a value for each element selected so far and groups the elements by the given key. + ///// + //[] + //member inline this.GroupValBy<'T,'Key,'Value,'Q when 'Key : equality> (source, [] resultSelector, [] keySelector) = qb.GroupValBy<'T,'Key,'Value,'Q> (source, resultSelector, keySelector) + + /// A query operator that correlates two sets of selected values based on matching keys. + /// Normal usage is 'join y in elements2 on (key1 (source) = key2)'. (source) + /// + [] + member inline this.Join (outerSource, innerSource, outerKeySelector, innerKeySelector, resultSelector) = this.QueryBuilder.Join (outerSource, innerSource, outerKeySelector, innerKeySelector, resultSelector) + + ///// A query operator that correlates two sets of selected values based on matching keys and groups the results. + ///// Normal usage is 'groupJoin y in elements2 on (key1 (source) = key2) (source) into group'. + ///// + //[] + //member inline this.GroupJoin (outerSource, innerSource, outerKeySelector, innerKeySelector, resultSelector) = qb.GroupJoin (outerSource, innerSource, outerKeySelector, innerKeySelector, resultSelector) + + ///// A query operator that correlates two sets of selected values based on matching keys and groups the results. + ///// If any group is empty, a group with a single default value is used instead. + ///// Normal usage is 'leftOuterJoin y in elements2 on (key1 (source) = key2) (source) into group'. + ///// + //[] + //member inline this.LeftOuterJoin (outerSource, innerSource, outerKeySelector, innerKeySelector, resultSelector) = qb.LeftOuterJoin (outerSource, innerSource, outerKeySelector, innerKeySelector, resultSelector) + + /// A query operator that selects a nullable value for each element selected so far and returns the sum of these values. + /// If any nullable does not have a value, it is ignored. + /// + [] + member inline this.SumByNullable (source, [] valueSelector) = this.QueryBuilder.SumByNullable (source, valueSelector) + + /// A query operator that selects a nullable value for each element selected so far and returns the minimum of these values. + /// If any nullable does not have a value, it is ignored. + /// + [] + member inline this.MinByNullable (source, [] valueSelector) = this.QueryBuilder.MinByNullable (source, valueSelector) + + /// A query operator that selects a nullable value for each element selected so far and returns the maximum of these values. + /// If any nullable does not have a value, it is ignored. + /// + [] + member inline this.MaxByNullable (source, [] valueSelector) = this.QueryBuilder.MaxByNullable (source, valueSelector) + + /// A query operator that selects a nullable value for each element selected so far and returns the average of these values. + /// If any nullable does not have a value, it is ignored. + /// + [] + member inline this.AverageByNullable (source, [] projection) = this.QueryBuilder.AverageByNullable (source, projection) + + + /// A query operator that selects a value for each element selected so far and returns the average of these values. + /// + [] + member inline this.AverageBy (source, [] projection) = this.QueryBuilder.AverageBy (source, projection) + + + /// A query operator that selects distinct elements from the elements selected so far. + /// + [] + member inline this.Distinct (source) = this.QueryBuilder.Distinct (source) + + /// A query operator that determines whether any element selected so far satisfies a condition. + /// + [] + member inline this.Exists (source, [] predicate) = this.QueryBuilder.Exists (source, predicate) + + /// A query operator that selects the first element selected so far that satisfies a specified condition. + /// + [] + member inline this.Find (source, [] predicate) = this.QueryBuilder.Find (source, predicate) + + + /// A query operator that determines whether all elements selected so far satisfies a condition. + /// + [] + member inline this.All (source, [] predicate) = this.QueryBuilder.All (source, predicate) + + /// A query operator that selects the first element from those selected so far. + /// + [] + member inline this.Head (source) = this.QueryBuilder.Head (source) + + /// A query operator that selects the element at a specified index amongst those selected so far. + /// + [] + member inline this.Nth (source, index) = this.QueryBuilder.Nth (source, index) + + /// A query operator that bypasses a specified number of the elements selected so far and selects the remaining elements. + /// + [] + member inline this.Skip (source, count) = this.QueryBuilder.Skip (source, count) + + ///// A query operator that bypasses elements in a sequence as long as a specified condition is true and then selects the remaining elements. + ///// + //[] + //member inline this.SkipWhile (source, predicate) = qb.SkipWhile (source, predicate) + + /// A query operator that selects a value for each element selected so far and returns the sum of these values. + /// + [] + member inline this.SumBy (source, [] projection) = this.QueryBuilder.SumBy (source, projection) + + /// A query operator that selects a specified number of contiguous elements from those selected so far. + /// + [] + member inline this.Take (source, count) = this.QueryBuilder.Take (source, count) + + ///// A query operator that selects elements from a sequence as long as a specified condition is true, and then skips the remaining elements. + ///// + //[] + //member inline this.TakeWhile (source, predicate) = qb.TakeWhile (source, predicate) + + /// A query operator that sorts the elements selected so far in ascending order by the given nullable sorting key. + /// + [] + member inline this.SortByNullable (source, [] keySelector) = this.QueryBuilder.SortByNullable (source, keySelector) + + /// A query operator that sorts the elements selected so far in descending order by the given nullable sorting key. + /// + [] + member inline this.SortByNullableDescending (source, [] keySelector) = this.QueryBuilder.SortByNullableDescending (source, keySelector) + + /// A query operator that performs a subsequent ordering of the elements selected so far in ascending order by the given nullable sorting key. + /// This operator may only be used immediately after a 'sortBy', 'sortByDescending', 'thenBy' or 'thenByDescending', or their nullable variants. + /// + [] + member inline this.ThenByNullable (source, [] keySelector) = this.QueryBuilder.ThenByNullable (source, keySelector) + + /// A query operator that performs a subsequent ordering of the elements selected so far in descending order by the given nullable sorting key. + /// This operator may only be used immediately after a 'sortBy', 'sortByDescending', 'thenBy' or 'thenByDescending', or their nullable variants. + /// + [] + member inline this.ThenByNullableDescending (source, [] keySelector) = this.QueryBuilder.ThenByNullableDescending (source, keySelector) + + // WIP: Can we turn the (!!) operator into 'naturalJoin'? + + /// A query operator that correlates two sets of selected values based on a SQL foreign key relationship'. + /// + [] + member inline this.NaturalJoin (source) = !!source + +[] +module ExtraTopLevelOperators = + /// Builds a SQL query using query syntax and operators + let sqlQuery = SqlQueryBuilder() + + [] + module LowPriority = + type SqlQueryBuilder with + + /// + /// A method used to support the F# query syntax. Runs the given quotation as a query using LINQ rules. + /// + [] + member inline this.Run query = this.RunQueryAsValue query + + [] + module HighPriority = + type SqlQueryBuilder with + + /// + /// A method used to support the F# query syntax. Runs the given quotation as a query using LINQ IEnumerable rules. + /// + [] + member inline this.Run query = this.RunQueryAsEnumerable query diff --git a/tests/SqlProvider.Tests/CrudTests.fs b/tests/SqlProvider.Tests/CrudTests.fs index 501b7ef56..5c05ed4a3 100644 --- a/tests/SqlProvider.Tests/CrudTests.fs +++ b/tests/SqlProvider.Tests/CrudTests.fs @@ -36,8 +36,10 @@ let ``Can create and delete an entity``() = let dc = sql.GetDataContext() let originalCustomers = - query { for cust in dc.Main.Customers do - select cust } + sqlQuery { + for cust in dc.Main.Customers do + select cust + } |> Seq.toList createCustomer dc |> ignore @@ -45,8 +47,10 @@ let ``Can create and delete an entity``() = dc.SubmitUpdates() let newCustomers = - query { for cust in dc.Main.Customers do - select cust } + sqlQuery { + for cust in dc.Main.Customers do + select cust + } |> Seq.toList let created = @@ -62,8 +66,10 @@ let ``Can create, update and delete an entity``() = let dc = sql.GetDataContext() let originalCustomers = - query { for cust in dc.Main.Customers do - select cust } + sqlQuery { + for cust in dc.Main.Customers do + select cust + } |> Seq.toList let ent = createCustomer dc @@ -80,8 +86,10 @@ let ``Can create, update and delete an entity``() = let dc2 = sql.GetDataContext() let newCustomers = - query { for cust in dc2.Main.Customers do - select cust } + sqlQuery { + for cust in dc2.Main.Customers do + select cust + } |> Seq.toList let created = @@ -95,8 +103,10 @@ let ``Can create, update and delete an entity``() = dc2.SubmitUpdates() let reallyDeleted = - query { for cust in dc2.Main.Customers do - select cust } + sqlQuery { + for cust in dc2.Main.Customers do + select cust + } |> Seq.toList Assert.AreEqual(originalCustomers.Length, reallyDeleted.Length) @@ -112,9 +122,11 @@ let ``Can persist a blob``() = dc.SubmitUpdates() let reloadedEntity = - query { for image in dc.Main.Pictures do - where (image.Id = savedEntity.Id) - head } + sqlQuery { + for image in dc.Main.Pictures do + where (image.Id = savedEntity.Id) + head + } Assert.That( reloadedEntity.Image, Is.EqualTo(imageBytes)) @@ -132,9 +144,10 @@ let ``Can enlist in a transaction scope and rollback changes without complete``( let dc2 = sql.GetDataContext() let created = - query { for cust in dc2.Main.Customers do - where (cust.CustomerId = "SQLPROVIDER") - select cust + sqlQuery { + for cust in dc2.Main.Customers do + where (cust.CustomerId = "SQLPROVIDER") + select cust } |> Seq.toList diff --git a/tests/SqlProvider.Tests/QueryTests.fs b/tests/SqlProvider.Tests/QueryTests.fs index eff8b0f41..45a044b80 100644 --- a/tests/SqlProvider.Tests/QueryTests.fs +++ b/tests/SqlProvider.Tests/QueryTests.fs @@ -27,7 +27,7 @@ let isMono = Type.GetType ("Mono.Runtime") <> null let ``simple select with contains query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select cust.CustomerId contains "ALFKI" @@ -38,7 +38,7 @@ let ``simple select with contains query``() = let ``simple select with contains query with where``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.City <> "") select cust.CustomerId @@ -50,7 +50,7 @@ let ``simple select with contains query with where``() = let ``simple select with contains query when not exists``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select cust.CustomerId contains "ALFKI2" @@ -61,7 +61,7 @@ let ``simple select with contains query when not exists``() = let ``simple select with count``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select cust.CustomerId count @@ -72,7 +72,7 @@ let ``simple select with count``() = let ``simple select with last``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do sortBy cust.CustomerId select cust.CustomerId @@ -84,7 +84,7 @@ let ``simple select with last``() = let ``simple select with last or default when not exists``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId = "ZZZZ") select cust.CustomerId @@ -96,7 +96,7 @@ let ``simple select with last or default when not exists``() = let ``simple exists when exists``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do exists (cust.CustomerId = "WOLZA") } @@ -106,7 +106,7 @@ let ``simple exists when exists``() = let ``simple select with last or default when exists``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId = "WOLZA") select cust.CustomerId @@ -118,7 +118,7 @@ let ``simple select with last or default when exists``() = let ``simple select with exactly one``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId = "ALFKI") select cust.CustomerId @@ -130,7 +130,7 @@ let ``simple select with exactly one``() = let ``simple select with exactly one when not exists``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId = "ZZZZ") select cust.CustomerId @@ -142,7 +142,7 @@ let ``simple select with exactly one when not exists``() = let ``simple select with head``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId = "ALFKI") select cust.CustomerId @@ -154,7 +154,7 @@ let ``simple select with head``() = let ``simple select with head or Default``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId = "ALFKI") select cust.CustomerId @@ -166,7 +166,7 @@ let ``simple select with head or Default``() = let ``simple select with head or Default when not exists``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId = "ZZZZ") select cust.CustomerId @@ -178,7 +178,7 @@ let ``simple select with head or Default when not exists``() = let ``simple select query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select cust } |> Seq.toArray @@ -189,7 +189,7 @@ let ``simple select query``() = let ``simplest select query let temp``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do let y = cust.City select cust.Address @@ -201,7 +201,7 @@ let ``simplest select query let temp``() = let ``simple select query let temp nested``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do let y1 = cust.Address let y2 = cust.City @@ -214,7 +214,7 @@ let ``simple select query let temp nested``() = let ``simple select query let temp``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do let y = cust.City + "test" select y @@ -229,7 +229,7 @@ let ``simple select query let temp``() = let ``simple select query let where``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do let y = cust.City + "test" where (cust.Address <> "") @@ -243,7 +243,7 @@ let ``simple select query let where``() = let ``simple select query let temp used in where``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do let y = cust.City + "test" where (y <> "") @@ -258,7 +258,7 @@ let ``simple select query let temp used in where``() = let ``simple select query with operations in select``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select (cust.Country + " " + cust.Address + (1).ToString()) } |> Seq.toArray @@ -269,7 +269,7 @@ let ``simple select query with operations in select``() = let ``simple select where query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId = "ALFKI") select cust @@ -284,7 +284,7 @@ let ``simple select where query``() = let ``simple select where null query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId <> null) select cust @@ -299,7 +299,7 @@ let ``simple select where null query``() = let ``simple select where query right side``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where ("ALFKI" = cust.CustomerId) select cust @@ -313,7 +313,7 @@ let ``simple select where query right side``() = let ``simple select where query between col properties``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for order in dc.Main.Orders do where (order.ShippedDate > order.RequiredDate) select (order.ShippedDate, order.RequiredDate) @@ -326,7 +326,7 @@ let ``simple select where query between col properties``() = let ``simple nth query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select cust nth 4 @@ -338,7 +338,7 @@ let ``simple nth query``() = let ``simple select where not query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (not(cust.CustomerId = "ALFKI")) select cust @@ -352,7 +352,7 @@ let ``simple select where in query``() = let dc = sql.GetDataContext() let arr = ["ALFKI"; "ANATR"; "AROUT"] let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (arr.Contains(cust.CustomerId)) select cust.CustomerId @@ -369,7 +369,7 @@ let ``simple select where not-in query``() = let dc = sql.GetDataContext() let arr = ["ALFKI"; "ANATR"; "AROUT"] let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (not(arr.Contains(cust.CustomerId))) select cust.CustomerId @@ -385,14 +385,14 @@ let ``simple select where in queryable query``() = let dc = sql.GetDataContext() let query1 = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.City="London") select cust.CustomerId } let query2 = - query { + sqlQuery { for cust in dc.Main.Customers do where (query1.Contains(cust.CustomerId)) select cust.CustomerId @@ -409,7 +409,7 @@ let ``simple select where in query custom syntax``() = let dc = sql.GetDataContext() let arr = ["ALFKI"; "ANATR"; "AROUT"] let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId |=| arr) select cust.CustomerId @@ -423,7 +423,7 @@ let ``simple select where in query custom syntax``() = let ``simple select where like query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId.Contains("a")) select cust.CustomerId @@ -435,7 +435,7 @@ let ``simple select where like query``() = let ``simple select where query with operations in where``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId = "ALFKI" && (cust.City.StartsWith("B"))) select cust @@ -449,7 +449,7 @@ let ``simple select where query with operations in where``() = let ``simple select query with minBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for ord in dc.Main.OrderDetails do minBy (decimal ord.Discount) } @@ -459,7 +459,7 @@ let ``simple select query with minBy``() = let ``simple select query with minBy2``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for ord in dc.Main.OrderDetails do minBy (ord.Discount) } @@ -470,7 +470,7 @@ let ``simple select query with minBy2``() = let ``simple select query with minBy DateTime``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for emp in dc.Main.Employees do minBy (emp.BirthDate) } @@ -480,9 +480,9 @@ let ``simple select query with minBy DateTime``() = let ``simple select query with sumBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for od in dc.Main.OrderDetails do - sumBy od.UnitPrice + sumBy (od.UnitPrice) } Assert.Greater(56501m, qry) Assert.Less(56499m, qry) @@ -491,7 +491,7 @@ let ``simple select query with sumBy``() = let ``simple select query with averageBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for od in dc.Main.OrderDetails do averageBy od.UnitPrice } @@ -502,7 +502,7 @@ let ``simple select query with averageBy``() = let ``simplest select query with groupBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do groupBy cust.City } |> Seq.toArray @@ -513,7 +513,7 @@ let ``simplest select query with groupBy``() = let ``simple select query with groupBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do groupBy cust.City into c select (c.Key, c.Count()) @@ -526,7 +526,7 @@ let ``simple select query with groupBy``() = let ``simple select query with groupBy and then sort``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for order in dc.Main.Orders do groupBy (order.ShipCity) into ts where (ts.Count() > 1) @@ -544,7 +544,7 @@ let ``simple select query with groupBy and then sort``() = let ``simple select query with groupBy multiple columns``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for p in dc.Main.Products do groupBy (p.ReorderLevel, p.CategoryId) into c select (c.Key, c.Sum(fun i -> i.UnitPrice)) @@ -556,7 +556,7 @@ let ``simple select query with groupBy multiple columns``() = let ``simple select query with groupBy sum``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for od in dc.Main.OrderDetails do groupBy od.ProductId into p select (p.Key, p.Sum(fun f -> f.UnitPrice), p.Sum(fun f -> f.Discount)) @@ -572,7 +572,7 @@ let ``simple select query with groupBy sum``() = let ``simple select query with groupBy having count``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do groupBy cust.City into c where (c.Count() > 1) @@ -587,7 +587,7 @@ let ``simple select query with groupBy having count``() = let ``simple select query with groupBy where and having``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for emp in dc.Main.Employees do where (emp.Country = "USA") groupBy emp.City into grp @@ -605,7 +605,7 @@ let ``simple select query with groupBy where and having``() = let ``simple select query with groupBy having key``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do groupBy cust.City into c where (c.Key = "London") @@ -619,7 +619,7 @@ let ``simple select query with groupBy having key``() = let ``simple select query with groupBy date``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for emp in dc.Main.Employees do groupBy emp.BirthDate into e select (e.Key, e.Count()) @@ -631,7 +631,7 @@ let ``simple select query with groupBy date``() = let ``simple select query with groupBy2``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.City = "London") groupBy (cust.Country, cust.City) into c @@ -644,7 +644,7 @@ let ``simple select query with groupBy2``() = let ``simple select query with groupValBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do groupValBy cust.ContactTitle cust.City into g select (g.Key, g.Count()) @@ -655,10 +655,10 @@ let ``simple select query with groupValBy``() = let ``complex select query with groupValBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do groupValBy (cust.ContactTitle, Int32.Parse(cust.PostalCode)) (cust.PostalCode, cust.City) into g - let maxPlusOne = 1 + query {for i in g do sumBy (snd i) } + let maxPlusOne = 1 + sqlQuery {for i in g do sumBy (snd i) } select (snd(g.Key), maxPlusOne) } |> dict Assert.IsNotEmpty(qry) @@ -667,7 +667,7 @@ let ``complex select query with groupValBy``() = let ``simple if query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do if cust.Country = "UK" then select cust.City } |> Seq.toArray @@ -682,7 +682,7 @@ let ``simple select query with case``() = // Actual: SELECT [cust].[Country] as 'Country',[cust].[City] as 'City' FROM main.Customers as [cust] let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select (if cust.Country = "UK" then (cust.City) else ("Outside UK")) @@ -693,7 +693,7 @@ let ``simple select query with case``() = let ``simple select and sort query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do sortBy cust.City select cust.City @@ -706,7 +706,7 @@ let ``simple select and sort query``() = let ``simple select and sort desc query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do sortByDescending cust.City select cust.City @@ -719,7 +719,7 @@ let ``simple select and sort desc query``() = let ``simple select and sort query with then by query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do sortBy cust.Country thenBy cust.City @@ -733,7 +733,7 @@ let ``simple select and sort query with then by query``() = let ``simple select and sort query with then by desc query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do sortBy cust.Country thenByDescending cust.City @@ -747,7 +747,7 @@ let ``simple select and sort query with then by desc query``() = let ``simple sort query with lambda cast to IComparable``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do sortBy ((fun (c : sql.dataContext.``main.CustomersEntity``) -> c.CustomerId :> IComparable) cust) select cust.City @@ -761,7 +761,7 @@ let ``simple sort query with lambda cast to IComparable``() = let ``simple sort query with then by desc query with lambda cast to IComparable``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do sortBy cust.CompanyName thenByDescending ((fun (c : sql.dataContext.``main.CustomersEntity``) -> c.CustomerId :> IComparable) cust) @@ -776,7 +776,7 @@ let ``simple sort query with then by desc query with lambda cast to IComparable` let ``simple select query with join``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do join order in dc.Main.Orders on (cust.CustomerId = order.CustomerId) select (cust.CustomerId, order.OrderDate) @@ -796,7 +796,7 @@ let ``simple select query with join``() = let ``simple select query with join and then groupBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do join order in dc.Main.Orders on (cust.CustomerId = order.CustomerId) groupBy cust.City into c @@ -810,7 +810,7 @@ let ``simple select query with join and then groupBy``() = let ``simple select query with groupBy and then join``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do groupBy cust.City into c join order in dc.Main.Orders on (c.Key = order.ShipCity) @@ -825,7 +825,7 @@ let ``simple select query with groupBy and then join``() = let ``simple select query with join multi columns``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do join order in dc.Main.Orders on ((cust.CustomerId, cust.CustomerId) = (order.CustomerId, order.CustomerId)) select (cust.CustomerId, order.OrderDate) @@ -845,7 +845,7 @@ let ``simple select query with join multi columns``() = let ``simple select query with join using relationships``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do for order in cust.``main.Orders by CustomerID`` do select (cust.CustomerId, order.OrderDate) @@ -864,7 +864,7 @@ let ``simple select query with join using relationships``() = let ``simple select query with group join``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do groupJoin ord in dc.Main.Orders on (cust.CustomerId = ord.CustomerId) into g for order in g do @@ -889,7 +889,7 @@ let ``simple select query with group join``() = let ``simple select query with multiple joins on relationships``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do for order in cust.``main.Orders by CustomerID`` do for orderDetail in order.``main.OrderDetails by OrderID`` do @@ -913,7 +913,7 @@ let ``simple select query with multiple joins on relationships``() = let ``simple select query with left outer join``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do leftOuterJoin order in dc.Main.Orders on (cust.CustomerId = order.CustomerId) into result for order in result.DefaultIfEmpty() do @@ -933,7 +933,7 @@ let ``simple select query with left outer join``() = let ``simple sumBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for od in dc.Main.OrderDetails do sumBy od.UnitPrice } @@ -943,7 +943,7 @@ let ``simple sumBy``() = let ``simple async sum``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for od in dc.Main.OrderDetails do select od.UnitPrice } |> Seq.sumAsync |> Async.RunSynchronously @@ -953,7 +953,7 @@ let ``simple async sum``() = let ``simple averageBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for od in dc.Main.OrderDetails do averageBy od.UnitPrice } @@ -963,7 +963,7 @@ let ``simple averageBy``() = let ``simple averageByNullable``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for od in dc.Main.OrderDetails do averageByNullable (System.Nullable(od.UnitPrice)) } @@ -973,7 +973,7 @@ let ``simple averageByNullable``() = let ``simple select with distinct``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select cust.City distinct @@ -987,7 +987,7 @@ let ``simple select with distinct``() = let ``simple select with skip``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select cust.City skip 5 @@ -1002,7 +1002,7 @@ let ``simple select with skip``() = let ``simple select with take``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select cust.City take 5 @@ -1017,7 +1017,7 @@ let ``simple select with take``() = let ``simple select query with all``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for ord in dc.Main.OrderDetails do all (ord.UnitPrice > 0m) } @@ -1027,7 +1027,7 @@ let ``simple select query with all``() = let ``simple select query with all false``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for ord in dc.Main.OrderDetails do all (ord.UnitPrice > 10m) } @@ -1037,7 +1037,7 @@ let ``simple select query with all false``() = let ``simple select query with find``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for ord in dc.Main.OrderDetails do find (ord.UnitPrice > 10m) } @@ -1051,7 +1051,7 @@ type Dummy<'t> = D of 't let ``simple select into a generic type`` () = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for emp in dc.Main.Customers do select (D {First=emp.ContactName}) } |> Seq.toList @@ -1066,7 +1066,7 @@ let ``simple select into a generic type with pipe`` () = else let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for emp in dc.Main.Customers do select ({First=emp.ContactName} |> D) } |> Seq.toList @@ -1084,7 +1084,7 @@ let ``simple select with bool outside query``() = let myCond4 = 4 let qry = - query { + sqlQuery { for cust in dc.Main.Customers do // Simple booleans outside queries are supported: where (((myCond1 && myCond1=true) && cust.City="Helsinki" || myCond1) || cust.City="London") @@ -1106,7 +1106,7 @@ let ``simple select with bool outside query2``() = let myCond4 = 4 let qry = - query { + sqlQuery { for cust in dc.Main.Customers do // Simple booleans outside queries are supported: where (myCond4 > 3 || (myCond2 && cust.Address="test" && not(myCond2))) @@ -1122,7 +1122,7 @@ let ``simple select query async``() = let task = async { let! asyncquery = - query { + sqlQuery { for cust in dc.Main.Customers do select cust } |> Seq.executeQueryAsync @@ -1137,7 +1137,7 @@ type sqlOption = SqlDataProvider Seq.toList let inlogics = - query { + sqlQuery { for cust in enumtest do groupBy cust.City into c select (c.Key, c.Count()) } |> Seq.toArray |> Array.sortBy (fun (k,v) -> k ) let groupqry = - query { + sqlQuery { for cust in dc.Main.Customers do groupBy cust.City into c select (c.Key, c.Count()) @@ -1263,7 +1263,7 @@ let ``verify groupBy results``() = [] let ``simple delete where query``() = let dc = sql.GetDataContext() - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.City = "Atlantis" || cust.CompanyName = "Home") } |> Seq.``delete all items from single table`` @@ -1274,7 +1274,7 @@ let ``simple delete where query``() = let ``simple left join``() = let dc = sqlOption.GetDataContext() let qry = - query { + sqlQuery { for o in dc.Main.Orders do for c in (!!) o.``main.Customers by CustomerID`` do select (o.CustomerId, c.CustomerId) From a759ba42b2d902366010c8b8e0132e584e9e18f0 Mon Sep 17 00:00:00 2001 From: Stefano Pian Date: Fri, 29 Sep 2017 17:19:38 +0200 Subject: [PATCH 2/4] Add sqlQuery computation expression --- src/SQLProvider/SqlProvider.fsproj | 1 + src/SQLProvider/SqlQueryBuilder.fs | 321 ++++++++++++++++++++++++++ tests/SqlProvider.Tests/CrudTests.fs | 45 ++-- tests/SqlProvider.Tests/QueryTests.fs | 190 +++++++-------- 4 files changed, 446 insertions(+), 111 deletions(-) create mode 100644 src/SQLProvider/SqlQueryBuilder.fs diff --git a/src/SQLProvider/SqlProvider.fsproj b/src/SQLProvider/SqlProvider.fsproj index ad1085001..d9e5b465b 100644 --- a/src/SQLProvider/SqlProvider.fsproj +++ b/src/SQLProvider/SqlProvider.fsproj @@ -72,6 +72,7 @@ ExpressionOptimizer.fs + diff --git a/src/SQLProvider/SqlQueryBuilder.fs b/src/SQLProvider/SqlQueryBuilder.fs new file mode 100644 index 000000000..57e1bd270 --- /dev/null +++ b/src/SQLProvider/SqlQueryBuilder.fs @@ -0,0 +1,321 @@ +namespace FSharp.Data.Sql + +open System.Linq +open System.Collections.Generic +open Microsoft.FSharp.Linq +open Microsoft.FSharp.Quotations + +/// The type used to support queries against a database. Use 'sqlQuery { ... }' to use the query syntax. +type SqlQueryBuilder() = + + let qb = QueryBuilder() + + // The user should not have any need to access the underlying builder, + // but it needs to be public so we can inline the numeric operators (sumBy etc.) + + /// + /// The LINQ query builder underlying this SQL query builder. + /// + member __.QueryBuilder = qb + + /// + /// A method used to support queries against a database. Inputs to queries are implicitly wrapped by a call to one of the overloads of this method. + /// + member inline this.Source (source : IQueryable<_>) = this.QueryBuilder.Source source + + /// + /// A method used to support queries against a database. Inputs to queries are implicitly wrapped by a call to one of the overloads of this method. + /// + member inline this.Source (source : IEnumerable<_>) = this.QueryBuilder.Source source + + /// + /// A method used to support queries against a database. Projects each element of a sequence to another sequence and combines the resulting sequences into one sequence. + /// + member inline this.For (source, body) = this.QueryBuilder.For (source, body) + + /// + /// A method used to support queries against a database. Returns an empty sequence that has the specified type argument. + /// + member inline this.Zero () = this.QueryBuilder.Zero () + + /// + /// A method used to support queries against a database. Returns a sequence of length one that contains the specified value. + /// + member inline this.Yield (value) = this.QueryBuilder.Yield (value) + + /// + /// A method used to support queries against a database. Returns a sequence that contains the specified values. + /// + member inline this.YieldFrom (computation) = this.QueryBuilder.YieldFrom (computation) + + /// + /// A method used to support queries against a database. Indicates that the query should be passed as a quotation to the Run method. + /// + member inline this.Quote (query) = this.QueryBuilder.Quote (query) + + /// + /// A method used to support queries against a database. Runs the given quotation as a query using LINQ rules. + /// + member inline internal this.RunQueryAsValue (query: Expr<'T>) : 'T = this.QueryBuilder.Run (query) + + /// + /// A method used to support queries against a database. Runs the given quotation as a query using LINQ IQueryable rules. + /// + member inline internal this.RunQueryAsEnumerable (query: Expr>) : IEnumerable<'T> = this.QueryBuilder.Run (query) + + /// + /// A method used to support queries against a database. Runs the given quotation as a query using LINQ IEnumerable rules. + /// + member inline internal this.RunQueryAsQueryable (expression: Expr>) : IQueryable<'T> = this.QueryBuilder.Run(expression) + + /// + /// A method used to support queries against a database. Runs the given quotation as a query using LINQ IEnumerable rules. + /// + member inline this.Run (expression) = this.RunQueryAsQueryable (expression) + + /// A query operator that determines whether the selected elements contains a specified element. + /// + [] + member inline this.Contains (source, key) = this.QueryBuilder.Contains (source, key) + + /// A query operator that returns the number of selected elements. + /// + [] + member inline this.Count (source) = this.QueryBuilder.Count (source) + + ///// A query operator that selects the last element of those selected so far. + ///// + //[] + //member inline this.Last (source) = qb.Last (source) + + ///// A query operator that selects the last element of those selected so far, or a default value if no element is found. + ///// + //[] + //member inline this.LastOrDefault (source) = qb.LastOrDefault (source) + + /// A query operator that selects the single, specific element selected so far + /// + [] + member inline this.ExactlyOne (source) = this.QueryBuilder.ExactlyOne (source) + + /// A query operator that selects the single, specific element of those selected so far, or a default value if that element is not found. + /// + [] + member inline this.ExactlyOneOrDefault (source) = this.QueryBuilder.ExactlyOneOrDefault (source) + + /// A query operator that selects the first element of those selected so far, or a default value if the sequence contains no elements. + /// + [] + member inline this.HeadOrDefault (source) = this.QueryBuilder.HeadOrDefault (source) + + /// A query operator that projects each of the elements selected so far. + /// + [] + member inline this.Select (source, [] projection) = this.QueryBuilder.Select (source, projection) + + /// A query operator that selects those elements based on a specified predicate. + /// + [] + member inline this.Where (source, [] predicate) = this.QueryBuilder.Where (source, predicate) + + /// A query operator that selects a value for each element selected so far and returns the minimum resulting value. + /// + [] + member inline this.MinBy (source, [] valueSelector) = this.QueryBuilder.MinBy (source, valueSelector) + + /// A query operator that selects a value for each element selected so far and returns the maximum resulting value. + /// + [] + member inline this.MaxBy (source, [] valueSelector) = this.QueryBuilder.MaxBy (source, valueSelector) + + /// A query operator that groups the elements selected so far according to a specified key selector. + /// + [] + member inline this.GroupBy (source, [] keySelector) = this.QueryBuilder.GroupBy (source, keySelector) + + /// A query operator that sorts the elements selected so far in ascending order by the given sorting key. + /// + [] + member inline this.SortBy (source, [] keySelector) = this.QueryBuilder.SortBy (source, keySelector) + + /// A query operator that sorts the elements selected so far in descending order by the given sorting key. + /// + [] + member inline this.SortByDescending (source, [] keySelector) = this.QueryBuilder.SortByDescending (source, keySelector) + + /// A query operator that performs a subsequent ordering of the elements selected so far in ascending order by the given sorting key. + /// This operator may only be used immediately after a 'sortBy', 'sortByDescending', 'thenBy' or 'thenByDescending', or their nullable variants. + /// + [] + member inline this.ThenBy (source, [] keySelector) = this.QueryBuilder.ThenBy (source, keySelector) + + /// A query operator that performs a subsequent ordering of the elements selected so far in descending order by the given sorting key. + /// This operator may only be used immediately after a 'sortBy', 'sortByDescending', 'thenBy' or 'thenByDescending', or their nullable variants. + /// + [] + member inline this.ThenByDescending (source, [] keySelector) = this.QueryBuilder.ThenByDescending (source, keySelector) + + ///// A query operator that selects a value for each element selected so far and groups the elements by the given key. + ///// + //[] + //member inline this.GroupValBy<'T,'Key,'Value,'Q when 'Key : equality> (source, [] resultSelector, [] keySelector) = qb.GroupValBy<'T,'Key,'Value,'Q> (source, resultSelector, keySelector) + + /// A query operator that correlates two sets of selected values based on matching keys. + /// Normal usage is 'join y in elements2 on (key1 (source) = key2)'. (source) + /// + [] + member inline this.Join (outerSource, innerSource, outerKeySelector, innerKeySelector, resultSelector) = this.QueryBuilder.Join (outerSource, innerSource, outerKeySelector, innerKeySelector, resultSelector) + + ///// A query operator that correlates two sets of selected values based on matching keys and groups the results. + ///// Normal usage is 'groupJoin y in elements2 on (key1 (source) = key2) (source) into group'. + ///// + //[] + //member inline this.GroupJoin (outerSource, innerSource, outerKeySelector, innerKeySelector, resultSelector) = qb.GroupJoin (outerSource, innerSource, outerKeySelector, innerKeySelector, resultSelector) + + ///// A query operator that correlates two sets of selected values based on matching keys and groups the results. + ///// If any group is empty, a group with a single default value is used instead. + ///// Normal usage is 'leftOuterJoin y in elements2 on (key1 (source) = key2) (source) into group'. + ///// + //[] + //member inline this.LeftOuterJoin (outerSource, innerSource, outerKeySelector, innerKeySelector, resultSelector) = qb.LeftOuterJoin (outerSource, innerSource, outerKeySelector, innerKeySelector, resultSelector) + + /// A query operator that selects a nullable value for each element selected so far and returns the sum of these values. + /// If any nullable does not have a value, it is ignored. + /// + [] + member inline this.SumByNullable (source, [] valueSelector) = this.QueryBuilder.SumByNullable (source, valueSelector) + + /// A query operator that selects a nullable value for each element selected so far and returns the minimum of these values. + /// If any nullable does not have a value, it is ignored. + /// + [] + member inline this.MinByNullable (source, [] valueSelector) = this.QueryBuilder.MinByNullable (source, valueSelector) + + /// A query operator that selects a nullable value for each element selected so far and returns the maximum of these values. + /// If any nullable does not have a value, it is ignored. + /// + [] + member inline this.MaxByNullable (source, [] valueSelector) = this.QueryBuilder.MaxByNullable (source, valueSelector) + + /// A query operator that selects a nullable value for each element selected so far and returns the average of these values. + /// If any nullable does not have a value, it is ignored. + /// + [] + member inline this.AverageByNullable (source, [] projection) = this.QueryBuilder.AverageByNullable (source, projection) + + + /// A query operator that selects a value for each element selected so far and returns the average of these values. + /// + [] + member inline this.AverageBy (source, [] projection) = this.QueryBuilder.AverageBy (source, projection) + + + /// A query operator that selects distinct elements from the elements selected so far. + /// + [] + member inline this.Distinct (source) = this.QueryBuilder.Distinct (source) + + /// A query operator that determines whether any element selected so far satisfies a condition. + /// + [] + member inline this.Exists (source, [] predicate) = this.QueryBuilder.Exists (source, predicate) + + /// A query operator that selects the first element selected so far that satisfies a specified condition. + /// + [] + member inline this.Find (source, [] predicate) = this.QueryBuilder.Find (source, predicate) + + + /// A query operator that determines whether all elements selected so far satisfies a condition. + /// + [] + member inline this.All (source, [] predicate) = this.QueryBuilder.All (source, predicate) + + /// A query operator that selects the first element from those selected so far. + /// + [] + member inline this.Head (source) = this.QueryBuilder.Head (source) + + /// A query operator that selects the element at a specified index amongst those selected so far. + /// + [] + member inline this.Nth (source, index) = this.QueryBuilder.Nth (source, index) + + /// A query operator that bypasses a specified number of the elements selected so far and selects the remaining elements. + /// + [] + member inline this.Skip (source, count) = this.QueryBuilder.Skip (source, count) + + ///// A query operator that bypasses elements in a sequence as long as a specified condition is true and then selects the remaining elements. + ///// + //[] + //member inline this.SkipWhile (source, predicate) = qb.SkipWhile (source, predicate) + + /// A query operator that selects a value for each element selected so far and returns the sum of these values. + /// + [] + member inline this.SumBy (source, [] projection) = this.QueryBuilder.SumBy (source, projection) + + /// A query operator that selects a specified number of contiguous elements from those selected so far. + /// + [] + member inline this.Take (source, count) = this.QueryBuilder.Take (source, count) + + ///// A query operator that selects elements from a sequence as long as a specified condition is true, and then skips the remaining elements. + ///// + //[] + //member inline this.TakeWhile (source, predicate) = qb.TakeWhile (source, predicate) + + /// A query operator that sorts the elements selected so far in ascending order by the given nullable sorting key. + /// + [] + member inline this.SortByNullable (source, [] keySelector) = this.QueryBuilder.SortByNullable (source, keySelector) + + /// A query operator that sorts the elements selected so far in descending order by the given nullable sorting key. + /// + [] + member inline this.SortByNullableDescending (source, [] keySelector) = this.QueryBuilder.SortByNullableDescending (source, keySelector) + + /// A query operator that performs a subsequent ordering of the elements selected so far in ascending order by the given nullable sorting key. + /// This operator may only be used immediately after a 'sortBy', 'sortByDescending', 'thenBy' or 'thenByDescending', or their nullable variants. + /// + [] + member inline this.ThenByNullable (source, [] keySelector) = this.QueryBuilder.ThenByNullable (source, keySelector) + + /// A query operator that performs a subsequent ordering of the elements selected so far in descending order by the given nullable sorting key. + /// This operator may only be used immediately after a 'sortBy', 'sortByDescending', 'thenBy' or 'thenByDescending', or their nullable variants. + /// + [] + member inline this.ThenByNullableDescending (source, [] keySelector) = this.QueryBuilder.ThenByNullableDescending (source, keySelector) + + // WIP: Can we turn the (!!) operator into 'naturalJoin'? + + /// A query operator that correlates two sets of selected values based on a SQL foreign key relationship'. + /// + [] + member inline this.NaturalJoin (source) = !!source + +[] +module ExtraTopLevelOperators = + /// Builds a SQL query using query syntax and operators + let sqlQuery = SqlQueryBuilder() + + [] + module LowPriority = + type SqlQueryBuilder with + + /// + /// A method used to support the F# query syntax. Runs the given quotation as a query using LINQ rules. + /// + [] + member inline this.Run query = this.RunQueryAsValue query + + [] + module HighPriority = + type SqlQueryBuilder with + + /// + /// A method used to support the F# query syntax. Runs the given quotation as a query using LINQ IEnumerable rules. + /// + [] + member inline this.Run query = this.RunQueryAsEnumerable query diff --git a/tests/SqlProvider.Tests/CrudTests.fs b/tests/SqlProvider.Tests/CrudTests.fs index 6dd288a94..6e60c4efe 100644 --- a/tests/SqlProvider.Tests/CrudTests.fs +++ b/tests/SqlProvider.Tests/CrudTests.fs @@ -42,8 +42,10 @@ let ``Can create and delete an entity``() = let dc = sql.GetDataContext() let originalCustomers = - query { for cust in dc.Main.Customers do - select cust } + sqlQuery { + for cust in dc.Main.Customers do + select cust + } |> Seq.toList createCustomer dc |> ignore @@ -51,8 +53,10 @@ let ``Can create and delete an entity``() = dc.SubmitUpdates() let newCustomers = - query { for cust in dc.Main.Customers do - select cust } + sqlQuery { + for cust in dc.Main.Customers do + select cust + } |> Seq.toList let created = @@ -68,8 +72,10 @@ let ``Can create, update and delete an entity``() = let dc = sql.GetDataContext() let originalCustomers = - query { for cust in dc.Main.Customers do - select cust } + sqlQuery { + for cust in dc.Main.Customers do + select cust + } |> Seq.toList let ent = createCustomer dc @@ -86,8 +92,10 @@ let ``Can create, update and delete an entity``() = let dc2 = sql.GetDataContext() let newCustomers = - query { for cust in dc2.Main.Customers do - select cust } + sqlQuery { + for cust in dc2.Main.Customers do + select cust + } |> Seq.toList let created = @@ -101,8 +109,10 @@ let ``Can create, update and delete an entity``() = dc2.SubmitUpdates() let reallyDeleted = - query { for cust in dc2.Main.Customers do - select cust } + sqlQuery { + for cust in dc2.Main.Customers do + select cust + } |> Seq.toList Assert.AreEqual(originalCustomers.Length, reallyDeleted.Length) @@ -118,9 +128,11 @@ let ``Can persist a blob``() = dc.SubmitUpdates() let reloadedEntity = - query { for image in dc.Main.Pictures do - where (image.Id = savedEntity.Id) - head } + sqlQuery { + for image in dc.Main.Pictures do + where (image.Id = savedEntity.Id) + head + } Assert.That( reloadedEntity.Image, Is.EqualTo(imageBytes)) @@ -138,9 +150,10 @@ let ``Can enlist in a transaction scope and rollback changes without complete``( let dc2 = sql.GetDataContext() let created = - query { for cust in dc2.Main.Customers do - where (cust.CustomerId = "SQLPROVIDER") - select cust + sqlQuery { + for cust in dc2.Main.Customers do + where (cust.CustomerId = "SQLPROVIDER") + select cust } |> Seq.toList diff --git a/tests/SqlProvider.Tests/QueryTests.fs b/tests/SqlProvider.Tests/QueryTests.fs index eff8b0f41..45a044b80 100644 --- a/tests/SqlProvider.Tests/QueryTests.fs +++ b/tests/SqlProvider.Tests/QueryTests.fs @@ -27,7 +27,7 @@ let isMono = Type.GetType ("Mono.Runtime") <> null let ``simple select with contains query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select cust.CustomerId contains "ALFKI" @@ -38,7 +38,7 @@ let ``simple select with contains query``() = let ``simple select with contains query with where``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.City <> "") select cust.CustomerId @@ -50,7 +50,7 @@ let ``simple select with contains query with where``() = let ``simple select with contains query when not exists``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select cust.CustomerId contains "ALFKI2" @@ -61,7 +61,7 @@ let ``simple select with contains query when not exists``() = let ``simple select with count``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select cust.CustomerId count @@ -72,7 +72,7 @@ let ``simple select with count``() = let ``simple select with last``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do sortBy cust.CustomerId select cust.CustomerId @@ -84,7 +84,7 @@ let ``simple select with last``() = let ``simple select with last or default when not exists``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId = "ZZZZ") select cust.CustomerId @@ -96,7 +96,7 @@ let ``simple select with last or default when not exists``() = let ``simple exists when exists``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do exists (cust.CustomerId = "WOLZA") } @@ -106,7 +106,7 @@ let ``simple exists when exists``() = let ``simple select with last or default when exists``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId = "WOLZA") select cust.CustomerId @@ -118,7 +118,7 @@ let ``simple select with last or default when exists``() = let ``simple select with exactly one``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId = "ALFKI") select cust.CustomerId @@ -130,7 +130,7 @@ let ``simple select with exactly one``() = let ``simple select with exactly one when not exists``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId = "ZZZZ") select cust.CustomerId @@ -142,7 +142,7 @@ let ``simple select with exactly one when not exists``() = let ``simple select with head``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId = "ALFKI") select cust.CustomerId @@ -154,7 +154,7 @@ let ``simple select with head``() = let ``simple select with head or Default``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId = "ALFKI") select cust.CustomerId @@ -166,7 +166,7 @@ let ``simple select with head or Default``() = let ``simple select with head or Default when not exists``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId = "ZZZZ") select cust.CustomerId @@ -178,7 +178,7 @@ let ``simple select with head or Default when not exists``() = let ``simple select query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select cust } |> Seq.toArray @@ -189,7 +189,7 @@ let ``simple select query``() = let ``simplest select query let temp``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do let y = cust.City select cust.Address @@ -201,7 +201,7 @@ let ``simplest select query let temp``() = let ``simple select query let temp nested``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do let y1 = cust.Address let y2 = cust.City @@ -214,7 +214,7 @@ let ``simple select query let temp nested``() = let ``simple select query let temp``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do let y = cust.City + "test" select y @@ -229,7 +229,7 @@ let ``simple select query let temp``() = let ``simple select query let where``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do let y = cust.City + "test" where (cust.Address <> "") @@ -243,7 +243,7 @@ let ``simple select query let where``() = let ``simple select query let temp used in where``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do let y = cust.City + "test" where (y <> "") @@ -258,7 +258,7 @@ let ``simple select query let temp used in where``() = let ``simple select query with operations in select``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select (cust.Country + " " + cust.Address + (1).ToString()) } |> Seq.toArray @@ -269,7 +269,7 @@ let ``simple select query with operations in select``() = let ``simple select where query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId = "ALFKI") select cust @@ -284,7 +284,7 @@ let ``simple select where query``() = let ``simple select where null query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId <> null) select cust @@ -299,7 +299,7 @@ let ``simple select where null query``() = let ``simple select where query right side``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where ("ALFKI" = cust.CustomerId) select cust @@ -313,7 +313,7 @@ let ``simple select where query right side``() = let ``simple select where query between col properties``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for order in dc.Main.Orders do where (order.ShippedDate > order.RequiredDate) select (order.ShippedDate, order.RequiredDate) @@ -326,7 +326,7 @@ let ``simple select where query between col properties``() = let ``simple nth query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select cust nth 4 @@ -338,7 +338,7 @@ let ``simple nth query``() = let ``simple select where not query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (not(cust.CustomerId = "ALFKI")) select cust @@ -352,7 +352,7 @@ let ``simple select where in query``() = let dc = sql.GetDataContext() let arr = ["ALFKI"; "ANATR"; "AROUT"] let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (arr.Contains(cust.CustomerId)) select cust.CustomerId @@ -369,7 +369,7 @@ let ``simple select where not-in query``() = let dc = sql.GetDataContext() let arr = ["ALFKI"; "ANATR"; "AROUT"] let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (not(arr.Contains(cust.CustomerId))) select cust.CustomerId @@ -385,14 +385,14 @@ let ``simple select where in queryable query``() = let dc = sql.GetDataContext() let query1 = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.City="London") select cust.CustomerId } let query2 = - query { + sqlQuery { for cust in dc.Main.Customers do where (query1.Contains(cust.CustomerId)) select cust.CustomerId @@ -409,7 +409,7 @@ let ``simple select where in query custom syntax``() = let dc = sql.GetDataContext() let arr = ["ALFKI"; "ANATR"; "AROUT"] let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId |=| arr) select cust.CustomerId @@ -423,7 +423,7 @@ let ``simple select where in query custom syntax``() = let ``simple select where like query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId.Contains("a")) select cust.CustomerId @@ -435,7 +435,7 @@ let ``simple select where like query``() = let ``simple select where query with operations in where``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.CustomerId = "ALFKI" && (cust.City.StartsWith("B"))) select cust @@ -449,7 +449,7 @@ let ``simple select where query with operations in where``() = let ``simple select query with minBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for ord in dc.Main.OrderDetails do minBy (decimal ord.Discount) } @@ -459,7 +459,7 @@ let ``simple select query with minBy``() = let ``simple select query with minBy2``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for ord in dc.Main.OrderDetails do minBy (ord.Discount) } @@ -470,7 +470,7 @@ let ``simple select query with minBy2``() = let ``simple select query with minBy DateTime``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for emp in dc.Main.Employees do minBy (emp.BirthDate) } @@ -480,9 +480,9 @@ let ``simple select query with minBy DateTime``() = let ``simple select query with sumBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for od in dc.Main.OrderDetails do - sumBy od.UnitPrice + sumBy (od.UnitPrice) } Assert.Greater(56501m, qry) Assert.Less(56499m, qry) @@ -491,7 +491,7 @@ let ``simple select query with sumBy``() = let ``simple select query with averageBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for od in dc.Main.OrderDetails do averageBy od.UnitPrice } @@ -502,7 +502,7 @@ let ``simple select query with averageBy``() = let ``simplest select query with groupBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do groupBy cust.City } |> Seq.toArray @@ -513,7 +513,7 @@ let ``simplest select query with groupBy``() = let ``simple select query with groupBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do groupBy cust.City into c select (c.Key, c.Count()) @@ -526,7 +526,7 @@ let ``simple select query with groupBy``() = let ``simple select query with groupBy and then sort``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for order in dc.Main.Orders do groupBy (order.ShipCity) into ts where (ts.Count() > 1) @@ -544,7 +544,7 @@ let ``simple select query with groupBy and then sort``() = let ``simple select query with groupBy multiple columns``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for p in dc.Main.Products do groupBy (p.ReorderLevel, p.CategoryId) into c select (c.Key, c.Sum(fun i -> i.UnitPrice)) @@ -556,7 +556,7 @@ let ``simple select query with groupBy multiple columns``() = let ``simple select query with groupBy sum``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for od in dc.Main.OrderDetails do groupBy od.ProductId into p select (p.Key, p.Sum(fun f -> f.UnitPrice), p.Sum(fun f -> f.Discount)) @@ -572,7 +572,7 @@ let ``simple select query with groupBy sum``() = let ``simple select query with groupBy having count``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do groupBy cust.City into c where (c.Count() > 1) @@ -587,7 +587,7 @@ let ``simple select query with groupBy having count``() = let ``simple select query with groupBy where and having``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for emp in dc.Main.Employees do where (emp.Country = "USA") groupBy emp.City into grp @@ -605,7 +605,7 @@ let ``simple select query with groupBy where and having``() = let ``simple select query with groupBy having key``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do groupBy cust.City into c where (c.Key = "London") @@ -619,7 +619,7 @@ let ``simple select query with groupBy having key``() = let ``simple select query with groupBy date``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for emp in dc.Main.Employees do groupBy emp.BirthDate into e select (e.Key, e.Count()) @@ -631,7 +631,7 @@ let ``simple select query with groupBy date``() = let ``simple select query with groupBy2``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.City = "London") groupBy (cust.Country, cust.City) into c @@ -644,7 +644,7 @@ let ``simple select query with groupBy2``() = let ``simple select query with groupValBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do groupValBy cust.ContactTitle cust.City into g select (g.Key, g.Count()) @@ -655,10 +655,10 @@ let ``simple select query with groupValBy``() = let ``complex select query with groupValBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do groupValBy (cust.ContactTitle, Int32.Parse(cust.PostalCode)) (cust.PostalCode, cust.City) into g - let maxPlusOne = 1 + query {for i in g do sumBy (snd i) } + let maxPlusOne = 1 + sqlQuery {for i in g do sumBy (snd i) } select (snd(g.Key), maxPlusOne) } |> dict Assert.IsNotEmpty(qry) @@ -667,7 +667,7 @@ let ``complex select query with groupValBy``() = let ``simple if query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do if cust.Country = "UK" then select cust.City } |> Seq.toArray @@ -682,7 +682,7 @@ let ``simple select query with case``() = // Actual: SELECT [cust].[Country] as 'Country',[cust].[City] as 'City' FROM main.Customers as [cust] let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select (if cust.Country = "UK" then (cust.City) else ("Outside UK")) @@ -693,7 +693,7 @@ let ``simple select query with case``() = let ``simple select and sort query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do sortBy cust.City select cust.City @@ -706,7 +706,7 @@ let ``simple select and sort query``() = let ``simple select and sort desc query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do sortByDescending cust.City select cust.City @@ -719,7 +719,7 @@ let ``simple select and sort desc query``() = let ``simple select and sort query with then by query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do sortBy cust.Country thenBy cust.City @@ -733,7 +733,7 @@ let ``simple select and sort query with then by query``() = let ``simple select and sort query with then by desc query``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do sortBy cust.Country thenByDescending cust.City @@ -747,7 +747,7 @@ let ``simple select and sort query with then by desc query``() = let ``simple sort query with lambda cast to IComparable``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do sortBy ((fun (c : sql.dataContext.``main.CustomersEntity``) -> c.CustomerId :> IComparable) cust) select cust.City @@ -761,7 +761,7 @@ let ``simple sort query with lambda cast to IComparable``() = let ``simple sort query with then by desc query with lambda cast to IComparable``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do sortBy cust.CompanyName thenByDescending ((fun (c : sql.dataContext.``main.CustomersEntity``) -> c.CustomerId :> IComparable) cust) @@ -776,7 +776,7 @@ let ``simple sort query with then by desc query with lambda cast to IComparable` let ``simple select query with join``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do join order in dc.Main.Orders on (cust.CustomerId = order.CustomerId) select (cust.CustomerId, order.OrderDate) @@ -796,7 +796,7 @@ let ``simple select query with join``() = let ``simple select query with join and then groupBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do join order in dc.Main.Orders on (cust.CustomerId = order.CustomerId) groupBy cust.City into c @@ -810,7 +810,7 @@ let ``simple select query with join and then groupBy``() = let ``simple select query with groupBy and then join``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do groupBy cust.City into c join order in dc.Main.Orders on (c.Key = order.ShipCity) @@ -825,7 +825,7 @@ let ``simple select query with groupBy and then join``() = let ``simple select query with join multi columns``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do join order in dc.Main.Orders on ((cust.CustomerId, cust.CustomerId) = (order.CustomerId, order.CustomerId)) select (cust.CustomerId, order.OrderDate) @@ -845,7 +845,7 @@ let ``simple select query with join multi columns``() = let ``simple select query with join using relationships``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do for order in cust.``main.Orders by CustomerID`` do select (cust.CustomerId, order.OrderDate) @@ -864,7 +864,7 @@ let ``simple select query with join using relationships``() = let ``simple select query with group join``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do groupJoin ord in dc.Main.Orders on (cust.CustomerId = ord.CustomerId) into g for order in g do @@ -889,7 +889,7 @@ let ``simple select query with group join``() = let ``simple select query with multiple joins on relationships``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do for order in cust.``main.Orders by CustomerID`` do for orderDetail in order.``main.OrderDetails by OrderID`` do @@ -913,7 +913,7 @@ let ``simple select query with multiple joins on relationships``() = let ``simple select query with left outer join``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do leftOuterJoin order in dc.Main.Orders on (cust.CustomerId = order.CustomerId) into result for order in result.DefaultIfEmpty() do @@ -933,7 +933,7 @@ let ``simple select query with left outer join``() = let ``simple sumBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for od in dc.Main.OrderDetails do sumBy od.UnitPrice } @@ -943,7 +943,7 @@ let ``simple sumBy``() = let ``simple async sum``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for od in dc.Main.OrderDetails do select od.UnitPrice } |> Seq.sumAsync |> Async.RunSynchronously @@ -953,7 +953,7 @@ let ``simple async sum``() = let ``simple averageBy``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for od in dc.Main.OrderDetails do averageBy od.UnitPrice } @@ -963,7 +963,7 @@ let ``simple averageBy``() = let ``simple averageByNullable``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for od in dc.Main.OrderDetails do averageByNullable (System.Nullable(od.UnitPrice)) } @@ -973,7 +973,7 @@ let ``simple averageByNullable``() = let ``simple select with distinct``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select cust.City distinct @@ -987,7 +987,7 @@ let ``simple select with distinct``() = let ``simple select with skip``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select cust.City skip 5 @@ -1002,7 +1002,7 @@ let ``simple select with skip``() = let ``simple select with take``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for cust in dc.Main.Customers do select cust.City take 5 @@ -1017,7 +1017,7 @@ let ``simple select with take``() = let ``simple select query with all``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for ord in dc.Main.OrderDetails do all (ord.UnitPrice > 0m) } @@ -1027,7 +1027,7 @@ let ``simple select query with all``() = let ``simple select query with all false``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for ord in dc.Main.OrderDetails do all (ord.UnitPrice > 10m) } @@ -1037,7 +1037,7 @@ let ``simple select query with all false``() = let ``simple select query with find``() = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for ord in dc.Main.OrderDetails do find (ord.UnitPrice > 10m) } @@ -1051,7 +1051,7 @@ type Dummy<'t> = D of 't let ``simple select into a generic type`` () = let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for emp in dc.Main.Customers do select (D {First=emp.ContactName}) } |> Seq.toList @@ -1066,7 +1066,7 @@ let ``simple select into a generic type with pipe`` () = else let dc = sql.GetDataContext() let qry = - query { + sqlQuery { for emp in dc.Main.Customers do select ({First=emp.ContactName} |> D) } |> Seq.toList @@ -1084,7 +1084,7 @@ let ``simple select with bool outside query``() = let myCond4 = 4 let qry = - query { + sqlQuery { for cust in dc.Main.Customers do // Simple booleans outside queries are supported: where (((myCond1 && myCond1=true) && cust.City="Helsinki" || myCond1) || cust.City="London") @@ -1106,7 +1106,7 @@ let ``simple select with bool outside query2``() = let myCond4 = 4 let qry = - query { + sqlQuery { for cust in dc.Main.Customers do // Simple booleans outside queries are supported: where (myCond4 > 3 || (myCond2 && cust.Address="test" && not(myCond2))) @@ -1122,7 +1122,7 @@ let ``simple select query async``() = let task = async { let! asyncquery = - query { + sqlQuery { for cust in dc.Main.Customers do select cust } |> Seq.executeQueryAsync @@ -1137,7 +1137,7 @@ type sqlOption = SqlDataProvider Seq.toList let inlogics = - query { + sqlQuery { for cust in enumtest do groupBy cust.City into c select (c.Key, c.Count()) } |> Seq.toArray |> Array.sortBy (fun (k,v) -> k ) let groupqry = - query { + sqlQuery { for cust in dc.Main.Customers do groupBy cust.City into c select (c.Key, c.Count()) @@ -1263,7 +1263,7 @@ let ``verify groupBy results``() = [] let ``simple delete where query``() = let dc = sql.GetDataContext() - query { + sqlQuery { for cust in dc.Main.Customers do where (cust.City = "Atlantis" || cust.CompanyName = "Home") } |> Seq.``delete all items from single table`` @@ -1274,7 +1274,7 @@ let ``simple delete where query``() = let ``simple left join``() = let dc = sqlOption.GetDataContext() let qry = - query { + sqlQuery { for o in dc.Main.Orders do for c in (!!) o.``main.Customers by CustomerID`` do select (o.CustomerId, c.CustomerId) From a59efdbc9c286e0331b82249c9aed55e7ef5c599 Mon Sep 17 00:00:00 2001 From: Stefano Pian Date: Fri, 29 Sep 2017 18:24:39 +0200 Subject: [PATCH 3/4] WIP on naturalJoin --- src/SQLProvider/SqlQueryBuilder.fs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/SQLProvider/SqlQueryBuilder.fs b/src/SQLProvider/SqlQueryBuilder.fs index 57e1bd270..fe4a3527f 100644 --- a/src/SQLProvider/SqlQueryBuilder.fs +++ b/src/SQLProvider/SqlQueryBuilder.fs @@ -287,13 +287,21 @@ type SqlQueryBuilder() = /// [] member inline this.ThenByNullableDescending (source, [] keySelector) = this.QueryBuilder.ThenByNullableDescending (source, keySelector) + + //// WIP + + ///// + ///// A query operator that selects elements from another table based on a SQL foreign key relationship'. + ///// + //[] + //member inline this.NaturalJoin (source:QuerySource<'T,'Q>, body: 'T -> QuerySource<'Result,'Q2>) : QuerySource<'Result,'Q> = + // // QueryBuilder.For is defined as : + // // QuerySource (Seq.collect (fun x -> (body x).Source) source.Source) + + // // QueryBuilder.Join is defined as : + // // QuerySource (System.Linq.Enumerable.Join(outerSource.Source, innerSource.Source, Func<_,_>(outerKeySelector), Func<_,_>(innerKeySelector), Func<_,_,_>(elementSelector))) - // WIP: Can we turn the (!!) operator into 'naturalJoin'? - - /// A query operator that correlates two sets of selected values based on a SQL foreign key relationship'. - /// - [] - member inline this.NaturalJoin (source) = !!source + // this.QueryBuilder.For(QuerySource (!! (source)), body) [] module ExtraTopLevelOperators = From b6231efc54807a18b4133bc7de3f7a10603478da Mon Sep 17 00:00:00 2001 From: Stefano Pian Date: Fri, 29 Sep 2017 18:44:54 +0200 Subject: [PATCH 4/4] sqlQuery -> sql --- src/SQLProvider/SqlQueryBuilder.fs | 2 +- tests/SqlProvider.Tests/CrudTests.fs | 30 +-- tests/SqlProvider.Tests/QueryTests.fs | 364 +++++++++++++------------- 3 files changed, 198 insertions(+), 198 deletions(-) diff --git a/src/SQLProvider/SqlQueryBuilder.fs b/src/SQLProvider/SqlQueryBuilder.fs index fe4a3527f..07dc78aac 100644 --- a/src/SQLProvider/SqlQueryBuilder.fs +++ b/src/SQLProvider/SqlQueryBuilder.fs @@ -306,7 +306,7 @@ type SqlQueryBuilder() = [] module ExtraTopLevelOperators = /// Builds a SQL query using query syntax and operators - let sqlQuery = SqlQueryBuilder() + let sql = SqlQueryBuilder() [] module LowPriority = diff --git a/tests/SqlProvider.Tests/CrudTests.fs b/tests/SqlProvider.Tests/CrudTests.fs index 6e60c4efe..46b5e9efd 100644 --- a/tests/SqlProvider.Tests/CrudTests.fs +++ b/tests/SqlProvider.Tests/CrudTests.fs @@ -18,11 +18,11 @@ open System.Transactions let connectionString = @"Data Source=./db/northwindEF.db;Version=3;Read Only=false;FailIfMissing=True;" -type sql = SqlDataProvider +type Sql = SqlDataProvider FSharp.Data.Sql.Common.QueryEvents.SqlQueryEvent |> Event.add (printfn "Executing SQL: %O") -let createCustomer (dc:sql.dataContext) = +let createCustomer (dc:Sql.dataContext) = let newCustomer = dc.Main.Customers.Create() newCustomer.CustomerId <- "SQLPROVIDER" newCustomer.Address <- "FsPRojects" @@ -39,10 +39,10 @@ let createCustomer (dc:sql.dataContext) = [] let ``Can create and delete an entity``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let originalCustomers = - sqlQuery { + sql { for cust in dc.Main.Customers do select cust } @@ -53,7 +53,7 @@ let ``Can create and delete an entity``() = dc.SubmitUpdates() let newCustomers = - sqlQuery { + sql { for cust in dc.Main.Customers do select cust } @@ -69,10 +69,10 @@ let ``Can create and delete an entity``() = [] let ``Can create, update and delete an entity``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let originalCustomers = - sqlQuery { + sql { for cust in dc.Main.Customers do select cust } @@ -89,10 +89,10 @@ let ``Can create, update and delete an entity``() = dc.SubmitUpdates() // let's create new context just to test that it is actually there. - let dc2 = sql.GetDataContext() + let dc2 = Sql.GetDataContext() let newCustomers = - sqlQuery { + sql { for cust in dc2.Main.Customers do select cust } @@ -109,7 +109,7 @@ let ``Can create, update and delete an entity``() = dc2.SubmitUpdates() let reallyDeleted = - sqlQuery { + sql { for cust in dc2.Main.Customers do select cust } @@ -119,7 +119,7 @@ let ``Can create, update and delete an entity``() = [] let ``Can persist a blob``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let imageBytes = [| 0uy .. 100uy |] @@ -128,7 +128,7 @@ let ``Can persist a blob``() = dc.SubmitUpdates() let reloadedEntity = - sqlQuery { + sql { for image in dc.Main.Pictures do where (image.Id = savedEntity.Id) head @@ -140,17 +140,17 @@ let ``Can persist a blob``() = let ``Can enlist in a transaction scope and rollback changes without complete``() = try - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() use ts = new TransactionScope() createCustomer dc |> ignore dc.SubmitUpdates() ts.Dispose() finally GC.Collect() - let dc2 = sql.GetDataContext() + let dc2 = Sql.GetDataContext() let created = - sqlQuery { + sql { for cust in dc2.Main.Customers do where (cust.CustomerId = "SQLPROVIDER") select cust diff --git a/tests/SqlProvider.Tests/QueryTests.fs b/tests/SqlProvider.Tests/QueryTests.fs index 45a044b80..dec561c8b 100644 --- a/tests/SqlProvider.Tests/QueryTests.fs +++ b/tests/SqlProvider.Tests/QueryTests.fs @@ -17,7 +17,7 @@ let connectionString = @"Data Source=./db/northwindEF.db;Version=3;Read Only=fal // Tools -> Extensions and Updates... -> Online -> NUnit Test Adapter for Visual Studio // http://nunit.org/index.php?p=vsTestAdapter&r=2.6.4 -type sql = SqlDataProvider +type Sql = SqlDataProvider FSharp.Data.Sql.Common.QueryEvents.SqlQueryEvent |> Event.add (printfn "Executing SQL: %O") @@ -25,9 +25,9 @@ let isMono = Type.GetType ("Mono.Runtime") <> null [] let ``simple select with contains query``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do select cust.CustomerId contains "ALFKI" @@ -36,9 +36,9 @@ let ``simple select with contains query``() = [] let ``simple select with contains query with where``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do where (cust.City <> "") select cust.CustomerId @@ -48,9 +48,9 @@ let ``simple select with contains query with where``() = [] let ``simple select with contains query when not exists``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do select cust.CustomerId contains "ALFKI2" @@ -59,9 +59,9 @@ let ``simple select with contains query when not exists``() = [] let ``simple select with count``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do select cust.CustomerId count @@ -70,9 +70,9 @@ let ``simple select with count``() = [] let ``simple select with last``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do sortBy cust.CustomerId select cust.CustomerId @@ -82,9 +82,9 @@ let ``simple select with last``() = [] let ``simple select with last or default when not exists``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do where (cust.CustomerId = "ZZZZ") select cust.CustomerId @@ -94,9 +94,9 @@ let ``simple select with last or default when not exists``() = [] let ``simple exists when exists``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do exists (cust.CustomerId = "WOLZA") } @@ -104,9 +104,9 @@ let ``simple exists when exists``() = [] let ``simple select with last or default when exists``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do where (cust.CustomerId = "WOLZA") select cust.CustomerId @@ -116,9 +116,9 @@ let ``simple select with last or default when exists``() = [] let ``simple select with exactly one``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do where (cust.CustomerId = "ALFKI") select cust.CustomerId @@ -128,9 +128,9 @@ let ``simple select with exactly one``() = [] let ``simple select with exactly one when not exists``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do where (cust.CustomerId = "ZZZZ") select cust.CustomerId @@ -140,9 +140,9 @@ let ``simple select with exactly one when not exists``() = [] let ``simple select with head``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do where (cust.CustomerId = "ALFKI") select cust.CustomerId @@ -152,9 +152,9 @@ let ``simple select with head``() = [] let ``simple select with head or Default``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do where (cust.CustomerId = "ALFKI") select cust.CustomerId @@ -164,9 +164,9 @@ let ``simple select with head or Default``() = [] let ``simple select with head or Default when not exists``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do where (cust.CustomerId = "ZZZZ") select cust.CustomerId @@ -176,9 +176,9 @@ let ``simple select with head or Default when not exists``() = [] let ``simple select query``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do select cust } |> Seq.toArray @@ -187,9 +187,9 @@ let ``simple select query``() = [] let ``simplest select query let temp``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do let y = cust.City select cust.Address @@ -199,9 +199,9 @@ let ``simplest select query let temp``() = [] let ``simple select query let temp nested``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do let y1 = cust.Address let y2 = cust.City @@ -212,9 +212,9 @@ let ``simple select query let temp nested``() = [] let ``simple select query let temp``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do let y = cust.City + "test" select y @@ -227,9 +227,9 @@ let ``simple select query let temp``() = [] let ``simple select query let where``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do let y = cust.City + "test" where (cust.Address <> "") @@ -241,9 +241,9 @@ let ``simple select query let where``() = [] let ``simple select query let temp used in where``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do let y = cust.City + "test" where (y <> "") @@ -256,9 +256,9 @@ let ``simple select query let temp used in where``() = [] let ``simple select query with operations in select``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do select (cust.Country + " " + cust.Address + (1).ToString()) } |> Seq.toArray @@ -267,9 +267,9 @@ let ``simple select query with operations in select``() = [] let ``simple select where query``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do where (cust.CustomerId = "ALFKI") select cust @@ -282,9 +282,9 @@ let ``simple select where query``() = [] let ``simple select where null query``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do where (cust.CustomerId <> null) select cust @@ -297,9 +297,9 @@ let ``simple select where null query``() = [] let ``simple select where query right side``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do where ("ALFKI" = cust.CustomerId) select cust @@ -311,9 +311,9 @@ let ``simple select where query right side``() = [] let ``simple select where query between col properties``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for order in dc.Main.Orders do where (order.ShippedDate > order.RequiredDate) select (order.ShippedDate, order.RequiredDate) @@ -324,9 +324,9 @@ let ``simple select where query between col properties``() = [] let ``simple nth query``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do select cust nth 4 @@ -336,9 +336,9 @@ let ``simple nth query``() = [] let ``simple select where not query``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do where (not(cust.CustomerId = "ALFKI")) select cust @@ -349,10 +349,10 @@ let ``simple select where not query``() = [] let ``simple select where in query``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let arr = ["ALFKI"; "ANATR"; "AROUT"] let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do where (arr.Contains(cust.CustomerId)) select cust.CustomerId @@ -366,10 +366,10 @@ let ``simple select where in query``() = [] let ``simple select where not-in query``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let arr = ["ALFKI"; "ANATR"; "AROUT"] let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do where (not(arr.Contains(cust.CustomerId))) select cust.CustomerId @@ -383,16 +383,16 @@ let ``simple select where not-in query``() = [] let ``simple select where in queryable query``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let query1 = - sqlQuery { + sql { for cust in dc.Main.Customers do where (cust.City="London") select cust.CustomerId } let query2 = - sqlQuery { + sql { for cust in dc.Main.Customers do where (query1.Contains(cust.CustomerId)) select cust.CustomerId @@ -406,10 +406,10 @@ let ``simple select where in queryable query``() = [] let ``simple select where in query custom syntax``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let arr = ["ALFKI"; "ANATR"; "AROUT"] let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do where (cust.CustomerId |=| arr) select cust.CustomerId @@ -421,9 +421,9 @@ let ``simple select where in query custom syntax``() = [] let ``simple select where like query``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do where (cust.CustomerId.Contains("a")) select cust.CustomerId @@ -433,9 +433,9 @@ let ``simple select where like query``() = [] let ``simple select where query with operations in where``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do where (cust.CustomerId = "ALFKI" && (cust.City.StartsWith("B"))) select cust @@ -447,9 +447,9 @@ let ``simple select where query with operations in where``() = [] let ``simple select query with minBy``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for ord in dc.Main.OrderDetails do minBy (decimal ord.Discount) } @@ -457,9 +457,9 @@ let ``simple select query with minBy``() = [] let ``simple select query with minBy2``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for ord in dc.Main.OrderDetails do minBy (ord.Discount) } @@ -468,9 +468,9 @@ let ``simple select query with minBy2``() = [] let ``simple select query with minBy DateTime``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for emp in dc.Main.Employees do minBy (emp.BirthDate) } @@ -478,9 +478,9 @@ let ``simple select query with minBy DateTime``() = [] let ``simple select query with sumBy``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for od in dc.Main.OrderDetails do sumBy (od.UnitPrice) } @@ -489,9 +489,9 @@ let ``simple select query with sumBy``() = [] let ``simple select query with averageBy``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for od in dc.Main.OrderDetails do averageBy od.UnitPrice } @@ -500,9 +500,9 @@ let ``simple select query with averageBy``() = [] let ``simplest select query with groupBy``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do groupBy cust.City } |> Seq.toArray @@ -511,9 +511,9 @@ let ``simplest select query with groupBy``() = [] let ``simple select query with groupBy``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do groupBy cust.City into c select (c.Key, c.Count()) @@ -524,9 +524,9 @@ let ``simple select query with groupBy``() = [] let ``simple select query with groupBy and then sort``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for order in dc.Main.Orders do groupBy (order.ShipCity) into ts where (ts.Count() > 1) @@ -542,9 +542,9 @@ let ``simple select query with groupBy and then sort``() = [] let ``simple select query with groupBy multiple columns``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for p in dc.Main.Products do groupBy (p.ReorderLevel, p.CategoryId) into c select (c.Key, c.Sum(fun i -> i.UnitPrice)) @@ -554,9 +554,9 @@ let ``simple select query with groupBy multiple columns``() = [] let ``simple select query with groupBy sum``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for od in dc.Main.OrderDetails do groupBy od.ProductId into p select (p.Key, p.Sum(fun f -> f.UnitPrice), p.Sum(fun f -> f.Discount)) @@ -570,9 +570,9 @@ let ``simple select query with groupBy sum``() = [] let ``simple select query with groupBy having count``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do groupBy cust.City into c where (c.Count() > 1) @@ -585,9 +585,9 @@ let ``simple select query with groupBy having count``() = [] let ``simple select query with groupBy where and having``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for emp in dc.Main.Employees do where (emp.Country = "USA") groupBy emp.City into grp @@ -603,9 +603,9 @@ let ``simple select query with groupBy where and having``() = [] let ``simple select query with groupBy having key``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do groupBy cust.City into c where (c.Key = "London") @@ -617,9 +617,9 @@ let ``simple select query with groupBy having key``() = [] let ``simple select query with groupBy date``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for emp in dc.Main.Employees do groupBy emp.BirthDate into e select (e.Key, e.Count()) @@ -629,9 +629,9 @@ let ``simple select query with groupBy date``() = [] let ``simple select query with groupBy2``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do where (cust.City = "London") groupBy (cust.Country, cust.City) into c @@ -642,9 +642,9 @@ let ``simple select query with groupBy2``() = [] let ``simple select query with groupValBy``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do groupValBy cust.ContactTitle cust.City into g select (g.Key, g.Count()) @@ -653,21 +653,21 @@ let ``simple select query with groupValBy``() = [] let ``complex select query with groupValBy``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do groupValBy (cust.ContactTitle, Int32.Parse(cust.PostalCode)) (cust.PostalCode, cust.City) into g - let maxPlusOne = 1 + sqlQuery {for i in g do sumBy (snd i) } + let maxPlusOne = 1 + sql {for i in g do sumBy (snd i) } select (snd(g.Key), maxPlusOne) } |> dict Assert.IsNotEmpty(qry) [] let ``simple if query``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do if cust.Country = "UK" then select cust.City } |> Seq.toArray @@ -680,9 +680,9 @@ let ``simple select query with case``() = // Works but wrong implementation. Doesn't transfer logics to SQL. // Expected: SELECT CASE [cust].[Country] WHEN "UK" THEN [cust].[City] ELSE "Outside UK" END as 'City' FROM main.Customers as [cust] // Actual: SELECT [cust].[Country] as 'Country',[cust].[City] as 'City' FROM main.Customers as [cust] - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do select (if cust.Country = "UK" then (cust.City) else ("Outside UK")) @@ -691,9 +691,9 @@ let ``simple select query with case``() = [] let ``simple select and sort query``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do sortBy cust.City select cust.City @@ -704,9 +704,9 @@ let ``simple select and sort query``() = [] let ``simple select and sort desc query``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do sortByDescending cust.City select cust.City @@ -717,9 +717,9 @@ let ``simple select and sort desc query``() = [] let ``simple select and sort query with then by query``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do sortBy cust.Country thenBy cust.City @@ -731,9 +731,9 @@ let ``simple select and sort query with then by query``() = [] let ``simple select and sort query with then by desc query``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do sortBy cust.Country thenByDescending cust.City @@ -745,9 +745,9 @@ let ``simple select and sort query with then by desc query``() = [] let ``simple sort query with lambda cast to IComparable``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do sortBy ((fun (c : sql.dataContext.``main.CustomersEntity``) -> c.CustomerId :> IComparable) cust) select cust.City @@ -759,12 +759,12 @@ let ``simple sort query with lambda cast to IComparable``() = [] let ``simple sort query with then by desc query with lambda cast to IComparable``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do sortBy cust.CompanyName - thenByDescending ((fun (c : sql.dataContext.``main.CustomersEntity``) -> c.CustomerId :> IComparable) cust) + thenByDescending ((fun (c : Sql.dataContext.``main.CustomersEntity``) -> c.CustomerId :> IComparable) cust) select cust.City take 1 } |> Seq.toArray @@ -774,9 +774,9 @@ let ``simple sort query with then by desc query with lambda cast to IComparable` [] let ``simple select query with join``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do join order in dc.Main.Orders on (cust.CustomerId = order.CustomerId) select (cust.CustomerId, order.OrderDate) @@ -794,9 +794,9 @@ let ``simple select query with join``() = [] let ``simple select query with join and then groupBy``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do join order in dc.Main.Orders on (cust.CustomerId = order.CustomerId) groupBy cust.City into c @@ -808,9 +808,9 @@ let ``simple select query with join and then groupBy``() = [] let ``simple select query with groupBy and then join``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do groupBy cust.City into c join order in dc.Main.Orders on (c.Key = order.ShipCity) @@ -823,9 +823,9 @@ let ``simple select query with groupBy and then join``() = [] let ``simple select query with join multi columns``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do join order in dc.Main.Orders on ((cust.CustomerId, cust.CustomerId) = (order.CustomerId, order.CustomerId)) select (cust.CustomerId, order.OrderDate) @@ -843,9 +843,9 @@ let ``simple select query with join multi columns``() = [] let ``simple select query with join using relationships``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do for order in cust.``main.Orders by CustomerID`` do select (cust.CustomerId, order.OrderDate) @@ -862,9 +862,9 @@ let ``simple select query with join using relationships``() = [] let ``simple select query with group join``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do groupJoin ord in dc.Main.Orders on (cust.CustomerId = ord.CustomerId) into g for order in g do @@ -887,9 +887,9 @@ let ``simple select query with group join``() = [] let ``simple select query with multiple joins on relationships``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do for order in cust.``main.Orders by CustomerID`` do for orderDetail in order.``main.OrderDetails by OrderID`` do @@ -911,9 +911,9 @@ let ``simple select query with multiple joins on relationships``() = [] let ``simple select query with left outer join``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do leftOuterJoin order in dc.Main.Orders on (cust.CustomerId = order.CustomerId) into result for order in result.DefaultIfEmpty() do @@ -931,9 +931,9 @@ let ``simple select query with left outer join``() = [] let ``simple sumBy``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for od in dc.Main.OrderDetails do sumBy od.UnitPrice } @@ -941,9 +941,9 @@ let ``simple sumBy``() = [] let ``simple async sum``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for od in dc.Main.OrderDetails do select od.UnitPrice } |> Seq.sumAsync |> Async.RunSynchronously @@ -951,9 +951,9 @@ let ``simple async sum``() = [] let ``simple averageBy``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for od in dc.Main.OrderDetails do averageBy od.UnitPrice } @@ -961,9 +961,9 @@ let ``simple averageBy``() = [] let ``simple averageByNullable``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for od in dc.Main.OrderDetails do averageByNullable (System.Nullable(od.UnitPrice)) } @@ -971,9 +971,9 @@ let ``simple averageByNullable``() = [] let ``simple select with distinct``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do select cust.City distinct @@ -985,9 +985,9 @@ let ``simple select with distinct``() = [] let ``simple select with skip``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do select cust.City skip 5 @@ -1000,9 +1000,9 @@ let ``simple select with skip``() = [] let ``simple select with take``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do select cust.City take 5 @@ -1015,9 +1015,9 @@ let ``simple select with take``() = [] let ``simple select query with all``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for ord in dc.Main.OrderDetails do all (ord.UnitPrice > 0m) } @@ -1025,9 +1025,9 @@ let ``simple select query with all``() = [] let ``simple select query with all false``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for ord in dc.Main.OrderDetails do all (ord.UnitPrice > 10m) } @@ -1035,9 +1035,9 @@ let ``simple select query with all false``() = [] let ``simple select query with find``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for ord in dc.Main.OrderDetails do find (ord.UnitPrice > 10m) } @@ -1049,9 +1049,9 @@ type Dummy<'t> = D of 't [] let ``simple select into a generic type`` () = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for emp in dc.Main.Customers do select (D {First=emp.ContactName}) } |> Seq.toList @@ -1064,9 +1064,9 @@ let ``simple select into a generic type with pipe`` () = Console.WriteLine "This is not supported in Mono: simple select into a generic type with pipe" Assert.IsFalse false else - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for emp in dc.Main.Customers do select ({First=emp.ContactName} |> D) } |> Seq.toList @@ -1075,7 +1075,7 @@ let ``simple select into a generic type with pipe`` () = [] let ``simple select with bool outside query``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let rnd = System.Random() // Direct booleans outside LINQ: let myCond1 = true @@ -1084,7 +1084,7 @@ let ``simple select with bool outside query``() = let myCond4 = 4 let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do // Simple booleans outside queries are supported: where (((myCond1 && myCond1=true) && cust.City="Helsinki" || myCond1) || cust.City="London") @@ -1097,7 +1097,7 @@ let ``simple select with bool outside query``() = [] let ``simple select with bool outside query2``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let rnd = System.Random() // Direct booleans outside LINQ: let myCond1 = true @@ -1106,7 +1106,7 @@ let ``simple select with bool outside query2``() = let myCond4 = 4 let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do // Simple booleans outside queries are supported: where (myCond4 > 3 || (myCond2 && cust.Address="test" && not(myCond2))) @@ -1118,11 +1118,11 @@ let ``simple select with bool outside query2``() = [] let ``simple select query async``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let task = async { let! asyncquery = - sqlQuery { + sql { for cust in dc.Main.Customers do select cust } |> Seq.executeQueryAsync @@ -1137,7 +1137,7 @@ type sqlOption = SqlDataProvider] let ``simple canonical operation substing query``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = - sqlQuery { + sql { for cust in dc.Main.Customers do // Database spesific warning here: Substring of SQLite starts from 1. where (cust.CustomerId.Substring(2,3)+"F"="NATF") @@ -1163,11 +1163,11 @@ let ``simple canonical operation substing query``() = [] let ``simple canonical operations query``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let qry = let L = "L" - sqlQuery { + sql { // Silly query not hitting indexes, so testing purposes only... for cust in dc.Main.Customers do // This is not yet working: @@ -1193,14 +1193,14 @@ let ``simple canonical operations query``() = [] let ``simple union query test``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let query1 = - sqlQuery { + sql { for cus in dc.Main.Customers do select (cus.City) } let query2 = - sqlQuery { + sql { for emp in dc.Main.Employees do select (emp.City) } @@ -1218,14 +1218,14 @@ let ``simple union query test``() = [] let ``simple union all query test``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let query1 = - sqlQuery { + sql { for cus in dc.Main.Customers do select (cus.City) } let query2 = - sqlQuery { + sql { for emp in dc.Main.Employees do select (emp.City) } @@ -1237,21 +1237,21 @@ let ``simple union all query test``() = [] let ``verify groupBy results``() = - let dc = sql.GetDataContext() + let dc = Sql.GetDataContext() let enumtest = - sqlQuery { + sql { for cust in dc.Main.Customers do select (cust) } |> Seq.toList let inlogics = - sqlQuery { + sql { for cust in enumtest do groupBy cust.City into c select (c.Key, c.Count()) } |> Seq.toArray |> Array.sortBy (fun (k,v) -> k ) let groupqry = - sqlQuery { + sql { for cust in dc.Main.Customers do groupBy cust.City into c select (c.Key, c.Count()) @@ -1262,8 +1262,8 @@ let ``verify groupBy results``() = [] let ``simple delete where query``() = - let dc = sql.GetDataContext() - sqlQuery { + let dc = Sql.GetDataContext() + sql { for cust in dc.Main.Customers do where (cust.City = "Atlantis" || cust.CompanyName = "Home") } |> Seq.``delete all items from single table`` @@ -1274,7 +1274,7 @@ let ``simple delete where query``() = let ``simple left join``() = let dc = sqlOption.GetDataContext() let qry = - sqlQuery { + sql { for o in dc.Main.Orders do for c in (!!) o.``main.Customers by CustomerID`` do select (o.CustomerId, c.CustomerId)