-- | Stuff gutted from Translate.Select
module Hasura.Backends.Postgres.Translate.Select.Internal.Aliases
  ( mkAggregateOrderByAlias,
    mkAnnOrderByAlias,
    mkArrayRelationAlias,
    mkArrayRelationSourcePrefix,
    mkBaseTableAlias,
    mkBaseTableIdentifier,
    contextualizeBaseTableColumn,
    contextualizeField,
    contextualizeAggregateInput,
    mkComputedFieldTableIdentifier,
    mkObjectRelationTableAlias,
    mkOrderByFieldName,
  )
where

import Control.Monad.Writer.Strict
import Data.HashMap.Strict qualified as HashMap
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.BackendType
import Hasura.RQL.Types.Column
import Hasura.RQL.Types.Common
import Hasura.RQL.Types.ComputedField
import Hasura.RQL.Types.Relationships.Local

-- | Generate alias for order by extractors
mkAnnOrderByAlias ::
  TableIdentifier -> FieldName -> SimilarArrayFields -> AnnotatedOrderByElement ('Postgres pgKind) v -> S.ColumnAlias
mkAnnOrderByAlias :: forall (pgKind :: PostgresKind) v.
TableIdentifier
-> FieldName
-> SimilarArrayFields
-> AnnotatedOrderByElement ('Postgres pgKind) v
-> ColumnAlias
mkAnnOrderByAlias TableIdentifier
tablePrefix FieldName
parAls SimilarArrayFields
similarFields = \case
  AOCColumn ColumnInfo ('Postgres pgKind)
pgColumnInfo AnnRedactionExp ('Postgres pgKind) v
_redactionExp ->
    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 = TableIdentifier -> PGCol -> ColumnAlias
contextualizeBaseTableColumn TableIdentifier
tablePrefix 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 :: TableIdentifier
relPfx = TableIdentifier -> RelName -> TableIdentifier
mkObjectRelationTableAlias TableIdentifier
tablePrefix RelName
rn
        ordByFldName :: FieldName
ordByFldName = RelName -> FieldName
forall a. ToTxt a => a -> FieldName
mkOrderByFieldName RelName
rn
        nesAls :: ColumnAlias
nesAls = TableIdentifier
-> FieldName
-> SimilarArrayFields
-> AnnotatedOrderByElement ('Postgres pgKind) v
-> ColumnAlias
forall (pgKind :: PostgresKind) v.
TableIdentifier
-> FieldName
-> SimilarArrayFields
-> AnnotatedOrderByElement ('Postgres pgKind) v
-> ColumnAlias
mkAnnOrderByAlias TableIdentifier
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) v
aggOrderBy ->
    let rn :: RelName
rn = RelInfo ('Postgres pgKind) -> RelName
forall (b :: BackendType). RelInfo b -> RelName
riName RelInfo ('Postgres pgKind)
relInfo
        arrPfx :: TableIdentifier
arrPfx =
          TableIdentifier
-> FieldName -> SimilarArrayFields -> FieldName -> TableIdentifier
mkArrayRelationSourcePrefix TableIdentifier
tablePrefix FieldName
parAls SimilarArrayFields
similarFields
            (FieldName -> TableIdentifier) -> FieldName -> TableIdentifier
forall a b. (a -> b) -> a -> b
$ RelName -> FieldName
forall a. ToTxt a => a -> FieldName
mkOrderByFieldName RelName
rn
        obAls :: ColumnAlias
obAls = TableIdentifier -> ColumnAlias
S.tableIdentifierToColumnAlias TableIdentifier
arrPfx ColumnAlias -> ColumnAlias -> ColumnAlias
forall a. Semigroup a => a -> a -> a
<> ColumnAlias
"." ColumnAlias -> ColumnAlias -> ColumnAlias
forall a. Semigroup a => a -> a -> a
<> AnnotatedAggregateOrderBy ('Postgres pgKind) v -> ColumnAlias
forall (pgKind :: PostgresKind) v.
AnnotatedAggregateOrderBy ('Postgres pgKind) v -> ColumnAlias
mkAggregateOrderByAlias AnnotatedAggregateOrderBy ('Postgres pgKind) v
aggOrderBy
     in ColumnAlias -> ColumnAlias
forall a. IsIdentifier a => a -> ColumnAlias
S.toColumnAlias ColumnAlias
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)
_ AnnRedactionExp ('Postgres pgKind) v
_redactionExp -> TableIdentifier -> ColumnAlias
S.tableIdentifierToColumnAlias (TableIdentifier -> ColumnAlias) -> TableIdentifier -> ColumnAlias
forall a b. (a -> b) -> a -> b
$ TableIdentifier -> FieldName -> TableIdentifier
mkComputedFieldTableIdentifier TableIdentifier
tablePrefix FieldName
fieldName
          CFOBETableAggregation TableName ('Postgres pgKind)
_ AnnBoolExp ('Postgres pgKind) v
_ AnnotatedAggregateOrderBy ('Postgres pgKind) v
aggOrderBy ->
            let cfPfx :: TableIdentifier
cfPfx = TableIdentifier -> FieldName -> TableIdentifier
mkComputedFieldTableIdentifier TableIdentifier
tablePrefix FieldName
fieldName
                obAls :: ColumnAlias
obAls = TableIdentifier -> ColumnAlias
S.tableIdentifierToColumnAlias TableIdentifier
cfPfx ColumnAlias -> ColumnAlias -> ColumnAlias
forall a. Semigroup a => a -> a -> a
<> ColumnAlias
"." ColumnAlias -> ColumnAlias -> ColumnAlias
forall a. Semigroup a => a -> a -> a
<> AnnotatedAggregateOrderBy ('Postgres pgKind) v -> ColumnAlias
forall (pgKind :: PostgresKind) v.
AnnotatedAggregateOrderBy ('Postgres pgKind) v -> ColumnAlias
mkAggregateOrderByAlias AnnotatedAggregateOrderBy ('Postgres pgKind) v
aggOrderBy
             in ColumnAlias -> ColumnAlias
forall a. IsIdentifier a => a -> ColumnAlias
S.toColumnAlias ColumnAlias
obAls

mkObjectRelationTableAlias :: TableIdentifier -> RelName -> TableIdentifier
mkObjectRelationTableAlias :: TableIdentifier -> RelName -> TableIdentifier
mkObjectRelationTableAlias TableIdentifier
pfx RelName
relName =
  TableIdentifier
pfx TableIdentifier -> TableIdentifier -> TableIdentifier
forall a. Semigroup a => a -> a -> a
<> Text -> TableIdentifier
TableIdentifier (Text
".or." Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> RelName -> Text
relNameToTxt RelName
relName)

mkComputedFieldTableIdentifier :: TableIdentifier -> FieldName -> TableIdentifier
mkComputedFieldTableIdentifier :: TableIdentifier -> FieldName -> TableIdentifier
mkComputedFieldTableIdentifier TableIdentifier
pfx FieldName
fldAls =
  TableIdentifier
pfx TableIdentifier -> TableIdentifier -> TableIdentifier
forall a. Semigroup a => a -> a -> a
<> Text -> TableIdentifier
TableIdentifier Text
".cf." TableIdentifier -> TableIdentifier -> TableIdentifier
forall a. Semigroup a => a -> a -> a
<> Text -> TableIdentifier
TableIdentifier (FieldName -> Text
getFieldNameTxt FieldName
fldAls)

mkBaseTableIdentifier :: TableIdentifier -> TableIdentifier
mkBaseTableIdentifier :: TableIdentifier -> TableIdentifier
mkBaseTableIdentifier TableIdentifier
pfx = TableIdentifier
pfx TableIdentifier -> TableIdentifier -> TableIdentifier
forall a. Semigroup a => a -> a -> a
<> Text -> TableIdentifier
TableIdentifier Text
".base"

mkBaseTableAlias :: S.TableAlias -> S.TableAlias
mkBaseTableAlias :: TableAlias -> TableAlias
mkBaseTableAlias TableAlias
pfx = TableAlias
pfx TableAlias -> TableAlias -> TableAlias
forall a. Semigroup a => a -> a -> a
<> TableAlias
".base"

contextualizeBaseTableColumn :: TableIdentifier -> PGCol -> S.ColumnAlias
contextualizeBaseTableColumn :: TableIdentifier -> PGCol -> ColumnAlias
contextualizeBaseTableColumn TableIdentifier
pfx PGCol
pgColumn =
  TableIdentifier -> ColumnAlias
S.tableIdentifierToColumnAlias TableIdentifier
pfx ColumnAlias -> ColumnAlias -> ColumnAlias
forall a. Semigroup a => a -> a -> a
<> ColumnAlias
".pg." ColumnAlias -> ColumnAlias -> ColumnAlias
forall a. Semigroup a => a -> a -> a
<> PGCol -> ColumnAlias
forall a. IsIdentifier a => a -> ColumnAlias
S.toColumnAlias PGCol
pgColumn

contextualizeField :: TableIdentifier -> FieldName -> S.ColumnAlias
contextualizeField :: TableIdentifier -> FieldName -> ColumnAlias
contextualizeField TableIdentifier
pfx FieldName
field =
  TableIdentifier -> ColumnAlias
S.tableIdentifierToColumnAlias TableIdentifier
pfx ColumnAlias -> ColumnAlias -> ColumnAlias
forall a. Semigroup a => a -> a -> a
<> ColumnAlias
".f." ColumnAlias -> ColumnAlias -> ColumnAlias
forall a. Semigroup a => a -> a -> a
<> FieldName -> ColumnAlias
forall a. IsIdentifier a => a -> ColumnAlias
S.toColumnAlias FieldName
field

contextualizeAggregateInput :: TableIdentifier -> FieldName -> FieldName -> S.ColumnAlias
contextualizeAggregateInput :: TableIdentifier -> FieldName -> FieldName -> ColumnAlias
contextualizeAggregateInput TableIdentifier
pfx FieldName
aggregateField FieldName
field =
  TableIdentifier -> ColumnAlias
S.tableIdentifierToColumnAlias TableIdentifier
pfx ColumnAlias -> ColumnAlias -> ColumnAlias
forall a. Semigroup a => a -> a -> a
<> ColumnAlias
".ai." ColumnAlias -> ColumnAlias -> ColumnAlias
forall a. Semigroup a => a -> a -> a
<> FieldName -> ColumnAlias
forall a. IsIdentifier a => a -> ColumnAlias
S.toColumnAlias FieldName
aggregateField ColumnAlias -> ColumnAlias -> ColumnAlias
forall a. Semigroup a => a -> a -> a
<> ColumnAlias
"." ColumnAlias -> ColumnAlias -> ColumnAlias
forall a. Semigroup a => a -> a -> a
<> FieldName -> ColumnAlias
forall a. IsIdentifier a => a -> ColumnAlias
S.toColumnAlias FieldName
field

mkAggregateOrderByAlias :: AnnotatedAggregateOrderBy ('Postgres pgKind) v -> S.ColumnAlias
mkAggregateOrderByAlias :: forall (pgKind :: PostgresKind) v.
AnnotatedAggregateOrderBy ('Postgres pgKind) v -> 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) v -> Text)
-> AnnotatedAggregateOrderBy ('Postgres pgKind) v
-> ColumnAlias
forall b c a. (b -> c) -> (a -> b) -> a -> c
. \case
    AnnotatedAggregateOrderBy ('Postgres pgKind) v
AAOCount -> Text
"count"
    AAOOp AggregateOrderByColumn {Text
ColumnType ('Postgres pgKind)
ColumnInfo ('Postgres pgKind)
AnnRedactionExp ('Postgres pgKind) v
_aobcAggregateFunctionName :: Text
_aobcAggregateFunctionReturnType :: ColumnType ('Postgres pgKind)
_aobcColumn :: ColumnInfo ('Postgres pgKind)
_aobcRedactionExpression :: AnnRedactionExp ('Postgres pgKind) v
$sel:_aobcAggregateFunctionName:AggregateOrderByColumn :: forall (b :: BackendType) v. AggregateOrderByColumn b v -> Text
$sel:_aobcAggregateFunctionReturnType:AggregateOrderByColumn :: forall (b :: BackendType) v.
AggregateOrderByColumn b v -> ColumnType b
$sel:_aobcColumn:AggregateOrderByColumn :: forall (b :: BackendType) v.
AggregateOrderByColumn b v -> ColumnInfo b
$sel:_aobcRedactionExpression:AggregateOrderByColumn :: forall (b :: BackendType) v.
AggregateOrderByColumn b v -> AnnRedactionExp b v
..} -> Text
_aobcAggregateFunctionName 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)
_aobcColumn)

mkOrderByFieldName :: (ToTxt a) => a -> FieldName
mkOrderByFieldName :: forall a. ToTxt a => 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 ::
  TableIdentifier ->
  FieldName ->
  HashMap.HashMap FieldName [FieldName] ->
  FieldName ->
  TableIdentifier
mkArrayRelationSourcePrefix :: TableIdentifier
-> FieldName -> SimilarArrayFields -> FieldName -> TableIdentifier
mkArrayRelationSourcePrefix TableIdentifier
parentSourcePrefix FieldName
parentFieldName SimilarArrayFields
similarFieldsMap FieldName
fieldName =
  TableIdentifier -> FieldName -> [FieldName] -> TableIdentifier
mkArrayRelationTableIdentifier TableIdentifier
parentSourcePrefix FieldName
parentFieldName
    ([FieldName] -> TableIdentifier) -> [FieldName] -> TableIdentifier
forall a b. (a -> b) -> a -> b
$ [FieldName] -> FieldName -> SimilarArrayFields -> [FieldName]
forall k v. (Eq k, Hashable k) => v -> k -> HashMap k v -> v
HashMap.lookupDefault [FieldName
fieldName] FieldName
fieldName SimilarArrayFields
similarFieldsMap

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

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

-- array relationships are not grouped, so have to be prefixed by
-- parent's alias
mkUniqArrayRelationAlias :: FieldName -> [FieldName] -> Text
mkUniqArrayRelationAlias :: FieldName -> [FieldName] -> Text
mkUniqArrayRelationAlias FieldName
parAls [FieldName]
flds =
  let sortedFields :: [FieldName]
sortedFields = [FieldName] -> [FieldName]
forall a. Ord a => [a] -> [a]
sort [FieldName]
flds
   in 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)