-- | Extractors are a pair of an SQL expression and an alias; they get
-- translated like "[SELECT ...] <expr> as <alias>"
module Hasura.Backends.Postgres.Translate.Select.Internal.Extractor
  ( aggregateFieldsToExtractorExps,
    mkAggregateOrderByExtractorAndFields,
    withJsonAggExtr,
    asSingleRowExtr,
    asJsonAggExtr,
  )
where

import Control.Monad.Writer.Strict
import Data.List.NonEmpty qualified as NE
import Hasura.Backends.Postgres.SQL.DML qualified as S
import Hasura.Backends.Postgres.SQL.Types
import Hasura.Backends.Postgres.Translate.Select.Internal.Aliases
import Hasura.Backends.Postgres.Translate.Types (PermissionLimitSubQuery (..))
import Hasura.Prelude
import Hasura.RQL.IR.Select
import Hasura.RQL.Types.Backend
import Hasura.RQL.Types.Column
import Hasura.RQL.Types.Common
import Hasura.SQL.Backend

aggregateFieldsToExtractorExps ::
  Identifier -> AggregateFields ('Postgres pgKind) -> [(S.ColumnAlias, S.SQLExp)]
aggregateFieldsToExtractorExps :: Identifier
-> AggregateFields ('Postgres pgKind) -> [(ColumnAlias, SQLExp)]
aggregateFieldsToExtractorExps Identifier
sourcePrefix AggregateFields ('Postgres pgKind)
aggregateFields =
  (((FieldName, AggregateField ('Postgres pgKind))
  -> [(ColumnAlias, SQLExp)])
 -> AggregateFields ('Postgres pgKind) -> [(ColumnAlias, SQLExp)])
-> AggregateFields ('Postgres pgKind)
-> ((FieldName, AggregateField ('Postgres pgKind))
    -> [(ColumnAlias, SQLExp)])
-> [(ColumnAlias, SQLExp)]
forall a b c. (a -> b -> c) -> b -> a -> c
flip ((FieldName, AggregateField ('Postgres pgKind))
 -> [(ColumnAlias, SQLExp)])
-> AggregateFields ('Postgres pgKind) -> [(ColumnAlias, SQLExp)]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap AggregateFields ('Postgres pgKind)
aggregateFields (((FieldName, AggregateField ('Postgres pgKind))
  -> [(ColumnAlias, SQLExp)])
 -> [(ColumnAlias, SQLExp)])
-> ((FieldName, AggregateField ('Postgres pgKind))
    -> [(ColumnAlias, SQLExp)])
-> [(ColumnAlias, SQLExp)]
forall a b. (a -> b) -> a -> b
$ \(FieldName
_, AggregateField ('Postgres pgKind)
field) ->
    case AggregateField ('Postgres pgKind)
field of
      AFCount CountType ('Postgres pgKind)
cty -> case CountType ('Postgres pgKind)
cty of
        CountType ('Postgres pgKind)
S.CTStar -> []
        S.CTSimple cols -> [PGCol] -> [(ColumnAlias, SQLExp)]
colsToExps [PGCol]
cols
        S.CTDistinct cols -> [PGCol] -> [(ColumnAlias, SQLExp)]
colsToExps [PGCol]
cols
      AFOp AggregateOp ('Postgres pgKind)
aggOp -> AggregateOp ('Postgres pgKind) -> [(ColumnAlias, SQLExp)]
aggOpToExps AggregateOp ('Postgres pgKind)
aggOp
      AFExp Text
_ -> []
  where
    colsToExps :: [PGCol] -> [(ColumnAlias, SQLExp)]
colsToExps = (PGCol -> (ColumnAlias, SQLExp))
-> [PGCol] -> [(ColumnAlias, SQLExp)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap PGCol -> (ColumnAlias, SQLExp)
mkColExp

    aggOpToExps :: AggregateOp ('Postgres pgKind) -> [(ColumnAlias, SQLExp)]
aggOpToExps = ((FieldName, ColFld ('Postgres pgKind))
 -> Maybe (ColumnAlias, SQLExp))
-> [(FieldName, ColFld ('Postgres pgKind))]
-> [(ColumnAlias, SQLExp)]
forall (f :: * -> *) a b.
Filterable f =>
(a -> Maybe b) -> f a -> f b
mapMaybe (FieldName, ColFld ('Postgres pgKind))
-> Maybe (ColumnAlias, SQLExp)
colToMaybeExp ([(FieldName, ColFld ('Postgres pgKind))]
 -> [(ColumnAlias, SQLExp)])
-> (AggregateOp ('Postgres pgKind)
    -> [(FieldName, ColFld ('Postgres pgKind))])
-> AggregateOp ('Postgres pgKind)
-> [(ColumnAlias, SQLExp)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AggregateOp ('Postgres pgKind)
-> [(FieldName, ColFld ('Postgres pgKind))]
forall (b :: BackendType). AggregateOp b -> ColumnFields b
_aoFields
    colToMaybeExp :: (FieldName, ColFld ('Postgres pgKind))
-> Maybe (ColumnAlias, SQLExp)
colToMaybeExp = \case
      (FieldName
_, CFCol Column ('Postgres pgKind)
col ColumnType ('Postgres pgKind)
_) -> (ColumnAlias, SQLExp) -> Maybe (ColumnAlias, SQLExp)
forall a. a -> Maybe a
Just ((ColumnAlias, SQLExp) -> Maybe (ColumnAlias, SQLExp))
-> (ColumnAlias, SQLExp) -> Maybe (ColumnAlias, SQLExp)
forall a b. (a -> b) -> a -> b
$ PGCol -> (ColumnAlias, SQLExp)
mkColExp Column ('Postgres pgKind)
PGCol
col
      (FieldName, ColFld ('Postgres pgKind))
_ -> Maybe (ColumnAlias, SQLExp)
forall a. Maybe a
Nothing

    mkColExp :: PGCol -> (ColumnAlias, SQLExp)
mkColExp PGCol
c =
      let qualCol :: SQLExp
qualCol = TableAlias -> Identifier -> SQLExp
forall a b. (IsIdentifier a, IsIdentifier b) => a -> b -> SQLExp
S.mkQIdenExp (Identifier -> TableAlias
mkBaseTableAlias Identifier
sourcePrefix) (PGCol -> Identifier
forall a. IsIdentifier a => a -> Identifier
toIdentifier PGCol
c)
          colAls :: Identifier
colAls = PGCol -> Identifier
forall a. IsIdentifier a => a -> Identifier
toIdentifier PGCol
c
       in (Identifier -> ColumnAlias
forall a. IsIdentifier a => a -> ColumnAlias
S.toColumnAlias Identifier
colAls, SQLExp
qualCol)

mkAggregateOrderByExtractorAndFields ::
  forall pgKind.
  Backend ('Postgres pgKind) =>
  AnnotatedAggregateOrderBy ('Postgres pgKind) ->
  (S.Extractor, AggregateFields ('Postgres pgKind))
mkAggregateOrderByExtractorAndFields :: AnnotatedAggregateOrderBy ('Postgres pgKind)
-> (Extractor, AggregateFields ('Postgres pgKind))
mkAggregateOrderByExtractorAndFields AnnotatedAggregateOrderBy ('Postgres pgKind)
annAggOrderBy =
  case AnnotatedAggregateOrderBy ('Postgres pgKind)
annAggOrderBy of
    AnnotatedAggregateOrderBy ('Postgres pgKind)
AAOCount ->
      ( SQLExp -> Maybe ColumnAlias -> Extractor
S.Extractor SQLExp
S.countStar Maybe ColumnAlias
alias,
        [(Text -> FieldName
FieldName Text
"count", CountType ('Postgres pgKind) -> AggregateField ('Postgres pgKind)
forall (b :: BackendType). CountType b -> AggregateField b
AFCount CountType ('Postgres pgKind)
CountType
S.CTStar)]
      )
    AAOOp Text
opText ColumnInfo ('Postgres pgKind)
pgColumnInfo ->
      let pgColumn :: Column ('Postgres pgKind)
pgColumn = ColumnInfo ('Postgres pgKind) -> Column ('Postgres pgKind)
forall (b :: BackendType). ColumnInfo b -> Column b
ciColumn ColumnInfo ('Postgres pgKind)
pgColumnInfo
          pgType :: ColumnType ('Postgres pgKind)
pgType = ColumnInfo ('Postgres pgKind) -> ColumnType ('Postgres pgKind)
forall (b :: BackendType). ColumnInfo b -> ColumnType b
ciType ColumnInfo ('Postgres pgKind)
pgColumnInfo
       in ( SQLExp -> Maybe ColumnAlias -> Extractor
S.Extractor (Text -> [SQLExp] -> Maybe OrderByExp -> SQLExp
S.SEFnApp Text
opText [Identifier -> SQLExp
S.SEIdentifier (Identifier -> SQLExp) -> Identifier -> SQLExp
forall a b. (a -> b) -> a -> b
$ PGCol -> Identifier
forall a. IsIdentifier a => a -> Identifier
toIdentifier Column ('Postgres pgKind)
PGCol
pgColumn] Maybe OrderByExp
forall a. Maybe a
Nothing) Maybe ColumnAlias
alias,
            [ ( Text -> FieldName
FieldName Text
opText,
                AggregateOp ('Postgres pgKind) -> AggregateField ('Postgres pgKind)
forall (b :: BackendType). AggregateOp b -> AggregateField b
AFOp (AggregateOp ('Postgres pgKind)
 -> AggregateField ('Postgres pgKind))
-> AggregateOp ('Postgres pgKind)
-> AggregateField ('Postgres pgKind)
forall a b. (a -> b) -> a -> b
$
                  Text
-> ColumnFields ('Postgres pgKind)
-> AggregateOp ('Postgres pgKind)
forall (b :: BackendType). Text -> ColumnFields b -> AggregateOp b
AggregateOp
                    Text
opText
                    [ ( Column ('Postgres pgKind) -> FieldName
forall (b :: BackendType). Backend b => Column b -> FieldName
fromCol @('Postgres pgKind) Column ('Postgres pgKind)
pgColumn,
                        Column ('Postgres pgKind)
-> ColumnType ('Postgres pgKind) -> ColFld ('Postgres pgKind)
forall (b :: BackendType). Column b -> ColumnType b -> ColFld b
CFCol Column ('Postgres pgKind)
pgColumn ColumnType ('Postgres pgKind)
pgType
                      )
                    ]
              )
            ]
          )
  where
    alias :: Maybe ColumnAlias
alias = ColumnAlias -> Maybe ColumnAlias
forall a. a -> Maybe a
Just (ColumnAlias -> Maybe ColumnAlias)
-> ColumnAlias -> Maybe ColumnAlias
forall a b. (a -> b) -> a -> b
$ AnnotatedAggregateOrderBy ('Postgres pgKind) -> ColumnAlias
forall (pgKind :: PostgresKind).
AnnotatedAggregateOrderBy ('Postgres pgKind) -> ColumnAlias
mkAggregateOrderByAlias AnnotatedAggregateOrderBy ('Postgres pgKind)
annAggOrderBy

withJsonAggExtr ::
  PermissionLimitSubQuery -> Maybe S.OrderByExp -> S.ColumnAlias -> S.SQLExp
withJsonAggExtr :: PermissionLimitSubQuery
-> Maybe OrderByExp -> ColumnAlias -> SQLExp
withJsonAggExtr PermissionLimitSubQuery
permLimitSubQuery Maybe OrderByExp
ordBy ColumnAlias
alias =
  -- if select has aggregations then use subquery to apply permission limit
  case PermissionLimitSubQuery
permLimitSubQuery of
    PLSQRequired Int
permLimit -> Int -> SQLExp
withPermLimit Int
permLimit
    PermissionLimitSubQuery
PLSQNotRequired -> SQLExp
simpleJsonAgg
  where
    simpleJsonAgg :: SQLExp
simpleJsonAgg = SQLExp -> Maybe OrderByExp -> SQLExp
mkSimpleJsonAgg SQLExp
rowIdenExp Maybe OrderByExp
ordBy
    rowIdenExp :: SQLExp
rowIdenExp = Identifier -> SQLExp
S.SEIdentifier (Identifier -> SQLExp) -> Identifier -> SQLExp
forall a b. (a -> b) -> a -> b
$ ColumnAlias -> Identifier
forall a. IsIdentifier a => a -> Identifier
toIdentifier ColumnAlias
alias
    subSelAls :: Identifier
subSelAls = Text -> Identifier
Identifier Text
"sub_query"
    unnestTable :: Identifier
unnestTable = Text -> Identifier
Identifier Text
"unnest_table"

    mkSimpleJsonAgg :: SQLExp -> Maybe OrderByExp -> SQLExp
mkSimpleJsonAgg SQLExp
rowExp Maybe OrderByExp
ob =
      let jsonAggExp :: SQLExp
jsonAggExp = Text -> [SQLExp] -> Maybe OrderByExp -> SQLExp
S.SEFnApp Text
"json_agg" [SQLExp
rowExp] Maybe OrderByExp
ob
       in Text -> [SQLExp] -> Maybe OrderByExp -> SQLExp
S.SEFnApp Text
"coalesce" [SQLExp
jsonAggExp, Text -> SQLExp
S.SELit Text
"[]"] Maybe OrderByExp
forall a. Maybe a
Nothing

    withPermLimit :: Int -> SQLExp
withPermLimit Int
limit =
      let subSelect :: Select
subSelect = Int -> Select
mkSubSelect Int
limit
          rowIdentifier :: SQLExp
rowIdentifier = Identifier -> ColumnAlias -> SQLExp
forall a b. (IsIdentifier a, IsIdentifier b) => a -> b -> SQLExp
S.mkQIdenExp Identifier
subSelAls ColumnAlias
alias
          extr :: Extractor
extr = SQLExp -> Maybe ColumnAlias -> Extractor
S.Extractor (SQLExp -> Maybe OrderByExp -> SQLExp
mkSimpleJsonAgg SQLExp
rowIdentifier Maybe OrderByExp
newOrderBy) Maybe ColumnAlias
forall a. Maybe a
Nothing
          fromExp :: FromExp
fromExp =
            [FromItem] -> FromExp
S.FromExp ([FromItem] -> FromExp) -> [FromItem] -> FromExp
forall a b. (a -> b) -> a -> b
$
              FromItem -> [FromItem]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (FromItem -> [FromItem]) -> FromItem -> [FromItem]
forall a b. (a -> b) -> a -> b
$
                Select -> TableAlias -> FromItem
S.mkSelFromItem Select
subSelect (TableAlias -> FromItem) -> TableAlias -> FromItem
forall a b. (a -> b) -> a -> b
$ Identifier -> TableAlias
forall a. IsIdentifier a => a -> TableAlias
S.toTableAlias Identifier
subSelAls
       in Select -> SQLExp
S.SESelect (Select -> SQLExp) -> Select -> SQLExp
forall a b. (a -> b) -> a -> b
$
            Select
S.mkSelect
              { selExtr :: [Extractor]
S.selExtr = Extractor -> [Extractor]
forall (f :: * -> *) a. Applicative f => a -> f a
pure Extractor
extr,
                selFrom :: Maybe FromExp
S.selFrom = FromExp -> Maybe FromExp
forall a. a -> Maybe a
Just FromExp
fromExp
              }

    mkSubSelect :: Int -> Select
mkSubSelect Int
limit =
      let jsonRowExtr :: Extractor
jsonRowExtr =
            (SQLExp -> Maybe ColumnAlias -> Extractor)
-> Maybe ColumnAlias -> SQLExp -> Extractor
forall a b c. (a -> b -> c) -> b -> a -> c
flip SQLExp -> Maybe ColumnAlias -> Extractor
S.Extractor (ColumnAlias -> Maybe ColumnAlias
forall a. a -> Maybe a
Just ColumnAlias
alias) (SQLExp -> Extractor) -> SQLExp -> Extractor
forall a b. (a -> b) -> a -> b
$
              Identifier -> ColumnAlias -> SQLExp
forall a b. (IsIdentifier a, IsIdentifier b) => a -> b -> SQLExp
S.mkQIdenExp Identifier
unnestTable ColumnAlias
alias
          obExtrs :: [Extractor]
obExtrs = ((Identifier -> Extractor) -> [Identifier] -> [Extractor])
-> [Identifier] -> (Identifier -> Extractor) -> [Extractor]
forall a b c. (a -> b -> c) -> b -> a -> c
flip (Identifier -> Extractor) -> [Identifier] -> [Extractor]
forall a b. (a -> b) -> [a] -> [b]
map [Identifier]
newOBAliases ((Identifier -> Extractor) -> [Extractor])
-> (Identifier -> Extractor) -> [Extractor]
forall a b. (a -> b) -> a -> b
$ \Identifier
a ->
            SQLExp -> Maybe ColumnAlias -> Extractor
S.Extractor (Identifier -> Identifier -> SQLExp
forall a b. (IsIdentifier a, IsIdentifier b) => a -> b -> SQLExp
S.mkQIdenExp Identifier
unnestTable Identifier
a) (Maybe ColumnAlias -> Extractor) -> Maybe ColumnAlias -> Extractor
forall a b. (a -> b) -> a -> b
$ ColumnAlias -> Maybe ColumnAlias
forall a. a -> Maybe a
Just (ColumnAlias -> Maybe ColumnAlias)
-> ColumnAlias -> Maybe ColumnAlias
forall a b. (a -> b) -> a -> b
$ Identifier -> ColumnAlias
forall a. IsIdentifier a => a -> ColumnAlias
S.toColumnAlias Identifier
a
       in Select
S.mkSelect
            { selExtr :: [Extractor]
S.selExtr = Extractor
jsonRowExtr Extractor -> [Extractor] -> [Extractor]
forall a. a -> [a] -> [a]
: [Extractor]
obExtrs,
              selFrom :: Maybe FromExp
S.selFrom = FromExp -> Maybe FromExp
forall a. a -> Maybe a
Just (FromExp -> Maybe FromExp) -> FromExp -> Maybe FromExp
forall a b. (a -> b) -> a -> b
$ [FromItem] -> FromExp
S.FromExp ([FromItem] -> FromExp) -> [FromItem] -> FromExp
forall a b. (a -> b) -> a -> b
$ FromItem -> [FromItem]
forall (f :: * -> *) a. Applicative f => a -> f a
pure FromItem
unnestFromItem,
              selLimit :: Maybe LimitExp
S.selLimit = LimitExp -> Maybe LimitExp
forall a. a -> Maybe a
Just (LimitExp -> Maybe LimitExp) -> LimitExp -> Maybe LimitExp
forall a b. (a -> b) -> a -> b
$ SQLExp -> LimitExp
S.LimitExp (SQLExp -> LimitExp) -> SQLExp -> LimitExp
forall a b. (a -> b) -> a -> b
$ Int -> SQLExp
S.intToSQLExp Int
limit,
              selOrderBy :: Maybe OrderByExp
S.selOrderBy = Maybe OrderByExp
newOrderBy
            }

    unnestFromItem :: FromItem
unnestFromItem =
      let arrayAggItems :: [SQLExp]
arrayAggItems = ((SQLExp -> SQLExp) -> [SQLExp] -> [SQLExp])
-> [SQLExp] -> (SQLExp -> SQLExp) -> [SQLExp]
forall a b c. (a -> b -> c) -> b -> a -> c
flip (SQLExp -> SQLExp) -> [SQLExp] -> [SQLExp]
forall a b. (a -> b) -> [a] -> [b]
map (SQLExp
rowIdenExp SQLExp -> [SQLExp] -> [SQLExp]
forall a. a -> [a] -> [a]
: [SQLExp]
obCols) ((SQLExp -> SQLExp) -> [SQLExp]) -> (SQLExp -> SQLExp) -> [SQLExp]
forall a b. (a -> b) -> a -> b
$
            \SQLExp
s -> Text -> [SQLExp] -> Maybe OrderByExp -> SQLExp
S.SEFnApp Text
"array_agg" [SQLExp
s] Maybe OrderByExp
forall a. Maybe a
Nothing
       in [SQLExp] -> TableAlias -> [ColumnAlias] -> FromItem
S.FIUnnest [SQLExp]
arrayAggItems (Identifier -> TableAlias
forall a. IsIdentifier a => a -> TableAlias
S.toTableAlias Identifier
unnestTable) ([ColumnAlias] -> FromItem) -> [ColumnAlias] -> FromItem
forall a b. (a -> b) -> a -> b
$
            ColumnAlias
alias ColumnAlias -> [ColumnAlias] -> [ColumnAlias]
forall a. a -> [a] -> [a]
: (Identifier -> ColumnAlias) -> [Identifier] -> [ColumnAlias]
forall a b. (a -> b) -> [a] -> [b]
map Identifier -> ColumnAlias
forall a. IsIdentifier a => a -> ColumnAlias
S.toColumnAlias [Identifier]
newOBAliases

    newOrderBy :: Maybe OrderByExp
newOrderBy = NonEmpty OrderByItem -> OrderByExp
S.OrderByExp (NonEmpty OrderByItem -> OrderByExp)
-> Maybe (NonEmpty OrderByItem) -> Maybe OrderByExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [OrderByItem] -> Maybe (NonEmpty OrderByItem)
forall a. [a] -> Maybe (NonEmpty a)
NE.nonEmpty [OrderByItem]
newOBItems

    ([OrderByItem]
newOBItems, [SQLExp]
obCols, [Identifier]
newOBAliases) = ([OrderByItem], [SQLExp], [Identifier])
-> (OrderByExp -> ([OrderByItem], [SQLExp], [Identifier]))
-> Maybe OrderByExp
-> ([OrderByItem], [SQLExp], [Identifier])
forall b a. b -> (a -> b) -> Maybe a -> b
maybe ([], [], []) OrderByExp -> ([OrderByItem], [SQLExp], [Identifier])
transformOrderBy Maybe OrderByExp
ordBy
    transformOrderBy :: OrderByExp -> ([OrderByItem], [SQLExp], [Identifier])
transformOrderBy (S.OrderByExp NonEmpty OrderByItem
l) = [(OrderByItem, SQLExp, Identifier)]
-> ([OrderByItem], [SQLExp], [Identifier])
forall a b c. [(a, b, c)] -> ([a], [b], [c])
unzip3 ([(OrderByItem, SQLExp, Identifier)]
 -> ([OrderByItem], [SQLExp], [Identifier]))
-> [(OrderByItem, SQLExp, Identifier)]
-> ([OrderByItem], [SQLExp], [Identifier])
forall a b. (a -> b) -> a -> b
$
      (((OrderByItem, Int) -> (OrderByItem, SQLExp, Identifier))
 -> [(OrderByItem, Int)] -> [(OrderByItem, SQLExp, Identifier)])
-> [(OrderByItem, Int)]
-> ((OrderByItem, Int) -> (OrderByItem, SQLExp, Identifier))
-> [(OrderByItem, SQLExp, Identifier)]
forall a b c. (a -> b -> c) -> b -> a -> c
flip ((OrderByItem, Int) -> (OrderByItem, SQLExp, Identifier))
-> [(OrderByItem, Int)] -> [(OrderByItem, SQLExp, Identifier)]
forall a b. (a -> b) -> [a] -> [b]
map ([OrderByItem] -> [Int] -> [(OrderByItem, Int)]
forall a b. [a] -> [b] -> [(a, b)]
zip (NonEmpty OrderByItem -> [OrderByItem]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList NonEmpty OrderByItem
l) [Int
1 ..]) (((OrderByItem, Int) -> (OrderByItem, SQLExp, Identifier))
 -> [(OrderByItem, SQLExp, Identifier)])
-> ((OrderByItem, Int) -> (OrderByItem, SQLExp, Identifier))
-> [(OrderByItem, SQLExp, Identifier)]
forall a b. (a -> b) -> a -> b
$ \(OrderByItem
obItem, Int
i :: Int) ->
        let iden :: Identifier
iden = Text -> Identifier
Identifier (Text -> Identifier) -> Text -> Identifier
forall a b. (a -> b) -> a -> b
$ Text
"ob_col_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall a. Show a => a -> Text
tshow Int
i
         in ( OrderByItem
obItem {oExpression :: SQLExp
S.oExpression = Identifier -> SQLExp
S.SEIdentifier Identifier
iden},
              OrderByItem -> SQLExp
S.oExpression OrderByItem
obItem,
              Identifier
iden
            )

asSingleRowExtr :: S.ColumnAlias -> S.SQLExp
asSingleRowExtr :: ColumnAlias -> SQLExp
asSingleRowExtr ColumnAlias
col =
  Text -> [SQLExp] -> Maybe OrderByExp -> SQLExp
S.SEFnApp Text
"coalesce" [SQLExp
jsonAgg, Text -> SQLExp
S.SELit Text
"null"] Maybe OrderByExp
forall a. Maybe a
Nothing
  where
    jsonAgg :: SQLExp
jsonAgg =
      SQLOp -> [SQLExp] -> SQLExp
S.SEOpApp
        (Text -> SQLOp
S.SQLOp Text
"->")
        [ Text -> [SQLExp] -> Maybe OrderByExp -> SQLExp
S.SEFnApp Text
"json_agg" [Identifier -> SQLExp
S.SEIdentifier (Identifier -> SQLExp) -> Identifier -> SQLExp
forall a b. (a -> b) -> a -> b
$ ColumnAlias -> Identifier
forall a. IsIdentifier a => a -> Identifier
toIdentifier ColumnAlias
col] Maybe OrderByExp
forall a. Maybe a
Nothing,
          Text -> SQLExp
S.SEUnsafe Text
"0"
        ]

asJsonAggExtr ::
  JsonAggSelect -> S.ColumnAlias -> PermissionLimitSubQuery -> Maybe S.OrderByExp -> S.Extractor
asJsonAggExtr :: JsonAggSelect
-> ColumnAlias
-> PermissionLimitSubQuery
-> Maybe OrderByExp
-> Extractor
asJsonAggExtr JsonAggSelect
jsonAggSelect ColumnAlias
als PermissionLimitSubQuery
permLimitSubQuery Maybe OrderByExp
ordByExpM =
  (SQLExp -> Maybe ColumnAlias -> Extractor)
-> Maybe ColumnAlias -> SQLExp -> Extractor
forall a b c. (a -> b -> c) -> b -> a -> c
flip SQLExp -> Maybe ColumnAlias -> Extractor
S.Extractor (ColumnAlias -> Maybe ColumnAlias
forall a. a -> Maybe a
Just ColumnAlias
als) (SQLExp -> Extractor) -> SQLExp -> Extractor
forall a b. (a -> b) -> a -> b
$ case JsonAggSelect
jsonAggSelect of
    JsonAggSelect
JASMultipleRows -> PermissionLimitSubQuery
-> Maybe OrderByExp -> ColumnAlias -> SQLExp
withJsonAggExtr PermissionLimitSubQuery
permLimitSubQuery Maybe OrderByExp
ordByExpM ColumnAlias
als
    JsonAggSelect
JASSingleObject -> ColumnAlias -> SQLExp
asSingleRowExtr ColumnAlias
als