Safe Haskell | None |
---|---|
Language | Haskell2010 |
SQLFragment is a type safe SQL combinator based on the idea that, a SQL query
- is a monoid
- joins can be deduced automatically from an join graph.
SQLFragment main intent is to allow to build easily complex query by reusing and combining pre-made fragments (which can be typed or typeless).
This is especially useful when building reporting tools, when a lot of queries are similar and the results
are either table or charts. In that case, query output can be used "raw" (.i.e a list of tuple or equivalent) and don't need to be mapped to any complex data type.
Unlike many other SQL package, which make it hard to combine SQLFragment
and String
, SQLFragment makes it easy to write raw SQL if needed.
Its purpose is to help write query quickly not make developper life hard.
We trust the developper to not use "unsafe" string.
SQLFragment also provide support for dimensional units, HList
records and automatic fragments generation from a database. The fragments generation use a space separated values file which can be generated from the database (see corresponding backend).
For more details look at the Database.SQLFragment.SQLFragment and Database.SQLFragment.Operators.
- module Database.SQLFragment.SQLFragment
- module Database.SQLFragment.Operators
- module Database.SQLFragment.Dimensional
- autoJoin :: SQLFragment e p -> [Join] -> Either (SQLFragment e p) (SQLFragment e p)
- (!->!) :: Boolables (ZipToBool a b) => SQLFragment a ([] *) -> SQLFragment b ([] *) -> [Join]
- (!*->!) :: Boolables (ZipToBool a b) => SQLFragment a ([] *) -> SQLFragment b ([] *) -> [Join]
- (!-*>!) :: Boolables (ZipToBool a b) => SQLFragment a ([] *) -> SQLFragment b ([] *) -> [Join]
- (!<->!) :: (Boolables (ZipToBool b a), Boolables (ZipToBool a b)) => SQLFragment b ([] *) -> SQLFragment a ([] *) -> [Join]
- (!<-*>!) :: (Boolables (ZipToBool b a), Boolables (ZipToBool a b)) => SQLFragment b ([] *) -> SQLFragment a ([] *) -> [Join]
- (!<-*->!) :: (Boolables (ZipToBool b a), Boolables (ZipToBool a b)) => SQLFragment b ([] *) -> SQLFragment a ([] *) -> [Join]
- (!<*>!) :: (Boolables (ZipToBool b a), Boolables (ZipToBool a b)) => SQLFragment b ([] *) -> SQLFragment a ([] *) -> [Join]
Example
Let's say we have a table of customers, products, and orders, joining a customer to n products. I want to display in table the list of the customer which ordered the product 'blue T-shirt'.
With SQLFragment, supposing I have defined email
and blue
fragments so that
> toSelectQuery email "SELECT email FROM customers" > toSelectQuery blue "FROM product WHERE description = 'blue T-shirt'"
and the join graph as been properly set up in joins
.
I can simply combine those two fragment using <>
.
> toSelectQuery $ email <> blue !@! joins "SELECT email FROM customers JOIN orders ON (customers.id = customer_id) JOIN products ON (products.id = product_id) WHERE products.description = 'blue T-shirt' "
Documentation
autoJoin :: SQLFragment e p -> [Join] -> Either (SQLFragment e p) (SQLFragment e p)
Computes the mimimum set of join needed to cover the tables from an SQLFragment
.
>>>
:set -XDataKinds -XOverloadedStrings
>>>
let a = "a.x" :: SQLFragment '[] '[]
>>>
let b = "b.x" :: SQLFragment '[] '[]
>>>
let c = "c.x" :: SQLFragment '[] '[]
>>>
let g = a !->! b ++ b !->! c
>>>
either toSelectQuery toSelectQuery $ autoJoin (a !&! b) g
"SELECT a.x, b.x\nFROM a JOIN b ON ((a.x)=(b.x))">>>
either toSelectQuery toSelectQuery $ autoJoin (b !&! c) g
"SELECT b.x, c.x\nFROM b JOIN c ON ((b.x)=(c.x))">>>
either toSelectQuery toSelectQuery $ autoJoin (a !&!c) g
"SELECT a.x, c.x\nFROM a JOIN b ON ((a.x)=(b.x)) JOIN c ON ((b.x)=(c.x))"
(!->!) :: Boolables (ZipToBool a b) => SQLFragment a ([] *) -> SQLFragment b ([] *) -> [Join]
Unidirectional inner join
(!*->!) :: Boolables (ZipToBool a b) => SQLFragment a ([] *) -> SQLFragment b ([] *) -> [Join]
Unidirectional right join
(!-*>!) :: Boolables (ZipToBool a b) => SQLFragment a ([] *) -> SQLFragment b ([] *) -> [Join]
Unidirectional left join
(!<->!) :: (Boolables (ZipToBool b a), Boolables (ZipToBool a b)) => SQLFragment b ([] *) -> SQLFragment a ([] *) -> [Join]
Bidirectional inner join
(!<-*>!) :: (Boolables (ZipToBool b a), Boolables (ZipToBool a b)) => SQLFragment b ([] *) -> SQLFragment a ([] *) -> [Join]
Bidirectional left,right join
(!<-*->!) :: (Boolables (ZipToBool b a), Boolables (ZipToBool a b)) => SQLFragment b ([] *) -> SQLFragment a ([] *) -> [Join]
Bidirectional left,left join
(!<*>!) :: (Boolables (ZipToBool b a), Boolables (ZipToBool a b)) => SQLFragment b ([] *) -> SQLFragment a ([] *) -> [Join]
Bidirectional outer join