-- | This module contains worker definitions pertaining to generating 'Select'
-- AST nodes.
--
-- NOTE: 'generateSQLSelect' is mutually recursive with
-- 'connectionToSelectWith', thus these two cohabitate in this module.
module Hasura.Backends.Postgres.Translate.Select.Internal.GenerateSelect
  ( generateSQLSelect,
    generateSQLSelectFromArrayNode,
    connectionToSelectWith,
  )
where

import Data.HashMap.Strict qualified as HM
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 (mkBaseTableAlias)
import Hasura.Backends.Postgres.Translate.Select.Internal.Helpers
  ( cursorIdentifier,
    cursorsSelectAliasIdentifier,
    endCursorIdentifier,
    hasNextPageIdentifier,
    hasPreviousPageIdentifier,
    mkFirstElementExp,
    mkLastElementExp,
    pageInfoSelectAliasIdentifier,
    startCursorIdentifier,
  )
import Hasura.Backends.Postgres.Translate.Types
import Hasura.Prelude
import Hasura.RQL.IR.Select (ConnectionSlice (SliceFirst, SliceLast))

generateSQLSelect ::
  -- | Pre join condition
  S.BoolExp ->
  SelectSource ->
  SelectNode ->
  S.Select
generateSQLSelect :: BoolExp -> SelectSource -> SelectNode -> Select
generateSQLSelect BoolExp
joinCondition SelectSource
selectSource SelectNode
selectNode =
  Select
S.mkSelect
    { selExtr :: [Extractor]
S.selExtr = [SQLExp -> Maybe ColumnAlias -> Extractor
S.Extractor SQLExp
e (Maybe ColumnAlias -> Extractor) -> Maybe ColumnAlias -> Extractor
forall a b. (a -> b) -> a -> b
$ ColumnAlias -> Maybe ColumnAlias
forall a. a -> Maybe a
Just ColumnAlias
a | (ColumnAlias
a, SQLExp
e) <- HashMap ColumnAlias SQLExp -> [(ColumnAlias, SQLExp)]
forall k v. HashMap k v -> [(k, v)]
HM.toList HashMap ColumnAlias SQLExp
extractors],
      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
joinedFrom],
      selOrderBy :: Maybe OrderByExp
S.selOrderBy = Maybe OrderByExp
nodeOrderBy,
      selLimit :: Maybe LimitExp
S.selLimit = SQLExp -> LimitExp
S.LimitExp (SQLExp -> LimitExp) -> (Int -> SQLExp) -> Int -> LimitExp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> SQLExp
S.intToSQLExp (Int -> LimitExp) -> Maybe Int -> Maybe LimitExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SelectSlicing -> Maybe Int
_ssLimit SelectSlicing
nodeSlicing,
      selOffset :: Maybe OffsetExp
S.selOffset = SQLExp -> OffsetExp
S.OffsetExp (SQLExp -> OffsetExp) -> (Int64 -> SQLExp) -> Int64 -> OffsetExp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> SQLExp
S.int64ToSQLExp (Int64 -> OffsetExp) -> Maybe Int64 -> Maybe OffsetExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SelectSlicing -> Maybe Int64
_ssOffset SelectSlicing
nodeSlicing,
      selDistinct :: Maybe DistinctExpr
S.selDistinct = Maybe DistinctExpr
nodeDistinctOn
    }
  where
    SelectSource Identifier
sourcePrefix FromItem
fromItem BoolExp
whereExp SortingAndSlicing
sortAndSlice = SelectSource
selectSource
    SelectNode HashMap ColumnAlias SQLExp
extractors JoinTree
joinTree = SelectNode
selectNode
    JoinTree HashMap ObjectRelationSource SelectNode
objectRelations HashMap ArrayRelationSource MultiRowSelectNode
arrayRelations HashMap ArrayConnectionSource MultiRowSelectNode
arrayConnections HashMap ComputedFieldTableSetSource MultiRowSelectNode
computedFields = JoinTree
joinTree
    ApplySortingAndSlicing
      (Maybe OrderByExp
baseOrderBy, SelectSlicing
baseSlicing, Maybe DistinctExpr
baseDistinctOn)
      (Maybe OrderByExp
nodeOrderBy, SelectSlicing
nodeSlicing, Maybe DistinctExpr
nodeDistinctOn) = SortingAndSlicing -> ApplySortingAndSlicing
applySortingAndSlicing SortingAndSlicing
sortAndSlice

    -- this is the table which is aliased as "sourcePrefix.base"
    baseSelect :: Select
baseSelect =
      Select
S.mkSelect
        { selExtr :: [Extractor]
S.selExtr = [SQLExp -> Maybe ColumnAlias -> Extractor
S.Extractor (Maybe Qual -> SQLExp
S.SEStar Maybe Qual
forall a. Maybe a
Nothing) Maybe ColumnAlias
forall a. Maybe a
Nothing],
          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
fromItem],
          selWhere :: Maybe WhereFrag
