-- | Postgres SQL Rename Identifiers
--
-- 1. Prefix table names with underscores to avoid issues where column names and tables conflict.
--    This can happen because we give columns and tables the name @root@ for some reason,
--    and that can trip up @row_to_json@.
--    See <https://github.com/PostgREST/postgrest/issues/993#issuecomment-340377813>.
--    An alternative solution would be to not create a @TableAlias@ with the name @root@,
--    but that seemed a bit complicated for me to do at the time.
--
-- 2. Bypass the Postgres limitation of truncating identifiers to 63 characters long
--    by prepending they identifier's md5 hash when they are longer than 63 characters.
--
--   We do both operations in the same traversal for performance reasons, but a simpler
--   implementation of (1) would be @transformBi prefixHash@ from the uniplate or the
--   generic-plate package.
--
-- See Postgres docs:
-- <https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS>
module Hasura.Backends.Postgres.SQL.RenameIdentifiers
  ( -- * Exported API
    renameIdentifiers,
    renameIdentifiersSelectWith,
  )
where

import Crypto.Hash.MD5 qualified as MD5
import Data.ByteString.Base16 qualified as Base16
import Data.HashSet qualified as Set
import Data.Text qualified as T
import Data.Text.Encoding qualified as T
import Hasura.Backends.Postgres.SQL.DML qualified as S
import Hasura.Backends.Postgres.SQL.Types (Identifier (..))
import Hasura.Prelude

{- Note [Postgres identifier length limitations]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Postgres truncates identifiers to a maximum of 63 characters by default (see
https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS).
-}

------------------------------------------------

-- * API

-- | Prefix table names with undescores and rename long identifiers.
renameIdentifiers :: S.Select -> S.Select
renameIdentifiers :: Select -> Select
renameIdentifiers = Select -> Select
renameTablesAndLongIdentifiers

-- | prefix table names with undescores and rename long identifiers.
renameIdentifiersSelectWith :: S.SelectWithG S.Select -> S.SelectWithG S.Select
renameIdentifiersSelectWith :: SelectWithG Select -> SelectWithG Select
renameIdentifiersSelectWith = SelectWithG Select -> SelectWithG Select
renameTablesAndLongIdentifiersWith

------------------------------------------------

-- * Prefix long identifiers

-- $prefix_md5_implementation
--
-- We are traversing the query transform 'Identifier's in a query that are
-- longer than 63 characters by prefixing them with their md5 hash.
--
-- This works because:
--
-- 1. Database table references and column references also use `Identifier`, but they
--    cannot be more than 63 characters long, so we will never transform those cases.
-- 2. The md5 hash is a 32 characters long deterministic representation of the identifier,
--    so even when truncated by postgres, it will always be enough to identify the identifiers.
-- 3. It is possible in theory for to identifiers to produce the same hash, but extremely
--    unlikely and I don't think we'll ever run into such a case.
--
-- /Note/ that we could in theory replace the identifier with a hash,
-- but we prefix the hash instead for our benefit as developers - if we need
-- to read the query at some point we can look at the rest of the identifier
-- ignoring the hash for a readable representation.

---------------------------------------------------

-- | Prefix md5 hash if identifier length is over 63 characters.
--   We assume (rightly) that identifiers with names longer than 63 characters are not
--   database table columns, and are made by us using aliases, so we should be free to
--   rename them.
prefixHash :: Identifier -> Identifier
prefixHash :: Identifier -> Identifier
prefixHash (Identifier Text
name) =
  Text -> Identifier
Identifier (Text -> Identifier) -> Text -> Identifier
forall a b. (a -> b) -> a -> b
$
    if Text -> Int
T.length Text
name Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
63
      then
        let hash :: Text
hash = ByteString -> Text
T.decodeUtf8 (ByteString -> Text) -> (Text -> ByteString) -> Text -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
Base16.encode (ByteString -> ByteString)
-> (Text -> ByteString) -> Text -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
MD5.hash (ByteString -> ByteString)
-> (Text -> ByteString) -> Text -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
T.encodeUtf8 (Text -> Text) -> Text -> Text
forall a b. (a -> b) -> a -> b
$ Text
name
         in Text
"md5_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
hash Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
name
      else Text
name

------------------------------------------------

-- * Prefix table names with underscore

-- $prefix_md5_implementation

-- | Prefix table names with underscores to avoid issues where column names and tables conflict.
--   This can happen because we give columns and tables the name @root@ for some reason,
--   and that can trip up @row_to_json@.
--   See <https://github.com/PostgREST/postgrest/issues/993#issuecomment-340377813>.
--   An alternative solution would be to not create a @TableAlias@ with the name @root@,
--   but that seemed a bit complicated for me to do at the time.

-- ** API

renameTablesAndLongIdentifiers :: S.Select -> S.Select
renameTablesAndLongIdentifiers :: Select -> Select
renameTablesAndLongIdentifiers = MyState Select -> Select
forall a. MyState a -> a
runMyState (MyState Select -> Select)
-> (Select -> MyState Select) -> Select -> Select
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Select -> MyState Select
uSelect

renameTablesAndLongIdentifiersWith :: S.SelectWithG S.Select -> S.SelectWithG S.Select
renameTablesAndLongIdentifiersWith :: SelectWithG Select -> SelectWithG Select
renameTablesAndLongIdentifiersWith = MyState (SelectWithG Select) -> SelectWithG Select
forall a. MyState a -> a
runMyState (MyState (SelectWithG Select) -> SelectWithG Select)
-> (SelectWithG Select -> MyState (SelectWithG Select))
-> SelectWithG Select
-> SelectWithG Select
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SelectWithG Select -> MyState (SelectWithG Select)
uSelectWith

------------------------------------------------

-- ** Data types

runMyState :: MyState a -> a
runMyState :: MyState a -> a
runMyState = (MyState a -> TableNames -> a) -> TableNames -> MyState a -> a
forall a b c. (a -> b -> c) -> b -> a -> c
flip MyState a -> TableNames -> a
forall s a. State s a -> s -> a
evalState TableNames
noTables

noTables :: TableNames
noTables :: TableNames
noTables = HashSet Identifier -> TableNames
TableNames HashSet Identifier
forall a. Monoid a => a
mempty

-- | The tables in scope
newtype TableNames = TableNames
  { TableNames -> HashSet Identifier
_tables :: Set.HashSet Identifier
  }
  deriving (Int -> TableNames -> ShowS
[TableNames] -> ShowS
TableNames -> String
(Int -> TableNames -> ShowS)
-> (TableNames -> String)
-> ([TableNames] -> ShowS)
-> Show TableNames
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [TableNames] -> ShowS
$cshowList :: [TableNames] -> ShowS
show :: TableNames -> String
$cshow :: TableNames -> String
showsPrec :: Int -> TableNames -> ShowS
$cshowsPrec :: Int -> TableNames -> ShowS
Show, TableNames -> TableNames -> Bool
(TableNames -> TableNames -> Bool)
-> (TableNames -> TableNames -> Bool) -> Eq TableNames
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: TableNames -> TableNames -> Bool
$c/= :: TableNames -> TableNames -> Bool
== :: TableNames -> TableNames -> Bool
$c== :: TableNames -> TableNames -> Bool
Eq)

type MyState = State TableNames

------------------------------------------------

-- ** Utilities

-- | attach a prefix to an identifier
mkPrefixedName :: Identifier -> Identifier
mkPrefixedName :: Identifier -> Identifier
mkPrefixedName Identifier
identifier = Identifier -> Identifier
prefixHash (Identifier -> Identifier) -> Identifier -> Identifier
forall a b. (a -> b) -> a -> b
$ Text -> Identifier
Identifier Text
"_" Identifier -> Identifier -> Identifier
forall a. Semigroup a => a -> a -> a
<> Identifier
identifier

