diff --git a/src/Database/Esqueleto.hs b/src/Database/Esqueleto.hs index cd92302..2b7a50d 100644 --- a/src/Database/Esqueleto.hs +++ b/src/Database/Esqueleto.hs @@ -1,4 +1,7 @@ -{-# LANGUAGE FlexibleContexts, FlexibleInstances, GADTs, RankNTypes #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE RankNTypes #-} -- | The @esqueleto@ EDSL (embedded domain specific language). -- This module replaces @Database.Persist@, so instead of -- importing that module you should just import this one: @@ -125,8 +128,8 @@ import Control.Monad.Trans.Reader (ReaderT) import Data.Int (Int64) import qualified Data.Map.Strict as Map import Database.Esqueleto.Internal.Language -import Database.Esqueleto.Internal.Sql import Database.Esqueleto.Internal.PersistentImport +import Database.Esqueleto.Internal.Sql import qualified Database.Persist diff --git a/src/Database/Esqueleto/Experimental.hs b/src/Database/Esqueleto/Experimental.hs index 2564837..3b83f40 100644 --- a/src/Database/Esqueleto/Experimental.hs +++ b/src/Database/Esqueleto/Experimental.hs @@ -216,6 +216,7 @@ import Database.Esqueleto.Experimental.From.SqlSetOperation import Database.Esqueleto.Experimental.ToAlias import Database.Esqueleto.Experimental.ToAliasReference import Database.Esqueleto.Experimental.ToMaybe + -- $setup -- -- If you're already using "Database.Esqueleto", then you can get diff --git a/src/Database/Esqueleto/Experimental/From.hs b/src/Database/Esqueleto/Experimental/From.hs index 2c0cef8..d8e5c8c 100644 --- a/src/Database/Esqueleto/Experimental/From.hs +++ b/src/Database/Esqueleto/Experimental/From.hs @@ -1,25 +1,28 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE CPP #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} -{-# LANGUAGE GADTs #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE PatternSynonyms #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE TypeOperators #-} -{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE UndecidableInstances #-} module Database.Esqueleto.Experimental.From where -import qualified Control.Monad.Trans.Writer as W -import Data.Proxy -import Database.Esqueleto.Experimental.ToAlias -import Database.Esqueleto.Experimental.ToAliasReference -import Database.Esqueleto.Internal.Internal hiding (From(..), from, on) -import Database.Esqueleto.Internal.PersistentImport +import qualified Control.Monad.Trans.Writer as W +import Data.Proxy +import qualified Data.Text.Lazy.Builder as TLB +import Database.Esqueleto.Experimental.ToAlias +import Database.Esqueleto.Experimental.ToAliasReference +import Database.Esqueleto.Internal.Internal hiding + (From (..), + from, on) +import Database.Esqueleto.Internal.PersistentImport -- | 'FROM' clause, used to bring entities into scope. -- @@ -33,12 +36,13 @@ import Database.Esqueleto.Internal.PersistentImport from :: From a => a -> SqlQuery (FromT a) from parts = do (a, clause) <- runFrom parts - Q $ W.tell mempty{sdFromClause=[clause]} + Q $ W.tell mempty{sdFromClause=[FromRaw clause]} pure a +type RawFn = NeedParens -> IdentInfo -> (TLB.Builder, [PersistValue]) class From a where type FromT a - runFrom :: a -> SqlQuery (FromT a, FromClause) + runFrom :: a -> SqlQuery (FromT a, RawFn) -- | Data type for bringing a Table into scope in a JOIN tree -- @@ -53,11 +57,20 @@ instance PersistEntity a => From (Table a) where let ed = entityDef $ getVal e ident <- newIdentFor (entityDB ed) let entity = unsafeSqlEntity ident - pure $ (entity, FromStart ident ed) + pure $ ( entity, \p -> base p ident ed ) where getVal :: Table ent -> Proxy ent getVal = const Proxy + base _ ident@(I identText) def info = + let db@(DBName dbText) = entityDB def + in ( fromDBName info db <> + if dbText == identText + then mempty + else " AS " <> useIdent info ident + , mempty + ) + {-# DEPRECATED SubQuery "/Since: 3.4.0.0/ - It is no longer necessary to tag 'SqlQuery' values with @SubQuery@" #-} newtype SubQuery a = SubQuery a @@ -92,7 +105,7 @@ fromSubQuery , ToAlias a , ToAliasReference a ) - => SubQueryType -> SqlQuery a -> SqlQuery (a, FromClause) + => SubQueryType -> SqlQuery a -> SqlQuery (a, RawFn) fromSubQuery subqueryType subquery = do -- We want to update the IdentState without writing the query to side data (ret, sideData) <- Q $ W.censor (\_ -> mempty) $ W.listen $ unQ subquery @@ -105,4 +118,16 @@ fromSubQuery subqueryType subquery = do -- create aliased references from the outer query results (e.g value from subquery will be `subquery`.`value`), -- this is probably overkill as the aliases should already be unique but seems to be good practice. ref <- toAliasReference subqueryAlias aliasedValue - pure (ref , FromQuery subqueryAlias (\info -> toRawSql SELECT info aliasedQuery) subqueryType) + + pure (ref, \_ info -> + let (queryText,queryVals) = toRawSql SELECT info aliasedQuery + lateralKeyword = + case subqueryType of + NormalSubQuery -> "" + LateralSubQuery -> "LATERAL " + in + ( lateralKeyword <> (parens queryText) <> " AS " <> useIdent info subqueryAlias + , queryVals + ) + ) + diff --git a/src/Database/Esqueleto/Experimental/From/CommonTableExpression.hs b/src/Database/Esqueleto/Experimental/From/CommonTableExpression.hs index 50e67c2..8f5ff20 100644 --- a/src/Database/Esqueleto/Experimental/From/CommonTableExpression.hs +++ b/src/Database/Esqueleto/Experimental/From/CommonTableExpression.hs @@ -1,24 +1,26 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeFamilies #-} module Database.Esqueleto.Experimental.From.CommonTableExpression where -import qualified Control.Monad.Trans.Writer as W -import qualified Data.Text.Lazy.Builder as TLB -import Database.Esqueleto.Experimental.From -import Database.Esqueleto.Experimental.From.SqlSetOperation -import Database.Esqueleto.Experimental.ToAlias -import Database.Esqueleto.Experimental.ToAliasReference -import Database.Esqueleto.Internal.Internal hiding (From(..), from, on) -import Database.Esqueleto.Internal.PersistentImport (DBName(..)) +import qualified Control.Monad.Trans.Writer as W +import qualified Data.Text.Lazy.Builder as TLB +import Database.Esqueleto.Experimental.From +import Database.Esqueleto.Experimental.From.SqlSetOperation +import Database.Esqueleto.Experimental.ToAlias +import Database.Esqueleto.Experimental.ToAliasReference +import Database.Esqueleto.Internal.Internal hiding + (From (..), + from, on) +import Database.Esqueleto.Internal.PersistentImport (DBName (..)) data CommonTableExpression ref = CommonTableExpression Ident ref instance From (CommonTableExpression ref) where type FromT (CommonTableExpression ref) = ref runFrom (CommonTableExpression ident ref) = - pure (ref, FromIdent ident) + pure (ref, (\_ info -> (useIdent info ident, mempty))) -- | @WITH@ clause used to introduce a [Common Table Expression (CTE)](https://en.wikipedia.org/wiki/Hierarchical_and_recursive_queries_in_SQL#Common_table_expression). -- CTEs are supported in most modern SQL engines and can be useful diff --git a/src/Database/Esqueleto/Experimental/From/Join.hs b/src/Database/Esqueleto/Experimental/From/Join.hs index a5f520e..67b8aa3 100644 --- a/src/Database/Esqueleto/Experimental/From/Join.hs +++ b/src/Database/Esqueleto/Experimental/From/Join.hs @@ -1,6 +1,7 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} @@ -8,6 +9,7 @@ module Database.Esqueleto.Experimental.From.Join where +import Data.Bifunctor (first) import Data.Kind (Constraint) import Data.Proxy import Database.Esqueleto.Experimental.From @@ -90,7 +92,25 @@ instance {-# OVERLAPPABLE #-} From (FullOuterJoin a b) where runFrom = undefined class FromInnerJoin lateral lhs rhs res where - runFromInnerJoin :: Proxy lateral -> lhs -> rhs -> (res -> SqlExpr (Value Bool)) -> SqlQuery (res, FromClause) + runFromInnerJoin :: Proxy lateral -> lhs -> rhs -> (res -> SqlExpr (Value Bool)) -> SqlQuery (res, RawFn) + +fromJoin_ :: RawFn -> JoinKind -> RawFn -> Maybe (SqlExpr (Value Bool)) -> RawFn +fromJoin_ lhs kind rhs monClause = + \paren info -> + first (parensM paren) $ + mconcat [ lhs Never info + , (fromKind kind, mempty) + , rhs Parens info + , maybe mempty (makeOnClause info) monClause + ] + where + fromKind InnerJoinKind = " INNER JOIN " + fromKind CrossJoinKind = " CROSS JOIN " + fromKind LeftOuterJoinKind = " LEFT OUTER JOIN " + fromKind RightOuterJoinKind = " RIGHT OUTER JOIN " + fromKind FullOuterJoinKind = " FULL OUTER JOIN " + + makeOnClause info (ERaw _ f) = first (" ON " <>) (f Never info) instance ( SqlSelect b r , ToAlias b @@ -102,7 +122,7 @@ instance ( SqlSelect b r (leftVal, leftFrom) <- runFrom leftPart (rightVal, rightFrom) <- fromSubQuery LateralSubQuery (q leftVal) let ret = leftVal :& rightVal - pure $ (ret, FromJoin leftFrom InnerJoinKind rightFrom (Just (on' ret))) + pure $ (ret, fromJoin_ leftFrom InnerJoinKind rightFrom (Just (on' ret))) instance (From a, FromT a ~ a', From b, FromT b ~ b') => FromInnerJoin NotLateral a b (a' :& b') where @@ -110,7 +130,7 @@ instance (From a, FromT a ~ a', From b, FromT b ~ b') (leftVal, leftFrom) <- runFrom leftPart (rightVal, rightFrom) <- runFrom rightPart let ret = leftVal :& rightVal - pure $ (ret, FromJoin leftFrom InnerJoinKind rightFrom (Just (on' ret))) + pure $ (ret, fromJoin_ leftFrom InnerJoinKind rightFrom (Just (on' ret))) instance (FromInnerJoin (IsLateral b) a b b') => From (InnerJoin a (b, b' -> SqlExpr (Value Bool))) where type FromT (InnerJoin a (b, b' -> SqlExpr (Value Bool))) = FromOnClause (b, b' -> SqlExpr(Value Bool)) @@ -132,7 +152,7 @@ instance ( From a (leftVal, leftFrom) <- runFrom leftPart (rightVal, rightFrom) <- runFrom rightPart let ret = leftVal :& rightVal - pure $ (ret, FromJoin leftFrom CrossJoinKind rightFrom Nothing) + pure $ (ret, fromJoin_ leftFrom CrossJoinKind rightFrom Nothing) instance {-# OVERLAPPING #-} ( From a @@ -146,10 +166,10 @@ instance {-# OVERLAPPING #-} (leftVal, leftFrom) <- runFrom leftPart (rightVal, rightFrom) <- fromSubQuery LateralSubQuery (q leftVal) let ret = leftVal :& rightVal - pure $ (ret, FromJoin leftFrom CrossJoinKind rightFrom Nothing) + pure $ (ret, fromJoin_ leftFrom CrossJoinKind rightFrom Nothing) class FromLeftJoin lateral lhs rhs res where - runFromLeftJoin :: Proxy lateral -> lhs -> rhs -> (res -> SqlExpr (Value Bool)) -> SqlQuery (res, FromClause) + runFromLeftJoin :: Proxy lateral -> lhs -> rhs -> (res -> SqlExpr (Value Bool)) -> SqlQuery (res, RawFn) instance ( From a , FromT a ~ a' @@ -163,7 +183,7 @@ instance ( From a (leftVal, leftFrom) <- runFrom leftPart (rightVal, rightFrom) <- fromSubQuery LateralSubQuery (q leftVal) let ret = leftVal :& (toMaybe rightVal) - pure $ (ret, FromJoin leftFrom LeftOuterJoinKind rightFrom (Just (on' ret))) + pure $ (ret, fromJoin_ leftFrom LeftOuterJoinKind rightFrom (Just (on' ret))) instance ( From a , FromT a ~ a' @@ -176,7 +196,7 @@ instance ( From a (leftVal, leftFrom) <- runFrom leftPart (rightVal, rightFrom) <- runFrom rightPart let ret = leftVal :& (toMaybe rightVal) - pure $ (ret, FromJoin leftFrom LeftOuterJoinKind rightFrom (Just (on' ret))) + pure $ (ret, fromJoin_ leftFrom LeftOuterJoinKind rightFrom (Just (on' ret))) instance ( FromLeftJoin (IsLateral b) a b b' ) => From (LeftOuterJoin a (b, b' -> SqlExpr (Value Bool))) where @@ -202,7 +222,7 @@ instance ( From a (leftVal, leftFrom) <- runFrom leftPart (rightVal, rightFrom) <- runFrom rightPart let ret = (toMaybe leftVal) :& (toMaybe rightVal) - pure $ (ret, FromJoin leftFrom FullOuterJoinKind rightFrom (Just (on' ret))) + pure $ (ret, fromJoin_ leftFrom FullOuterJoinKind rightFrom (Just (on' ret))) instance ( From a , FromT a ~ a' @@ -217,7 +237,7 @@ instance ( From a (leftVal, leftFrom) <- runFrom leftPart (rightVal, rightFrom) <- runFrom rightPart let ret = (toMaybe leftVal) :& rightVal - pure $ (ret, FromJoin leftFrom RightOuterJoinKind rightFrom (Just (on' ret))) + pure $ (ret, fromJoin_ leftFrom RightOuterJoinKind rightFrom (Just (on' ret))) instance (ToMaybe a, ToMaybe b) => ToMaybe (a :& b) where type ToMaybeT (a :& b) = (ToMaybeT a :& ToMaybeT b) diff --git a/src/Database/Esqueleto/Experimental/From/SqlSetOperation.hs b/src/Database/Esqueleto/Experimental/From/SqlSetOperation.hs index 05df34f..67413f4 100644 --- a/src/Database/Esqueleto/Experimental/From/SqlSetOperation.hs +++ b/src/Database/Esqueleto/Experimental/From/SqlSetOperation.hs @@ -1,23 +1,27 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE PatternSynonyms #-} -{-# LANGUAGE TypeFamilies #-} -{-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE UndecidableInstances #-} module Database.Esqueleto.Experimental.From.SqlSetOperation where -import Control.Monad.Trans.Class (lift) -import qualified Control.Monad.Trans.State as S -import qualified Control.Monad.Trans.Writer as W -import Database.Esqueleto.Experimental.From -import Database.Esqueleto.Experimental.ToAlias -import Database.Esqueleto.Experimental.ToAliasReference -import Database.Esqueleto.Internal.Internal hiding (From(..), from, on) -import Database.Esqueleto.Internal.PersistentImport (DBName(..)) +import Control.Monad.Trans.Class (lift) +import qualified Control.Monad.Trans.State as S +import qualified Control.Monad.Trans.Writer as W +import qualified Data.Text.Lazy.Builder as TLB +import Database.Esqueleto.Experimental.From +import Database.Esqueleto.Experimental.ToAlias +import Database.Esqueleto.Experimental.ToAliasReference +import Database.Esqueleto.Internal.Internal hiding + (From (..), + from, on) +import Database.Esqueleto.Internal.PersistentImport (DBName (..), + PersistValue) data SqlSetOperation a = SqlSetUnion (SqlSetOperation a) (SqlSetOperation a) @@ -27,12 +31,16 @@ data SqlSetOperation a | SelectQueryP NeedParens (SqlQuery a) runSetOperation :: (SqlSelect a r, ToAlias a, ToAliasReference a) - => SqlSetOperation a -> SqlQuery (a, FromClause) + => SqlSetOperation a -> SqlQuery (a, NeedParens -> IdentInfo -> (TLB.Builder, [PersistValue])) runSetOperation operation = do (aliasedOperation, ret) <- aliasQueries operation ident <- newIdentFor (DBName "u") ref <- toAliasReference ident ret - pure (ref, FromQuery ident (operationToSql aliasedOperation) NormalSubQuery) + pure ( ref + , \_ info -> + let (queryText, queryVals) = operationToSql aliasedOperation info + in (parens queryText <> " AS " <> useIdent info ident, queryVals) + ) where aliasQueries o = @@ -200,4 +208,4 @@ instance (SqlSelect a r, ToAlias a, ToAliasReference a) => From (SqlSetOperation -- If someone uses just a plain SelectQuery it should behave like a normal subquery runFrom (SelectQueryP _ subquery) = fromSubQuery NormalSubQuery subquery -- Otherwise use the SqlSetOperation - runFrom u = runSetOperation $ toSetOperation u + runFrom u = runSetOperation $ toSetOperation u diff --git a/src/Database/Esqueleto/Internal/Internal.hs b/src/Database/Esqueleto/Internal/Internal.hs index 96c6bce..4610569 100644 --- a/src/Database/Esqueleto/Internal/Internal.hs +++ b/src/Database/Esqueleto/Internal/Internal.hs @@ -24,7 +24,7 @@ module Database.Esqueleto.Internal.Internal where import Control.Applicative ((<|>)) -import Data.Coerce (coerce) +import Data.Coerce (Coercible, coerce) import Control.Arrow (first, (***)) import Control.Exception (Exception, throw, throwIO) import Control.Monad (MonadPlus(..), guard, void) @@ -533,8 +533,7 @@ subSelectUnsafe :: PersistField a => SqlQuery (SqlExpr (Value a)) -> SqlExpr (Va subSelectUnsafe = sub SELECT -- | Project a field of an entity. -(^.) - :: forall typ val. (PersistEntity val, PersistField typ) +(^.) :: forall typ val . (PersistEntity val, PersistField typ) => SqlExpr (Entity val) -> EntityField val typ -> SqlExpr (Value typ) @@ -584,9 +583,20 @@ withNonNull field f = do where_ $ not_ $ isNothing field f $ veryUnsafeCoerceSqlExprValue field +class (PersistEntity ent, PersistField val) + => MaybeHasSqlField entity ent value val + | entity val -> value + , entity value -> val + , entity -> ent + , value ent val -> entity where +instance (PersistEntity ent, PersistField val) + => MaybeHasSqlField (Maybe (Entity ent)) ent (Maybe val) val + +class WithMaybe noMaybe withMaybe | withMaybe -> noMaybe +instance WithMaybe a (Maybe a) + -- | Project a field of an entity that may be null. -(?.) - :: (PersistEntity val, PersistField typ) +(?.) :: ( PersistEntity val , PersistField typ) => SqlExpr (Maybe (Entity val)) -> EntityField val typ -> SqlExpr (Value (Maybe typ)) @@ -1738,8 +1748,7 @@ data FromClause = FromStart Ident EntityDef | FromJoin FromClause JoinKind FromClause (Maybe (SqlExpr (Value Bool))) | OnClause (SqlExpr (Value Bool)) - | FromQuery Ident (IdentInfo -> (TLB.Builder, [PersistValue])) SubQueryType - | FromIdent Ident + | FromRaw (NeedParens -> IdentInfo -> (TLB.Builder, [PersistValue])) data CommonTableExpressionKind = RecursiveCommonTableExpression @@ -1759,8 +1768,7 @@ collectIdents fc = case fc of FromStart i _ -> Set.singleton i FromJoin lhs _ rhs _ -> collectIdents lhs <> collectIdents rhs OnClause _ -> mempty - FromQuery _ _ _ -> mempty - FromIdent _ -> mempty + FromRaw _ -> mempty instance Show FromClause where show fc = case fc of @@ -1782,10 +1790,8 @@ instance Show FromClause where ] OnClause expr -> "(OnClause " <> render' expr <> ")" - FromQuery ident _ subQueryType -> - "(FromQuery " <> show ident <> " " <> show subQueryType <> ")" - FromIdent ident -> - "(FromIdent " <> show ident <> ")" + FromRaw _ -> + "(FromRaw _)" where dummy = SqlBackend @@ -1839,14 +1845,12 @@ collectOnClauses sqlBackend = go Set.empty [] findRightmostIdent (FromStart i _) = Just i findRightmostIdent (FromJoin _ _ r _) = findRightmostIdent r findRightmostIdent (OnClause {}) = Nothing - findRightmostIdent (FromQuery _ _ _) = Nothing - findRightmostIdent (FromIdent _) = Nothing + findRightmostIdent (FromRaw _) = Nothing findLeftmostIdent (FromStart i _) = Just i findLeftmostIdent (FromJoin l _ _ _) = findLeftmostIdent l findLeftmostIdent (OnClause {}) = Nothing - findLeftmostIdent (FromQuery _ _ _) = Nothing - findLeftmostIdent (FromIdent _) = Nothing + findLeftmostIdent (FromRaw _) = Nothing tryMatch :: Set Ident @@ -2819,18 +2823,7 @@ makeFrom info mode fs = ret , maybe mempty makeOnClause monClause ] mk _ (OnClause _) = throw (UnexpectedCaseErr MakeFromError) - mk _ (FromQuery ident f subqueryType) = - let (queryText, queryVals) = f info - lateralKeyword = - case subqueryType of - NormalSubQuery -> "" - LateralSubQuery -> "LATERAL " - in - ( lateralKeyword <> (parens queryText) <> " AS " <> useIdent info ident - , queryVals - ) - mk _ (FromIdent ident) = - (useIdent info ident, mempty) + mk paren (FromRaw f) = f paren info base ident@(I identText) def = let db@(DBName dbText) = entityDB def @@ -2914,13 +2907,6 @@ makeLocking = flip (,) [] . maybe mempty toTLB . Monoid.getLast parens :: TLB.Builder -> TLB.Builder parens b = "(" <> (b <> ")") -aliasedValueIdentToRawSql :: Ident -> IdentInfo -> (TLB.Builder, [PersistValue]) -aliasedValueIdentToRawSql i info = (useIdent info i, mempty) - -valueReferenceToRawSql :: Ident -> (IdentInfo -> Ident) -> IdentInfo -> (TLB.Builder, [PersistValue]) -valueReferenceToRawSql sourceIdent columnIdentF info = - (useIdent info sourceIdent <> "." <> useIdent info (columnIdentF info), mempty) - aliasedEntityColumnIdent :: Ident -> FieldDef -> Ident aliasedEntityColumnIdent (I baseIdent) field = I (baseIdent <> "_" <> (unDBName $ fieldDB field))