S.selWhere = WhereFrag -> Maybe WhereFrag
forall a. a -> Maybe a
Just (WhereFrag -> Maybe WhereFrag) -> WhereFrag -> Maybe WhereFrag
forall a b. (a -> b) -> a -> b
$ BoolExp -> BoolExp -> WhereFrag
injectJoinCond BoolExp
joinCondition BoolExp
whereExp,
          selOrderBy :: Maybe OrderByExp
S.selOrderBy = Maybe OrderByExp
baseOrderBy,
          selLimit :: Maybe LimitExp
S.selLimit = SQLExp -> LimitExp
S.LimitExp (SQLExp -> LimitExp) -> (Int -> SQLExp) -> Int -> LimitExp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> SQLExp
S.intToSQLExp (Int -> LimitExp) -> Maybe Int -> Maybe LimitExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SelectSlicing -> Maybe Int
_ssLimit SelectSlicing
baseSlicing,
          selOffset :: Maybe OffsetExp
S.selOffset = SQLExp -> OffsetExp
S.OffsetExp (SQLExp -> OffsetExp) -> (Int64 -> SQLExp) -> Int64 -> OffsetExp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> SQLExp
S.int64ToSQLExp (Int64 -> OffsetExp) -> Maybe Int64 -> Maybe OffsetExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SelectSlicing -> Maybe Int64
_ssOffset SelectSlicing
baseSlicing,
          selDistinct :: Maybe DistinctExpr
S.selDistinct = Maybe DistinctExpr
baseDistinctOn
        }
    baseSelectAlias :: TableAlias
baseSelectAlias = Identifier -> TableAlias
mkBaseTableAlias Identifier
sourcePrefix
    baseFromItem :: FromItem
baseFromItem = Select -> TableAlias -> FromItem
S.mkSelFromItem Select
baseSelect TableAlias
baseSelectAlias

    injectJoinCond ::
      S.BoolExp -> -- Join condition
      S.BoolExp -> -- Where condition
      S.WhereFrag -- New where frag
    injectJoinCond :: BoolExp -> BoolExp -> WhereFrag
injectJoinCond BoolExp
joinCond BoolExp
whereCond =
      BoolExp -> WhereFrag
S.WhereFrag (BoolExp -> WhereFrag) -> BoolExp -> WhereFrag
forall a b. (a -> b) -> a -> b
$ BoolExp -> BoolExp
S.simplifyBoolExp (BoolExp -> BoolExp) -> BoolExp -> BoolExp
forall a b. (a -> b) -> a -> b
$ BinOp -> BoolExp -> BoolExp -> BoolExp
S.BEBin BinOp
S.AndOp BoolExp
joinCond BoolExp
whereCond

    -- function to create a joined from item from two from items
    leftOuterJoin :: FromItem -> FromItem -> FromItem
leftOuterJoin FromItem
current FromItem
new =
      JoinExpr -> FromItem
S.FIJoin (JoinExpr -> FromItem) -> JoinExpr -> FromItem
forall a b. (a -> b) -> a -> b
$
        FromItem -> JoinType -> FromItem -> JoinCond -> JoinExpr
S.JoinExpr FromItem
current JoinType
S.LeftOuter FromItem
new (JoinCond -> JoinExpr) -> JoinCond -> JoinExpr
forall a b. (a -> b) -> a -> b
$
          BoolExp -> JoinCond
S.JoinOn (BoolExp -> JoinCond) -> BoolExp -> JoinCond
forall a b. (a -> b) -> a -> b
$ Bool -> BoolExp
S.BELit Bool
True

    -- this is the from eexp for the final select
    joinedFrom :: S.FromItem
    joinedFrom :: FromItem
joinedFrom =
      (FromItem -> FromItem -> FromItem)
-> FromItem -> [FromItem] -> FromItem
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' FromItem -> FromItem -> FromItem
leftOuterJoin FromItem
baseFromItem ([FromItem] -> FromItem) -> [FromItem] -> FromItem
forall a b. (a -> b) -> a -> b
$
        ((ObjectRelationSource, SelectNode) -> FromItem)
-> [(ObjectRelationSource, SelectNode)] -> [FromItem]
forall a b. (a -> b) -> [a] -> [b]
map (ObjectRelationSource, SelectNode) -> FromItem
objectRelationToFromItem (HashMap ObjectRelationSource SelectNode
-> [(ObjectRelationSource, SelectNode)]
forall k v. HashMap k v -> [(k, v)]
HM.toList HashMap ObjectRelationSource SelectNode
objectRelations)
          [FromItem] -> [FromItem] -> [FromItem]
