HORIZON HASKELLDocslts/ghc-10.0.x8c2f8e82026-10-19Search names, modules, packages, or :: a typeCtrl K

GHC 10.0.0.20260917 · lts/ghc-10.0.x · 8c2f8e8 · 2026-10-19

Moduleesqueleto-3.6.0.3Haskell2010

Database.Esqueleto.Internal.Internal

This is an internal module, anything exported by this module may change without a major version bump. Please use only Database.Esqueleto if possible.

If you use this module, please report what your use case is on the issue tracker so we can safely support it.

  • 55 types
  • 11 classes
  • 228 values
  • Packageesqueleto-3.6.0.3
  • Exports296
  • LanguageHaskell2010
  • LicenceBSD-3-Clause
  • SourceInternal.hs

(Internal) Start a from query with an entity. from does two kinds of magic using fromStart, fromJoin and fromFinish:

  1. The simple but tedious magic of allowing tuples to be used.

  2. The more advanced magic of creating JOINs. The JOIN is processed from right to left. The rightmost entity of the JOIN is created with fromStart. Each JOIN step is then translated into a call to fromJoin. In the end, fromFinish is called to materialize the JOIN.

valueon :: SqlExpr (Value Bool) -> SqlQuery ()
#

An ON clause, useful to describe how two tables are related. Cross joins and tuple-joins do not need an on clause, but InnerJoin and the various outer joins do.

Database.Esqueleto.Experimental in version 4.0.0.0 of the library. The Experimental module has a dramatically improved means for introducing tables and entities that provides more power and less potential for runtime errors.

If you don't include an on clause (or include too many!) then a runtime exception will be thrown.

As an example, consider this simple join:

select $
from $ \(foo `InnerJoin` bar) -> do
  on (foo ^. FooId ==. bar ^. BarFooId)
  ...

We need to specify the clause for joining the two columns together. If we had this:

select $
from $ \(foo `CrossJoin` bar) -> do
  ...

Then we can safely omit the on clause, because the cross join will make pairs of all records possible.

You can do multiple on clauses in a query. This query joins three tables, and has two on clauses:

select $
from $ \(foo `InnerJoin` bar `InnerJoin` baz) -> do
  on (baz ^. BazId ==. bar ^. BarBazId)
  on (foo ^. FooId ==. bar ^. BarFooId)
  ...

Old versions of esqueleto required that you provide the on clauses in reverse order. This restriction has been lifted - you can now provide on clauses in any order, and the SQL should work itself out. The above query is now totally equivalent to this:

select $
from $ \(foo `InnerJoin` bar `InnerJoin` baz) -> do
  on (foo ^. FooId ==. bar ^. BarFooId)
  on (baz ^. BazId ==. bar ^. BarBazId)
  ...
valuegroupBy :: ToSomeValues a => a -> SqlQuery ()
#

GROUP BY clause. You can enclose multiple columns in a tuple.

select $ from \(foo `InnerJoin` bar) -> do
  on (foo ^. FooBarId ==. bar ^. BarId)
  groupBy (bar ^. BarId, bar ^. BarName)
  return (bar ^. BarId, bar ^. BarName, countRows)

With groupBy you can sort by aggregate functions, like so (we used let to restrict the more general countRows to SqlSqlExpr (Value Int) to avoid ambiguity---the second use of countRows has its type restricted by the :: Int below):