-- | Add the alias to the set and return a prefixed alias.
addAliasAndPrefixHash :: S.TableAlias -> MyState S.TableAlias
addAliasAndPrefixHash :: TableAlias -> MyState TableAlias
addAliasAndPrefixHash (S.TableAlias Identifier
identifier) = do
  HashSet Identifier
tables <- TableNames -> HashSet Identifier
_tables (TableNames -> HashSet Identifier)
-> StateT TableNames Identity TableNames
-> StateT TableNames Identity (HashSet Identifier)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StateT TableNames Identity TableNames
forall s (m :: * -> *). MonadState s m => m s
get
  TableNames -> StateT TableNames Identity ()
forall s (m :: * -> *). MonadState s m => s -> m ()
put (TableNames -> StateT TableNames Identity ())
-> TableNames -> StateT TableNames Identity ()
forall a b. (a -> b) -> a -> b
$ HashSet Identifier -> TableNames
TableNames (HashSet Identifier -> TableNames)
-> HashSet Identifier -> TableNames
forall a b. (a -> b) -> a -> b
$ Identifier -> HashSet Identifier -> HashSet Identifier
forall a. (Eq a, Hashable a) => a -> HashSet a -> HashSet a
Set.insert Identifier
identifier HashSet Identifier
tables
  TableAlias -> MyState TableAlias
forall (f :: * -> *) a. Applicative f => a -> f a
pure (TableAlias -> MyState TableAlias)
-> TableAlias -> MyState TableAlias
forall a b. (a -> b) -> a -> b
$ Identifier -> TableAlias
S.TableAlias (Identifier -> TableAlias) -> Identifier -> TableAlias
forall a b. (a -> b) -> a -> b
$ Identifier -> Identifier
mkPrefixedName Identifier
identifier

-- | Search for the identifier in the table names set and return
--   a prefixed identifier if found, or the original identifier
--   if not found in the set.
getTableNameAndPrefixHash :: Identifier -> MyState Identifier
getTableNameAndPrefixHash :: Identifier -> MyState Identifier
getTableNameAndPrefixHash Identifier
identifier = do
  HashSet Identifier
tables <- TableNames -> HashSet Identifier
_tables (TableNames -> HashSet Identifier)
-> StateT TableNames Identity TableNames
-> StateT TableNames Identity (HashSet Identifier)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StateT TableNames Identity TableNames
forall s (m :: * -> *). MonadState s m => m s
get
  Identifier -> MyState Identifier
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Identifier -> MyState Identifier)
-> Identifier -> MyState Identifier
forall a b. (a -> b) -> a -> b
$
    if Identifier -> HashSet Identifier -> Bool
forall a. (Eq a, Hashable a) => a -> HashSet a -> Bool
Set.member Identifier
identifier HashSet Identifier
tables
      then Identifier -> Identifier
mkPrefixedName Identifier
identifier
      else Identifier
identifier

-- | Run an action that might change the tables names set
--   and discard the changes made to the set.
restoringTables :: MyState a -> MyState a
restoringTables :: MyState a -> MyState a
restoringTables MyState a
action = do
  HashSet Identifier
tables <- TableNames -> HashSet Identifier
_tables (TableNames -> HashSet Identifier)
-> StateT TableNames Identity TableNames
-> StateT TableNames Identity (HashSet Identifier)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StateT TableNames Identity TableNames
forall s (m :: * -> *). MonadState s m => m s
get
  a
res <- MyState a
action
  -- restore the tables to before the action
  (TableNames -> TableNames) -> StateT TableNames Identity ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify' ((TableNames -> TableNames) -> StateT TableNames Identity ())
-> (TableNames -> TableNames) -> StateT TableNames Identity ()
forall a b. (a -> b) -> a -> b
$ \TableNames
s -> TableNames
s {_tables :: HashSet Identifier
_tables = HashSet Identifier
tables}
  a -> MyState a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
res

------------------------------------------------

-- ** Algorithm

-- | We run the algorithm on each CTE separately and discard the table names set,
--   then we run the algorithm on the main select and return that result
--   (with the table names found in scope).
uSelectWith :: S.SelectWithG S.Select -> MyState (S.SelectWithG S.Select)
uSelectWith :: SelectWithG Select -> MyState (SelectWithG Select)
uSelectWith (S.SelectWith [(TableAlias, Select)]
ctes Select
baseSelect) =
  [(TableAlias, Select)] -> Select -> SelectWithG Select
forall statement.
[(TableAlias, statement)] -> Select -> SelectWithG statement
S.SelectWith
    ([(TableAlias, Select)] -> Select -> SelectWithG Select)
-> StateT TableNames Identity [(TableAlias, Select)]
-> StateT TableNames Identity (Select -> SelectWithG Select)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(TableAlias, Select)]
-> ((TableAlias, Select)
    -> StateT TableNames Identity (TableAlias, Select))
-> StateT TableNames Identity [(TableAlias, Select)]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [(TableAlias, Select)]
ctes (\(TableAlias
alias, Select
sel) -> (TableAlias -> TableAlias
prefixHashTableAlias TableAlias
alias,) (Select -> (TableAlias, Select))
-> MyState Select
-> StateT TableNames Identity (TableAlias, Select)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> MyState Select -> MyState Select
forall a. MyState a -> MyState a
restoringTables (Select -> MyState Select
uSelect Select
sel))
    StateT TableNames Identity (Select -> SelectWithG Select)
-> MyState Select -> MyState (SelectWithG Select)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Select -> MyState Select
uSelect Select
baseSelect

-- | We go in order of each component in the select, starting with
--   the from and CTE clauses (as those introduce new table names to scope).
--   We return a transformed 'Select' (with the table names).
uSelect :: S.Select -> MyState S.Select
uSelect :: Select -> MyState Select
uSelect (S.Select [(TableAlias, Select)]
ctes Maybe DistinctExpr
distinctM [Extractor]
extrs Maybe FromExp
fromM Maybe WhereFrag
whereM Maybe GroupByExp
groupByM Maybe HavingExp
havingM Maybe OrderByExp
orderByM Maybe LimitExp
limitM Maybe OffsetExp
offsetM) = do
  -- Potentially introduces a new alias in subsequent CTEs and the main select,
  -- so it should go first.
  [(TableAlias, Select)]
newCTEs <- [(TableAlias, Select)]
-> ((TableAlias, Select)
    -> StateT TableNames Identity (TableAlias, Select))
-> StateT TableNames Identity [(TableAlias, Select)]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
t a -> (a -> f b) -> f (t b)
for [(TableAlias, Select)]
ctes (((TableAlias, Select)
  -> StateT TableNames Identity (TableAlias, Select))
 -> StateT TableNames Identity [(TableAlias, Select)])
-> ((TableAlias, Select)
    -> StateT TableNames Identity (TableAlias, Select))
-> StateT TableNames Identity [(TableAlias, Select)]
forall a b. (a -> b) -> a -> b
$ \(TableAlias
alias, Select
cte) ->
    (,)
      (TableAlias -> Select -> (TableAlias, Select))
-> MyState TableAlias
-> StateT TableNames Identity (Select -> (TableAlias, Select))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TableAlias -> MyState TableAlias
addAliasAndPrefixHash TableAlias
alias
      StateT TableNames Identity (Select -> (TableAlias, Select))
-> MyState Select
-> StateT TableNames Identity (TableAlias, Select)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Select -> MyState Select
uSelect Select
cte

  -- Potentially introduces a new alias so it should go before the rest.
  Maybe FromExp
