hunk.code_window:     S.BECompare <$> return op <*> uSqlExp left <*> uSqlExp right
     S.BENull e -> S.BENull <$> uSqlExp e
     S.BENotNull e -> S.BENotNull <$> uSqlExp e
     S.BEExists sel -> S.BEExists <$> uSelect sel
   uOrderBy :: S.OrderByExp -> Uniq S.OrderByExp
   uOrderBy (S.OrderByExp ordByItems) =
  
hunk.labels: keep keep keep add keep keep keep
hunk.after_edit:   S.BEEqualsAny l rExps ->
       S.BEEqualsAny <$> uSqlExp l <*> mapM uSqlExp rExps
  
hunk.file_path: server/src-lib/Hasura/SQL/Rewrite.hs
hunk.type: add
file: {-# LANGUAGE LambdaCase        #-}
  {-# LANGUAGE MultiWayIf        #-}
  {-# LANGUAGE OverloadedStrings #-}
  
  module Hasura.SQL.Rewrite
    ( prefixNumToAliases
    ) where
  
  import qualified Data.HashMap.Strict as Map
  import qualified Data.Text           as T
  import           Hasura.Prelude
  import qualified Hasura.SQL.DML      as S
  import           Hasura.SQL.Types    (Iden (..))
  
  
  -- add an int as a prefix to all aliases.
  -- This is needed in cases identifiers exceed 63 chars
  -- as postgres only considers first 63 chars of
  -- an identifier
  prefixNumToAliases :: S.Select -> S.Select
  prefixNumToAliases s =
    uSelect s `evalState` UniqSt 0 Map.empty
  
  type Rewrite a = State a
  
  data UniqSt
    = UniqSt
    { _uqVar   :: !Int
    , _uqIdens :: !(Map.HashMap Iden Int)
    } deriving (Show, Eq)
  
  type Uniq = Rewrite UniqSt
  
  withNumPfx :: Iden -> Int -> Iden
  withNumPfx iden i =
    Iden pfx <> iden
    where
      pfx = T.pack $ "_" <> show i <> "_"
  
  addAlias :: S.Alias -> Uniq S.Alias
  addAlias (S.Alias iden) = do
    UniqSt var idens <- get
    put $ UniqSt (var + 1) $ Map.insert iden var idens
    return $ S.Alias $ withNumPfx iden var
  
  getIden :: Iden -> Uniq Iden
  getIden iden = do
    UniqSt _ idens <- get
    let varNumM = Map.lookup iden idens
    return $ maybe iden (withNumPfx iden) varNumM
  
  restoringIdens :: Uniq a -> Uniq a
  restoringIdens action = do
    UniqSt _ idens <- get
    res <- action
    -- restore the idens to before the action
    modify' $ \s -> s { _uqIdens = idens }
    return res
  
  uSelect :: S.Select -> Uniq S.Select
  uSelect sel = do
    -- this has to be the first thing to process
    newFromM  <- mapM uFromExp fromM
  
    newWhereM <- forM whereM $
                 \(S.WhereFrag be) -> S.WhereFrag <$> uBoolExp be
    newGrpM   <- forM grpM $
                 \(S.GroupByExp l) -> S.GroupByExp <$> mapM uSqlExp l
    newHavnM  <- forM havnM $
                 \(S.HavingExp be) -> S.HavingExp <$> uBoolExp be
    newOrdM   <- mapM uOrderBy ordByM
    newDistM  <- mapM uDistinct distM
    newExtrs  <- mapM uExtractor extrs
    return $ S.Select newDistM newExtrs newFromM newWhereM newGrpM
      newHavnM newOrdM limitM offM
    where
      S.Select distM extrs fromM whereM grpM havnM ordByM limitM offM = sel
      uDistinct = \case
        S.DistinctSimple -> return S.DistinctSimple
        S.DistinctOn l   -> S.DistinctOn <$> mapM uSqlExp l
      uExtractor (S.Extractor e alM) =
        S.Extractor <$> uSqlExp e <*> return alM
  
  uFromExp :: S.FromExp -> Uniq S.FromExp
  uFromExp (S.FromExp fromItems) =
    S.FromExp <$> mapM uFromItem fromItems
  
  uFromItem :: S.FromItem -> Uniq S.FromItem
  uFromItem fromItem = case fromItem of
    S.FISimple t alM ->
      S.FISimple t <$> mapM addAlias alM
    S.FIIden iden ->
      S.FIIden <$> return iden
    S.FISelect isLateral sel al -> do
      -- we are kind of ignoring if we have to reset
      -- idens to empty based on correlation
      -- unless isLateral $ modify' $ \s -> s { _uqIdens = Map.empty}
      newSel <- restoringIdens $ uSelect sel
      newAls <- addAlias al
      return $ S.FISelect isLateral newSel newAls
    S.FIJoin joinExp ->
      S.FIJoin <$> uJoinExp joinExp
  
  uJoinExp :: S.JoinExpr -> Uniq S.JoinExpr
  uJoinExp (S.JoinExpr left ty right joinCond) = do
    leftN  <- uFromItem left
    rightN <- uFromItem right
    S.JoinExpr leftN ty rightN <$> uJoinCond joinCond
  
  uJoinCond :: S.JoinCond -> Uniq S.JoinCond
  uJoinCond joinCond = case joinCond of
    S.JoinOn be      -> S.JoinOn <$> uBoolExp be
    S.JoinUsing cols -> return $ S.JoinUsing cols
  
  uBoolExp :: S.BoolExp -> Uniq S.BoolExp
  uBoolExp = restoringIdens . \case
    S.BELit b -> return $ S.BELit b
    S.BEBin op left right ->
      S.BEBin <$> return op <*> uBoolExp left <*> uBoolExp right
    S.BENot b -> S.BENot <$> uBoolExp b
    S.BECompare op left right ->
      S.BECompare <$> return op <*> uSqlExp left <*> uSqlExp right
    S.BENull e -> S.BENull <$> uSqlExp e
    S.BENotNull e -> S.BENotNull <$> uSqlExp e
    S.BEExists sel -> S.BEExists <$> uSelect sel
  
  uOrderBy :: S.OrderByExp -> Uniq S.OrderByExp
  uOrderBy (S.OrderByExp ordByItems) =
    S.OrderByExp <$> mapM uOrderByItem ordByItems
    where
      uOrderByItem (S.OrderByItem e ordTyM nullsOrdM) =
        S.OrderByItem
        <$> uSqlExp e
        <*> return ordTyM
        <*> return nullsOrdM
  
  uSqlExp :: S.SQLExp -> Uniq S.SQLExp
  uSqlExp = restoringIdens . \case
    S.SEPrep i                    -> return $ S.SEPrep i
    S.SELit t                     -> return $ S.SELit t
    S.SEUnsafe t                  -> return $ S.SEUnsafe t
    S.SESelect s                  -> S.SESelect <$> uSelect s
    S.SEStar                      -> return S.SEStar
    -- this is for row expressions
    -- todo: check if this is always okay
    S.SEIden iden                 -> return $ S.SEIden iden
    S.SERowIden iden              -> S.SERowIden <$> getIden iden
    S.SEQIden (S.QIden qual iden) -> do
      newQual <- uQual qual
      return $ S.SEQIden $ S.QIden newQual iden
    S.SEFnApp fn args ordByM      ->
      S.SEFnApp
      <$> return fn
      <*> mapM uSqlExp args
      <*> mapM uOrderBy ordByM
    S.SEOpApp op args             ->
      S.SEOpApp op
      <$> mapM uSqlExp args
    S.SETyAnn e ty                ->
      S.SETyAnn
      <$> uSqlExp e
      <*> return ty
    S.SECond be onTrue onFalse    ->
      S.SECond
      <$> uBoolExp be
      <*> uSqlExp onTrue
      <*> uSqlExp onFalse
    S.SEBool be                   ->
      S.SEBool <$> uBoolExp be
    S.SEExcluded t                ->
      S.SEExcluded <$> return t
    S.SEArray l                   ->
      S.SEArray <$> mapM uSqlExp l
    where
      uQual = \case
        S.QualIden iden -> S.QualIden <$> getIden iden
        S.QualTable t   -> return $ S.QualTable t
        S.QualVar t     -> return $ S.QualVar t
  