The simple but tedious magic of allowing tuples to be
used.
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.
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.
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:
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' = countRowsorderBy [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:
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.
Deprecated. This function is deprecated, as it is only supported in Postgresql. Please use the function defined in Database.Esqueleto.PostgreSQL instead.
A convenience function that calls both distinctOn and
orderBy. In other words,
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 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.
Execute a subquery SELECT in a SqlExpr. This function is a shorthand
for the common joinV . subSelect idiom, where you are calling
subSelect on an expression that would be Maybe already.
As an example, you would use this function when calling sum_ or max_,
which have Maybe in the result type (for a 0 row query).
Execute a subquery SELECT in a SqlExpr that returns a list. This is an
alias for subList_select and is provided for symmetry with the other safe
subselect functions.
Execute a subquery SELECT in a SqlExpr. This function is unsafe,
because it can throw runtime exceptions in two cases:
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.
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.
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}
Analogous to Just, promotes a value of type typ into
one of type Maybe typ. It should hold that val . Just
=== just . val.
This function will try not to produce a nested Maybe. This is in accord
with how SQL represents NULL. That means that just . just = just.
This behavior was changed in v3.6.0.0. If you want to produce nested Maybe,
see just'.
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'
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.
COALESCE function. Evaluates the arguments in order and
returns the value of the first non-NULL SqlExpression, or NULL
(Nothing) otherwise. Some RDBMSs (such as SQLite) require
at least two arguments; please refer to the appropriate
documentation.
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:
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.
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.
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.
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
This instance allows you to use record.field notation with GC 9.2's
OverloadedRecordDot extension.
Example:
-- persistent model:
Person
name Text
BlogPost
title Text
authorId PersonId
-- query:
select $ do
(p :& bp) <- from $
tablePerson
leftJoin table BlogPost
on do
\(p :& bp) ->
just p.id ==. bp.authorId
pure (p.name, bp.title)
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).
Convert a constructor for a Unique key on a record to the UniqueDef
that defines it. You can supply just the constructor itself, or a value of
the type - the library is capable of figuring it out from there.
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.
select $ do
(p :& bp) <- from $
table Person
innerJoin table BlogPost
on do
(p :& bp) -> p ^. PersonId ==. b ^. BlogPostAuthorId
forUpdateOf (p :& b) skipLocked
return p
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
SqlStringHtmlDefined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
SqlStringByteStringDefined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
SqlStringTextDefined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
SqlStringTextDefined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
SqlStringa => SqlString (Maybea)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
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.
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.
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):
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.
Folks often want the ability to promote a Haskell function into the
SqlExpr expression language - and naturally reach for fmap.
Unfortunately, this is impossible. We cannot send *functions* to the
database, which is what we would need to do in order for this to make sense.
Let's consider the type of fmap for SqlExpr:
This type signature is making a pretty strong claim: "Give me a Haskell
function from a -> b. I will then transform a SQL expression representing
a Haskell value of type a and turn it into a SQL expression representing
a Haskell value of type b."
Let's suppose we *could* do this - fmap (+1) would have to somehow inspect
the function expression means "add one", and then translate that to the
appropriate SQL.
This is why esqueleto defines a bunch of operators: x +. (val 1) can
be used instead of fmap (+1) x.
This instance allows you to use record.field notation with GC 9.2's
OverloadedRecordDot extension.
Example:
-- persistent model:
Person
name Text
BlogPost
title Text
authorId PersonId
-- query:
select $ do
(p :& bp) <- from $
tablePerson
leftJoin table BlogPost
on do
\(p :& bp) ->
just p.id ==. bp.authorId
pure (p.name, bp.title)
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:
While veryUnsafeCoerceSqlExpr allows you to coerce anything at all, this
requires that the two types are Coercible in Haskell. This is not truly
safe: after all, the point of newtype is to allow you to provide different
instances of classes like PersistFieldSql and SqlSelect. Using this may
break your code if you change the underlying SQL representation.
StypetypeSqlExprFunctorMessage = (('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.
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
typ~typ' => NullableFieldProjectiontyptyp'Defined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
typ~typ' => NullableFieldProjection (Maybetyp) typ'Defined in esqueleto-3.6.0.3 · Database.Esqueleto.Internal.Internal
(Internal) Create a custom binary operator. You shouldnot 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.
(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).
Execute an esqueletoSELECT 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).
Execute an esqueletoDELETE query inside persistent's
SqlPersistT monad. Note that currently there are no type
checks for statements that should not appear on a DELETE
query.
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:
Execute an esqueletoUPDATE 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)
(Internal) Pretty prints a SqlQuery into a SQL query.
Note: if you're curious about the SQL query being generated by
esqueleto, instead of manually using this function (which is
possible but tedious), see the renderQueryToText function (along with
renderQuerySelect, renderQueryUpdate, etc).
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.
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.
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 fmapassociateJoin 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.
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.