newFromM <- (FromExp -> StateT TableNames Identity FromExp)
-> Maybe FromExp -> StateT TableNames Identity (Maybe FromExp)
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM FromExp -> StateT TableNames Identity FromExp
uFromExp Maybe FromExp
fromM

  Maybe WhereFrag
newWhereM <- Maybe WhereFrag
-> (WhereFrag -> StateT TableNames Identity WhereFrag)
-> StateT TableNames Identity (Maybe WhereFrag)
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM Maybe WhereFrag
whereM ((WhereFrag -> StateT TableNames Identity WhereFrag)
 -> StateT TableNames Identity (Maybe WhereFrag))
-> (WhereFrag -> StateT TableNames Identity WhereFrag)
-> StateT TableNames Identity (Maybe WhereFrag)
forall a b. (a -> b) -> a -> b
$
    \(S.WhereFrag BoolExp
be) -> BoolExp -> WhereFrag
S.WhereFrag (BoolExp -> WhereFrag)
-> StateT TableNames Identity BoolExp
-> StateT TableNames Identity WhereFrag
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> BoolExp -> StateT TableNames Identity BoolExp
uBoolExp BoolExp
be
  Maybe GroupByExp
newGroupByM <- Maybe GroupByExp
-> (GroupByExp -> StateT TableNames Identity GroupByExp)
-> StateT TableNames Identity (Maybe GroupByExp)
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM Maybe GroupByExp
groupByM ((GroupByExp -> StateT TableNames Identity GroupByExp)
 -> StateT TableNames Identity (Maybe GroupByExp))
-> (GroupByExp -> StateT TableNames Identity GroupByExp)
-> StateT TableNames Identity (Maybe GroupByExp)
forall a b. (a -> b) -> a -> b
$
    \(S.GroupByExp [SQLExp]
l) -> [SQLExp] -> GroupByExp
S.GroupByExp ([SQLExp] -> GroupByExp)
-> StateT TableNames Identity [SQLExp]
-> StateT TableNames Identity GroupByExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (SQLExp -> StateT TableNames Identity SQLExp)
-> [SQLExp] -> StateT TableNames Identity [SQLExp]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM SQLExp -> StateT TableNames Identity SQLExp
uSqlExp [SQLExp]
l
  Maybe HavingExp
newHavingM <- Maybe HavingExp
-> (HavingExp -> StateT TableNames Identity HavingExp)
-> StateT TableNames Identity (Maybe HavingExp)
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM Maybe HavingExp
havingM ((HavingExp -> StateT TableNames Identity HavingExp)
 -> StateT TableNames Identity (Maybe HavingExp))
-> (HavingExp -> StateT TableNames Identity HavingExp)
-> StateT TableNames Identity (Maybe HavingExp)
forall a b. (a -> b) -> a -> b
$
    \(S.HavingExp BoolExp
be) -> BoolExp -> HavingExp
S.HavingExp (BoolExp -> HavingExp)
-> StateT TableNames Identity BoolExp
-> StateT TableNames Identity HavingExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> BoolExp -> StateT TableNames Identity BoolExp
uBoolExp BoolExp
be
  Maybe OrderByExp
newOrderByM <- (OrderByExp -> StateT TableNames Identity OrderByExp)
-> Maybe OrderByExp
-> StateT TableNames Identity (Maybe OrderByExp)
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM OrderByExp -> StateT TableNames Identity OrderByExp
uOrderBy Maybe OrderByExp
orderByM
  Maybe DistinctExpr
newDistinctM <- (DistinctExpr -> StateT TableNames Identity DistinctExpr)
-> Maybe DistinctExpr
-> StateT TableNames Identity (Maybe DistinctExpr)
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM DistinctExpr -> StateT TableNames Identity DistinctExpr
uDistinct Maybe DistinctExpr
distinctM
  [Extractor]
newExtrs <- (Extractor -> StateT TableNames Identity Extractor)
-> [Extractor] -> StateT TableNames Identity [Extractor]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM Extractor -> StateT TableNames Identity Extractor
uExtractor [Extractor]
extrs
  Maybe LimitExp
newLimitM <- (LimitExp -> StateT TableNames Identity LimitExp)
-> Maybe LimitExp -> StateT TableNames Identity (Maybe LimitExp)
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM LimitExp -> StateT TableNames Identity LimitExp
uLimit Maybe LimitExp
limitM
  Maybe OffsetExp
newOffsetM <- (OffsetExp -> StateT TableNames Identity OffsetExp)
-> Maybe OffsetExp -> StateT TableNames Identity (Maybe OffsetExp)
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM OffsetExp -> StateT TableNames Identity OffsetExp
uOffset Maybe OffsetExp
offsetM
  Select -> MyState Select
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Select -> MyState Select) -> Select -> MyState Select
forall a b. (a -> b) -> a -> b
$
    [(TableAlias, Select)]
-> Maybe DistinctExpr
-> [Extractor]
-> Maybe FromExp
-> Maybe WhereFrag
-> Maybe GroupByExp
-> Maybe HavingExp
-> Maybe OrderByExp
-> Maybe LimitExp
-> Maybe OffsetExp
-> Select
S.Select
      [(TableAlias, Select)]
newCTEs
      Maybe DistinctExpr
newDistinctM
      [Extractor]
newExtrs
      Maybe FromExp
newFromM
      Maybe WhereFrag
newWhereM
      Maybe GroupByExp
newGroupByM
      Maybe HavingExp
newHavingM
      Maybe OrderByExp
newOrderByM
      Maybe LimitExp
newLimitM
      Maybe OffsetExp
newOffsetM
  where
    uDistinct :: DistinctExpr -> StateT TableNames Identity DistinctExpr
uDistinct = \case
      DistinctExpr
S.DistinctSimple -> DistinctExpr -> StateT TableNames Identity DistinctExpr
forall (f :: * -> *) a. Applicative f => a -> f a
pure DistinctExpr
S.DistinctSimple
      S.DistinctOn [SQLExp]
exprs -> [SQLExp] -> DistinctExpr
S.DistinctOn ([SQLExp] -> DistinctExpr)
-> StateT TableNames Identity [SQLExp]
-> StateT TableNames Identity DistinctExpr
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (SQLExp -> StateT TableNames Identity SQLExp)
-> [SQLExp] -> StateT TableNames Identity [SQLExp]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM SQLExp -> StateT TableNames Identity SQLExp
uSqlExp [SQLExp]
exprs
    uExtractor :: Extractor -> StateT TableNames Identity Extractor
uExtractor (S.Extractor SQLExp
expr Maybe ColumnAlias
alias) =
      SQLExp -> Maybe ColumnAlias -> Extractor
S.Extractor (SQLExp -> Maybe ColumnAlias -> Extractor)
-> StateT TableNames Identity SQLExp
-> StateT TableNames Identity (Maybe ColumnAlias -> Extractor)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SQLExp -> StateT TableNames Identity SQLExp
uSqlExp SQLExp
expr StateT TableNames Identity (Maybe ColumnAlias -> Extractor)
-> StateT TableNames Identity (Maybe ColumnAlias)
-> StateT TableNames Identity Extractor
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Maybe ColumnAlias -> StateT TableNames Identity (Maybe ColumnAlias)
forall (f :: * -> *) a. Applicative f => a -> f a
pure ((ColumnAlias -> ColumnAlias)
-> Maybe ColumnAlias -> Maybe ColumnAlias
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ColumnAlias -> ColumnAlias
prefixHashColumnAlias Maybe ColumnAlias
alias)
    uLimit :: LimitExp -> StateT TableNames Identity LimitExp
