-- | This module defines the top-level translation functions pertaining to
-- queries that are not aggregation queries, i.e. so-called "simple" selects
-- into Postgres AST.
module Hasura.Backends.Postgres.Translate.Select.Simple
  ( mkSQLSelect,
    selectQuerySQL,
  )
where

import Control.Monad.Writer.Strict (runWriter)
import Database.PG.Query (Query)
import Hasura.Backends.Postgres.SQL.DML qualified as S
import Hasura.Backends.Postgres.SQL.Types (IsIdentifier (toIdentifier))
import Hasura.Backends.Postgres.Translate.Select.AnnotatedFieldJSON
import Hasura.Backends.Postgres.Translate.Select.Internal.Extractor (asJsonAggExtr)
import Hasura.Backends.Postgres.Translate.Select.Internal.GenerateSelect (generateSQLSelectFromArrayNode)
import Hasura.Backends.Postgres.Translate.Select.Internal.Helpers (selectToSelectWith, toQuery)
import Hasura.Backends.Postgres.Translate.Select.Internal.Process (processAnnSimpleSelect)
import Hasura.Backends.Postgres.Translate.Types
import Hasura.Prelude
import Hasura.RQL.IR.Select
  ( AnnSelectG (_asnStrfyNum),
    AnnSimpleSelect,
  )
import Hasura.RQL.Types.Backend (Backend)
import Hasura.RQL.Types.BackendType (BackendType (Postgres))
import Hasura.RQL.Types.Common
  ( FieldName (FieldName),
    JsonAggSelect,
  )