forall a. Semigroup a => a -> a -> a
<> ((ArrayRelationSource, MultiRowSelectNode) -> FromItem)
-> [(ArrayRelationSource, MultiRowSelectNode)] -> [FromItem]
forall a b. (a -> b) -> [a] -> [b]
map (ArrayRelationSource, MultiRowSelectNode) -> FromItem
arrayRelationToFromItem (HashMap ArrayRelationSource MultiRowSelectNode
-> [(ArrayRelationSource, MultiRowSelectNode)]
forall k v. HashMap k v -> [(k, v)]
HM.toList HashMap ArrayRelationSource MultiRowSelectNode
arrayRelations)
          [FromItem] -> [FromItem] -> [FromItem]
forall a. Semigroup a => a -> a -> a
<> ((ArrayConnectionSource, MultiRowSelectNode) -> FromItem)
-> [(ArrayConnectionSource, MultiRowSelectNode)] -> [FromItem]
forall a b. (a -> b) -> [a] -> [b]
map (ArrayConnectionSource, MultiRowSelectNode) -> FromItem
arrayConnectionToFromItem (HashMap ArrayConnectionSource MultiRowSelectNode
-> [(ArrayConnectionSource, MultiRowSelectNode)]
forall k v. HashMap k v -> [(k, v)]
HM.toList HashMap ArrayConnectionSource MultiRowSelectNode
arrayConnections)
          [FromItem] -> [FromItem] -> [FromItem]
forall a. Semigroup a => a -> a -> a
<> ((ComputedFieldTableSetSource, MultiRowSelectNode) -> FromItem)
-> [(ComputedFieldTableSetSource, MultiRowSelectNode)]
-> [FromItem]
forall a b. (a -> b) -> [a] -> [b]
map (ComputedFieldTableSetSource, MultiRowSelectNode) -> FromItem
computedFieldToFromItem (HashMap ComputedFieldTableSetSource MultiRowSelectNode
-> [(ComputedFieldTableSetSource, MultiRowSelectNode)]
forall k v. HashMap k v -> [(k, v)]
HM.toList HashMap ComputedFieldTableSetSource MultiRowSelectNode
computedFields)

    objectRelationToFromItem ::
      (ObjectRelationSource, SelectNode) -> S.FromItem
    objectRelationToFromItem :: (ObjectRelationSource, SelectNode) -> FromItem
objectRelationToFromItem (ObjectRelationSource
objectRelationSource, SelectNode
node) =
      let ObjectRelationSource RelName
_ HashMap PGCol PGCol
colMapping ObjectSelectSource
objectSelectSource = ObjectRelationSource
objectRelationSource
          alias :: TableAlias
alias = Identifier -> TableAlias
forall a. IsIdentifier a => a -> TableAlias
S.toTableAlias (Identifier -> TableAlias) -> Identifier -> TableAlias
forall a b. (a -> b) -> a -> b
$ ObjectSelectSource -> Identifier
_ossPrefix ObjectSelectSource
objectSelectSource
          source :: SelectSource
source = ObjectSelectSource -> SelectSource
objectSelectSourceToSelectSource ObjectSelectSource
objectSelectSource
          select :: Select
select = BoolExp -> SelectSource -> SelectNode -> Select
generateSQLSelect (TableAlias -> HashMap PGCol PGCol -> BoolExp
mkJoinCond TableAlias
baseSelectAlias HashMap PGCol PGCol
colMapping) SelectSource
source SelectNode
node
       in Select -> TableAlias -> FromItem
S.mkLateralFromItem Select
select TableAlias
alias

    arrayRelationToFromItem ::
      (ArrayRelationSource, MultiRowSelectNode) -> S.FromItem
    arrayRelationToFromItem :: (ArrayRelationSource, MultiRowSelectNode) -> FromItem
arrayRelationToFromItem (ArrayRelationSource
arrayRelationSource, MultiRowSelectNode
arraySelectNode) =
      let ArrayRelationSource TableAlias
_ HashMap PGCol PGCol
colMapping SelectSource
source = ArrayRelationSource
arrayRelationSource
          alias :: TableAlias
alias = Identifier -> TableAlias
forall a. IsIdentifier a => a -> TableAlias
S.toTableAlias (Identifier -> TableAlias) -> Identifier -> TableAlias
forall a b. (a -> b) -> a -> b
$ SelectSource -> Identifier
_ssPrefix SelectSource
source
          select :: Select
select =
            SelectSource -> MultiRowSelectNode -> BoolExp -> Select
generateSQLSelectFromArrayNode SelectSource
source MultiRowSelectNode
arraySelectNode (BoolExp -> Select) -> BoolExp -> Select
forall a b. (a -> b) -> a -> b
$
              TableAlias -> HashMap PGCol PGCol -> BoolExp
mkJoinCond TableAlias
baseSelectAlias HashMap PGCol PGCol
colMapping
       in Select -> TableAlias -> FromItem