uLimit (S.LimitExp SQLExp
expr) = SQLExp -> LimitExp
S.LimitExp (SQLExp -> LimitExp)
-> StateT TableNames Identity SQLExp
-> StateT TableNames Identity LimitExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SQLExp -> StateT TableNames Identity SQLExp
uSqlExp SQLExp
expr
    uOffset :: OffsetExp -> StateT TableNames Identity OffsetExp
uOffset (S.OffsetExp SQLExp
expr) = SQLExp -> OffsetExp
S.OffsetExp (SQLExp -> OffsetExp)
-> StateT TableNames Identity SQLExp
-> StateT TableNames Identity OffsetExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SQLExp -> StateT TableNames Identity SQLExp
uSqlExp SQLExp
expr

-- | Transform every @from_item@.
--   Potentially introduces a new alias.
uFromExp :: S.FromExp -> MyState S.FromExp
uFromExp :: FromExp -> StateT TableNames Identity FromExp
uFromExp (S.FromExp [FromItem]
fromItems) =
  [FromItem] -> FromExp
S.FromExp ([FromItem] -> FromExp)
-> StateT TableNames Identity [FromItem]
-> StateT TableNames Identity FromExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (FromItem -> StateT TableNames Identity FromItem)
-> [FromItem] -> StateT TableNames Identity [FromItem]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM FromItem -> StateT TableNames Identity FromItem
uFromItem [FromItem]
fromItems

-- | Transform a single @from_item@.
--   Potentially introduces a new alias.
uFromItem :: S.FromItem -> MyState S.FromItem
uFromItem :: FromItem -> StateT TableNames Identity FromItem
uFromItem FromItem
fromItem = case FromItem
fromItem of
  -- _Note_: Potentially introduces a new alias
  -- qualifiedTable represents a database table so we don't need to prefix it with a hash.
  S.FISimple QualifiedTable
qualifiedTable Maybe TableAlias
maybeAlias ->
    QualifiedTable -> Maybe TableAlias -> FromItem
S.FISimple QualifiedTable
qualifiedTable (Maybe TableAlias -> FromItem)
-> StateT TableNames Identity (Maybe TableAlias)
-> StateT TableNames Identity FromItem
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (TableAlias -> MyState TableAlias)
-> Maybe TableAlias
-> StateT TableNames Identity (Maybe TableAlias)
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM TableAlias -> MyState TableAlias
addAliasAndPrefixHash Maybe TableAlias
maybeAlias
  S.FIIdentifier Identifier
identifier ->
    Identifier -> FromItem
S.FIIdentifier (Identifier -> FromItem)
-> MyState Identifier -> StateT TableNames Identity FromItem
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Identifier -> MyState Identifier
getTableNameAndPrefixHash Identifier
identifier
  S.FIFunc FunctionExp
funcExp ->
    FunctionExp -> FromItem
S.FIFunc (FunctionExp -> FromItem)
-> StateT TableNames Identity FunctionExp
-> StateT TableNames Identity FromItem
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FunctionExp -> StateT TableNames Identity FunctionExp
uFunctionExp FunctionExp
funcExp
  -- We transform the arguments and result table alias
  -- Note: Potentially introduces a new alias
  S.FIUnnest [SQLExp]
args TableAlias
tableAlias [ColumnAlias]
columnAliases ->
    [SQLExp] -> TableAlias -> [ColumnAlias] -> FromItem
S.FIUnnest
      ([SQLExp] -> TableAlias -> [ColumnAlias] -> FromItem)
-> StateT TableNames Identity [SQLExp]
-> StateT
     TableNames Identity (TableAlias -> [ColumnAlias] -> FromItem)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (SQLExp -> StateT TableNames Identity SQLExp)
-> [SQLExp] -> StateT TableNames Identity [SQLExp]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM SQLExp -> StateT TableNames Identity SQLExp
uSqlExp [SQLExp]
args
      StateT
  TableNames Identity (TableAlias -> [ColumnAlias] -> FromItem)
-> MyState TableAlias
-> StateT TableNames Identity ([ColumnAlias] -> FromItem)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> TableAlias -> MyState TableAlias
addAliasAndPrefixHash TableAlias
tableAlias
      StateT TableNames Identity ([ColumnAlias] -> FromItem)
-> StateT TableNames Identity [ColumnAlias]
-> StateT TableNames Identity FromItem
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> [ColumnAlias] -> StateT TableNames Identity [ColumnAlias]
forall (f :: * -> *) a. Applicative f => a -> f a
pure ((ColumnAlias -> ColumnAlias) -> [ColumnAlias] -> [ColumnAlias]
forall a b. (a -> b) -> [a] -> [b]
map ColumnAlias -> ColumnAlias
prefixHashColumnAlias [ColumnAlias]
columnAliases)
  -- Note: Potentially introduces a new alias
  S.FISelect Lateral
isLateral Select
select TableAlias
alias -> do
    -- we are kind of ignoring if we have to reset
    -- identifiers to empty based on correlation.
    -- If this select is not part of a lateral join, then it shouldn't
    -- have access to tables exposed previously.
    -- > unless isLateral $ modify' $ \s -> s { _uqIdentifiers = Map.empty}
    Select
newSel <- MyState Select -> MyState Select
forall a. MyState a -> MyState a
restoringTables (MyState Select -> MyState Select)
-> MyState Select -> MyState Select
forall a b. (a -> b) -> a -> b
$ Select -> MyState Select
uSelect Select
select
    TableAlias
newAls <- TableAlias -> MyState TableAlias
addAliasAndPrefixHash TableAlias
alias
    FromItem -> StateT TableNames Identity FromItem
forall (f :: * -> *) a. Applicative f => a -> f a
pure (FromItem -> StateT TableNames Identity FromItem)
-> FromItem -> StateT TableNames Identity FromItem
forall a b. (a -> b) -> a -> b
$ Lateral -> Select -> TableAlias -> FromItem
S.FISelect Lateral
isLateral Select
newSel TableAlias
newAls
  -- _Note_: Potentially introduces a new alias
  S.FISelectWith Lateral
isLateral SelectWithG Select
selectWith TableAlias
alias -> do
    SelectWithG Select
newSelectWith <- SelectWithG Select -> MyState (SelectWithG Select)
uSelectWith SelectWithG Select
selectWith
    TableAlias
newAls <- TableAlias -> MyState TableAlias
addAliasAndPrefixHash TableAlias
alias
    FromItem -> StateT TableNames Identity FromItem
forall (f :: * -> *) a. Applicative f => a -> f a
pure (FromItem -> StateT TableNames Identity FromItem)
-> FromItem -> StateT TableNames Identity FromItem
forall a b. (a -> b) -> a -> b
$ Lateral -> SelectWithG Select -> TableAlias -> FromItem
S.FISelectWith Lateral
isLateral SelectWithG Select
newSelectWith TableAlias
newAls
  S.FIValues (S.ValuesExp [TupleExp]
tups) TableAlias
alias Maybe [ColumnAlias]
mCols -> do
    ValuesExp
newValExp <- ([TupleExp] -> ValuesExp)
-> StateT TableNames Identity [TupleExp]
-> StateT TableNames Identity ValuesExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [TupleExp] -> ValuesExp
S.ValuesExp (StateT TableNames Identity [TupleExp]
 -> StateT TableNames Identity ValuesExp)
-> StateT TableNames Identity [TupleExp]
-> StateT TableNames Identity ValuesExp
forall a b. (a -> b) -> a -> b
$
      [TupleExp]
-> (TupleExp -> StateT TableNames Identity TupleExp)
-> StateT TableNames Identity [TupleExp]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [TupleExp]
tups ((TupleExp -> StateT TableNames Identity TupleExp)
 -> StateT TableNames Identity [TupleExp])
