-- | Stuff gutted from Translate.Select
module Hasura.Backends.Postgres.Translate.Select.Internal.Aliases
  ( mkAggregateOrderByAlias,
    mkAnnOrderByAlias,
    mkArrayRelationAlias,
    mkArrayRelationSourcePrefix,
    mkBaseTableAlias,
    mkBaseTableColumnAlias,
    mkComputedFieldTableAlias,
    mkObjectRelationTableAlias,
    mkOrderByFieldName,
  )
where

import Control.Monad.Writer.Strict
import Data.HashMap.Strict qualified as HM
import Data.Text qualified as T
import Data.Text.Extended
import Hasura.Backends.Postgres.SQL.DML qualified as S
import Hasura.Backends.Postgres.SQL.Types
import Hasura.Backends.Postgres.Translate.Types
import Hasura.Prelude
import Hasura.RQL.IR.Select
import Hasura.RQL.Types.Column
import Hasura.RQL.Types.Common
import Hasura.RQL.Types.ComputedField
import Hasura.RQL.Types.Relationships.Local
import Hasura.SQL.Backend

-- | Generate alias for order by extractors
mkAnnOrderByAlias ::
  Identifier -> FieldName -> SimilarArrayFields -> AnnotatedOrderByElement ('Postgres pgKind) v -> S.ColumnAlias
mkAnnOrderByAlias :: Identifier
-> FieldName
-> SimilarArrayFields
-> AnnotatedOrderByElement ('Postgres pgKind) v
-> ColumnAlias
mkAnnOrderByAlias Identifier
pfx FieldName
parAls SimilarArrayFields
similarFields = \case
  AOCColumn 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
        obColAls :: ColumnAlias
obColAls = Identifier -> PGCol -> ColumnAlias
mkBaseTableColumnAlias Identifier
pfx Column ('Postgres pgKind)
PGCol
pgColumn
     in ColumnAlias
obColAls
  -- "pfx.or.relname"."pfx.ob.or.relname.rest" AS "pfx.ob.or.relname.rest"
  AOCObjectRelation RelInfo ('Postgres pgKind)
relInfo AnnBoolExp ('Postgres pgKind) v
_ AnnotatedOrderByElement ('Postgres pgKind) v
rest ->
    let rn :: RelName
rn = RelInfo ('Postgres pgKind) -> RelName
forall (b :: BackendType). RelInfo b -> RelName
riName RelInfo ('Postgres pgKind)
relInfo
        relPfx :: Identifier
relPfx = Identifier -> RelName -> Identifier
mkObjectRelationTableAlias Identifier
pfx RelName
rn
        ordByFldName :: FieldName
ordByFldName = RelName -> FieldName
forall a. ToTxt a => a -> FieldName
mkOrderByFieldName RelName
rn
        nesAls :: ColumnAlias
nesAls = Identifier
-> FieldName
-> SimilarArrayFields
-> AnnotatedOrderByElement ('Postgres pgKind) v
-> ColumnAlias
forall (pgKind :: PostgresKind) v.
Identifier
-> FieldName
-> SimilarArrayFields
-> AnnotatedOrderByElement ('Postgres pgKind) v
-> ColumnAlias
mkAnnOrderByAlias Identifier
relPfx FieldName
ordByFldName SimilarArrayFields
forall a. Monoid a => a
mempty AnnotatedOrderByElement ('Postgres pgKind) v
rest
     in ColumnAlias
nesAls
  AOCArrayAggregation RelInfo ('Postgres pgKind)
relInfo AnnBoolExp ('Postgres pgKind) v
_ AnnotatedAggregateOrderBy ('Postgres pgKind)
aggOrderBy ->
    let rn :: RelName
rn = RelInfo ('Postgres pgKind) -> RelName
forall (b :: BackendType). RelInfo b -> RelName
riName RelInfo ('Postgres pgKind)
relInfo
        arrPfx :: Identifier
arrPfx =
          Identifier
-> FieldName -> SimilarArrayFields -> FieldName -> Identifier
mkArrayRelationSourcePrefix Identifier
pfx FieldName
parAls SimilarArrayFields
similarFields (FieldName -> Identifier) -> FieldName -> Identifier
forall a b. (a -> b) -> a -> b
$
            RelName -> FieldName
forall a. ToTxt a => a -> FieldName
mkOrderByFieldName RelName
rn
        obAls :: Identifier
obAls = Identifier
arrPfx Identifier -> Identifier -> Identifier
forall a. Semigroup a => a -> a -> a
<> Text -> Identifier
Identifier Text
"." Identifier -> Identifier -> Identifier
forall a. Semigroup a => a -> a -> a
<> ColumnAlias -> Identifier
forall a. IsIdentifier a => a -> Identifier
toIdentifier (AnnotatedAggregateOrderBy ('Postgres pgKind) -> ColumnAlias
forall (pgKind :: PostgresKind).
AnnotatedAggregateOrderBy ('Postgres pgKind) -> ColumnAlias
mkAggregateOrderByAlias AnnotatedAggregateOrderBy ('Postgres pgKind)
aggOrderBy)
     in Identifier -> ColumnAlias
forall a. IsIdentifier a => a -> ColumnAlias
S.toColumnAlias Identifier
obAls
  AOCComputedField ComputedFieldOrderBy ('Postgres pgKind) v
cfOrderBy ->
    let fieldName :: FieldName
fieldName = ComputedFieldName -> FieldName
fromComputedField (ComputedFieldName -> FieldName) -> ComputedFieldName -> FieldName
forall a b. (a -> b) -> a -> b
$ ComputedFieldOrderBy ('Postgres pgKind) v -> ComputedFieldName
forall (b :: BackendType) v.
ComputedFieldOrderBy b v -> ComputedFieldName
_cfobName ComputedFieldOrderBy ('Postgres pgKind) v
cfOrderBy
     in case ComputedFieldOrderBy ('Postgres pgKind) v
-> ComputedFieldOrderByElement ('Postgres pgKind) v
forall (b :: BackendType) v.
ComputedFieldOrderBy b v -> ComputedFieldOrderByElement b v
_cfobOrderByElement ComputedFieldOrderBy ('Postgres pgKind) v
cfOrderBy of
          CFOBEScalar ScalarType ('Postgres pgKind)
_ -> Identifier -> ColumnAlias
forall a. IsIdentifier a => a -> ColumnAlias
S.toColumnAlias (Identifier -> ColumnAlias) -> Identifier -> ColumnAlias
forall a b. (a -> b) -> a -> b
$ Identifier -> FieldName -> Identifier
mkComputedFieldTableAlias Identifier
pfx FieldName
fieldName
          CFOBETableAggregation TableName ('Postgres pgKind)
_ AnnBoolExp ('Postgres pgKind) v
_ AnnotatedAggregateOrderBy ('Postgres pgKind)
aggOrderBy ->
            let cfPfx :: Identifier
cfPfx = Identifier -> FieldName -> Identifier
mkComputedFieldTableAlias Identifier
pfx FieldName
fieldName
                obAls :: Identifier
obAls = Identifier
cfPfx Identifier -> Identifier -> Identifier
forall a. Semigroup a => a -> a -> a
<> Text -> Identifier
Identifier Text
"." Identifier -> Identifier -> Identifier
forall a. Semigroup a => a -> a -> a
<> ColumnAlias -> Identifier
forall a. IsIdentifier a => a -> Identifier
toIdentifier (AnnotatedAggregateOrderBy ('Postgres pgKind) -> ColumnAlias
forall (pgKind :: PostgresKind).
AnnotatedAggregateOrderBy ('Postgres pgKind) -> ColumnAlias
mkAggregateOrderByAlias AnnotatedAggregateOrderBy ('Postgres pgKind)
aggOrderBy)
             in Identifier -> ColumnAlias
forall a. IsIdentifier a => a -> ColumnAlias
S.toColumnAlias Identifier
obAls

-- array relationships are not grouped, so have to be prefixed by
-- parent's alias
mkUniqArrayRelationAlias :: FieldName -> [FieldName] -> Identifier
mkUniqArrayRelationAlias :: FieldName -> [FieldName] -> Identifier
mkUniqArrayRelationAlias FieldName
parAls [FieldName]
flds =
  let sortedFields :: [FieldName]
sortedFields = [FieldName] -> [FieldName]
forall a. Ord a => [a] -> [a]
sort [FieldName]
flds
   in Text -> Identifier
Identifier (Text -> Identifier) -> Text -> Identifier
forall a b. (a -> b) -> a -> b
$
        FieldName -> Text
getFieldNameTxt FieldName
parAls Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"."
          Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> [Text] -> Text
T.intercalate Text
"." ((FieldName -> Text) -> [FieldName] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map FieldName -> Text
getFieldNameTxt [FieldName]
sortedFields)

mkArrayRelationTableAlias :: Identifier -> FieldName -> [FieldName] -> Identifier
mkArrayRelationTableAlias :: Identifier -> FieldName -> [FieldName] -> Identifier
mkArrayRelationTableAlias Identifier
pfx FieldName
parAls [FieldName]
flds =
  Identifier
pfx Identifier -> Identifier -> Identifier
forall a. Semigroup a => a -> a -> a
<> Text -> Identifier
Identifier Text
".ar." Identifier -> Identifier -> Identifier
forall a. Semigroup a => a -> a -> a
<> Identifier
uniqArrRelAls
  where
    uniqArrRelAls :: Identifier
uniqArrRelAls = FieldName -> [FieldName] -> Identifier
mkUniqArrayRelationAlias FieldName
parAls [FieldName]
flds

mkObjectRelationTableAlias :: Identifier -> RelName -> Identifier
mkObjectRelationTableAlias :: Identifier -> RelName -> Identifier
mkObjectRelationTableAlias Identifier
pfx RelName
relName =
  Identifier
pfx Identifier -> Identifier -> Identifier
forall a. Semigroup a => a -> a -> a
<> Text -> Identifier
Identifier Text
".or." Identifier -> Identifier -> Identifier
forall a. Semigroup a => a -> a -> a
<> RelName -> Identifier
forall a. IsIdentifier a => a -> Identifier
toIdentifier RelName
relName

mkComputedFieldTableAlias :: Identifier -> FieldName -> Identifier
mkComputedFieldTableAlias :: Identifier -> FieldName -> Identifier
mkComputedFieldTableAlias Identifier
pfx FieldName
fldAls =
  Identifier
pfx Identifier -> Identifier -> Identifier
forall a. Semigroup a => a -> a -> a
<> Text -> Identifier
Identifier Text
".cf." Identifier -> Identifier -> Identifier
forall a. Semigroup a => a -> a -> a
<> FieldName -> Identifier
forall a. IsIdentifier a => a -> Identifier
toIdentifier FieldName
fldAls

mkBaseTableAlias :: Identifier -> S.TableAlias
mkBaseTableAlias :: Identifier -> TableAlias
mkBaseTableAlias Identifier
pfx =
  Identifier -> TableAlias
forall a. IsIdentifier a => a -> TableAlias
S.toTableAlias (Identifier -> TableAlias) -> Identifier -> TableAlias
forall a b. (a -> b) -> a -> b
$ Identifier
pfx Identifier -> Identifier -> Identifier
forall a. Semigroup a => a -> a -> a
<> Text -> Identifier
Identifier Text
".base"

mkBaseTableColumnAlias :: Identifier -> PGCol -> S.ColumnAlias
mkBaseTableColumnAlias :: Identifier -> PGCol -> ColumnAlias
mkBaseTableColumnAlias Identifier
pfx PGCol
pgColumn =
  Identifier -> ColumnAlias
forall a. IsIdentifier a => a -> ColumnAlias
S.toColumnAlias (Identifier -> ColumnAlias) -> Identifier -> ColumnAlias
forall a b. (a -> b) -> a -> b
$ Identifier
pfx Identifier -> Identifier -> Identifier
forall a. Semigroup a => a -> a -> a
<> Text -> Identifier
Identifier Text
".pg." Identifier -> Identifier -> Identifier
forall a. Semigroup a => a -> a -> a
<> PGCol -> Identifier
forall a. IsIdentifier a => a -> Identifier
toIdentifier PGCol
pgColumn

mkAggregateOrderByAlias :: AnnotatedAggregateOrderBy ('Postgres pgKind) -> S.ColumnAlias
mkAggregateOrderByAlias :: AnnotatedAggregateOrderBy ('Postgres pgKind) -> ColumnAlias
mkAggregateOrderByAlias =
  (Identifier -> ColumnAlias
forall a. IsIdentifier a => a -> ColumnAlias
S.toColumnAlias (Identifier -> ColumnAlias)
-> (Text -> Identifier) -> Text -> ColumnAlias
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Identifier
Identifier) (Text -> ColumnAlias)
-> (AnnotatedAggregateOrderBy ('Postgres pgKind) -> Text)
-> AnnotatedAggregateOrderBy ('Postgres pgKind)
-> ColumnAlias
forall b c a. (b -> c) -> (a -> b) -> a -> c
. \case
    AnnotatedAggregateOrderBy ('Postgres pgKind)
AAOCount -> Text
"count"
    AAOOp Text
opText ColumnInfo ('Postgres pgKind)
col -> Text
opText Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"." Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> PGCol -> Text
getPGColTxt (ColumnInfo ('Postgres pgKind) -> Column ('Postgres pgKind)
forall (b :: BackendType). ColumnInfo b -> Column b
ciColumn ColumnInfo ('Postgres pgKind)
col)

mkOrderByFieldName :: ToTxt a => a -> FieldName
mkOrderByFieldName :: a -> FieldName
mkOrderByFieldName a
name =
  Text -> FieldName
FieldName (Text -> FieldName) -> Text -> FieldName
forall a b. (a -> b) -> a -> b
$ a -> Text
forall a. ToTxt a => a -> Text
toTxt a
name Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"." Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"order_by"

mkArrayRelationSourcePrefix ::
  Identifier ->
  FieldName ->
  HM.HashMap FieldName [FieldName] ->
  FieldName ->
  Identifier
mkArrayRelationSourcePrefix :: Identifier
-> FieldName -> SimilarArrayFields -> FieldName -> Identifier
mkArrayRelationSourcePrefix Identifier
parentSourcePrefix FieldName
parentFieldName SimilarArrayFields
similarFieldsMap FieldName
fieldName =
  Identifier -> FieldName -> [FieldName] -> Identifier
mkArrayRelationTableAlias Identifier
parentSourcePrefix FieldName
parentFieldName ([FieldName] -> Identifier) -> [FieldName] -> Identifier
forall a b. (a -> b) -> a -> b
$
    [FieldName] -> FieldName -> SimilarArrayFields -> [FieldName]
forall k v. (Eq k, Hashable k) => v -> k -> HashMap k v -> v
HM.lookupDefault [FieldName
fieldName] FieldName
fieldName SimilarArrayFields
similarFieldsMap

mkArrayRelationAlias ::
  FieldName ->
  HM.HashMap FieldName [FieldName] ->
  FieldName ->
  S.TableAlias
mkArrayRelationAlias :: FieldName -> SimilarArrayFields -> FieldName -> TableAlias
mkArrayRelationAlias FieldName
parentFieldName SimilarArrayFields
similarFieldsMap FieldName
fieldName =
  Identifier -> TableAlias
forall a. IsIdentifier a => a -> TableAlias
S.toTableAlias (Identifier -> TableAlias) -> Identifier -> TableAlias
forall a b. (a -> b) -> a -> b
$
    FieldName -> [FieldName] -> Identifier
mkUniqArrayRelationAlias FieldName
parentFieldName ([FieldName] -> Identifier) -> [FieldName] -> Identifier
forall a b. (a -> b) -> a -> b
$
      [FieldName] -> FieldName -> SimilarArrayFields -> [FieldName]
forall k v. (Eq k, Hashable k) => v -> k -> HashMap k v -> v
HM.lookupDefault [FieldName
fieldName] FieldName
fieldName SimilarArrayFields
similarFieldsMap