-- | This module defines the top-level translation functions pertaining to
-- queries that use aggregation (i.e., /not/ so-called "simple" selects) into
-- Postgres AST.
module Hasura.Backends.Postgres.Translate.Select.Aggregate
  ( mkAggregateSelect,
    selectAggregateQuerySQL,
  )
where

import Control.Monad.Writer.Strict (runWriter)
import Database.PG.Query (Query, fromBuilder)
import Hasura.Backends.Postgres.SQL.DML (BoolExp (BELit), Select)
import Hasura.Backends.Postgres.SQL.RenameIdentifiers
  ( renameIdentifiers,
  )
import Hasura.Backends.Postgres.SQL.Types
  ( IsIdentifier (toIdentifier),
  )
import Hasura.Backends.Postgres.Translate.Select.AnnotatedFieldJSON
import Hasura.Backends.Postgres.Translate.Select.Internal.GenerateSelect (generateSQLSelectFromArrayNode)
import Hasura.Backends.Postgres.Translate.Select.Internal.Process (processAnnAggregateSelect)
import Hasura.Backends.Postgres.Translate.Types
  ( MultiRowSelectNode (MultiRowSelectNode),
    SelectNode (SelectNode),
    SourcePrefixes (SourcePrefixes),
  )
import Hasura.Prelude
import Hasura.RQL.IR.Select
  ( AnnAggregateSelect,
    AnnSelectG (_asnStrfyNum),
  )
import Hasura.RQL.Types.Backend (Backend)
import Hasura.RQL.Types.Common (FieldName (FieldName))
import Hasura.SQL.Backend (BackendType (Postgres))
import Hasura.SQL.Types (ToSQL (toSQL))