-> (TupleExp -> StateT TableNames Identity TupleExp)
-> StateT TableNames Identity [TupleExp]
forall a b. (a -> b) -> a -> b
$ \(S.TupleExp [SQLExp]
ts) ->
        [SQLExp] -> TupleExp
S.TupleExp ([SQLExp] -> TupleExp)
-> StateT TableNames Identity [SQLExp]
-> StateT TableNames Identity TupleExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (SQLExp -> StateT TableNames Identity SQLExp)
-> [SQLExp] -> StateT TableNames Identity [SQLExp]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM SQLExp -> StateT TableNames Identity SQLExp
uSqlExp [SQLExp]
ts
    FromItem -> StateT TableNames Identity FromItem
forall (f :: * -> *) a. Applicative f => a -> f a
pure (FromItem -> StateT TableNames Identity FromItem)
-> FromItem -> StateT TableNames Identity FromItem
forall a b. (a -> b) -> a -> b
$ ValuesExp -> TableAlias -> Maybe [ColumnAlias] -> FromItem
S.FIValues ValuesExp
newValExp (TableAlias -> TableAlias
prefixHashTableAlias TableAlias
alias) (([ColumnAlias] -> [ColumnAlias])
-> Maybe [ColumnAlias] -> Maybe [ColumnAlias]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((ColumnAlias -> ColumnAlias) -> [ColumnAlias] -> [ColumnAlias]
forall a b. (a -> b) -> [a] -> [b]
map ColumnAlias -> ColumnAlias
prefixHashColumnAlias) Maybe [ColumnAlias]
mCols)
  -- _Note_: Potentially introduces a new alias
  S.FIJoin JoinExpr
joinExp ->
    JoinExpr -> FromItem
S.FIJoin (JoinExpr -> FromItem)
-> StateT TableNames Identity JoinExpr
-> StateT TableNames Identity FromItem
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> JoinExpr -> StateT TableNames Identity JoinExpr
uJoinExp JoinExpr
joinExp

-- | Transform a function call expression.
uFunctionExp :: S.FunctionExp -> MyState S.FunctionExp
uFunctionExp :: FunctionExp -> StateT TableNames Identity FunctionExp
uFunctionExp (S.FunctionExp QualifiedFunction
functionName FunctionArgs
args Maybe FunctionAlias
maybeAlias) =
  QualifiedFunction
-> FunctionArgs -> Maybe FunctionAlias -> FunctionExp
S.FunctionExp QualifiedFunction
functionName
    (FunctionArgs -> Maybe FunctionAlias -> FunctionExp)
-> StateT TableNames Identity FunctionArgs
-> StateT TableNames Identity (Maybe FunctionAlias -> FunctionExp)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FunctionArgs -> StateT TableNames Identity FunctionArgs
uFunctionArgs FunctionArgs
args
    StateT TableNames Identity (Maybe FunctionAlias -> FunctionExp)
-> StateT TableNames Identity (Maybe FunctionAlias)
-> StateT TableNames Identity FunctionExp
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (FunctionAlias -> StateT TableNames Identity FunctionAlias)
-> Maybe FunctionAlias
-> StateT TableNames Identity (Maybe FunctionAlias)
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM FunctionAlias -> StateT TableNames Identity FunctionAlias
uFunctionAlias Maybe FunctionAlias
maybeAlias

-- | Transform function call arguments.
uFunctionArgs :: S.FunctionArgs -> MyState S.FunctionArgs
uFunctionArgs :: FunctionArgs -> StateT TableNames Identity FunctionArgs
uFunctionArgs (S.FunctionArgs [SQLExp]
positional HashMap Text SQLExp
named) =
  [SQLExp] -> HashMap Text SQLExp -> FunctionArgs
S.FunctionArgs ([SQLExp] -> HashMap Text SQLExp -> FunctionArgs)
-> StateT TableNames Identity [SQLExp]
-> StateT TableNames Identity (HashMap Text SQLExp -> FunctionArgs)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (SQLExp -> StateT TableNames Identity SQLExp)
-> [SQLExp] -> StateT TableNames Identity [SQLExp]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM SQLExp -> StateT TableNames Identity SQLExp
uSqlExp [SQLExp]
positional StateT TableNames Identity (HashMap Text SQLExp -> FunctionArgs)
-> StateT TableNames Identity (HashMap Text SQLExp)
-> StateT TableNames Identity FunctionArgs
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (SQLExp -> StateT TableNames Identity SQLExp)
-> HashMap Text SQLExp
-> StateT TableNames Identity (HashMap Text SQLExp)
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM SQLExp -> StateT TableNames Identity SQLExp
uSqlExp HashMap Text SQLExp
named

-- | Transform a function call alias.
uFunctionAlias :: S.FunctionAlias -> MyState S.FunctionAlias
uFunctionAlias :: FunctionAlias -> StateT TableNames Identity FunctionAlias
uFunctionAlias (S.FunctionAlias TableAlias
alias Maybe [FunctionDefinitionListItem]
definitionList) =
  TableAlias -> Maybe [FunctionDefinitionListItem] -> FunctionAlias
S.FunctionAlias
    (TableAlias -> Maybe [FunctionDefinitionListItem] -> FunctionAlias)
-> MyState TableAlias
-> StateT
     TableNames
     Identity
     (Maybe [FunctionDefinitionListItem] -> FunctionAlias)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TableAlias -> MyState TableAlias
addAliasAndPrefixHash TableAlias
alias
    StateT
  TableNames
  Identity
  (Maybe [FunctionDefinitionListItem] -> FunctionAlias)
-> StateT TableNames Identity (Maybe [FunctionDefinitionListItem])
-> StateT TableNames Identity FunctionAlias
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Maybe [FunctionDefinitionListItem]
-> StateT TableNames Identity (Maybe [FunctionDefinitionListItem])
forall (f :: * -> *) a. Applicative f => a -> f a
pure (([FunctionDefinitionListItem] -> [FunctionDefinitionListItem])
-> Maybe [FunctionDefinitionListItem]
-> Maybe [FunctionDefinitionListItem]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((FunctionDefinitionListItem -> FunctionDefinitionListItem)
-> [FunctionDefinitionListItem] -> [FunctionDefinitionListItem]
forall a b. (a -> b) -> [a] -> [b]
map FunctionDefinitionListItem -> FunctionDefinitionListItem
uDefinitionList) Maybe [FunctionDefinitionListItem]
definitionList)
  where
    uDefinitionList :: FunctionDefinitionListItem -> FunctionDefinitionListItem
uDefinitionList (S.FunctionDefinitionListItem ColumnAlias
columnAlias PGScalarType
typ) =
      ColumnAlias -> PGScalarType -> FunctionDefinitionListItem
S.FunctionDefinitionListItem (ColumnAlias -> ColumnAlias
prefixHashColumnAlias ColumnAlias
columnAlias) PGScalarType
typ

-- | Transform join expressions.
--   Potentially introduces a new alias.
uJoinExp :: S.JoinExpr -> MyState S.JoinExpr
uJoinExp :: JoinExpr -> StateT TableNames Identity JoinExpr
uJoinExp (S.JoinExpr FromItem
left JoinType
joinType FromItem
right JoinCond
joinCond) = do
  FromItem
leftN <- FromItem -> StateT TableNames Identity FromItem
uFromItem FromItem
left
  FromItem
rightN <- FromItem -> StateT TableNames Identity FromItem
uFromItem FromItem
right
  JoinCond
joinCondN <- JoinCond -> MyState JoinCond
uJoinCond JoinCond
joinCond
  JoinExpr -> StateT TableNames Identity JoinExpr