S.mkLateralFromItem Select
select TableAlias
alias

    arrayConnectionToFromItem ::
      (ArrayConnectionSource, MultiRowSelectNode) -> S.FromItem
    arrayConnectionToFromItem :: (ArrayConnectionSource, MultiRowSelectNode) -> FromItem
arrayConnectionToFromItem (ArrayConnectionSource
arrayConnectionSource, MultiRowSelectNode
arraySelectNode) =
      let selectWith :: SelectWithG Select
selectWith = TableAlias
-> ArrayConnectionSource
-> MultiRowSelectNode
-> SelectWithG Select
connectionToSelectWith TableAlias
baseSelectAlias ArrayConnectionSource
arrayConnectionSource MultiRowSelectNode
arraySelectNode
          alias :: TableAlias
alias = Identifier -> TableAlias
forall a. IsIdentifier a => a -> TableAlias
S.toTableAlias (Identifier -> TableAlias) -> Identifier -> TableAlias
forall a b. (a -> b) -> a -> b
$ SelectSource -> Identifier
_ssPrefix (SelectSource -> Identifier) -> SelectSource -> Identifier
forall a b. (a -> b) -> a -> b
$ ArrayConnectionSource -> SelectSource
_acsSource ArrayConnectionSource
arrayConnectionSource
       in Lateral -> SelectWithG Select -> TableAlias -> FromItem
S.FISelectWith (Bool -> Lateral
S.Lateral Bool
True) SelectWithG Select
selectWith TableAlias
alias

    computedFieldToFromItem ::
      (ComputedFieldTableSetSource, MultiRowSelectNode) -> S.FromItem
    computedFieldToFromItem :: (ComputedFieldTableSetSource, MultiRowSelectNode) -> FromItem
computedFieldToFromItem (ComputedFieldTableSetSource
computedFieldTableSource, MultiRowSelectNode
node) =
      let ComputedFieldTableSetSource FieldName
_ SelectSource
source = ComputedFieldTableSetSource
computedFieldTableSource
          internalSelect :: Select
internalSelect = BoolExp -> SelectSource -> SelectNode -> Select
generateSQLSelect (Bool -> BoolExp
S.BELit Bool
True) SelectSource
source (SelectNode -> Select) -> SelectNode -> Select
forall a b. (a -> b) -> a -> b
$ MultiRowSelectNode -> SelectNode
_mrsnSelectNode MultiRowSelectNode
node
          alias :: TableAlias
alias = Identifier -> TableAlias
forall a. IsIdentifier a => a -> TableAlias
S.toTableAlias (Identifier -> TableAlias) -> Identifier -> TableAlias
forall a b. (a -> b) -> a -> b
$ SelectSource -> Identifier
_ssPrefix SelectSource
source
          select :: Select
select =
            Select
S.mkSelect
              { selExtr :: [Extractor]
S.selExtr = MultiRowSelectNode -> [Extractor]
_mrsnTopExtractors MultiRowSelectNode
node,
                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 [Select -> TableAlias -> FromItem
S.mkSelFromItem Select
internalSelect TableAlias
alias]
              }
       in Select -> TableAlias -> FromItem
S.mkLateralFromItem Select
select TableAlias
alias

generateSQLSelectFromArrayNode ::
  SelectSource ->
  MultiRowSelectNode ->
  S.BoolExp ->
  S.Select
generateSQLSelectFromArrayNode :: SelectSource -> MultiRowSelectNode -> BoolExp -> Select
generateSQLSelectFromArrayNode SelectSource
selectSource MultiRowSelectNode
arraySelectNode BoolExp
joinCondition =
  Select
S.mkSelect
    { selExtr :: [Extractor]
S.selExtr = [Extractor]
topExtractors,
      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
selectFrom]
    }
  where
    MultiRowSelectNode [Extractor]
topExtractors SelectNode
selectNode = MultiRowSelectNode
arraySelectNode
    selectFrom :: FromItem
selectFrom =
      Select -> TableAlias -> FromItem
S.mkSelFromItem
        (BoolExp -> SelectSource -> SelectNode -> Select
generateSQLSelect BoolExp
joinCondition SelectSource
selectSource SelectNode
selectNode)
        (TableAlias -> FromItem) -> TableAlias -> FromItem
forall a b. (a -> b) -> a -> b
$ Identifier -> TableAlias
forall a. IsIdentifier a => a -> TableAlias
S.toTableAlias (Identifier -> TableAlias) -> Identifier -> TableAlias
forall a b. (a -> b) -> a -> b
$ SelectSource -> Identifier
_ssPrefix SelectSource
selectSource

