{-# OPTIONS_GHC -fno-warn-name-shadowing -fno-warn-unused-binds #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE CPP #-}
module Database.Beam.Query.SQL92
( buildSql92Query' ) where
import Database.Beam.Query.Internal
import Database.Beam.Backend.SQL
import Control.Monad.Free.Church
import Control.Monad.Free
#if !MIN_VERSION_base(4, 11, 0)
import Control.Monad.Writer hiding ((<>))
import Data.Semigroup
#endif
import Data.Maybe
import Data.Proxy (Proxy(Proxy))
import Data.String
import qualified Data.Text as T
andE' :: IsSql92ExpressionSyntax expr =>
Maybe expr -> Maybe expr -> Maybe expr
andE' Nothing Nothing = Nothing
andE' (Just x) Nothing = Just x
andE' Nothing (Just y) = Just y
andE' (Just x) (Just y) = Just (andE x y)
newtype PreserveLeft a b = PreserveLeft { unPreserveLeft :: (a, b) }
instance (Monoid a, ProjectibleWithPredicate c syntax res b) => ProjectibleWithPredicate c syntax res (PreserveLeft a b) where
project' context be f (PreserveLeft (a, b)) =
PreserveLeft . (a,) <$> project' context be f b
projectSkeleton' ctxt be mkM =
PreserveLeft . (mempty,) <$> projectSkeleton' ctxt be mkM
type SelectStmtFn be
= BeamSqlBackendSelectTableSyntax be
-> [BeamSqlBackendOrderingSyntax be]
-> Maybe Integer
-> Maybe Integer
-> BeamSqlBackendSelectSyntax be
data QueryBuilder be
= QueryBuilder
{ qbNextTblRef :: Int
, qbFrom :: Maybe (BeamSqlBackendFromSyntax be)
, qbWhere :: Maybe (BeamSqlBackendExpressionSyntax be) }
data SelectBuilder be (db :: (* -> *) -> *) a where
SelectBuilderQ :: ( BeamSqlBackend be
, Projectible be a )
=> a -> QueryBuilder be -> SelectBuilder be db a
SelectBuilderGrouping
:: ( BeamSqlBackend be
, Projectible be a )
=> a -> QueryBuilder be
-> Maybe (BeamSqlBackendGroupingSyntax be)
-> Maybe (BeamSqlBackendExpressionSyntax be)
-> Maybe (BeamSqlBackendSetQuantifierSyntax be)
-> SelectBuilder be db a
SelectBuilderSelectSyntax :: Bool
-> a -> BeamSqlBackendSelectTableSyntax be
-> SelectBuilder be db a
SelectBuilderTopLevel ::
{ sbLimit, sbOffset :: Maybe Integer
, sbOrdering :: [ BeamSqlBackendOrderingSyntax be ]
, sbTable :: SelectBuilder be db a
, sbSelectFn :: Maybe (SelectStmtFn be)
} -> SelectBuilder be db a
sbContainsSetOperation :: SelectBuilder syntax db a -> Bool
sbContainsSetOperation (SelectBuilderSelectSyntax contains _ _) = contains
sbContainsSetOperation (SelectBuilderTopLevel { sbTable = tbl }) = sbContainsSetOperation tbl
sbContainsSetOperation _ = False
fieldNameFunc :: IsSql92ExpressionSyntax expr =>
(T.Text -> Sql92ExpressionFieldNameSyntax expr) -> Int
-> expr
fieldNameFunc mkField i = fieldE (mkField ("res" <> fromString (show i)))
nextTblPfx :: TablePrefix -> TablePrefix
nextTblPfx = ("sub_" <>)
defaultProjection :: Projectible be x
=> Proxy be -> TablePrefix -> x -> [ ( BeamSqlBackendExpressionSyntax be , Maybe T.Text ) ]
defaultProjection be pfx =
zipWith (\i e -> (e, Just (fromString "res" <> fromString (show (i :: Integer)))))
[0..] . flip (project be) (nextTblPfx pfx)
buildSelect :: forall be db a
. ( BeamSqlBackend be, Projectible be a )
=> TablePrefix -> SelectBuilder be db a -> BeamSqlBackendSelectSyntax be
buildSelect _ (SelectBuilderTopLevel limit offset ordering (SelectBuilderSelectSyntax _ _ table) selectStmt') =
(fromMaybe selectStmt selectStmt') table ordering limit offset
buildSelect pfx (SelectBuilderTopLevel limit offset ordering (SelectBuilderQ proj (QueryBuilder _ from where_)) selectStmt') =
(fromMaybe selectStmt selectStmt') (selectTableStmt Nothing (projExprs (defaultProjection (Proxy @be) pfx proj)) from where_ Nothing Nothing) ordering limit offset
buildSelect pfx (SelectBuilderTopLevel limit offset ordering (SelectBuilderGrouping proj (QueryBuilder _ from where_) grouping having distinct) selectStmt') =
(fromMaybe selectStmt selectStmt') (selectTableStmt distinct (projExprs (defaultProjection (Proxy @be) pfx proj)) from where_ grouping having) ordering limit offset
buildSelect pfx x = buildSelect pfx (SelectBuilderTopLevel Nothing Nothing [] x Nothing)
selectBuilderToTableSource :: forall be db a
. ( BeamSqlBackend be, Projectible be a )
=> TablePrefix -> SelectBuilder be db a -> BeamSqlBackendSelectTableSyntax be
selectBuilderToTableSource _ (SelectBuilderSelectSyntax _ _ x) = x
selectBuilderToTableSource pfx (SelectBuilderQ x (QueryBuilder _ from where_)) =
selectTableStmt Nothing (projExprs (defaultProjection (Proxy @be) pfx x)) from where_ Nothing Nothing
selectBuilderToTableSource pfx (SelectBuilderGrouping x (QueryBuilder _ from where_) grouping having distinct) =
selectTableStmt distinct (projExprs (defaultProjection (Proxy @be) pfx x)) from where_ grouping having
selectBuilderToTableSource pfx sb =
let (x, QueryBuilder _ from where_) = selectBuilderToQueryBuilder pfx sb
in selectTableStmt Nothing (projExprs (defaultProjection (Proxy @be) pfx x)) from where_ Nothing Nothing
selectBuilderToQueryBuilder :: forall be db a
. ( BeamSqlBackend be, Projectible be a)
=> TablePrefix -> SelectBuilder be db a -> (a, QueryBuilder be)
selectBuilderToQueryBuilder pfx sb =
let select = buildSelect pfx sb
x' = reproject (Proxy @be) (fieldNameFunc (qualifiedField t0)) (sbProj sb)
t0 = pfx <> "0"
in (x', QueryBuilder 1 (Just (fromTable (tableFromSubSelect select) (Just (t0, Nothing)))) Nothing)
emptyQb :: QueryBuilder select
emptyQb = QueryBuilder 0 Nothing Nothing
sbProj :: SelectBuilder syntax db a -> a
sbProj (SelectBuilderQ proj _) = proj
sbProj (SelectBuilderGrouping proj _ _ _ _) = proj
sbProj (SelectBuilderSelectSyntax _ proj _) = proj
sbProj (SelectBuilderTopLevel _ _ _ sb _) = sbProj sb
setSelectBuilderProjection :: Projectible be b
=> SelectBuilder be db a -> b -> SelectBuilder be db b
setSelectBuilderProjection (SelectBuilderQ _ q) proj = SelectBuilderQ proj q
setSelectBuilderProjection (SelectBuilderGrouping _ q grouping having d) proj = SelectBuilderGrouping proj q grouping having d
setSelectBuilderProjection (SelectBuilderSelectSyntax containsSetOp _ q) proj = SelectBuilderSelectSyntax containsSetOp proj q
setSelectBuilderProjection (SelectBuilderTopLevel limit offset ord sb s) proj =
SelectBuilderTopLevel limit offset ord (setSelectBuilderProjection sb proj) s
limitSelectBuilder, offsetSelectBuilder :: Integer -> SelectBuilder syntax db a -> SelectBuilder syntax db a
limitSelectBuilder limit (SelectBuilderTopLevel limit' offset ordering tbl build) =
SelectBuilderTopLevel (Just $ maybe limit (min limit) limit') offset ordering tbl build
limitSelectBuilder limit x = SelectBuilderTopLevel (Just limit) Nothing [] x Nothing
offsetSelectBuilder offset (SelectBuilderTopLevel Nothing offset' ordering tbl build) =
SelectBuilderTopLevel Nothing (Just $ offset + fromMaybe 0 offset') ordering tbl build
offsetSelectBuilder offset (SelectBuilderTopLevel (Just limit) offset' ordering tbl build) =
SelectBuilderTopLevel (Just $ max 0 (limit - offset)) (Just $ offset + fromMaybe 0 offset') ordering tbl build
offsetSelectBuilder offset x = SelectBuilderTopLevel Nothing (Just offset) [] x Nothing
exprWithContext :: TablePrefix -> WithExprContext a -> a
exprWithContext pfx = ($ nextTblPfx pfx)
buildJoinTableSourceQuery
:: forall be x
. ( BeamSqlBackend be, Projectible be x )
=> TablePrefix -> BeamSqlBackendSelectSyntax be
-> x -> QueryBuilder be
-> (x, QueryBuilder be)
buildJoinTableSourceQuery tblPfx tblSource x qb =
let qb' = QueryBuilder (tblRef + 1) from' (qbWhere qb)
!tblRef = qbNextTblRef qb
from' = case qbFrom qb of
Nothing -> Just newSource
Just oldFrom -> Just (innerJoin oldFrom newSource Nothing)
newSource = fromTable (tableFromSubSelect tblSource) (Just (newTblNm, Nothing))
newTblNm = tblPfx <> fromString (show tblRef)
in (reproject (Proxy @be) (fieldNameFunc (qualifiedField newTblNm)) x, qb')
buildInnerJoinQuery
:: forall be r
. BeamSqlBackend be
=> TablePrefix -> (TablePrefix -> T.Text -> BeamSqlBackendFromSyntax be)
-> (T.Text -> r)
-> (r-> Maybe (WithExprContext (BeamSqlBackendExpressionSyntax be)))
-> QueryBuilder be -> (T.Text, r, QueryBuilder be)
buildInnerJoinQuery tblPfx mkFrom mkTbl mkOn qb =
let qb' = QueryBuilder (tblRef + 1) from' where'
tblRef = qbNextTblRef qb
newTblNm = tblPfx <> fromString (show tblRef)
newSource = mkFrom (nextTblPfx tblPfx) newTblNm
(from', where') =
case qbFrom qb of
Nothing -> (Just newSource, andE' (qbWhere qb) (exprWithContext tblPfx <$> mkOn newTbl))
Just oldFrom -> (Just (innerJoin oldFrom newSource (exprWithContext tblPfx <$> mkOn newTbl)), qbWhere qb)
newTbl = mkTbl newTblNm
in (newTblNm, newTbl, qb')
nextTbl :: BeamSqlBackend be
=> QueryBuilder be -> TablePrefix
-> (T.Text -> r)
-> ( r, T.Text, QueryBuilder be )
nextTbl qb tblPfx mkTbl =
let tblRef = qbNextTblRef qb
newTblNm = tblPfx <> fromString (show tblRef)
newTbl = mkTbl newTblNm
in (newTbl, newTblNm, qb { qbNextTblRef = qbNextTblRef qb + 1})
projOrder :: Projectible be x
=> Proxy be -> x -> WithExprContext [ BeamSqlBackendExpressionSyntax be ]
projOrder = project
buildSql92Query' :: forall be db s a
. ( BeamSqlBackend be, Projectible be a)
=> Bool
-> T.Text
-> Q be db s a
-> BeamSqlBackendSelectSyntax be
buildSql92Query' arbitrarilyNestedCombinations tblPfx (Q q) =
buildSelect tblPfx (buildQuery (fromF q))
where
be :: Proxy be
be = Proxy
buildQuery :: forall s x
. Projectible be x
=> Free (QF be db s) x
-> SelectBuilder be db x
buildQuery (Pure x) = SelectBuilderQ x emptyQb
buildQuery (Free (QGuard _ next)) = buildQuery next
buildQuery f@(Free QAll {}) = buildJoinedQuery f emptyQb
buildQuery f@(Free QArbitraryJoin {}) = buildJoinedQuery f emptyQb
buildQuery f@(Free QTwoWayJoin {}) = buildJoinedQuery f emptyQb
buildQuery (Free (QSubSelect q' next)) =
let sb = buildQuery (fromF q')
(proj, qb) = selectBuilderToQueryBuilder tblPfx sb
in buildJoinedQuery (next proj) qb
buildQuery (Free (QDistinct nubType q' next)) =
let (proj, qb, gp, hv) =
case buildQuery (fromF q') of
SelectBuilderQ proj qb ->
( proj, qb, Nothing, Nothing)
SelectBuilderGrouping proj qb gp hv Nothing ->
( proj, qb, gp, hv)
sb ->
let (proj, qb) = selectBuilderToQueryBuilder tblPfx sb
in ( proj, qb, Nothing, Nothing)
in case next proj of
Pure x -> SelectBuilderGrouping x qb gp hv (Just (exprWithContext tblPfx (nubType proj)))
_ -> let ( proj', qb' ) = selectBuilderToQueryBuilder tblPfx (SelectBuilderGrouping proj qb gp hv (Just (exprWithContext tblPfx (nubType proj))))
in buildJoinedQuery (next proj') qb'
buildQuery (Free (QAggregate mkAgg q' next)) =
let sb = buildQuery (fromF q')
(groupingSyntax, aggProj) = mkAgg (sbProj sb) (nextTblPfx tblPfx)
in case tryBuildGuardsOnly (next aggProj) Nothing of
Just (proj, having) ->
case sb of
SelectBuilderQ _ q'' -> SelectBuilderGrouping proj q'' groupingSyntax having Nothing
_ -> let (subProj, qb) = selectBuilderToQueryBuilder tblPfx sb
(groupingSyntax, aggProj') = mkAgg subProj (nextTblPfx tblPfx)
in case tryBuildGuardsOnly (next aggProj') Nothing of
Nothing -> error "buildQuery (Free (QAggregate ...)): Impossible"
Just (aggProj'', having') ->
SelectBuilderGrouping aggProj'' qb groupingSyntax having' Nothing
Nothing ->
let (_, having) = tryCollectHaving (next aggProj') Nothing
(next', _) = tryCollectHaving (next x') Nothing
(groupingSyntax', aggProj', qb) =
case sb of
SelectBuilderQ _ q'' -> (groupingSyntax, aggProj, q'')
_ -> let (proj', qb''') = selectBuilderToQueryBuilder tblPfx sb
(groupingSyntax', aggProj') = mkAgg proj' (nextTblPfx tblPfx)
in (groupingSyntax', aggProj', qb''')
(x', qb') = selectBuilderToQueryBuilder tblPfx $
SelectBuilderGrouping aggProj' qb groupingSyntax' having Nothing
in buildJoinedQuery next' qb'
buildQuery (Free (QOrderBy mkOrdering q' next)) =
let sb = buildQuery (fromF q')
proj = sbProj sb
ordering = exprWithContext tblPfx (mkOrdering proj)
doJoined =
let sb' = case sb of
SelectBuilderQ {} ->
SelectBuilderTopLevel Nothing Nothing ordering sb Nothing
SelectBuilderGrouping {} ->
SelectBuilderTopLevel Nothing Nothing ordering sb Nothing
SelectBuilderSelectSyntax {} ->
SelectBuilderTopLevel Nothing Nothing ordering sb Nothing
SelectBuilderTopLevel Nothing Nothing [] sb' build ->
SelectBuilderTopLevel Nothing Nothing ordering sb' build
SelectBuilderTopLevel Nothing (Just 0) [] sb' build ->
SelectBuilderTopLevel Nothing (Just 0) ordering sb' build
SelectBuilderTopLevel {}
| (proj'', qb) <- selectBuilderToQueryBuilder tblPfx sb ->
SelectBuilderTopLevel Nothing Nothing (exprWithContext tblPfx (mkOrdering proj'')) (SelectBuilderQ proj'' qb) Nothing
| otherwise -> error "buildQuery (Free (QOrderBy ...)): query inspected expression"
(joinedProj, qb) = selectBuilderToQueryBuilder tblPfx sb'
in buildJoinedQuery (next joinedProj) qb
in case next proj of
Pure proj' ->
case ordering of
[] -> setSelectBuilderProjection sb proj'
ordering ->
case sb of
SelectBuilderQ {} ->
SelectBuilderTopLevel Nothing Nothing ordering (setSelectBuilderProjection sb proj') Nothing
SelectBuilderGrouping {} ->
SelectBuilderTopLevel Nothing Nothing ordering (setSelectBuilderProjection sb proj') Nothing
SelectBuilderSelectSyntax {} ->
SelectBuilderTopLevel Nothing Nothing ordering (setSelectBuilderProjection sb proj') Nothing
SelectBuilderTopLevel Nothing Nothing [] sb' build ->
SelectBuilderTopLevel Nothing Nothing ordering (setSelectBuilderProjection sb' proj') build
SelectBuilderTopLevel (Just 0) (Just 0) [] sb' build ->
SelectBuilderTopLevel (Just 0) (Just 0) ordering (setSelectBuilderProjection sb' proj') build
SelectBuilderTopLevel {}
| (proj'', qb) <- selectBuilderToQueryBuilder tblPfx sb,
Pure proj''' <- next proj'' ->
SelectBuilderTopLevel Nothing Nothing (exprWithContext tblPfx (mkOrdering proj'')) (SelectBuilderQ proj''' qb) Nothing
| otherwise -> error "buildQuery (Free (QOrderBy ...)): query inspected expression"
_ -> doJoined
buildQuery (Free (QWindowOver mkWindows mkProjection q' next)) =
let sb = buildQuery (fromF q')
x = sbProj sb
windows = mkWindows x
projection = mkProjection x windows
in case next projection of
Pure x' ->
case setSelectBuilderProjection sb x' of
sb'@SelectBuilderTopLevel {} -> sb'
sb' -> SelectBuilderTopLevel Nothing Nothing [] sb' Nothing
_ ->
let (x', qb) = selectBuilderToQueryBuilder tblPfx (setSelectBuilderProjection sb projection)
in buildJoinedQuery (next x') qb
buildQuery (Free (QLimit limit q' next)) =
let sb = limitSelectBuilder limit (buildQuery (fromF q'))
x = sbProj sb
in case next x of
Pure x' -> setSelectBuilderProjection sb x'
_ -> let (x', qb) = selectBuilderToQueryBuilder tblPfx sb
in buildJoinedQuery (next x') qb
buildQuery (Free (QOffset offset q' next)) =
let sb = offsetSelectBuilder offset (buildQuery (fromF q'))
x = sbProj sb
in case next x of
Pure x' -> setSelectBuilderProjection sb x'
_ -> let (x', qb) = selectBuilderToQueryBuilder tblPfx sb
in buildJoinedQuery (next x') qb
buildQuery (Free (QSetOp combine left right next)) =
buildTableCombination combine left right next
buildQuery (Free (QForceSelect selectStmt' over next)) =
let sb = buildQuery (fromF over)
x = sbProj sb
selectStmt'' = selectStmt' (sbProj sb)
sb' = case sb of
SelectBuilderTopLevel { sbSelectFn = Nothing } ->
sb { sbSelectFn = Just selectStmt'' }
SelectBuilderTopLevel { sbSelectFn = Just {} } ->
error "Force select too hard"
_ -> SelectBuilderTopLevel Nothing Nothing [] sb (Just selectStmt'')
in case next (sbProj sb') of
Pure x' -> setSelectBuilderProjection sb' x'
_ -> let (x', qb) = selectBuilderToQueryBuilder tblPfx sb'
in buildJoinedQuery (next x') qb
tryBuildGuardsOnly :: forall s x
. Free (QF be db s) x
-> Maybe (BeamSqlBackendExpressionSyntax be)
-> Maybe (x, Maybe (BeamSqlBackendExpressionSyntax be))
tryBuildGuardsOnly next having =
case tryCollectHaving next having of
(Pure x, having') -> Just (x, having')
_ -> Nothing
tryCollectHaving :: forall s x.
Free (QF be db s) x
-> Maybe (BeamSqlBackendExpressionSyntax be)
-> (Free (QF be db s) x, Maybe (BeamSqlBackendExpressionSyntax be))
tryCollectHaving (Free (QGuard cond next)) having = tryCollectHaving next (andE' having (Just (exprWithContext tblPfx cond)))
tryCollectHaving next having = (next, having)
buildTableCombination
:: forall s x r
. ( Projectible be r, Projectible be x )
=> (BeamSqlBackendSelectTableSyntax be -> BeamSqlBackendSelectTableSyntax be -> BeamSqlBackendSelectTableSyntax be)
-> QM be db (QNested s) x -> QM be db (QNested s) x -> (x -> Free (QF be db s) r) -> SelectBuilder be db r
buildTableCombination combineTables left right next =
let leftSb = buildQuery (fromF left)
leftTb = selectBuilderToTableSource tblPfx leftSb
rightSb = buildQuery (fromF right)
rightTb = selectBuilderToTableSource tblPfx rightSb
proj = reproject be (fieldNameFunc unqualifiedField) (sbProj leftSb)
leftTb' | arbitrarilyNestedCombinations = leftTb
| sbContainsSetOperation leftSb =
let (x', qb) = selectBuilderToQueryBuilder tblPfx leftSb
in selectBuilderToTableSource tblPfx (SelectBuilderQ x' qb)
| otherwise = leftTb
rightTb' | arbitrarilyNestedCombinations = rightTb
| sbContainsSetOperation rightSb =
let (x', qb) = selectBuilderToQueryBuilder tblPfx rightSb
in selectBuilderToTableSource tblPfx (SelectBuilderQ x' qb)
| otherwise = rightTb
sb = SelectBuilderSelectSyntax True proj (combineTables leftTb' rightTb')
in case next proj of
Pure proj'
| projOrder be proj (nextTblPfx tblPfx) == projOrder be proj' (nextTblPfx tblPfx) ->
setSelectBuilderProjection sb proj'
_ -> let (x', qb) = selectBuilderToQueryBuilder tblPfx sb
in buildJoinedQuery (next x') qb
buildJoinedQuery :: forall s x.
Projectible be x =>
Free (QF be db s) x -> QueryBuilder be -> SelectBuilder be db x
buildJoinedQuery (Pure x) qb = SelectBuilderQ x qb
buildJoinedQuery (Free (QAll mkFrom mkTbl on next)) qb =
let (newTblNm, newTbl, qb') = buildInnerJoinQuery tblPfx mkFrom mkTbl on qb
in buildJoinedQuery (next (newTblNm, newTbl)) qb'
buildJoinedQuery (Free (QArbitraryJoin q mkJoin on next)) qb =
case fromF q of
Free (QAll mkDbFrom dbMkTbl on' next')
| (newTbl, newTblNm, qb') <- nextTbl qb tblPfx dbMkTbl,
Nothing <- exprWithContext tblPfx <$> on' newTbl,
Pure proj <- next' (newTblNm, newTbl) ->
let newSource = mkDbFrom (nextTblPfx tblPfx) newTblNm
on'' = exprWithContext tblPfx <$> on proj
(from', where') =
case qbFrom qb' of
Nothing -> (Just newSource, andE' (qbWhere qb) on'')
Just oldFrom -> (Just (mkJoin oldFrom newSource on''), qbWhere qb)
in buildJoinedQuery (next proj) (qb' { qbFrom = from', qbWhere = where' })
q' -> let sb = buildQuery q'
tblSource = buildSelect tblPfx sb
newTblNm = tblPfx <> fromString (show (qbNextTblRef qb))
newSource = fromTable (tableFromSubSelect tblSource) (Just (newTblNm, Nothing))
proj' = reproject be (fieldNameFunc (qualifiedField newTblNm)) (sbProj sb)
on' = exprWithContext tblPfx <$> on proj'
(from', where') =
case qbFrom qb of
Nothing -> (Just newSource, andE' (qbWhere qb) on')
Just oldFrom -> (Just (mkJoin oldFrom newSource on'), qbWhere qb)
in buildJoinedQuery (next proj') (qb { qbNextTblRef = qbNextTblRef qb + 1
, qbFrom = from', qbWhere = where' })
buildJoinedQuery (Free (QTwoWayJoin a b mkJoin on next)) qb =
let (aProj, aSource, qb') =
case fromF a of
Free (QAll mkDbFrom dbMkTbl on' next')
| (newTbl, newTblNm, qb') <- nextTbl qb tblPfx dbMkTbl,
Nothing <- on' newTbl, Pure proj <- next' (newTblNm, newTbl) ->
(proj, mkDbFrom (nextTblPfx tblPfx) newTblNm, qb')
a -> let sb = buildQuery a
tblSource = buildSelect tblPfx sb
newTblNm = tblPfx <> fromString (show (qbNextTblRef qb))
proj' = reproject be (fieldNameFunc (qualifiedField newTblNm)) (sbProj sb)
in (proj', fromTable (tableFromSubSelect tblSource) (Just (newTblNm, Nothing)), qb { qbNextTblRef = qbNextTblRef qb + 1 })
(bProj, bSource, qb'') =
case fromF b of
Free (QAll mkDbFrom dbMkTbl on' next')
| (newTbl, newTblNm, qb'') <- nextTbl qb' tblPfx dbMkTbl,
Nothing <- on' newTbl, Pure proj <- next' (newTblNm, newTbl) ->
(proj, mkDbFrom (nextTblPfx tblPfx) newTblNm, qb'')
b -> let sb = buildQuery b
tblSource = buildSelect tblPfx sb
newTblNm = tblPfx <> fromString (show (qbNextTblRef qb))
proj' = reproject be (fieldNameFunc (qualifiedField newTblNm)) (sbProj sb)
in (proj', fromTable (tableFromSubSelect tblSource) (Just (newTblNm, Nothing)), qb { qbNextTblRef = qbNextTblRef qb + 1 })
abSource = mkJoin aSource bSource (exprWithContext tblPfx <$> on (aProj, bProj))
from' =
case qbFrom qb'' of
Nothing -> Just abSource
Just oldFrom -> Just (innerJoin oldFrom abSource Nothing)
in buildJoinedQuery (next (aProj, bProj)) (qb'' { qbFrom = from' })
buildJoinedQuery (Free (QGuard cond next)) qb =
buildJoinedQuery next (qb { qbWhere = andE' (qbWhere qb) (Just (exprWithContext tblPfx cond)) })
buildJoinedQuery now qb =
onlyQ now
(\now' next ->
let sb = buildQuery now'
tblSource = buildSelect tblPfx sb
(x', qb') = buildJoinTableSourceQuery tblPfx tblSource (sbProj sb) qb
in buildJoinedQuery (next x') qb')
onlyQ :: forall s x.
Free (QF be db s) x
-> (forall a'. Projectible be a' => Free (QF be db s) a' -> (a' -> Free (QF be db s) x) -> SelectBuilder be db x)
-> SelectBuilder be db x
onlyQ (Free (QAll entityNm mkTbl mkOn next)) f =
f (Free (QAll entityNm mkTbl mkOn (Pure . PreserveLeft))) (next . unPreserveLeft)
onlyQ (Free (QArbitraryJoin entity mkJoin mkOn next)) f =
f (Free (QArbitraryJoin entity mkJoin mkOn Pure)) next
onlyQ (Free (QTwoWayJoin a b mkJoin mkOn next)) f =
f (Free (QTwoWayJoin a b mkJoin mkOn Pure)) next
onlyQ (Free (QSubSelect q' next)) f =
f (Free (QSubSelect q' Pure)) next
onlyQ (Free (QLimit limit q' next)) f =
f (Free (QLimit limit q' Pure)) next
onlyQ (Free (QOffset offset q' next)) f =
f (Free (QOffset offset q' Pure)) next
onlyQ (Free (QSetOp combine a b next)) f =
f (Free (QSetOp combine a b Pure)) next
onlyQ (Free (QOrderBy mkOrdering q' next)) f =
f (Free (QOrderBy mkOrdering q' Pure)) next
onlyQ (Free (QWindowOver mkWindow mkProj q' next)) f =
f (Free (QWindowOver mkWindow mkProj q' Pure)) next
onlyQ (Free (QAggregate mkAgg q' next)) f =
f (Free (QAggregate mkAgg q' Pure)) next
onlyQ (Free (QDistinct d q' next)) f =
f (Free (QDistinct d q' Pure)) next
onlyQ (Free (QForceSelect s over next)) f =
f (Free (QForceSelect s over Pure)) next
onlyQ _ _ = error "impossible"