forall (f :: * -> *) a. Applicative f => a -> f a
pure (JoinExpr -> StateT TableNames Identity JoinExpr)
-> JoinExpr -> StateT TableNames Identity JoinExpr
forall a b. (a -> b) -> a -> b
$ FromItem -> JoinType -> FromItem -> JoinCond -> JoinExpr
S.JoinExpr FromItem
leftN JoinType
joinType FromItem
rightN JoinCond
joinCondN

-- | Transform Join condition. `ON` join condition might contain references
--   to table names and aliases.
uJoinCond :: S.JoinCond -> MyState S.JoinCond
uJoinCond :: JoinCond -> MyState JoinCond
uJoinCond JoinCond
joinCond = case JoinCond
joinCond of
  S.JoinOn BoolExp
be -> BoolExp -> JoinCond
S.JoinOn (BoolExp -> JoinCond)
-> StateT TableNames Identity BoolExp -> MyState JoinCond
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> BoolExp -> StateT TableNames Identity BoolExp
uBoolExp BoolExp
be
  S.JoinUsing [Identifier]
cols -> JoinCond -> MyState JoinCond
forall (f :: * -> *) a. Applicative f => a -> f a
pure (JoinCond -> MyState JoinCond) -> JoinCond -> MyState JoinCond
forall a b. (a -> b) -> a -> b
$ [Identifier] -> JoinCond
S.JoinUsing ([Identifier] -> JoinCond) -> [Identifier] -> JoinCond
forall a b. (a -> b) -> a -> b
$ (Identifier -> Identifier) -> [Identifier] -> [Identifier]
forall a b. (a -> b) -> [a] -> [b]
map Identifier -> Identifier
prefixHash [Identifier]
cols

-- | Transform boolean expression.
--
--   The boolean expression structure does not contain a table name currently,
--   So we look for 'SQLExp's and transform those, as those may contain table
--   names and aliases.
--
--   We discard table names that might be introduced here because we don't
--   use them outside of the boolean expression.
uBoolExp :: S.BoolExp -> MyState S.BoolExp
uBoolExp :: BoolExp -> StateT TableNames Identity BoolExp
uBoolExp =
  StateT TableNames Identity BoolExp
-> StateT TableNames Identity BoolExp
forall a. MyState a -> MyState a
restoringTables (StateT TableNames Identity BoolExp
 -> StateT TableNames Identity BoolExp)
-> (BoolExp -> StateT TableNames Identity BoolExp)
-> BoolExp
-> StateT TableNames Identity BoolExp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. \case
    S.BELit Bool
b -> BoolExp -> StateT TableNames Identity BoolExp
forall (f :: * -> *) a. Applicative f => a -> f a
pure (BoolExp -> StateT TableNames Identity BoolExp)
-> BoolExp -> StateT TableNames Identity BoolExp
forall a b. (a -> b) -> a -> b
$ Bool -> BoolExp
S.BELit Bool
b
    S.BEBin BinOp
op BoolExp
left BoolExp
right ->
      BinOp -> BoolExp -> BoolExp -> BoolExp
S.BEBin BinOp
op (BoolExp -> BoolExp -> BoolExp)
-> StateT TableNames Identity BoolExp
-> StateT TableNames Identity (BoolExp -> BoolExp)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> BoolExp -> StateT TableNames Identity BoolExp
uBoolExp BoolExp
left StateT TableNames Identity (BoolExp -> BoolExp)
-> StateT TableNames Identity BoolExp
-> StateT TableNames Identity BoolExp
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> BoolExp -> StateT TableNames Identity BoolExp
uBoolExp BoolExp
right
    S.BENot BoolExp
b -> BoolExp -> BoolExp
S.BENot (BoolExp -> BoolExp)
-> StateT TableNames Identity BoolExp
-> StateT TableNames Identity BoolExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> BoolExp -> StateT TableNames Identity BoolExp
uBoolExp BoolExp
b
    S.BECompare CompareOp
op SQLExp
left SQLExp
right ->
      CompareOp -> SQLExp -> SQLExp -> BoolExp
S.BECompare CompareOp
op (SQLExp -> SQLExp -> BoolExp)
-> StateT TableNames Identity SQLExp
-> StateT TableNames Identity (SQLExp -> BoolExp)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SQLExp -> StateT TableNames Identity SQLExp
uSqlExp SQLExp
left StateT TableNames Identity (SQLExp -> BoolExp)
-> StateT TableNames Identity SQLExp
-> StateT TableNames Identity BoolExp
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> SQLExp -> StateT TableNames Identity SQLExp
uSqlExp SQLExp
right
    S.BECompareAny CompareOp
op SQLExp
left SQLExp
right ->
      CompareOp -> SQLExp -> SQLExp -> BoolExp
S.BECompareAny CompareOp
op (SQLExp -> SQLExp -> BoolExp)
-> StateT TableNames Identity SQLExp
-> StateT TableNames Identity (SQLExp -> BoolExp)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SQLExp -> StateT TableNames Identity SQLExp
uSqlExp SQLExp
left StateT TableNames Identity (SQLExp -> BoolExp)
-> StateT TableNames Identity SQLExp
-> StateT TableNames Identity BoolExp
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> SQLExp -> StateT TableNames Identity SQLExp
uSqlExp SQLExp
right
    S.BENull SQLExp
e -> SQLExp -> BoolExp
S.BENull (SQLExp -> BoolExp)
-> StateT TableNames Identity SQLExp
-> StateT TableNames Identity BoolExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SQLExp -> StateT TableNames Identity SQLExp
uSqlExp SQLExp
e
    S.BENotNull SQLExp
e -> SQLExp -> BoolExp
S.BENotNull (SQLExp -> BoolExp)
-> StateT TableNames Identity SQLExp
-> StateT TableNames Identity BoolExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SQLExp -> StateT TableNames Identity SQLExp
uSqlExp SQLExp
e
    S.BEExists Select
sel -> Select -> BoolExp
S.BEExists (Select -> BoolExp)
-> MyState Select -> StateT TableNames Identity BoolExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Select -> MyState Select
uSelect Select
sel
    S.BEIN SQLExp
left [SQLExp]
exps -> SQLExp -> [SQLExp] -> BoolExp
S.BEIN (SQLExp -> [SQLExp] -> BoolExp)
-> StateT TableNames Identity SQLExp
-> StateT TableNames Identity ([SQLExp] -> BoolExp)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SQLExp -> StateT TableNames Identity SQLExp
uSqlExp SQLExp
left StateT TableNames Identity ([SQLExp] -> BoolExp)
-> StateT TableNames Identity [SQLExp]
-> StateT TableNames Identity BoolExp
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (SQLExp -> StateT TableNames Identity SQLExp)
-> [SQLExp] -> StateT TableNames Identity [SQLExp]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM SQLExp -> StateT TableNames Identity SQLExp
uSqlExp [SQLExp]
exps
    S.BEExp SQLExp
e -> SQLExp -> BoolExp
S.BEExp (SQLExp -> BoolExp)
-> StateT TableNames Identity SQLExp
-> StateT TableNames Identity BoolExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SQLExp -> StateT TableNames Identity SQLExp
uSqlExp SQLExp
e

-- | Transform a SQL expression.
--   We look for table names and aliases and rename them if needed.
--   SQL expressions do not introduce new table aliases, so we discard
--   the new aliases that might be generated here.
uSqlExp :: S.SQLExp -> MyState S.SQLExp
uSqlExp :: SQLExp -> StateT TableNames Identity SQLExp
uSqlExp =
  StateT TableNames Identity SQLExp
-> StateT TableNames Identity SQLExp
forall a. MyState a -> MyState a
restoringTables (StateT TableNames Identity SQLExp
 -> StateT TableNames Identity SQLExp)