mkJoinCond :: S.TableAlias -> HashMap PGCol PGCol -> S.BoolExp
mkJoinCond :: TableAlias -> HashMap PGCol PGCol -> BoolExp
mkJoinCond TableAlias
baseTablepfx HashMap PGCol PGCol
colMapn =
  (BoolExp -> BoolExp -> BoolExp) -> BoolExp -> [BoolExp] -> BoolExp
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (BinOp -> BoolExp -> BoolExp -> BoolExp
S.BEBin BinOp
S.AndOp) (Bool -> BoolExp
S.BELit Bool
True) ([BoolExp] -> BoolExp) -> [BoolExp] -> BoolExp
forall a b. (a -> b) -> a -> b
$
    (((PGCol, PGCol) -> BoolExp) -> [(PGCol, PGCol)] -> [BoolExp])
-> [(PGCol, PGCol)] -> ((PGCol, PGCol) -> BoolExp) -> [BoolExp]
forall a b c. (a -> b -> c) -> b -> a -> c
flip ((PGCol, PGCol) -> BoolExp) -> [(PGCol, PGCol)] -> [BoolExp]
forall a b. (a -> b) -> [a] -> [b]
map (HashMap PGCol PGCol -> [(PGCol, PGCol)]
forall k v. HashMap k v -> [(k, v)]
HM.toList HashMap PGCol PGCol
colMapn) (((PGCol, PGCol) -> BoolExp) -> [BoolExp])
-> ((PGCol, PGCol) -> BoolExp) -> [BoolExp]
forall a b. (a -> b) -> a -> b
$ \(PGCol
lCol, PGCol
rCol) ->
      CompareOp -> SQLExp -> SQLExp -> BoolExp
S.BECompare CompareOp
S.SEQ (TableAlias -> PGCol -> SQLExp
forall a b. (IsIdentifier a, IsIdentifier b) => a -> b -> SQLExp
S.mkQIdenExp TableAlias
baseTablepfx PGCol
lCol) (PGCol -> SQLExp
forall a. IsIdentifier a => a -> SQLExp
S.mkSIdenExp PGCol
rCol)

connectionToSelectWith ::
  S.TableAlias ->
  ArrayConnectionSource ->
  MultiRowSelectNode ->
  S.SelectWithG S.Select
connectionToSelectWith :: TableAlias
-> ArrayConnectionSource
-> MultiRowSelectNode
-> SelectWithG Select
connectionToSelectWith TableAlias
baseSelectAlias ArrayConnectionSource
arrayConnectionSource MultiRowSelectNode
arraySelectNode =
  let extractionSelect :: Select
extractionSelect =
        Select
S.mkSelect
          { selExtr :: [Extractor]
S.selExtr = [Extractor]
topExtractors,
            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 [Identifier -> FromItem
S.FIIdentifier Identifier
finalSelectIdentifier]
          }
   in [(TableAlias, Select)] -> Select -> SelectWithG Select
forall statement.
[(TableAlias, statement)] -> Select -> SelectWithG statement
S.SelectWith [(TableAlias, Select)]
fromBaseSelections Select
extractionSelect
  where
    ArrayConnectionSource TableAlias
_ HashMap PGCol PGCol
columnMapping Maybe BoolExp
maybeSplit Maybe ConnectionSlice
maybeSlice SelectSource
selectSource =
      ArrayConnectionSource
arrayConnectionSource
    MultiRowSelectNode [Extractor]
topExtractors SelectNode
selectNode = MultiRowSelectNode
arraySelectNode
    baseSelectIdentifier :: Identifier
baseSelectIdentifier = Text -> Identifier
Identifier Text
"__base_select"
    splitSelectIdentifier :: Identifier
splitSelectIdentifier = Text -> Identifier
Identifier Text
"__split_select"
    sliceSelectIdentifier :: Identifier
sliceSelectIdentifier = Text -> Identifier
Identifier Text
"__slice_select"
    finalSelectIdentifier :: Identifier
finalSelectIdentifier = Text -> Identifier
Identifier Text
"__final_select"

    rowNumberIdentifier :: Identifier
rowNumberIdentifier = Text -> Identifier
Identifier Text
"__row_number"
    rowNumberExp :: SQLExp
rowNumberExp = Text -> SQLExp
S.SEUnsafe Text
"(row_number() over (partition by 1))"
    startRowNumberIdentifier :: Identifier
startRowNumberIdentifier = Text -> Identifier
Identifier Text
"__start_row_number"
    endRowNumberIdentifier :: Identifier
endRowNumberIdentifier = Text -> Identifier
Identifier Text
"__end_row_number"

    startCursorExp :: SQLExp
startCursorExp = SQLExp -> SQLExp
mkFirstElementExp (SQLExp -> SQLExp) -> SQLExp -> SQLExp
forall a b. (a -> b) -> a -> b
$ Identifier -> SQLExp
S.SEIdentifier Identifier
cursorIdentifier
    endCursorExp :: SQLExp
