packages feed

sqel-0.0.1.0: lib/Sqel/Query/SelectExpr.hs

module Sqel.Query.SelectExpr where

import Generics.SOP (All, K (K), NP ((:*)), hcmap, hcollapse)

import Sqel.Class.Mods (GetMod (getMod), MaybeMod (maybeMod))
import Sqel.Data.Dd (
  Comp (Prod, Sum),
  CompInc (Merge, Nest),
  Dd (Dd),
  DdInc (DdMerge, DdNest),
  DdK (DdK),
  DdSort (DdSum),
  DdStruct (DdComp, DdPrim),
  QOp (QAnd),
  Struct (Comp, Prim),
  )
import Sqel.Data.FragType (FragType (Where))
import Sqel.Data.Mods (Ignore (Ignore))
import Sqel.Data.Sel (Sel (SelSymbol, SelUnused), SelW (SelWAuto))
import Sqel.Data.SelectExpr (
  SelectAtom (SelectAtom),
  SelectExpr (SelectExprAtom, SelectExprIgnore, SelectExprList, SelectExprSum),
  )
import Sqel.Data.Selector (Selector (Selector))
import Sqel.Data.Sql (Sql (Sql), sql)
import Sqel.Prim (IndexColumn)
import Sqel.Query.Combinators (whereEq)
import Sqel.Query.Fragments (ColumnPrefix, QFragmentPrefix (qfragmentPrefix), prefixed)
import Sqel.Sql.Prepared (dollar)
import Sqel.Text.DbIdentifier (dbSymbol)

guardSum :: SelectExpr -> SelectExpr
guardSum = \case
  SelectExprAtom Where code -> SelectExprAtom Where \ i -> [sql|(#{dollar i} is null or #{code i})|]
  SelectExprList op sub -> SelectExprList op (guardSum <$> sub)
  expr -> expr

class ToSelectExpr query where
  toSelectExpr :: ColumnPrefix -> Dd query -> SelectExpr

-- TODO this creates an invalid fragment, but it seems not to be used
instance (
    GetMod () SelectAtom ps,
    MaybeMod Ignore ps
  ) => ToSelectExpr ('DdK 'SelUnused ps q 'Prim) where
  toSelectExpr _ (Dd _ p DdPrim) =
    case maybeMod p of
      Just Ignore -> SelectExprIgnore
      Nothing -> SelectExprAtom type_ (code "")
    where
      SelectAtom type_ code = getMod @() whereEq p

instance (
    KnownSymbol n,
    GetMod () SelectAtom ps
  ) => ToSelectExpr ('DdK ('SelSymbol n) ps q 'Prim) where
  toSelectExpr pre (Dd _ p DdPrim) =
    SelectExprAtom type_ (code (Selector (Sql (prefixed (dbSymbol @n) pre))))
    where
      SelectAtom type_ code = getMod @() whereEq p

prodSelectExpr ::
  ∀ sel s .
  All ToSelectExpr s =>
  QFragmentPrefix sel =>
  SelW sel ->
  ColumnPrefix ->
  QOp ->
  NP Dd s ->
  SelectExpr
prodSelectExpr sel pre op =
  SelectExprList op . hcollapse . hcmap (Proxy @ToSelectExpr) (K . toSelectExpr (qfragmentPrefix sel pre))

sumSelectExpr ::
  ∀ sel s .
  All ToSelectExpr s =>
  QFragmentPrefix sel =>
  SelW sel ->
  ColumnPrefix ->
  NP Dd s ->
  SelectExpr
sumSelectExpr sel pre =
  SelectExprSum . hcollapse . hcmap (Proxy @ToSelectExpr) (K . toSelectExpr (qfragmentPrefix sel pre))

-- TODO add QOp param lookup
instance (
    All ToSelectExpr sub,
    QFragmentPrefix sel
  ) => ToSelectExpr ('DdK sel p q ('Comp tsel ('Prod con) 'Nest sub)) where
  toSelectExpr pre = \case
    Dd sel _ (DdComp _ _ DdNest sub) ->
      prodSelectExpr sel pre QAnd sub

instance (
    All ToSelectExpr sub
  ) => ToSelectExpr ('DdK sel p q ('Comp tsel ('Prod con) 'Merge sub)) where
  toSelectExpr pre = \case
    Dd _ _ (DdComp _ _ DdMerge sub) ->
      prodSelectExpr SelWAuto pre QAnd sub

instance (
    All ToSelectExpr sub,
    QFragmentPrefix sel
  ) => ToSelectExpr ('DdK sel p q ('Comp tsel 'Sum 'Nest (IndexColumn name : sub))) where
  toSelectExpr pre = \case
    Dd sel _ (DdComp _ DdSum DdNest (_ :* sub)) ->
      guardSum (sumSelectExpr sel pre sub)