-> (SQLExp -> StateT TableNames Identity SQLExp)
-> SQLExp
-> StateT TableNames Identity SQLExp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. \case
    S.SEPrep Int
i -> SQLExp -> StateT TableNames Identity SQLExp
forall (f :: * -> *) a. Applicative f => a -> f a
pure (SQLExp -> StateT TableNames Identity SQLExp)
-> SQLExp -> StateT TableNames Identity SQLExp
forall a b. (a -> b) -> a -> b
$ Int -> SQLExp
S.SEPrep Int
i
    SQLExp
S.SENull -> SQLExp -> StateT TableNames Identity SQLExp
forall (f :: * -> *) a. Applicative f => a -> f a
pure SQLExp
S.SENull
    S.SELit Text
t -> SQLExp -> StateT TableNames Identity SQLExp
forall (f :: * -> *) a. Applicative f => a -> f a
pure (SQLExp -> StateT TableNames Identity SQLExp)
-> SQLExp -> StateT TableNames Identity SQLExp
forall a b. (a -> b) -> a -> b
$ Text -> SQLExp
S.SELit Text
t
    S.SEUnsafe Text
t -> SQLExp -> StateT TableNames Identity SQLExp
forall (f :: * -> *) a. Applicative f => a -> f a
pure (SQLExp -> StateT TableNames Identity SQLExp)
-> SQLExp -> StateT TableNames Identity SQLExp
forall a b. (a -> b) -> a -> b
$ Text -> SQLExp
S.SEUnsafe Text
t
    S.SESelect Select
s -> Select -> SQLExp
S.SESelect (Select -> SQLExp)
-> MyState Select -> StateT TableNames Identity SQLExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Select -> MyState Select
uSelect Select
s
    S.SEStar Maybe Qual
qual -> Maybe Qual -> SQLExp
S.SEStar (Maybe Qual -> SQLExp)
-> StateT TableNames Identity (Maybe Qual)
-> StateT TableNames Identity SQLExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Qual -> StateT TableNames Identity Qual)
-> Maybe Qual -> StateT TableNames Identity (Maybe Qual)
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse Qual -> StateT TableNames Identity Qual
uQual Maybe Qual
qual
    S.SEIdentifier Identifier
identifier -> SQLExp -> StateT TableNames Identity SQLExp
forall (f :: * -> *) a. Applicative f => a -> f a
pure (SQLExp -> StateT TableNames Identity SQLExp)
-> SQLExp -> StateT TableNames Identity SQLExp
forall a b. (a -> b) -> a -> b
$ Identifier -> SQLExp
S.SEIdentifier (Identifier -> SQLExp) -> Identifier -> SQLExp
forall a b. (a -> b) -> a -> b
$ Identifier -> Identifier
prefixHash Identifier
identifier
    -- this is for row expressions
    S.SERowIdentifier Identifier
identifier -> Identifier -> SQLExp
S.SERowIdentifier (Identifier -> SQLExp)
-> MyState Identifier -> StateT TableNames Identity SQLExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Identifier -> MyState Identifier
getTableNameAndPrefixHash Identifier
identifier
    -- we rename the table alias if needed
    S.SEQIdentifier (S.QIdentifier Qual
qualifier Identifier
identifier) -> do
      Qual
newQualifier <- Qual -> StateT TableNames Identity Qual
uQual Qual
qualifier
      SQLExp -> StateT TableNames Identity SQLExp
forall (f :: * -> *) a. Applicative f => a -> f a
pure (SQLExp -> StateT TableNames Identity SQLExp)
-> SQLExp -> StateT TableNames Identity SQLExp
forall a b. (a -> b) -> a -> b
$ QIdentifier -> SQLExp
S.SEQIdentifier (QIdentifier -> SQLExp) -> QIdentifier -> SQLExp
forall a b. (a -> b) -> a -> b
$ Qual -> Identifier -> QIdentifier
S.QIdentifier Qual
newQualifier (Identifier -> QIdentifier) -> Identifier -> QIdentifier
forall a b. (a -> b) -> a -> b
$ Identifier -> Identifier
prefixHash Identifier
identifier
    S.SEFnApp Text
fn [SQLExp]
args Maybe OrderByExp
orderBy ->
      Text -> [SQLExp] -> Maybe OrderByExp -> SQLExp
S.SEFnApp Text
fn
        ([SQLExp] -> Maybe OrderByExp -> SQLExp)
-> StateT TableNames Identity [SQLExp]
-> StateT TableNames Identity (Maybe OrderByExp -> SQLExp)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (SQLExp -> StateT TableNames Identity SQLExp)
-> [SQLExp] -> StateT TableNames Identity [SQLExp]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM SQLExp -> StateT TableNames Identity SQLExp
uSqlExp [SQLExp]
args
        StateT TableNames Identity (Maybe OrderByExp -> SQLExp)
-> StateT TableNames Identity (Maybe OrderByExp)
-> StateT TableNames Identity SQLExp
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (OrderByExp -> StateT TableNames Identity OrderByExp)
-> Maybe OrderByExp
-> StateT TableNames Identity (Maybe OrderByExp)
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM OrderByExp -> StateT TableNames Identity OrderByExp
uOrderBy Maybe OrderByExp
orderBy
    S.SEOpApp SQLOp
op [SQLExp]
args ->
      SQLOp -> [SQLExp] -> SQLExp
S.SEOpApp SQLOp
op ([SQLExp] -> SQLExp)
-> StateT TableNames Identity [SQLExp]
-> StateT TableNames Identity SQLExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (SQLExp -> StateT TableNames Identity SQLExp)
-> [SQLExp] -> StateT TableNames Identity [SQLExp]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM SQLExp -> StateT TableNames Identity SQLExp
uSqlExp [SQLExp]
args
    S.SETyAnn SQLExp
e TypeAnn
ty ->
      SQLExp -> TypeAnn -> SQLExp
S.SETyAnn
        (SQLExp -> TypeAnn -> SQLExp)
-> StateT TableNames Identity SQLExp
-> StateT TableNames Identity (TypeAnn -> SQLExp)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SQLExp -> StateT TableNames Identity SQLExp
uSqlExp SQLExp
e
        StateT TableNames Identity (TypeAnn -> SQLExp)
-> StateT TableNames Identity TypeAnn
-> StateT TableNames Identity SQLExp
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> TypeAnn -> StateT TableNames Identity TypeAnn
forall (f :: * -> *) a. Applicative f => a -> f a
pure TypeAnn
ty
    S.SECond BoolExp
be SQLExp
onTrue SQLExp
onFalse ->
      BoolExp -> SQLExp -> SQLExp -> SQLExp
S.SECond
        (BoolExp -> SQLExp -> SQLExp -> SQLExp)
-> StateT TableNames Identity BoolExp
-> StateT TableNames Identity (SQLExp -> SQLExp -> SQLExp)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> BoolExp -> StateT TableNames Identity BoolExp
uBoolExp BoolExp
be
        StateT TableNames Identity (SQLExp -> SQLExp -> SQLExp)
-> StateT TableNames Identity SQLExp
-> StateT TableNames Identity (SQLExp -> SQLExp)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> SQLExp -> StateT TableNames Identity SQLExp
uSqlExp SQLExp
onTrue
        StateT TableNames Identity (SQLExp -> SQLExp)
-> StateT TableNames Identity SQLExp
-> StateT TableNames Identity SQLExp
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> SQLExp -> StateT TableNames Identity SQLExp
uSqlExp SQLExp
onFalse
    S.SEBool BoolExp
be ->
      BoolExp -> SQLExp
