Skip to content

Commit 9f97fae

Browse files
authored
3.6 fixups (#425)
* Re-export Nullable from ToMaybe * Fixity on ilike * lolwhoops * changelog link * add toBaseIdMaybe and fromBaseIdMaybe * start sketching out the sqlcoerce class * no sqlcoerce yet * ok for convenience
1 parent 1010b1e commit 9f97fae

File tree

7 files changed

+97
-3
lines changed

7 files changed

+97
-3
lines changed

changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,17 @@
6464
- The ability to `coerce` `SqlExpr` was removed. Instead, use
6565
`veryUnsafeCoerceSqlExpr`. See the documentation on
6666
`veryUnsafeCoerceSqlExpr` for safe use example.
67+
- `unsafeCeorceSqlExpr` is provided as an option when the underlying
68+
Haskell types are coercible. This is still unsafe, as different
69+
`PersistFieldSql` instances may be at play.
6770
- [#420](https://github.com/bitemyapp/esqueleto/pull/421)
6871
- The `LockingKind` constructors are deprecated, and will be removed
6972
from non-Internal modules in a future release. Smart constructors
7073
replace them, and you may need to import them from a different
7174
database-specific module.
75+
- [#425](https://github.com/bitemyapp/esqueleto/pull/425)
76+
- `fromBaseId` is introduced as the inverse of `toBaseId`.
77+
- `toBaseIdMaybe` and `fromBaseIdMaybe` are introduced.
7278
7379
3.5.14.0
7480
========

src/Database/Esqueleto.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ module Database.Esqueleto {-# WARNING "This module will switch over to the Exper
6666
, subList_select, valList, justList
6767
, in_, notIn, exists, notExists
6868
, set, (=.), (+=.), (-=.), (*=.), (/=.)
69-
, case_, toBaseId
69+
, case_, toBaseId, fromBaseId, toBaseIdMaybe, fromBaseIdMaybe
7070
, subSelect
7171
, subSelectMaybe
7272
, subSelectCount

src/Database/Esqueleto/Experimental.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ module Database.Esqueleto.Experimental
5454
, ToAliasReference(..)
5555
, ToSqlSetOperation(..)
5656
, SqlSelect
57+
, Nullable
5758

5859
-- * The Normal Stuff
5960
, where_
@@ -153,6 +154,9 @@ module Database.Esqueleto.Experimental
153154

154155
, case_
155156
, toBaseId
157+
, toBaseIdMaybe
158+
, fromBaseId
159+
, fromBaseIdMaybe
156160
, subSelect
157161
, subSelectMaybe
158162
, subSelectCount

src/Database/Esqueleto/Experimental/ToMaybe.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
{-# LANGUAGE TypeFamilies #-}
33

44
module Database.Esqueleto.Experimental.ToMaybe
5+
( module Database.Esqueleto.Experimental.ToMaybe
6+
, Nullable
7+
)
58
where
69

710
import Database.Esqueleto.Internal.Internal hiding (From(..), from, on)

src/Database/Esqueleto/Internal/Internal.hs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# LANGUAGE CPP #-}
2+
{-# language AllowAmbiguousTypes #-}
23
{-# LANGUAGE RoleAnnotations #-}
34
{-# LANGUAGE DeriveFunctor #-}
45
{-# LANGUAGE DataKinds #-}
@@ -36,6 +37,8 @@
3637
-- tracker so we can safely support it.
3738
module Database.Esqueleto.Internal.Internal where
3839

40+
import Data.Typeable (TypeRep, typeRep)
41+
import Data.Coerce (Coercible)
3942
import Control.Applicative ((<|>))
4043
import Control.Arrow (first, (***))
4144
import Control.Exception (Exception, throw, throwIO)
@@ -1283,6 +1286,65 @@ case_ = unsafeSqlCase
12831286
toBaseId :: ToBaseId ent => SqlExpr (Value (Key ent)) -> SqlExpr (Value (Key (BaseEnt ent)))
12841287
toBaseId = veryUnsafeCoerceSqlExprValue
12851288

1289+
-- | Like 'toBaseId', but works on 'Maybe' keys.
1290+
--
1291+
-- @since 3.6.0.0
1292+
toBaseIdMaybe
1293+
:: (ToBaseId ent)
1294+
=> SqlExpr (Value (Maybe (Key ent)))
1295+
-> SqlExpr (Value (Maybe (Key (BaseEnt ent))))
1296+
toBaseIdMaybe = veryUnsafeCoerceSqlExprValue
1297+
1298+
-- | The inverse of 'toBaseId'. Note that this is somewhat less "safe" than
1299+
-- 'toBaseId'. Calling 'toBaseId' will usually mean that a foreign key
1300+
-- constraint is present that guarantees the presence of the base ID.
1301+
-- 'fromBaseId' has no such guarantee. Consider the code example given in
1302+
-- 'toBaseId':
1303+
--
1304+
-- @
1305+
-- Bar
1306+
-- barNum Int
1307+
-- Foo
1308+
-- bar BarId
1309+
-- fooNum Int
1310+
-- Primary bar
1311+
-- @
1312+
--
1313+
-- @
1314+
-- instance ToBaseId Foo where
1315+
-- type BaseEnt Foo = Bar
1316+
-- toBaseIdWitness barId = FooKey barId
1317+
-- @
1318+
--
1319+
-- The type of 'toBaseId' for @Foo@ would be:
1320+
--
1321+
-- @
1322+
-- toBaseId :: SqlExpr (Value FooId) -> SqlExpr (Value BarId)
1323+
-- @
1324+
--
1325+
-- The foreign key constraint on @Foo@ means that every @FooId@ points to
1326+
-- a @BarId@ in the database. However, 'fromBaseId' will not have this:
1327+
--
1328+
-- @
1329+
-- fromBaseId :: SqlExpr (Value BarId) -> SqlExpr (Value FooId)
1330+
-- @
1331+
--
1332+
-- @since 3.6.0.0
1333+
fromBaseId
1334+
:: (ToBaseId ent)
1335+
=> SqlExpr (Value (Key (BaseEnt ent)))
1336+
-> SqlExpr (Value (Key ent))
1337+
fromBaseId = veryUnsafeCoerceSqlExprValue
1338+
1339+
-- | As 'fromBaseId', but works on 'Maybe' keys.
1340+
--
1341+
-- @since 3.6.0.0
1342+
fromBaseIdMaybe
1343+
:: (ToBaseId ent)
1344+
=> SqlExpr (Value (Maybe (Key (BaseEnt ent))))
1345+
-> SqlExpr (Value (Maybe (Key ent)))
1346+
fromBaseIdMaybe = veryUnsafeCoerceSqlExprValue
1347+
12861348
-- Fixity declarations
12871349
infixl 9 ^., ?.
12881350
infixl 7 *., /.
@@ -2451,6 +2513,23 @@ type role SqlExpr nominal
24512513
veryUnsafeCoerceSqlExpr :: SqlExpr a -> SqlExpr b
24522514
veryUnsafeCoerceSqlExpr (ERaw m k) = ERaw m k
24532515

2516+
-- | While 'veryUnsafeCoerceSqlExpr' allows you to coerce anything at all, this
2517+
-- requires that the two types are 'Coercible' in Haskell. This is not truly
2518+
-- safe: after all, the point of @newtype@ is to allow you to provide different
2519+
-- instances of classes like 'PersistFieldSql' and 'SqlSelect'. Using this may
2520+
-- break your code if you change the underlying SQL representation.
2521+
--
2522+
-- @since 3.6.0.0
2523+
unsafeCoerceSqlExpr :: (Coercible a b) => SqlExpr a -> SqlExpr b
2524+
unsafeCoerceSqlExpr = veryUnsafeCoerceSqlExpr
2525+
2526+
-- | Like 'unsafeCoerceSqlExpr' but for the common case where you are
2527+
-- coercing a 'Value'.
2528+
--
2529+
-- @since 3.6.0.0
2530+
unsafeCoerceSqlExprValue :: (Coercible a b) => SqlExpr (Value a) -> SqlExpr (Value b)
2531+
unsafeCoerceSqlExprValue = veryUnsafeCoerceSqlExpr
2532+
24542533
-- | Folks often want the ability to promote a Haskell function into the
24552534
-- 'SqlExpr' expression language - and naturally reach for 'fmap'.
24562535
-- Unfortunately, this is impossible. We cannot send *functions* to the

src/Database/Esqueleto/Legacy.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ module Database.Esqueleto.Legacy
6767
, subList_select, valList, justList
6868
, in_, notIn, exists, notExists
6969
, set, (=.), (+=.), (-=.), (*=.), (/=.)
70-
, case_, toBaseId
70+
, case_, toBaseId, fromBaseId, fromBaseIdMaybe, toBaseIdMaybe
7171
, subSelect
7272
, subSelectMaybe
7373
, subSelectCount

src/Database/Esqueleto/PostgreSQL.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ distinctOn exprs = Q (W.tell mempty { sdDistinctClause = DistinctOn exprs })
137137
-- ...
138138
-- @
139139
--
140-
-- @since 3.6.0
140+
-- @since 3.6.0.0
141141
distinctOnOrderBy :: [SqlExpr OrderBy] -> SqlQuery ()
142142
distinctOnOrderBy exprs = do
143143
distinctOn (toDistinctOn <$> exprs)
@@ -151,6 +151,7 @@ distinctOnOrderBy exprs = do
151151
$ TL.replace " ASC" ""
152152
$ TLB.toLazyText b
153153
, vals )
154+
154155
-- | Empty array literal. (@val []@) does unfortunately not work
155156
emptyArray :: SqlExpr (Value [a])
156157
emptyArray = unsafeSqlValue "'{}'"
@@ -680,6 +681,7 @@ forKeyShareOf lockableEntities onLockedBehavior =
680681
-- @since 2.2.3
681682
ilike :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -> SqlExpr (Value Bool)
682683
ilike = unsafeSqlBinOp " ILIKE "
684+
infixr 2 `ilike`
683685

684686
-- | @WITH@ @MATERIALIZED@ clause is used to introduce a
685687
-- [Common Table Expression (CTE)](https://en.wikipedia.org/wiki/Hierarchical_and_recursive_queries_in_SQL#Common_table_expression)

0 commit comments

Comments
 (0)