-- | 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 HashMap
import Data.HashMap.Strict.InsOrd qualified as InsOrdHashMap
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.SQL.Types qualified as S
import Hasura.Backends.Postgres.Translate.Select.Internal.Aliases (mkBaseTableAlias)
import Hasura.Backends.Postgres.Translate.Select.Internal.Helpers
  ( cursorIdentifier,
    cursorsSelectAlias,
    cursorsSelectAliasIdentifier,
    endCursorIdentifier,
    hasNextPageIdentifier,
    hasPreviousPageIdentifier,
    mkFirstElementExp,
    mkLastElementExp,
    pageInfoSelectAlias,
    startCursorIdentifier,
  )
import Hasura.Backends.Postgres.Translate.Types
import Hasura.Prelude
import Hasura.RQL.IR.Select (ConnectionSlice (SliceFirst, SliceLast))
import Hasura.RQL.Types.Relationships.Local (Nullable (..))

generateSQLSelect ::
  -- | Pre join condition for lateral joins
  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 =
        case [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) <- InsOrdHashMap ColumnAlias SQLExp -> [(ColumnAlias, SQLExp)]
forall k v. InsOrdHashMap k v -> [(k, v)]
InsOrdHashMap.toList InsOrdHashMap ColumnAlias SQLExp
extractors] of
          -- If the select list is empty we will generated code which looks like this:
          -- > SELECT FROM ...
          -- This works for postgres, but not for cockroach, which expects a non-empty
          -- select list. So we add a dummy value:
          [] -> [Extractor]
S.dummySelectList -- SELECT 1 FROM ...
          [Extractor]
exts -> [Extractor]
exts,
      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 InsOrdHashMap 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
        }
    -- This is why 'SelectSource{sourcePrefix}' needs to be a TableAlias!
    baseSelectAlias :: TableAlias
baseSelectAlias = TableAlias -> TableAlias
mkBaseTableAlias (Identifier -> TableAlias
forall a. IsIdentifier a => a -> TableAlias
S.toTableAlias Identifier
sourcePrefix)
    baseSelectIdentifier :: TableIdentifier
baseSelectIdentifier = TableAlias -> TableIdentifier
S.tableAliasToIdentifier TableAlias
baseSelectAlias
    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, JoinType) -> FromItem
leftOuterJoin FromItem
current (FromItem
new, JoinType
joinType) =
      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
joinType 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, JoinType) -> FromItem)
-> FromItem -> [(FromItem, JoinType)] -> FromItem
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' FromItem -> (FromItem, JoinType) -> FromItem
leftOuterJoin FromItem
baseFromItem
        ([(FromItem, JoinType)] -> FromItem)
-> [(FromItem, JoinType)] -> FromItem
forall a b. (a -> b) -> a -> b
$ ((ObjectRelationSource, SelectNode) -> (FromItem, JoinType))
-> [(ObjectRelationSource, SelectNode)] -> [(FromItem, JoinType)]
forall a b. (a -> b) -> [a] -> [b]
map (ObjectRelationSource, SelectNode) -> (FromItem, JoinType)
objectRelationToFromItem (HashMap ObjectRelationSource SelectNode
-> [(ObjectRelationSource, SelectNode)]
forall k v. HashMap k v -> [(k, v)]
HashMap.toList HashMap ObjectRelationSource SelectNode
objectRelations)
        [(FromItem, JoinType)]
-> [(FromItem, JoinType)] -> [(FromItem, JoinType)]
forall a. Semigroup a => a -> a -> a
<> ((ArrayRelationSource, MultiRowSelectNode) -> (FromItem, JoinType))
-> [(ArrayRelationSource, MultiRowSelectNode)]
-> [(FromItem, JoinType)]
forall a b. (a -> b) -> [a] -> [b]
map (ArrayRelationSource, MultiRowSelectNode) -> (FromItem, JoinType)
arrayRelationToFromItem (HashMap ArrayRelationSource MultiRowSelectNode
-> [(ArrayRelationSource, MultiRowSelectNode)]
forall k v. HashMap k v -> [(k, v)]
HashMap.toList HashMap ArrayRelationSource MultiRowSelectNode
arrayRelations)
        [(FromItem, JoinType)]
-> [(FromItem, JoinType)] -> [(FromItem, JoinType)]
forall a. Semigroup a => a -> a -> a
<> ((ArrayConnectionSource, MultiRowSelectNode)
 -> (FromItem, JoinType))
-> [(ArrayConnectionSource, MultiRowSelectNode)]
-> [(FromItem, JoinType)]
forall a b. (a -> b) -> [a] -> [b]
map (ArrayConnectionSource, MultiRowSelectNode) -> (FromItem, JoinType)
arrayConnectionToFromItem (HashMap ArrayConnectionSource MultiRowSelectNode
-> [(ArrayConnectionSource, MultiRowSelectNode)]
forall k v. HashMap k v -> [(k, v)]
HashMap.toList HashMap ArrayConnectionSource MultiRowSelectNode
arrayConnections)
        [(FromItem, JoinType)]