-- | Translates IR to Postgres queries for simple SELECTs (select queries that
-- are not aggregations, including subscriptions).
--
-- See 'mkSQLSelect' for the Postgres AST.
selectQuerySQL ::
  forall pgKind.
  (Backend ('Postgres pgKind), PostgresAnnotatedFieldJSON pgKind) =>
  JsonAggSelect ->
  AnnSimpleSelect ('Postgres pgKind) ->
  Query
selectQuerySQL :: forall (pgKind :: PostgresKind).
(Backend ('Postgres pgKind), PostgresAnnotatedFieldJSON pgKind) =>
JsonAggSelect -> AnnSimpleSelect ('Postgres pgKind) -> Query
selectQuerySQL JsonAggSelect
jsonAggSelect =
  SelectWithG TopLevelCTE -> Query
toQuery
    (SelectWithG TopLevelCTE -> Query)
-> (AnnSimpleSelectG ('Postgres pgKind) Void SQLExp
    -> SelectWithG TopLevelCTE)
-> AnnSimpleSelectG ('Postgres pgKind) Void SQLExp
-> Query
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Writer CustomSQLCTEs Select -> SelectWithG TopLevelCTE
selectToSelectWith
    (Writer CustomSQLCTEs Select -> SelectWithG TopLevelCTE)
-> (AnnSimpleSelectG ('Postgres pgKind) Void SQLExp
    -> Writer CustomSQLCTEs Select)
-> AnnSimpleSelectG ('Postgres pgKind) Void SQLExp
-> SelectWithG TopLevelCTE
forall b c a. (b -> c) -> (a -> b) -> a -> c
. JsonAggSelect
-> AnnSelectG
     ('Postgres pgKind)
     (AnnFieldG ('Postgres pgKind) Void)
     (SQLExpression ('Postgres pgKind))
-> Writer CustomSQLCTEs Select
forall (pgKind :: PostgresKind) (m :: * -> *).
(Backend ('Postgres pgKind), PostgresAnnotatedFieldJSON pgKind,
 MonadWriter CustomSQLCTEs m) =>
JsonAggSelect -> AnnSimpleSelect ('Postgres pgKind) -> m Select
mkSQLSelect JsonAggSelect
jsonAggSelect

mkSQLSelect ::
  forall pgKind m.
  ( Backend ('Postgres pgKind),
    PostgresAnnotatedFieldJSON pgKind,
    MonadWriter CustomSQLCTEs m
  ) =>
  JsonAggSelect ->
  AnnSimpleSelect ('Postgres pgKind) ->
  m S.Select
mkSQLSelect :: forall (pgKind :: PostgresKind) (m :: * -> *).
(Backend ('Postgres pgKind), PostgresAnnotatedFieldJSON pgKind,
 MonadWriter CustomSQLCTEs m) =>
JsonAggSelect -> AnnSimpleSelect ('Postgres pgKind) -> m Select
mkSQLSelect JsonAggSelect
jsonAggSelect AnnSimpleSelect ('Postgres pgKind)
annSel = do
  let permLimitSubQuery :: PermissionLimitSubQuery
permLimitSubQuery = PermissionLimitSubQuery
PLSQNotRequired
      -- run an intermediary step in order to obtain:
      -- SelectSource: the primary source, along with its where clause and sorting/slicing information
      -- NodeExtractors: a map from aliases which need to be selected to the corresponding required SQLExp to select the value
      -- : the join tree required for relationships (built via @MonadWriter@)
      -- : any top-level Common Table Expressions needed for Native Queries
      ((SelectSource
selectSource, InsOrdHashMap ColumnAlias SQLExp
nodeExtractors), SelectWriter {_swJoinTree :: SelectWriter -> JoinTree
_swJoinTree = JoinTree
joinTree, _swCustomSQLCTEs :: SelectWriter -> CustomSQLCTEs
_swCustomSQLCTEs = CustomSQLCTEs
customSQLCTEs}) =
        Writer
  SelectWriter (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
-> ((SelectSource, InsOrdHashMap ColumnAlias SQLExp), SelectWriter)
forall w a. Writer w a -> (a, w)
runWriter
          (Writer
   SelectWriter (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
 -> ((SelectSource, InsOrdHashMap ColumnAlias SQLExp),
     SelectWriter))
-> Writer
     SelectWriter (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
-> ((SelectSource, InsOrdHashMap ColumnAlias SQLExp), SelectWriter)
forall a b. (a -> b) -> a -> b
$ (ReaderT
   StringifyNumbers
   (WriterT SelectWriter Identity)
   (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
 -> StringifyNumbers
 -> Writer
      SelectWriter (SelectSource, InsOrdHashMap ColumnAlias SQLExp))
-> StringifyNumbers
-> ReaderT
     StringifyNumbers
     (WriterT SelectWriter Identity)
     (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
-> Writer
     SelectWriter (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
forall a b c. (a -> b -> c) -> b -> a -> c
flip ReaderT
  StringifyNumbers
  (WriterT SelectWriter Identity)
  (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
-> StringifyNumbers
-> Writer
     SelectWriter (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT StringifyNumbers
strfyNum
          (ReaderT
   StringifyNumbers
   (WriterT SelectWriter Identity)
   (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
 -> Writer
      SelectWriter (SelectSource, InsOrdHashMap ColumnAlias SQLExp))
-> ReaderT
     StringifyNumbers
     (WriterT SelectWriter Identity)
     (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
-> Writer
     SelectWriter (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
forall a b. (a -> b) -> a -> b
$ (StateT
   NativeQueryFreshIdStore
   (ReaderT StringifyNumbers (WriterT SelectWriter Identity))
   (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
 -> NativeQueryFreshIdStore
 -> ReaderT
      StringifyNumbers
      (WriterT SelectWriter Identity)
      (SelectSource, InsOrdHashMap ColumnAlias SQLExp))
-> NativeQueryFreshIdStore
-> StateT
     NativeQueryFreshIdStore
     (ReaderT StringifyNumbers (WriterT SelectWriter Identity))
     (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
-> ReaderT
     StringifyNumbers
     (WriterT SelectWriter Identity)
     (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
forall a b c. (a -> b -> c) -> b -> a -> c
flip StateT
  NativeQueryFreshIdStore
  (ReaderT StringifyNumbers (WriterT SelectWriter Identity))
  (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
-> NativeQueryFreshIdStore
-> ReaderT
     StringifyNumbers
     (WriterT SelectWriter Identity)
     (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m a
evalStateT NativeQueryFreshIdStore
initialNativeQueryFreshIdStore
          (StateT
   NativeQueryFreshIdStore
   (ReaderT StringifyNumbers (WriterT SelectWriter Identity))
   (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
 -> ReaderT
      StringifyNumbers
      (WriterT SelectWriter Identity)
      (SelectSource, InsOrdHashMap ColumnAlias SQLExp))
-> StateT
     NativeQueryFreshIdStore
     (ReaderT StringifyNumbers (WriterT SelectWriter Identity))
     (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
-> ReaderT
     StringifyNumbers
     (WriterT SelectWriter Identity)
     (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
forall a b. (a -> b) -> a -> b
$ SourcePrefixes
-> FieldName
-> PermissionLimitSubQuery
-> AnnSimpleSelect ('Postgres pgKind)
-> StateT
     NativeQueryFreshIdStore
     (ReaderT StringifyNumbers (WriterT SelectWriter Identity))
     (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
forall (pgKind :: PostgresKind) (m :: * -> *).
(MonadReader StringifyNumbers m,
 MonadState NativeQueryFreshIdStore m, MonadWriter SelectWriter m,
 Backend ('Postgres pgKind), PostgresAnnotatedFieldJSON pgKind) =>
SourcePrefixes
-> FieldName
-> PermissionLimitSubQuery
-> AnnSimpleSelect ('Postgres pgKind)
-> m (SelectSource, InsOrdHashMap ColumnAlias SQLExp)
processAnnSimpleSelect SourcePrefixes
sourcePrefixes FieldName
rootFldName PermissionLimitSubQuery
permLimitSubQuery AnnSimpleSelect ('Postgres pgKind)
annSel

      selectNode :: SelectNode
selectNode = InsOrdHashMap ColumnAlias SQLExp -> JoinTree -> SelectNode
SelectNode InsOrdHashMap ColumnAlias SQLExp
nodeExtractors JoinTree
joinTree
      topExtractor :: Extractor
topExtractor =
        JsonAggSelect
-> ColumnAlias
-> PermissionLimitSubQuery
-> Maybe OrderByExp
-> Extractor
asJsonAggExtr JsonAggSelect
jsonAggSelect ColumnAlias
rootFldAls PermissionLimitSubQuery
permLimitSubQuery
          (Maybe OrderByExp -> Extractor) -> Maybe OrderByExp -> Extractor
forall a b. (a -> b) -> a -> b
$ SelectSource -> Maybe OrderByExp
orderByForJsonAgg SelectSource
selectSource
      arrayNode :: MultiRowSelectNode
arrayNode = [Extractor] -> SelectNode -> MultiRowSelectNode
MultiRowSelectNode [Extractor
topExtractor] SelectNode
selectNode
  CustomSQLCTEs -> m ()
forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell CustomSQLCTEs
customSQLCTEs

  Select -> m Select
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Select -> m Select) -> Select -> m 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
S.BELit Bool
True
  where
    strfyNum :: StringifyNumbers
strfyNum = AnnSelectG
  ('Postgres pgKind) (AnnFieldG ('Postgres pgKind) Void) SQLExp
-> StringifyNumbers
forall (b :: BackendType) (f :: * -> *) v.
AnnSelectG b f v -> StringifyNumbers
_asnStrfyNum AnnSimpleSelect ('Postgres pgKind)
AnnSelectG
  ('Postgres pgKind) (AnnFieldG ('Postgres pgKind) Void) SQLExp
annSel
    rootFldIdentifier :: Identifier
rootFldIdentifier = FieldName -> Identifier
forall a. IsIdentifier a => a -> Identifier
toIdentifier FieldName
rootFldName
    sourcePrefixes :: SourcePrefixes
sourcePrefixes = Identifier -> Identifier -> SourcePrefixes
SourcePrefixes Identifier
rootFldIdentifier Identifier
rootFldIdentifier
    rootFldName :: FieldName
rootFldName = Text -> FieldName
FieldName Text
"root"
    rootFldAls :: ColumnAlias
rootFldAls = Identifier -> ColumnAlias
forall a. IsIdentifier a => a -> ColumnAlias
S.toColumnAlias (Identifier -> ColumnAlias) -> Identifier -> ColumnAlias
forall a b. (a -> b) -> a -> b
$ FieldName -> Identifier
forall a. IsIdentifier a => a -> Identifier
toIdentifier FieldName
rootFldName