endCursorExp = SQLExp -> SQLExp
mkLastElementExp (SQLExp -> SQLExp) -> SQLExp -> SQLExp
forall a b. (a -> b) -> a -> b
$ Identifier -> SQLExp
S.SEIdentifier Identifier
cursorIdentifier

    startRowNumberExp :: SQLExp
startRowNumberExp = SQLExp -> SQLExp
mkFirstElementExp (SQLExp -> SQLExp) -> SQLExp -> SQLExp
forall a b. (a -> b) -> a -> b
$ Identifier -> SQLExp
S.SEIdentifier Identifier
rowNumberIdentifier
    endRowNumberExp :: SQLExp
endRowNumberExp = SQLExp -> SQLExp
mkLastElementExp (SQLExp -> SQLExp) -> SQLExp -> SQLExp
forall a b. (a -> b) -> a -> b
$ Identifier -> SQLExp
S.SEIdentifier Identifier
rowNumberIdentifier

    fromBaseSelections :: [(TableAlias, Select)]
fromBaseSelections =
      let joinCond :: BoolExp
joinCond = TableAlias -> HashMap PGCol PGCol -> BoolExp
mkJoinCond TableAlias
baseSelectAlias HashMap PGCol PGCol
columnMapping
          baseSelectFrom :: FromItem
baseSelectFrom =
            Select -> TableAlias -> FromItem
S.mkSelFromItem
              (BoolExp -> SelectSource -> SelectNode -> Select
generateSQLSelect BoolExp
joinCond SelectSource
selectSource SelectNode
selectNode)
              (TableAlias -> FromItem) -> TableAlias -> FromItem
forall a b. (a -> b) -> a -> b
$ Identifier -> TableAlias
forall a. IsIdentifier a => a -> TableAlias
S.toTableAlias (Identifier -> TableAlias) -> Identifier -> TableAlias
forall a b. (a -> b) -> a -> b
$ SelectSource -> Identifier
_ssPrefix SelectSource
selectSource
          select :: Select
select =
            Select
S.mkSelect
              { selExtr :: [Extractor]
S.selExtr =
                  [ Extractor
S.selectStar,
                    SQLExp -> Maybe ColumnAlias -> Extractor
S.Extractor SQLExp
rowNumberExp (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
rowNumberIdentifier
                  ],
                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
baseSelectFrom]
              }
       in (Identifier -> TableAlias
forall a. IsIdentifier a => a -> TableAlias
S.toTableAlias Identifier
baseSelectIdentifier, Select
select) (TableAlias, Select)
-> [(TableAlias, Select)] -> [(TableAlias, Select)]
forall a. a -> [a] -> [a]
: [(TableAlias, Select)]
fromSplitSelection

    mkStarSelect :: Identifier -> Select
mkStarSelect Identifier
fromIdentifier =
      Select
S.mkSelect
        { selExtr :: [Extractor]
S.selExtr = [Extractor
S.selectStar],
          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 [Identifier -> FromItem
S.FIIdentifier Identifier
fromIdentifier]
        }

    fromSplitSelection :: [(TableAlias, Select)]
fromSplitSelection = case Maybe BoolExp
maybeSplit of
      Maybe BoolExp
Nothing -> Identifier -> [(TableAlias, Select)]
fromSliceSelection Identifier
baseSelectIdentifier
      Just BoolExp
splitBool ->
        let select :: Select
select =
              (Identifier -> Select
mkStarSelect Identifier
baseSelectIdentifier) {selWhere :: Maybe WhereFrag
S.selWhere = WhereFrag -> Maybe WhereFrag
forall a. a -> Maybe a
Just (WhereFrag -> Maybe WhereFrag) -> WhereFrag -> Maybe WhereFrag
forall a b. (a -> b) -> a -> b
$ BoolExp -> WhereFrag
S.WhereFrag BoolExp
splitBool}
         in (Identifier -> TableAlias
forall a. IsIdentifier a => a -> TableAlias
S.toTableAlias Identifier
splitSelectIdentifier, Select
select) (TableAlias, Select)
-> [(TableAlias, Select)] -> [(TableAlias, Select)]
forall a. a -> [a] -> [a]
: Identifier -> [(TableAlias, Select)]
fromSliceSelection Identifier
splitSelectIdentifier

    fromSliceSelection :: Identifier -> [(TableAlias, Select)]
fromSliceSelection Identifier
prevSelect = case Maybe ConnectionSlice
maybeSlice of
      Maybe ConnectionSlice
Nothing -> Identifier -> [(TableAlias, Select)]
fromFinalSelect Identifier
prevSelect
      Just ConnectionSlice
slice ->
        let select :: Select
select = case ConnectionSlice
slice of
              SliceFirst Int
