11{-# LANGUAGE CPP #-}
22{-# LANGUAGE DataKinds #-}
3+ {-# LANGUAGE DerivingStrategies #-}
34{-# LANGUAGE FlexibleContexts #-}
45{-# LANGUAGE FlexibleInstances #-}
56{-# LANGUAGE FunctionalDependencies #-}
67{-# LANGUAGE GADTs #-}
8+ {-# LANGUAGE GeneralizedNewtypeDeriving #-}
79{-# LANGUAGE MultiParamTypeClasses #-}
810{-# LANGUAGE OverloadedStrings #-}
911{-# LANGUAGE PatternSynonyms #-}
12+ {-# LANGUAGE ScopedTypeVariables #-}
13+ {-# LANGUAGE TypeApplications #-}
1014{-# LANGUAGE TypeFamilies #-}
1115{-# LANGUAGE TypeOperators #-}
1216{-# LANGUAGE UndecidableInstances #-}
1317
1418module Database.Esqueleto.Experimental.From
1519 where
1620
21+ import Control.Arrow (first )
22+ import Control.Monad (ap )
1723import qualified Control.Monad.Trans.Writer as W
1824import Data.Proxy
25+ import qualified Data.Text.Lazy.Builder as TLB
1926import Database.Esqueleto.Experimental.ToAlias
2027import Database.Esqueleto.Experimental.ToAliasReference
2128import Database.Esqueleto.Internal.Internal hiding (From (.. ), from , on )
@@ -30,70 +37,56 @@ import Database.Esqueleto.Internal.PersistentImport
3037-- instances of `From`. This implementation eliminates certain
3138-- types of runtime errors by preventing the construction of
3239-- invalid SQL (e.g. illegal nested-@from@).
33- from :: From a => a -> SqlQuery ( FromT a )
34- from parts = do
35- (a, clause) <- runFrom parts
36- Q $ W. tell mempty {sdFromClause= [clause]}
40+ from :: ToFrom a a' => a -> SqlQuery a'
41+ from f = do
42+ (a, clause) <- unFrom (toFrom f)
43+ Q $ W. tell mempty {sdFromClause= [FromRaw $ clause]}
3744 pure a
3845
39- class From a where
40- type FromT a
41- runFrom :: a -> SqlQuery (FromT a , FromClause )
46+ type RawFn = NeedParens -> IdentInfo -> (TLB. Builder , [PersistValue ])
47+ newtype From a = From
48+ { unFrom :: SqlQuery (a , RawFn )}
49+
50+ class ToFrom a r | a -> r where
51+ toFrom :: a -> From r
52+ instance ToFrom (From a ) a where
53+ toFrom = id
4254
4355-- | Data type for bringing a Table into scope in a JOIN tree
4456--
4557-- @
4658-- select $ from $ Table \@People
4759-- @
4860data Table a = Table
61+ instance PersistEntity ent => ToFrom (Table ent ) (SqlExpr (Entity ent )) where
62+ toFrom _ = table
4963
50- instance PersistEntity a => From (Table a ) where
51- type FromT (Table a ) = SqlExpr (Entity a )
52- runFrom e@ Table = do
53- let ed = entityDef $ getVal e
54- ident <- newIdentFor (entityDB ed)
55- let entity = unsafeSqlEntity ident
56- pure $ (entity, FromStart ident ed)
57- where
58- getVal :: Table ent -> Proxy ent
59- getVal = const Proxy
64+ table :: forall ent . PersistEntity ent => From (SqlExpr (Entity ent ))
65+ table = From $ do
66+ let ed = entityDef (Proxy @ ent )
67+ ident <- newIdentFor (entityDB ed)
68+ let entity = unsafeSqlEntity ident
69+ pure $ ( entity, const $ base ident ed )
70+ where
71+ base ident@ (I identText) def info =
72+ let db@ (DBName dbText) = entityDB def
73+ in ( fromDBName info db <>
74+ if dbText == identText
75+ then mempty
76+ else " AS " <> useIdent info ident
77+ , mempty
78+ )
6079
6180
6281{-# DEPRECATED SubQuery "/Since: 3.4.0.0/ - It is no longer necessary to tag 'SqlQuery' values with @SubQuery@" #-}
6382newtype SubQuery a = SubQuery a
83+ instance (SqlSelect a r , ToAlias a , ToAliasReference a ) => ToFrom (SubQuery (SqlQuery a )) a where
84+ toFrom (SubQuery q) = selectQuery q
85+ instance (SqlSelect a r , ToAlias a , ToAliasReference a ) => ToFrom (SqlQuery a ) a where
86+ toFrom = selectQuery
6487
65- instance
66- ( ToAlias a
67- , ToAliasReference a
68- , SqlSelect a r
69- )
70- =>
71- From (SqlQuery a )
72- where
73- type FromT (SqlQuery a ) = a
74- runFrom subquery =
75- fromSubQuery NormalSubQuery subquery
76-
77- instance
78- ( ToAlias a
79- , ToAliasReference a
80- , SqlSelect a r
81- )
82- =>
83- From (SubQuery (SqlQuery a ))
84- where
85- type FromT (SubQuery (SqlQuery a )) = a
86- runFrom (SubQuery subquery) =
87- fromSubQuery NormalSubQuery subquery
88-
89- fromSubQuery
90- ::
91- ( SqlSelect a r
92- , ToAlias a
93- , ToAliasReference a
94- )
95- => SubQueryType -> SqlQuery a -> SqlQuery (a , FromClause )
96- fromSubQuery subqueryType subquery = do
88+ selectQuery :: (SqlSelect a r , ToAlias a , ToAliasReference a ) => SqlQuery a -> From a
89+ selectQuery subquery = From $ do
9790 -- We want to update the IdentState without writing the query to side data
9891 (ret, sideData) <- Q $ W. censor (\ _ -> mempty ) $ W. listen $ unQ subquery
9992 aliasedValue <- toAlias ret
@@ -105,4 +98,11 @@ fromSubQuery subqueryType subquery = do
10598 -- create aliased references from the outer query results (e.g value from subquery will be `subquery`.`value`),
10699 -- this is probably overkill as the aliases should already be unique but seems to be good practice.
107100 ref <- toAliasReference subqueryAlias aliasedValue
108- pure (ref , FromQuery subqueryAlias (\ info -> toRawSql SELECT info aliasedQuery) subqueryType)
101+
102+ pure (ref, \ _ info ->
103+ let (queryText,queryVals) = toRawSql SELECT info aliasedQuery
104+ in
105+ ( (parens queryText) <> " AS " <> useIdent info subqueryAlias
106+ , queryVals
107+ )
108+ )
0 commit comments