{-# OPTIONS_GHC -fno-warn-name-shadowing -fno-warn-unused-binds #-}
{-# LANGUAGE UndecidableInstances #-}
module Database.Beam.Query.SQL92
( buildSql92Query' ) where
import Database.Beam.Query.Internal
import Database.Beam.Backend.SQL
import Database.Beam.Schema.Tables
import Control.Monad.Free.Church
import Control.Monad.Free
import Control.Monad.Writer
import Data.Maybe
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 ProjectibleWithPredicate c syntax b => ProjectibleWithPredicate c syntax (PreserveLeft a b) where
project' p f (PreserveLeft (a, b)) =
PreserveLeft . (a,) <$> project' p f b
type SelectStmtFn select
= Sql92SelectSelectTableSyntax select
-> [Sql92SelectOrderingSyntax select]
-> Maybe Integer
-> Maybe Integer
-> select
data QueryBuilder select
= QueryBuilder
{ qbNextTblRef :: Int
, qbFrom :: Maybe (Sql92SelectTableFromSyntax (Sql92SelectSelectTableSyntax select))
, qbWhere :: Maybe (Sql92SelectExpressionSyntax select) }
data SelectBuilder syntax (db :: (* -> *) -> *) a where
SelectBuilderQ :: ( IsSql92SelectSyntax syntax
, Projectible (Sql92ProjectionExpressionSyntax (Sql92SelectTableProjectionSyntax (Sql92SelectSelectTableSyntax syntax))) a ) =>
a -> QueryBuilder syntax -> SelectBuilder syntax db a
SelectBuilderGrouping
:: ( IsSql92SelectSyntax syntax
, Projectible (Sql92ProjectionExpressionSyntax (Sql92SelectTableProjectionSyntax (Sql92SelectSelectTableSyntax syntax))) a )
=> a -> QueryBuilder syntax
-> Maybe (Sql92SelectGroupingSyntax syntax)
-> Maybe (Sql92SelectExpressionSyntax syntax)
-> Maybe (Sql92SelectTableSetQuantifierSyntax (Sql92SelectSelectTableSyntax syntax))
-> SelectBuilder syntax db a
SelectBuilderSelectSyntax :: Bool
-> a -> Sql92SelectSelectTableSyntax syntax
-> SelectBuilder syntax db a
SelectBuilderTopLevel ::
{ sbLimit, sbOffset :: Maybe Integer
, sbOrdering :: [ Sql92SelectOrderingSyntax syntax ]
, sbTable :: SelectBuilder syntax db a
, sbSelectFn :: Maybe (SelectStmtFn syntax)
} -> SelectBuilder syntax 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 expr x =>
TablePrefix -> x -> [ ( expr, Maybe T.Text ) ]
defaultProjection pfx =
zipWith (\i e -> (e, Just (fromString "res" <> fromString (show (i :: Integer)))))
[0..] . flip project (nextTblPfx pfx)
buildSelect :: ( IsSql92SelectSyntax syntax
, Projectible (Sql92ProjectionExpressionSyntax (Sql92SelectProjectionSyntax syntax)) a ) =>
TablePrefix -> SelectBuilder syntax db a -> syntax
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 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 pfx proj)) from where_ grouping having) ordering limit offset
buildSelect pfx x = buildSelect pfx (SelectBuilderTopLevel Nothing Nothing [] x Nothing)
selectBuilderToTableSource :: ( Sql92TableSourceSelectSyntax (Sql92FromTableSourceSyntax (Sql92SelectFromSyntax syntax)) ~ syntax
, IsSql92SelectSyntax syntax
, Projectible (Sql92ProjectionExpressionSyntax (Sql92SelectProjectionSyntax syntax)) a ) =>
TablePrefix -> SelectBuilder syntax db a -> Sql92SelectSelectTableSyntax syntax
selectBuilderToTableSource _ (SelectBuilderSelectSyntax _ _ x) = x
selectBuilderToTableSource pfx (SelectBuilderQ x (QueryBuilder _ from where_)) =
selectTableStmt Nothing (projExprs (defaultProjection pfx x)) from where_ Nothing Nothing
selectBuilderToTableSource pfx (SelectBuilderGrouping x (QueryBuilder _ from where_) grouping having distinct) =
selectTableStmt distinct (projExprs (defaultProjection pfx x)) from where_ grouping having
selectBuilderToTableSource pfx sb =
let (x, QueryBuilder _ from where_) = selectBuilderToQueryBuilder pfx sb
in selectTableStmt Nothing (projExprs (defaultProjection pfx x)) from where_ Nothing Nothing
selectBuilderToQueryBuilder :: ( Sql92TableSourceSelectSyntax (Sql92FromTableSourceSyntax (Sql92SelectFromSyntax syntax)) ~ syntax
, IsSql92SelectSyntax syntax
, Projectible (Sql92ProjectionExpressionSyntax (Sql92SelectProjectionSyntax syntax)) a ) =>
TablePrefix -> SelectBuilder syntax db a -> (a, QueryBuilder syntax)
selectBuilderToQueryBuilder pfx sb =
let select = buildSelect pfx sb
x' = reproject (fieldNameFunc (qualifiedField t0)) (sbProj sb)
t0 = pfx <> "0"
in (x', QueryBuilder 1 (Just (fromTable (tableFromSubSelect select) (Just t0))) 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 (Sql92ProjectionExpressionSyntax (Sql92SelectProjectionSyntax syntax)) b =>
SelectBuilder syntax db a -> b -> SelectBuilder syntax 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
:: ( IsSql92SelectSyntax select
, Projectible (Sql92SelectExpressionSyntax select) x
, Sql92TableSourceSelectSyntax (Sql92FromTableSourceSyntax (Sql92SelectFromSyntax select)) ~ select )
=> TablePrefix -> select
-> x -> QueryBuilder select
-> (x, QueryBuilder select)
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)
newTblNm = tblPfx <> fromString (show tblRef)
in (reproject (fieldNameFunc (qualifiedField newTblNm)) x, qb')
buildInnerJoinQuery
:: forall select s table
. (Beamable table, IsSql92SelectSyntax select)
=> TablePrefix -> (TablePrefix -> T.Text -> Sql92SelectFromSyntax select) -> TableSettings table
-> (table (QExpr (Sql92SelectExpressionSyntax select) s) -> Maybe (WithExprContext (Sql92SelectExpressionSyntax select)))
-> QueryBuilder select -> (T.Text, table (QExpr (Sql92SelectExpressionSyntax select) s), QueryBuilder select)
buildInnerJoinQuery tblPfx mkFrom tblSettings 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 = changeBeamRep (\(Columnar' f) -> Columnar' (QExpr (\_ -> fieldE (qualifiedField newTblNm (_fieldName f))))) tblSettings
in (newTblNm, newTbl, qb')
nextTbl :: (IsSql92SelectSyntax select, Beamable table)
=> QueryBuilder select
-> TablePrefix -> TableSettings table
-> ( table (QExpr (Sql92SelectExpressionSyntax select) s)
, T.Text
, QueryBuilder select )
nextTbl qb tblPfx tblSettings =
let tblRef = qbNextTblRef qb
newTblNm = tblPfx <> fromString (show tblRef)
newTbl = changeBeamRep (\(Columnar' f) -> Columnar' (QExpr (\_ -> fieldE (qualifiedField newTblNm (_fieldName f))))) tblSettings
in (newTbl, newTblNm, qb { qbNextTblRef = qbNextTblRef qb + 1})
projOrder :: Projectible expr x =>
x -> WithExprContext [ expr ]
projOrder = project
buildSql92Query' ::
forall select projSyntax db s a.
( IsSql92SelectSyntax select
, Eq (Sql92SelectExpressionSyntax select)
, projSyntax ~ Sql92SelectTableProjectionSyntax (Sql92SelectSelectTableSyntax select)
, Sql92TableSourceSelectSyntax (Sql92FromTableSourceSyntax (Sql92SelectFromSyntax select)) ~ select
, Sql92ProjectionExpressionSyntax projSyntax ~ Sql92SelectExpressionSyntax select
, Projectible (Sql92ProjectionExpressionSyntax projSyntax) a ) =>
Bool ->
T.Text ->
Q select db s a ->
select
buildSql92Query' arbitrarilyNestedCombinations tblPfx (Q q) =
buildSelect tblPfx (buildQuery (fromF q))
where
buildQuery :: forall s x.
Projectible (Sql92ProjectionExpressionSyntax projSyntax) x =>
Free (QF select db s) x
-> SelectBuilder select 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 (QUnion all_ left right next)) =
buildTableCombination (unionTables all_) left right next
buildQuery (Free (QIntersect all_ left right next)) =
buildTableCombination (intersectTables all_) left right next
buildQuery (Free (QExcept all_ left right next)) =
buildTableCombination (exceptTable all_) 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 select db s) x
-> Maybe (Sql92SelectExpressionSyntax select)
-> Maybe (x, Maybe (Sql92SelectExpressionSyntax select))
tryBuildGuardsOnly next having =
case tryCollectHaving next having of
(Pure x, having') -> Just (x, having')
_ -> Nothing
tryCollectHaving :: forall s x.
Free (QF select db s) x
-> Maybe (Sql92SelectExpressionSyntax select)
-> (Free (QF select db s) x, Maybe (Sql92SelectExpressionSyntax select))
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 (Sql92ProjectionExpressionSyntax projSyntax) r
, Projectible (Sql92ProjectionExpressionSyntax projSyntax) x ) =>
(Sql92SelectSelectTableSyntax select -> Sql92SelectSelectTableSyntax select -> Sql92SelectSelectTableSyntax select) ->
QM select db (QNested s) x -> QM select db (QNested s) x -> (x -> Free (QF select db s) r) -> SelectBuilder select 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 (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 proj (nextTblPfx tblPfx) == projOrder proj' (nextTblPfx tblPfx) ->
setSelectBuilderProjection sb proj'
_ -> let (x', qb) = selectBuilderToQueryBuilder tblPfx sb
in buildJoinedQuery (next x') qb
buildJoinedQuery :: forall s x.
Projectible (Sql92ProjectionExpressionSyntax projSyntax) x =>
Free (QF select db s) x -> QueryBuilder select -> SelectBuilder select db x
buildJoinedQuery (Pure x) qb = SelectBuilderQ x qb
buildJoinedQuery (Free (QAll mkFrom tblSettings on next)) qb =
let (newTblNm, newTbl, qb') = buildInnerJoinQuery tblPfx mkFrom tblSettings on qb
in buildJoinedQuery (next (newTblNm, newTbl)) qb'
buildJoinedQuery (Free (QArbitraryJoin q mkJoin on next)) qb =
case fromF q of
Free (QAll mkDbFrom dbTblSettings on' next')
| (newTbl, newTblNm, qb') <- nextTbl qb tblPfx dbTblSettings,
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)
proj' = reproject (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 dbTblSettings on' next')
| (newTbl, newTblNm, qb') <- nextTbl qb tblPfx dbTblSettings,
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 (fieldNameFunc (qualifiedField newTblNm)) (sbProj sb)
in (proj', fromTable (tableFromSubSelect tblSource) (Just newTblNm), qb { qbNextTblRef = qbNextTblRef qb + 1 })
(bProj, bSource, qb'') =
case fromF b of
Free (QAll mkDbFrom dbTblSettings on' next')
| (newTbl, newTblNm, qb'') <- nextTbl qb' tblPfx dbTblSettings,
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 (fieldNameFunc (qualifiedField newTblNm)) (sbProj sb)
in (proj', fromTable (tableFromSubSelect tblSource) (Just newTblNm), 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 select db s) x
-> (forall a'. Projectible (Sql92SelectExpressionSyntax select) a' => Free (QF select db s) a' -> (a' -> Free (QF select db s) x) -> SelectBuilder select db x)
-> SelectBuilder select db x
onlyQ (Free (QAll entityNm entitySettings mkOn next)) f =
f (Free (QAll entityNm entitySettings 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 (QUnion all_ a b next)) f =
f (Free (QUnion all_ a b Pure)) next
onlyQ (Free (QIntersect all_ a b next)) f =
f (Free (QIntersect all_ a b Pure)) next
onlyQ (Free (QExcept all_ a b next)) f =
f (Free (QExcept all_ 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"