limit ->
                (Identifier -> Select
mkStarSelect Identifier
prevSelect)
                  { selLimit :: Maybe LimitExp
S.selLimit = (LimitExp -> Maybe LimitExp
forall a. a -> Maybe a
Just (LimitExp -> Maybe LimitExp)
-> (Int -> LimitExp) -> Int -> Maybe LimitExp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SQLExp -> LimitExp
S.LimitExp (SQLExp -> LimitExp) -> (Int -> SQLExp) -> Int -> LimitExp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> SQLExp
S.intToSQLExp) Int
limit
                  }
              SliceLast Int
limit ->
                let mkRowNumberOrderBy :: OrderType -> OrderByExp
mkRowNumberOrderBy OrderType
obType =
                      let orderByItem :: OrderByItem
orderByItem =
                            SQLExp -> Maybe OrderType -> Maybe NullsOrder -> OrderByItem
S.OrderByItem (Identifier -> SQLExp
S.SEIdentifier Identifier
rowNumberIdentifier) (OrderType -> Maybe OrderType
forall a. a -> Maybe a
Just OrderType
obType) Maybe NullsOrder
forall a. Maybe a
Nothing
                       in NonEmpty OrderByItem -> OrderByExp
S.OrderByExp (NonEmpty OrderByItem -> OrderByExp)
-> NonEmpty OrderByItem -> OrderByExp
forall a b. (a -> b) -> a -> b
$ OrderByItem
orderByItem OrderByItem -> [OrderByItem] -> NonEmpty OrderByItem
forall a. a -> [a] -> NonEmpty a
NE.:| []

                    sliceLastSelect :: Select
sliceLastSelect =
                      (Identifier -> Select
mkStarSelect Identifier
prevSelect)
                        { selLimit :: Maybe LimitExp
S.selLimit = (LimitExp -> Maybe LimitExp
forall a. a -> Maybe a
Just (LimitExp -> Maybe LimitExp)
-> (Int -> LimitExp) -> Int -> Maybe LimitExp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SQLExp -> LimitExp
S.LimitExp (SQLExp -> LimitExp) -> (Int -> SQLExp) -> Int -> LimitExp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> SQLExp
S.intToSQLExp) Int
limit,
                          selOrderBy :: Maybe OrderByExp
S.selOrderBy = OrderByExp -> Maybe OrderByExp
forall a. a -> Maybe a
Just (OrderByExp -> Maybe OrderByExp) -> OrderByExp -> Maybe OrderByExp
forall a b. (a -> b) -> a -> b
$ OrderType -> OrderByExp
mkRowNumberOrderBy OrderType
S.OTDesc
                        }
                    sliceLastSelectFrom :: FromItem
sliceLastSelectFrom =
                      Select -> TableAlias -> FromItem
S.mkSelFromItem Select
sliceLastSelect (TableAlias -> FromItem) -> TableAlias -> FromItem
forall a b. (a -> b) -> a -> b
$ Identifier -> TableAlias
forall a. IsIdentifier a => a -> TableAlias
S.toTableAlias Identifier
sliceSelectIdentifier
                 in Select
S.mkSelect
                      { selExtr :: [Extractor]
S.selExtr = [Extractor
S.selectStar],
                        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
sliceLastSelectFrom],
                        selOrderBy :: Maybe OrderByExp
S.selOrderBy = OrderByExp -> Maybe OrderByExp
forall a. a -> Maybe a
Just (OrderByExp -> Maybe OrderByExp) -> OrderByExp -> Maybe OrderByExp
forall a b. (a -> b) -> a -> b
$ OrderType -> OrderByExp
mkRowNumberOrderBy OrderType
S.OTAsc
                      }
         in (Identifier -> TableAlias
forall a. IsIdentifier a => a -> TableAlias
S.toTableAlias Identifier
sliceSelectIdentifier, Select
select) (TableAlias, Select)
-> [(TableAlias, Select)] -> [(TableAlias, Select)]
forall a. a -> [a] -> [a]
: Identifier -> [(TableAlias, Select)]
fromFinalSelect Identifier
sliceSelectIdentifier

    fromFinalSelect :: Identifier -> [(TableAlias, Select)]
fromFinalSelect Identifier
prevSelect =
      let select :: Select
select = Identifier -> Select
mkStarSelect Identifier
prevSelect
       in (Identifier -> TableAlias
forall a. IsIdentifier a => a -> TableAlias
S.toTableAlias Identifier
finalSelectIdentifier, Select
select) (TableAlias, Select)
-> [(TableAlias, Select)] -> [(TableAlias, Select)]
forall a. a -> [a] -> [a]
: [(TableAlias, Select)]
fromCursorSelection

    fromCursorSelection :: [(TableAlias, Select)]
fromCursorSelection =
      let extrs :: [Extractor]