r <- select $ from \(foo `InnerJoin` bar) -> do
  on (foo ^. FooBarId ==. bar ^. BarId)
  groupBy $ bar ^. BarName
  let countRows' = countRows
  orderBy [asc countRows']
  return (bar ^. BarName, countRows')
forM_ r $ \(Value name, Value count) -> do
  print name
  print (count :: Int)
Need more columns?

The ToSomeValues class is defined for SqlExpr and tuples of SqlExprs. We only have definitions for up to 8 elements in a tuple right now, so it's possible that you may need to have more than 8 elements.

For example, consider a query with a groupBy call like this:

groupBy (e0, e1, e2, e3, e4, e5, e6, e7)

This is the biggest you can get with a single tuple. However, you can easily nest the tuples to add more:

groupBy ((e0, e1, e2, e3, e4, e5, e6, e7), e8, e9)
valuedistinct :: SqlQuery a -> SqlQuery a
#

DISTINCT. Change the current SELECT into SELECT DISTINCT. For example:

select $ distinct $
  from \foo -> do
  ...

Note that this also has the same effect:

select $
  from \foo -> do
  distinct (return ())
  ...

Deprecated. This function is deprecated, as it is only supported in Postgresql. Please use the variant in Database.Esqueleto.PostgreSQL instead.

DISTINCT ON. Change the current SELECT into SELECT DISTINCT ON (SqlExpressions). For example:

select $
  from \foo ->
  distinctOn [don (foo ^. FooName), don (foo ^. FooState)] $ do
  ...

You can also chain different calls to distinctOn. The above is equivalent to:

select $
  from \foo ->
  distinctOn [don (foo ^. FooName)] $
  distinctOn [don (foo ^. FooState)] $ do
  ...

Each call to distinctOn adds more SqlExpressions. Calls to distinctOn override any calls to distinct.

Note that PostgreSQL requires the SqlExpressions on DISTINCT ON to be the first ones to appear on a ORDER BY. This is not managed automatically by esqueleto, keeping its spirit of trying to be close to raw SQL.

Supported by PostgreSQL only.

valuelocking :: LockingKind -> SqlQuery ()
#

Add a locking clause to the query. Please read LockingKind documentation and your RDBMS manual. Unsafe since not all locking clauses are implemented for every RDBMS

If multiple calls to locking are made on the same query, the last one is used.

Execute a subquery SELECT in a SqlExpr. The query passed to this function will only return a single result - it has a LIMIT 1 passed in to the query to make it safe, and the return type is Maybe to indicate that the subquery might result in 0 rows.

If you find yourself writing joinV . subSelect, then consider using subSelectMaybe.

If you're performing a countRows, then you can use subSelectCount which is safe.

If you know that the subquery will always return exactly one row (eg a foreign key constraint guarantees that you'll get exactly one row), then consider subSelectUnsafe, along with a comment explaining why it is safe.

valuesubSelectForeign
  1. :: (BackendCompatible SqlBackend (PersistEntityBackend val1), PersistEntity val1, PersistEntity val2, PersistField a)
  2. => SqlExpr (Entity val2)

    An expression representing the table you have access to now.

  3. -> EntityField val2 (Key val1)

    The foreign key field on the table.

  4. -> (SqlExpr (Entity val1) -> SqlExpr (Value a))

    A function to extract a value from the foreign reference table.

  5. -> SqlExpr (Value a)
#

Performs a sub-select using the given foreign key on the entity. This is useful to extract values that are known to be present by the database schema.

As an example, consider the following persistent definition:

User
  profile ProfileId

Profile
  name    Text

The following query will return the name of the user.

getUserWithName =
    select $
    from $ user ->
    pure (user, subSelectForeign user UserProfile (^. ProfileName)

Execute a subquery SELECT in a SqlExpr. This function is unsafe, because it can throw runtime exceptions in two cases:

  1. If the query passed has 0 result rows, then it will return a NULL value. The persistent parsing operations will fail on an unexpected NULL.

  2. If the query passed returns more than one row, then the SQL engine will fail with an error like "More than one row returned by a subquery used as an expression".

This function is safe if you guarantee that exactly one row will be returned, or if the result already has a Maybe type for some reason.

For variants with the safety encoded already, see subSelect and subSelectMaybe. For the most common safe use of this, see subSelectCount.

valueisNothing
  1. :: PersistField typ
  2. => SqlExpr (Value (Maybe typ))
  3. -> SqlExpr (Value Bool)
#

IS NULL comparison.

For IS NOT NULL, you can negate this with not_, as in not_ (isNothing (person ^. PersonAge))

Warning: Persistent and Esqueleto have different behavior for != Nothing:

Haskell

SQL

Persistent

!=. Nothing

IS NOT NULL

Esqueleto

!=. Nothing

!= NULL

In SQL, = NULL and != NULL return NULL instead of true or false. For this reason, you very likely do not want to use !=. Nothing in Esqueleto. You may find these hlint rules helpful to enforce this:

- error: {lhs: v Database.Esqueleto.==. Database.Esqueleto.nothing, rhs: Database.Esqueleto.isNothing v, name: Use Esqueleto's isNothing}
- error: {lhs: v Database.Esqueleto.==. Database.Esqueleto.val Nothing, rhs: Database.Esqueleto.isNothing v, name: Use Esqueleto's isNothing}
- error: {lhs: v Database.Esqueleto.!=. Database.Esqueleto.nothing, rhs: not_ (Database.Esqueleto.isNothing v), name: Use Esqueleto's not isNothing}
- error: {lhs: v Database.Esqueleto.!=. Database.Esqueleto.val Nothing, rhs: not_ (Database.Esqueleto.isNothing v), name: Use Esqueleto's not isNothing}
valuejoinV
  1. :: NullableFieldProjection typ typ'
  2. => SqlExpr (Value (Maybe typ))
  3. -> SqlExpr (Value (Maybe typ'))
#

Join nested Maybes in a Value into one. This is useful when calling aggregate functions on nullable fields.

As of v3.6.0.0, this function will attempt to work on both SqlExpr (Value (Maybe a)) as well as SqlExpr (Value (Maybe (Maybe a))) inputs to make transitioning to NullableFieldProjection easier. This may make type inference worse in some cases. If you want the monomorphic variant, see joinV'

value(==.)
  1. :: PersistField typ
  2. => SqlExpr (Value typ)
  3. -> SqlExpr (Value typ)
  4. -> SqlExpr (Value Bool)
#

This operator produces the SQL operator =, which is used to compare values for equality.

Example:

 query :: UserId -> SqlPersistT IO [Entity User]
 query userId = select $ do
     user <- from $ table @User
     where_ (user ^. UserId ==. val userId)
     pure user

This would generate the following SQL:

 SELECT user.*
 FROM user
 WHERE user.id = ?
value(+.)
  1. :: PersistField a
  2. => SqlExpr (Value a)
  3. -> SqlExpr (Value a)
  4. -> SqlExpr (Value a)
#

This operator translates to the SQL operator +.

This does not require or assume anything about the SQL values. Interpreting what +. means for a given type is left to the database engine.

Example:

 user ^. UserAge +. val 10
value(-.)
  1. :: PersistField a
  2. => SqlExpr (Value a)
  3. -> SqlExpr (Value a)
  4. -> SqlExpr (Value a)
#

This operator translates to the SQL operator -.

This does not require or assume anything about the SQL values. Interpreting what -. means for a given type is left to the database engine.

Example:

 user ^. UserAge -. val 10
value(/.)
  1. :: PersistField a
  2. => SqlExpr (Value a)
  3. -> SqlExpr (Value a)
  4. -> SqlExpr (Value a)
#

This operator translates to the SQL operator /.

This does not require or assume anything about the SQL values. Interpreting what /. means for a given type is left to the database engine.

Example:

 user ^. UserAge /. val 10
value(*.)
  1. :: PersistField a
  2. => SqlExpr (Value a)
  3. -> SqlExpr (Value a)
  4. -> SqlExpr (Value a)
#

This operator translates to the SQL operator *.

This does not require or assume anything about the SQL values. Interpreting what *. means for a given type is left to the database engine.

Example:

 user ^. UserAge *. val 10
valuecastNum :: (Num a, Num b) => SqlExpr (Value a) -> SqlExpr (Value b)
#

Allow a number of one type to be used as one of another type via an implicit cast. An explicit cast is not made, this function changes only the types on the Haskell side.

Caveat: Trying to use castNum from Double to Int will not result in an integer, the original fractional number will still be used! Use round_, ceiling_ or floor_ instead.

Safety: This operation is mostly safe due to the Num constraint between the types and the fact that RDBMSs usually allow numbers of different types to be used interchangeably. However, there may still be issues with the query not being accepted by the RDBMS or persistent not being able to parse it.

value(%) :: SqlString s => SqlExpr (Value s)
#

The string %. May be useful while using like and concatenation (concat_ or ++., depending on your database). Note that you always have to type the parenthesis, for example:

name `like` (%) ++. val "John" ++. (%)
valueconcat_ :: SqlString s => [SqlExpr (Value s)] -> SqlExpr (Value s)
#

The CONCAT function with a variable number of parameters. Supported by MySQL and PostgreSQL. SQLite supports this in versions after 3.44.0, and persistent-sqlite supports this in versions 2.13.3.0 and after.

valuecastString
  1. :: (SqlString s, SqlString r)
  2. => SqlExpr (Value s)
  3. -> SqlExpr (Value r)
#

Cast a string type into Text. This function is very useful if you want to use newtypes, or if you want to apply functions such as like to strings of different types.

Safety: This is a slightly unsafe function, especially if you have defined your own instances of SqlString. Also, since Maybe is an instance of SqlString, it's possible to turn a nullable value into a non-nullable one. Avoid using this function if possible.

valuein_
  1. :: PersistField typ
  2. => SqlExpr (Value typ)
  3. -> SqlExpr (ValueList typ)
  4. -> SqlExpr (Value Bool)
#

IN operator. For example if you want to select all Persons by a list of IDs:

SELECT *
FROM Person
WHERE Person.id IN (?)

In esqueleto, we may write the same query above as:

select $
from $ \person -> do
where_ $ person ^. PersonId `in_` valList personIds
return person

Where personIds is of type [Key Person].

valuecase_
  1. :: PersistField a
  2. => [(SqlExpr (Value Bool), SqlExpr (Value a))]
  3. -> SqlExpr (Value a)
  4. -> SqlExpr (Value a)
#

CASE statement. For example:

select $
return $
case_
   [ when_
       (exists $
       from $ \p -> do
       where_ (p ^. PersonName ==. val "Mike"))
     then_
       (subSelect $
       from $ \v -> do
       let sub =
               from $ \c -> do
               where_ (c ^. PersonName ==. val "Mike")
               return (c ^. PersonFavNum)
       where_ (just (v ^. PersonFavNum) >. subSelect sub)
       return $ count (v ^. PersonName) +. val (1 :: Int)) ]
   (else_ $ val (-1))

This query is a bit complicated, but basically it checks if a person named "Mike" exists, and if that person does, run the subquery to find out how many people have a ranking (by Fav Num) higher than "Mike".

NOTE: There are a few things to be aware about this statement.

  • This only implements the full CASE statement, it does not implement the "simple" CASE statement.

  • At least one when_ and then_ is mandatory otherwise it will emit an error.

  • The else_ is also mandatory, unlike the SQL statement in which if the ELSE is omitted it will return a NULL. You can reproduce this via nothing.

valuetoBaseId
  1. :: ToBaseId ent
  2. => SqlExpr (Value (Key ent))
  3. -> SqlExpr (Value (Key (BaseEnt ent)))
#

Convert an entity's key into another entity's.

This function is to be used when you change an entity's Id to be that of another entity. For example:

Bar
  barNum Int
Foo
  bar BarId
  fooNum Int
  Primary bar

In this example, Bar is said to be the BaseEnt(ity), and Foo the child. To model this in Esqueleto, declare:

instance ToBaseId Foo where
  type BaseEnt Foo = Bar
  toBaseIdWitness barId = FooKey barId

Now you're able to write queries such as:

select $
from $ (bar `InnerJoin` foo) -> do
on (toBaseId (foo ^. FooId) ==. bar ^. BarId)
return (bar, foo)

Note: this function may be unsafe to use in conditions not like the one of the example above.

valuefromBaseId
  1. :: ToBaseId ent
  2. => SqlExpr (Value (Key (BaseEnt ent)))
  3. -> SqlExpr (Value (Key ent))
#

The inverse of toBaseId. Note that this is somewhat less "safe" than toBaseId. Calling toBaseId will usually mean that a foreign key constraint is present that guarantees the presence of the base ID. fromBaseId has no such guarantee. Consider the code example given in toBaseId:

Bar
  barNum Int
Foo
  bar BarId
  fooNum Int
  Primary bar
instance ToBaseId Foo where
  type BaseEnt Foo = Bar
  toBaseIdWitness barId = FooKey barId

The type of toBaseId for Foo would be:

toBaseId :: SqlExpr (Value FooId) -> SqlExpr (Value BarId)

The foreign key constraint on Foo means that every FooId points to a BarId in the database. However, fromBaseId will not have this:

fromBaseId :: SqlExpr (Value BarId) -> SqlExpr (Value FooId)
valueelse_ :: expr a -> expr a
#

Syntax sugar for case_.

newtypenewtype Value a
#

A single value (as opposed to a whole entity). You may use (^.) or (?.) to get a Value from an Entity.

Constructors

Instances18Monad, Functor, Applicative, Foldable, Traversable, HasField, …
newtypenewtype ValueList a
#

A list of single values. There's a limited set of functions able to work with this data type (such as subList_select, valList, in_ and exists).

Constructors

Instances3Eq, Ord, Show
  • Eq a => Eq (ValueList a)Defined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
  • Ord a => Ord (ValueList a)Defined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
  • Show a => Show (ValueList a)Defined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
classclass ToSomeValues a where
#

A class of things that can be converted into a list of SomeValue. It has instances for tuples and is the reason why groupBy can take tuples, like groupBy (foo ^. FooId, foo ^. FooName, foo ^. FooType).

Methods

Instances8ToSomeValues, …
classclass FinalResult a where
#

A class for constructors or function which result type is known.

Methods

Instances2FinalResult
  • FinalResult (Unique val)Defined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
  • FinalResult b => FinalResult (a -> b)Defined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
datadata InnerJoin a b
#

Data type that represents an INNER JOIN (see LeftOuterJoin for an example).

Constructors

Instances3IsJoinKind, From, ToFrom
datadata CrossJoin a b
#

Data type that represents a CROSS JOIN (see LeftOuterJoin for an example).

Constructors

Instances3IsJoinKind, From, ToFrom
datadata LeftOuterJoin a b
#

Data type that represents a LEFT OUTER JOIN. For example,

select $
from $ \(person `LeftOuterJoin` pet) ->
  ...

is translated into

SELECT ...
FROM Person LEFT OUTER JOIN Pet
...

See also: from.

Constructors

Instances3IsJoinKind, From, ToFrom
datadata RightOuterJoin a b
#

Data type that represents a RIGHT OUTER JOIN (see LeftOuterJoin for an example).

Constructors

Instances3IsJoinKind, From, ToFrom
datadata FullOuterJoin a b
#

Data type that represents a FULL OUTER JOIN (see LeftOuterJoin for an example).

Constructors

Instances3IsJoinKind, From, ToFrom
classclass IsJoinKind (join :: Type -> Type -> Type) where
#

(Internal) Functions that operate on types (that should be) of kind JoinKind.

Methods

  • smartJoin :: a -> b -> join a b

    (Internal) smartJoin a b is a JOIN of the correct kind.

  • reifyJoinKind :: join a b -> JoinKind

    (Internal) Reify a JoinKind from a JOIN. This function is non-strict.

Instances5IsJoinKind

Exception thrown whenever on is used to create an ON clause but no matching JOIN is found.

Instances4Eq, Ord, Show, Exception
datadata Update
#

Phantom type for a SET operation on an entity of the given type (see set and (=.)).

datadata (:&) a b
#

A left-precedence pair. Pronounced "and". Used to represent expressions that have been joined together.

The precedence behavior can be demonstrated by:

a :& b :& c == ((a :& b) :& c)

See the examples at the beginning of this module to see how this operator is used in JOIN operations.

Constructors

  • a :& binfixl 2
Instances19Bifunctor, DoInnerJoin, DoLeftJoin, DoCrossJoin, GetFirstTable, Functor, …
datadata LockingKind
#

Different kinds of locking clauses supported by locking.

Note that each RDBMS has different locking support. The constructors of this datatype specify only the syntax of the locking mechanism, not its semantics. For example, even though both MySQL and PostgreSQL support ForUpdate, there are no guarantees that they will behave the same.

Constructors

  • ForUpdate

    FOR UPDATE syntax. Supported by MySQL, Oracle and PostgreSQL.

  • ForUpdateSkipLocked

    FOR UPDATE SKIP LOCKED syntax. Supported by MySQL, Oracle and PostgreSQL.

  • ForShare

    FOR SHARE syntax. Supported by PostgreSQL.

  • LockInShareMode

    LOCK IN SHARE MODE syntax. Supported by MySQL.

datadata OnLockedBehavior
#

Constructors

  • NoWait

    NOWAIT syntax locking behaviour. query excutes immediately failing on locked rows

  • SkipLocked

    SKIP LOCKED syntax locking behaviour. query skips locked rows

  • Wait

    default locking behaviour. query will wait on locked rows

Instances3Eq, Ord, Show
classclass LockableEntity a where
#

Lockable entity

Example use:

select $ do
    (p :& bp) <- from $
        table Person
        innerJoin table BlogPost
            on do
                (p :& bp) -> p ^. PersonId ==. b ^. BlogPostAuthorId
    forUpdateOf (p :& b) skipLocked
    return p
Instances2LockableEntity
classclass PersistField a => SqlString a
#

Phantom class of data types that are treated as strings by the RDBMS. It has no methods because it's only used to avoid type errors such as trying to concatenate integers.

If you have a custom data type or newtype, feel free to make it an instance of this class.

Instances6SqlString
  • SqlString HtmlDefined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
  • SqlString ByteStringDefined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
  • SqlString TextDefined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
  • SqlString TextDefined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
  • SqlString a => SqlString (Maybe a)Defined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
  • a ~ Char => SqlString [a]Defined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
classclass ToBaseId ent where
#

Class that enables one to use toBaseId to convert an entity's key on a query into another (cf. toBaseId).

Associated types

  • type family BaseEnt ent

    e.g. type BaseEnt MyBase = MyChild

Methods

  • toBaseIdWitness :: Key (BaseEnt ent) -> Key ent

    Convert from the key of the BaseEnt(ity) to the key of the child entity. This function is not actually called, but that it typechecks proves this operation is safe.

valuefrom :: From a => (a -> SqlQuery b) -> SqlQuery b
#

FROM clause: bring entities into scope.

Note that this function will be replaced by the one in Database.Esqueleto.Experimental in version 4.0.0.0 of the library. The Experimental module has a dramatically improved means for introducing tables and entities that provides more power and less potential for runtime errors.

This function internally uses two type classes in order to provide some flexibility of how you may call it. Internally we refer to these type classes as the two different magics.

The innermost magic allows you to use from with the following types:

  • expr (Entity val), which brings a single entity into scope.

  • expr (Maybe (Entity val)), which brings a single entity that may be NULL into scope. Used for OUTER JOINs.

  • A JOIN of any other two types allowed by the innermost magic, where a JOIN may be an InnerJoin, a CrossJoin, a LeftOuterJoin, a RightOuterJoin, or a FullOuterJoin. The JOINs have left fixity.

The outermost magic allows you to use from on any tuples of types supported by innermost magic (and also tuples of tuples, and so on), up to 8-tuples.

Note that using from for the same entity twice does work and corresponds to a self-join. You don't even need to use two different calls to from, you may use a JOIN or a tuple.

The following are valid examples of uses of from (the types of the arguments of the lambda are inside square brackets):

from $ \person -> ...
from $ \(person, blogPost) -> ...
from $ \(p `LeftOuterJoin` mb) -> ...
from $ \(p1 `InnerJoin` f `InnerJoin` p2) -> ...
from $ \((p1 `InnerJoin` f) `InnerJoin` p2) -> ...

The types of the arguments to the lambdas above are, respectively:

person
  :: ( Esqueleto query expr backend
     , PersistEntity Person
     , PersistEntityBackend Person ~ backend
     ) => expr (Entity Person)
(person, blogPost)
  :: (...) => (expr (Entity Person), expr (Entity BlogPost))
(p `LeftOuterJoin` mb)
  :: (...) => InnerJoin (expr (Entity Person)) (expr (Maybe (Entity BlogPost)))
(p1 `InnerJoin` f `InnerJoin` p2)
  :: (...) => InnerJoin
                (InnerJoin (expr (Entity Person))
                           (expr (Entity Follow)))
                (expr (Entity Person))
(p1 `InnerJoin` (f `InnerJoin` p2)) ::
  :: (...) => InnerJoin
                (expr (Entity Person))
                (InnerJoin (expr (Entity Follow))
                           (expr (Entity Person)))

Note that some backends may not support all kinds of JOINs.

classclass From a where
#

(Internal) Class that implements the tuple from magic (see fromStart).

Methods

Instances14From, …
classclass FromPreprocess a where
#

(Internal) Class that implements the JOIN from magic (see fromStart).

Instances3FromPreprocess
newtypenewtype SqlQuery a
#

SQL backend for esqueleto using SqlPersistT.

Constructors

Instances9Monad, Functor, Applicative, DoInnerJoin, DoLeftJoin, DoCrossJoin, …
datadata SideData
#
Instances2Semigroup, Monoid
  • Semigroup SideDataDefined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
  • Monoid SideDataDefined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
newtypenewtype Ident
#

Identifier used for table names.

Constructors

Instances3Eq, Ord, Show
  • Eq IdentDefined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
  • Ord IdentDefined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
  • Show IdentDefined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
datadata SqlExpr a
#

An expression on the SQL backend.

Raw expression: Contains a SqlExprMeta and a function for building the expr. It recieves a parameter telling it whether it is in a parenthesized context, and takes information about the SQL connection (mainly for escaping names) and returns both an string (Builder) and a list of values to be interpolated by the SQL backend.

Instances29Functor, HasField, DoInnerJoin, DoLeftJoin, From, LockableEntity, …

The type SqlExpr a represents a SQL expression that evaluates to a value that can be parsed in Haskell to an a. There are often many underlying SQL values that can parse exactly. The function veryUnsafeCoerceSqlExpr allows you to change this type.

There is no guarantee that the result works! To be safe, you want to provide a local helper function that calls this at a tested type.

As an example, you may know that two types share an identical SQL representation, and can be parsed exactly the same way. Perhaps you have a data SomeEnum which you represent as a TEXT in Postgres, and you want to treat it as a TEXT. You could define a top-level type-restricted alias to this which allows this to be done safely:

 enumToText :: SqlExpr (Value SomeEnum) -> SqlExpr (Value Text)
 enumToText = veryUnsafeCoerceSqlExpr

Note that this is fragile: if you change the encoding of SomeEnum to be anything other than Text, then your code will fail at runtime.

typetype SqlExprFunctorMessage = (('Text "You're trying to treat `SqlExpr` like a `Functor`, but it cannot be one." ':$$: 'Text "We would need to send arbitrary functions to the database for interpretation to support that instance.") ':$$: 'Text "See the docs for the fake instance of `Functor SqlExpr` for more information.") ':$$: 'Text "Consider using a SQL function with `unsafeSqlFunction` and a good type signature."
#

The type error message given when you try to do fmap on a SqlExpr. This is intended to guide folks towards the docs, which should guide them towards alternative implementations.

classclass NullableFieldProjection typ typ'
#

The NullableFieldProjection type is used to determine whether a Maybe should be stripped off or not. This is used in the HasField for SqlExpr (Maybe (Entity a)) to allow you to only have a single level of Maybe.

MyTable
  column         Int Maybe
  someTableId    SomeTableId

 select $ do
     (_ :& maybeMyTable) <-
         from $ table SomeTable
             leftJoin table MyTable
                 on do
                     (someTable :& maybeMyTable) ->
                         just someTable.id ==. maybeMyTable.someTableId
       where_ $ maybeMyTable.column ==. just (val 10)
       pure maybeMyTable

Without this class, projecting a field with type Maybe typ would have resulted in a SqlExpr (Value (Maybe (Maybe typ))).

Instances2NullableFieldProjection
datadata InsertFinal
#

Phantom type used to mark a INSERT INTO query.

(Internal) Create a custom binary operator. You should not use this function directly since its type is very general, you should always use it with an explicit type signature. For example:

(==.) :: SqlExpr (Value a) -> SqlExpr (Value a) -> SqlExpr (Value Bool)
(==.) = unsafeSqlBinOp " = "

In the example above, we constraint the arguments to be of the same type and constraint the result to be a boolean value.

Similar to unsafeSqlBinOp, but may also be applied to composite keys. Uses the operator given as the second argument whenever applied to composite keys.

Usage example:

(==.) :: SqlExpr (Value a) -> SqlExpr (Value a) -> SqlExpr (Value Bool)
(==.) = unsafeSqlBinOpComposite " = " " AND "

Persistent has a hack for implementing composite keys (see ECompositeKey doc for more details), so we're forced to use a hack here as well. We deconstruct ERaw values based on two rules:

  • If it is a single placeholder, then it's assumed to be coming from a PersistList and thus its components are separated so that they may be applied to a composite key.

  • If it is not a single placeholder, then it's assumed to be a foreign (composite or not) key, so we enforce that it has no placeholders and split it on the commas.

classclass UnsafeSqlFunctionArgument a where
#

(Internal) This class allows unsafeSqlFunction to work with different numbers of arguments; specifically it allows providing arguments to a sql function via an n-tuple of SqlExpr (Value _) values, which are not all necessarily required to be the same type. There are instances for up to 10-tuples, but for sql functions which take more than 10 arguments, you can also nest tuples, as e.g. toArgList ((a,b),(c,d)) is the same as toArgList (a,b,c,d).

Methods

Instances12UnsafeSqlFunctionArgument, …
valueselect
  1. :: (SqlSelect a r, MonadIO m, SqlBackendCanRead backend)
  2. => SqlQuery a
  3. -> ReaderT backend m [r]
#

Execute an esqueleto SELECT query inside persistent's SqlPersistT monad and return a list of rows.

We've seen that from has some magic about which kinds of things you may bring into scope. This select function also has some magic for which kinds of things you may bring back to Haskell-land by using SqlQuery's return:

  • You may return a SqlExpr (Entity v) for an entity v (i.e., like the * in SQL), which is then returned to Haskell-land as just Entity v.

  • You may return a SqlExpr (Maybe (Entity v)) for an entity v that may be NULL, which is then returned to Haskell-land as Maybe (Entity v). Used for OUTER JOINs.

  • You may return a SqlExpr (Value t) for a value t (i.e., a single column), where t is any instance of PersistField, which is then returned to Haskell-land as Value t. You may use Value to return projections of an Entity (see (^.) and (?.)) or to return any other value calculated on the query (e.g., countRows or subSelect).

The SqlSelect a r class has functional dependencies that allow type information to flow both from a to r and vice-versa. This means that you'll almost never have to give any type signatures for esqueleto queries. For example, the query select $ from $ \p -> return p alone is ambiguous, but in the context of

do ps <- select $
         from $ \p ->
         return p
   liftIO $ mapM_ (putStrLn . personName . entityVal) ps

we are able to infer from that single personName . entityVal function composition that the p inside the query is of type SqlExpr (Entity Person).

valueselectOne
  1. :: (SqlSelect a r, MonadIO m, SqlBackendCanRead backend)
  2. => SqlQuery a
  3. -> ReaderT backend m (Maybe r)
#

Execute an esqueleto SELECT query inside persistent's SqlPersistT monad and return the first entry wrapped in a Maybe. @since 3.5.1.0

Example usage
firstPerson :: MonadIO m => SqlPersistT m (Maybe (Entity Person))
firstPerson =
 selectOne $ do
     person <- from $ table @Person
     return person

The above query is equivalent to a select combined with limit but you would still have to transform the results from a list:

firstPerson :: MonadIO m => SqlPersistT m [Entity Person]
firstPerson =
 select $ do
     person <- from $ table @Person
     limit 1
     return person
valuedelete
  1. :: (MonadIO m, SqlBackendCanWrite backend)
  2. => SqlQuery ()
  3. -> ReaderT backend m ()
#

Execute an esqueleto DELETE query inside persistent's SqlPersistT monad. Note that currently there are no type checks for statements that should not appear on a DELETE query.

Example of usage:

delete $
from $ \appointment ->
where_ (appointment ^. AppointmentDate <. val now)

Unlike select, there is a useful way of using delete that will lead to type ambiguities. If you want to delete all rows (i.e., no where_ clause), you'll have to use a type signature:

delete $
from $ \(appointment :: SqlExpr (Entity Appointment)) ->
return ()
Database.Esqueleto.Experimental:
 delete $ do
   userFeature <- from $ table @UserFeature
   where_ ((userFeature ^. UserFeatureFeature) notIn valList allKnownFeatureFlags)

Execute an esqueleto UPDATE query inside persistent's SqlPersistT monad. Note that currently there are no type checks for statements that should not appear on a UPDATE query.

Example of usage:

update $ \p -> do
set p [ PersonAge =. just (val thisYear) -. p ^. PersonBorn ]
where_ $ isNothing (p ^. PersonAge)
valuerenderQueryToText
  1. :: (SqlSelect a r, BackendCompatible SqlBackend backend, Monad m)
  2. => Mode

    Whether to render as an SELECT, DELETE, etc.

  3. -> SqlQuery a

    The SQL query you want to render.

  4. -> ReaderT backend m (Text, [PersistValue])
#

Renders a SqlQuery into a Text value along with the list of PersistValues that would be supplied to the database for ? placeholders.

You must ensure that the Mode you pass to this function corresponds with the actual SqlQuery. If you pass a query that uses incompatible features (like an INSERT statement with a SELECT mode) then you'll get a weird result.

classclass SqlSelect a r | a -> r, r -> a where
#

(Internal) Class for mapping results coming from SqlQuery into actual results.

This looks very similar to RawSql, and it is! However, there are some crucial differences and ultimately they're different classes.

Methods

Instances21SqlSelect, …
valuefrom3 :: (a, b, c) -> ((a, b), c)
#
valueto3 :: ((a, b), c) -> (a, b, c)
#
valuefrom4 :: (a, b, c, d) -> ((a, b), (c, d))
#
valueto4 :: ((a, b), (c, d)) -> (a, b, c, d)
#
valuefrom5 :: (a, b, c, d, e) -> ((a, b), (c, d), e)
#
valueto5 :: ((a, b), (c, d), e) -> (a, b, c, d, e)
#
valuefrom6 :: (a, b, c, d, e, f) -> ((a, b), (c, d), (e, f))
#
valueto6 :: ((a, b), (c, d), (e, f)) -> (a, b, c, d, e, f)
#
valuefrom7P :: Proxy (a, b, c, d, e, f, g) -> Proxy ((a, b), (c, d), (e, f), g)
#
valuefrom7 :: (a, b, c, d, e, f, g) -> ((a, b), (c, d), (e, f), g)
#
valueto7 :: ((a, b), (c, d), (e, f), g) -> (a, b, c, d, e, f, g)
#
valuefrom8P
  1. :: Proxy (a, b, c, d, e, f, g, h)
  2. -> Proxy ((a, b), (c, d), (e, f), (g, h))
#
valuefrom8 :: (a, b, c, d, e, f, g, h) -> ((a, b), (c, d), (e, f), (g, h))
#
valueto8 :: ((a, b), (c, d), (e, f), (g, h)) -> (a, b, c, d, e, f, g, h)
#
valuefrom9P
  1. :: Proxy (a, b, c, d, e, f, g, h, i)
  2. -> Proxy ((a, b), (c, d), (e, f), (g, h), i)
#
valuefrom9 :: (a, b, c, d, e, f, g, h, i) -> ((a, b), (c, d), (e, f), (g, h), i)
#
valueto9 :: ((a, b), (c, d), (e, f), (g, h), i) -> (a, b, c, d, e, f, g, h, i)
#
valuefrom10P
  1. :: Proxy (a, b, c, d, e, f, g, h, i, j)
  2. -> Proxy ((a, b), (c, d), (e, f), (g, h), (i, j))
#
valuefrom10
  1. :: (a, b, c, d, e, f, g, h, i, j)
  2. -> ((a, b), (c, d), (e, f), (g, h), (i, j))
#
valueto10
  1. :: ((a, b), (c, d), (e, f), (g, h), (i, j))
  2. -> (a, b, c, d, e, f, g, h, i, j)
#
valuefrom11P
  1. :: Proxy (a, b, c, d, e, f, g, h, i, j, k)
  2. -> Proxy ((a, b), (c, d), (e, f), (g, h), (i, j), k)
#
valuefrom11
  1. :: (a, b, c, d, e, f, g, h, i, j, k)
  2. -> ((a, b), (c, d), (e, f), (g, h), (i, j), k)
#
valueto11
  1. :: ((a, b), (c, d), (e, f), (g, h), (i, j), k)
  2. -> (a, b, c, d, e, f, g, h, i, j, k)
#
valuefrom12P
  1. :: Proxy (a, b, c, d, e, f, g, h, i, j, k, l)
  2. -> Proxy ((a, b), (c, d), (e, f), (g, h), (i, j), (k, l))
#
valuefrom12
  1. :: (a, b, c, d, e, f, g, h, i, j, k, l)
  2. -> ((a, b), (c, d), (e, f), (g, h), (i, j), (k, l))
#
valueto12
  1. :: ((a, b), (c, d), (e, f), (g, h), (i, j), (k, l))
  2. -> (a, b, c, d, e, f, g, h, i, j, k, l)
#
valuefrom13P
  1. :: Proxy (a, b, c, d, e, f, g, h, i, j, k, l, m)
  2. -> Proxy ((a, b), (c, d), (e, f), (g, h), (i, j), (k, l), m)
#
valuefrom13
  1. :: (a, b, c, d, e, f, g, h, i, j, k, l, m)
  2. -> ((a, b), (c, d), (e, f), (g, h), (i, j), (k, l), m)
#
valueto13
  1. :: ((a, b), (c, d), (e, f), (g, h), (i, j), (k, l), m)
  2. -> (a, b, c, d, e, f, g, h, i, j, k, l, m)
#
valuefrom14P
  1. :: Proxy (a, b, c, d, e, f, g, h, i, j, k, l, m, n)
  2. -> Proxy ((a, b), (c, d), (e, f), (g, h), (i, j), (k, l), (m, n))
#
valuefrom14
  1. :: (a, b, c, d, e, f, g, h, i, j, k, l, m, n)
  2. -> ((a, b), (c, d), (e, f), (g, h), (i, j), (k, l), (m, n))
#
valueto14
  1. :: ((a, b), (c, d), (e, f), (g, h), (i, j), (k, l), (m, n))
  2. -> (a, b, c, d, e, f, g, h, i, j, k, l, m, n)
#
valuefrom15P
  1. :: Proxy (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)
  2. -> Proxy ((a, b), (c, d), (e, f), (g, h), (i, j), (k, l), (m, n), o)
#
valuefrom15
  1. :: (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)
  2. -> ((a, b), (c, d), (e, f), (g, h), (i, j), (k, l), (m, n), o)
#
valueto15
  1. :: ((a, b), (c, d), (e, f), (g, h), (i, j), (k, l), (m, n), o)
  2. -> (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)
#
valuefrom16P
  1. :: Proxy (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)
  2. -> Proxy ((a, b), (c, d), (e, f), (g, h), (i, j), (k, l), (m, n), (o, p))
#
valuefrom16
  1. :: (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)
  2. -> ((a, b), (c, d), (e, f), (g, h), (i, j), (k, l), (m, n), (o, p))
#
valueto16
  1. :: ((a, b), (c, d), (e, f), (g, h), (i, j), (k, l), (m, n), (o, p))
  2. -> (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)
#
valuevalJ
  1. :: PersistField (Key entity)
  2. => Value (Key entity)
  3. -> SqlExpr (Value (Key entity))
#

valJ is like val but for something that is already a Value. The use case it was written for was, given a Value lift the Key for that Value into the query expression in a type safe way. However, the implementation is more generic than that so we call it valJ.

Its important to note that the input entity and the output entity are constrained to be the same by the type signature on the function (https://github.com/prowdsponsor/esqueleto/pull/69).

valueassociateJoin
  1. :: Ord (Key e0)
  2. => [(Entity e0, e1)]
  3. -> Map (Key e0) (e0, [e1])
#

Avoid N+1 queries and join entities into a map structure.

This function is useful to call on the result of a single JOIN. For example, suppose you have this query:

getFoosAndNestedBarsFromParent
    :: ParentId
    -> SqlPersistT IO [(Entity Foo, Maybe (Entity Bar))]
getFoosAndNestedBarsFromParent parentId =
    select $ do
        (foo :& bar) <- from $
            table Foo
            `LeftOuterJoin`
            table Bar
                `on` do
                    \(foo :& bar) ->
                        foo ^. FooId ==. bar ?. BarFooId
        where_ $
            foo ^. FooParentId ==. val parentId
        pure (foo, bar)

This is a natural result type for SQL - a list of tuples. However, it's not what we usually want in Haskell - each Foo in the list will be represented multiple times, once for each Bar.

We can write fmap associateJoin and it will translate it into a Map that is keyed on the Key of the left Entity, and the value is a tuple of the entity's value as well as the list of each coresponding entity.

getFoosAndNestedBarsFromParentHaskellese
    :: ParentId
    -> SqlPersistT (Map (Key Foo) (Foo, [Maybe (Entity Bar)]))
getFoosAndNestedBarsFromParentHaskellese parentId =
    fmap associateJoin $ getFoosdAndNestedBarsFromParent parentId

What if you have multiple joins?

Let's use associateJoin with a *two* join query.

userPostComments
    :: SqlQuery (SqlExpr (Entity User, Entity Post, Entity Comment))
userPostsComment = do
    (u :& p :& c) <- from $
        table User
        `InnerJoin`
        table Post
            on do
                \(u :& p) ->
                    u ^. UserId ==. p ^. PostUserId
        `InnerJoin`
        table @Comment
            `on` do
                \(_ :& p :& c) ->
                    p ^. PostId ==. c ^. CommentPostId
    pure (u, p, c)

This query returns a User, with all of the users Posts, and then all of the Comments on that post.

First, we *nest* the tuple.

nest :: (a, b, c) -> (a, (b, c))
nest (a, b, c) = (a, (b, c))

This makes the return of the query conform to the input expected from associateJoin.

nestedUserPostComments
    :: SqlPersistT IO [(Entity User, (Entity Post, Entity Comment))]
nestedUserPostComments =
    fmap nest $ select userPostsComments

Now, we can call associateJoin on it.

associateUsers
    :: [(Entity User, (Entity Post, Entity Comment))]
    -> Map UserId (User, [(Entity Post, Entity Comment)])
associateUsers =
    associateJoin

Next, we'll use the Functor instances for Map and tuple to call associateJoin on the [(Entity Post, Entity Comment)].

associatePostsAndComments
    :: Map UserId (User, [(Entity Post, Entity Comment)])
    -> Map UserId (User, Map PostId (Post, [Entity Comment]))
associatePostsAndComments =
    fmap (fmap associateJoin)

For more reading on this topic, see this Foxhound Systems blog post.