-> [(FromItem, JoinType)] -> [(FromItem, JoinType)]
forall a. Semigroup a => a -> a -> a
<> ((ComputedFieldTableSetSource, MultiRowSelectNode)
 -> (FromItem, JoinType))
-> [(ComputedFieldTableSetSource, MultiRowSelectNode)]
-> [(FromItem, JoinType)]
forall a b. (a -> b) -> [a] -> [b]
map (ComputedFieldTableSetSource, MultiRowSelectNode)
-> (FromItem, JoinType)
computedFieldToFromItem (HashMap ComputedFieldTableSetSource MultiRowSelectNode
-> [(ComputedFieldTableSetSource, MultiRowSelectNode)]
forall k v. HashMap k v -> [(k, v)]
HashMap.toList HashMap ComputedFieldTableSetSource MultiRowSelectNode
computedFields)

    objectRelationToFromItem ::
      (ObjectRelationSource, SelectNode) -> (S.FromItem, S.JoinType)
    objectRelationToFromItem :: (ObjectRelationSource, SelectNode) -> (FromItem, JoinType)
objectRelationToFromItem (ObjectRelationSource
objectRelationSource, SelectNode
node) =
      let ObjectRelationSource
            { _orsRelationMapping :: ObjectRelationSource -> HashMap PGCol PGCol
_orsRelationMapping = HashMap PGCol PGCol
colMapping,
              _orsSelectSource :: ObjectRelationSource -> ObjectSelectSource
_orsSelectSource = ObjectSelectSource
objectSelectSource,
              _orsNullable :: ObjectRelationSource -> Nullable
_orsNullable = Nullable
nullable
            } = 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 (TableIdentifier -> HashMap PGCol PGCol -> BoolExp
mkJoinCond TableIdentifier
baseSelectIdentifier HashMap PGCol PGCol
colMapping) SelectSource
source SelectNode
node
          joinType :: JoinType
joinType = case Nullable
nullable of
            Nullable
Nullable -> JoinType
S.LeftOuter
            Nullable
NotNullable -> JoinType
S.Inner
       in (Select -> TableAlias -> FromItem
S.mkLateralFromItem Select
select TableAlias
alias, JoinType
joinType)

    arrayRelationToFromItem ::
      (ArrayRelationSource, MultiRowSelectNode) -> (S.FromItem, S.JoinType)
    arrayRelationToFromItem :: (ArrayRelationSource, MultiRowSelectNode) -> (FromItem, JoinType)
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
$ TableIdentifier -> HashMap PGCol PGCol -> BoolExp
mkJoinCond TableIdentifier
baseSelectIdentifier HashMap PGCol PGCol
colMapping
       in (Select -> TableAlias -> FromItem
S.mkLateralFromItem Select
select TableAlias
alias, JoinType
S.LeftOuter)

    arrayConnectionToFromItem ::
      (ArrayConnectionSource, MultiRowSelectNode) -> (S.FromItem, S.JoinType)
    arrayConnectionToFromItem :: (ArrayConnectionSource, MultiRowSelectNode) -> (FromItem, JoinType)
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, JoinType
S.LeftOuter)

    computedFieldToFromItem ::
      (ComputedFieldTableSetSource, MultiRowSelectNode) -> (S.FromItem, S.JoinType)
    computedFieldToFromItem :: (ComputedFieldTableSetSource, MultiRowSelectNode)
-> (FromItem, JoinType)
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, JoinType
S.LeftOuter)

-- | Create a select query
generateSQLSelectFromArrayNode ::
  SelectSource ->
  MultiRowSelectNode ->
  S.BoolExp ->
  S.Select