-- | Translates IR to Postgres queries for aggregated SELECTs.
--
-- See 'mkAggregateSelect' for the Postgres AST.
selectAggregateQuerySQL ::
  forall pgKind.
  (Backend ('Postgres pgKind), PostgresAnnotatedFieldJSON pgKind) =>
  AnnAggregateSelect ('Postgres pgKind) ->
  Query
selectAggregateQuerySQL :: AnnAggregateSelect ('Postgres pgKind) -> Query
selectAggregateQuerySQL =
  Builder -> Query
fromBuilder (Builder -> Query)
-> (AnnAggregateSelectG ('Postgres pgKind) Void SQLExp -> Builder)
-> AnnAggregateSelectG ('Postgres pgKind) Void SQLExp
-> Query
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Select -> Builder
forall a. ToSQL a => a -> Builder
toSQL (Select -> Builder)
-> (AnnAggregateSelectG ('Postgres pgKind) Void SQLExp -> Select)
-> AnnAggregateSelectG ('Postgres pgKind) Void SQLExp
-> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AnnAggregateSelectG ('Postgres pgKind) Void SQLExp -> Select
forall (pgKind :: PostgresKind).
(Backend ('Postgres pgKind), PostgresAnnotatedFieldJSON pgKind) =>
AnnAggregateSelect ('Postgres pgKind) -> Select
mkAggregateSelect

mkAggregateSelect ::
  forall pgKind.
  ( Backend ('Postgres pgKind),
    PostgresAnnotatedFieldJSON pgKind
  ) =>
  AnnAggregateSelect ('Postgres pgKind) ->
  Select
mkAggregateSelect :: AnnAggregateSelect ('Postgres pgKind) -> Select
mkAggregateSelect AnnAggregateSelect ('Postgres pgKind)
annAggSel =
  let ((SelectSource
selectSource, HashMap ColumnAlias SQLExp
nodeExtractors, Extractor
topExtractor), JoinTree
joinTree) =
        Writer
  JoinTree (SelectSource, HashMap ColumnAlias SQLExp, Extractor)
-> ((SelectSource, HashMap ColumnAlias SQLExp, Extractor),
    JoinTree)
forall w a. Writer w a -> (a, w)
runWriter (Writer
   JoinTree (SelectSource, HashMap ColumnAlias SQLExp, Extractor)
 -> ((SelectSource, HashMap ColumnAlias SQLExp, Extractor),
     JoinTree))
-> Writer
     JoinTree (SelectSource, HashMap ColumnAlias SQLExp, Extractor)
-> ((SelectSource, HashMap ColumnAlias SQLExp, Extractor),
    JoinTree)
forall a b. (a -> b) -> a -> b
$
          (ReaderT
   StringifyNumbers
   (WriterT JoinTree Identity)
   (SelectSource, HashMap ColumnAlias SQLExp, Extractor)
 -> StringifyNumbers
 -> Writer
      JoinTree (SelectSource, HashMap ColumnAlias SQLExp, Extractor))
-> StringifyNumbers
-> ReaderT
     StringifyNumbers
     (WriterT JoinTree Identity)
     (SelectSource, HashMap ColumnAlias SQLExp, Extractor)
-> Writer
     JoinTree (SelectSource, HashMap ColumnAlias SQLExp, Extractor)
forall a b c. (a -> b -> c) -> b -> a -> c
flip ReaderT
  StringifyNumbers
  (WriterT JoinTree Identity)
  (SelectSource, HashMap ColumnAlias SQLExp, Extractor)
-> StringifyNumbers
-> Writer
     JoinTree (SelectSource, HashMap ColumnAlias SQLExp, Extractor)
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT StringifyNumbers
strfyNum (ReaderT
   StringifyNumbers
   (WriterT JoinTree Identity)
   (SelectSource, HashMap ColumnAlias SQLExp, Extractor)
 -> Writer
      JoinTree (SelectSource, HashMap ColumnAlias SQLExp, Extractor))
-> ReaderT
     StringifyNumbers
     (WriterT JoinTree Identity)
     (SelectSource, HashMap ColumnAlias SQLExp, Extractor)
-> Writer
     JoinTree (SelectSource, HashMap ColumnAlias SQLExp, Extractor)
forall a b. (a -> b) -> a -> b
$
            SourcePrefixes
-> FieldName
-> AnnAggregateSelect ('Postgres pgKind)
-> ReaderT
     StringifyNumbers
     (WriterT JoinTree Identity)
     (SelectSource, HashMap ColumnAlias SQLExp, Extractor)
forall (pgKind :: PostgresKind) (m :: * -> *).
(MonadReader StringifyNumbers m, MonadWriter JoinTree m,
 Backend ('Postgres pgKind), PostgresAnnotatedFieldJSON pgKind) =>
SourcePrefixes
-> FieldName
-> AnnAggregateSelect ('Postgres pgKind)
-> m (SelectSource, HashMap ColumnAlias SQLExp, Extractor)
processAnnAggregateSelect SourcePrefixes
sourcePrefixes FieldName
rootFieldName AnnAggregateSelect ('Postgres pgKind)
annAggSel
      selectNode :: SelectNode
selectNode = HashMap ColumnAlias SQLExp -> JoinTree -> SelectNode
SelectNode HashMap ColumnAlias SQLExp
nodeExtractors JoinTree
joinTree
      arrayNode :: MultiRowSelectNode
arrayNode = [Extractor] -> SelectNode -> MultiRowSelectNode
MultiRowSelectNode [Extractor
topExtractor] SelectNode
selectNode
   in Select -> Select
renameIdentifiers (Select -> Select) -> Select -> Select
forall a b. (a -> b) -> a -> b
$
        SelectSource -> MultiRowSelectNode -> BoolExp -> Select
generateSQLSelectFromArrayNode SelectSource
selectSource MultiRowSelectNode
arrayNode (BoolExp -> Select) -> BoolExp -> Select
forall a b. (a -> b) -> a -> b
$ Bool -> BoolExp
BELit Bool
True
  where
    strfyNum :: StringifyNumbers
strfyNum = AnnSelectG
  ('Postgres pgKind)
  (TableAggregateFieldG ('Postgres pgKind) Void)
  SQLExp
-> StringifyNumbers
forall (b :: BackendType) (f :: * -> *) v.
AnnSelectG b f v -> StringifyNumbers
_asnStrfyNum AnnAggregateSelect ('Postgres pgKind)
AnnSelectG
  ('Postgres pgKind)
  (TableAggregateFieldG ('Postgres pgKind) Void)
  SQLExp
annAggSel
    rootFieldName :: FieldName
rootFieldName = Text -> FieldName
FieldName Text
"root"
    rootIdentifier :: Identifier
rootIdentifier = FieldName -> Identifier
forall a. IsIdentifier a => a -> Identifier
toIdentifier FieldName
rootFieldName
    sourcePrefixes :: SourcePrefixes
sourcePrefixes = Identifier -> Identifier -> SourcePrefixes
SourcePrefixes Identifier
rootIdentifier Identifier
rootIdentifier