S.SEBool (BoolExp -> SQLExp)
-> StateT TableNames Identity BoolExp
-> StateT TableNames Identity SQLExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> BoolExp -> StateT TableNames Identity BoolExp
uBoolExp BoolExp
be
    S.SEExcluded Identifier
t ->
      SQLExp -> StateT TableNames Identity SQLExp
forall (f :: * -> *) a. Applicative f => a -> f a
pure (SQLExp -> StateT TableNames Identity SQLExp)
-> SQLExp -> StateT TableNames Identity SQLExp
forall a b. (a -> b) -> a -> b
$ Identifier -> SQLExp
S.SEExcluded Identifier
t
    S.SEArray [SQLExp]
l ->
      [SQLExp] -> SQLExp
S.SEArray ([SQLExp] -> SQLExp)
-> StateT TableNames Identity [SQLExp]
-> StateT TableNames Identity SQLExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (SQLExp -> StateT TableNames Identity SQLExp)
-> [SQLExp] -> StateT TableNames Identity [SQLExp]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM SQLExp -> StateT TableNames Identity SQLExp
uSqlExp [SQLExp]
l
    S.SEArrayIndex SQLExp
arrayExp SQLExp
indexExp ->
      SQLExp -> SQLExp -> SQLExp
S.SEArrayIndex (SQLExp -> SQLExp -> SQLExp)
-> StateT TableNames Identity SQLExp
-> StateT TableNames Identity (SQLExp -> SQLExp)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SQLExp -> StateT TableNames Identity SQLExp
uSqlExp SQLExp
arrayExp StateT TableNames Identity (SQLExp -> SQLExp)
-> StateT TableNames Identity SQLExp
-> StateT TableNames Identity SQLExp
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> SQLExp -> StateT TableNames Identity SQLExp
uSqlExp SQLExp
indexExp
    S.SETuple (S.TupleExp [SQLExp]
l) ->
      TupleExp -> SQLExp
S.SETuple (TupleExp -> SQLExp)
-> ([SQLExp] -> TupleExp) -> [SQLExp] -> SQLExp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [SQLExp] -> TupleExp
S.TupleExp ([SQLExp] -> SQLExp)
-> StateT TableNames Identity [SQLExp]
-> StateT TableNames Identity SQLExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (SQLExp -> StateT TableNames Identity SQLExp)
-> [SQLExp] -> StateT TableNames Identity [SQLExp]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM SQLExp -> StateT TableNames Identity SQLExp
uSqlExp [SQLExp]
l
    S.SECount CountType
cty -> SQLExp -> StateT TableNames Identity SQLExp
forall (f :: * -> *) a. Applicative f => a -> f a
pure (SQLExp -> StateT TableNames Identity SQLExp)
-> SQLExp -> StateT TableNames Identity SQLExp
forall a b. (a -> b) -> a -> b
$ CountType -> SQLExp
S.SECount CountType
cty
    S.SENamedArg Identifier
arg SQLExp
val -> Identifier -> SQLExp -> SQLExp
S.SENamedArg Identifier
arg (SQLExp -> SQLExp)
-> StateT TableNames Identity SQLExp
-> StateT TableNames Identity SQLExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SQLExp -> StateT TableNames Identity SQLExp
uSqlExp SQLExp
val
    S.SEFunction FunctionExp
funcExp -> FunctionExp -> SQLExp
S.SEFunction (FunctionExp -> SQLExp)
-> StateT TableNames Identity FunctionExp
-> StateT TableNames Identity SQLExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FunctionExp -> StateT TableNames Identity FunctionExp
uFunctionExp FunctionExp
funcExp
  where
    -- rename the table alias if needed
    uQual :: Qual -> StateT TableNames Identity Qual
uQual = \case
      S.QualifiedIdentifier Identifier
identifier Maybe TypeAnn
typeAnnotation ->
        Identifier -> Maybe TypeAnn -> Qual
S.QualifiedIdentifier (Identifier -> Maybe TypeAnn -> Qual)
-> MyState Identifier
-> StateT TableNames Identity (Maybe TypeAnn -> Qual)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Identifier -> MyState Identifier
getTableNameAndPrefixHash Identifier
identifier StateT TableNames Identity (Maybe TypeAnn -> Qual)
-> StateT TableNames Identity (Maybe TypeAnn)
-> StateT TableNames Identity Qual
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Maybe TypeAnn -> StateT TableNames Identity (Maybe TypeAnn)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe TypeAnn
typeAnnotation
      -- refers to a database table
      S.QualTable QualifiedTable
t -> Qual -> StateT TableNames Identity Qual
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Qual -> StateT TableNames Identity Qual)
-> Qual -> StateT TableNames Identity Qual
forall a b. (a -> b) -> a -> b
$ QualifiedTable -> Qual
S.QualTable QualifiedTable
t
      S.QualVar Text
t -> Qual -> StateT TableNames Identity Qual
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Qual -> StateT TableNames Identity Qual)
-> Qual -> StateT TableNames Identity Qual
forall a b. (a -> b) -> a -> b
$ Text -> Qual
S.QualVar Text
t

-- | Transform order by clauses.
--   Since order by does not introduce new aliases we can discard the new names
--   that might be added, this is already done by `uSqlExp` though.
uOrderBy :: S.OrderByExp -> MyState S.OrderByExp
uOrderBy :: OrderByExp -> StateT TableNames Identity OrderByExp
uOrderBy (S.OrderByExp NonEmpty OrderByItem
ordByItems) =
  NonEmpty OrderByItem -> OrderByExp
S.OrderByExp (NonEmpty OrderByItem -> OrderByExp)
-> StateT TableNames Identity (NonEmpty OrderByItem)
-> StateT TableNames Identity OrderByExp
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (OrderByItem -> StateT TableNames Identity OrderByItem)
-> NonEmpty OrderByItem
-> StateT TableNames Identity (NonEmpty OrderByItem)
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM OrderByItem -> StateT TableNames Identity OrderByItem
uOrderByItem NonEmpty OrderByItem
ordByItems
  where
    uOrderByItem :: OrderByItem -> StateT TableNames Identity OrderByItem
uOrderByItem (S.OrderByItem SQLExp
expr Maybe OrderType
ordering Maybe NullsOrder
nullsOrder) = do
      SQLExp
exprN <- SQLExp -> StateT TableNames Identity SQLExp
uSqlExp SQLExp
expr
      OrderByItem -> StateT TableNames Identity OrderByItem
forall (f :: * -> *) a. Applicative f => a -> f a
pure (OrderByItem -> StateT TableNames Identity OrderByItem)
-> OrderByItem -> StateT TableNames Identity OrderByItem
forall a b. (a -> b) -> a -> b
$ SQLExp -> Maybe OrderType -> Maybe NullsOrder -> OrderByItem
S.OrderByItem SQLExp
exprN Maybe OrderType
ordering Maybe NullsOrder
nullsOrder

-- | Prefix a table alias with a hash if needed.
prefixHashTableAlias :: S.TableAlias -> S.TableAlias
prefixHashTableAlias :: TableAlias -> TableAlias
prefixHashTableAlias (S.TableAlias Identifier
identifier) = Identifier -> TableAlias
S.TableAlias (Identifier -> Identifier
prefixHash Identifier
identifier)

-- | Prefix a column alias with a hash if needed.
prefixHashColumnAlias :: S.ColumnAlias -> S.ColumnAlias
prefixHashColumnAlias :: ColumnAlias -> ColumnAlias
prefixHashColumnAlias (S.ColumnAlias Identifier
identifier) = Identifier -> ColumnAlias
S.ColumnAlias (Identifier -> Identifier
prefixHash Identifier
identifier)