generateSQLSelectFromArrayNode :: SelectSource -> MultiRowSelectNode -> BoolExp -> Select
generateSQLSelectFromArrayNode SelectSource
selectSource (MultiRowSelectNode [Extractor]
topExtractors SelectNode
selectNode) 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
            [ 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.TableIdentifier -> HashMap PGCol PGCol -> S.BoolExp
mkJoinCond :: TableIdentifier -> HashMap PGCol PGCol -> BoolExp
mkJoinCond TableIdentifier
baseTablepfx HashMap PGCol PGCol
colMapn =
  (BoolExp -> BoolExp -> BoolExp) -> BoolExp -> [BoolExp] -> BoolExp
forall b a. (b -> a -> b) -> b -> [a] -> b
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)]
HashMap.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 (TableIdentifier -> PGCol -> SQLExp
forall b. IsIdentifier b => TableIdentifier -> b -> SQLExp
S.mkQIdenExp TableIdentifier
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
rootSelectAlias 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 [TableIdentifier -> FromItem
S.FIIdentifier TableIdentifier
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

    rootSelectIdentifier :: TableIdentifier
rootSelectIdentifier = TableAlias -> TableIdentifier
S.tableAliasToIdentifier TableAlias
rootSelectAlias

    baseSelectAlias :: TableAlias
baseSelectAlias = Text -> TableAlias
S.mkTableAlias Text
"__base_select"
    baseSelectIdentifier :: TableIdentifier
baseSelectIdentifier = TableAlias -> TableIdentifier
S.tableAliasToIdentifier TableAlias
baseSelectAlias

    splitSelectAlias :: TableAlias
splitSelectAlias = Text -> TableAlias
S.mkTableAlias Text
"__split_select"
    splitSelectIdentifier :: TableIdentifier
splitSelectIdentifier = TableAlias -> TableIdentifier
S.tableAliasToIdentifier TableAlias
splitSelectAlias

    sliceSelectAlias :: TableAlias
sliceSelectAlias = Text -> TableAlias
S.mkTableAlias Text
"__slice_select"
    sliceSelectIdentifier :: TableIdentifier
sliceSelectIdentifier = TableAlias -> TableIdentifier
S.tableAliasToIdentifier TableAlias
sliceSelectAlias

    finalSelectAlias :: TableAlias
finalSelectAlias = Text -> TableAlias
S.mkTableAlias Text
"__final_select"
    finalSelectIdentifier :: TableIdentifier
finalSelectIdentifier = TableAlias -> TableIdentifier
S.tableAliasToIdentifier TableAlias
finalSelectAlias

    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 = TableIdentifier -> HashMap PGCol PGCol -> BoolExp
mkJoinCond TableIdentifier
rootSelectIdentifier 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 (TableAlias
baseSelectAlias, Select
select) (TableAlias, Select)
-> [(TableAlias, Select)] -> [(TableAlias, Select)]
forall a. a -> [a] -> [a]
: [(TableAlias, Select)]
fromSplitSelection

    mkStarSelect :: TableIdentifier -> Select
mkStarSelect TableIdentifier
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 [TableIdentifier -> FromItem
S.FIIdentifier TableIdentifier
fromIdentifier]
        }

    fromSplitSelection :: [(TableAlias, Select)]
fromSplitSelection = case Maybe BoolExp
maybeSplit of
      Maybe BoolExp
Nothing -> TableIdentifier -> [(TableAlias, Select)]
fromSliceSelection TableIdentifier
baseSelectIdentifier
      Just BoolExp
splitBool ->
        let select :: Select
select =
              (TableIdentifier -> Select
mkStarSelect TableIdentifier
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 (TableAlias
splitSelectAlias, Select
select) (TableAlias, Select)
-> [(TableAlias, Select)] -> [(TableAlias, Select)]
forall a. a -> [a] -> [a]
: TableIdentifier -> [(TableAlias, Select)]
fromSliceSelection TableIdentifier
splitSelectIdentifier

    fromSliceSelection :: TableIdentifier -> [(TableAlias, Select)]
fromSliceSelection TableIdentifier
prevSelect = case Maybe ConnectionSlice
maybeSlice of
      Maybe ConnectionSlice
Nothing -> TableIdentifier -> [(TableAlias, Select)]
fromFinalSelect TableIdentifier
prevSelect
      Just ConnectionSlice
slice ->
        let select :: Select
select = case ConnectionSlice
slice of
              SliceFirst Int
limit ->
                (TableIdentifier -> Select
mkStarSelect TableIdentifier
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 =
                      (TableIdentifier -> Select
mkStarSelect TableIdentifier
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
$ TableAlias
sliceSelectAlias
                 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 (TableAlias
sliceSelectAlias, Select
select) (TableAlias, Select)
-> [(TableAlias, Select)] -> [(TableAlias, Select)]
forall a. a -> [a] -> [a]
: TableIdentifier -> [(TableAlias, Select)]
fromFinalSelect TableIdentifier
sliceSelectIdentifier

    fromFinalSelect :: TableIdentifier -> [(TableAlias, Select)]
fromFinalSelect TableIdentifier
prevSelect =
      let select :: Select
select = TableIdentifier -> Select
mkStarSelect TableIdentifier
prevSelect
       in (TableAlias
finalSelectAlias, 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 [TableIdentifier -> FromItem
S.FIIdentifier TableIdentifier
finalSelectIdentifier]
              }
       in (TableAlias
cursorsSelectAlias, 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 (TableIdentifier -> FromItem
S.FIIdentifier TableIdentifier
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 [TableIdentifier -> FromItem
S.FIIdentifier TableIdentifier
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 (TableIdentifier -> FromItem
S.FIIdentifier TableIdentifier
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 [TableIdentifier -> FromItem
S.FIIdentifier TableIdentifier
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 a. a -> [a]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (TableAlias
pageInfoSelectAlias, Select
select)