extrs =
            [ SQLExp -> Maybe ColumnAlias -> Extractor
S.Extractor SQLExp
startCursorExp (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
startCursorIdentifier,
              SQLExp -> Maybe ColumnAlias -> Extractor
S.Extractor SQLExp
endCursorExp (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
endCursorIdentifier,
              SQLExp -> Maybe ColumnAlias -> Extractor
S.Extractor SQLExp
startRowNumberExp (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
startRowNumberIdentifier,
              SQLExp -> Maybe ColumnAlias -> Extractor
S.Extractor SQLExp
endRowNumberExp (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
endRowNumberIdentifier
            ]
          select :: Select
select =
            Select
S.mkSelect
              { selExtr :: [Extractor]
S.selExtr = [Extractor]
extrs,
                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 [Identifier -> FromItem
S.FIIdentifier Identifier
finalSelectIdentifier]
              }
       in (Identifier -> TableAlias
forall a. IsIdentifier a => a -> TableAlias
S.toTableAlias Identifier
cursorsSelectAliasIdentifier, Select
select) (TableAlias, Select)
-> [(TableAlias, Select)] -> [(TableAlias, Select)]
forall a. a -> [a] -> [a]
: [(TableAlias, Select)]
fromPageInfoSelection

    fromPageInfoSelection :: [(TableAlias, Select)]
fromPageInfoSelection =
      let hasPrevPage :: SQLExp
hasPrevPage =
            BoolExp -> SQLExp
S.SEBool (BoolExp -> SQLExp) -> BoolExp -> SQLExp
forall a b. (a -> b) -> a -> b
$
              FromItem -> BoolExp -> BoolExp
S.mkExists (Identifier -> FromItem
S.FIIdentifier Identifier
baseSelectIdentifier) (BoolExp -> BoolExp) -> BoolExp -> BoolExp
forall a b. (a -> b) -> a -> b
$
                CompareOp -> SQLExp -> SQLExp -> BoolExp
S.BECompare CompareOp
S.SLT (Identifier -> SQLExp
S.SEIdentifier Identifier
rowNumberIdentifier) (SQLExp -> BoolExp) -> SQLExp -> BoolExp
forall a b. (a -> b) -> a -> b
$
                  Select -> SQLExp
S.SESelect (Select -> SQLExp) -> Select -> SQLExp
forall a b. (a -> b) -> a -> b
$
                    Select
S.mkSelect
                      { 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 [Identifier -> FromItem
S.FIIdentifier Identifier
cursorsSelectAliasIdentifier],
                        selExtr :: [Extractor]
S.selExtr = [SQLExp -> Maybe ColumnAlias -> Extractor
S.Extractor (Identifier -> SQLExp
S.SEIdentifier Identifier
startRowNumberIdentifier) Maybe ColumnAlias
forall a. Maybe a
Nothing]
                      }
          hasNextPage :: SQLExp
hasNextPage =
            BoolExp -> SQLExp
S.SEBool (BoolExp -> SQLExp) -> BoolExp -> SQLExp
forall a b. (a -> b) -> a -> b
$
              FromItem -> BoolExp -> BoolExp
S.mkExists (Identifier -> FromItem
S.FIIdentifier Identifier
baseSelectIdentifier) (BoolExp -> BoolExp) -> BoolExp -> BoolExp
forall a b. (a -> b) -> a -> b
$
                CompareOp -> SQLExp -> SQLExp -> BoolExp
S.BECompare CompareOp
S.SGT (Identifier -> SQLExp
S.SEIdentifier Identifier
rowNumberIdentifier) (SQLExp -> BoolExp) -> SQLExp -> BoolExp
forall a b. (a -> b) -> a -> b
$
                  Select -> SQLExp
S.SESelect (Select -> SQLExp) -> Select -> SQLExp
forall a b. (a -> b) -> a -> b
$
                    Select
S.mkSelect
                      { 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 [Identifier -> FromItem
S.FIIdentifier Identifier
cursorsSelectAliasIdentifier],
                        selExtr :: [Extractor]
S.selExtr = [SQLExp -> Maybe ColumnAlias -> Extractor
S.Extractor (Identifier -> SQLExp
S.SEIdentifier Identifier
endRowNumberIdentifier) Maybe ColumnAlias
forall a. Maybe a
Nothing]
                      }

          select :: Select
select =
            Select
S.mkSelect
              { selExtr :: [Extractor]
S.selExtr =
                  [ SQLExp -> Maybe ColumnAlias -> Extractor
S.Extractor SQLExp
hasPrevPage (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
hasPreviousPageIdentifier,
                    SQLExp -> Maybe ColumnAlias -> Extractor
S.Extractor SQLExp
hasNextPage (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
hasNextPageIdentifier
                  ]
              }
       in (TableAlias, Select) -> [(TableAlias, Select)]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Identifier -> TableAlias
forall a. IsIdentifier a => a -> TableAlias
S.toTableAlias Identifier
pageInfoSelectAliasIdentifier, Select
select)