-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Persistent based event sourcing. -- -- Event source library for persistent databases. @package persistent-event-source @version 0.1.0 -- | Classy shim around Database.Esqueleto.Legacy -- -- In the style of Database.Persist.Monad, this exposes a -- "classy" (typeclass-using) API for Esqueleto functions, allowing them -- to be used with MonadSqlQuery constraints rather than a -- ReaderT SqlBackend concrete type. -- -- The goal of this module is to be a drop-in replacement for -- Database.Esqueleto.Legacy. module Database.Esqueleto.Monad.Legacy -- | Classy version of select select :: (MonadSqlQuery m, SqlSelect a r) => SqlQuery a -> m [r] -- | Classy version of selectOne selectOne :: (MonadSqlQuery m, SqlSelect a r) => SqlQuery a -> m (Maybe r) -- | Classy version of delete delete :: MonadSqlQuery m => SqlQuery () -> m () -- | Classy version of update update :: (MonadSqlQuery m, PersistEntity val, BackendCompatible SqlBackend (PersistEntityBackend val)) => (SqlExpr (Entity val) -> SqlQuery ()) -> m () renderQuerySelect :: (MonadSqlQuery m, SqlSelect a r) => SqlQuery a -> m (Text, [PersistValue]) -- | The lifted version of deleteWhere deleteWhere :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => [Filter record] -> m () -- | The lifted version of get get :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => Key record -> m (Maybe record) -- | The lifted version of getBy getBy :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => Unique record -> m (Maybe (Entity record)) -- | The lifted version of getEntity getEntity :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => Key record -> m (Maybe (Entity record)) -- | The lifted version of getMany getMany :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => [Key record] -> m (Map (Key record) record) -- | The lifted version of insert insert :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => record -> m (Key record) -- | The lifted version of insert_ insert_ :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => record -> m () -- | The lifted version of insertKey insertKey :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => Key record -> record -> m () -- | The lifted version of insertMany_ insertMany_ :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => [record] -> m () -- | The lifted version of insertEntityMany insertEntityMany :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => [Entity record] -> m () -- | The lifted version of selectFirst selectFirst :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => [Filter record] -> [SelectOpt record] -> m (Maybe (Entity record)) -- | The lifted version of updateWhere updateWhere :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => [Filter record] -> [Update record] -> m () -- | 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. associateJoin :: forall e1 e0. Ord (Key e0) => [(Entity e0, e1)] -> Map (Key e0) (e0, [e1]) -- | Synonym for delete that does not clash with -- esqueleto's delete. deleteKey :: forall backend val (m :: Type -> Type). (PersistStore backend, BaseBackend backend ~ PersistEntityBackend val, MonadIO m, PersistEntity val) => Key val -> ReaderT backend m () -- | 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). valJ :: PersistField (Key entity) => Value (Key entity) -> SqlExpr (Value (Key entity)) -- | valkey i = val . toSqlKey -- (https://github.com/prowdsponsor/esqueleto/issues/9). valkey :: (ToBackendKey SqlBackend entity, PersistField (Key entity)) => Int64 -> SqlExpr (Value (Key entity)) -- | Insert a PersistField for every selected value, return the -- count afterward insertSelectCount :: forall (m :: Type -> Type) a backend. (MonadIO m, PersistEntity a, SqlBackendCanWrite backend) => SqlQuery (SqlExpr (Insertion a)) -> ReaderT backend m Int64 -- | Insert a PersistField for every selected value. insertSelect :: forall (m :: Type -> Type) a backend. (MonadIO m, PersistEntity a, SqlBackendCanWrite backend) => SqlQuery (SqlExpr (Insertion a)) -> ReaderT backend m () -- | Renders a SqlQuery into a Text value along with the -- list of PersistValues that would be supplied to the database -- for ? placeholders. renderQueryInsertInto :: forall a r backend (m :: Type -> Type). (SqlSelect a r, BackendCompatible SqlBackend backend, Monad m) => SqlQuery a -> 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. renderQueryUpdate :: forall a r backend (m :: Type -> Type). (SqlSelect a r, BackendCompatible SqlBackend backend, Monad m) => SqlQuery a -> 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. renderQueryDelete :: forall a r backend (m :: Type -> Type). (SqlSelect a r, BackendCompatible SqlBackend backend, Monad m) => SqlQuery a -> 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. renderQueryToText :: forall a r backend (m :: Type -> Type). (SqlSelect a r, BackendCompatible SqlBackend backend, Monad m) => Mode -> SqlQuery a -> ReaderT backend m (Text, [PersistValue]) -- | Same as update, but returns the number of rows affected. updateCount :: forall (m :: Type -> Type) val backend. (MonadIO m, PersistEntity val, BackendCompatible SqlBackend (PersistEntityBackend val), SqlBackendCanWrite backend) => (SqlExpr (Entity val) -> SqlQuery ()) -> ReaderT backend m Int64 -- | Same as delete, but returns the number of rows affected. deleteCount :: forall (m :: Type -> Type) backend. (MonadIO m, SqlBackendCanWrite backend) => SqlQuery () -> ReaderT backend m Int64 -- | Execute an esqueleto SELECT query inside -- persistent's SqlPersistT monad and return a -- Source of rows. selectSource :: forall a r backend (m :: Type -> Type). (SqlSelect a r, BackendCompatible SqlBackend backend, IsPersistBackend backend, PersistQueryRead backend, PersistStoreRead backend, PersistUniqueRead backend, MonadResource m) => SqlQuery a -> ConduitT () r (ReaderT backend m) () -- | 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: -- --
-- 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. from :: From a => (a -> SqlQuery b) -> SqlQuery b -- | Syntax sugar for case_. else_ :: expr a -> expr a -- | Syntax sugar for case_. then_ :: () -- | Syntax sugar for case_. when_ :: expr (Value Bool) -> () -> expr a -> (expr (Value Bool), expr a) -- | 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. toBaseId :: ToBaseId ent => SqlExpr (Value (Key ent)) -> SqlExpr (Value (Key (BaseEnt ent))) -- | CASE statement. For example: -- --
-- select $ -- return $ -- case_ -- [ when_ -- (exists $ -- from $ \p -> do -- where_ (p ^. PersonName ==. val "Mike")) -- then_ -- (sub_select $ -- from $ \v -> do -- let sub = -- from $ \c -> do -- where_ (c ^. PersonName ==. val "Mike") -- return (c ^. PersonFavNum) -- where_ (v ^. PersonFavNum >. sub_select 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. -- --
-- select $ -- from $ \person -> do -- where_ $ exists $ -- from $ \post -> do -- where_ (post ^. BlogPostAuthorId ==. person ^. PersonId) -- return person --exists :: SqlQuery () -> SqlExpr (Value Bool) -- | NOT IN operator. notIn :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (ValueList typ) -> 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]. in_ :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (ValueList typ) -> SqlExpr (Value Bool) -- | Same as just but for ValueList. Most of the time you -- won't need it, though, because you can use just from inside -- subList_select or Just from inside valList. justList :: SqlExpr (ValueList typ) -> SqlExpr (ValueList (Maybe typ)) -- | Lift a list of constant value from Haskell-land to the query. valList :: PersistField typ => [typ] -> SqlExpr (ValueList typ) -- | Execute a subquery SELECT in an SqlExpression. Returns a list -- of values. subList_select :: PersistField a => SqlQuery (SqlExpr (Value a)) -> SqlExpr (ValueList a) -- | 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. castString :: (SqlString s, SqlString r) => SqlExpr (Value s) -> SqlExpr (Value r) -- | The || string concatenation operator (named after Haskell's -- ++ in order to avoid naming clash with ||.). Supported -- by SQLite and PostgreSQL. (++.) :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -> SqlExpr (Value s) infixr 5 ++. -- | The CONCAT function with a variable number of parameters. -- Supported by MySQL and PostgreSQL. concat_ :: SqlString s => [SqlExpr (Value 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" ++. (%) --(%) :: SqlString s => SqlExpr (Value s) -- | ILIKE operator (case-insensitive LIKE). -- -- Supported by PostgreSQL only. ilike :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -> SqlExpr (Value Bool) infixr 2 `ilike` -- | LIKE operator. like :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -> SqlExpr (Value Bool) infixr 2 `like` -- | RIGHT function. @since 3.3.0 right_ :: (SqlString s, Num a) => (SqlExpr (Value s), SqlExpr (Value a)) -> SqlExpr (Value s) -- | LEFT function. @since 3.3.0 left_ :: (SqlString s, Num a) => (SqlExpr (Value s), SqlExpr (Value a)) -> SqlExpr (Value s) -- | LENGTH function. @since 3.3.0 length_ :: (SqlString s, Num a) => SqlExpr (Value s) -> SqlExpr (Value a) -- | LTRIM function. @since 3.3.0 ltrim_ :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -- | RTRIM function. @since 3.3.0 rtrim_ :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -- | TRIM function. @since 3.3.0 trim_ :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -- | UPPER function. @since 3.3.0 upper_ :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -- | LOWER function. lower_ :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -- | Like coalesce, but takes a non-nullable SqlExpression placed -- at the end of the SqlExpression list, which guarantees a non-NULL -- result. coalesceDefault :: PersistField a => [SqlExpr (Value (Maybe a))] -> SqlExpr (Value a) -> SqlExpr (Value a) -- | 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. coalesce :: PersistField a => [SqlExpr (Value (Maybe a))] -> SqlExpr (Value (Maybe a)) -- | Same as castNum, but for nullable values. castNumM :: (Num a, Num b) => SqlExpr (Value (Maybe a)) -> SqlExpr (Value (Maybe 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. castNum :: (Num a, Num b) => SqlExpr (Value a) -> SqlExpr (Value b) avg_ :: (PersistField a, PersistField b) => SqlExpr (Value a) -> SqlExpr (Value (Maybe b)) max_ :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value (Maybe a)) min_ :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value (Maybe a)) sum_ :: (PersistField a, PersistField b) => SqlExpr (Value a) -> SqlExpr (Value (Maybe b)) floor_ :: (PersistField a, Num a, PersistField b, Num b) => SqlExpr (Value a) -> SqlExpr (Value b) ceiling_ :: (PersistField a, Num a, PersistField b, Num b) => SqlExpr (Value a) -> SqlExpr (Value b) round_ :: (PersistField a, Num a, PersistField b, Num b) => SqlExpr (Value a) -> SqlExpr (Value b) random_ :: (PersistField a, Num a) => SqlExpr (Value a) -- | BETWEEN. -- -- @since: 3.1.0 between :: PersistField a => SqlExpr (Value a) -> (SqlExpr (Value a), SqlExpr (Value a)) -> SqlExpr (Value Bool) (*.) :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value a) -> SqlExpr (Value a) infixl 7 *. (/.) :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value a) -> SqlExpr (Value a) infixl 7 /. (-.) :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value a) -> SqlExpr (Value a) infixl 6 -. (+.) :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value a) -> SqlExpr (Value a) infixl 6 +. (||.) :: SqlExpr (Value Bool) -> SqlExpr (Value Bool) -> SqlExpr (Value Bool) infixr 2 ||. (&&.) :: SqlExpr (Value Bool) -> SqlExpr (Value Bool) -> SqlExpr (Value Bool) infixr 3 &&. (!=.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool) infix 4 !=. (<.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool) infix 4 <. (<=.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool) infix 4 <=. (>.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool) infix 4 >. (>=.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool) infix 4 >=. (==.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool) infix 4 ==. not_ :: SqlExpr (Value Bool) -> SqlExpr (Value Bool) -- | COUNT(DISTINCT x). countDistinct :: Num a => SqlExpr (Value typ) -> SqlExpr (Value a) -- | COUNT. count :: Num a => SqlExpr (Value typ) -> SqlExpr (Value a) -- | COUNT(*) value. countRows :: Num a => SqlExpr (Value a) -- | Join nested Maybes in a Value into one. This is useful -- when calling aggregate functions on nullable fields. joinV :: SqlExpr (Value (Maybe (Maybe typ))) -> SqlExpr (Value (Maybe typ)) -- | NULL value. nothing :: SqlExpr (Value (Maybe typ)) -- | Analogous to Just, promotes a value of type typ into -- one of type Maybe typ. It should hold that val . -- Just === just . val. just :: SqlExpr (Value typ) -> SqlExpr (Value (Maybe typ)) -- | 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: -- -- TODO: table -- -- 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}
--
isNothing :: PersistField typ => SqlExpr (Value (Maybe typ)) -> SqlExpr (Value Bool)
-- | Lift a constant value from Haskell-land to the query.
val :: PersistField typ => typ -> SqlExpr (Value typ)
-- | Project a field of an entity that may be null.
(?.) :: (PersistEntity val, PersistField typ) => SqlExpr (Maybe (Entity val)) -> EntityField val typ -> SqlExpr (Value (Maybe typ))
-- | Project an SqlExpression that may be null, guarding against null
-- cases.
withNonNull :: PersistField typ => SqlExpr (Value (Maybe typ)) -> (SqlExpr (Value typ) -> SqlQuery a) -> SqlQuery a
-- | Project a field of an entity.
(^.) :: forall typ val. (PersistEntity val, PersistField typ) => SqlExpr (Entity val) -> EntityField val typ -> SqlExpr (Value typ)
infixl 9 ^.
-- | Execute a subquery SELECT in a SqlExpr. This function
-- is unsafe, because it can throw runtime exceptions in two cases:
--
-- -- 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) --subSelectForeign :: (BackendCompatible SqlBackend (PersistEntityBackend val1), PersistEntity val1, PersistEntity val2, PersistField a) => SqlExpr (Entity val2) -> EntityField val2 (Key val1) -> (SqlExpr (Entity val1) -> SqlExpr (Value a)) -> SqlExpr (Value a) -- | 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. subSelectList :: PersistField a => SqlQuery (SqlExpr (Value a)) -> SqlExpr (ValueList a) -- | Performs a COUNT of the given query in a subSelect -- manner. This is always guaranteed to return a result value, and is -- completely safe. subSelectCount :: (Num a, PersistField a) => SqlQuery ignored -> SqlExpr (Value a) -- | 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). subSelectMaybe :: PersistField a => SqlQuery (SqlExpr (Value (Maybe a))) -> SqlExpr (Value (Maybe a)) -- | 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. subSelect :: PersistField a => SqlQuery (SqlExpr (Value a)) -> SqlExpr (Value (Maybe a)) -- | Execute a subquery SELECT in an SqlExpression. Returns a -- simple value so should be used only when the SELECT query is -- guaranteed to return just one row. -- -- Deprecated in 3.2.0. sub_select :: PersistField a => SqlQuery (SqlExpr (Value a)) -> SqlExpr (Value a) -- | Add a locking clause to the query. Please read LockingKind -- documentation and your RDBMS manual. -- -- If multiple calls to locking are made on the same query, the -- last one is used. locking :: LockingKind -> SqlQuery () -- | HAVING. having :: SqlExpr (Value Bool) -> SqlQuery () -- | ORDER BY random() clause. rand :: SqlExpr OrderBy -- | A convenience function that calls both distinctOn and -- orderBy. In other words, -- --
-- distinctOnOrderBy [asc foo, desc bar, desc quux] $ do -- ... ---- -- is the same as: -- --
-- distinctOn [don foo, don bar, don quux] $ do -- orderBy [asc foo, desc bar, desc quux] -- ... --distinctOnOrderBy :: [SqlExpr OrderBy] -> SqlQuery a -> SqlQuery a -- | Erase an SqlExpression's type so that it's suitable to be used by -- distinctOn. don :: SqlExpr (Value a) -> SqlExpr DistinctOn -- | 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. distinctOn :: [SqlExpr DistinctOn] -> 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 ()) -- ... --distinct :: SqlQuery a -> SqlQuery a -- | OFFSET. Usually used with limit. offset :: Int64 -> SqlQuery () -- | LIMIT. Limit the number of returned rows. limit :: Int64 -> SqlQuery () -- | Descending order of this field or SqlExpression. desc :: PersistField a => SqlExpr (Value a) -> SqlExpr OrderBy -- | Ascending order of this field or SqlExpression. asc :: PersistField a => SqlExpr (Value a) -> SqlExpr OrderBy -- | ORDER BY clause. See also asc and desc. -- -- Multiple calls to orderBy get concatenated on the final query, -- including distinctOnOrderBy. orderBy :: [SqlExpr OrderBy] -> 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) ---- --
-- 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) --groupBy :: ToSomeValues a => a -> 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) -- ... --on :: SqlExpr (Value Bool) -> SqlQuery () -- | WHERE clause: restrict the query's result. where_ :: SqlExpr (Value Bool) -> SqlQuery () -- | A single value (as opposed to a whole entity). You may use -- (^.) or (?.) to get a Value -- from an Entity. newtype Value a Value :: a -> Value a [unValue] :: Value a -> 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). newtype ValueList a ValueList :: a -> ValueList a -- | Data type that represents an INNER JOIN (see -- LeftOuterJoin for an example). data InnerJoin a b InnerJoin :: a -> b -> InnerJoin a b infixl 2 `InnerJoin` infixl 2 `InnerJoin` -- | Data type that represents a CROSS JOIN (see -- LeftOuterJoin for an example). data CrossJoin a b CrossJoin :: a -> b -> CrossJoin a b infixl 2 `CrossJoin` infixl 2 `CrossJoin` -- | 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. data LeftOuterJoin a b LeftOuterJoin :: a -> b -> LeftOuterJoin a b infixl 2 `LeftOuterJoin` infixl 2 `LeftOuterJoin` -- | Data type that represents a RIGHT OUTER JOIN (see -- LeftOuterJoin for an example). data RightOuterJoin a b RightOuterJoin :: a -> b -> RightOuterJoin a b infixl 2 `RightOuterJoin` infixl 2 `RightOuterJoin` -- | Data type that represents a FULL OUTER JOIN (see -- LeftOuterJoin for an example). data FullOuterJoin a b FullOuterJoin :: a -> b -> FullOuterJoin a b infixl 2 `FullOuterJoin` infixl 2 `FullOuterJoin` -- | (Internal) A kind of JOIN. data JoinKind -- |
-- INNER JOIN --InnerJoinKind :: JoinKind -- |
-- CROSS JOIN --CrossJoinKind :: JoinKind -- |
-- LEFT OUTER JOIN --LeftOuterJoinKind :: JoinKind -- |
-- RIGHT OUTER JOIN --RightOuterJoinKind :: JoinKind -- |
-- FULL OUTER JOIN --FullOuterJoinKind :: JoinKind -- | Exception thrown whenever on is used to create an ON -- clause but no matching JOIN is found. data OnClauseWithoutMatchingJoinException OnClauseWithoutMatchingJoinException :: String -> OnClauseWithoutMatchingJoinException -- | Phantom type used by orderBy, asc and desc. data OrderBy -- | Phantom type used by distinctOn and don. data DistinctOn -- | 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. data LockingKind -- | FOR UPDATE syntax. Supported by MySQL, Oracle and PostgreSQL. ForUpdate :: LockingKind -- | FOR UPDATE SKIP LOCKED syntax. Supported by MySQL, Oracle and -- PostgreSQL. ForUpdateSkipLocked :: LockingKind -- | FOR SHARE syntax. Supported by PostgreSQL. ForShare :: LockingKind -- | LOCK IN SHARE MODE syntax. Supported by MySQL. LockInShareMode :: LockingKind -- | 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. class PersistField a => SqlString a -- | e.g. type BaseEnt MyBase = MyChild type family BaseEnt ent -- | Class that enables one to use toBaseId to convert an entity's -- key on a query into another (cf. toBaseId). class ToBaseId ent where { -- | e.g. type BaseEnt MyBase = MyChild type family BaseEnt 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. toBaseIdWitness :: ToBaseId ent => Key (BaseEnt ent) -> Key ent -- | (Internal) Class that implements the tuple from magic (see -- fromStart). class From a -- | SQL backend for esqueleto using SqlPersistT. data SqlQuery a -- | Constraint synonym for persistent entities whose backend is -- SqlBackend. type SqlEntity ent = (PersistEntity ent, PersistEntityBackend ent ~ SqlBackend) -- | 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. data SqlExpr a -- | Represents a value containing all the configuration options for a -- specific backend. This abstraction makes it easier to write code that -- can easily swap backends. class PersistConfig c where { type family PersistConfigBackend c :: Type -> Type -> Type -> Type; type family PersistConfigPool c; } -- | Load the config settings from a Value, most likely taken from a -- YAML config file. loadConfig :: PersistConfig c => Value -> Parser c -- | Modify the config settings based on environment variables. applyEnv :: PersistConfig c => c -> IO c -- | Create a new connection pool based on the given config settings. createPoolConfig :: PersistConfig c => c -> IO (PersistConfigPool c) -- | Run a database action by taking a connection from the pool. runPool :: (PersistConfig c, MonadUnliftIO m) => c -> PersistConfigBackend c m a -> PersistConfigPool c -> m a type family PersistConfigBackend c :: Type -> Type -> Type -> Type type family PersistConfigPool c -- | An ConstraintNameHS represents the Haskell-side name that -- persistent will use for a constraint. newtype ConstraintNameHS ConstraintNameHS :: Text -> ConstraintNameHS [unConstraintNameHS] :: ConstraintNameHS -> Text -- | A ConstraintNameDB represents the datastore-side name that -- persistent will use for a constraint. newtype ConstraintNameDB ConstraintNameDB :: Text -> ConstraintNameDB [unConstraintNameDB] :: ConstraintNameDB -> Text -- | An EntityNameDB represents the datastore-side name that -- persistent will use for an entity. newtype EntityNameDB EntityNameDB :: Text -> EntityNameDB [unEntityNameDB] :: EntityNameDB -> Text -- | An EntityNameHS represents the Haskell-side name that -- persistent will use for an entity. newtype EntityNameHS EntityNameHS :: Text -> EntityNameHS [unEntityNameHS] :: EntityNameHS -> Text -- | A FieldNameHS represents the Haskell-side name that -- persistent will use for a field. newtype FieldNameHS FieldNameHS :: Text -> FieldNameHS [unFieldNameHS] :: FieldNameHS -> Text -- | An EntityNameDB represents the datastore-side name that -- persistent will use for an entity. newtype FieldNameDB FieldNameDB :: Text -> FieldNameDB [unFieldNameDB] :: FieldNameDB -> Text -- | Convenience operations for working with '-NameDB' types. class DatabaseName a escapeWith :: DatabaseName a => (Text -> str) -> a -> str -- | A type that determines how a backend should handle the literal. data LiteralType -- | The accompanying value will be escaped before inserting into the -- database. This is the correct default choice to use. Escaped :: LiteralType -- | The accompanying value will not be escaped when inserting into the -- database. This is potentially dangerous - use this with care. Unescaped :: LiteralType -- | The DbSpecific constructor corresponds to the legacy -- PersistDbSpecific constructor. We need to keep this around -- because old databases may have serialized JSON representations that -- reference this. We don't want to break the ability of a database to -- load rows. DbSpecific :: LiteralType -- | A raw value which can be stored in any backend and can be marshalled -- to and from a PersistField. data PersistValue PersistText :: Text -> PersistValue PersistByteString :: ByteString -> PersistValue PersistInt64 :: Int64 -> PersistValue PersistDouble :: Double -> PersistValue PersistRational :: Rational -> PersistValue PersistBool :: Bool -> PersistValue PersistDay :: Day -> PersistValue PersistTimeOfDay :: TimeOfDay -> PersistValue PersistUTCTime :: UTCTime -> PersistValue PersistNull :: PersistValue PersistList :: [PersistValue] -> PersistValue PersistMap :: [(Text, PersistValue)] -> PersistValue -- | Intended especially for MongoDB backend PersistObjectId :: ByteString -> PersistValue -- | Intended especially for PostgreSQL backend for text arrays PersistArray :: [PersistValue] -> PersistValue -- | This constructor is used to specify some raw literal value for the -- backend. The LiteralType value specifies how the value should -- be escaped. This can be used to make special, custom types avaialable -- in the back end. PersistLiteral_ :: LiteralType -> ByteString -> PersistValue -- | This pattern synonym used to be a data constructor on -- PersistValue, but was changed into a catch-all pattern synonym -- to allow backwards compatiblity with database types. See the -- documentation on PersistDbSpecific for more details. pattern PersistLiteral :: ByteString -> PersistValue -- | This pattern synonym used to be a data constructor for the -- PersistValue type. It was changed to be a pattern so that -- JSON-encoded database values could be parsed into their corresponding -- values. You should not use this, and instead prefer to pattern match -- on PersistLiteral_ directly. -- -- If you use this, it will overlap a patern match on the -- 'PersistLiteral_, PersistLiteral, and -- PersistLiteralEscaped patterns. If you need to disambiguate -- between these constructors, pattern match on PersistLiteral_ -- directly. pattern PersistDbSpecific :: ByteString -> PersistValue -- | This pattern synonym used to be a data constructor on -- PersistValue, but was changed into a catch-all pattern synonym -- to allow backwards compatiblity with database types. See the -- documentation on PersistDbSpecific for more details. pattern PersistLiteralEscaped :: ByteString -> PersistValue fromPersistValueText :: PersistValue -> Either Text Text -- | Please refer to the documentation for the database in question for a -- full overview of the semantics of the varying isloation levels data IsolationLevel ReadUncommitted :: IsolationLevel ReadCommitted :: IsolationLevel RepeatableRead :: IsolationLevel Serializable :: IsolationLevel -- | A FieldDef represents the inormation that persistent -- knows about a field of a datatype. This includes information used to -- parse the field out of the database and what the field corresponds to. data FieldDef FieldDef :: !FieldNameHS -> !FieldNameDB -> !FieldType -> !SqlType -> ![FieldAttr] -> !Bool -> !ReferenceDef -> !FieldCascade -> !Maybe Text -> !Maybe Text -> !Bool -> FieldDef -- | The name of the field. Note that this does not corresponds to the -- record labels generated for the particular entity - record labels are -- generated with the type name prefixed to the field, so a -- FieldDef that contains a FieldNameHS "name" for -- a type User will have a record field userName. [fieldHaskell] :: FieldDef -> !FieldNameHS -- | The name of the field in the database. For SQL databases, this -- corresponds to the column name. [fieldDB] :: FieldDef -> !FieldNameDB -- | The type of the field in Haskell. [fieldType] :: FieldDef -> !FieldType -- | The type of the field in a SQL database. [fieldSqlType] :: FieldDef -> !SqlType -- | User annotations for a field. These are provided with the ! -- operator. [fieldAttrs] :: FieldDef -> ![FieldAttr] -- | If this is True, then the Haskell datatype will have a strict -- record field. The default value for this is True. [fieldStrict] :: FieldDef -> !Bool [fieldReference] :: FieldDef -> !ReferenceDef -- | Defines how operations on the field cascade on to the referenced -- tables. This doesn't have any meaning if the fieldReference is -- set to NoReference or SelfReference. The cascade option -- here should be the same as the one obtained in the -- fieldReference. [fieldCascade] :: FieldDef -> !FieldCascade -- | Optional comments for a Field. There is not currently a way -- to attach comments to a field in the quasiquoter. [fieldComments] :: FieldDef -> !Maybe Text -- | Whether or not the field is a GENERATED column, and -- additionally the expression to use for generation. [fieldGenerated] :: FieldDef -> !Maybe Text -- | True if the field is an implicit ID column. False -- otherwise. [fieldIsImplicitIdColumn] :: FieldDef -> !Bool data PersistUpdate Assign :: PersistUpdate Add :: PersistUpdate Subtract :: PersistUpdate Multiply :: PersistUpdate Divide :: PersistUpdate BackendSpecificUpdate :: Text -> PersistUpdate data UpdateException KeyNotFound :: String -> UpdateException UpsertError :: String -> UpdateException data PersistFilter Eq :: PersistFilter Ne :: PersistFilter Gt :: PersistFilter Lt :: PersistFilter Ge :: PersistFilter Le :: PersistFilter In :: PersistFilter NotIn :: PersistFilter -- | A SQL data type. Naming attempts to reflect the underlying Haskell -- datatypes, eg SqlString instead of SqlVarchar. Different SQL databases -- may have different translations for these types. data SqlType SqlString :: SqlType SqlInt32 :: SqlType SqlInt64 :: SqlType SqlReal :: SqlType SqlNumeric :: Word32 -> Word32 -> SqlType SqlBool :: SqlType SqlDay :: SqlType SqlTime :: SqlType -- | Always uses UTC timezone SqlDayTime :: SqlType SqlBlob :: SqlType -- | a backend-specific name SqlOther :: Text -> SqlType data PersistException -- | Generic Exception PersistError :: Text -> PersistException PersistMarshalError :: Text -> PersistException PersistInvalidField :: Text -> PersistException PersistForeignConstraintUnmet :: Text -> PersistException PersistMongoDBError :: Text -> PersistException PersistMongoDBUnsupported :: Text -> PersistException -- | An action that might happen on a deletion or update on a foreign key -- change. data CascadeAction Cascade :: CascadeAction Restrict :: CascadeAction SetNull :: CascadeAction SetDefault :: CascadeAction -- | This datatype describes how a foreign reference field cascades deletes -- or updates. -- -- This type is used in both parsing the model definitions and performing -- migrations. A Nothing in either of the field values means that -- the user has not specified a CascadeAction. An unspecified -- CascadeAction is defaulted to Restrict when doing -- migrations. data FieldCascade FieldCascade :: !Maybe CascadeAction -> !Maybe CascadeAction -> FieldCascade [fcOnUpdate] :: FieldCascade -> !Maybe CascadeAction [fcOnDelete] :: FieldCascade -> !Maybe CascadeAction data ForeignDef ForeignDef :: !EntityNameHS -> !EntityNameDB -> !ConstraintNameHS -> !ConstraintNameDB -> !FieldCascade -> ![(ForeignFieldDef, ForeignFieldDef)] -> ![Attr] -> Bool -> Bool -> ForeignDef [foreignRefTableHaskell] :: ForeignDef -> !EntityNameHS [foreignRefTableDBName] :: ForeignDef -> !EntityNameDB [foreignConstraintNameHaskell] :: ForeignDef -> !ConstraintNameHS [foreignConstraintNameDBName] :: ForeignDef -> !ConstraintNameDB -- | Determine how the field will cascade on updates and deletions. [foreignFieldCascade] :: ForeignDef -> !FieldCascade [foreignFields] :: ForeignDef -> ![(ForeignFieldDef, ForeignFieldDef)] [foreignAttrs] :: ForeignDef -> ![Attr] [foreignNullable] :: ForeignDef -> Bool -- | Determines if the reference is towards a Primary Key or not. [foreignToPrimary] :: ForeignDef -> Bool -- | Used instead of FieldDef to generate a smaller amount of code type ForeignFieldDef = (FieldNameHS, FieldNameDB) data CompositeDef CompositeDef :: !NonEmpty FieldDef -> ![Attr] -> CompositeDef [compositeFields] :: CompositeDef -> !NonEmpty FieldDef [compositeAttrs] :: CompositeDef -> ![Attr] -- | Type for storing the Uniqueness constraint in the Schema. Assume you -- have the following schema with a uniqueness constraint: -- --
-- Person -- name String -- age Int -- UniqueAge age ---- -- This will be represented as: -- --
-- UniqueDef
-- { uniqueHaskell = ConstraintNameHS (packPTH UniqueAge)
-- , uniqueDBName = ConstraintNameDB (packPTH "unique_age")
-- , uniqueFields = [(FieldNameHS (packPTH "age"), FieldNameDB (packPTH "age"))]
-- , uniqueAttrs = []
-- }
--
data UniqueDef
UniqueDef :: !ConstraintNameHS -> !ConstraintNameDB -> !NonEmpty (FieldNameHS, FieldNameDB) -> ![Attr] -> UniqueDef
[uniqueHaskell] :: UniqueDef -> !ConstraintNameHS
[uniqueDBName] :: UniqueDef -> !ConstraintNameDB
[uniqueFields] :: UniqueDef -> !NonEmpty (FieldNameHS, FieldNameDB)
[uniqueAttrs] :: UniqueDef -> ![Attr]
-- | An EmbedFieldDef is the same as a FieldDef But it is only used for
-- embeddedFields so it only has data needed for embedding
data EmbedFieldDef
EmbedFieldDef :: FieldNameDB -> Maybe (Either SelfEmbed EntityNameHS) -> EmbedFieldDef
[emFieldDB] :: EmbedFieldDef -> FieldNameDB
[emFieldEmbed] :: EmbedFieldDef -> Maybe (Either SelfEmbed EntityNameHS)
-- | An EmbedEntityDef is the same as an EntityDef But it is only used for
-- fieldReference so it only has data needed for embedding
data EmbedEntityDef
EmbedEntityDef :: EntityNameHS -> [EmbedFieldDef] -> EmbedEntityDef
[embeddedHaskell] :: EmbedEntityDef -> EntityNameHS
[embeddedFields] :: EmbedEntityDef -> [EmbedFieldDef]
-- | There are 3 kinds of references 1) composite (to fields that exist in
-- the record) 2) single field 3) embedded
data ReferenceDef
NoReference :: ReferenceDef
-- | A ForeignRef has a late binding to the EntityDef it references via
-- name and has the Haskell type of the foreign key in the form of
-- FieldType
ForeignRef :: !EntityNameHS -> ReferenceDef
EmbedRef :: EntityNameHS -> ReferenceDef
CompositeRef :: CompositeDef -> ReferenceDef
-- | A SelfReference stops an immediate cycle which causes non-termination
-- at compile-time (issue #311).
SelfReference :: ReferenceDef
-- | A FieldType describes a field parsed from the QuasiQuoter and
-- is used to determine the Haskell type in the generated code.
--
-- name Text parses into FTTypeCon Nothing Text
--
-- name T.Text parses into FTTypeCon (Just T
-- Text)
--
-- name (Jsonb User) parses into:
--
-- -- FTApp (FTTypeCon Nothing Jsonb) (FTTypeCon Nothing User) --data FieldType -- | Optional module and name. FTTypeCon :: Maybe Text -> Text -> FieldType FTTypePromoted :: Text -> FieldType FTApp :: FieldType -> FieldType -> FieldType FTList :: FieldType -> FieldType -- | Attributes that may be attached to fields that can affect migrations -- and serialization in backend-specific ways. -- -- While we endeavor to, we can't forsee all use cases for all backends, -- and so FieldAttr is extensible through its constructor -- FieldAttrOther. data FieldAttr -- | The Maybe keyword goes after the type. This indicates that the -- column is nullable, and the generated Haskell code will have a -- Maybe type for it. -- -- Example: -- --
-- User -- name Text Maybe --FieldAttrMaybe :: FieldAttr -- | This indicates that the column is nullable, but should not have a -- Maybe type. For this to work out, you need to ensure that the -- PersistField instance for the type in question can support a -- PersistNull value. -- --
-- data What = NoWhat | Hello Text -- -- instance PersistField What where -- fromPersistValue PersistNull = -- pure NoWhat -- fromPersistValue pv = -- Hello $ fromPersistValue pv -- -- instance PersistFieldSql What where -- sqlType _ = SqlString -- -- User -- what What nullable --FieldAttrNullable :: FieldAttr -- | This tag means that the column will not be present on the Haskell -- code, but will not be removed from the database. Useful to deprecate -- fields in phases. -- -- You should set the column to be nullable in the database. Otherwise, -- inserts won't have values. -- --
-- User -- oldName Text MigrationOnly -- newName Text --FieldAttrMigrationOnly :: FieldAttr -- | A SafeToRemove attribute is not present on the Haskell -- datatype, and the backend migrations should attempt to drop the column -- without triggering any unsafe migration warnings. -- -- Useful after you've used MigrationOnly to remove a column -- from the database in phases. -- --
-- User -- oldName Text SafeToRemove -- newName Text --FieldAttrSafeToRemove :: FieldAttr -- | This attribute indicates that we should create a foreign key reference -- from a column. By default, persistent will try and create a -- foreign key reference for a column if it can determine that the type -- of the column is a Key entity or an -- EntityId and the Entity's name was present in -- mkPersist. -- -- This is useful if you want to use the explicit foreign key syntax. -- --
-- Post -- title Text -- -- Comment -- postId PostId noreference -- Foreign Post fk_comment_post postId --FieldAttrNoreference :: FieldAttr -- | This is set to specify precisely the database table the column refers -- to. -- --
-- Post -- title Text -- -- Comment -- postId PostId references="post" ---- -- You should not need this - persistent should be capable of -- correctly determining the target table's name. If you do need this, -- please file an issue describing why. FieldAttrReference :: Text -> FieldAttr -- | Specify a name for the constraint on the foreign key reference for -- this table. -- --
-- Post -- title Text -- -- Comment -- postId PostId constraint="my_cool_constraint_name" --FieldAttrConstraint :: Text -> FieldAttr -- | Specify the default value for a column. -- --
-- User -- createdAt UTCTime default="NOW()" ---- -- Note that a default= attribute does not mean you can omit the -- value while inserting. FieldAttrDefault :: Text -> FieldAttr -- | Specify a custom SQL type for the column. Generally, you should define -- a custom datatype with a custom PersistFieldSql instance -- instead of using this. -- --
-- User -- uuid Text sqltype=UUID --FieldAttrSqltype :: Text -> FieldAttr -- | Set a maximum length for a column. Useful for VARCHAR and indexes. -- --
-- User -- name Text maxlen=200 -- -- UniqueName name --FieldAttrMaxlen :: Integer -> FieldAttr -- | Specify the database name of the column. -- --
-- User -- blarghle Int sql="b_l_a_r_g_h_l_e" ---- -- Useful for performing phased migrations, where one column is renamed -- to another column over time. FieldAttrSql :: Text -> FieldAttr -- | A grab bag of random attributes that were unrecognized by the parser. FieldAttrOther :: Text -> FieldAttr type Attr = Text type ExtraLine = [Text] -- | The definition for the entity's primary key ID. data EntityIdDef -- | The entity has a single key column, and it is a surrogate key - that -- is, you can't go from rec -> Key rec. EntityIdField :: !FieldDef -> EntityIdDef -- | The entity has a natural key. This means you can write rec -> -- Key rec because all the key fields are present on the datatype. -- -- A natural key can have one or more columns. EntityIdNaturalKey :: !CompositeDef -> EntityIdDef -- | An EntityDef represents the information that -- persistent knows about an Entity. It uses this information to -- generate the Haskell datatype, the SQL migrations, and other relevant -- conversions. data EntityDef -- | The reason why a field is nullable is very important. A field -- that is nullable because of a Maybe tag will have its type -- changed from A to Maybe A. OTOH, a field that is -- nullable because of a nullable tag will remain with the same -- type. data WhyNullable ByMaybeAttr :: WhyNullable ByNullableAttr :: WhyNullable data IsNullable Nullable :: !WhyNullable -> IsNullable NotNullable :: IsNullable -- | A Checkmark should be used as a field type whenever a -- uniqueness constraint should guarantee that a certain kind of record -- may appear at most once, but other kinds of records may appear any -- number of times. -- -- NOTE: You need to mark any Checkmark fields as -- nullable (see the following example). -- -- For example, suppose there's a Location entity that -- represents where a user has lived: -- --
-- Location -- user UserId -- name Text -- current Checkmark nullable -- -- UniqueLocation user current ---- -- The UniqueLocation constraint allows any number of -- Inactive Locations to be current. However, -- there may be at most one current Location per user -- (i.e., either zero or one per user). -- -- This data type works because of the way that SQL treats -- NULLable fields within uniqueness constraints. The SQL -- standard says that NULL values should be considered -- different, so we represent Inactive as SQL NULL, thus -- allowing any number of Inactive records. On the other hand, we -- represent Active as TRUE, so the uniqueness constraint -- will disallow more than one Active record. -- -- Note: There may be DBMSs that do not respect the SQL standard's -- treatment of NULL values on uniqueness constraints, please -- check if this data type works before relying on it. -- -- The SQL BOOLEAN type is used because it's the smallest data -- type available. Note that we never use FALSE, just -- TRUE and NULL. Provides the same behavior Maybe -- () would if () was a valid PersistField. data Checkmark -- | When used on a uniqueness constraint, there may be at most one -- Active record. Active :: Checkmark -- | When used on a uniqueness constraint, there may be any number of -- Inactive records. Inactive :: Checkmark fieldAttrsContainsNullable :: [FieldAttr] -> IsNullable -- | Return the [FieldDef] for the entity keys. entitiesPrimary :: EntityDef -> NonEmpty FieldDef entityPrimary :: EntityDef -> Maybe CompositeDef -- | Returns a NonEmpty list of FieldDef that correspond with -- the key columns for an EntityDef. keyAndEntityFields :: EntityDef -> NonEmpty FieldDef -- | Parse raw field attributes into structured form. Any unrecognized -- attributes will be preserved, identically as they are encountered, as -- FieldAttrOther values. parseFieldAttrs :: [Text] -> [FieldAttr] isFieldNotGenerated :: FieldDef -> Bool -- | Returns True if the FieldDef does not have a -- MigrationOnly or SafeToRemove flag from the -- QuasiQuoter. isHaskellField :: FieldDef -> Bool -- | A FieldCascade that does nothing. noCascade :: FieldCascade -- | Renders a FieldCascade value such that it can be used in SQL -- migrations. renderFieldCascade :: FieldCascade -> Text -- | Render a CascadeAction to Text such that it can be used -- in a SQL command. renderCascadeAction :: CascadeAction -> Text -- | A Statement is a representation of a database query that has -- been prepared and stored on the server side. data Statement Statement :: IO () -> IO () -> ([PersistValue] -> IO Int64) -> (forall (m :: Type -> Type). MonadIO m => [PersistValue] -> Acquire (ConduitM () [PersistValue] m ())) -> Statement [stmtFinalize] :: Statement -> IO () [stmtReset] :: Statement -> IO () [stmtExecute] :: Statement -> [PersistValue] -> IO Int64 [stmtQuery] :: Statement -> forall (m :: Type -> Type). MonadIO m => [PersistValue] -> Acquire (ConduitM () [PersistValue] m ()) data InsertSqlResult ISRSingle :: Text -> InsertSqlResult ISRInsertGet :: Text -> Text -> InsertSqlResult ISRManyKeys :: Text -> [PersistValue] -> InsertSqlResult type LogFunc = Loc -> LogSource -> LogLevel -> LogStr -> IO () -- | Replace the FieldDef FieldAttr with the new list. setFieldAttrs :: [FieldAttr] -> FieldDef -> FieldDef -- | Modify the list of field attributes. overFieldAttrs :: ([FieldAttr] -> [FieldAttr]) -> FieldDef -> FieldDef -- | Add an attribute to the list of field attributes. addFieldAttr :: FieldAttr -> FieldDef -> FieldDef -- | Check if the field definition is nullable isFieldNullable :: FieldDef -> IsNullable -- | Check if the field is `Maybe a` isFieldMaybe :: FieldDef -> Bool -- | Retrieve the list of UniqueDef from an EntityDef. This -- currently does not include a Primary key, if one is defined. -- A future version of persistent will include a -- Primary key among the Unique constructors for the -- Entity. getEntityUniques :: EntityDef -> [UniqueDef] -- | Retrieve the Haskell name of the given entity. getEntityHaskellName :: EntityDef -> EntityNameHS -- | Return the database name for the given entity. getEntityDBName :: EntityDef -> EntityNameDB getEntityExtra :: EntityDef -> Map Text [[Text]] setEntityDBName :: EntityNameDB -> EntityDef -> EntityDef getEntityComments :: EntityDef -> Maybe Text getEntityForeignDefs :: EntityDef -> [ForeignDef] -- | Retrieve the list of FieldDef that makes up the fields of the -- entity. -- -- This does not return the fields for an Id column or an -- implicit id. It will return the key columns if you used the -- Primary syntax for defining the primary key. -- -- This does not return fields that are marked SafeToRemove or -- MigrationOnly - so it only returns fields that are -- represented in the Haskell type. If you need those fields, use -- getEntityFieldsDatabase. getEntityFields :: EntityDef -> [FieldDef] -- | This returns all of the FieldDef defined for the -- EntityDef, including those fields that are marked as -- MigrationOnly (and therefore only present in the database) or -- SafeToRemove (and a migration will drop the column if it -- exists in the database). -- -- For all the fields that are present on the Haskell-type, see -- getEntityFields. getEntityFieldsDatabase :: EntityDef -> [FieldDef] isEntitySum :: EntityDef -> Bool getEntityId :: EntityDef -> EntityIdDef getEntityIdField :: EntityDef -> Maybe FieldDef -- | Set an entityId to be the given FieldDef. setEntityId :: FieldDef -> EntityDef -> EntityDef setEntityIdDef :: EntityIdDef -> EntityDef -> EntityDef getEntityKeyFields :: EntityDef -> NonEmpty FieldDef -- | Perform a mapping function over all of the entity fields, as -- determined by getEntityFieldsDatabase. overEntityFields :: ([FieldDef] -> [FieldDef]) -> EntityDef -> EntityDef data SomePersistField SomePersistField :: a -> SomePersistField -- | Prior to persistent-2.11.0, we provided an instance of -- PersistField for the Natural type. This was in error, -- because Natural represents an infinite value, and databases -- don't have reasonable types for this. -- -- The instance for Natural used the Int64 underlying type, -- which will cause underflow and overflow errors. This type has the -- exact same code in the instances, and will work seamlessly. -- -- A more appropriate type for this is the Word series of types -- from Data.Word. These have a bounded size, are guaranteed to be -- non-negative, and are quite efficient for the database to store. newtype OverflowNatural OverflowNatural :: Natural -> OverflowNatural [unOverflowNatural] :: OverflowNatural -> Natural -- | This class teaches Persistent how to take a custom type and marshal it -- to and from a PersistValue, allowing it to be stored in a -- database. -- --
-- {-# LANGUAGE GeneralizedNewtypeDeriving #-}
--
-- newtype HashedPassword = HashedPassword ByteString
-- deriving (Eq, Show, PersistField, PersistFieldSql)
--
--
--
-- {-# LANGUAGE GeneralizedNewtypeDeriving #-}
-- import qualified Data.Text as T
-- import qualified Data.Char as C
--
-- -- | An American Social Security Number
-- newtype SSN = SSN Text
-- deriving (Eq, Show, PersistFieldSql)
--
-- mkSSN :: Text -> Either Text SSN
-- mkSSN t = if (T.length t == 9) && (T.all C.isDigit t)
-- then Right $ SSN t
-- else Left $ "Invalid SSN: " <> t
--
-- instance PersistField SSN where
-- toPersistValue (SSN t) = PersistText t
-- fromPersistValue (PersistText t) = mkSSN t
-- -- Handle cases where the database does not give us PersistText
-- fromPersistValue x = Left $ "File.hs: When trying to deserialize an SSN: expected PersistText, received: " <> T.pack (show x)
--
--
-- Tips:
--
-- -- instance ToJSON (Entity User) where -- toJSON = keyValueEntityToJSON --keyValueEntityToJSON :: (PersistEntity record, ToJSON record) => Entity record -> Value -- | Predefined parseJSON. The input JSON looks like {"key": -- 1, "value": {"name": ...}}. -- -- The typical usage is: -- --
-- instance FromJSON (Entity User) where -- parseJSON = keyValueEntityFromJSON --keyValueEntityFromJSON :: (PersistEntity record, FromJSON record) => Value -> Parser (Entity record) -- | Predefined toJSON. The resulting JSON looks like {"id": -- 1, "name": ...}. -- -- The typical usage is: -- --
-- instance ToJSON (Entity User) where -- toJSON = entityIdToJSON --entityIdToJSON :: (PersistEntity record, ToJSON record) => Entity record -> Value -- | Predefined parseJSON. The input JSON looks like {"id": 1, -- "name": ...}. -- -- The typical usage is: -- --
-- instance FromJSON (Entity User) where -- parseJSON = entityIdFromJSON --entityIdFromJSON :: (PersistEntity record, FromJSON record) => Value -> Parser (Entity record) -- | Convenience function for getting a free PersistField instance -- from a type with JSON instances. -- -- Example usage in combination with fromPersistValueJSON: -- --
-- instance PersistField MyData where -- fromPersistValue = fromPersistValueJSON -- toPersistValue = toPersistValueJSON --toPersistValueJSON :: ToJSON a => a -> PersistValue -- | Convenience function for getting a free PersistField instance -- from a type with JSON instances. The JSON parser used will accept JSON -- values other that object and arrays. So, if your instance serializes -- the data to a JSON string, this will still work. -- -- Example usage in combination with toPersistValueJSON: -- --
-- instance PersistField MyData where -- fromPersistValue = fromPersistValueJSON -- toPersistValue = toPersistValueJSON --fromPersistValueJSON :: FromJSON a => PersistValue -> Either Text a class (Show BackendKey backend, Read BackendKey backend, Eq BackendKey backend, Ord BackendKey backend, PersistStoreRead backend, PersistField BackendKey backend, ToJSON BackendKey backend, FromJSON BackendKey backend) => PersistStoreWrite backend -- | Create multiple records in the database and return their Keys. -- -- If you don't need the inserted Keys, use insertMany_. -- -- The MongoDB and PostgreSQL backends insert all records and retrieve -- their keys in one database query. -- -- The SQLite and MySQL backends use the slow, default implementation of -- mapM insert. -- --
-- insertUsers :: MonadIO m => ReaderT SqlBackend m [Key User] -- insertUsers = insertMany [User "John" 30, User "Nick" 32, User "Jane" 20] ---- --
-- userIds <- insertUsers ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |SPJ |40 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ -- |3 |John |30 | -- +-----+------+-----+ -- |4 |Nick |32 | -- +-----+------+-----+ -- |5 |Jane |20 | -- +-----+------+-----+ --insertMany :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => [record] -> ReaderT backend m [Key record] -- | Put the record in the database with the given key. Unlike -- replace, if a record with the given key does not exist then a -- new record will be inserted. -- --
-- insertPhilip :: MonadIO m => ReaderT SqlBackend m (Key User) -- insertPhilip = insert $ User "Philip" 42 ---- --
-- philipId <- insertPhilip ---- -- This query will produce: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |SPJ |40 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ -- |3 |Philip|42 | -- +-----+------+-----+ ---- --
-- repsertHaskell :: MonadIO m => Key record -> ReaderT SqlBackend m () -- repsertHaskell id = repsert id $ User "Haskell" 81 ---- --
-- repsertHaskell philipId ---- -- This query will replace Philip's record with Haskell's one: -- --
-- +-----+-----------------+--------+ -- |id |name |age | -- +-----+-----------------+--------+ -- |1 |SPJ |40 | -- +-----+-----------------+--------+ -- |2 |Simon |41 | -- +-----+-----------------+--------+ -- |3 |Philip -> Haskell|42 -> 81| -- +-----+-----------------+--------+ ---- -- repsert inserts the given record if the key doesn't exist. -- --
-- repsertXToUnknown :: MonadIO m => ReaderT SqlBackend m () -- repsertXToUnknown = repsert unknownId $ User "X" 999 ---- -- For example, applying the above query to dataset-1 will produce -- this: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |SPJ |40 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ -- |3 |X |999 | -- +-----+------+-----+ --repsert :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => Key record -> record -> ReaderT backend m () -- | Put many entities into the database. -- -- Batch version of repsert for SQL backends. -- -- Useful when migrating data from one entity to another and want to -- preserve ids. -- --
-- repsertManyUsers :: MonadIO m =>ReaderT SqlBackend m () -- repsertManyusers = repsertMany [(simonId, User "Philip" 20), (unknownId999, User "Mr. X" 999)] ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+----------------+---------+ -- |id |name |age | -- +-----+----------------+---------+ -- |1 |SPJ |40 | -- +-----+----------------+---------+ -- |2 |Simon -> Philip |41 -> 20 | -- +-----+----------------+---------+ -- |999 |Mr. X |999 | -- +-----+----------------+---------+ --repsertMany :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => [(Key record, record)] -> ReaderT backend m () -- | Replace the record in the database with the given key. Note that the -- result is undefined if such record does not exist, so you must use -- insertKey or repsert in these cases. -- --
-- replaceSpj :: MonadIO m => User -> ReaderT SqlBackend m () -- replaceSpj record = replace spjId record ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |Mike |45 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ --replace :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => Key record -> record -> ReaderT backend m () -- | Update individual fields on a specific record, and retrieve the -- updated value from the database. -- -- Note that this function will throw an exception if the given key is -- not found in the database. -- --
-- updateGetSpj :: MonadIO m => [Update User] -> ReaderT SqlBackend m User -- updateGetSpj updates = updateGet spjId updates ---- --
-- spj <- updateGetSpj [UserAge +=. 100] ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |SPJ |140 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ --updateGet :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => Key record -> [Update record] -> ReaderT backend m record class (Show BackendKey backend, Read BackendKey backend, Eq BackendKey backend, Ord BackendKey backend, PersistCore backend, PersistField BackendKey backend, ToJSON BackendKey backend, FromJSON BackendKey backend) => PersistStoreRead backend class PersistCore backend where { data family BackendKey backend; } data family BackendKey backend -- | ToBackendKey converts a PersistEntity Key into a -- BackendKey This can be used by each backend to convert between -- a Key and a plain Haskell type. For Sql, that is done with -- toSqlKey and fromSqlKey. -- -- By default, a PersistEntity uses the default BackendKey -- for its Key and is an instance of ToBackendKey -- -- A Key that instead uses a custom type will not be an instance -- of ToBackendKey. class (PersistEntity record, PersistEntityBackend record ~ backend, PersistCore backend) => ToBackendKey backend record toBackendKey :: ToBackendKey backend record => Key record -> BackendKey backend fromBackendKey :: ToBackendKey backend record => BackendKey backend -> Key record -- | A convenient alias for common type signatures type PersistRecordBackend record backend = (PersistEntity record, PersistEntityBackend record ~ BaseBackend backend) -- | This class witnesses that two backend are compatible, and that you can -- convert from the sub backend into the sup backend. -- This is similar to the HasPersistBackend and -- IsPersistBackend classes, but where you don't want to fix the -- type associated with the PersistEntityBackend of a record. -- -- Generally speaking, where you might have: -- --
-- foo :: -- ( PersistEntity record -- , PeristEntityBackend record ~ BaseBackend backend -- , IsSqlBackend backend -- ) ---- -- this can be replaced with: -- --
-- foo :: -- ( PersistEntity record, -- , PersistEntityBackend record ~ backend -- , BackendCompatible SqlBackend backend -- ) ---- -- This works for SqlReadBackend because of the instance -- BackendCompatible SqlBackend -- SqlReadBackend, without needing to go through the -- BaseBackend type family. -- -- Likewise, functions that are currently hardcoded to use -- SqlBackend can be generalized: -- --
-- -- before: -- asdf :: ReaderT SqlBackend m () -- asdf = pure () -- -- -- after: -- asdf' :: BackendCompatible SqlBackend backend => ReaderT backend m () -- asdf' = withCompatibleBackend asdf --class BackendCompatible sup sub projectBackend :: BackendCompatible sup sub => sub -> sup -- | Class which witnesses that backend is essentially the same as -- BaseBackend backend. That is, they're isomorphic and -- backend is just some wrapper over BaseBackend -- backend. class HasPersistBackend backend => IsPersistBackend backend -- | Class which allows the plucking of a BaseBackend backend from -- some larger type. For example, instance HasPersistBackend -- (SqlReadBackend, Int) where type BaseBackend (SqlReadBackend, Int) = -- SqlBackend persistBackend = unSqlReadBackend . fst class HasPersistBackend backend where { type family BaseBackend backend; } persistBackend :: HasPersistBackend backend => backend -> BaseBackend backend type family BaseBackend backend -- | Run a query against a larger backend by plucking out BaseBackend -- backend -- -- This is a helper for reusing existing queries when expanding the -- backend type. withBaseBackend :: forall backend (m :: Type -> Type) a. HasPersistBackend backend => ReaderT (BaseBackend backend) m a -> ReaderT backend m a -- | Run a query against a compatible backend, by projecting the backend -- -- This is a helper for using queries which run against a specific -- backend type that your backend is compatible with. withCompatibleBackend :: forall sup sub (m :: Type -> Type) a. BackendCompatible sup sub => ReaderT sup m a -> ReaderT sub m a liftPersist :: (MonadIO m, MonadReader backend m) => ReaderT backend IO b -> m b -- | Same as get, but for a non-null (not Maybe) foreign key. Unsafe -- unless your database is enforcing that the foreign key is valid. -- --
-- getJustSpj :: MonadIO m => ReaderT SqlBackend m User -- getJustSpj = getJust spjId ---- --
-- spj <- getJust spjId ---- -- The above query when applied on dataset-1, will get this -- record: -- --
-- +----+------+-----+ -- | id | name | age | -- +----+------+-----+ -- | 1 | SPJ | 40 | -- +----+------+-----+ ---- --
-- getJustUnknown :: MonadIO m => ReaderT SqlBackend m User -- getJustUnknown = getJust unknownId ---- -- mrx <- getJustUnknown -- -- This just throws an error. getJust :: forall record backend (m :: Type -> Type). (PersistStoreRead backend, PersistRecordBackend record backend, MonadIO m) => Key record -> ReaderT backend m record -- | Same as getJust, but returns an Entity instead of just -- the record. -- --
-- getJustEntitySpj :: MonadIO m => ReaderT SqlBackend m (Entity User) -- getJustEntitySpj = getJustEntity spjId ---- --
-- spjEnt <- getJustEntitySpj ---- -- The above query when applied on dataset-1, will get this -- entity: -- --
-- +----+------+-----+ -- | id | name | age | -- +----+------+-----+ -- | 1 | SPJ | 40 | -- +----+------+-----+ --getJustEntity :: forall record backend (m :: Type -> Type). (PersistEntityBackend record ~ BaseBackend backend, MonadIO m, PersistEntity record, PersistStoreRead backend) => Key record -> ReaderT backend m (Entity record) -- | Curry this to make a convenience function that loads an associated -- model. -- --
-- foreign = belongsTo foreignId --belongsTo :: forall ent1 ent2 backend (m :: Type -> Type). (PersistStoreRead backend, PersistEntity ent1, PersistRecordBackend ent2 backend, MonadIO m) => (ent1 -> Maybe (Key ent2)) -> ent1 -> ReaderT backend m (Maybe ent2) -- | Same as belongsTo, but uses getJust and therefore is -- similarly unsafe. belongsToJust :: forall ent1 ent2 backend (m :: Type -> Type). (PersistStoreRead backend, PersistEntity ent1, PersistRecordBackend ent2 backend, MonadIO m) => (ent1 -> Key ent2) -> ent1 -> ReaderT backend m ent2 -- | Like insert, but returns the complete Entity. -- --
-- insertHaskellEntity :: MonadIO m => ReaderT SqlBackend m (Entity User) -- insertHaskellEntity = insertEntity $ User "Haskell" 81 ---- --
-- haskellEnt <- insertHaskellEntity ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +----+---------+-----+ -- | id | name | age | -- +----+---------+-----+ -- | 1 | SPJ | 40 | -- +----+---------+-----+ -- | 2 | Simon | 41 | -- +----+---------+-----+ -- | 3 | Haskell | 81 | -- +----+---------+-----+ --insertEntity :: forall e backend (m :: Type -> Type). (PersistStoreWrite backend, PersistRecordBackend e backend, MonadIO m) => e -> ReaderT backend m (Entity e) -- | Like insertEntity but just returns the record instead of -- Entity. -- --
-- insertDaveRecord :: MonadIO m => ReaderT SqlBackend m User -- insertDaveRecord = insertRecord $ User "Dave" 50 ---- --
-- dave <- insertDaveRecord ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |SPJ |40 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ -- |3 |Dave |50 | -- +-----+------+-----+ --insertRecord :: forall record backend (m :: Type -> Type). (PersistEntityBackend record ~ BaseBackend backend, PersistEntity record, MonadIO m, PersistStoreWrite backend) => record -> ReaderT backend m record -- | A SqlBackend represents a handle or connection to a database. -- It contains functions and values that allow databases to have more -- optimized implementations, as well as references that benefit -- performance and sharing. -- -- Instead of using the SqlBackend constructor directly, use the -- mkSqlBackend function. -- -- A SqlBackend is *not* thread-safe. You should not assume that a -- SqlBackend can be shared among threads and run concurrent -- queries. This *will* result in problems. Instead, you should create a -- Pool SqlBackend, known as a -- ConnectionPool, and pass that around in multi-threaded -- applications. -- -- To run actions in the persistent library, you should use the -- runSqlConn function. If you're using a multithreaded -- application, use the runSqlPool function. data SqlBackend -- | This class is used to ensure that functions requring at least one -- unique key are not called with records that have 0 unique keys. The -- quasiquoter automatically writes working instances for appropriate -- entities, and generates TypeError instances for records that -- have 0 unique keys. class PersistEntity record => AtLeastOneUniqueKey record requireUniquesP :: AtLeastOneUniqueKey record => record -> NonEmpty (Unique record) -- | This is an error message. It is used when an entity has multiple -- unique keys, and the function expects a single unique key. type MultipleUniqueKeysError ty = 'Text "The entity " :<>: 'ShowType ty :<>: 'Text " has multiple unique keys." :$$: 'Text "The function you are trying to call requires only a single " :<>: 'Text "unique key." :$$: 'Text "There is probably a variant of the function with 'By' " :<>: 'Text "appended that will allow you to select a unique key " :<>: 'Text "for the operation." -- | This is an error message. It is used when writing instances of -- OnlyOneUniqueKey for an entity that has no unique keys. type NoUniqueKeysError ty = 'Text "The entity " :<>: 'ShowType ty :<>: 'Text " does not have any unique keys." :$$: 'Text "The function you are trying to call requires a unique key " :<>: 'Text "to be defined on the entity." -- | This class is used to ensure that upsert is only called on -- records that have a single Unique key. The quasiquoter -- automatically generates working instances for appropriate records, and -- generates TypeError instances for records that have 0 or -- multiple unique keys. class PersistEntity record => OnlyOneUniqueKey record onlyUniqueP :: OnlyOneUniqueKey record => record -> Unique record -- | Some functions in this module (insertUnique, insertBy, -- and replaceUnique) first query the unique indexes to check for -- conflicts. You could instead optimistically attempt to perform the -- operation (e.g. replace instead of replaceUnique). -- However, -- --
-- deleteBySpjName :: MonadIO m => ReaderT SqlBackend m () -- deleteBySpjName = deleteBy UniqueUserName "SPJ" ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ --deleteBy :: forall record (m :: Type -> Type). (PersistUniqueWrite backend, MonadIO m, PersistRecordBackend record backend) => Unique record -> ReaderT backend m () -- | Like insert, but returns Nothing when the record -- couldn't be inserted because of a uniqueness constraint. -- --
-- linusId <- insertUnique $ User "Linus" 48 -- spjId <- insertUnique $ User "SPJ" 90 ---- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |SPJ |40 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ -- |3 |Linus |48 | -- +-----+------+-----+ ---- -- Linus's record was inserted to dataset-1, while SPJ wasn't -- because SPJ already exists in dataset-1. insertUnique :: forall record (m :: Type -> Type). (PersistUniqueWrite backend, MonadIO m, PersistRecordBackend record backend) => record -> ReaderT backend m (Maybe (Key record)) -- | Update based on a uniqueness constraint or insert: -- --
-- upsertSpj :: MonadIO m => [Update User] -> ReaderT SqlBackend m (Maybe (Entity User)) -- upsertSpj updates = upsert (User "SPJ" 999) upadtes ---- --
-- mSpjEnt <- upsertSpj [UserAge +=. 15] ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+-----+--------+ -- |id |name |age | -- +-----+-----+--------+ -- |1 |SPJ |40 -> 55| -- +-----+-----+--------+ -- |2 |Simon|41 | -- +-----+-----+--------+ ---- --
-- upsertX :: MonadIO m => [Update User] -> ReaderT SqlBackend m (Maybe (Entity User)) -- upsertX updates = upsert (User "X" 999) updates ---- --
-- mXEnt <- upsertX [UserAge +=. 15] ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+-----+--------+ -- |id |name |age | -- +-----+-----+--------+ -- |1 |SPJ |40 | -- +-----+-----+--------+ -- |2 |Simon|41 | -- +-----+-----+--------+ -- |3 |X |999 | -- +-----+-----+--------+ ---- -- Next, what if the schema has two uniqueness constraints? Let's check -- it out using schema-2: -- --
-- mSpjEnt <- upsertSpj [UserAge +=. 15] ---- -- This fails with a compile-time type error alerting us to the fact that -- this record has multiple unique keys, and suggests that we look for -- upsertBy to select the unique key we want. upsert :: forall record (m :: Type -> Type). (PersistUniqueWrite backend, MonadIO m, PersistRecordBackend record backend, OnlyOneUniqueKey record) => record -> [Update record] -> ReaderT backend m (Entity record) -- | Update based on a given uniqueness constraint or insert: -- --
-- upsertBySpjName :: MonadIO m => User -> [Update User] -> ReaderT SqlBackend m (Entity User) -- upsertBySpjName record updates = upsertBy (UniqueUserName "SPJ") record updates ---- --
-- mSpjEnt <- upsertBySpjName (Person "X" 999) [PersonAge += .15] ---- -- The above query will alter dataset-1 to: -- --
-- +-----+-----+--------+ -- |id |name |age | -- +-----+-----+--------+ -- |1 |SPJ |40 -> 55| -- +-----+-----+--------+ -- |2 |Simon|41 | -- +-----+-----+--------+ ---- --
-- upsertBySimonAge :: MonadIO m => User -> [Update User] -> ReaderT SqlBackend m (Entity User) -- upsertBySimonAge record updates = upsertBy (UniqueUserName "SPJ") record updates ---- --
-- mPhilipEnt <- upsertBySimonAge (User "X" 999) [UserName =. "Philip"] ---- -- The above query will alter dataset-1 to: -- --
-- +----+-----------------+-----+ -- | id | name | age | -- +----+-----------------+-----+ -- | 1 | SPJ | 40 | -- +----+-----------------+-----+ -- | 2 | Simon -> Philip | 41 | -- +----+-----------------+-----+ ---- --
-- upsertByUnknownName :: MonadIO m => User -> [Update User] -> ReaderT SqlBackend m (Entity User) -- upsertByUnknownName record updates = upsertBy (UniqueUserName "Unknown") record updates ---- --
-- mXEnt <- upsertByUnknownName (User "X" 999) [UserAge +=. 15] ---- -- This query will alter dataset-1 to: -- --
-- +-----+-----+-----+ -- |id |name |age | -- +-----+-----+-----+ -- |1 |SPJ |40 | -- +-----+-----+-----+ -- |2 |Simon|41 | -- +-----+-----+-----+ -- |3 |X |999 | -- +-----+-----+-----+ --upsertBy :: forall record (m :: Type -> Type). (PersistUniqueWrite backend, MonadIO m, PersistRecordBackend record backend) => Unique record -> record -> [Update record] -> ReaderT backend m (Entity record) -- | Put many records into db -- --
-- l1 <- insertBy $ User "SPJ" 20 -- l2 <- insertBy $ User "XXX" 41 -- l3 <- insertBy $ User "SPJ" 40 -- r1 <- insertBy $ User "XXX" 100 ---- -- First three lines return Left because there're duplicates in -- given record's uniqueness constraints. While the last line returns a -- new key as Right. insertBy :: forall record backend (m :: Type -> Type). (MonadIO m, PersistUniqueWrite backend, PersistRecordBackend record backend, AtLeastOneUniqueKey record) => record -> ReaderT backend m (Either (Entity record) (Key record)) -- | Like insertEntity, but returns Nothing when the record -- couldn't be inserted because of a uniqueness constraint. -- --
-- insertUniqueSpjEntity :: MonadIO m => ReaderT SqlBackend m (Maybe (Entity User)) -- insertUniqueSpjEntity = insertUniqueEntity $ User "SPJ" 50 ---- --
-- mSpjEnt <- insertUniqueSpjEntity ---- -- The above query results Nothing as SPJ already exists. -- --
-- insertUniqueAlexaEntity :: MonadIO m => ReaderT SqlBackend m (Maybe (Entity User)) -- insertUniqueAlexaEntity = insertUniqueEntity $ User "Alexa" 3 ---- --
-- mAlexaEnt <- insertUniqueSpjEntity ---- -- Because there's no such unique keywords of the given record, the above -- query when applied on dataset-1, will produce this: -- --
-- +----+-------+-----+ -- | id | name | age | -- +----+-------+-----+ -- | 1 | SPJ | 40 | -- +----+-------+-----+ -- | 2 | Simon | 41 | -- +----+-------+-----+ -- | 3 | Alexa | 3 | -- +----+-------+-----+ --insertUniqueEntity :: forall record backend (m :: Type -> Type). (MonadIO m, PersistRecordBackend record backend, PersistUniqueWrite backend) => record -> ReaderT backend m (Maybe (Entity record)) -- | Return the single unique key for a record. -- --
-- onlySimonConst :: MonadIO m => ReaderT SqlBackend m (Unique User) -- onlySimonConst = onlyUnique $ User "Simon" 999 ---- --
-- mSimonConst <- onlySimonConst ---- -- mSimonConst would be Simon's uniqueness constraint. Note that -- onlyUnique doesn't work if there're more than two -- constraints. It will fail with a type error instead. onlyUnique :: forall record backend (m :: Type -> Type). (MonadIO m, PersistUniqueWrite backend, PersistRecordBackend record backend, OnlyOneUniqueKey record) => record -> ReaderT backend m (Unique record) -- | A modification of getBy, which takes the PersistEntity -- itself instead of a Unique record. Returns a record matching -- one of the unique keys. This function makes the most sense on -- entities with a single Unique constructor. -- --
-- mSpjEnt <- getBySpjValue ---- -- The above query when applied on dataset-1, will get this -- record: -- --
-- +----+------+-----+ -- | id | name | age | -- +----+------+-----+ -- | 1 | SPJ | 40 | -- +----+------+-----+ --getByValue :: forall record (m :: Type -> Type) backend. (MonadIO m, PersistUniqueRead backend, PersistRecordBackend record backend, AtLeastOneUniqueKey record) => record -> ReaderT backend m (Maybe (Entity record)) -- | Attempt to replace the record of the given key with the given new -- record. First query the unique fields to make sure the replacement -- maintains uniqueness constraints. -- -- Return Nothing if the replacement was made. If uniqueness is -- violated, return a Just with the Unique violation replaceUnique :: forall record backend (m :: Type -> Type). (MonadIO m, Eq (Unique record), PersistRecordBackend record backend, PersistUniqueWrite backend) => Key record -> record -> ReaderT backend m (Maybe (Unique record)) -- | Check whether there are any conflicts for unique keys with this entity -- and existing entities in the database. -- -- Returns Nothing if the entity would be unique, and could thus -- safely be inserted. on a conflict returns the conflicting key -- --
-- mAlanConst <- checkUnique $ User "Alan" 70 ---- -- While this would be Just because SPJ already exists: -- --
-- mSpjConst <- checkUnique $ User "SPJ" 60 --checkUnique :: forall record backend (m :: Type -> Type). (MonadIO m, PersistRecordBackend record backend, PersistUniqueRead backend) => record -> ReaderT backend m (Maybe (Unique record)) -- | Check whether there are any conflicts for unique keys with this entity -- and existing entities in the database. -- -- Returns Nothing if the entity would stay unique, and could thus -- safely be updated. on a conflict returns the conflicting key -- -- This is similar to checkUnique, except it's useful for updating -- - when the particular entity already exists, it would normally -- conflict with itself. This variant ignores those conflicts -- --
-- mAlanConst <- checkUnique $ User "Alan" 70 ---- -- While this would be Just because SPJ already exists: -- --
-- mSpjConst <- checkUnique $ User "SPJ" 60 --checkUniqueUpdateable :: forall record backend (m :: Type -> Type). (MonadIO m, PersistRecordBackend record backend, PersistUniqueRead backend) => Entity record -> ReaderT backend m (Maybe (Unique record)) -- | Backends supporting conditional write operations class (PersistQueryRead backend, PersistStoreWrite backend) => PersistQueryWrite backend -- | Backends supporting conditional read operations. class (PersistCore backend, PersistStoreRead backend) => PersistQueryRead backend -- | Get all records matching the given criterion in the specified order. -- Returns also the identifiers. -- -- NOTE: This function returns an Acquire and a ConduitM, -- which implies that it streams from the database. It does not. Please -- use selectList to simplify the code. If you want streaming -- behavior, consider persistent-pagination which efficiently -- chunks a query into ranges, or investigate a backend-specific -- streaming solution. selectSourceRes :: forall record (m1 :: Type -> Type) (m2 :: Type -> Type). (PersistQueryRead backend, PersistRecordBackend record backend, MonadIO m1, MonadIO m2) => [Filter record] -> [SelectOpt record] -> ReaderT backend m1 (Acquire (ConduitM () (Entity record) m2 ())) -- | Get the Keys of all records matching the given criterion. selectKeysRes :: forall (m1 :: Type -> Type) (m2 :: Type -> Type) record. (PersistQueryRead backend, MonadIO m1, MonadIO m2, PersistRecordBackend record backend) => [Filter record] -> [SelectOpt record] -> ReaderT backend m1 (Acquire (ConduitM () (Key record) m2 ())) -- | Get the Keys of all records matching the given criterion. -- -- For an example, see selectList. selectKeys :: forall record backend (m :: Type -> Type). (PersistQueryRead backend, MonadResource m, PersistRecordBackend record backend, MonadReader backend m) => [Filter record] -> [SelectOpt record] -> ConduitM () (Key record) m () -- | For combinations of backends and entities that support -- cascade-deletion. “Cascade-deletion” means that entries that depend on -- other entries to be deleted will be deleted as well. class (PersistStoreWrite backend, PersistEntity record, BaseBackend backend ~ PersistEntityBackend record) => DeleteCascade record backend -- | Perform cascade-deletion of single database entry. deleteCascade :: forall (m :: Type -> Type). (DeleteCascade record backend, MonadIO m) => Key record -> ReaderT backend m () -- | Cascade-deletion of entries satisfying given filters. deleteCascadeWhere :: forall record backend (m :: Type -> Type). (MonadIO m, DeleteCascade record backend, PersistQueryWrite backend) => [Filter record] -> ReaderT backend m () -- | A backwards-compatible alias for those that don't care about -- distinguishing between read and write queries. It signifies the -- assumption that, by default, a backend can write as well as read. type PersistStore a = PersistStoreWrite a -- | A backwards-compatible alias for those that don't care about -- distinguishing between read and write queries. It signifies the -- assumption that, by default, a backend can write as well as read. type PersistUnique a = PersistUniqueWrite a -- | A backend which is a wrapper around SqlBackend. type IsSqlBackend backend = (IsPersistBackend backend, BaseBackend backend ~ SqlBackend) -- | Like SqlPersistT but compatible with any SQL backend which -- can handle read and write queries. type SqlWriteT (m :: Type -> Type) a = forall backend. SqlBackendCanWrite backend => ReaderT backend m a -- | Like SqlPersistT but compatible with any SQL backend which -- can handle read queries. type SqlReadT (m :: Type -> Type) a = forall backend. SqlBackendCanRead backend => ReaderT backend m a -- | A constraint synonym which witnesses that a backend is SQL and can run -- read and write queries. type SqlBackendCanWrite backend = (SqlBackendCanRead backend, PersistQueryWrite backend, PersistStoreWrite backend, PersistUniqueWrite backend) -- | A constraint synonym which witnesses that a backend is SQL and can run -- read queries. type SqlBackendCanRead backend = (BackendCompatible SqlBackend backend, PersistQueryRead backend, PersistStoreRead backend, PersistUniqueRead backend) -- | An SQL backend which can handle read or write queries -- -- The constructor was exposed in 2.10.0 newtype SqlWriteBackend SqlWriteBackend :: SqlBackend -> SqlWriteBackend [$sel:unSqlWriteBackend:SqlWriteBackend] :: SqlWriteBackend -> SqlBackend -- | An SQL backend which can only handle read queries -- -- The constructor was exposed in 2.10.0. newtype SqlReadBackend SqlReadBackend :: SqlBackend -> SqlReadBackend [$sel:unSqlReadBackend:SqlReadBackend] :: SqlReadBackend -> SqlBackend -- | Useful for running a write query against an untagged backend with -- unknown capabilities. writeToUnknown :: forall (m :: Type -> Type) a. Monad m => ReaderT SqlWriteBackend m a -> ReaderT SqlBackend m a -- | Useful for running a read query against a backend with read and write -- capabilities. readToWrite :: forall (m :: Type -> Type) a. Monad m => ReaderT SqlReadBackend m a -> ReaderT SqlWriteBackend m a -- | Useful for running a read query against a backend with unknown -- capabilities. readToUnknown :: forall (m :: Type -> Type) a. Monad m => ReaderT SqlReadBackend m a -> ReaderT SqlBackend m a -- | A single column (see rawSql). Any PersistField may -- be used here, including PersistValue (which does not do any -- processing). newtype Single a Single :: a -> Single a [unSingle] :: Single a -> a -- | Values to configure a pool of database connections. See -- Data.Pool for details. data ConnectionPoolConfig ConnectionPoolConfig :: Int -> NominalDiffTime -> Int -> ConnectionPoolConfig -- | How many stripes to divide the pool into. See Data.Pool for -- details. Default: 1. [connectionPoolConfigStripes] :: ConnectionPoolConfig -> Int -- | How long connections can remain idle before being disposed of, in -- seconds. Default: 600 [connectionPoolConfigIdleTimeout] :: ConnectionPoolConfig -> NominalDiffTime -- | How many connections should be held in the connection pool. Default: -- 10 [connectionPoolConfigSize] :: ConnectionPoolConfig -> Int type ConnectionPool = Pool SqlBackend type SqlPersistM = SqlPersistT NoLoggingT ResourceT IO type SqlPersistT = ReaderT SqlBackend data PersistentSqlException StatementAlreadyFinalized :: Text -> PersistentSqlException Couldn'tGetSQLConnection :: PersistentSqlException -- | This value specifies how a field references another table. data ColumnReference ColumnReference :: !EntityNameDB -> !ConstraintNameDB -> !FieldCascade -> ColumnReference -- | The table name that the [crTableName] :: ColumnReference -> !EntityNameDB -- | The name of the foreign key constraint. [crConstraintName] :: ColumnReference -> !ConstraintNameDB -- | Whether or not updates/deletions to the referenced table cascade to -- this table. [crFieldCascade] :: ColumnReference -> !FieldCascade data Column Column :: !FieldNameDB -> !Bool -> !SqlType -> !Maybe Text -> !Maybe Text -> !Maybe ConstraintNameDB -> !Maybe Integer -> !Maybe ColumnReference -> Column [cName] :: Column -> !FieldNameDB [cNull] :: Column -> !Bool [cSqlType] :: Column -> !SqlType [cDefault] :: Column -> !Maybe Text [cGenerated] :: Column -> !Maybe Text [cDefaultConstraintName] :: Column -> !Maybe ConstraintNameDB [cMaxLen] :: Column -> !Maybe Integer [cReference] :: Column -> !Maybe ColumnReference -- | Initializes a ConnectionPoolConfig with default values. See the -- documentation of ConnectionPoolConfig for each field's default -- value. defaultConnectionPoolConfig :: ConnectionPoolConfig -- | Record of functions to override the default behavior in -- mkColumns. It is recommended you initialize this with -- emptyBackendSpecificOverrides and override the default values, -- so that as new fields are added, your code still compiles. -- -- For added safety, use the getBackendSpecific* and -- setBackendSpecific* functions, as a breaking change to the -- record field labels won't be reflected in a major version bump of the -- library. data BackendSpecificOverrides -- | If the override is defined, then this returns a function that accepts -- an entity name and field name and provides the ConstraintNameDB -- for the foreign key constraint. -- -- An abstract accessor for the BackendSpecificOverrides getBackendSpecificForeignKeyName :: BackendSpecificOverrides -> Maybe (EntityNameDB -> FieldNameDB -> ConstraintNameDB) -- | Set the backend's foreign key generation function to this value. setBackendSpecificForeignKeyName :: (EntityNameDB -> FieldNameDB -> ConstraintNameDB) -> BackendSpecificOverrides -> BackendSpecificOverrides -- | Creates an empty BackendSpecificOverrides (i.e. use the default -- behavior; no overrides) emptyBackendSpecificOverrides :: BackendSpecificOverrides defaultAttribute :: [FieldAttr] -> Maybe Text -- | Create the list of columns for the given entity. mkColumns :: [EntityDef] -> EntityDef -> BackendSpecificOverrides -> ([Column], [UniqueDef], [ForeignDef]) -- | A more general way to convert instances of ToJSON type class to -- strict text Text. toJsonText :: ToJSON j => j -> Text -- | Tells Persistent what database column type should be used to store a -- Haskell type. -- --
-- data Switch = On | Off -- deriving (Show, Eq) -- -- instance PersistField Switch where -- toPersistValue s = case s of -- On -> PersistBool True -- Off -> PersistBool False -- fromPersistValue (PersistBool b) = if b then Right On else Right Off -- fromPersistValue x = Left $ "File.hs: When trying to deserialize a Switch: expected PersistBool, received: " <> T.pack (show x) -- -- instance PersistFieldSql Switch where -- sqlType _ = SqlBool ---- --
-- import qualified Data.UUID as UUID -- instance PersistField UUID where -- toPersistValue = PersistLiteralEncoded . toASCIIBytes -- fromPersistValue (PersistLiteralEncoded uuid) = -- case fromASCIIBytes uuid of -- Nothing -> Left $ "Model/CustomTypes.hs: Failed to deserialize a UUID; received: " <> T.pack (show uuid) -- Just uuid' -> Right uuid' -- fromPersistValue x = Left $ "File.hs: When trying to deserialize a UUID: expected PersistLiteralEncoded, received: "-- > <> T.pack (show x) -- -- instance PersistFieldSql UUID where -- sqlType _ = SqlOther "uuid" ---- --
-- CREATE DOMAIN ssn AS text
-- CHECK ( value ~ '^[0-9]{9}$');
--
--
-- -- instance PersistFieldSQL SSN where -- sqlType _ = SqlOther "ssn" ---- --
-- CREATE TYPE rainbow_color AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet');
--
--
-- -- instance PersistFieldSQL RainbowColor where -- sqlType _ = SqlOther "rainbow_color" --class PersistField a => PersistFieldSql a sqlType :: PersistFieldSql a => Proxy a -> SqlType -- | This newtype wrapper is useful when selecting an entity out of the -- database and you want to provide a prefix to the table being selected. -- -- Consider this raw SQL query: -- --
-- SELECT ?? -- FROM my_long_table_name AS mltn -- INNER JOIN other_table AS ot -- ON mltn.some_col = ot.other_col -- WHERE ... ---- -- We don't want to refer to my_long_table_name every time, so -- we create an alias. If we want to select it, we have to tell the raw -- SQL quasi-quoter that we expect the entity to be prefixed with some -- other name. -- -- We can give the above query a type with this, like: -- --
-- getStuff :: SqlPersistM [EntityWithPrefix "mltn" MyLongTableName] -- getStuff = rawSql queryText [] ---- -- The EntityWithPrefix bit is a boilerplate newtype wrapper, so -- you can remove it with unPrefix, like this: -- --
-- getStuff :: SqlPersistM [Entity MyLongTableName] -- getStuff = unPrefix @"mltn" <$> rawSql queryText [] ---- -- The symbol is a "type application" and requires the -- TypeApplications@ language extension. newtype EntityWithPrefix (prefix :: Symbol) record EntityWithPrefix :: Entity record -> EntityWithPrefix (prefix :: Symbol) record [unEntityWithPrefix] :: EntityWithPrefix (prefix :: Symbol) record -> Entity record -- | Class for data types that may be retrived from a rawSql -- query. class RawSql a -- | Number of columns that this data type needs and the list of -- substitutions for SELECT placeholders ??. rawSqlCols :: RawSql a => (Text -> Text) -> a -> (Int, [Text]) -- | A string telling the user why the column count is what it is. rawSqlColCountReason :: RawSql a => a -> String -- | Transform a row of the result into the data type. rawSqlProcessRow :: RawSql a => [PersistValue] -> Either Text a -- | A helper function to tell GHC what the EntityWithPrefix prefix -- should be. This allows you to use a type application to specify the -- prefix, instead of specifying the etype on the result. -- -- As an example, here's code that uses this: -- --
-- myQuery :: SqlPersistM [Entity Person] -- myQuery = fmap (unPrefix @"p") $ rawSql query [] -- where -- query = "SELECT ?? FROM person AS p" --unPrefix :: forall (prefix :: Symbol) record. EntityWithPrefix prefix record -> Entity record rawQuery :: forall (m :: Type -> Type) env. (MonadResource m, MonadReader env m, BackendCompatible SqlBackend env) => Text -> [PersistValue] -> ConduitM () [PersistValue] m () rawQueryRes :: forall (m1 :: Type -> Type) (m2 :: Type -> Type) env. (MonadIO m1, MonadIO m2, BackendCompatible SqlBackend env) => Text -> [PersistValue] -> ReaderT env m1 (Acquire (ConduitM () [PersistValue] m2 ())) -- | Execute a raw SQL statement rawExecute :: forall (m :: Type -> Type) backend. (MonadIO m, BackendCompatible SqlBackend backend) => Text -> [PersistValue] -> ReaderT backend m () -- | Execute a raw SQL statement and return the number of rows it has -- modified. rawExecuteCount :: forall (m :: Type -> Type) backend. (MonadIO m, BackendCompatible SqlBackend backend) => Text -> [PersistValue] -> ReaderT backend m Int64 getStmtConn :: SqlBackend -> Text -> IO Statement -- | Execute a raw SQL statement and return its results as a list. If you -- do not expect a return value, use of rawExecute is recommended. -- -- If you're using Entitys (which is quite likely), then -- you must use entity selection placeholders (double question -- mark, ??). These ?? placeholders are then replaced -- for the names of the columns that we need for your entities. You'll -- receive an error if you don't use the placeholders. Please see the -- Entitys documentation for more details. -- -- You may put value placeholders (question marks, ?) in your -- SQL query. These placeholders are then replaced by the values you pass -- on the second parameter, already correctly escaped. You may want to -- use toPersistValue to help you constructing the placeholder -- values. -- -- Since you're giving a raw SQL statement, you don't get any guarantees -- regarding safety. If rawSql is not able to parse the results of -- your query back, then an exception is raised. However, most common -- problems are mitigated by using the entity selection placeholder -- ??, and you shouldn't see any error at all if you're not -- using Single. -- -- Some example of rawSql based on this schema: -- --
-- share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase| -- Person -- name String -- age Int Maybe -- deriving Show -- BlogPost -- title String -- authorId PersonId -- deriving Show -- |] ---- -- Examples based on the above schema: -- --
-- getPerson :: MonadIO m => ReaderT SqlBackend m [Entity Person] -- getPerson = rawSql "select ?? from person where name=?" [PersistText "john"] -- -- getAge :: MonadIO m => ReaderT SqlBackend m [Single Int] -- getAge = rawSql "select person.age from person where name=?" [PersistText "john"] -- -- getAgeName :: MonadIO m => ReaderT SqlBackend m [(Single Int, Single Text)] -- getAgeName = rawSql "select person.age, person.name from person where name=?" [PersistText "john"] -- -- getPersonBlog :: MonadIO m => ReaderT SqlBackend m [(Entity Person, Entity BlogPost)] -- getPersonBlog = rawSql "select ??,?? from person,blog_post where person.id = blog_post.author_id" [] ---- -- Minimal working program for PostgreSQL backend based on the above -- concepts: -- --
-- {-# LANGUAGE EmptyDataDecls #-}
-- {-# LANGUAGE FlexibleContexts #-}
-- {-# LANGUAGE GADTs #-}
-- {-# LANGUAGE GeneralizedNewtypeDeriving #-}
-- {-# LANGUAGE MultiParamTypeClasses #-}
-- {-# LANGUAGE OverloadedStrings #-}
-- {-# LANGUAGE QuasiQuotes #-}
-- {-# LANGUAGE TemplateHaskell #-}
-- {-# LANGUAGE TypeFamilies #-}
--
-- import Control.Monad.IO.Class (liftIO)
-- import Control.Monad.Logger (runStderrLoggingT)
-- import Database.Persist
-- import Control.Monad.Reader
-- import Data.Text
-- import Database.Persist.Sql
-- import Database.Persist.Postgresql
-- import Database.Persist.TH
--
-- share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
-- Person
-- name String
-- age Int Maybe
-- deriving Show
-- |]
--
-- conn = "host=localhost dbname=new_db user=postgres password=postgres port=5432"
--
-- getPerson :: MonadIO m => ReaderT SqlBackend m [Entity Person]
-- getPerson = rawSql "select ?? from person where name=?" [PersistText "sibi"]
--
-- liftSqlPersistMPool y x = liftIO (runSqlPersistMPool y x)
--
-- main :: IO ()
-- main = runStderrLoggingT $ withPostgresqlPool conn 10 $ liftSqlPersistMPool $ do
-- runMigration migrateAll
-- xs <- getPerson
-- liftIO (print xs)
--
rawSql :: forall a (m :: Type -> Type) backend. (RawSql a, MonadIO m, BackendCompatible SqlBackend backend) => Text -> [PersistValue] -> ReaderT backend m [a]
-- | Get a connection from the pool, run the given action, and then return
-- the connection to the pool.
--
-- This function performs the given action in a transaction. If an
-- exception occurs during the action, then the transaction is rolled
-- back.
--
-- Note: This function previously timed out after 2 seconds, but this
-- behavior was buggy and caused more problems than it solved. Since
-- version 2.1.2, it performs no timeout checks.
runSqlPool :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> Pool backend -> m a
-- | Like runSqlPool, but supports specifying an isolation level.
runSqlPoolWithIsolation :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> Pool backend -> IsolationLevel -> m a
-- | Like runSqlPool, but does not surround the action in a
-- transaction. This action might leave your database in a weird state.
runSqlPoolNoTransaction :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> Pool backend -> Maybe IsolationLevel -> m a
-- | This function is how runSqlPool and
-- runSqlPoolNoTransaction are defined. In addition to the action
-- to be performed and the Pool of conections to use, we give you
-- the opportunity to provide three actions - initialize, afterwards, and
-- onException.
runSqlPoolWithHooks :: forall backend m a before after onException. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> Pool backend -> Maybe IsolationLevel -> (backend -> m before) -> (backend -> m after) -> (backend -> SomeException -> m onException) -> m a
-- | This function is how runSqlPoolWithHooks is defined.
--
-- It's currently the most general function for using a SQL pool.
runSqlPoolWithExtensibleHooks :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> Pool backend -> Maybe IsolationLevel -> SqlPoolHooks m backend -> m a
-- | Starts a new transaction on the connection. When the acquired
-- connection is released the transaction is committed and the connection
-- returned to the pool.
--
-- Upon an exception the transaction is rolled back and the connection
-- destroyed.
--
-- This is equivalent to runSqlConn but does not incur the
-- MonadUnliftIO constraint, meaning it can be used within, for
-- example, a Conduit pipeline.
acquireSqlConn :: (MonadReader backend m, BackendCompatible SqlBackend backend) => m (Acquire backend)
-- | Like acquireSqlConn, but lets you specify an explicit isolation
-- level.
acquireSqlConnWithIsolation :: (MonadReader backend m, BackendCompatible SqlBackend backend) => IsolationLevel -> m (Acquire backend)
runSqlConn :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> backend -> m a
-- | Like runSqlConn, but supports specifying an isolation level.
runSqlConnWithIsolation :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> backend -> IsolationLevel -> m a
runSqlPersistM :: BackendCompatible SqlBackend backend => ReaderT backend (NoLoggingT (ResourceT IO)) a -> backend -> IO a
runSqlPersistMPool :: BackendCompatible SqlBackend backend => ReaderT backend (NoLoggingT (ResourceT IO)) a -> Pool backend -> IO a
liftSqlPersistMPool :: forall backend m a. (MonadIO m, BackendCompatible SqlBackend backend) => ReaderT backend (NoLoggingT (ResourceT IO)) a -> Pool backend -> m a
withSqlPool :: forall backend m a. (MonadLoggerIO m, MonadUnliftIO m, BackendCompatible SqlBackend backend) => (LogFunc -> IO backend) -> Int -> (Pool backend -> m a) -> m a
-- | Creates a pool of connections to a SQL database which can be used by
-- the Pool backend -> m a function. After the function
-- completes, the connections are destroyed.
withSqlPoolWithConfig :: forall backend m a. (MonadLoggerIO m, MonadUnliftIO m, BackendCompatible SqlBackend backend) => (LogFunc -> IO backend) -> ConnectionPoolConfig -> (Pool backend -> m a) -> m a
createSqlPool :: forall backend m. (MonadLoggerIO m, MonadUnliftIO m, BackendCompatible SqlBackend backend) => (LogFunc -> IO backend) -> Int -> m (Pool backend)
-- | Creates a pool of connections to a SQL database.
createSqlPoolWithConfig :: (MonadLoggerIO m, MonadUnliftIO m, BackendCompatible SqlBackend backend) => (LogFunc -> IO backend) -> ConnectionPoolConfig -> m (Pool backend)
-- | Create a connection and run sql queries within it. This function
-- automatically closes the connection on it's completion.
--
--
-- {-# LANGUAGE GADTs #-}
-- {-# LANGUAGE ScopedTypeVariables #-}
-- {-# LANGUAGE OverloadedStrings #-}
-- {-# LANGUAGE MultiParamTypeClasses #-}
-- {-# LANGUAGE TypeFamilies#-}
-- {-# LANGUAGE TemplateHaskell#-}
-- {-# LANGUAGE QuasiQuotes#-}
-- {-# LANGUAGE GeneralizedNewtypeDeriving #-}
--
-- import Control.Monad.IO.Class (liftIO)
-- import Control.Monad.Logger
-- import Conduit
-- import Database.Persist
-- import Database.Sqlite
-- import Database.Persist.Sqlite
-- import Database.Persist.TH
--
-- share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
-- Person
-- name String
-- age Int Maybe
-- deriving Show
-- |]
--
-- openConnection :: LogFunc -> IO SqlBackend
-- openConnection logfn = do
-- conn <- open "/home/sibi/test.db"
-- wrapConnection conn logfn
--
-- main :: IO ()
-- main = do
-- runNoLoggingT $ runResourceT $ withSqlConn openConnection (\backend ->
-- flip runSqlConn backend $ do
-- runMigration migrateAll
-- insert_ $ Person "John doe" $ Just 35
-- insert_ $ Person "Divya" $ Just 36
-- (pers :: [Entity Person]) <- selectList [] []
-- liftIO $ print pers
-- return ()
-- )
--
--
-- On executing it, you get this output:
--
--
-- Migrating: CREATE TABLE "person"("id" INTEGER PRIMARY KEY,"name" VARCHAR NOT NULL,"age" INTEGER NULL)
-- [Entity {entityKey = PersonKey {unPersonKey = SqlBackendKey {unSqlBackendKey = 1}}, entityVal = Person {personName = "John doe", personAge = Just 35}},Entity {entityKey = PersonKey {unPersonKey = SqlBackendKey {unSqlBackendKey = 2}}, entityVal = Person {personName = "Hema", personAge = Just 36}}]
--
withSqlConn :: forall backend m a. (MonadUnliftIO m, MonadLoggerIO m, BackendCompatible SqlBackend backend) => (LogFunc -> IO backend) -> (backend -> m a) -> m a
close' :: BackendCompatible SqlBackend backend => backend -> IO ()
withRawQuery :: forall (m :: Type -> Type) a. MonadIO m => Text -> [PersistValue] -> ConduitM [PersistValue] Void IO a -> ReaderT SqlBackend m a
toSqlKey :: ToBackendKey SqlBackend record => Int64 -> Key record
fromSqlKey :: ToBackendKey SqlBackend record => Key record -> Int64
-- | get the SQL string for the table that a PeristEntity represents Useful
-- for raw SQL queries
--
-- Your backend may provide a more convenient tableName function which
-- does not operate in a Monad
getTableName :: forall record (m :: Type -> Type) backend. (PersistEntity record, BackendCompatible SqlBackend backend, Monad m) => record -> ReaderT backend m Text
-- | useful for a backend to implement tableName by adding escaping
tableDBName :: PersistEntity record => record -> EntityNameDB
-- | get the SQL string for the field that an EntityField represents Useful
-- for raw SQL queries
--
-- Your backend may provide a more convenient fieldName function which
-- does not operate in a Monad
getFieldName :: forall record typ (m :: Type -> Type) backend. (PersistEntity record, PersistEntityBackend record ~ SqlBackend, BackendCompatible SqlBackend backend, Monad m) => EntityField record typ -> ReaderT backend m Text
-- | useful for a backend to implement fieldName by adding escaping
fieldDBName :: PersistEntity record => EntityField record typ -> FieldNameDB
-- | Used when determining how to prefix a column name in a WHERE
-- clause.
data FilterTablePrefix
-- | Prefix the column with the table name. This is useful if the column
-- name might be ambiguous.
PrefixTableName :: FilterTablePrefix
-- | Prefix the column name with the EXCLUDED keyword. This is
-- used with the Postgresql backend when doing ON CONFLICT DO
-- UPDATE clauses - see the documentation on upsertWhere
-- and upsertManyWhere.
PrefixExcluded :: FilterTablePrefix
-- | Render a [Filter record] into a Text value
-- suitable for inclusion into a SQL query.
filterClause :: PersistEntity val => Maybe FilterTablePrefix -> SqlBackend -> [Filter val] -> Text
-- | Render a [Filter record] into a Text value
-- suitable for inclusion into a SQL query, as well as the
-- [PersistValue] to properly fill in the ?
-- place holders.
filterClauseWithVals :: PersistEntity val => Maybe FilterTablePrefix -> SqlBackend -> [Filter val] -> (Text, [PersistValue])
-- | Render a [SelectOpt record] made up *only* of
-- Asc and Desc constructors into a Text value
-- suitable for inclusion into a SQL query.
orderClause :: PersistEntity val => Maybe FilterTablePrefix -> SqlBackend -> [SelectOpt val] -> Text
-- | Generates sql for limit and offset for postgres, sqlite and mysql.
decorateSQLWithLimitOffset :: Text -> (Int, Int) -> Text -> Text
-- | An exception indicating that Persistent refused to run some unsafe
-- migrations. Contains a list of pairs where the Bool tracks whether the
-- migration was unsafe (True means unsafe), and the Sql is the sql
-- statement for the migration.
newtype PersistUnsafeMigrationException
PersistUnsafeMigrationException :: [(Bool, Sql)] -> PersistUnsafeMigrationException
-- | A Migration is a four level monad stack consisting of:
--
-- -- runSqlCommand $ rawExecute "CREATE EXTENSION IF NOT EXISTS "uuid-ossp";" [] --runSqlCommand :: SqlPersistT IO () -> Migration -- | Commit the current transaction and begin a new one. This is used when -- a transaction commit is required within the context of -- runSqlConn (which brackets its provided action with a -- transaction begin/commit pair). transactionSave :: forall (m :: Type -> Type). MonadIO m => ReaderT SqlBackend m () -- | Commit the current transaction and begin a new one with the specified -- isolation level. transactionSaveWithIsolation :: forall (m :: Type -> Type). MonadIO m => IsolationLevel -> ReaderT SqlBackend m () -- | Roll back the current transaction and begin a new one. This rolls back -- to the state of the last call to transactionSave or the -- enclosing runSqlConn call. transactionUndo :: forall (m :: Type -> Type). MonadIO m => ReaderT SqlBackend m () -- | Roll back the current transaction and begin a new one with the -- specified isolation level. transactionUndoWithIsolation :: forall (m :: Type -> Type). MonadIO m => IsolationLevel -> ReaderT SqlBackend m () -- | Classy shim around Database.Esqueleto.Experimental -- -- In the style of Database.Persist.Monad, this exposes a -- "classy" (typeclass-using) API for Esqueleto functions, allowing them -- to be used with MonadSqlQuery constraints rather than a -- ReaderT SqlBackend concrete type. -- -- The goal of this module is to be a drop-in replacement for -- Database.Esqueleto.Experimental. module Database.Esqueleto.Monad.Experimental -- | 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. associateJoin :: forall e1 e0. Ord (Key e0) => [(Entity e0, e1)] -> Map (Key e0) (e0, [e1]) -- | Synonym for delete that does not clash with -- esqueleto's delete. deleteKey :: forall backend val (m :: Type -> Type). (PersistStore backend, BaseBackend backend ~ PersistEntityBackend val, MonadIO m, PersistEntity val) => Key val -> ReaderT backend m () -- | 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). valJ :: PersistField (Key entity) => Value (Key entity) -> SqlExpr (Value (Key entity)) -- | valkey i = val . toSqlKey -- (https://github.com/prowdsponsor/esqueleto/issues/9). valkey :: (ToBackendKey SqlBackend entity, PersistField (Key entity)) => Int64 -> SqlExpr (Value (Key entity)) -- | Insert a PersistField for every selected value, return the -- count afterward insertSelectCount :: forall (m :: Type -> Type) a backend. (MonadIO m, PersistEntity a, SqlBackendCanWrite backend) => SqlQuery (SqlExpr (Insertion a)) -> ReaderT backend m Int64 -- | Insert a PersistField for every selected value. insertSelect :: forall (m :: Type -> Type) a backend. (MonadIO m, PersistEntity a, SqlBackendCanWrite backend) => SqlQuery (SqlExpr (Insertion a)) -> ReaderT backend m () -- | Renders a SqlQuery into a Text value along with the -- list of PersistValues that would be supplied to the database -- for ? placeholders. renderQueryInsertInto :: forall a r backend (m :: Type -> Type). (SqlSelect a r, BackendCompatible SqlBackend backend, Monad m) => SqlQuery a -> 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. renderQueryUpdate :: forall a r backend (m :: Type -> Type). (SqlSelect a r, BackendCompatible SqlBackend backend, Monad m) => SqlQuery a -> 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. renderQueryDelete :: forall a r backend (m :: Type -> Type). (SqlSelect a r, BackendCompatible SqlBackend backend, Monad m) => SqlQuery a -> 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. renderQueryToText :: forall a r backend (m :: Type -> Type). (SqlSelect a r, BackendCompatible SqlBackend backend, Monad m) => Mode -> SqlQuery a -> ReaderT backend m (Text, [PersistValue]) -- | Same as update, but returns the number of rows affected. updateCount :: forall (m :: Type -> Type) val backend. (MonadIO m, PersistEntity val, BackendCompatible SqlBackend (PersistEntityBackend val), SqlBackendCanWrite backend) => (SqlExpr (Entity val) -> SqlQuery ()) -> ReaderT backend m Int64 -- | Same as delete, but returns the number of rows affected. deleteCount :: forall (m :: Type -> Type) backend. (MonadIO m, SqlBackendCanWrite backend) => SqlQuery () -> ReaderT backend m Int64 -- | Execute an esqueleto SELECT query inside -- persistent's SqlPersistT monad and return a -- Source of rows. selectSource :: forall a r backend (m :: Type -> Type). (SqlSelect a r, BackendCompatible SqlBackend backend, IsPersistBackend backend, PersistQueryRead backend, PersistStoreRead backend, PersistUniqueRead backend, MonadResource m) => SqlQuery a -> ConduitT () r (ReaderT backend m) () -- | Syntax sugar for case_. else_ :: expr a -> expr a -- | Syntax sugar for case_. then_ :: () -- | Syntax sugar for case_. when_ :: expr (Value Bool) -> () -> expr a -> (expr (Value Bool), expr a) -- | 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. toBaseId :: ToBaseId ent => SqlExpr (Value (Key ent)) -> SqlExpr (Value (Key (BaseEnt ent))) -- | CASE statement. For example: -- --
-- select $ -- return $ -- case_ -- [ when_ -- (exists $ -- from $ \p -> do -- where_ (p ^. PersonName ==. val "Mike")) -- then_ -- (sub_select $ -- from $ \v -> do -- let sub = -- from $ \c -> do -- where_ (c ^. PersonName ==. val "Mike") -- return (c ^. PersonFavNum) -- where_ (v ^. PersonFavNum >. sub_select 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. -- --
-- select $ -- from $ \person -> do -- where_ $ exists $ -- from $ \post -> do -- where_ (post ^. BlogPostAuthorId ==. person ^. PersonId) -- return person --exists :: SqlQuery () -> SqlExpr (Value Bool) -- | NOT IN operator. notIn :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (ValueList typ) -> 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]. in_ :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (ValueList typ) -> SqlExpr (Value Bool) -- | Same as just but for ValueList. Most of the time you -- won't need it, though, because you can use just from inside -- subList_select or Just from inside valList. justList :: SqlExpr (ValueList typ) -> SqlExpr (ValueList (Maybe typ)) -- | Lift a list of constant value from Haskell-land to the query. valList :: PersistField typ => [typ] -> SqlExpr (ValueList typ) -- | Execute a subquery SELECT in an SqlExpression. Returns a list -- of values. subList_select :: PersistField a => SqlQuery (SqlExpr (Value a)) -> SqlExpr (ValueList a) -- | 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. castString :: (SqlString s, SqlString r) => SqlExpr (Value s) -> SqlExpr (Value r) -- | The || string concatenation operator (named after Haskell's -- ++ in order to avoid naming clash with ||.). Supported -- by SQLite and PostgreSQL. (++.) :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -> SqlExpr (Value s) infixr 5 ++. -- | The CONCAT function with a variable number of parameters. -- Supported by MySQL and PostgreSQL. concat_ :: SqlString s => [SqlExpr (Value 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" ++. (%) --(%) :: SqlString s => SqlExpr (Value s) -- | ILIKE operator (case-insensitive LIKE). -- -- Supported by PostgreSQL only. ilike :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -> SqlExpr (Value Bool) infixr 2 `ilike` -- | LIKE operator. like :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -> SqlExpr (Value Bool) infixr 2 `like` -- | RIGHT function. @since 3.3.0 right_ :: (SqlString s, Num a) => (SqlExpr (Value s), SqlExpr (Value a)) -> SqlExpr (Value s) -- | LEFT function. @since 3.3.0 left_ :: (SqlString s, Num a) => (SqlExpr (Value s), SqlExpr (Value a)) -> SqlExpr (Value s) -- | LENGTH function. @since 3.3.0 length_ :: (SqlString s, Num a) => SqlExpr (Value s) -> SqlExpr (Value a) -- | LTRIM function. @since 3.3.0 ltrim_ :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -- | RTRIM function. @since 3.3.0 rtrim_ :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -- | TRIM function. @since 3.3.0 trim_ :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -- | UPPER function. @since 3.3.0 upper_ :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -- | LOWER function. lower_ :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -- | Like coalesce, but takes a non-nullable SqlExpression placed -- at the end of the SqlExpression list, which guarantees a non-NULL -- result. coalesceDefault :: PersistField a => [SqlExpr (Value (Maybe a))] -> SqlExpr (Value a) -> SqlExpr (Value a) -- | 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. coalesce :: PersistField a => [SqlExpr (Value (Maybe a))] -> SqlExpr (Value (Maybe a)) -- | Same as castNum, but for nullable values. castNumM :: (Num a, Num b) => SqlExpr (Value (Maybe a)) -> SqlExpr (Value (Maybe 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. castNum :: (Num a, Num b) => SqlExpr (Value a) -> SqlExpr (Value b) avg_ :: (PersistField a, PersistField b) => SqlExpr (Value a) -> SqlExpr (Value (Maybe b)) max_ :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value (Maybe a)) min_ :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value (Maybe a)) sum_ :: (PersistField a, PersistField b) => SqlExpr (Value a) -> SqlExpr (Value (Maybe b)) floor_ :: (PersistField a, Num a, PersistField b, Num b) => SqlExpr (Value a) -> SqlExpr (Value b) ceiling_ :: (PersistField a, Num a, PersistField b, Num b) => SqlExpr (Value a) -> SqlExpr (Value b) round_ :: (PersistField a, Num a, PersistField b, Num b) => SqlExpr (Value a) -> SqlExpr (Value b) random_ :: (PersistField a, Num a) => SqlExpr (Value a) -- | BETWEEN. -- -- @since: 3.1.0 between :: PersistField a => SqlExpr (Value a) -> (SqlExpr (Value a), SqlExpr (Value a)) -> SqlExpr (Value Bool) (*.) :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value a) -> SqlExpr (Value a) infixl 7 *. (/.) :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value a) -> SqlExpr (Value a) infixl 7 /. (-.) :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value a) -> SqlExpr (Value a) infixl 6 -. (+.) :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value a) -> SqlExpr (Value a) infixl 6 +. (||.) :: SqlExpr (Value Bool) -> SqlExpr (Value Bool) -> SqlExpr (Value Bool) infixr 2 ||. (&&.) :: SqlExpr (Value Bool) -> SqlExpr (Value Bool) -> SqlExpr (Value Bool) infixr 3 &&. (!=.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool) infix 4 !=. (<.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool) infix 4 <. (<=.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool) infix 4 <=. (>.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool) infix 4 >. (>=.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool) infix 4 >=. (==.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool) infix 4 ==. not_ :: SqlExpr (Value Bool) -> SqlExpr (Value Bool) -- | COUNT(DISTINCT x). countDistinct :: Num a => SqlExpr (Value typ) -> SqlExpr (Value a) -- | COUNT. count :: Num a => SqlExpr (Value typ) -> SqlExpr (Value a) -- | COUNT(*) value. countRows :: Num a => SqlExpr (Value a) -- | Join nested Maybes in a Value into one. This is useful -- when calling aggregate functions on nullable fields. joinV :: SqlExpr (Value (Maybe (Maybe typ))) -> SqlExpr (Value (Maybe typ)) -- | NULL value. nothing :: SqlExpr (Value (Maybe typ)) -- | Analogous to Just, promotes a value of type typ into -- one of type Maybe typ. It should hold that val . -- Just === just . val. just :: SqlExpr (Value typ) -> SqlExpr (Value (Maybe typ)) -- | 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: -- -- TODO: table -- -- 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}
--
isNothing :: PersistField typ => SqlExpr (Value (Maybe typ)) -> SqlExpr (Value Bool)
-- | Lift a constant value from Haskell-land to the query.
val :: PersistField typ => typ -> SqlExpr (Value typ)
-- | Project a field of an entity that may be null.
(?.) :: (PersistEntity val, PersistField typ) => SqlExpr (Maybe (Entity val)) -> EntityField val typ -> SqlExpr (Value (Maybe typ))
-- | Project an SqlExpression that may be null, guarding against null
-- cases.
withNonNull :: PersistField typ => SqlExpr (Value (Maybe typ)) -> (SqlExpr (Value typ) -> SqlQuery a) -> SqlQuery a
-- | Project a field of an entity.
(^.) :: forall typ val. (PersistEntity val, PersistField typ) => SqlExpr (Entity val) -> EntityField val typ -> SqlExpr (Value typ)
infixl 9 ^.
-- | Execute a subquery SELECT in a SqlExpr. This function
-- is unsafe, because it can throw runtime exceptions in two cases:
--
-- -- 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) --subSelectForeign :: (BackendCompatible SqlBackend (PersistEntityBackend val1), PersistEntity val1, PersistEntity val2, PersistField a) => SqlExpr (Entity val2) -> EntityField val2 (Key val1) -> (SqlExpr (Entity val1) -> SqlExpr (Value a)) -> SqlExpr (Value a) -- | 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. subSelectList :: PersistField a => SqlQuery (SqlExpr (Value a)) -> SqlExpr (ValueList a) -- | Performs a COUNT of the given query in a subSelect -- manner. This is always guaranteed to return a result value, and is -- completely safe. subSelectCount :: (Num a, PersistField a) => SqlQuery ignored -> SqlExpr (Value a) -- | 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). subSelectMaybe :: PersistField a => SqlQuery (SqlExpr (Value (Maybe a))) -> SqlExpr (Value (Maybe a)) -- | 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. subSelect :: PersistField a => SqlQuery (SqlExpr (Value a)) -> SqlExpr (Value (Maybe a)) -- | Execute a subquery SELECT in an SqlExpression. Returns a -- simple value so should be used only when the SELECT query is -- guaranteed to return just one row. -- -- Deprecated in 3.2.0. sub_select :: PersistField a => SqlQuery (SqlExpr (Value a)) -> SqlExpr (Value a) -- | Add a locking clause to the query. Please read LockingKind -- documentation and your RDBMS manual. -- -- If multiple calls to locking are made on the same query, the -- last one is used. locking :: LockingKind -> SqlQuery () -- | HAVING. having :: SqlExpr (Value Bool) -> SqlQuery () -- | ORDER BY random() clause. rand :: SqlExpr OrderBy -- | A convenience function that calls both distinctOn and -- orderBy. In other words, -- --
-- distinctOnOrderBy [asc foo, desc bar, desc quux] $ do -- ... ---- -- is the same as: -- --
-- distinctOn [don foo, don bar, don quux] $ do -- orderBy [asc foo, desc bar, desc quux] -- ... --distinctOnOrderBy :: [SqlExpr OrderBy] -> SqlQuery a -> SqlQuery a -- | Erase an SqlExpression's type so that it's suitable to be used by -- distinctOn. don :: SqlExpr (Value a) -> SqlExpr DistinctOn -- | 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. distinctOn :: [SqlExpr DistinctOn] -> 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 ()) -- ... --distinct :: SqlQuery a -> SqlQuery a -- | OFFSET. Usually used with limit. offset :: Int64 -> SqlQuery () -- | LIMIT. Limit the number of returned rows. limit :: Int64 -> SqlQuery () -- | Descending order of this field or SqlExpression. desc :: PersistField a => SqlExpr (Value a) -> SqlExpr OrderBy -- | Ascending order of this field or SqlExpression. asc :: PersistField a => SqlExpr (Value a) -> SqlExpr OrderBy -- | ORDER BY clause. See also asc and desc. -- -- Multiple calls to orderBy get concatenated on the final query, -- including distinctOnOrderBy. orderBy :: [SqlExpr OrderBy] -> 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) ---- --
-- 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) --groupBy :: ToSomeValues a => a -> SqlQuery () -- | WHERE clause: restrict the query's result. where_ :: SqlExpr (Value Bool) -> SqlQuery () -- | A single value (as opposed to a whole entity). You may use -- (^.) or (?.) to get a Value -- from an Entity. newtype Value a Value :: a -> Value a [unValue] :: Value a -> 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). newtype ValueList a ValueList :: a -> ValueList a -- | Data type that represents an INNER JOIN (see -- LeftOuterJoin for an example). data InnerJoin a b InnerJoin :: a -> b -> InnerJoin a b infixl 2 `InnerJoin` infixl 2 `InnerJoin` -- | Data type that represents a CROSS JOIN (see -- LeftOuterJoin for an example). data CrossJoin a b CrossJoin :: a -> b -> CrossJoin a b infixl 2 `CrossJoin` infixl 2 `CrossJoin` -- | 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. data LeftOuterJoin a b LeftOuterJoin :: a -> b -> LeftOuterJoin a b infixl 2 `LeftOuterJoin` infixl 2 `LeftOuterJoin` -- | Data type that represents a RIGHT OUTER JOIN (see -- LeftOuterJoin for an example). data RightOuterJoin a b RightOuterJoin :: a -> b -> RightOuterJoin a b infixl 2 `RightOuterJoin` infixl 2 `RightOuterJoin` -- | Data type that represents a FULL OUTER JOIN (see -- LeftOuterJoin for an example). data FullOuterJoin a b FullOuterJoin :: a -> b -> FullOuterJoin a b infixl 2 `FullOuterJoin` infixl 2 `FullOuterJoin` -- | (Internal) A kind of JOIN. data JoinKind -- |
-- INNER JOIN --InnerJoinKind :: JoinKind -- |
-- CROSS JOIN --CrossJoinKind :: JoinKind -- |
-- LEFT OUTER JOIN --LeftOuterJoinKind :: JoinKind -- |
-- RIGHT OUTER JOIN --RightOuterJoinKind :: JoinKind -- |
-- FULL OUTER JOIN --FullOuterJoinKind :: JoinKind -- | Exception thrown whenever on is used to create an ON -- clause but no matching JOIN is found. data OnClauseWithoutMatchingJoinException OnClauseWithoutMatchingJoinException :: String -> OnClauseWithoutMatchingJoinException -- | Phantom type used by orderBy, asc and desc. data OrderBy -- | Phantom type used by distinctOn and don. data DistinctOn -- | 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. data LockingKind -- | FOR UPDATE syntax. Supported by MySQL, Oracle and PostgreSQL. ForUpdate :: LockingKind -- | FOR UPDATE SKIP LOCKED syntax. Supported by MySQL, Oracle and -- PostgreSQL. ForUpdateSkipLocked :: LockingKind -- | FOR SHARE syntax. Supported by PostgreSQL. ForShare :: LockingKind -- | LOCK IN SHARE MODE syntax. Supported by MySQL. LockInShareMode :: LockingKind -- | 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. class PersistField a => SqlString a -- | e.g. type BaseEnt MyBase = MyChild type family BaseEnt ent -- | Class that enables one to use toBaseId to convert an entity's -- key on a query into another (cf. toBaseId). class ToBaseId ent where { -- | e.g. type BaseEnt MyBase = MyChild type family BaseEnt 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. toBaseIdWitness :: ToBaseId ent => Key (BaseEnt ent) -> Key ent -- | SQL backend for esqueleto using SqlPersistT. data SqlQuery a -- | Constraint synonym for persistent entities whose backend is -- SqlBackend. type SqlEntity ent = (PersistEntity ent, PersistEntityBackend ent ~ SqlBackend) -- | 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. data SqlExpr a -- | Represents a value containing all the configuration options for a -- specific backend. This abstraction makes it easier to write code that -- can easily swap backends. class PersistConfig c where { type family PersistConfigBackend c :: Type -> Type -> Type -> Type; type family PersistConfigPool c; } -- | Load the config settings from a Value, most likely taken from a -- YAML config file. loadConfig :: PersistConfig c => Value -> Parser c -- | Modify the config settings based on environment variables. applyEnv :: PersistConfig c => c -> IO c -- | Create a new connection pool based on the given config settings. createPoolConfig :: PersistConfig c => c -> IO (PersistConfigPool c) -- | Run a database action by taking a connection from the pool. runPool :: (PersistConfig c, MonadUnliftIO m) => c -> PersistConfigBackend c m a -> PersistConfigPool c -> m a type family PersistConfigBackend c :: Type -> Type -> Type -> Type type family PersistConfigPool c -- | An ConstraintNameHS represents the Haskell-side name that -- persistent will use for a constraint. newtype ConstraintNameHS ConstraintNameHS :: Text -> ConstraintNameHS [unConstraintNameHS] :: ConstraintNameHS -> Text -- | A ConstraintNameDB represents the datastore-side name that -- persistent will use for a constraint. newtype ConstraintNameDB ConstraintNameDB :: Text -> ConstraintNameDB [unConstraintNameDB] :: ConstraintNameDB -> Text -- | An EntityNameDB represents the datastore-side name that -- persistent will use for an entity. newtype EntityNameDB EntityNameDB :: Text -> EntityNameDB [unEntityNameDB] :: EntityNameDB -> Text -- | An EntityNameHS represents the Haskell-side name that -- persistent will use for an entity. newtype EntityNameHS EntityNameHS :: Text -> EntityNameHS [unEntityNameHS] :: EntityNameHS -> Text -- | A FieldNameHS represents the Haskell-side name that -- persistent will use for a field. newtype FieldNameHS FieldNameHS :: Text -> FieldNameHS [unFieldNameHS] :: FieldNameHS -> Text -- | An EntityNameDB represents the datastore-side name that -- persistent will use for an entity. newtype FieldNameDB FieldNameDB :: Text -> FieldNameDB [unFieldNameDB] :: FieldNameDB -> Text -- | Convenience operations for working with '-NameDB' types. class DatabaseName a escapeWith :: DatabaseName a => (Text -> str) -> a -> str -- | A type that determines how a backend should handle the literal. data LiteralType -- | The accompanying value will be escaped before inserting into the -- database. This is the correct default choice to use. Escaped :: LiteralType -- | The accompanying value will not be escaped when inserting into the -- database. This is potentially dangerous - use this with care. Unescaped :: LiteralType -- | The DbSpecific constructor corresponds to the legacy -- PersistDbSpecific constructor. We need to keep this around -- because old databases may have serialized JSON representations that -- reference this. We don't want to break the ability of a database to -- load rows. DbSpecific :: LiteralType -- | A raw value which can be stored in any backend and can be marshalled -- to and from a PersistField. data PersistValue PersistText :: Text -> PersistValue PersistByteString :: ByteString -> PersistValue PersistInt64 :: Int64 -> PersistValue PersistDouble :: Double -> PersistValue PersistRational :: Rational -> PersistValue PersistBool :: Bool -> PersistValue PersistDay :: Day -> PersistValue PersistTimeOfDay :: TimeOfDay -> PersistValue PersistUTCTime :: UTCTime -> PersistValue PersistNull :: PersistValue PersistList :: [PersistValue] -> PersistValue PersistMap :: [(Text, PersistValue)] -> PersistValue -- | Intended especially for MongoDB backend PersistObjectId :: ByteString -> PersistValue -- | Intended especially for PostgreSQL backend for text arrays PersistArray :: [PersistValue] -> PersistValue -- | This constructor is used to specify some raw literal value for the -- backend. The LiteralType value specifies how the value should -- be escaped. This can be used to make special, custom types avaialable -- in the back end. PersistLiteral_ :: LiteralType -> ByteString -> PersistValue -- | This pattern synonym used to be a data constructor on -- PersistValue, but was changed into a catch-all pattern synonym -- to allow backwards compatiblity with database types. See the -- documentation on PersistDbSpecific for more details. pattern PersistLiteral :: ByteString -> PersistValue -- | This pattern synonym used to be a data constructor for the -- PersistValue type. It was changed to be a pattern so that -- JSON-encoded database values could be parsed into their corresponding -- values. You should not use this, and instead prefer to pattern match -- on PersistLiteral_ directly. -- -- If you use this, it will overlap a patern match on the -- 'PersistLiteral_, PersistLiteral, and -- PersistLiteralEscaped patterns. If you need to disambiguate -- between these constructors, pattern match on PersistLiteral_ -- directly. pattern PersistDbSpecific :: ByteString -> PersistValue -- | This pattern synonym used to be a data constructor on -- PersistValue, but was changed into a catch-all pattern synonym -- to allow backwards compatiblity with database types. See the -- documentation on PersistDbSpecific for more details. pattern PersistLiteralEscaped :: ByteString -> PersistValue fromPersistValueText :: PersistValue -> Either Text Text -- | Please refer to the documentation for the database in question for a -- full overview of the semantics of the varying isloation levels data IsolationLevel ReadUncommitted :: IsolationLevel ReadCommitted :: IsolationLevel RepeatableRead :: IsolationLevel Serializable :: IsolationLevel -- | A FieldDef represents the inormation that persistent -- knows about a field of a datatype. This includes information used to -- parse the field out of the database and what the field corresponds to. data FieldDef FieldDef :: !FieldNameHS -> !FieldNameDB -> !FieldType -> !SqlType -> ![FieldAttr] -> !Bool -> !ReferenceDef -> !FieldCascade -> !Maybe Text -> !Maybe Text -> !Bool -> FieldDef -- | The name of the field. Note that this does not corresponds to the -- record labels generated for the particular entity - record labels are -- generated with the type name prefixed to the field, so a -- FieldDef that contains a FieldNameHS "name" for -- a type User will have a record field userName. [fieldHaskell] :: FieldDef -> !FieldNameHS -- | The name of the field in the database. For SQL databases, this -- corresponds to the column name. [fieldDB] :: FieldDef -> !FieldNameDB -- | The type of the field in Haskell. [fieldType] :: FieldDef -> !FieldType -- | The type of the field in a SQL database. [fieldSqlType] :: FieldDef -> !SqlType -- | User annotations for a field. These are provided with the ! -- operator. [fieldAttrs] :: FieldDef -> ![FieldAttr] -- | If this is True, then the Haskell datatype will have a strict -- record field. The default value for this is True. [fieldStrict] :: FieldDef -> !Bool [fieldReference] :: FieldDef -> !ReferenceDef -- | Defines how operations on the field cascade on to the referenced -- tables. This doesn't have any meaning if the fieldReference is -- set to NoReference or SelfReference. The cascade option -- here should be the same as the one obtained in the -- fieldReference. [fieldCascade] :: FieldDef -> !FieldCascade -- | Optional comments for a Field. There is not currently a way -- to attach comments to a field in the quasiquoter. [fieldComments] :: FieldDef -> !Maybe Text -- | Whether or not the field is a GENERATED column, and -- additionally the expression to use for generation. [fieldGenerated] :: FieldDef -> !Maybe Text -- | True if the field is an implicit ID column. False -- otherwise. [fieldIsImplicitIdColumn] :: FieldDef -> !Bool data PersistUpdate Assign :: PersistUpdate Add :: PersistUpdate Subtract :: PersistUpdate Multiply :: PersistUpdate Divide :: PersistUpdate BackendSpecificUpdate :: Text -> PersistUpdate data UpdateException KeyNotFound :: String -> UpdateException UpsertError :: String -> UpdateException data PersistFilter Eq :: PersistFilter Ne :: PersistFilter Gt :: PersistFilter Lt :: PersistFilter Ge :: PersistFilter Le :: PersistFilter In :: PersistFilter NotIn :: PersistFilter -- | A SQL data type. Naming attempts to reflect the underlying Haskell -- datatypes, eg SqlString instead of SqlVarchar. Different SQL databases -- may have different translations for these types. data SqlType SqlString :: SqlType SqlInt32 :: SqlType SqlInt64 :: SqlType SqlReal :: SqlType SqlNumeric :: Word32 -> Word32 -> SqlType SqlBool :: SqlType SqlDay :: SqlType SqlTime :: SqlType -- | Always uses UTC timezone SqlDayTime :: SqlType SqlBlob :: SqlType -- | a backend-specific name SqlOther :: Text -> SqlType data PersistException -- | Generic Exception PersistError :: Text -> PersistException PersistMarshalError :: Text -> PersistException PersistInvalidField :: Text -> PersistException PersistForeignConstraintUnmet :: Text -> PersistException PersistMongoDBError :: Text -> PersistException PersistMongoDBUnsupported :: Text -> PersistException -- | An action that might happen on a deletion or update on a foreign key -- change. data CascadeAction Cascade :: CascadeAction Restrict :: CascadeAction SetNull :: CascadeAction SetDefault :: CascadeAction -- | This datatype describes how a foreign reference field cascades deletes -- or updates. -- -- This type is used in both parsing the model definitions and performing -- migrations. A Nothing in either of the field values means that -- the user has not specified a CascadeAction. An unspecified -- CascadeAction is defaulted to Restrict when doing -- migrations. data FieldCascade FieldCascade :: !Maybe CascadeAction -> !Maybe CascadeAction -> FieldCascade [fcOnUpdate] :: FieldCascade -> !Maybe CascadeAction [fcOnDelete] :: FieldCascade -> !Maybe CascadeAction data ForeignDef ForeignDef :: !EntityNameHS -> !EntityNameDB -> !ConstraintNameHS -> !ConstraintNameDB -> !FieldCascade -> ![(ForeignFieldDef, ForeignFieldDef)] -> ![Attr] -> Bool -> Bool -> ForeignDef [foreignRefTableHaskell] :: ForeignDef -> !EntityNameHS [foreignRefTableDBName] :: ForeignDef -> !EntityNameDB [foreignConstraintNameHaskell] :: ForeignDef -> !ConstraintNameHS [foreignConstraintNameDBName] :: ForeignDef -> !ConstraintNameDB -- | Determine how the field will cascade on updates and deletions. [foreignFieldCascade] :: ForeignDef -> !FieldCascade [foreignFields] :: ForeignDef -> ![(ForeignFieldDef, ForeignFieldDef)] [foreignAttrs] :: ForeignDef -> ![Attr] [foreignNullable] :: ForeignDef -> Bool -- | Determines if the reference is towards a Primary Key or not. [foreignToPrimary] :: ForeignDef -> Bool -- | Used instead of FieldDef to generate a smaller amount of code type ForeignFieldDef = (FieldNameHS, FieldNameDB) data CompositeDef CompositeDef :: !NonEmpty FieldDef -> ![Attr] -> CompositeDef [compositeFields] :: CompositeDef -> !NonEmpty FieldDef [compositeAttrs] :: CompositeDef -> ![Attr] -- | Type for storing the Uniqueness constraint in the Schema. Assume you -- have the following schema with a uniqueness constraint: -- --
-- Person -- name String -- age Int -- UniqueAge age ---- -- This will be represented as: -- --
-- UniqueDef
-- { uniqueHaskell = ConstraintNameHS (packPTH UniqueAge)
-- , uniqueDBName = ConstraintNameDB (packPTH "unique_age")
-- , uniqueFields = [(FieldNameHS (packPTH "age"), FieldNameDB (packPTH "age"))]
-- , uniqueAttrs = []
-- }
--
data UniqueDef
UniqueDef :: !ConstraintNameHS -> !ConstraintNameDB -> !NonEmpty (FieldNameHS, FieldNameDB) -> ![Attr] -> UniqueDef
[uniqueHaskell] :: UniqueDef -> !ConstraintNameHS
[uniqueDBName] :: UniqueDef -> !ConstraintNameDB
[uniqueFields] :: UniqueDef -> !NonEmpty (FieldNameHS, FieldNameDB)
[uniqueAttrs] :: UniqueDef -> ![Attr]
-- | An EmbedFieldDef is the same as a FieldDef But it is only used for
-- embeddedFields so it only has data needed for embedding
data EmbedFieldDef
EmbedFieldDef :: FieldNameDB -> Maybe (Either SelfEmbed EntityNameHS) -> EmbedFieldDef
[emFieldDB] :: EmbedFieldDef -> FieldNameDB
[emFieldEmbed] :: EmbedFieldDef -> Maybe (Either SelfEmbed EntityNameHS)
-- | An EmbedEntityDef is the same as an EntityDef But it is only used for
-- fieldReference so it only has data needed for embedding
data EmbedEntityDef
EmbedEntityDef :: EntityNameHS -> [EmbedFieldDef] -> EmbedEntityDef
[embeddedHaskell] :: EmbedEntityDef -> EntityNameHS
[embeddedFields] :: EmbedEntityDef -> [EmbedFieldDef]
-- | There are 3 kinds of references 1) composite (to fields that exist in
-- the record) 2) single field 3) embedded
data ReferenceDef
NoReference :: ReferenceDef
-- | A ForeignRef has a late binding to the EntityDef it references via
-- name and has the Haskell type of the foreign key in the form of
-- FieldType
ForeignRef :: !EntityNameHS -> ReferenceDef
EmbedRef :: EntityNameHS -> ReferenceDef
CompositeRef :: CompositeDef -> ReferenceDef
-- | A SelfReference stops an immediate cycle which causes non-termination
-- at compile-time (issue #311).
SelfReference :: ReferenceDef
-- | A FieldType describes a field parsed from the QuasiQuoter and
-- is used to determine the Haskell type in the generated code.
--
-- name Text parses into FTTypeCon Nothing Text
--
-- name T.Text parses into FTTypeCon (Just T
-- Text)
--
-- name (Jsonb User) parses into:
--
-- -- FTApp (FTTypeCon Nothing Jsonb) (FTTypeCon Nothing User) --data FieldType -- | Optional module and name. FTTypeCon :: Maybe Text -> Text -> FieldType FTTypePromoted :: Text -> FieldType FTApp :: FieldType -> FieldType -> FieldType FTList :: FieldType -> FieldType -- | Attributes that may be attached to fields that can affect migrations -- and serialization in backend-specific ways. -- -- While we endeavor to, we can't forsee all use cases for all backends, -- and so FieldAttr is extensible through its constructor -- FieldAttrOther. data FieldAttr -- | The Maybe keyword goes after the type. This indicates that the -- column is nullable, and the generated Haskell code will have a -- Maybe type for it. -- -- Example: -- --
-- User -- name Text Maybe --FieldAttrMaybe :: FieldAttr -- | This indicates that the column is nullable, but should not have a -- Maybe type. For this to work out, you need to ensure that the -- PersistField instance for the type in question can support a -- PersistNull value. -- --
-- data What = NoWhat | Hello Text -- -- instance PersistField What where -- fromPersistValue PersistNull = -- pure NoWhat -- fromPersistValue pv = -- Hello $ fromPersistValue pv -- -- instance PersistFieldSql What where -- sqlType _ = SqlString -- -- User -- what What nullable --FieldAttrNullable :: FieldAttr -- | This tag means that the column will not be present on the Haskell -- code, but will not be removed from the database. Useful to deprecate -- fields in phases. -- -- You should set the column to be nullable in the database. Otherwise, -- inserts won't have values. -- --
-- User -- oldName Text MigrationOnly -- newName Text --FieldAttrMigrationOnly :: FieldAttr -- | A SafeToRemove attribute is not present on the Haskell -- datatype, and the backend migrations should attempt to drop the column -- without triggering any unsafe migration warnings. -- -- Useful after you've used MigrationOnly to remove a column -- from the database in phases. -- --
-- User -- oldName Text SafeToRemove -- newName Text --FieldAttrSafeToRemove :: FieldAttr -- | This attribute indicates that we should create a foreign key reference -- from a column. By default, persistent will try and create a -- foreign key reference for a column if it can determine that the type -- of the column is a Key entity or an -- EntityId and the Entity's name was present in -- mkPersist. -- -- This is useful if you want to use the explicit foreign key syntax. -- --
-- Post -- title Text -- -- Comment -- postId PostId noreference -- Foreign Post fk_comment_post postId --FieldAttrNoreference :: FieldAttr -- | This is set to specify precisely the database table the column refers -- to. -- --
-- Post -- title Text -- -- Comment -- postId PostId references="post" ---- -- You should not need this - persistent should be capable of -- correctly determining the target table's name. If you do need this, -- please file an issue describing why. FieldAttrReference :: Text -> FieldAttr -- | Specify a name for the constraint on the foreign key reference for -- this table. -- --
-- Post -- title Text -- -- Comment -- postId PostId constraint="my_cool_constraint_name" --FieldAttrConstraint :: Text -> FieldAttr -- | Specify the default value for a column. -- --
-- User -- createdAt UTCTime default="NOW()" ---- -- Note that a default= attribute does not mean you can omit the -- value while inserting. FieldAttrDefault :: Text -> FieldAttr -- | Specify a custom SQL type for the column. Generally, you should define -- a custom datatype with a custom PersistFieldSql instance -- instead of using this. -- --
-- User -- uuid Text sqltype=UUID --FieldAttrSqltype :: Text -> FieldAttr -- | Set a maximum length for a column. Useful for VARCHAR and indexes. -- --
-- User -- name Text maxlen=200 -- -- UniqueName name --FieldAttrMaxlen :: Integer -> FieldAttr -- | Specify the database name of the column. -- --
-- User -- blarghle Int sql="b_l_a_r_g_h_l_e" ---- -- Useful for performing phased migrations, where one column is renamed -- to another column over time. FieldAttrSql :: Text -> FieldAttr -- | A grab bag of random attributes that were unrecognized by the parser. FieldAttrOther :: Text -> FieldAttr type Attr = Text type ExtraLine = [Text] -- | The definition for the entity's primary key ID. data EntityIdDef -- | The entity has a single key column, and it is a surrogate key - that -- is, you can't go from rec -> Key rec. EntityIdField :: !FieldDef -> EntityIdDef -- | The entity has a natural key. This means you can write rec -> -- Key rec because all the key fields are present on the datatype. -- -- A natural key can have one or more columns. EntityIdNaturalKey :: !CompositeDef -> EntityIdDef -- | An EntityDef represents the information that -- persistent knows about an Entity. It uses this information to -- generate the Haskell datatype, the SQL migrations, and other relevant -- conversions. data EntityDef -- | The reason why a field is nullable is very important. A field -- that is nullable because of a Maybe tag will have its type -- changed from A to Maybe A. OTOH, a field that is -- nullable because of a nullable tag will remain with the same -- type. data WhyNullable ByMaybeAttr :: WhyNullable ByNullableAttr :: WhyNullable data IsNullable Nullable :: !WhyNullable -> IsNullable NotNullable :: IsNullable -- | A Checkmark should be used as a field type whenever a -- uniqueness constraint should guarantee that a certain kind of record -- may appear at most once, but other kinds of records may appear any -- number of times. -- -- NOTE: You need to mark any Checkmark fields as -- nullable (see the following example). -- -- For example, suppose there's a Location entity that -- represents where a user has lived: -- --
-- Location -- user UserId -- name Text -- current Checkmark nullable -- -- UniqueLocation user current ---- -- The UniqueLocation constraint allows any number of -- Inactive Locations to be current. However, -- there may be at most one current Location per user -- (i.e., either zero or one per user). -- -- This data type works because of the way that SQL treats -- NULLable fields within uniqueness constraints. The SQL -- standard says that NULL values should be considered -- different, so we represent Inactive as SQL NULL, thus -- allowing any number of Inactive records. On the other hand, we -- represent Active as TRUE, so the uniqueness constraint -- will disallow more than one Active record. -- -- Note: There may be DBMSs that do not respect the SQL standard's -- treatment of NULL values on uniqueness constraints, please -- check if this data type works before relying on it. -- -- The SQL BOOLEAN type is used because it's the smallest data -- type available. Note that we never use FALSE, just -- TRUE and NULL. Provides the same behavior Maybe -- () would if () was a valid PersistField. data Checkmark -- | When used on a uniqueness constraint, there may be at most one -- Active record. Active :: Checkmark -- | When used on a uniqueness constraint, there may be any number of -- Inactive records. Inactive :: Checkmark fieldAttrsContainsNullable :: [FieldAttr] -> IsNullable -- | Return the [FieldDef] for the entity keys. entitiesPrimary :: EntityDef -> NonEmpty FieldDef entityPrimary :: EntityDef -> Maybe CompositeDef -- | Returns a NonEmpty list of FieldDef that correspond with -- the key columns for an EntityDef. keyAndEntityFields :: EntityDef -> NonEmpty FieldDef -- | Parse raw field attributes into structured form. Any unrecognized -- attributes will be preserved, identically as they are encountered, as -- FieldAttrOther values. parseFieldAttrs :: [Text] -> [FieldAttr] isFieldNotGenerated :: FieldDef -> Bool -- | Returns True if the FieldDef does not have a -- MigrationOnly or SafeToRemove flag from the -- QuasiQuoter. isHaskellField :: FieldDef -> Bool -- | A FieldCascade that does nothing. noCascade :: FieldCascade -- | Renders a FieldCascade value such that it can be used in SQL -- migrations. renderFieldCascade :: FieldCascade -> Text -- | Render a CascadeAction to Text such that it can be used -- in a SQL command. renderCascadeAction :: CascadeAction -> Text -- | A Statement is a representation of a database query that has -- been prepared and stored on the server side. data Statement Statement :: IO () -> IO () -> ([PersistValue] -> IO Int64) -> (forall (m :: Type -> Type). MonadIO m => [PersistValue] -> Acquire (ConduitM () [PersistValue] m ())) -> Statement [stmtFinalize] :: Statement -> IO () [stmtReset] :: Statement -> IO () [stmtExecute] :: Statement -> [PersistValue] -> IO Int64 [stmtQuery] :: Statement -> forall (m :: Type -> Type). MonadIO m => [PersistValue] -> Acquire (ConduitM () [PersistValue] m ()) data InsertSqlResult ISRSingle :: Text -> InsertSqlResult ISRInsertGet :: Text -> Text -> InsertSqlResult ISRManyKeys :: Text -> [PersistValue] -> InsertSqlResult type LogFunc = Loc -> LogSource -> LogLevel -> LogStr -> IO () -- | Replace the FieldDef FieldAttr with the new list. setFieldAttrs :: [FieldAttr] -> FieldDef -> FieldDef -- | Modify the list of field attributes. overFieldAttrs :: ([FieldAttr] -> [FieldAttr]) -> FieldDef -> FieldDef -- | Add an attribute to the list of field attributes. addFieldAttr :: FieldAttr -> FieldDef -> FieldDef -- | Check if the field definition is nullable isFieldNullable :: FieldDef -> IsNullable -- | Check if the field is `Maybe a` isFieldMaybe :: FieldDef -> Bool -- | Retrieve the list of UniqueDef from an EntityDef. This -- currently does not include a Primary key, if one is defined. -- A future version of persistent will include a -- Primary key among the Unique constructors for the -- Entity. getEntityUniques :: EntityDef -> [UniqueDef] -- | Retrieve the Haskell name of the given entity. getEntityHaskellName :: EntityDef -> EntityNameHS -- | Return the database name for the given entity. getEntityDBName :: EntityDef -> EntityNameDB getEntityExtra :: EntityDef -> Map Text [[Text]] setEntityDBName :: EntityNameDB -> EntityDef -> EntityDef getEntityComments :: EntityDef -> Maybe Text getEntityForeignDefs :: EntityDef -> [ForeignDef] -- | Retrieve the list of FieldDef that makes up the fields of the -- entity. -- -- This does not return the fields for an Id column or an -- implicit id. It will return the key columns if you used the -- Primary syntax for defining the primary key. -- -- This does not return fields that are marked SafeToRemove or -- MigrationOnly - so it only returns fields that are -- represented in the Haskell type. If you need those fields, use -- getEntityFieldsDatabase. getEntityFields :: EntityDef -> [FieldDef] -- | This returns all of the FieldDef defined for the -- EntityDef, including those fields that are marked as -- MigrationOnly (and therefore only present in the database) or -- SafeToRemove (and a migration will drop the column if it -- exists in the database). -- -- For all the fields that are present on the Haskell-type, see -- getEntityFields. getEntityFieldsDatabase :: EntityDef -> [FieldDef] isEntitySum :: EntityDef -> Bool getEntityId :: EntityDef -> EntityIdDef getEntityIdField :: EntityDef -> Maybe FieldDef -- | Set an entityId to be the given FieldDef. setEntityId :: FieldDef -> EntityDef -> EntityDef setEntityIdDef :: EntityIdDef -> EntityDef -> EntityDef getEntityKeyFields :: EntityDef -> NonEmpty FieldDef -- | Perform a mapping function over all of the entity fields, as -- determined by getEntityFieldsDatabase. overEntityFields :: ([FieldDef] -> [FieldDef]) -> EntityDef -> EntityDef data SomePersistField SomePersistField :: a -> SomePersistField -- | Prior to persistent-2.11.0, we provided an instance of -- PersistField for the Natural type. This was in error, -- because Natural represents an infinite value, and databases -- don't have reasonable types for this. -- -- The instance for Natural used the Int64 underlying type, -- which will cause underflow and overflow errors. This type has the -- exact same code in the instances, and will work seamlessly. -- -- A more appropriate type for this is the Word series of types -- from Data.Word. These have a bounded size, are guaranteed to be -- non-negative, and are quite efficient for the database to store. newtype OverflowNatural OverflowNatural :: Natural -> OverflowNatural [unOverflowNatural] :: OverflowNatural -> Natural -- | This class teaches Persistent how to take a custom type and marshal it -- to and from a PersistValue, allowing it to be stored in a -- database. -- --
-- {-# LANGUAGE GeneralizedNewtypeDeriving #-}
--
-- newtype HashedPassword = HashedPassword ByteString
-- deriving (Eq, Show, PersistField, PersistFieldSql)
--
--
--
-- {-# LANGUAGE GeneralizedNewtypeDeriving #-}
-- import qualified Data.Text as T
-- import qualified Data.Char as C
--
-- -- | An American Social Security Number
-- newtype SSN = SSN Text
-- deriving (Eq, Show, PersistFieldSql)
--
-- mkSSN :: Text -> Either Text SSN
-- mkSSN t = if (T.length t == 9) && (T.all C.isDigit t)
-- then Right $ SSN t
-- else Left $ "Invalid SSN: " <> t
--
-- instance PersistField SSN where
-- toPersistValue (SSN t) = PersistText t
-- fromPersistValue (PersistText t) = mkSSN t
-- -- Handle cases where the database does not give us PersistText
-- fromPersistValue x = Left $ "File.hs: When trying to deserialize an SSN: expected PersistText, received: " <> T.pack (show x)
--
--
-- Tips:
--
-- -- instance ToJSON (Entity User) where -- toJSON = keyValueEntityToJSON --keyValueEntityToJSON :: (PersistEntity record, ToJSON record) => Entity record -> Value -- | Predefined parseJSON. The input JSON looks like {"key": -- 1, "value": {"name": ...}}. -- -- The typical usage is: -- --
-- instance FromJSON (Entity User) where -- parseJSON = keyValueEntityFromJSON --keyValueEntityFromJSON :: (PersistEntity record, FromJSON record) => Value -> Parser (Entity record) -- | Predefined toJSON. The resulting JSON looks like {"id": -- 1, "name": ...}. -- -- The typical usage is: -- --
-- instance ToJSON (Entity User) where -- toJSON = entityIdToJSON --entityIdToJSON :: (PersistEntity record, ToJSON record) => Entity record -> Value -- | Predefined parseJSON. The input JSON looks like {"id": 1, -- "name": ...}. -- -- The typical usage is: -- --
-- instance FromJSON (Entity User) where -- parseJSON = entityIdFromJSON --entityIdFromJSON :: (PersistEntity record, FromJSON record) => Value -> Parser (Entity record) -- | Convenience function for getting a free PersistField instance -- from a type with JSON instances. -- -- Example usage in combination with fromPersistValueJSON: -- --
-- instance PersistField MyData where -- fromPersistValue = fromPersistValueJSON -- toPersistValue = toPersistValueJSON --toPersistValueJSON :: ToJSON a => a -> PersistValue -- | Convenience function for getting a free PersistField instance -- from a type with JSON instances. The JSON parser used will accept JSON -- values other that object and arrays. So, if your instance serializes -- the data to a JSON string, this will still work. -- -- Example usage in combination with toPersistValueJSON: -- --
-- instance PersistField MyData where -- fromPersistValue = fromPersistValueJSON -- toPersistValue = toPersistValueJSON --fromPersistValueJSON :: FromJSON a => PersistValue -> Either Text a class (Show BackendKey backend, Read BackendKey backend, Eq BackendKey backend, Ord BackendKey backend, PersistStoreRead backend, PersistField BackendKey backend, ToJSON BackendKey backend, FromJSON BackendKey backend) => PersistStoreWrite backend -- | Create multiple records in the database and return their Keys. -- -- If you don't need the inserted Keys, use insertMany_. -- -- The MongoDB and PostgreSQL backends insert all records and retrieve -- their keys in one database query. -- -- The SQLite and MySQL backends use the slow, default implementation of -- mapM insert. -- --
-- insertUsers :: MonadIO m => ReaderT SqlBackend m [Key User] -- insertUsers = insertMany [User "John" 30, User "Nick" 32, User "Jane" 20] ---- --
-- userIds <- insertUsers ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |SPJ |40 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ -- |3 |John |30 | -- +-----+------+-----+ -- |4 |Nick |32 | -- +-----+------+-----+ -- |5 |Jane |20 | -- +-----+------+-----+ --insertMany :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => [record] -> ReaderT backend m [Key record] -- | Put the record in the database with the given key. Unlike -- replace, if a record with the given key does not exist then a -- new record will be inserted. -- --
-- insertPhilip :: MonadIO m => ReaderT SqlBackend m (Key User) -- insertPhilip = insert $ User "Philip" 42 ---- --
-- philipId <- insertPhilip ---- -- This query will produce: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |SPJ |40 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ -- |3 |Philip|42 | -- +-----+------+-----+ ---- --
-- repsertHaskell :: MonadIO m => Key record -> ReaderT SqlBackend m () -- repsertHaskell id = repsert id $ User "Haskell" 81 ---- --
-- repsertHaskell philipId ---- -- This query will replace Philip's record with Haskell's one: -- --
-- +-----+-----------------+--------+ -- |id |name |age | -- +-----+-----------------+--------+ -- |1 |SPJ |40 | -- +-----+-----------------+--------+ -- |2 |Simon |41 | -- +-----+-----------------+--------+ -- |3 |Philip -> Haskell|42 -> 81| -- +-----+-----------------+--------+ ---- -- repsert inserts the given record if the key doesn't exist. -- --
-- repsertXToUnknown :: MonadIO m => ReaderT SqlBackend m () -- repsertXToUnknown = repsert unknownId $ User "X" 999 ---- -- For example, applying the above query to dataset-1 will produce -- this: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |SPJ |40 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ -- |3 |X |999 | -- +-----+------+-----+ --repsert :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => Key record -> record -> ReaderT backend m () -- | Put many entities into the database. -- -- Batch version of repsert for SQL backends. -- -- Useful when migrating data from one entity to another and want to -- preserve ids. -- --
-- repsertManyUsers :: MonadIO m =>ReaderT SqlBackend m () -- repsertManyusers = repsertMany [(simonId, User "Philip" 20), (unknownId999, User "Mr. X" 999)] ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+----------------+---------+ -- |id |name |age | -- +-----+----------------+---------+ -- |1 |SPJ |40 | -- +-----+----------------+---------+ -- |2 |Simon -> Philip |41 -> 20 | -- +-----+----------------+---------+ -- |999 |Mr. X |999 | -- +-----+----------------+---------+ --repsertMany :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => [(Key record, record)] -> ReaderT backend m () -- | Replace the record in the database with the given key. Note that the -- result is undefined if such record does not exist, so you must use -- insertKey or repsert in these cases. -- --
-- replaceSpj :: MonadIO m => User -> ReaderT SqlBackend m () -- replaceSpj record = replace spjId record ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |Mike |45 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ --replace :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => Key record -> record -> ReaderT backend m () -- | Update individual fields on a specific record, and retrieve the -- updated value from the database. -- -- Note that this function will throw an exception if the given key is -- not found in the database. -- --
-- updateGetSpj :: MonadIO m => [Update User] -> ReaderT SqlBackend m User -- updateGetSpj updates = updateGet spjId updates ---- --
-- spj <- updateGetSpj [UserAge +=. 100] ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |SPJ |140 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ --updateGet :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => Key record -> [Update record] -> ReaderT backend m record class (Show BackendKey backend, Read BackendKey backend, Eq BackendKey backend, Ord BackendKey backend, PersistCore backend, PersistField BackendKey backend, ToJSON BackendKey backend, FromJSON BackendKey backend) => PersistStoreRead backend class PersistCore backend where { data family BackendKey backend; } data family BackendKey backend -- | ToBackendKey converts a PersistEntity Key into a -- BackendKey This can be used by each backend to convert between -- a Key and a plain Haskell type. For Sql, that is done with -- toSqlKey and fromSqlKey. -- -- By default, a PersistEntity uses the default BackendKey -- for its Key and is an instance of ToBackendKey -- -- A Key that instead uses a custom type will not be an instance -- of ToBackendKey. class (PersistEntity record, PersistEntityBackend record ~ backend, PersistCore backend) => ToBackendKey backend record toBackendKey :: ToBackendKey backend record => Key record -> BackendKey backend fromBackendKey :: ToBackendKey backend record => BackendKey backend -> Key record -- | A convenient alias for common type signatures type PersistRecordBackend record backend = (PersistEntity record, PersistEntityBackend record ~ BaseBackend backend) -- | This class witnesses that two backend are compatible, and that you can -- convert from the sub backend into the sup backend. -- This is similar to the HasPersistBackend and -- IsPersistBackend classes, but where you don't want to fix the -- type associated with the PersistEntityBackend of a record. -- -- Generally speaking, where you might have: -- --
-- foo :: -- ( PersistEntity record -- , PeristEntityBackend record ~ BaseBackend backend -- , IsSqlBackend backend -- ) ---- -- this can be replaced with: -- --
-- foo :: -- ( PersistEntity record, -- , PersistEntityBackend record ~ backend -- , BackendCompatible SqlBackend backend -- ) ---- -- This works for SqlReadBackend because of the instance -- BackendCompatible SqlBackend -- SqlReadBackend, without needing to go through the -- BaseBackend type family. -- -- Likewise, functions that are currently hardcoded to use -- SqlBackend can be generalized: -- --
-- -- before: -- asdf :: ReaderT SqlBackend m () -- asdf = pure () -- -- -- after: -- asdf' :: BackendCompatible SqlBackend backend => ReaderT backend m () -- asdf' = withCompatibleBackend asdf --class BackendCompatible sup sub projectBackend :: BackendCompatible sup sub => sub -> sup -- | Class which witnesses that backend is essentially the same as -- BaseBackend backend. That is, they're isomorphic and -- backend is just some wrapper over BaseBackend -- backend. class HasPersistBackend backend => IsPersistBackend backend -- | Class which allows the plucking of a BaseBackend backend from -- some larger type. For example, instance HasPersistBackend -- (SqlReadBackend, Int) where type BaseBackend (SqlReadBackend, Int) = -- SqlBackend persistBackend = unSqlReadBackend . fst class HasPersistBackend backend where { type family BaseBackend backend; } persistBackend :: HasPersistBackend backend => backend -> BaseBackend backend type family BaseBackend backend -- | Run a query against a larger backend by plucking out BaseBackend -- backend -- -- This is a helper for reusing existing queries when expanding the -- backend type. withBaseBackend :: forall backend (m :: Type -> Type) a. HasPersistBackend backend => ReaderT (BaseBackend backend) m a -> ReaderT backend m a -- | Run a query against a compatible backend, by projecting the backend -- -- This is a helper for using queries which run against a specific -- backend type that your backend is compatible with. withCompatibleBackend :: forall sup sub (m :: Type -> Type) a. BackendCompatible sup sub => ReaderT sup m a -> ReaderT sub m a liftPersist :: (MonadIO m, MonadReader backend m) => ReaderT backend IO b -> m b -- | Same as get, but for a non-null (not Maybe) foreign key. Unsafe -- unless your database is enforcing that the foreign key is valid. -- --
-- getJustSpj :: MonadIO m => ReaderT SqlBackend m User -- getJustSpj = getJust spjId ---- --
-- spj <- getJust spjId ---- -- The above query when applied on dataset-1, will get this -- record: -- --
-- +----+------+-----+ -- | id | name | age | -- +----+------+-----+ -- | 1 | SPJ | 40 | -- +----+------+-----+ ---- --
-- getJustUnknown :: MonadIO m => ReaderT SqlBackend m User -- getJustUnknown = getJust unknownId ---- -- mrx <- getJustUnknown -- -- This just throws an error. getJust :: forall record backend (m :: Type -> Type). (PersistStoreRead backend, PersistRecordBackend record backend, MonadIO m) => Key record -> ReaderT backend m record -- | Same as getJust, but returns an Entity instead of just -- the record. -- --
-- getJustEntitySpj :: MonadIO m => ReaderT SqlBackend m (Entity User) -- getJustEntitySpj = getJustEntity spjId ---- --
-- spjEnt <- getJustEntitySpj ---- -- The above query when applied on dataset-1, will get this -- entity: -- --
-- +----+------+-----+ -- | id | name | age | -- +----+------+-----+ -- | 1 | SPJ | 40 | -- +----+------+-----+ --getJustEntity :: forall record backend (m :: Type -> Type). (PersistEntityBackend record ~ BaseBackend backend, MonadIO m, PersistEntity record, PersistStoreRead backend) => Key record -> ReaderT backend m (Entity record) -- | Curry this to make a convenience function that loads an associated -- model. -- --
-- foreign = belongsTo foreignId --belongsTo :: forall ent1 ent2 backend (m :: Type -> Type). (PersistStoreRead backend, PersistEntity ent1, PersistRecordBackend ent2 backend, MonadIO m) => (ent1 -> Maybe (Key ent2)) -> ent1 -> ReaderT backend m (Maybe ent2) -- | Same as belongsTo, but uses getJust and therefore is -- similarly unsafe. belongsToJust :: forall ent1 ent2 backend (m :: Type -> Type). (PersistStoreRead backend, PersistEntity ent1, PersistRecordBackend ent2 backend, MonadIO m) => (ent1 -> Key ent2) -> ent1 -> ReaderT backend m ent2 -- | Like insert, but returns the complete Entity. -- --
-- insertHaskellEntity :: MonadIO m => ReaderT SqlBackend m (Entity User) -- insertHaskellEntity = insertEntity $ User "Haskell" 81 ---- --
-- haskellEnt <- insertHaskellEntity ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +----+---------+-----+ -- | id | name | age | -- +----+---------+-----+ -- | 1 | SPJ | 40 | -- +----+---------+-----+ -- | 2 | Simon | 41 | -- +----+---------+-----+ -- | 3 | Haskell | 81 | -- +----+---------+-----+ --insertEntity :: forall e backend (m :: Type -> Type). (PersistStoreWrite backend, PersistRecordBackend e backend, MonadIO m) => e -> ReaderT backend m (Entity e) -- | Like insertEntity but just returns the record instead of -- Entity. -- --
-- insertDaveRecord :: MonadIO m => ReaderT SqlBackend m User -- insertDaveRecord = insertRecord $ User "Dave" 50 ---- --
-- dave <- insertDaveRecord ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |SPJ |40 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ -- |3 |Dave |50 | -- +-----+------+-----+ --insertRecord :: forall record backend (m :: Type -> Type). (PersistEntityBackend record ~ BaseBackend backend, PersistEntity record, MonadIO m, PersistStoreWrite backend) => record -> ReaderT backend m record -- | A SqlBackend represents a handle or connection to a database. -- It contains functions and values that allow databases to have more -- optimized implementations, as well as references that benefit -- performance and sharing. -- -- Instead of using the SqlBackend constructor directly, use the -- mkSqlBackend function. -- -- A SqlBackend is *not* thread-safe. You should not assume that a -- SqlBackend can be shared among threads and run concurrent -- queries. This *will* result in problems. Instead, you should create a -- Pool SqlBackend, known as a -- ConnectionPool, and pass that around in multi-threaded -- applications. -- -- To run actions in the persistent library, you should use the -- runSqlConn function. If you're using a multithreaded -- application, use the runSqlPool function. data SqlBackend -- | This class is used to ensure that functions requring at least one -- unique key are not called with records that have 0 unique keys. The -- quasiquoter automatically writes working instances for appropriate -- entities, and generates TypeError instances for records that -- have 0 unique keys. class PersistEntity record => AtLeastOneUniqueKey record requireUniquesP :: AtLeastOneUniqueKey record => record -> NonEmpty (Unique record) -- | This is an error message. It is used when an entity has multiple -- unique keys, and the function expects a single unique key. type MultipleUniqueKeysError ty = 'Text "The entity " :<>: 'ShowType ty :<>: 'Text " has multiple unique keys." :$$: 'Text "The function you are trying to call requires only a single " :<>: 'Text "unique key." :$$: 'Text "There is probably a variant of the function with 'By' " :<>: 'Text "appended that will allow you to select a unique key " :<>: 'Text "for the operation." -- | This is an error message. It is used when writing instances of -- OnlyOneUniqueKey for an entity that has no unique keys. type NoUniqueKeysError ty = 'Text "The entity " :<>: 'ShowType ty :<>: 'Text " does not have any unique keys." :$$: 'Text "The function you are trying to call requires a unique key " :<>: 'Text "to be defined on the entity." -- | This class is used to ensure that upsert is only called on -- records that have a single Unique key. The quasiquoter -- automatically generates working instances for appropriate records, and -- generates TypeError instances for records that have 0 or -- multiple unique keys. class PersistEntity record => OnlyOneUniqueKey record onlyUniqueP :: OnlyOneUniqueKey record => record -> Unique record -- | Some functions in this module (insertUnique, insertBy, -- and replaceUnique) first query the unique indexes to check for -- conflicts. You could instead optimistically attempt to perform the -- operation (e.g. replace instead of replaceUnique). -- However, -- --
-- deleteBySpjName :: MonadIO m => ReaderT SqlBackend m () -- deleteBySpjName = deleteBy UniqueUserName "SPJ" ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ --deleteBy :: forall record (m :: Type -> Type). (PersistUniqueWrite backend, MonadIO m, PersistRecordBackend record backend) => Unique record -> ReaderT backend m () -- | Like insert, but returns Nothing when the record -- couldn't be inserted because of a uniqueness constraint. -- --
-- linusId <- insertUnique $ User "Linus" 48 -- spjId <- insertUnique $ User "SPJ" 90 ---- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |SPJ |40 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ -- |3 |Linus |48 | -- +-----+------+-----+ ---- -- Linus's record was inserted to dataset-1, while SPJ wasn't -- because SPJ already exists in dataset-1. insertUnique :: forall record (m :: Type -> Type). (PersistUniqueWrite backend, MonadIO m, PersistRecordBackend record backend) => record -> ReaderT backend m (Maybe (Key record)) -- | Update based on a uniqueness constraint or insert: -- --
-- upsertSpj :: MonadIO m => [Update User] -> ReaderT SqlBackend m (Maybe (Entity User)) -- upsertSpj updates = upsert (User "SPJ" 999) upadtes ---- --
-- mSpjEnt <- upsertSpj [UserAge +=. 15] ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+-----+--------+ -- |id |name |age | -- +-----+-----+--------+ -- |1 |SPJ |40 -> 55| -- +-----+-----+--------+ -- |2 |Simon|41 | -- +-----+-----+--------+ ---- --
-- upsertX :: MonadIO m => [Update User] -> ReaderT SqlBackend m (Maybe (Entity User)) -- upsertX updates = upsert (User "X" 999) updates ---- --
-- mXEnt <- upsertX [UserAge +=. 15] ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+-----+--------+ -- |id |name |age | -- +-----+-----+--------+ -- |1 |SPJ |40 | -- +-----+-----+--------+ -- |2 |Simon|41 | -- +-----+-----+--------+ -- |3 |X |999 | -- +-----+-----+--------+ ---- -- Next, what if the schema has two uniqueness constraints? Let's check -- it out using schema-2: -- --
-- mSpjEnt <- upsertSpj [UserAge +=. 15] ---- -- This fails with a compile-time type error alerting us to the fact that -- this record has multiple unique keys, and suggests that we look for -- upsertBy to select the unique key we want. upsert :: forall record (m :: Type -> Type). (PersistUniqueWrite backend, MonadIO m, PersistRecordBackend record backend, OnlyOneUniqueKey record) => record -> [Update record] -> ReaderT backend m (Entity record) -- | Update based on a given uniqueness constraint or insert: -- --
-- upsertBySpjName :: MonadIO m => User -> [Update User] -> ReaderT SqlBackend m (Entity User) -- upsertBySpjName record updates = upsertBy (UniqueUserName "SPJ") record updates ---- --
-- mSpjEnt <- upsertBySpjName (Person "X" 999) [PersonAge += .15] ---- -- The above query will alter dataset-1 to: -- --
-- +-----+-----+--------+ -- |id |name |age | -- +-----+-----+--------+ -- |1 |SPJ |40 -> 55| -- +-----+-----+--------+ -- |2 |Simon|41 | -- +-----+-----+--------+ ---- --
-- upsertBySimonAge :: MonadIO m => User -> [Update User] -> ReaderT SqlBackend m (Entity User) -- upsertBySimonAge record updates = upsertBy (UniqueUserName "SPJ") record updates ---- --
-- mPhilipEnt <- upsertBySimonAge (User "X" 999) [UserName =. "Philip"] ---- -- The above query will alter dataset-1 to: -- --
-- +----+-----------------+-----+ -- | id | name | age | -- +----+-----------------+-----+ -- | 1 | SPJ | 40 | -- +----+-----------------+-----+ -- | 2 | Simon -> Philip | 41 | -- +----+-----------------+-----+ ---- --
-- upsertByUnknownName :: MonadIO m => User -> [Update User] -> ReaderT SqlBackend m (Entity User) -- upsertByUnknownName record updates = upsertBy (UniqueUserName "Unknown") record updates ---- --
-- mXEnt <- upsertByUnknownName (User "X" 999) [UserAge +=. 15] ---- -- This query will alter dataset-1 to: -- --
-- +-----+-----+-----+ -- |id |name |age | -- +-----+-----+-----+ -- |1 |SPJ |40 | -- +-----+-----+-----+ -- |2 |Simon|41 | -- +-----+-----+-----+ -- |3 |X |999 | -- +-----+-----+-----+ --upsertBy :: forall record (m :: Type -> Type). (PersistUniqueWrite backend, MonadIO m, PersistRecordBackend record backend) => Unique record -> record -> [Update record] -> ReaderT backend m (Entity record) -- | Put many records into db -- --
-- l1 <- insertBy $ User "SPJ" 20 -- l2 <- insertBy $ User "XXX" 41 -- l3 <- insertBy $ User "SPJ" 40 -- r1 <- insertBy $ User "XXX" 100 ---- -- First three lines return Left because there're duplicates in -- given record's uniqueness constraints. While the last line returns a -- new key as Right. insertBy :: forall record backend (m :: Type -> Type). (MonadIO m, PersistUniqueWrite backend, PersistRecordBackend record backend, AtLeastOneUniqueKey record) => record -> ReaderT backend m (Either (Entity record) (Key record)) -- | Like insertEntity, but returns Nothing when the record -- couldn't be inserted because of a uniqueness constraint. -- --
-- insertUniqueSpjEntity :: MonadIO m => ReaderT SqlBackend m (Maybe (Entity User)) -- insertUniqueSpjEntity = insertUniqueEntity $ User "SPJ" 50 ---- --
-- mSpjEnt <- insertUniqueSpjEntity ---- -- The above query results Nothing as SPJ already exists. -- --
-- insertUniqueAlexaEntity :: MonadIO m => ReaderT SqlBackend m (Maybe (Entity User)) -- insertUniqueAlexaEntity = insertUniqueEntity $ User "Alexa" 3 ---- --
-- mAlexaEnt <- insertUniqueSpjEntity ---- -- Because there's no such unique keywords of the given record, the above -- query when applied on dataset-1, will produce this: -- --
-- +----+-------+-----+ -- | id | name | age | -- +----+-------+-----+ -- | 1 | SPJ | 40 | -- +----+-------+-----+ -- | 2 | Simon | 41 | -- +----+-------+-----+ -- | 3 | Alexa | 3 | -- +----+-------+-----+ --insertUniqueEntity :: forall record backend (m :: Type -> Type). (MonadIO m, PersistRecordBackend record backend, PersistUniqueWrite backend) => record -> ReaderT backend m (Maybe (Entity record)) -- | Return the single unique key for a record. -- --
-- onlySimonConst :: MonadIO m => ReaderT SqlBackend m (Unique User) -- onlySimonConst = onlyUnique $ User "Simon" 999 ---- --
-- mSimonConst <- onlySimonConst ---- -- mSimonConst would be Simon's uniqueness constraint. Note that -- onlyUnique doesn't work if there're more than two -- constraints. It will fail with a type error instead. onlyUnique :: forall record backend (m :: Type -> Type). (MonadIO m, PersistUniqueWrite backend, PersistRecordBackend record backend, OnlyOneUniqueKey record) => record -> ReaderT backend m (Unique record) -- | A modification of getBy, which takes the PersistEntity -- itself instead of a Unique record. Returns a record matching -- one of the unique keys. This function makes the most sense on -- entities with a single Unique constructor. -- --
-- mSpjEnt <- getBySpjValue ---- -- The above query when applied on dataset-1, will get this -- record: -- --
-- +----+------+-----+ -- | id | name | age | -- +----+------+-----+ -- | 1 | SPJ | 40 | -- +----+------+-----+ --getByValue :: forall record (m :: Type -> Type) backend. (MonadIO m, PersistUniqueRead backend, PersistRecordBackend record backend, AtLeastOneUniqueKey record) => record -> ReaderT backend m (Maybe (Entity record)) -- | Attempt to replace the record of the given key with the given new -- record. First query the unique fields to make sure the replacement -- maintains uniqueness constraints. -- -- Return Nothing if the replacement was made. If uniqueness is -- violated, return a Just with the Unique violation replaceUnique :: forall record backend (m :: Type -> Type). (MonadIO m, Eq (Unique record), PersistRecordBackend record backend, PersistUniqueWrite backend) => Key record -> record -> ReaderT backend m (Maybe (Unique record)) -- | Check whether there are any conflicts for unique keys with this entity -- and existing entities in the database. -- -- Returns Nothing if the entity would be unique, and could thus -- safely be inserted. on a conflict returns the conflicting key -- --
-- mAlanConst <- checkUnique $ User "Alan" 70 ---- -- While this would be Just because SPJ already exists: -- --
-- mSpjConst <- checkUnique $ User "SPJ" 60 --checkUnique :: forall record backend (m :: Type -> Type). (MonadIO m, PersistRecordBackend record backend, PersistUniqueRead backend) => record -> ReaderT backend m (Maybe (Unique record)) -- | Check whether there are any conflicts for unique keys with this entity -- and existing entities in the database. -- -- Returns Nothing if the entity would stay unique, and could thus -- safely be updated. on a conflict returns the conflicting key -- -- This is similar to checkUnique, except it's useful for updating -- - when the particular entity already exists, it would normally -- conflict with itself. This variant ignores those conflicts -- --
-- mAlanConst <- checkUnique $ User "Alan" 70 ---- -- While this would be Just because SPJ already exists: -- --
-- mSpjConst <- checkUnique $ User "SPJ" 60 --checkUniqueUpdateable :: forall record backend (m :: Type -> Type). (MonadIO m, PersistRecordBackend record backend, PersistUniqueRead backend) => Entity record -> ReaderT backend m (Maybe (Unique record)) -- | Backends supporting conditional write operations class (PersistQueryRead backend, PersistStoreWrite backend) => PersistQueryWrite backend -- | Backends supporting conditional read operations. class (PersistCore backend, PersistStoreRead backend) => PersistQueryRead backend -- | Get all records matching the given criterion in the specified order. -- Returns also the identifiers. -- -- NOTE: This function returns an Acquire and a ConduitM, -- which implies that it streams from the database. It does not. Please -- use selectList to simplify the code. If you want streaming -- behavior, consider persistent-pagination which efficiently -- chunks a query into ranges, or investigate a backend-specific -- streaming solution. selectSourceRes :: forall record (m1 :: Type -> Type) (m2 :: Type -> Type). (PersistQueryRead backend, PersistRecordBackend record backend, MonadIO m1, MonadIO m2) => [Filter record] -> [SelectOpt record] -> ReaderT backend m1 (Acquire (ConduitM () (Entity record) m2 ())) -- | Get the Keys of all records matching the given criterion. selectKeysRes :: forall (m1 :: Type -> Type) (m2 :: Type -> Type) record. (PersistQueryRead backend, MonadIO m1, MonadIO m2, PersistRecordBackend record backend) => [Filter record] -> [SelectOpt record] -> ReaderT backend m1 (Acquire (ConduitM () (Key record) m2 ())) -- | Get the Keys of all records matching the given criterion. -- -- For an example, see selectList. selectKeys :: forall record backend (m :: Type -> Type). (PersistQueryRead backend, MonadResource m, PersistRecordBackend record backend, MonadReader backend m) => [Filter record] -> [SelectOpt record] -> ConduitM () (Key record) m () -- | For combinations of backends and entities that support -- cascade-deletion. “Cascade-deletion” means that entries that depend on -- other entries to be deleted will be deleted as well. class (PersistStoreWrite backend, PersistEntity record, BaseBackend backend ~ PersistEntityBackend record) => DeleteCascade record backend -- | Perform cascade-deletion of single database entry. deleteCascade :: forall (m :: Type -> Type). (DeleteCascade record backend, MonadIO m) => Key record -> ReaderT backend m () -- | Cascade-deletion of entries satisfying given filters. deleteCascadeWhere :: forall record backend (m :: Type -> Type). (MonadIO m, DeleteCascade record backend, PersistQueryWrite backend) => [Filter record] -> ReaderT backend m () -- | A backwards-compatible alias for those that don't care about -- distinguishing between read and write queries. It signifies the -- assumption that, by default, a backend can write as well as read. type PersistStore a = PersistStoreWrite a -- | A backwards-compatible alias for those that don't care about -- distinguishing between read and write queries. It signifies the -- assumption that, by default, a backend can write as well as read. type PersistUnique a = PersistUniqueWrite a -- | A backend which is a wrapper around SqlBackend. type IsSqlBackend backend = (IsPersistBackend backend, BaseBackend backend ~ SqlBackend) -- | Like SqlPersistT but compatible with any SQL backend which -- can handle read and write queries. type SqlWriteT (m :: Type -> Type) a = forall backend. SqlBackendCanWrite backend => ReaderT backend m a -- | Like SqlPersistT but compatible with any SQL backend which -- can handle read queries. type SqlReadT (m :: Type -> Type) a = forall backend. SqlBackendCanRead backend => ReaderT backend m a -- | A constraint synonym which witnesses that a backend is SQL and can run -- read and write queries. type SqlBackendCanWrite backend = (SqlBackendCanRead backend, PersistQueryWrite backend, PersistStoreWrite backend, PersistUniqueWrite backend) -- | A constraint synonym which witnesses that a backend is SQL and can run -- read queries. type SqlBackendCanRead backend = (BackendCompatible SqlBackend backend, PersistQueryRead backend, PersistStoreRead backend, PersistUniqueRead backend) -- | An SQL backend which can handle read or write queries -- -- The constructor was exposed in 2.10.0 newtype SqlWriteBackend SqlWriteBackend :: SqlBackend -> SqlWriteBackend [$sel:unSqlWriteBackend:SqlWriteBackend] :: SqlWriteBackend -> SqlBackend -- | An SQL backend which can only handle read queries -- -- The constructor was exposed in 2.10.0. newtype SqlReadBackend SqlReadBackend :: SqlBackend -> SqlReadBackend [$sel:unSqlReadBackend:SqlReadBackend] :: SqlReadBackend -> SqlBackend -- | Useful for running a write query against an untagged backend with -- unknown capabilities. writeToUnknown :: forall (m :: Type -> Type) a. Monad m => ReaderT SqlWriteBackend m a -> ReaderT SqlBackend m a -- | Useful for running a read query against a backend with read and write -- capabilities. readToWrite :: forall (m :: Type -> Type) a. Monad m => ReaderT SqlReadBackend m a -> ReaderT SqlWriteBackend m a -- | Useful for running a read query against a backend with unknown -- capabilities. readToUnknown :: forall (m :: Type -> Type) a. Monad m => ReaderT SqlReadBackend m a -> ReaderT SqlBackend m a -- | A single column (see rawSql). Any PersistField may -- be used here, including PersistValue (which does not do any -- processing). newtype Single a Single :: a -> Single a [unSingle] :: Single a -> a -- | Values to configure a pool of database connections. See -- Data.Pool for details. data ConnectionPoolConfig ConnectionPoolConfig :: Int -> NominalDiffTime -> Int -> ConnectionPoolConfig -- | How many stripes to divide the pool into. See Data.Pool for -- details. Default: 1. [connectionPoolConfigStripes] :: ConnectionPoolConfig -> Int -- | How long connections can remain idle before being disposed of, in -- seconds. Default: 600 [connectionPoolConfigIdleTimeout] :: ConnectionPoolConfig -> NominalDiffTime -- | How many connections should be held in the connection pool. Default: -- 10 [connectionPoolConfigSize] :: ConnectionPoolConfig -> Int type ConnectionPool = Pool SqlBackend type SqlPersistM = SqlPersistT NoLoggingT ResourceT IO type SqlPersistT = ReaderT SqlBackend data PersistentSqlException StatementAlreadyFinalized :: Text -> PersistentSqlException Couldn'tGetSQLConnection :: PersistentSqlException -- | This value specifies how a field references another table. data ColumnReference ColumnReference :: !EntityNameDB -> !ConstraintNameDB -> !FieldCascade -> ColumnReference -- | The table name that the [crTableName] :: ColumnReference -> !EntityNameDB -- | The name of the foreign key constraint. [crConstraintName] :: ColumnReference -> !ConstraintNameDB -- | Whether or not updates/deletions to the referenced table cascade to -- this table. [crFieldCascade] :: ColumnReference -> !FieldCascade data Column Column :: !FieldNameDB -> !Bool -> !SqlType -> !Maybe Text -> !Maybe Text -> !Maybe ConstraintNameDB -> !Maybe Integer -> !Maybe ColumnReference -> Column [cName] :: Column -> !FieldNameDB [cNull] :: Column -> !Bool [cSqlType] :: Column -> !SqlType [cDefault] :: Column -> !Maybe Text [cGenerated] :: Column -> !Maybe Text [cDefaultConstraintName] :: Column -> !Maybe ConstraintNameDB [cMaxLen] :: Column -> !Maybe Integer [cReference] :: Column -> !Maybe ColumnReference -- | Initializes a ConnectionPoolConfig with default values. See the -- documentation of ConnectionPoolConfig for each field's default -- value. defaultConnectionPoolConfig :: ConnectionPoolConfig -- | Record of functions to override the default behavior in -- mkColumns. It is recommended you initialize this with -- emptyBackendSpecificOverrides and override the default values, -- so that as new fields are added, your code still compiles. -- -- For added safety, use the getBackendSpecific* and -- setBackendSpecific* functions, as a breaking change to the -- record field labels won't be reflected in a major version bump of the -- library. data BackendSpecificOverrides -- | If the override is defined, then this returns a function that accepts -- an entity name and field name and provides the ConstraintNameDB -- for the foreign key constraint. -- -- An abstract accessor for the BackendSpecificOverrides getBackendSpecificForeignKeyName :: BackendSpecificOverrides -> Maybe (EntityNameDB -> FieldNameDB -> ConstraintNameDB) -- | Set the backend's foreign key generation function to this value. setBackendSpecificForeignKeyName :: (EntityNameDB -> FieldNameDB -> ConstraintNameDB) -> BackendSpecificOverrides -> BackendSpecificOverrides -- | Creates an empty BackendSpecificOverrides (i.e. use the default -- behavior; no overrides) emptyBackendSpecificOverrides :: BackendSpecificOverrides defaultAttribute :: [FieldAttr] -> Maybe Text -- | Create the list of columns for the given entity. mkColumns :: [EntityDef] -> EntityDef -> BackendSpecificOverrides -> ([Column], [UniqueDef], [ForeignDef]) -- | A more general way to convert instances of ToJSON type class to -- strict text Text. toJsonText :: ToJSON j => j -> Text -- | Tells Persistent what database column type should be used to store a -- Haskell type. -- --
-- data Switch = On | Off -- deriving (Show, Eq) -- -- instance PersistField Switch where -- toPersistValue s = case s of -- On -> PersistBool True -- Off -> PersistBool False -- fromPersistValue (PersistBool b) = if b then Right On else Right Off -- fromPersistValue x = Left $ "File.hs: When trying to deserialize a Switch: expected PersistBool, received: " <> T.pack (show x) -- -- instance PersistFieldSql Switch where -- sqlType _ = SqlBool ---- --
-- import qualified Data.UUID as UUID -- instance PersistField UUID where -- toPersistValue = PersistLiteralEncoded . toASCIIBytes -- fromPersistValue (PersistLiteralEncoded uuid) = -- case fromASCIIBytes uuid of -- Nothing -> Left $ "Model/CustomTypes.hs: Failed to deserialize a UUID; received: " <> T.pack (show uuid) -- Just uuid' -> Right uuid' -- fromPersistValue x = Left $ "File.hs: When trying to deserialize a UUID: expected PersistLiteralEncoded, received: "-- > <> T.pack (show x) -- -- instance PersistFieldSql UUID where -- sqlType _ = SqlOther "uuid" ---- --
-- CREATE DOMAIN ssn AS text
-- CHECK ( value ~ '^[0-9]{9}$');
--
--
-- -- instance PersistFieldSQL SSN where -- sqlType _ = SqlOther "ssn" ---- --
-- CREATE TYPE rainbow_color AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet');
--
--
-- -- instance PersistFieldSQL RainbowColor where -- sqlType _ = SqlOther "rainbow_color" --class PersistField a => PersistFieldSql a sqlType :: PersistFieldSql a => Proxy a -> SqlType -- | This newtype wrapper is useful when selecting an entity out of the -- database and you want to provide a prefix to the table being selected. -- -- Consider this raw SQL query: -- --
-- SELECT ?? -- FROM my_long_table_name AS mltn -- INNER JOIN other_table AS ot -- ON mltn.some_col = ot.other_col -- WHERE ... ---- -- We don't want to refer to my_long_table_name every time, so -- we create an alias. If we want to select it, we have to tell the raw -- SQL quasi-quoter that we expect the entity to be prefixed with some -- other name. -- -- We can give the above query a type with this, like: -- --
-- getStuff :: SqlPersistM [EntityWithPrefix "mltn" MyLongTableName] -- getStuff = rawSql queryText [] ---- -- The EntityWithPrefix bit is a boilerplate newtype wrapper, so -- you can remove it with unPrefix, like this: -- --
-- getStuff :: SqlPersistM [Entity MyLongTableName] -- getStuff = unPrefix @"mltn" <$> rawSql queryText [] ---- -- The symbol is a "type application" and requires the -- TypeApplications@ language extension. newtype EntityWithPrefix (prefix :: Symbol) record EntityWithPrefix :: Entity record -> EntityWithPrefix (prefix :: Symbol) record [unEntityWithPrefix] :: EntityWithPrefix (prefix :: Symbol) record -> Entity record -- | Class for data types that may be retrived from a rawSql -- query. class RawSql a -- | Number of columns that this data type needs and the list of -- substitutions for SELECT placeholders ??. rawSqlCols :: RawSql a => (Text -> Text) -> a -> (Int, [Text]) -- | A string telling the user why the column count is what it is. rawSqlColCountReason :: RawSql a => a -> String -- | Transform a row of the result into the data type. rawSqlProcessRow :: RawSql a => [PersistValue] -> Either Text a -- | A helper function to tell GHC what the EntityWithPrefix prefix -- should be. This allows you to use a type application to specify the -- prefix, instead of specifying the etype on the result. -- -- As an example, here's code that uses this: -- --
-- myQuery :: SqlPersistM [Entity Person] -- myQuery = fmap (unPrefix @"p") $ rawSql query [] -- where -- query = "SELECT ?? FROM person AS p" --unPrefix :: forall (prefix :: Symbol) record. EntityWithPrefix prefix record -> Entity record rawQuery :: forall (m :: Type -> Type) env. (MonadResource m, MonadReader env m, BackendCompatible SqlBackend env) => Text -> [PersistValue] -> ConduitM () [PersistValue] m () rawQueryRes :: forall (m1 :: Type -> Type) (m2 :: Type -> Type) env. (MonadIO m1, MonadIO m2, BackendCompatible SqlBackend env) => Text -> [PersistValue] -> ReaderT env m1 (Acquire (ConduitM () [PersistValue] m2 ())) -- | Execute a raw SQL statement rawExecute :: forall (m :: Type -> Type) backend. (MonadIO m, BackendCompatible SqlBackend backend) => Text -> [PersistValue] -> ReaderT backend m () -- | Execute a raw SQL statement and return the number of rows it has -- modified. rawExecuteCount :: forall (m :: Type -> Type) backend. (MonadIO m, BackendCompatible SqlBackend backend) => Text -> [PersistValue] -> ReaderT backend m Int64 getStmtConn :: SqlBackend -> Text -> IO Statement -- | Execute a raw SQL statement and return its results as a list. If you -- do not expect a return value, use of rawExecute is recommended. -- -- If you're using Entitys (which is quite likely), then -- you must use entity selection placeholders (double question -- mark, ??). These ?? placeholders are then replaced -- for the names of the columns that we need for your entities. You'll -- receive an error if you don't use the placeholders. Please see the -- Entitys documentation for more details. -- -- You may put value placeholders (question marks, ?) in your -- SQL query. These placeholders are then replaced by the values you pass -- on the second parameter, already correctly escaped. You may want to -- use toPersistValue to help you constructing the placeholder -- values. -- -- Since you're giving a raw SQL statement, you don't get any guarantees -- regarding safety. If rawSql is not able to parse the results of -- your query back, then an exception is raised. However, most common -- problems are mitigated by using the entity selection placeholder -- ??, and you shouldn't see any error at all if you're not -- using Single. -- -- Some example of rawSql based on this schema: -- --
-- share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase| -- Person -- name String -- age Int Maybe -- deriving Show -- BlogPost -- title String -- authorId PersonId -- deriving Show -- |] ---- -- Examples based on the above schema: -- --
-- getPerson :: MonadIO m => ReaderT SqlBackend m [Entity Person] -- getPerson = rawSql "select ?? from person where name=?" [PersistText "john"] -- -- getAge :: MonadIO m => ReaderT SqlBackend m [Single Int] -- getAge = rawSql "select person.age from person where name=?" [PersistText "john"] -- -- getAgeName :: MonadIO m => ReaderT SqlBackend m [(Single Int, Single Text)] -- getAgeName = rawSql "select person.age, person.name from person where name=?" [PersistText "john"] -- -- getPersonBlog :: MonadIO m => ReaderT SqlBackend m [(Entity Person, Entity BlogPost)] -- getPersonBlog = rawSql "select ??,?? from person,blog_post where person.id = blog_post.author_id" [] ---- -- Minimal working program for PostgreSQL backend based on the above -- concepts: -- --
-- {-# LANGUAGE EmptyDataDecls #-}
-- {-# LANGUAGE FlexibleContexts #-}
-- {-# LANGUAGE GADTs #-}
-- {-# LANGUAGE GeneralizedNewtypeDeriving #-}
-- {-# LANGUAGE MultiParamTypeClasses #-}
-- {-# LANGUAGE OverloadedStrings #-}
-- {-# LANGUAGE QuasiQuotes #-}
-- {-# LANGUAGE TemplateHaskell #-}
-- {-# LANGUAGE TypeFamilies #-}
--
-- import Control.Monad.IO.Class (liftIO)
-- import Control.Monad.Logger (runStderrLoggingT)
-- import Database.Persist
-- import Control.Monad.Reader
-- import Data.Text
-- import Database.Persist.Sql
-- import Database.Persist.Postgresql
-- import Database.Persist.TH
--
-- share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
-- Person
-- name String
-- age Int Maybe
-- deriving Show
-- |]
--
-- conn = "host=localhost dbname=new_db user=postgres password=postgres port=5432"
--
-- getPerson :: MonadIO m => ReaderT SqlBackend m [Entity Person]
-- getPerson = rawSql "select ?? from person where name=?" [PersistText "sibi"]
--
-- liftSqlPersistMPool y x = liftIO (runSqlPersistMPool y x)
--
-- main :: IO ()
-- main = runStderrLoggingT $ withPostgresqlPool conn 10 $ liftSqlPersistMPool $ do
-- runMigration migrateAll
-- xs <- getPerson
-- liftIO (print xs)
--
rawSql :: forall a (m :: Type -> Type) backend. (RawSql a, MonadIO m, BackendCompatible SqlBackend backend) => Text -> [PersistValue] -> ReaderT backend m [a]
-- | Get a connection from the pool, run the given action, and then return
-- the connection to the pool.
--
-- This function performs the given action in a transaction. If an
-- exception occurs during the action, then the transaction is rolled
-- back.
--
-- Note: This function previously timed out after 2 seconds, but this
-- behavior was buggy and caused more problems than it solved. Since
-- version 2.1.2, it performs no timeout checks.
runSqlPool :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> Pool backend -> m a
-- | Like runSqlPool, but supports specifying an isolation level.
runSqlPoolWithIsolation :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> Pool backend -> IsolationLevel -> m a
-- | Like runSqlPool, but does not surround the action in a
-- transaction. This action might leave your database in a weird state.
runSqlPoolNoTransaction :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> Pool backend -> Maybe IsolationLevel -> m a
-- | This function is how runSqlPool and
-- runSqlPoolNoTransaction are defined. In addition to the action
-- to be performed and the Pool of conections to use, we give you
-- the opportunity to provide three actions - initialize, afterwards, and
-- onException.
runSqlPoolWithHooks :: forall backend m a before after onException. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> Pool backend -> Maybe IsolationLevel -> (backend -> m before) -> (backend -> m after) -> (backend -> SomeException -> m onException) -> m a
-- | This function is how runSqlPoolWithHooks is defined.
--
-- It's currently the most general function for using a SQL pool.
runSqlPoolWithExtensibleHooks :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> Pool backend -> Maybe IsolationLevel -> SqlPoolHooks m backend -> m a
-- | Starts a new transaction on the connection. When the acquired
-- connection is released the transaction is committed and the connection
-- returned to the pool.
--
-- Upon an exception the transaction is rolled back and the connection
-- destroyed.
--
-- This is equivalent to runSqlConn but does not incur the
-- MonadUnliftIO constraint, meaning it can be used within, for
-- example, a Conduit pipeline.
acquireSqlConn :: (MonadReader backend m, BackendCompatible SqlBackend backend) => m (Acquire backend)
-- | Like acquireSqlConn, but lets you specify an explicit isolation
-- level.
acquireSqlConnWithIsolation :: (MonadReader backend m, BackendCompatible SqlBackend backend) => IsolationLevel -> m (Acquire backend)
runSqlConn :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> backend -> m a
-- | Like runSqlConn, but supports specifying an isolation level.
runSqlConnWithIsolation :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> backend -> IsolationLevel -> m a
runSqlPersistM :: BackendCompatible SqlBackend backend => ReaderT backend (NoLoggingT (ResourceT IO)) a -> backend -> IO a
runSqlPersistMPool :: BackendCompatible SqlBackend backend => ReaderT backend (NoLoggingT (ResourceT IO)) a -> Pool backend -> IO a
liftSqlPersistMPool :: forall backend m a. (MonadIO m, BackendCompatible SqlBackend backend) => ReaderT backend (NoLoggingT (ResourceT IO)) a -> Pool backend -> m a
withSqlPool :: forall backend m a. (MonadLoggerIO m, MonadUnliftIO m, BackendCompatible SqlBackend backend) => (LogFunc -> IO backend) -> Int -> (Pool backend -> m a) -> m a
-- | Creates a pool of connections to a SQL database which can be used by
-- the Pool backend -> m a function. After the function
-- completes, the connections are destroyed.
withSqlPoolWithConfig :: forall backend m a. (MonadLoggerIO m, MonadUnliftIO m, BackendCompatible SqlBackend backend) => (LogFunc -> IO backend) -> ConnectionPoolConfig -> (Pool backend -> m a) -> m a
createSqlPool :: forall backend m. (MonadLoggerIO m, MonadUnliftIO m, BackendCompatible SqlBackend backend) => (LogFunc -> IO backend) -> Int -> m (Pool backend)
-- | Creates a pool of connections to a SQL database.
createSqlPoolWithConfig :: (MonadLoggerIO m, MonadUnliftIO m, BackendCompatible SqlBackend backend) => (LogFunc -> IO backend) -> ConnectionPoolConfig -> m (Pool backend)
-- | Create a connection and run sql queries within it. This function
-- automatically closes the connection on it's completion.
--
--
-- {-# LANGUAGE GADTs #-}
-- {-# LANGUAGE ScopedTypeVariables #-}
-- {-# LANGUAGE OverloadedStrings #-}
-- {-# LANGUAGE MultiParamTypeClasses #-}
-- {-# LANGUAGE TypeFamilies#-}
-- {-# LANGUAGE TemplateHaskell#-}
-- {-# LANGUAGE QuasiQuotes#-}
-- {-# LANGUAGE GeneralizedNewtypeDeriving #-}
--
-- import Control.Monad.IO.Class (liftIO)
-- import Control.Monad.Logger
-- import Conduit
-- import Database.Persist
-- import Database.Sqlite
-- import Database.Persist.Sqlite
-- import Database.Persist.TH
--
-- share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
-- Person
-- name String
-- age Int Maybe
-- deriving Show
-- |]
--
-- openConnection :: LogFunc -> IO SqlBackend
-- openConnection logfn = do
-- conn <- open "/home/sibi/test.db"
-- wrapConnection conn logfn
--
-- main :: IO ()
-- main = do
-- runNoLoggingT $ runResourceT $ withSqlConn openConnection (\backend ->
-- flip runSqlConn backend $ do
-- runMigration migrateAll
-- insert_ $ Person "John doe" $ Just 35
-- insert_ $ Person "Divya" $ Just 36
-- (pers :: [Entity Person]) <- selectList [] []
-- liftIO $ print pers
-- return ()
-- )
--
--
-- On executing it, you get this output:
--
--
-- Migrating: CREATE TABLE "person"("id" INTEGER PRIMARY KEY,"name" VARCHAR NOT NULL,"age" INTEGER NULL)
-- [Entity {entityKey = PersonKey {unPersonKey = SqlBackendKey {unSqlBackendKey = 1}}, entityVal = Person {personName = "John doe", personAge = Just 35}},Entity {entityKey = PersonKey {unPersonKey = SqlBackendKey {unSqlBackendKey = 2}}, entityVal = Person {personName = "Hema", personAge = Just 36}}]
--
withSqlConn :: forall backend m a. (MonadUnliftIO m, MonadLoggerIO m, BackendCompatible SqlBackend backend) => (LogFunc -> IO backend) -> (backend -> m a) -> m a
close' :: BackendCompatible SqlBackend backend => backend -> IO ()
withRawQuery :: forall (m :: Type -> Type) a. MonadIO m => Text -> [PersistValue] -> ConduitM [PersistValue] Void IO a -> ReaderT SqlBackend m a
toSqlKey :: ToBackendKey SqlBackend record => Int64 -> Key record
fromSqlKey :: ToBackendKey SqlBackend record => Key record -> Int64
-- | get the SQL string for the table that a PeristEntity represents Useful
-- for raw SQL queries
--
-- Your backend may provide a more convenient tableName function which
-- does not operate in a Monad
getTableName :: forall record (m :: Type -> Type) backend. (PersistEntity record, BackendCompatible SqlBackend backend, Monad m) => record -> ReaderT backend m Text
-- | useful for a backend to implement tableName by adding escaping
tableDBName :: PersistEntity record => record -> EntityNameDB
-- | get the SQL string for the field that an EntityField represents Useful
-- for raw SQL queries
--
-- Your backend may provide a more convenient fieldName function which
-- does not operate in a Monad
getFieldName :: forall record typ (m :: Type -> Type) backend. (PersistEntity record, PersistEntityBackend record ~ SqlBackend, BackendCompatible SqlBackend backend, Monad m) => EntityField record typ -> ReaderT backend m Text
-- | useful for a backend to implement fieldName by adding escaping
fieldDBName :: PersistEntity record => EntityField record typ -> FieldNameDB
-- | Used when determining how to prefix a column name in a WHERE
-- clause.
data FilterTablePrefix
-- | Prefix the column with the table name. This is useful if the column
-- name might be ambiguous.
PrefixTableName :: FilterTablePrefix
-- | Prefix the column name with the EXCLUDED keyword. This is
-- used with the Postgresql backend when doing ON CONFLICT DO
-- UPDATE clauses - see the documentation on upsertWhere
-- and upsertManyWhere.
PrefixExcluded :: FilterTablePrefix
-- | Render a [Filter record] into a Text value
-- suitable for inclusion into a SQL query.
filterClause :: PersistEntity val => Maybe FilterTablePrefix -> SqlBackend -> [Filter val] -> Text
-- | Render a [Filter record] into a Text value
-- suitable for inclusion into a SQL query, as well as the
-- [PersistValue] to properly fill in the ?
-- place holders.
filterClauseWithVals :: PersistEntity val => Maybe FilterTablePrefix -> SqlBackend -> [Filter val] -> (Text, [PersistValue])
-- | Render a [SelectOpt record] made up *only* of
-- Asc and Desc constructors into a Text value
-- suitable for inclusion into a SQL query.
orderClause :: PersistEntity val => Maybe FilterTablePrefix -> SqlBackend -> [SelectOpt val] -> Text
-- | Generates sql for limit and offset for postgres, sqlite and mysql.
decorateSQLWithLimitOffset :: Text -> (Int, Int) -> Text -> Text
-- | An exception indicating that Persistent refused to run some unsafe
-- migrations. Contains a list of pairs where the Bool tracks whether the
-- migration was unsafe (True means unsafe), and the Sql is the sql
-- statement for the migration.
newtype PersistUnsafeMigrationException
PersistUnsafeMigrationException :: [(Bool, Sql)] -> PersistUnsafeMigrationException
-- | A Migration is a four level monad stack consisting of:
--
-- -- runSqlCommand $ rawExecute "CREATE EXTENSION IF NOT EXISTS "uuid-ossp";" [] --runSqlCommand :: SqlPersistT IO () -> Migration -- | Commit the current transaction and begin a new one. This is used when -- a transaction commit is required within the context of -- runSqlConn (which brackets its provided action with a -- transaction begin/commit pair). transactionSave :: forall (m :: Type -> Type). MonadIO m => ReaderT SqlBackend m () -- | Commit the current transaction and begin a new one with the specified -- isolation level. transactionSaveWithIsolation :: forall (m :: Type -> Type). MonadIO m => IsolationLevel -> ReaderT SqlBackend m () -- | Roll back the current transaction and begin a new one. This rolls back -- to the state of the last call to transactionSave or the -- enclosing runSqlConn call. transactionUndo :: forall (m :: Type -> Type). MonadIO m => ReaderT SqlBackend m () -- | Roll back the current transaction and begin a new one with the -- specified isolation level. transactionUndoWithIsolation :: forall (m :: Type -> Type). MonadIO m => IsolationLevel -> ReaderT SqlBackend m () -- | The lifted version of deleteWhere deleteWhere :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => [Filter record] -> m () -- | The lifted version of updateWhere updateWhere :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => [Filter record] -> [Update record] -> m () -- | The lifted version of selectFirst selectFirst :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => [Filter record] -> [SelectOpt record] -> m (Maybe (Entity record)) -- | The lifted version of getBy getBy :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => Unique record -> m (Maybe (Entity record)) -- | The lifted version of insertKey insertKey :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => Key record -> record -> m () -- | The lifted version of insertEntityMany insertEntityMany :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => [Entity record] -> m () -- | The lifted version of insertMany_ insertMany_ :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => [record] -> m () -- | The lifted version of insert_ insert_ :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => record -> m () -- | The lifted version of insert insert :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => record -> m (Key record) -- | The lifted version of getEntity getEntity :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => Key record -> m (Maybe (Entity record)) -- | The lifted version of getMany getMany :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => [Key record] -> m (Map (Key record) record) -- | The lifted version of get get :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => Key record -> m (Maybe record) -- | Classy version of select select :: (MonadSqlQuery m, SqlSelect a r) => SqlQuery a -> m [r] -- | Classy version of selectOne selectOne :: (MonadSqlQuery m, SqlSelect a r) => SqlQuery a -> m (Maybe r) -- | Classy version of delete delete :: MonadSqlQuery m => SqlQuery () -> m () -- | Classy version of update update :: (MonadSqlQuery m, PersistEntity val, BackendCompatible SqlBackend (PersistEntityBackend val)) => (SqlExpr (Entity val) -> SqlQuery ()) -> m () renderQuerySelect :: (MonadSqlQuery m, SqlSelect a r) => SqlQuery a -> m (Text, [PersistValue]) -- | WITH RECURSIVE allows one to make a recursive -- subquery, which can reference itself. Like WITH, this is -- supported in most modern SQL engines. Useful for hierarchical, -- self-referential data, like a tree of data. -- --
-- select $ do -- cte <- withRecursive -- (do -- person <- from $ table @Person -- where_ $ person ^. PersonId ==. val personId -- pure person -- ) -- unionAll_ -- (\self -> do -- (p :& f :& p2 :& pSelf) <- from self -- `innerJoin` $ table @Follow -- `on` (\(p :& f) -> -- p ^. PersonId ==. f ^. FollowFollower) -- `innerJoin` $ table @Person -- `on` (\(p :& f :& p2) -> -- f ^. FollowFollowed ==. p2 ^. PersonId) -- `leftJoin` self -- `on` (\(_ :& _ :& p2 :& pSelf) -> -- just (p2 ^. PersonId) ==. pSelf ?. PersonId) -- where_ $ isNothing (pSelf ?. PersonId) -- groupBy (p2 ^. PersonId) -- pure p2 -- ) -- from cte ---- -- Since: 3.4.0.0 withRecursive :: (ToAlias a, ToAliasReference a, SqlSelect a r) => SqlQuery a -> UnionKind -> (From a -> SqlQuery a) -> SqlQuery (From a) -- | WITH clause used to introduce a Common Table Expression -- (CTE). CTEs are supported in most modern SQL engines and can be -- useful in performance tuning. In Esqueleto, CTEs should be used as a -- subquery memoization tactic. When writing plain SQL, CTEs are -- sometimes used to organize the SQL code, in Esqueleto, this is better -- achieved through function that return SqlQuery values. -- --
-- select $ do -- cte <- with subQuery -- cteResult <- from cte -- where_ $ cteResult ... -- pure cteResult ---- -- WARNING: In some SQL engines using a CTE can diminish -- performance. In these engines the CTE is treated as an optimization -- fence. You should always verify that using a CTE will in fact improve -- your performance over a regular subquery. -- -- Notably, in PostgreSQL prior to version 12, CTEs are always fully -- calculated, which can potentially significantly pessimize queries. As -- of PostgreSQL 12, non-recursive and side-effect-free queries may be -- inlined and optimized accordingly if not declared -- MATERIALIZED to get the previous behaviour. See the -- PostgreSQL CTE documentation, section Materialization, for more -- information. -- -- Since: 3.4.0.0 with :: (ToAlias a, ToAliasReference a, SqlSelect a r) => SqlQuery a -> SqlQuery (From a) -- | FULL OUTER JOIN -- -- Join where both sides of the join may not exist. Because of this the -- result needs to be handled as a Maybe -- -- Used as an infix operator `fullOuterJoin` -- --
-- select $ -- from $ table @Person -- `fullOuterJoin` table @BlogPost -- `on` (\(p :& bp) -> -- p ?. PersonId ==. bp ?. BlogPostAuthorId) --fullOuterJoin :: (ToFrom a a', ToFrom b b', ToMaybe a', ToMaybe b', HasOnClause rhs (ToMaybeT a' :& ToMaybeT b'), rhs ~ (b, (ToMaybeT a' :& ToMaybeT b') -> SqlExpr (Value Bool))) => a -> rhs -> From (ToMaybeT a' :& ToMaybeT b') infixl 2 `fullOuterJoin` -- | RIGHT OUTER JOIN -- -- Join where the left side may not exist. If the on clause fails then -- the left side will be NULL'ed Because of this the left side needs to -- be handled as a Maybe -- -- Used as an infix operator `rightJoin` -- --
-- select $ -- from $ table @Person -- `rightJoin` table @BlogPost -- `on` (\(p :& bp) -> -- p ?. PersonId ==. bp ^. BlogPostAuthorId) --rightJoin :: (ToFrom a a', ToFrom b b', ToMaybe a', HasOnClause rhs (ToMaybeT a' :& b'), rhs ~ (b, (ToMaybeT a' :& b') -> SqlExpr (Value Bool))) => a -> rhs -> From (ToMaybeT a' :& b') infixl 2 `rightJoin` -- | LEFT OUTER JOIN LATERAL -- -- Lateral join where the right side may not exist. In the case that the -- query returns nothing or the on clause fails the right side of the -- join will be NULL'ed Because of this the right side needs to be -- handled as a Maybe -- -- Used as an infix operator `leftJoinLateral` -- -- See example 6 for how to use LATERAL leftJoinLateral :: (ToFrom a a', SqlSelect b r, HasOnClause rhs (a' :& ToMaybeT b), ToAlias b, ToAliasReference b, ToMaybe b, rhs ~ (a' -> SqlQuery b, (a' :& ToMaybeT b) -> SqlExpr (Value Bool))) => a -> rhs -> From (a' :& ToMaybeT b) infixl 2 `leftJoinLateral` -- | LEFT OUTER JOIN -- -- Join where the right side may not exist. If the on clause fails then -- the right side will be NULL'ed Because of this the right side needs to -- be handled as a Maybe -- -- Used as an infix operator `leftJoin` -- --
-- select $ -- from $ table @Person -- `leftJoin` table @BlogPost -- `on` (\(p :& bp) -> -- p ^. PersonId ==. bp ?. BlogPostAuthorId) --leftJoin :: (ToFrom a a', ToFrom b b', ToMaybe b', HasOnClause rhs (a' :& ToMaybeT b'), rhs ~ (b, (a' :& ToMaybeT b') -> SqlExpr (Value Bool))) => a -> rhs -> From (a' :& ToMaybeT b') infixl 2 `leftJoin` -- | CROSS JOIN LATERAL -- -- A Lateral subquery join allows the joined query to reference entities -- from the left hand side of the join. -- -- Used as an infix operator `crossJoinLateral` -- -- See example 6 crossJoinLateral :: (ToFrom a a', SqlSelect b r, ToAlias b, ToAliasReference b) => a -> (a' -> SqlQuery b) -> From (a' :& b) infixl 2 `crossJoinLateral` -- | CROSS JOIN -- -- Used as an infix `crossJoin` -- --
-- select $ do -- from $ table @Person -- `crossJoin` table @BlogPost --crossJoin :: (ToFrom a a', ToFrom b b') => a -> b -> From (a' :& b') infixl 2 `crossJoin` -- | INNER JOIN LATERAL -- -- A Lateral subquery join allows the joined query to reference entities -- from the left hand side of the join. Discards rows that don't match -- the on clause -- -- Used as an infix operator `innerJoinLateral` -- -- See example 6 innerJoinLateral :: (ToFrom a a', HasOnClause rhs (a' :& b), SqlSelect b r, ToAlias b, ToAliasReference b, rhs ~ (a' -> SqlQuery b, (a' :& b) -> SqlExpr (Value Bool))) => a -> rhs -> From (a' :& b) infixl 2 `innerJoinLateral` -- | INNER JOIN -- -- Used as an infix operator `innerJoin` -- --
-- select $ -- from $ table @Person -- `innerJoin` table @BlogPost -- `on` (\(p :& bp) -> -- p ^. PersonId ==. bp ^. BlogPostAuthorId) --innerJoin :: (ToFrom a a', ToFrom b b', HasOnClause rhs (a' :& b'), rhs ~ (b, (a' :& b') -> SqlExpr (Value Bool))) => a -> rhs -> From (a' :& b') infixl 2 `innerJoin` -- | An ON clause that describes how two tables are related. This -- should be used as an infix operator after a JOIN. For -- example, -- --
-- select $ -- from $ table @Person -- `innerJoin` table @BlogPost -- `on` (\(p :& bP) -> -- p ^. PersonId ==. bP ^. BlogPostAuthorId) --on :: ValidOnClause a => a -> (b -> SqlExpr (Value Bool)) -> (a, b -> SqlExpr (Value Bool)) infix 9 `on` -- | 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. data a :& b (:&) :: a -> b -> (:&) a b infixl 2 :& infixl 2 :& -- | INTERSECT SQL set operation. Can be used as an infix function -- between SqlQuery values. intersect_ :: (ToSqlSetOperation a a', ToSqlSetOperation b a') => a -> b -> SqlSetOperation a' -- | EXCEPT SQL set operation. Can be used as an infix function -- between SqlQuery values. except_ :: (ToSqlSetOperation a a', ToSqlSetOperation b a') => a -> b -> SqlSetOperation a' pattern SelectQuery :: p -> p -- | Type class to support direct use of SqlQuery in a set -- operation tree class ToSqlSetOperation a r | a -> r toSqlSetOperation :: ToSqlSetOperation a r => a -> SqlSetOperation r data Union a b Union :: a -> b -> Union a b -- | UNION SQL set operation. Can be used as an infix function -- between SqlQuery values. union_ :: Union_ a => a -- | UNION ALL SQL set operation. Can be used as an infix -- function between SqlQuery values. unionAll_ :: UnionAll_ a => a data UnionAll a b UnionAll :: a -> b -> UnionAll a b data Except a b Except :: a -> b -> Except a b data Intersect a b Intersect :: a -> b -> Intersect a b -- | Select from a subquery, often used in conjuction with joins but can be -- used without any joins. Because SqlQuery has a -- ToFrom instance you probably dont need to use this function -- directly. -- --
-- select $ -- p <- from $ -- selectQuery do -- p <- from $ table @Person -- limit 5 -- orderBy [ asc p ^. PersonAge ] -- ... --selectQuery :: (SqlSelect a r, ToAlias a, ToAliasReference a) => SqlQuery a -> From a -- | Bring a PersistEntity into scope from a table -- --
-- select $ from $ table @People --table :: PersistEntity ent => From (SqlExpr (Entity ent)) -- | FROM clause, used to bring entities into scope. -- -- Internally, this function uses the From datatype. Unlike the -- old from, this does not take a function as a parameter, but -- rather a value that represents a JOIN tree constructed out of -- instances of From. This implementation eliminates certain types -- of runtime errors by preventing the construction of invalid SQL (e.g. -- illegal nested-from). from :: ToFrom a a' => a -> SqlQuery a' -- | Data type defining the From language. This should not -- constructed directly in application code. -- -- A From is a SqlQuery which returns a reference to the result -- of calling from and a function that produces a portion of a FROM -- clause. This gets passed to the FromRaw FromClause constructor -- directly when converting from a From to a SqlQuery -- using from newtype From a From :: SqlQuery (a, RawFn) -> From a [unFrom] :: From a -> SqlQuery (a, RawFn) data Table a Table :: Table a newtype SubQuery a SubQuery :: a -> SubQuery a type ToAliasT a = a class ToAlias a toAlias :: ToAlias a => a -> SqlQuery a type ToAliasReferenceT a = a class ToAliasReference a toAliasReference :: ToAliasReference a => Ident -> a -> SqlQuery a type family ToMaybeT a class ToMaybe a where { type family ToMaybeT a; } toMaybe :: ToMaybe a => a -> ToMaybeT a -- | 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. associateJoin :: forall e1 e0. Ord (Key e0) => [(Entity e0, e1)] -> Map (Key e0) (e0, [e1]) -- | Synonym for delete that does not clash with -- esqueleto's delete. deleteKey :: forall backend val (m :: Type -> Type). (PersistStore backend, BaseBackend backend ~ PersistEntityBackend val, MonadIO m, PersistEntity val) => Key val -> ReaderT backend m () -- | 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). valJ :: PersistField (Key entity) => Value (Key entity) -> SqlExpr (Value (Key entity)) -- | valkey i = val . toSqlKey -- (https://github.com/prowdsponsor/esqueleto/issues/9). valkey :: (ToBackendKey SqlBackend entity, PersistField (Key entity)) => Int64 -> SqlExpr (Value (Key entity)) -- | Insert a PersistField for every selected value, return the -- count afterward insertSelectCount :: forall (m :: Type -> Type) a backend. (MonadIO m, PersistEntity a, SqlBackendCanWrite backend) => SqlQuery (SqlExpr (Insertion a)) -> ReaderT backend m Int64 -- | Insert a PersistField for every selected value. insertSelect :: forall (m :: Type -> Type) a backend. (MonadIO m, PersistEntity a, SqlBackendCanWrite backend) => SqlQuery (SqlExpr (Insertion a)) -> ReaderT backend m () -- | Renders a SqlQuery into a Text value along with the -- list of PersistValues that would be supplied to the database -- for ? placeholders. renderQueryInsertInto :: forall a r backend (m :: Type -> Type). (SqlSelect a r, BackendCompatible SqlBackend backend, Monad m) => SqlQuery a -> 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. renderQueryUpdate :: forall a r backend (m :: Type -> Type). (SqlSelect a r, BackendCompatible SqlBackend backend, Monad m) => SqlQuery a -> 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. renderQueryDelete :: forall a r backend (m :: Type -> Type). (SqlSelect a r, BackendCompatible SqlBackend backend, Monad m) => SqlQuery a -> 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. renderQueryToText :: forall a r backend (m :: Type -> Type). (SqlSelect a r, BackendCompatible SqlBackend backend, Monad m) => Mode -> SqlQuery a -> ReaderT backend m (Text, [PersistValue]) -- | Same as update, but returns the number of rows affected. updateCount :: forall (m :: Type -> Type) val backend. (MonadIO m, PersistEntity val, BackendCompatible SqlBackend (PersistEntityBackend val), SqlBackendCanWrite backend) => (SqlExpr (Entity val) -> SqlQuery ()) -> ReaderT backend m Int64 -- | Same as delete, but returns the number of rows affected. deleteCount :: forall (m :: Type -> Type) backend. (MonadIO m, SqlBackendCanWrite backend) => SqlQuery () -> ReaderT backend m Int64 -- | Execute an esqueleto SELECT query inside -- persistent's SqlPersistT monad and return a -- Source of rows. selectSource :: forall a r backend (m :: Type -> Type). (SqlSelect a r, BackendCompatible SqlBackend backend, IsPersistBackend backend, PersistQueryRead backend, PersistStoreRead backend, PersistUniqueRead backend, MonadResource m) => SqlQuery a -> ConduitT () r (ReaderT backend m) () -- | Syntax sugar for case_. else_ :: expr a -> expr a -- | Syntax sugar for case_. then_ :: () -- | Syntax sugar for case_. when_ :: expr (Value Bool) -> () -> expr a -> (expr (Value Bool), expr a) -- | 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. toBaseId :: ToBaseId ent => SqlExpr (Value (Key ent)) -> SqlExpr (Value (Key (BaseEnt ent))) -- | CASE statement. For example: -- --
-- select $ -- return $ -- case_ -- [ when_ -- (exists $ -- from $ \p -> do -- where_ (p ^. PersonName ==. val "Mike")) -- then_ -- (sub_select $ -- from $ \v -> do -- let sub = -- from $ \c -> do -- where_ (c ^. PersonName ==. val "Mike") -- return (c ^. PersonFavNum) -- where_ (v ^. PersonFavNum >. sub_select 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. -- --
-- select $ -- from $ \person -> do -- where_ $ exists $ -- from $ \post -> do -- where_ (post ^. BlogPostAuthorId ==. person ^. PersonId) -- return person --exists :: SqlQuery () -> SqlExpr (Value Bool) -- | NOT IN operator. notIn :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (ValueList typ) -> 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]. in_ :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (ValueList typ) -> SqlExpr (Value Bool) -- | Same as just but for ValueList. Most of the time you -- won't need it, though, because you can use just from inside -- subList_select or Just from inside valList. justList :: SqlExpr (ValueList typ) -> SqlExpr (ValueList (Maybe typ)) -- | Lift a list of constant value from Haskell-land to the query. valList :: PersistField typ => [typ] -> SqlExpr (ValueList typ) -- | Execute a subquery SELECT in an SqlExpression. Returns a list -- of values. subList_select :: PersistField a => SqlQuery (SqlExpr (Value a)) -> SqlExpr (ValueList a) -- | 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. castString :: (SqlString s, SqlString r) => SqlExpr (Value s) -> SqlExpr (Value r) -- | The || string concatenation operator (named after Haskell's -- ++ in order to avoid naming clash with ||.). Supported -- by SQLite and PostgreSQL. (++.) :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -> SqlExpr (Value s) infixr 5 ++. -- | The CONCAT function with a variable number of parameters. -- Supported by MySQL and PostgreSQL. concat_ :: SqlString s => [SqlExpr (Value 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" ++. (%) --(%) :: SqlString s => SqlExpr (Value s) -- | ILIKE operator (case-insensitive LIKE). -- -- Supported by PostgreSQL only. ilike :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -> SqlExpr (Value Bool) infixr 2 `ilike` -- | LIKE operator. like :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -> SqlExpr (Value Bool) infixr 2 `like` -- | RIGHT function. @since 3.3.0 right_ :: (SqlString s, Num a) => (SqlExpr (Value s), SqlExpr (Value a)) -> SqlExpr (Value s) -- | LEFT function. @since 3.3.0 left_ :: (SqlString s, Num a) => (SqlExpr (Value s), SqlExpr (Value a)) -> SqlExpr (Value s) -- | LENGTH function. @since 3.3.0 length_ :: (SqlString s, Num a) => SqlExpr (Value s) -> SqlExpr (Value a) -- | LTRIM function. @since 3.3.0 ltrim_ :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -- | RTRIM function. @since 3.3.0 rtrim_ :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -- | TRIM function. @since 3.3.0 trim_ :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -- | UPPER function. @since 3.3.0 upper_ :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -- | LOWER function. lower_ :: SqlString s => SqlExpr (Value s) -> SqlExpr (Value s) -- | Like coalesce, but takes a non-nullable SqlExpression placed -- at the end of the SqlExpression list, which guarantees a non-NULL -- result. coalesceDefault :: PersistField a => [SqlExpr (Value (Maybe a))] -> SqlExpr (Value a) -> SqlExpr (Value a) -- | 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. coalesce :: PersistField a => [SqlExpr (Value (Maybe a))] -> SqlExpr (Value (Maybe a)) -- | Same as castNum, but for nullable values. castNumM :: (Num a, Num b) => SqlExpr (Value (Maybe a)) -> SqlExpr (Value (Maybe 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. castNum :: (Num a, Num b) => SqlExpr (Value a) -> SqlExpr (Value b) avg_ :: (PersistField a, PersistField b) => SqlExpr (Value a) -> SqlExpr (Value (Maybe b)) max_ :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value (Maybe a)) min_ :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value (Maybe a)) sum_ :: (PersistField a, PersistField b) => SqlExpr (Value a) -> SqlExpr (Value (Maybe b)) floor_ :: (PersistField a, Num a, PersistField b, Num b) => SqlExpr (Value a) -> SqlExpr (Value b) ceiling_ :: (PersistField a, Num a, PersistField b, Num b) => SqlExpr (Value a) -> SqlExpr (Value b) round_ :: (PersistField a, Num a, PersistField b, Num b) => SqlExpr (Value a) -> SqlExpr (Value b) random_ :: (PersistField a, Num a) => SqlExpr (Value a) -- | BETWEEN. -- -- @since: 3.1.0 between :: PersistField a => SqlExpr (Value a) -> (SqlExpr (Value a), SqlExpr (Value a)) -> SqlExpr (Value Bool) (*.) :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value a) -> SqlExpr (Value a) infixl 7 *. (/.) :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value a) -> SqlExpr (Value a) infixl 7 /. (-.) :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value a) -> SqlExpr (Value a) infixl 6 -. (+.) :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value a) -> SqlExpr (Value a) infixl 6 +. (||.) :: SqlExpr (Value Bool) -> SqlExpr (Value Bool) -> SqlExpr (Value Bool) infixr 2 ||. (&&.) :: SqlExpr (Value Bool) -> SqlExpr (Value Bool) -> SqlExpr (Value Bool) infixr 3 &&. (!=.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool) infix 4 !=. (<.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool) infix 4 <. (<=.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool) infix 4 <=. (>.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool) infix 4 >. (>=.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool) infix 4 >=. (==.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool) infix 4 ==. not_ :: SqlExpr (Value Bool) -> SqlExpr (Value Bool) -- | COUNT(DISTINCT x). countDistinct :: Num a => SqlExpr (Value typ) -> SqlExpr (Value a) -- | COUNT. count :: Num a => SqlExpr (Value typ) -> SqlExpr (Value a) -- | COUNT(*) value. countRows :: Num a => SqlExpr (Value a) -- | Join nested Maybes in a Value into one. This is useful -- when calling aggregate functions on nullable fields. joinV :: SqlExpr (Value (Maybe (Maybe typ))) -> SqlExpr (Value (Maybe typ)) -- | NULL value. nothing :: SqlExpr (Value (Maybe typ)) -- | Analogous to Just, promotes a value of type typ into -- one of type Maybe typ. It should hold that val . -- Just === just . val. just :: SqlExpr (Value typ) -> SqlExpr (Value (Maybe typ)) -- | 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: -- -- TODO: table -- -- 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}
--
isNothing :: PersistField typ => SqlExpr (Value (Maybe typ)) -> SqlExpr (Value Bool)
-- | Lift a constant value from Haskell-land to the query.
val :: PersistField typ => typ -> SqlExpr (Value typ)
-- | Project a field of an entity that may be null.
(?.) :: (PersistEntity val, PersistField typ) => SqlExpr (Maybe (Entity val)) -> EntityField val typ -> SqlExpr (Value (Maybe typ))
-- | Project an SqlExpression that may be null, guarding against null
-- cases.
withNonNull :: PersistField typ => SqlExpr (Value (Maybe typ)) -> (SqlExpr (Value typ) -> SqlQuery a) -> SqlQuery a
-- | Project a field of an entity.
(^.) :: forall typ val. (PersistEntity val, PersistField typ) => SqlExpr (Entity val) -> EntityField val typ -> SqlExpr (Value typ)
infixl 9 ^.
-- | Execute a subquery SELECT in a SqlExpr. This function
-- is unsafe, because it can throw runtime exceptions in two cases:
--
-- -- 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) --subSelectForeign :: (BackendCompatible SqlBackend (PersistEntityBackend val1), PersistEntity val1, PersistEntity val2, PersistField a) => SqlExpr (Entity val2) -> EntityField val2 (Key val1) -> (SqlExpr (Entity val1) -> SqlExpr (Value a)) -> SqlExpr (Value a) -- | 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. subSelectList :: PersistField a => SqlQuery (SqlExpr (Value a)) -> SqlExpr (ValueList a) -- | Performs a COUNT of the given query in a subSelect -- manner. This is always guaranteed to return a result value, and is -- completely safe. subSelectCount :: (Num a, PersistField a) => SqlQuery ignored -> SqlExpr (Value a) -- | 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). subSelectMaybe :: PersistField a => SqlQuery (SqlExpr (Value (Maybe a))) -> SqlExpr (Value (Maybe a)) -- | 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. subSelect :: PersistField a => SqlQuery (SqlExpr (Value a)) -> SqlExpr (Value (Maybe a)) -- | Execute a subquery SELECT in an SqlExpression. Returns a -- simple value so should be used only when the SELECT query is -- guaranteed to return just one row. -- -- Deprecated in 3.2.0. sub_select :: PersistField a => SqlQuery (SqlExpr (Value a)) -> SqlExpr (Value a) -- | Add a locking clause to the query. Please read LockingKind -- documentation and your RDBMS manual. -- -- If multiple calls to locking are made on the same query, the -- last one is used. locking :: LockingKind -> SqlQuery () -- | HAVING. having :: SqlExpr (Value Bool) -> SqlQuery () -- | ORDER BY random() clause. rand :: SqlExpr OrderBy -- | A convenience function that calls both distinctOn and -- orderBy. In other words, -- --
-- distinctOnOrderBy [asc foo, desc bar, desc quux] $ do -- ... ---- -- is the same as: -- --
-- distinctOn [don foo, don bar, don quux] $ do -- orderBy [asc foo, desc bar, desc quux] -- ... --distinctOnOrderBy :: [SqlExpr OrderBy] -> SqlQuery a -> SqlQuery a -- | Erase an SqlExpression's type so that it's suitable to be used by -- distinctOn. don :: SqlExpr (Value a) -> SqlExpr DistinctOn -- | 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. distinctOn :: [SqlExpr DistinctOn] -> 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 ()) -- ... --distinct :: SqlQuery a -> SqlQuery a -- | OFFSET. Usually used with limit. offset :: Int64 -> SqlQuery () -- | LIMIT. Limit the number of returned rows. limit :: Int64 -> SqlQuery () -- | Descending order of this field or SqlExpression. desc :: PersistField a => SqlExpr (Value a) -> SqlExpr OrderBy -- | Ascending order of this field or SqlExpression. asc :: PersistField a => SqlExpr (Value a) -> SqlExpr OrderBy -- | ORDER BY clause. See also asc and desc. -- -- Multiple calls to orderBy get concatenated on the final query, -- including distinctOnOrderBy. orderBy :: [SqlExpr OrderBy] -> 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) ---- --
-- 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) --groupBy :: ToSomeValues a => a -> SqlQuery () -- | WHERE clause: restrict the query's result. where_ :: SqlExpr (Value Bool) -> SqlQuery () -- | A single value (as opposed to a whole entity). You may use -- (^.) or (?.) to get a Value -- from an Entity. newtype Value a Value :: a -> Value a [unValue] :: Value a -> 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). newtype ValueList a ValueList :: a -> ValueList a -- | Data type that represents an INNER JOIN (see -- LeftOuterJoin for an example). data InnerJoin a b InnerJoin :: a -> b -> InnerJoin a b infixl 2 `InnerJoin` infixl 2 `InnerJoin` -- | Data type that represents a CROSS JOIN (see -- LeftOuterJoin for an example). data CrossJoin a b CrossJoin :: a -> b -> CrossJoin a b infixl 2 `CrossJoin` infixl 2 `CrossJoin` -- | 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. data LeftOuterJoin a b LeftOuterJoin :: a -> b -> LeftOuterJoin a b infixl 2 `LeftOuterJoin` infixl 2 `LeftOuterJoin` -- | Data type that represents a RIGHT OUTER JOIN (see -- LeftOuterJoin for an example). data RightOuterJoin a b RightOuterJoin :: a -> b -> RightOuterJoin a b infixl 2 `RightOuterJoin` infixl 2 `RightOuterJoin` -- | Data type that represents a FULL OUTER JOIN (see -- LeftOuterJoin for an example). data FullOuterJoin a b FullOuterJoin :: a -> b -> FullOuterJoin a b infixl 2 `FullOuterJoin` infixl 2 `FullOuterJoin` -- | (Internal) A kind of JOIN. data JoinKind -- |
-- INNER JOIN --InnerJoinKind :: JoinKind -- |
-- CROSS JOIN --CrossJoinKind :: JoinKind -- |
-- LEFT OUTER JOIN --LeftOuterJoinKind :: JoinKind -- |
-- RIGHT OUTER JOIN --RightOuterJoinKind :: JoinKind -- |
-- FULL OUTER JOIN --FullOuterJoinKind :: JoinKind -- | Exception thrown whenever on is used to create an ON -- clause but no matching JOIN is found. data OnClauseWithoutMatchingJoinException OnClauseWithoutMatchingJoinException :: String -> OnClauseWithoutMatchingJoinException -- | Phantom type used by orderBy, asc and desc. data OrderBy -- | Phantom type used by distinctOn and don. data DistinctOn -- | 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. data LockingKind -- | FOR UPDATE syntax. Supported by MySQL, Oracle and PostgreSQL. ForUpdate :: LockingKind -- | FOR UPDATE SKIP LOCKED syntax. Supported by MySQL, Oracle and -- PostgreSQL. ForUpdateSkipLocked :: LockingKind -- | FOR SHARE syntax. Supported by PostgreSQL. ForShare :: LockingKind -- | LOCK IN SHARE MODE syntax. Supported by MySQL. LockInShareMode :: LockingKind -- | 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. class PersistField a => SqlString a -- | e.g. type BaseEnt MyBase = MyChild type family BaseEnt ent -- | Class that enables one to use toBaseId to convert an entity's -- key on a query into another (cf. toBaseId). class ToBaseId ent where { -- | e.g. type BaseEnt MyBase = MyChild type family BaseEnt 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. toBaseIdWitness :: ToBaseId ent => Key (BaseEnt ent) -> Key ent -- | SQL backend for esqueleto using SqlPersistT. data SqlQuery a -- | Constraint synonym for persistent entities whose backend is -- SqlBackend. type SqlEntity ent = (PersistEntity ent, PersistEntityBackend ent ~ SqlBackend) -- | 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. data SqlExpr a -- | Represents a value containing all the configuration options for a -- specific backend. This abstraction makes it easier to write code that -- can easily swap backends. class PersistConfig c where { type family PersistConfigBackend c :: Type -> Type -> Type -> Type; type family PersistConfigPool c; } -- | Load the config settings from a Value, most likely taken from a -- YAML config file. loadConfig :: PersistConfig c => Value -> Parser c -- | Modify the config settings based on environment variables. applyEnv :: PersistConfig c => c -> IO c -- | Create a new connection pool based on the given config settings. createPoolConfig :: PersistConfig c => c -> IO (PersistConfigPool c) -- | Run a database action by taking a connection from the pool. runPool :: (PersistConfig c, MonadUnliftIO m) => c -> PersistConfigBackend c m a -> PersistConfigPool c -> m a type family PersistConfigBackend c :: Type -> Type -> Type -> Type type family PersistConfigPool c -- | An ConstraintNameHS represents the Haskell-side name that -- persistent will use for a constraint. newtype ConstraintNameHS ConstraintNameHS :: Text -> ConstraintNameHS [unConstraintNameHS] :: ConstraintNameHS -> Text -- | A ConstraintNameDB represents the datastore-side name that -- persistent will use for a constraint. newtype ConstraintNameDB ConstraintNameDB :: Text -> ConstraintNameDB [unConstraintNameDB] :: ConstraintNameDB -> Text -- | An EntityNameDB represents the datastore-side name that -- persistent will use for an entity. newtype EntityNameDB EntityNameDB :: Text -> EntityNameDB [unEntityNameDB] :: EntityNameDB -> Text -- | An EntityNameHS represents the Haskell-side name that -- persistent will use for an entity. newtype EntityNameHS EntityNameHS :: Text -> EntityNameHS [unEntityNameHS] :: EntityNameHS -> Text -- | A FieldNameHS represents the Haskell-side name that -- persistent will use for a field. newtype FieldNameHS FieldNameHS :: Text -> FieldNameHS [unFieldNameHS] :: FieldNameHS -> Text -- | An EntityNameDB represents the datastore-side name that -- persistent will use for an entity. newtype FieldNameDB FieldNameDB :: Text -> FieldNameDB [unFieldNameDB] :: FieldNameDB -> Text -- | Convenience operations for working with '-NameDB' types. class DatabaseName a escapeWith :: DatabaseName a => (Text -> str) -> a -> str -- | A type that determines how a backend should handle the literal. data LiteralType -- | The accompanying value will be escaped before inserting into the -- database. This is the correct default choice to use. Escaped :: LiteralType -- | The accompanying value will not be escaped when inserting into the -- database. This is potentially dangerous - use this with care. Unescaped :: LiteralType -- | The DbSpecific constructor corresponds to the legacy -- PersistDbSpecific constructor. We need to keep this around -- because old databases may have serialized JSON representations that -- reference this. We don't want to break the ability of a database to -- load rows. DbSpecific :: LiteralType -- | A raw value which can be stored in any backend and can be marshalled -- to and from a PersistField. data PersistValue PersistText :: Text -> PersistValue PersistByteString :: ByteString -> PersistValue PersistInt64 :: Int64 -> PersistValue PersistDouble :: Double -> PersistValue PersistRational :: Rational -> PersistValue PersistBool :: Bool -> PersistValue PersistDay :: Day -> PersistValue PersistTimeOfDay :: TimeOfDay -> PersistValue PersistUTCTime :: UTCTime -> PersistValue PersistNull :: PersistValue PersistList :: [PersistValue] -> PersistValue PersistMap :: [(Text, PersistValue)] -> PersistValue -- | Intended especially for MongoDB backend PersistObjectId :: ByteString -> PersistValue -- | Intended especially for PostgreSQL backend for text arrays PersistArray :: [PersistValue] -> PersistValue -- | This constructor is used to specify some raw literal value for the -- backend. The LiteralType value specifies how the value should -- be escaped. This can be used to make special, custom types avaialable -- in the back end. PersistLiteral_ :: LiteralType -> ByteString -> PersistValue -- | This pattern synonym used to be a data constructor on -- PersistValue, but was changed into a catch-all pattern synonym -- to allow backwards compatiblity with database types. See the -- documentation on PersistDbSpecific for more details. pattern PersistLiteral :: ByteString -> PersistValue -- | This pattern synonym used to be a data constructor for the -- PersistValue type. It was changed to be a pattern so that -- JSON-encoded database values could be parsed into their corresponding -- values. You should not use this, and instead prefer to pattern match -- on PersistLiteral_ directly. -- -- If you use this, it will overlap a patern match on the -- 'PersistLiteral_, PersistLiteral, and -- PersistLiteralEscaped patterns. If you need to disambiguate -- between these constructors, pattern match on PersistLiteral_ -- directly. pattern PersistDbSpecific :: ByteString -> PersistValue -- | This pattern synonym used to be a data constructor on -- PersistValue, but was changed into a catch-all pattern synonym -- to allow backwards compatiblity with database types. See the -- documentation on PersistDbSpecific for more details. pattern PersistLiteralEscaped :: ByteString -> PersistValue fromPersistValueText :: PersistValue -> Either Text Text -- | Please refer to the documentation for the database in question for a -- full overview of the semantics of the varying isloation levels data IsolationLevel ReadUncommitted :: IsolationLevel ReadCommitted :: IsolationLevel RepeatableRead :: IsolationLevel Serializable :: IsolationLevel -- | A FieldDef represents the inormation that persistent -- knows about a field of a datatype. This includes information used to -- parse the field out of the database and what the field corresponds to. data FieldDef FieldDef :: !FieldNameHS -> !FieldNameDB -> !FieldType -> !SqlType -> ![FieldAttr] -> !Bool -> !ReferenceDef -> !FieldCascade -> !Maybe Text -> !Maybe Text -> !Bool -> FieldDef -- | The name of the field. Note that this does not corresponds to the -- record labels generated for the particular entity - record labels are -- generated with the type name prefixed to the field, so a -- FieldDef that contains a FieldNameHS "name" for -- a type User will have a record field userName. [fieldHaskell] :: FieldDef -> !FieldNameHS -- | The name of the field in the database. For SQL databases, this -- corresponds to the column name. [fieldDB] :: FieldDef -> !FieldNameDB -- | The type of the field in Haskell. [fieldType] :: FieldDef -> !FieldType -- | The type of the field in a SQL database. [fieldSqlType] :: FieldDef -> !SqlType -- | User annotations for a field. These are provided with the ! -- operator. [fieldAttrs] :: FieldDef -> ![FieldAttr] -- | If this is True, then the Haskell datatype will have a strict -- record field. The default value for this is True. [fieldStrict] :: FieldDef -> !Bool [fieldReference] :: FieldDef -> !ReferenceDef -- | Defines how operations on the field cascade on to the referenced -- tables. This doesn't have any meaning if the fieldReference is -- set to NoReference or SelfReference. The cascade option -- here should be the same as the one obtained in the -- fieldReference. [fieldCascade] :: FieldDef -> !FieldCascade -- | Optional comments for a Field. There is not currently a way -- to attach comments to a field in the quasiquoter. [fieldComments] :: FieldDef -> !Maybe Text -- | Whether or not the field is a GENERATED column, and -- additionally the expression to use for generation. [fieldGenerated] :: FieldDef -> !Maybe Text -- | True if the field is an implicit ID column. False -- otherwise. [fieldIsImplicitIdColumn] :: FieldDef -> !Bool data PersistUpdate Assign :: PersistUpdate Add :: PersistUpdate Subtract :: PersistUpdate Multiply :: PersistUpdate Divide :: PersistUpdate BackendSpecificUpdate :: Text -> PersistUpdate data UpdateException KeyNotFound :: String -> UpdateException UpsertError :: String -> UpdateException data PersistFilter Eq :: PersistFilter Ne :: PersistFilter Gt :: PersistFilter Lt :: PersistFilter Ge :: PersistFilter Le :: PersistFilter In :: PersistFilter NotIn :: PersistFilter -- | A SQL data type. Naming attempts to reflect the underlying Haskell -- datatypes, eg SqlString instead of SqlVarchar. Different SQL databases -- may have different translations for these types. data SqlType SqlString :: SqlType SqlInt32 :: SqlType SqlInt64 :: SqlType SqlReal :: SqlType SqlNumeric :: Word32 -> Word32 -> SqlType SqlBool :: SqlType SqlDay :: SqlType SqlTime :: SqlType -- | Always uses UTC timezone SqlDayTime :: SqlType SqlBlob :: SqlType -- | a backend-specific name SqlOther :: Text -> SqlType data PersistException -- | Generic Exception PersistError :: Text -> PersistException PersistMarshalError :: Text -> PersistException PersistInvalidField :: Text -> PersistException PersistForeignConstraintUnmet :: Text -> PersistException PersistMongoDBError :: Text -> PersistException PersistMongoDBUnsupported :: Text -> PersistException -- | An action that might happen on a deletion or update on a foreign key -- change. data CascadeAction Cascade :: CascadeAction Restrict :: CascadeAction SetNull :: CascadeAction SetDefault :: CascadeAction -- | This datatype describes how a foreign reference field cascades deletes -- or updates. -- -- This type is used in both parsing the model definitions and performing -- migrations. A Nothing in either of the field values means that -- the user has not specified a CascadeAction. An unspecified -- CascadeAction is defaulted to Restrict when doing -- migrations. data FieldCascade FieldCascade :: !Maybe CascadeAction -> !Maybe CascadeAction -> FieldCascade [fcOnUpdate] :: FieldCascade -> !Maybe CascadeAction [fcOnDelete] :: FieldCascade -> !Maybe CascadeAction data ForeignDef ForeignDef :: !EntityNameHS -> !EntityNameDB -> !ConstraintNameHS -> !ConstraintNameDB -> !FieldCascade -> ![(ForeignFieldDef, ForeignFieldDef)] -> ![Attr] -> Bool -> Bool -> ForeignDef [foreignRefTableHaskell] :: ForeignDef -> !EntityNameHS [foreignRefTableDBName] :: ForeignDef -> !EntityNameDB [foreignConstraintNameHaskell] :: ForeignDef -> !ConstraintNameHS [foreignConstraintNameDBName] :: ForeignDef -> !ConstraintNameDB -- | Determine how the field will cascade on updates and deletions. [foreignFieldCascade] :: ForeignDef -> !FieldCascade [foreignFields] :: ForeignDef -> ![(ForeignFieldDef, ForeignFieldDef)] [foreignAttrs] :: ForeignDef -> ![Attr] [foreignNullable] :: ForeignDef -> Bool -- | Determines if the reference is towards a Primary Key or not. [foreignToPrimary] :: ForeignDef -> Bool -- | Used instead of FieldDef to generate a smaller amount of code type ForeignFieldDef = (FieldNameHS, FieldNameDB) data CompositeDef CompositeDef :: !NonEmpty FieldDef -> ![Attr] -> CompositeDef [compositeFields] :: CompositeDef -> !NonEmpty FieldDef [compositeAttrs] :: CompositeDef -> ![Attr] -- | Type for storing the Uniqueness constraint in the Schema. Assume you -- have the following schema with a uniqueness constraint: -- --
-- Person -- name String -- age Int -- UniqueAge age ---- -- This will be represented as: -- --
-- UniqueDef
-- { uniqueHaskell = ConstraintNameHS (packPTH UniqueAge)
-- , uniqueDBName = ConstraintNameDB (packPTH "unique_age")
-- , uniqueFields = [(FieldNameHS (packPTH "age"), FieldNameDB (packPTH "age"))]
-- , uniqueAttrs = []
-- }
--
data UniqueDef
UniqueDef :: !ConstraintNameHS -> !ConstraintNameDB -> !NonEmpty (FieldNameHS, FieldNameDB) -> ![Attr] -> UniqueDef
[uniqueHaskell] :: UniqueDef -> !ConstraintNameHS
[uniqueDBName] :: UniqueDef -> !ConstraintNameDB
[uniqueFields] :: UniqueDef -> !NonEmpty (FieldNameHS, FieldNameDB)
[uniqueAttrs] :: UniqueDef -> ![Attr]
-- | An EmbedFieldDef is the same as a FieldDef But it is only used for
-- embeddedFields so it only has data needed for embedding
data EmbedFieldDef
EmbedFieldDef :: FieldNameDB -> Maybe (Either SelfEmbed EntityNameHS) -> EmbedFieldDef
[emFieldDB] :: EmbedFieldDef -> FieldNameDB
[emFieldEmbed] :: EmbedFieldDef -> Maybe (Either SelfEmbed EntityNameHS)
-- | An EmbedEntityDef is the same as an EntityDef But it is only used for
-- fieldReference so it only has data needed for embedding
data EmbedEntityDef
EmbedEntityDef :: EntityNameHS -> [EmbedFieldDef] -> EmbedEntityDef
[embeddedHaskell] :: EmbedEntityDef -> EntityNameHS
[embeddedFields] :: EmbedEntityDef -> [EmbedFieldDef]
-- | There are 3 kinds of references 1) composite (to fields that exist in
-- the record) 2) single field 3) embedded
data ReferenceDef
NoReference :: ReferenceDef
-- | A ForeignRef has a late binding to the EntityDef it references via
-- name and has the Haskell type of the foreign key in the form of
-- FieldType
ForeignRef :: !EntityNameHS -> ReferenceDef
EmbedRef :: EntityNameHS -> ReferenceDef
CompositeRef :: CompositeDef -> ReferenceDef
-- | A SelfReference stops an immediate cycle which causes non-termination
-- at compile-time (issue #311).
SelfReference :: ReferenceDef
-- | A FieldType describes a field parsed from the QuasiQuoter and
-- is used to determine the Haskell type in the generated code.
--
-- name Text parses into FTTypeCon Nothing Text
--
-- name T.Text parses into FTTypeCon (Just T
-- Text)
--
-- name (Jsonb User) parses into:
--
-- -- FTApp (FTTypeCon Nothing Jsonb) (FTTypeCon Nothing User) --data FieldType -- | Optional module and name. FTTypeCon :: Maybe Text -> Text -> FieldType FTTypePromoted :: Text -> FieldType FTApp :: FieldType -> FieldType -> FieldType FTList :: FieldType -> FieldType -- | Attributes that may be attached to fields that can affect migrations -- and serialization in backend-specific ways. -- -- While we endeavor to, we can't forsee all use cases for all backends, -- and so FieldAttr is extensible through its constructor -- FieldAttrOther. data FieldAttr -- | The Maybe keyword goes after the type. This indicates that the -- column is nullable, and the generated Haskell code will have a -- Maybe type for it. -- -- Example: -- --
-- User -- name Text Maybe --FieldAttrMaybe :: FieldAttr -- | This indicates that the column is nullable, but should not have a -- Maybe type. For this to work out, you need to ensure that the -- PersistField instance for the type in question can support a -- PersistNull value. -- --
-- data What = NoWhat | Hello Text -- -- instance PersistField What where -- fromPersistValue PersistNull = -- pure NoWhat -- fromPersistValue pv = -- Hello $ fromPersistValue pv -- -- instance PersistFieldSql What where -- sqlType _ = SqlString -- -- User -- what What nullable --FieldAttrNullable :: FieldAttr -- | This tag means that the column will not be present on the Haskell -- code, but will not be removed from the database. Useful to deprecate -- fields in phases. -- -- You should set the column to be nullable in the database. Otherwise, -- inserts won't have values. -- --
-- User -- oldName Text MigrationOnly -- newName Text --FieldAttrMigrationOnly :: FieldAttr -- | A SafeToRemove attribute is not present on the Haskell -- datatype, and the backend migrations should attempt to drop the column -- without triggering any unsafe migration warnings. -- -- Useful after you've used MigrationOnly to remove a column -- from the database in phases. -- --
-- User -- oldName Text SafeToRemove -- newName Text --FieldAttrSafeToRemove :: FieldAttr -- | This attribute indicates that we should create a foreign key reference -- from a column. By default, persistent will try and create a -- foreign key reference for a column if it can determine that the type -- of the column is a Key entity or an -- EntityId and the Entity's name was present in -- mkPersist. -- -- This is useful if you want to use the explicit foreign key syntax. -- --
-- Post -- title Text -- -- Comment -- postId PostId noreference -- Foreign Post fk_comment_post postId --FieldAttrNoreference :: FieldAttr -- | This is set to specify precisely the database table the column refers -- to. -- --
-- Post -- title Text -- -- Comment -- postId PostId references="post" ---- -- You should not need this - persistent should be capable of -- correctly determining the target table's name. If you do need this, -- please file an issue describing why. FieldAttrReference :: Text -> FieldAttr -- | Specify a name for the constraint on the foreign key reference for -- this table. -- --
-- Post -- title Text -- -- Comment -- postId PostId constraint="my_cool_constraint_name" --FieldAttrConstraint :: Text -> FieldAttr -- | Specify the default value for a column. -- --
-- User -- createdAt UTCTime default="NOW()" ---- -- Note that a default= attribute does not mean you can omit the -- value while inserting. FieldAttrDefault :: Text -> FieldAttr -- | Specify a custom SQL type for the column. Generally, you should define -- a custom datatype with a custom PersistFieldSql instance -- instead of using this. -- --
-- User -- uuid Text sqltype=UUID --FieldAttrSqltype :: Text -> FieldAttr -- | Set a maximum length for a column. Useful for VARCHAR and indexes. -- --
-- User -- name Text maxlen=200 -- -- UniqueName name --FieldAttrMaxlen :: Integer -> FieldAttr -- | Specify the database name of the column. -- --
-- User -- blarghle Int sql="b_l_a_r_g_h_l_e" ---- -- Useful for performing phased migrations, where one column is renamed -- to another column over time. FieldAttrSql :: Text -> FieldAttr -- | A grab bag of random attributes that were unrecognized by the parser. FieldAttrOther :: Text -> FieldAttr type Attr = Text type ExtraLine = [Text] -- | The definition for the entity's primary key ID. data EntityIdDef -- | The entity has a single key column, and it is a surrogate key - that -- is, you can't go from rec -> Key rec. EntityIdField :: !FieldDef -> EntityIdDef -- | The entity has a natural key. This means you can write rec -> -- Key rec because all the key fields are present on the datatype. -- -- A natural key can have one or more columns. EntityIdNaturalKey :: !CompositeDef -> EntityIdDef -- | An EntityDef represents the information that -- persistent knows about an Entity. It uses this information to -- generate the Haskell datatype, the SQL migrations, and other relevant -- conversions. data EntityDef -- | The reason why a field is nullable is very important. A field -- that is nullable because of a Maybe tag will have its type -- changed from A to Maybe A. OTOH, a field that is -- nullable because of a nullable tag will remain with the same -- type. data WhyNullable ByMaybeAttr :: WhyNullable ByNullableAttr :: WhyNullable data IsNullable Nullable :: !WhyNullable -> IsNullable NotNullable :: IsNullable -- | A Checkmark should be used as a field type whenever a -- uniqueness constraint should guarantee that a certain kind of record -- may appear at most once, but other kinds of records may appear any -- number of times. -- -- NOTE: You need to mark any Checkmark fields as -- nullable (see the following example). -- -- For example, suppose there's a Location entity that -- represents where a user has lived: -- --
-- Location -- user UserId -- name Text -- current Checkmark nullable -- -- UniqueLocation user current ---- -- The UniqueLocation constraint allows any number of -- Inactive Locations to be current. However, -- there may be at most one current Location per user -- (i.e., either zero or one per user). -- -- This data type works because of the way that SQL treats -- NULLable fields within uniqueness constraints. The SQL -- standard says that NULL values should be considered -- different, so we represent Inactive as SQL NULL, thus -- allowing any number of Inactive records. On the other hand, we -- represent Active as TRUE, so the uniqueness constraint -- will disallow more than one Active record. -- -- Note: There may be DBMSs that do not respect the SQL standard's -- treatment of NULL values on uniqueness constraints, please -- check if this data type works before relying on it. -- -- The SQL BOOLEAN type is used because it's the smallest data -- type available. Note that we never use FALSE, just -- TRUE and NULL. Provides the same behavior Maybe -- () would if () was a valid PersistField. data Checkmark -- | When used on a uniqueness constraint, there may be at most one -- Active record. Active :: Checkmark -- | When used on a uniqueness constraint, there may be any number of -- Inactive records. Inactive :: Checkmark fieldAttrsContainsNullable :: [FieldAttr] -> IsNullable -- | Return the [FieldDef] for the entity keys. entitiesPrimary :: EntityDef -> NonEmpty FieldDef entityPrimary :: EntityDef -> Maybe CompositeDef -- | Returns a NonEmpty list of FieldDef that correspond with -- the key columns for an EntityDef. keyAndEntityFields :: EntityDef -> NonEmpty FieldDef -- | Parse raw field attributes into structured form. Any unrecognized -- attributes will be preserved, identically as they are encountered, as -- FieldAttrOther values. parseFieldAttrs :: [Text] -> [FieldAttr] isFieldNotGenerated :: FieldDef -> Bool -- | Returns True if the FieldDef does not have a -- MigrationOnly or SafeToRemove flag from the -- QuasiQuoter. isHaskellField :: FieldDef -> Bool -- | A FieldCascade that does nothing. noCascade :: FieldCascade -- | Renders a FieldCascade value such that it can be used in SQL -- migrations. renderFieldCascade :: FieldCascade -> Text -- | Render a CascadeAction to Text such that it can be used -- in a SQL command. renderCascadeAction :: CascadeAction -> Text -- | A Statement is a representation of a database query that has -- been prepared and stored on the server side. data Statement Statement :: IO () -> IO () -> ([PersistValue] -> IO Int64) -> (forall (m :: Type -> Type). MonadIO m => [PersistValue] -> Acquire (ConduitM () [PersistValue] m ())) -> Statement [stmtFinalize] :: Statement -> IO () [stmtReset] :: Statement -> IO () [stmtExecute] :: Statement -> [PersistValue] -> IO Int64 [stmtQuery] :: Statement -> forall (m :: Type -> Type). MonadIO m => [PersistValue] -> Acquire (ConduitM () [PersistValue] m ()) data InsertSqlResult ISRSingle :: Text -> InsertSqlResult ISRInsertGet :: Text -> Text -> InsertSqlResult ISRManyKeys :: Text -> [PersistValue] -> InsertSqlResult type LogFunc = Loc -> LogSource -> LogLevel -> LogStr -> IO () -- | Replace the FieldDef FieldAttr with the new list. setFieldAttrs :: [FieldAttr] -> FieldDef -> FieldDef -- | Modify the list of field attributes. overFieldAttrs :: ([FieldAttr] -> [FieldAttr]) -> FieldDef -> FieldDef -- | Add an attribute to the list of field attributes. addFieldAttr :: FieldAttr -> FieldDef -> FieldDef -- | Check if the field definition is nullable isFieldNullable :: FieldDef -> IsNullable -- | Check if the field is `Maybe a` isFieldMaybe :: FieldDef -> Bool -- | Retrieve the list of UniqueDef from an EntityDef. This -- currently does not include a Primary key, if one is defined. -- A future version of persistent will include a -- Primary key among the Unique constructors for the -- Entity. getEntityUniques :: EntityDef -> [UniqueDef] -- | Retrieve the Haskell name of the given entity. getEntityHaskellName :: EntityDef -> EntityNameHS -- | Return the database name for the given entity. getEntityDBName :: EntityDef -> EntityNameDB getEntityExtra :: EntityDef -> Map Text [[Text]] setEntityDBName :: EntityNameDB -> EntityDef -> EntityDef getEntityComments :: EntityDef -> Maybe Text getEntityForeignDefs :: EntityDef -> [ForeignDef] -- | Retrieve the list of FieldDef that makes up the fields of the -- entity. -- -- This does not return the fields for an Id column or an -- implicit id. It will return the key columns if you used the -- Primary syntax for defining the primary key. -- -- This does not return fields that are marked SafeToRemove or -- MigrationOnly - so it only returns fields that are -- represented in the Haskell type. If you need those fields, use -- getEntityFieldsDatabase. getEntityFields :: EntityDef -> [FieldDef] -- | This returns all of the FieldDef defined for the -- EntityDef, including those fields that are marked as -- MigrationOnly (and therefore only present in the database) or -- SafeToRemove (and a migration will drop the column if it -- exists in the database). -- -- For all the fields that are present on the Haskell-type, see -- getEntityFields. getEntityFieldsDatabase :: EntityDef -> [FieldDef] isEntitySum :: EntityDef -> Bool getEntityId :: EntityDef -> EntityIdDef getEntityIdField :: EntityDef -> Maybe FieldDef -- | Set an entityId to be the given FieldDef. setEntityId :: FieldDef -> EntityDef -> EntityDef setEntityIdDef :: EntityIdDef -> EntityDef -> EntityDef getEntityKeyFields :: EntityDef -> NonEmpty FieldDef -- | Perform a mapping function over all of the entity fields, as -- determined by getEntityFieldsDatabase. overEntityFields :: ([FieldDef] -> [FieldDef]) -> EntityDef -> EntityDef data SomePersistField SomePersistField :: a -> SomePersistField -- | Prior to persistent-2.11.0, we provided an instance of -- PersistField for the Natural type. This was in error, -- because Natural represents an infinite value, and databases -- don't have reasonable types for this. -- -- The instance for Natural used the Int64 underlying type, -- which will cause underflow and overflow errors. This type has the -- exact same code in the instances, and will work seamlessly. -- -- A more appropriate type for this is the Word series of types -- from Data.Word. These have a bounded size, are guaranteed to be -- non-negative, and are quite efficient for the database to store. newtype OverflowNatural OverflowNatural :: Natural -> OverflowNatural [unOverflowNatural] :: OverflowNatural -> Natural -- | This class teaches Persistent how to take a custom type and marshal it -- to and from a PersistValue, allowing it to be stored in a -- database. -- --
-- {-# LANGUAGE GeneralizedNewtypeDeriving #-}
--
-- newtype HashedPassword = HashedPassword ByteString
-- deriving (Eq, Show, PersistField, PersistFieldSql)
--
--
--
-- {-# LANGUAGE GeneralizedNewtypeDeriving #-}
-- import qualified Data.Text as T
-- import qualified Data.Char as C
--
-- -- | An American Social Security Number
-- newtype SSN = SSN Text
-- deriving (Eq, Show, PersistFieldSql)
--
-- mkSSN :: Text -> Either Text SSN
-- mkSSN t = if (T.length t == 9) && (T.all C.isDigit t)
-- then Right $ SSN t
-- else Left $ "Invalid SSN: " <> t
--
-- instance PersistField SSN where
-- toPersistValue (SSN t) = PersistText t
-- fromPersistValue (PersistText t) = mkSSN t
-- -- Handle cases where the database does not give us PersistText
-- fromPersistValue x = Left $ "File.hs: When trying to deserialize an SSN: expected PersistText, received: " <> T.pack (show x)
--
--
-- Tips:
--
-- -- instance ToJSON (Entity User) where -- toJSON = keyValueEntityToJSON --keyValueEntityToJSON :: (PersistEntity record, ToJSON record) => Entity record -> Value -- | Predefined parseJSON. The input JSON looks like {"key": -- 1, "value": {"name": ...}}. -- -- The typical usage is: -- --
-- instance FromJSON (Entity User) where -- parseJSON = keyValueEntityFromJSON --keyValueEntityFromJSON :: (PersistEntity record, FromJSON record) => Value -> Parser (Entity record) -- | Predefined toJSON. The resulting JSON looks like {"id": -- 1, "name": ...}. -- -- The typical usage is: -- --
-- instance ToJSON (Entity User) where -- toJSON = entityIdToJSON --entityIdToJSON :: (PersistEntity record, ToJSON record) => Entity record -> Value -- | Predefined parseJSON. The input JSON looks like {"id": 1, -- "name": ...}. -- -- The typical usage is: -- --
-- instance FromJSON (Entity User) where -- parseJSON = entityIdFromJSON --entityIdFromJSON :: (PersistEntity record, FromJSON record) => Value -> Parser (Entity record) -- | Convenience function for getting a free PersistField instance -- from a type with JSON instances. -- -- Example usage in combination with fromPersistValueJSON: -- --
-- instance PersistField MyData where -- fromPersistValue = fromPersistValueJSON -- toPersistValue = toPersistValueJSON --toPersistValueJSON :: ToJSON a => a -> PersistValue -- | Convenience function for getting a free PersistField instance -- from a type with JSON instances. The JSON parser used will accept JSON -- values other that object and arrays. So, if your instance serializes -- the data to a JSON string, this will still work. -- -- Example usage in combination with toPersistValueJSON: -- --
-- instance PersistField MyData where -- fromPersistValue = fromPersistValueJSON -- toPersistValue = toPersistValueJSON --fromPersistValueJSON :: FromJSON a => PersistValue -> Either Text a class (Show BackendKey backend, Read BackendKey backend, Eq BackendKey backend, Ord BackendKey backend, PersistStoreRead backend, PersistField BackendKey backend, ToJSON BackendKey backend, FromJSON BackendKey backend) => PersistStoreWrite backend -- | Create multiple records in the database and return their Keys. -- -- If you don't need the inserted Keys, use insertMany_. -- -- The MongoDB and PostgreSQL backends insert all records and retrieve -- their keys in one database query. -- -- The SQLite and MySQL backends use the slow, default implementation of -- mapM insert. -- --
-- insertUsers :: MonadIO m => ReaderT SqlBackend m [Key User] -- insertUsers = insertMany [User "John" 30, User "Nick" 32, User "Jane" 20] ---- --
-- userIds <- insertUsers ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |SPJ |40 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ -- |3 |John |30 | -- +-----+------+-----+ -- |4 |Nick |32 | -- +-----+------+-----+ -- |5 |Jane |20 | -- +-----+------+-----+ --insertMany :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => [record] -> ReaderT backend m [Key record] -- | Put the record in the database with the given key. Unlike -- replace, if a record with the given key does not exist then a -- new record will be inserted. -- --
-- insertPhilip :: MonadIO m => ReaderT SqlBackend m (Key User) -- insertPhilip = insert $ User "Philip" 42 ---- --
-- philipId <- insertPhilip ---- -- This query will produce: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |SPJ |40 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ -- |3 |Philip|42 | -- +-----+------+-----+ ---- --
-- repsertHaskell :: MonadIO m => Key record -> ReaderT SqlBackend m () -- repsertHaskell id = repsert id $ User "Haskell" 81 ---- --
-- repsertHaskell philipId ---- -- This query will replace Philip's record with Haskell's one: -- --
-- +-----+-----------------+--------+ -- |id |name |age | -- +-----+-----------------+--------+ -- |1 |SPJ |40 | -- +-----+-----------------+--------+ -- |2 |Simon |41 | -- +-----+-----------------+--------+ -- |3 |Philip -> Haskell|42 -> 81| -- +-----+-----------------+--------+ ---- -- repsert inserts the given record if the key doesn't exist. -- --
-- repsertXToUnknown :: MonadIO m => ReaderT SqlBackend m () -- repsertXToUnknown = repsert unknownId $ User "X" 999 ---- -- For example, applying the above query to dataset-1 will produce -- this: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |SPJ |40 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ -- |3 |X |999 | -- +-----+------+-----+ --repsert :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => Key record -> record -> ReaderT backend m () -- | Put many entities into the database. -- -- Batch version of repsert for SQL backends. -- -- Useful when migrating data from one entity to another and want to -- preserve ids. -- --
-- repsertManyUsers :: MonadIO m =>ReaderT SqlBackend m () -- repsertManyusers = repsertMany [(simonId, User "Philip" 20), (unknownId999, User "Mr. X" 999)] ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+----------------+---------+ -- |id |name |age | -- +-----+----------------+---------+ -- |1 |SPJ |40 | -- +-----+----------------+---------+ -- |2 |Simon -> Philip |41 -> 20 | -- +-----+----------------+---------+ -- |999 |Mr. X |999 | -- +-----+----------------+---------+ --repsertMany :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => [(Key record, record)] -> ReaderT backend m () -- | Replace the record in the database with the given key. Note that the -- result is undefined if such record does not exist, so you must use -- insertKey or repsert in these cases. -- --
-- replaceSpj :: MonadIO m => User -> ReaderT SqlBackend m () -- replaceSpj record = replace spjId record ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |Mike |45 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ --replace :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => Key record -> record -> ReaderT backend m () -- | Update individual fields on a specific record, and retrieve the -- updated value from the database. -- -- Note that this function will throw an exception if the given key is -- not found in the database. -- --
-- updateGetSpj :: MonadIO m => [Update User] -> ReaderT SqlBackend m User -- updateGetSpj updates = updateGet spjId updates ---- --
-- spj <- updateGetSpj [UserAge +=. 100] ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |SPJ |140 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ --updateGet :: forall record (m :: Type -> Type). (PersistStoreWrite backend, MonadIO m, PersistRecordBackend record backend) => Key record -> [Update record] -> ReaderT backend m record class (Show BackendKey backend, Read BackendKey backend, Eq BackendKey backend, Ord BackendKey backend, PersistCore backend, PersistField BackendKey backend, ToJSON BackendKey backend, FromJSON BackendKey backend) => PersistStoreRead backend class PersistCore backend where { data family BackendKey backend; } data family BackendKey backend -- | ToBackendKey converts a PersistEntity Key into a -- BackendKey This can be used by each backend to convert between -- a Key and a plain Haskell type. For Sql, that is done with -- toSqlKey and fromSqlKey. -- -- By default, a PersistEntity uses the default BackendKey -- for its Key and is an instance of ToBackendKey -- -- A Key that instead uses a custom type will not be an instance -- of ToBackendKey. class (PersistEntity record, PersistEntityBackend record ~ backend, PersistCore backend) => ToBackendKey backend record toBackendKey :: ToBackendKey backend record => Key record -> BackendKey backend fromBackendKey :: ToBackendKey backend record => BackendKey backend -> Key record -- | A convenient alias for common type signatures type PersistRecordBackend record backend = (PersistEntity record, PersistEntityBackend record ~ BaseBackend backend) -- | This class witnesses that two backend are compatible, and that you can -- convert from the sub backend into the sup backend. -- This is similar to the HasPersistBackend and -- IsPersistBackend classes, but where you don't want to fix the -- type associated with the PersistEntityBackend of a record. -- -- Generally speaking, where you might have: -- --
-- foo :: -- ( PersistEntity record -- , PeristEntityBackend record ~ BaseBackend backend -- , IsSqlBackend backend -- ) ---- -- this can be replaced with: -- --
-- foo :: -- ( PersistEntity record, -- , PersistEntityBackend record ~ backend -- , BackendCompatible SqlBackend backend -- ) ---- -- This works for SqlReadBackend because of the instance -- BackendCompatible SqlBackend -- SqlReadBackend, without needing to go through the -- BaseBackend type family. -- -- Likewise, functions that are currently hardcoded to use -- SqlBackend can be generalized: -- --
-- -- before: -- asdf :: ReaderT SqlBackend m () -- asdf = pure () -- -- -- after: -- asdf' :: BackendCompatible SqlBackend backend => ReaderT backend m () -- asdf' = withCompatibleBackend asdf --class BackendCompatible sup sub projectBackend :: BackendCompatible sup sub => sub -> sup -- | Class which witnesses that backend is essentially the same as -- BaseBackend backend. That is, they're isomorphic and -- backend is just some wrapper over BaseBackend -- backend. class HasPersistBackend backend => IsPersistBackend backend -- | Class which allows the plucking of a BaseBackend backend from -- some larger type. For example, instance HasPersistBackend -- (SqlReadBackend, Int) where type BaseBackend (SqlReadBackend, Int) = -- SqlBackend persistBackend = unSqlReadBackend . fst class HasPersistBackend backend where { type family BaseBackend backend; } persistBackend :: HasPersistBackend backend => backend -> BaseBackend backend type family BaseBackend backend -- | Run a query against a larger backend by plucking out BaseBackend -- backend -- -- This is a helper for reusing existing queries when expanding the -- backend type. withBaseBackend :: forall backend (m :: Type -> Type) a. HasPersistBackend backend => ReaderT (BaseBackend backend) m a -> ReaderT backend m a -- | Run a query against a compatible backend, by projecting the backend -- -- This is a helper for using queries which run against a specific -- backend type that your backend is compatible with. withCompatibleBackend :: forall sup sub (m :: Type -> Type) a. BackendCompatible sup sub => ReaderT sup m a -> ReaderT sub m a liftPersist :: (MonadIO m, MonadReader backend m) => ReaderT backend IO b -> m b -- | Same as get, but for a non-null (not Maybe) foreign key. Unsafe -- unless your database is enforcing that the foreign key is valid. -- --
-- getJustSpj :: MonadIO m => ReaderT SqlBackend m User -- getJustSpj = getJust spjId ---- --
-- spj <- getJust spjId ---- -- The above query when applied on dataset-1, will get this -- record: -- --
-- +----+------+-----+ -- | id | name | age | -- +----+------+-----+ -- | 1 | SPJ | 40 | -- +----+------+-----+ ---- --
-- getJustUnknown :: MonadIO m => ReaderT SqlBackend m User -- getJustUnknown = getJust unknownId ---- -- mrx <- getJustUnknown -- -- This just throws an error. getJust :: forall record backend (m :: Type -> Type). (PersistStoreRead backend, PersistRecordBackend record backend, MonadIO m) => Key record -> ReaderT backend m record -- | Same as getJust, but returns an Entity instead of just -- the record. -- --
-- getJustEntitySpj :: MonadIO m => ReaderT SqlBackend m (Entity User) -- getJustEntitySpj = getJustEntity spjId ---- --
-- spjEnt <- getJustEntitySpj ---- -- The above query when applied on dataset-1, will get this -- entity: -- --
-- +----+------+-----+ -- | id | name | age | -- +----+------+-----+ -- | 1 | SPJ | 40 | -- +----+------+-----+ --getJustEntity :: forall record backend (m :: Type -> Type). (PersistEntityBackend record ~ BaseBackend backend, MonadIO m, PersistEntity record, PersistStoreRead backend) => Key record -> ReaderT backend m (Entity record) -- | Curry this to make a convenience function that loads an associated -- model. -- --
-- foreign = belongsTo foreignId --belongsTo :: forall ent1 ent2 backend (m :: Type -> Type). (PersistStoreRead backend, PersistEntity ent1, PersistRecordBackend ent2 backend, MonadIO m) => (ent1 -> Maybe (Key ent2)) -> ent1 -> ReaderT backend m (Maybe ent2) -- | Same as belongsTo, but uses getJust and therefore is -- similarly unsafe. belongsToJust :: forall ent1 ent2 backend (m :: Type -> Type). (PersistStoreRead backend, PersistEntity ent1, PersistRecordBackend ent2 backend, MonadIO m) => (ent1 -> Key ent2) -> ent1 -> ReaderT backend m ent2 -- | Like insert, but returns the complete Entity. -- --
-- insertHaskellEntity :: MonadIO m => ReaderT SqlBackend m (Entity User) -- insertHaskellEntity = insertEntity $ User "Haskell" 81 ---- --
-- haskellEnt <- insertHaskellEntity ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +----+---------+-----+ -- | id | name | age | -- +----+---------+-----+ -- | 1 | SPJ | 40 | -- +----+---------+-----+ -- | 2 | Simon | 41 | -- +----+---------+-----+ -- | 3 | Haskell | 81 | -- +----+---------+-----+ --insertEntity :: forall e backend (m :: Type -> Type). (PersistStoreWrite backend, PersistRecordBackend e backend, MonadIO m) => e -> ReaderT backend m (Entity e) -- | Like insertEntity but just returns the record instead of -- Entity. -- --
-- insertDaveRecord :: MonadIO m => ReaderT SqlBackend m User -- insertDaveRecord = insertRecord $ User "Dave" 50 ---- --
-- dave <- insertDaveRecord ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |SPJ |40 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ -- |3 |Dave |50 | -- +-----+------+-----+ --insertRecord :: forall record backend (m :: Type -> Type). (PersistEntityBackend record ~ BaseBackend backend, PersistEntity record, MonadIO m, PersistStoreWrite backend) => record -> ReaderT backend m record -- | A SqlBackend represents a handle or connection to a database. -- It contains functions and values that allow databases to have more -- optimized implementations, as well as references that benefit -- performance and sharing. -- -- Instead of using the SqlBackend constructor directly, use the -- mkSqlBackend function. -- -- A SqlBackend is *not* thread-safe. You should not assume that a -- SqlBackend can be shared among threads and run concurrent -- queries. This *will* result in problems. Instead, you should create a -- Pool SqlBackend, known as a -- ConnectionPool, and pass that around in multi-threaded -- applications. -- -- To run actions in the persistent library, you should use the -- runSqlConn function. If you're using a multithreaded -- application, use the runSqlPool function. data SqlBackend -- | This class is used to ensure that functions requring at least one -- unique key are not called with records that have 0 unique keys. The -- quasiquoter automatically writes working instances for appropriate -- entities, and generates TypeError instances for records that -- have 0 unique keys. class PersistEntity record => AtLeastOneUniqueKey record requireUniquesP :: AtLeastOneUniqueKey record => record -> NonEmpty (Unique record) -- | This is an error message. It is used when an entity has multiple -- unique keys, and the function expects a single unique key. type MultipleUniqueKeysError ty = 'Text "The entity " :<>: 'ShowType ty :<>: 'Text " has multiple unique keys." :$$: 'Text "The function you are trying to call requires only a single " :<>: 'Text "unique key." :$$: 'Text "There is probably a variant of the function with 'By' " :<>: 'Text "appended that will allow you to select a unique key " :<>: 'Text "for the operation." -- | This is an error message. It is used when writing instances of -- OnlyOneUniqueKey for an entity that has no unique keys. type NoUniqueKeysError ty = 'Text "The entity " :<>: 'ShowType ty :<>: 'Text " does not have any unique keys." :$$: 'Text "The function you are trying to call requires a unique key " :<>: 'Text "to be defined on the entity." -- | This class is used to ensure that upsert is only called on -- records that have a single Unique key. The quasiquoter -- automatically generates working instances for appropriate records, and -- generates TypeError instances for records that have 0 or -- multiple unique keys. class PersistEntity record => OnlyOneUniqueKey record onlyUniqueP :: OnlyOneUniqueKey record => record -> Unique record -- | Some functions in this module (insertUnique, insertBy, -- and replaceUnique) first query the unique indexes to check for -- conflicts. You could instead optimistically attempt to perform the -- operation (e.g. replace instead of replaceUnique). -- However, -- --
-- deleteBySpjName :: MonadIO m => ReaderT SqlBackend m () -- deleteBySpjName = deleteBy UniqueUserName "SPJ" ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ --deleteBy :: forall record (m :: Type -> Type). (PersistUniqueWrite backend, MonadIO m, PersistRecordBackend record backend) => Unique record -> ReaderT backend m () -- | Like insert, but returns Nothing when the record -- couldn't be inserted because of a uniqueness constraint. -- --
-- linusId <- insertUnique $ User "Linus" 48 -- spjId <- insertUnique $ User "SPJ" 90 ---- --
-- +-----+------+-----+ -- |id |name |age | -- +-----+------+-----+ -- |1 |SPJ |40 | -- +-----+------+-----+ -- |2 |Simon |41 | -- +-----+------+-----+ -- |3 |Linus |48 | -- +-----+------+-----+ ---- -- Linus's record was inserted to dataset-1, while SPJ wasn't -- because SPJ already exists in dataset-1. insertUnique :: forall record (m :: Type -> Type). (PersistUniqueWrite backend, MonadIO m, PersistRecordBackend record backend) => record -> ReaderT backend m (Maybe (Key record)) -- | Update based on a uniqueness constraint or insert: -- --
-- upsertSpj :: MonadIO m => [Update User] -> ReaderT SqlBackend m (Maybe (Entity User)) -- upsertSpj updates = upsert (User "SPJ" 999) upadtes ---- --
-- mSpjEnt <- upsertSpj [UserAge +=. 15] ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+-----+--------+ -- |id |name |age | -- +-----+-----+--------+ -- |1 |SPJ |40 -> 55| -- +-----+-----+--------+ -- |2 |Simon|41 | -- +-----+-----+--------+ ---- --
-- upsertX :: MonadIO m => [Update User] -> ReaderT SqlBackend m (Maybe (Entity User)) -- upsertX updates = upsert (User "X" 999) updates ---- --
-- mXEnt <- upsertX [UserAge +=. 15] ---- -- The above query when applied on dataset-1, will produce this: -- --
-- +-----+-----+--------+ -- |id |name |age | -- +-----+-----+--------+ -- |1 |SPJ |40 | -- +-----+-----+--------+ -- |2 |Simon|41 | -- +-----+-----+--------+ -- |3 |X |999 | -- +-----+-----+--------+ ---- -- Next, what if the schema has two uniqueness constraints? Let's check -- it out using schema-2: -- --
-- mSpjEnt <- upsertSpj [UserAge +=. 15] ---- -- This fails with a compile-time type error alerting us to the fact that -- this record has multiple unique keys, and suggests that we look for -- upsertBy to select the unique key we want. upsert :: forall record (m :: Type -> Type). (PersistUniqueWrite backend, MonadIO m, PersistRecordBackend record backend, OnlyOneUniqueKey record) => record -> [Update record] -> ReaderT backend m (Entity record) -- | Update based on a given uniqueness constraint or insert: -- --
-- upsertBySpjName :: MonadIO m => User -> [Update User] -> ReaderT SqlBackend m (Entity User) -- upsertBySpjName record updates = upsertBy (UniqueUserName "SPJ") record updates ---- --
-- mSpjEnt <- upsertBySpjName (Person "X" 999) [PersonAge += .15] ---- -- The above query will alter dataset-1 to: -- --
-- +-----+-----+--------+ -- |id |name |age | -- +-----+-----+--------+ -- |1 |SPJ |40 -> 55| -- +-----+-----+--------+ -- |2 |Simon|41 | -- +-----+-----+--------+ ---- --
-- upsertBySimonAge :: MonadIO m => User -> [Update User] -> ReaderT SqlBackend m (Entity User) -- upsertBySimonAge record updates = upsertBy (UniqueUserName "SPJ") record updates ---- --
-- mPhilipEnt <- upsertBySimonAge (User "X" 999) [UserName =. "Philip"] ---- -- The above query will alter dataset-1 to: -- --
-- +----+-----------------+-----+ -- | id | name | age | -- +----+-----------------+-----+ -- | 1 | SPJ | 40 | -- +----+-----------------+-----+ -- | 2 | Simon -> Philip | 41 | -- +----+-----------------+-----+ ---- --
-- upsertByUnknownName :: MonadIO m => User -> [Update User] -> ReaderT SqlBackend m (Entity User) -- upsertByUnknownName record updates = upsertBy (UniqueUserName "Unknown") record updates ---- --
-- mXEnt <- upsertByUnknownName (User "X" 999) [UserAge +=. 15] ---- -- This query will alter dataset-1 to: -- --
-- +-----+-----+-----+ -- |id |name |age | -- +-----+-----+-----+ -- |1 |SPJ |40 | -- +-----+-----+-----+ -- |2 |Simon|41 | -- +-----+-----+-----+ -- |3 |X |999 | -- +-----+-----+-----+ --upsertBy :: forall record (m :: Type -> Type). (PersistUniqueWrite backend, MonadIO m, PersistRecordBackend record backend) => Unique record -> record -> [Update record] -> ReaderT backend m (Entity record) -- | Put many records into db -- --
-- l1 <- insertBy $ User "SPJ" 20 -- l2 <- insertBy $ User "XXX" 41 -- l3 <- insertBy $ User "SPJ" 40 -- r1 <- insertBy $ User "XXX" 100 ---- -- First three lines return Left because there're duplicates in -- given record's uniqueness constraints. While the last line returns a -- new key as Right. insertBy :: forall record backend (m :: Type -> Type). (MonadIO m, PersistUniqueWrite backend, PersistRecordBackend record backend, AtLeastOneUniqueKey record) => record -> ReaderT backend m (Either (Entity record) (Key record)) -- | Like insertEntity, but returns Nothing when the record -- couldn't be inserted because of a uniqueness constraint. -- --
-- insertUniqueSpjEntity :: MonadIO m => ReaderT SqlBackend m (Maybe (Entity User)) -- insertUniqueSpjEntity = insertUniqueEntity $ User "SPJ" 50 ---- --
-- mSpjEnt <- insertUniqueSpjEntity ---- -- The above query results Nothing as SPJ already exists. -- --
-- insertUniqueAlexaEntity :: MonadIO m => ReaderT SqlBackend m (Maybe (Entity User)) -- insertUniqueAlexaEntity = insertUniqueEntity $ User "Alexa" 3 ---- --
-- mAlexaEnt <- insertUniqueSpjEntity ---- -- Because there's no such unique keywords of the given record, the above -- query when applied on dataset-1, will produce this: -- --
-- +----+-------+-----+ -- | id | name | age | -- +----+-------+-----+ -- | 1 | SPJ | 40 | -- +----+-------+-----+ -- | 2 | Simon | 41 | -- +----+-------+-----+ -- | 3 | Alexa | 3 | -- +----+-------+-----+ --insertUniqueEntity :: forall record backend (m :: Type -> Type). (MonadIO m, PersistRecordBackend record backend, PersistUniqueWrite backend) => record -> ReaderT backend m (Maybe (Entity record)) -- | Return the single unique key for a record. -- --
-- onlySimonConst :: MonadIO m => ReaderT SqlBackend m (Unique User) -- onlySimonConst = onlyUnique $ User "Simon" 999 ---- --
-- mSimonConst <- onlySimonConst ---- -- mSimonConst would be Simon's uniqueness constraint. Note that -- onlyUnique doesn't work if there're more than two -- constraints. It will fail with a type error instead. onlyUnique :: forall record backend (m :: Type -> Type). (MonadIO m, PersistUniqueWrite backend, PersistRecordBackend record backend, OnlyOneUniqueKey record) => record -> ReaderT backend m (Unique record) -- | A modification of getBy, which takes the PersistEntity -- itself instead of a Unique record. Returns a record matching -- one of the unique keys. This function makes the most sense on -- entities with a single Unique constructor. -- --
-- mSpjEnt <- getBySpjValue ---- -- The above query when applied on dataset-1, will get this -- record: -- --
-- +----+------+-----+ -- | id | name | age | -- +----+------+-----+ -- | 1 | SPJ | 40 | -- +----+------+-----+ --getByValue :: forall record (m :: Type -> Type) backend. (MonadIO m, PersistUniqueRead backend, PersistRecordBackend record backend, AtLeastOneUniqueKey record) => record -> ReaderT backend m (Maybe (Entity record)) -- | Attempt to replace the record of the given key with the given new -- record. First query the unique fields to make sure the replacement -- maintains uniqueness constraints. -- -- Return Nothing if the replacement was made. If uniqueness is -- violated, return a Just with the Unique violation replaceUnique :: forall record backend (m :: Type -> Type). (MonadIO m, Eq (Unique record), PersistRecordBackend record backend, PersistUniqueWrite backend) => Key record -> record -> ReaderT backend m (Maybe (Unique record)) -- | Check whether there are any conflicts for unique keys with this entity -- and existing entities in the database. -- -- Returns Nothing if the entity would be unique, and could thus -- safely be inserted. on a conflict returns the conflicting key -- --
-- mAlanConst <- checkUnique $ User "Alan" 70 ---- -- While this would be Just because SPJ already exists: -- --
-- mSpjConst <- checkUnique $ User "SPJ" 60 --checkUnique :: forall record backend (m :: Type -> Type). (MonadIO m, PersistRecordBackend record backend, PersistUniqueRead backend) => record -> ReaderT backend m (Maybe (Unique record)) -- | Check whether there are any conflicts for unique keys with this entity -- and existing entities in the database. -- -- Returns Nothing if the entity would stay unique, and could thus -- safely be updated. on a conflict returns the conflicting key -- -- This is similar to checkUnique, except it's useful for updating -- - when the particular entity already exists, it would normally -- conflict with itself. This variant ignores those conflicts -- --
-- mAlanConst <- checkUnique $ User "Alan" 70 ---- -- While this would be Just because SPJ already exists: -- --
-- mSpjConst <- checkUnique $ User "SPJ" 60 --checkUniqueUpdateable :: forall record backend (m :: Type -> Type). (MonadIO m, PersistRecordBackend record backend, PersistUniqueRead backend) => Entity record -> ReaderT backend m (Maybe (Unique record)) -- | Backends supporting conditional write operations class (PersistQueryRead backend, PersistStoreWrite backend) => PersistQueryWrite backend -- | Backends supporting conditional read operations. class (PersistCore backend, PersistStoreRead backend) => PersistQueryRead backend -- | Get all records matching the given criterion in the specified order. -- Returns also the identifiers. -- -- NOTE: This function returns an Acquire and a ConduitM, -- which implies that it streams from the database. It does not. Please -- use selectList to simplify the code. If you want streaming -- behavior, consider persistent-pagination which efficiently -- chunks a query into ranges, or investigate a backend-specific -- streaming solution. selectSourceRes :: forall record (m1 :: Type -> Type) (m2 :: Type -> Type). (PersistQueryRead backend, PersistRecordBackend record backend, MonadIO m1, MonadIO m2) => [Filter record] -> [SelectOpt record] -> ReaderT backend m1 (Acquire (ConduitM () (Entity record) m2 ())) -- | Get the Keys of all records matching the given criterion. selectKeysRes :: forall (m1 :: Type -> Type) (m2 :: Type -> Type) record. (PersistQueryRead backend, MonadIO m1, MonadIO m2, PersistRecordBackend record backend) => [Filter record] -> [SelectOpt record] -> ReaderT backend m1 (Acquire (ConduitM () (Key record) m2 ())) -- | Get the Keys of all records matching the given criterion. -- -- For an example, see selectList. selectKeys :: forall record backend (m :: Type -> Type). (PersistQueryRead backend, MonadResource m, PersistRecordBackend record backend, MonadReader backend m) => [Filter record] -> [SelectOpt record] -> ConduitM () (Key record) m () -- | For combinations of backends and entities that support -- cascade-deletion. “Cascade-deletion” means that entries that depend on -- other entries to be deleted will be deleted as well. class (PersistStoreWrite backend, PersistEntity record, BaseBackend backend ~ PersistEntityBackend record) => DeleteCascade record backend -- | Perform cascade-deletion of single database entry. deleteCascade :: forall (m :: Type -> Type). (DeleteCascade record backend, MonadIO m) => Key record -> ReaderT backend m () -- | Cascade-deletion of entries satisfying given filters. deleteCascadeWhere :: forall record backend (m :: Type -> Type). (MonadIO m, DeleteCascade record backend, PersistQueryWrite backend) => [Filter record] -> ReaderT backend m () -- | A backwards-compatible alias for those that don't care about -- distinguishing between read and write queries. It signifies the -- assumption that, by default, a backend can write as well as read. type PersistStore a = PersistStoreWrite a -- | A backwards-compatible alias for those that don't care about -- distinguishing between read and write queries. It signifies the -- assumption that, by default, a backend can write as well as read. type PersistUnique a = PersistUniqueWrite a -- | A backend which is a wrapper around SqlBackend. type IsSqlBackend backend = (IsPersistBackend backend, BaseBackend backend ~ SqlBackend) -- | Like SqlPersistT but compatible with any SQL backend which -- can handle read and write queries. type SqlWriteT (m :: Type -> Type) a = forall backend. SqlBackendCanWrite backend => ReaderT backend m a -- | Like SqlPersistT but compatible with any SQL backend which -- can handle read queries. type SqlReadT (m :: Type -> Type) a = forall backend. SqlBackendCanRead backend => ReaderT backend m a -- | A constraint synonym which witnesses that a backend is SQL and can run -- read and write queries. type SqlBackendCanWrite backend = (SqlBackendCanRead backend, PersistQueryWrite backend, PersistStoreWrite backend, PersistUniqueWrite backend) -- | A constraint synonym which witnesses that a backend is SQL and can run -- read queries. type SqlBackendCanRead backend = (BackendCompatible SqlBackend backend, PersistQueryRead backend, PersistStoreRead backend, PersistUniqueRead backend) -- | An SQL backend which can handle read or write queries -- -- The constructor was exposed in 2.10.0 newtype SqlWriteBackend SqlWriteBackend :: SqlBackend -> SqlWriteBackend [$sel:unSqlWriteBackend:SqlWriteBackend] :: SqlWriteBackend -> SqlBackend -- | An SQL backend which can only handle read queries -- -- The constructor was exposed in 2.10.0. newtype SqlReadBackend SqlReadBackend :: SqlBackend -> SqlReadBackend [$sel:unSqlReadBackend:SqlReadBackend] :: SqlReadBackend -> SqlBackend -- | Useful for running a write query against an untagged backend with -- unknown capabilities. writeToUnknown :: forall (m :: Type -> Type) a. Monad m => ReaderT SqlWriteBackend m a -> ReaderT SqlBackend m a -- | Useful for running a read query against a backend with read and write -- capabilities. readToWrite :: forall (m :: Type -> Type) a. Monad m => ReaderT SqlReadBackend m a -> ReaderT SqlWriteBackend m a -- | Useful for running a read query against a backend with unknown -- capabilities. readToUnknown :: forall (m :: Type -> Type) a. Monad m => ReaderT SqlReadBackend m a -> ReaderT SqlBackend m a -- | A single column (see rawSql). Any PersistField may -- be used here, including PersistValue (which does not do any -- processing). newtype Single a Single :: a -> Single a [unSingle] :: Single a -> a -- | Values to configure a pool of database connections. See -- Data.Pool for details. data ConnectionPoolConfig ConnectionPoolConfig :: Int -> NominalDiffTime -> Int -> ConnectionPoolConfig -- | How many stripes to divide the pool into. See Data.Pool for -- details. Default: 1. [connectionPoolConfigStripes] :: ConnectionPoolConfig -> Int -- | How long connections can remain idle before being disposed of, in -- seconds. Default: 600 [connectionPoolConfigIdleTimeout] :: ConnectionPoolConfig -> NominalDiffTime -- | How many connections should be held in the connection pool. Default: -- 10 [connectionPoolConfigSize] :: ConnectionPoolConfig -> Int type ConnectionPool = Pool SqlBackend type SqlPersistM = SqlPersistT NoLoggingT ResourceT IO type SqlPersistT = ReaderT SqlBackend data PersistentSqlException StatementAlreadyFinalized :: Text -> PersistentSqlException Couldn'tGetSQLConnection :: PersistentSqlException -- | This value specifies how a field references another table. data ColumnReference ColumnReference :: !EntityNameDB -> !ConstraintNameDB -> !FieldCascade -> ColumnReference -- | The table name that the [crTableName] :: ColumnReference -> !EntityNameDB -- | The name of the foreign key constraint. [crConstraintName] :: ColumnReference -> !ConstraintNameDB -- | Whether or not updates/deletions to the referenced table cascade to -- this table. [crFieldCascade] :: ColumnReference -> !FieldCascade data Column Column :: !FieldNameDB -> !Bool -> !SqlType -> !Maybe Text -> !Maybe Text -> !Maybe ConstraintNameDB -> !Maybe Integer -> !Maybe ColumnReference -> Column [cName] :: Column -> !FieldNameDB [cNull] :: Column -> !Bool [cSqlType] :: Column -> !SqlType [cDefault] :: Column -> !Maybe Text [cGenerated] :: Column -> !Maybe Text [cDefaultConstraintName] :: Column -> !Maybe ConstraintNameDB [cMaxLen] :: Column -> !Maybe Integer [cReference] :: Column -> !Maybe ColumnReference -- | Initializes a ConnectionPoolConfig with default values. See the -- documentation of ConnectionPoolConfig for each field's default -- value. defaultConnectionPoolConfig :: ConnectionPoolConfig -- | Record of functions to override the default behavior in -- mkColumns. It is recommended you initialize this with -- emptyBackendSpecificOverrides and override the default values, -- so that as new fields are added, your code still compiles. -- -- For added safety, use the getBackendSpecific* and -- setBackendSpecific* functions, as a breaking change to the -- record field labels won't be reflected in a major version bump of the -- library. data BackendSpecificOverrides -- | If the override is defined, then this returns a function that accepts -- an entity name and field name and provides the ConstraintNameDB -- for the foreign key constraint. -- -- An abstract accessor for the BackendSpecificOverrides getBackendSpecificForeignKeyName :: BackendSpecificOverrides -> Maybe (EntityNameDB -> FieldNameDB -> ConstraintNameDB) -- | Set the backend's foreign key generation function to this value. setBackendSpecificForeignKeyName :: (EntityNameDB -> FieldNameDB -> ConstraintNameDB) -> BackendSpecificOverrides -> BackendSpecificOverrides -- | Creates an empty BackendSpecificOverrides (i.e. use the default -- behavior; no overrides) emptyBackendSpecificOverrides :: BackendSpecificOverrides defaultAttribute :: [FieldAttr] -> Maybe Text -- | Create the list of columns for the given entity. mkColumns :: [EntityDef] -> EntityDef -> BackendSpecificOverrides -> ([Column], [UniqueDef], [ForeignDef]) -- | A more general way to convert instances of ToJSON type class to -- strict text Text. toJsonText :: ToJSON j => j -> Text -- | Tells Persistent what database column type should be used to store a -- Haskell type. -- --
-- data Switch = On | Off -- deriving (Show, Eq) -- -- instance PersistField Switch where -- toPersistValue s = case s of -- On -> PersistBool True -- Off -> PersistBool False -- fromPersistValue (PersistBool b) = if b then Right On else Right Off -- fromPersistValue x = Left $ "File.hs: When trying to deserialize a Switch: expected PersistBool, received: " <> T.pack (show x) -- -- instance PersistFieldSql Switch where -- sqlType _ = SqlBool ---- --
-- import qualified Data.UUID as UUID -- instance PersistField UUID where -- toPersistValue = PersistLiteralEncoded . toASCIIBytes -- fromPersistValue (PersistLiteralEncoded uuid) = -- case fromASCIIBytes uuid of -- Nothing -> Left $ "Model/CustomTypes.hs: Failed to deserialize a UUID; received: " <> T.pack (show uuid) -- Just uuid' -> Right uuid' -- fromPersistValue x = Left $ "File.hs: When trying to deserialize a UUID: expected PersistLiteralEncoded, received: "-- > <> T.pack (show x) -- -- instance PersistFieldSql UUID where -- sqlType _ = SqlOther "uuid" ---- --
-- CREATE DOMAIN ssn AS text
-- CHECK ( value ~ '^[0-9]{9}$');
--
--
-- -- instance PersistFieldSQL SSN where -- sqlType _ = SqlOther "ssn" ---- --
-- CREATE TYPE rainbow_color AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet');
--
--
-- -- instance PersistFieldSQL RainbowColor where -- sqlType _ = SqlOther "rainbow_color" --class PersistField a => PersistFieldSql a sqlType :: PersistFieldSql a => Proxy a -> SqlType -- | This newtype wrapper is useful when selecting an entity out of the -- database and you want to provide a prefix to the table being selected. -- -- Consider this raw SQL query: -- --
-- SELECT ?? -- FROM my_long_table_name AS mltn -- INNER JOIN other_table AS ot -- ON mltn.some_col = ot.other_col -- WHERE ... ---- -- We don't want to refer to my_long_table_name every time, so -- we create an alias. If we want to select it, we have to tell the raw -- SQL quasi-quoter that we expect the entity to be prefixed with some -- other name. -- -- We can give the above query a type with this, like: -- --
-- getStuff :: SqlPersistM [EntityWithPrefix "mltn" MyLongTableName] -- getStuff = rawSql queryText [] ---- -- The EntityWithPrefix bit is a boilerplate newtype wrapper, so -- you can remove it with unPrefix, like this: -- --
-- getStuff :: SqlPersistM [Entity MyLongTableName] -- getStuff = unPrefix @"mltn" <$> rawSql queryText [] ---- -- The symbol is a "type application" and requires the -- TypeApplications@ language extension. newtype EntityWithPrefix (prefix :: Symbol) record EntityWithPrefix :: Entity record -> EntityWithPrefix (prefix :: Symbol) record [unEntityWithPrefix] :: EntityWithPrefix (prefix :: Symbol) record -> Entity record -- | Class for data types that may be retrived from a rawSql -- query. class RawSql a -- | Number of columns that this data type needs and the list of -- substitutions for SELECT placeholders ??. rawSqlCols :: RawSql a => (Text -> Text) -> a -> (Int, [Text]) -- | A string telling the user why the column count is what it is. rawSqlColCountReason :: RawSql a => a -> String -- | Transform a row of the result into the data type. rawSqlProcessRow :: RawSql a => [PersistValue] -> Either Text a -- | A helper function to tell GHC what the EntityWithPrefix prefix -- should be. This allows you to use a type application to specify the -- prefix, instead of specifying the etype on the result. -- -- As an example, here's code that uses this: -- --
-- myQuery :: SqlPersistM [Entity Person] -- myQuery = fmap (unPrefix @"p") $ rawSql query [] -- where -- query = "SELECT ?? FROM person AS p" --unPrefix :: forall (prefix :: Symbol) record. EntityWithPrefix prefix record -> Entity record rawQuery :: forall (m :: Type -> Type) env. (MonadResource m, MonadReader env m, BackendCompatible SqlBackend env) => Text -> [PersistValue] -> ConduitM () [PersistValue] m () rawQueryRes :: forall (m1 :: Type -> Type) (m2 :: Type -> Type) env. (MonadIO m1, MonadIO m2, BackendCompatible SqlBackend env) => Text -> [PersistValue] -> ReaderT env m1 (Acquire (ConduitM () [PersistValue] m2 ())) -- | Execute a raw SQL statement rawExecute :: forall (m :: Type -> Type) backend. (MonadIO m, BackendCompatible SqlBackend backend) => Text -> [PersistValue] -> ReaderT backend m () -- | Execute a raw SQL statement and return the number of rows it has -- modified. rawExecuteCount :: forall (m :: Type -> Type) backend. (MonadIO m, BackendCompatible SqlBackend backend) => Text -> [PersistValue] -> ReaderT backend m Int64 getStmtConn :: SqlBackend -> Text -> IO Statement -- | Execute a raw SQL statement and return its results as a list. If you -- do not expect a return value, use of rawExecute is recommended. -- -- If you're using Entitys (which is quite likely), then -- you must use entity selection placeholders (double question -- mark, ??). These ?? placeholders are then replaced -- for the names of the columns that we need for your entities. You'll -- receive an error if you don't use the placeholders. Please see the -- Entitys documentation for more details. -- -- You may put value placeholders (question marks, ?) in your -- SQL query. These placeholders are then replaced by the values you pass -- on the second parameter, already correctly escaped. You may want to -- use toPersistValue to help you constructing the placeholder -- values. -- -- Since you're giving a raw SQL statement, you don't get any guarantees -- regarding safety. If rawSql is not able to parse the results of -- your query back, then an exception is raised. However, most common -- problems are mitigated by using the entity selection placeholder -- ??, and you shouldn't see any error at all if you're not -- using Single. -- -- Some example of rawSql based on this schema: -- --
-- share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase| -- Person -- name String -- age Int Maybe -- deriving Show -- BlogPost -- title String -- authorId PersonId -- deriving Show -- |] ---- -- Examples based on the above schema: -- --
-- getPerson :: MonadIO m => ReaderT SqlBackend m [Entity Person] -- getPerson = rawSql "select ?? from person where name=?" [PersistText "john"] -- -- getAge :: MonadIO m => ReaderT SqlBackend m [Single Int] -- getAge = rawSql "select person.age from person where name=?" [PersistText "john"] -- -- getAgeName :: MonadIO m => ReaderT SqlBackend m [(Single Int, Single Text)] -- getAgeName = rawSql "select person.age, person.name from person where name=?" [PersistText "john"] -- -- getPersonBlog :: MonadIO m => ReaderT SqlBackend m [(Entity Person, Entity BlogPost)] -- getPersonBlog = rawSql "select ??,?? from person,blog_post where person.id = blog_post.author_id" [] ---- -- Minimal working program for PostgreSQL backend based on the above -- concepts: -- --
-- {-# LANGUAGE EmptyDataDecls #-}
-- {-# LANGUAGE FlexibleContexts #-}
-- {-# LANGUAGE GADTs #-}
-- {-# LANGUAGE GeneralizedNewtypeDeriving #-}
-- {-# LANGUAGE MultiParamTypeClasses #-}
-- {-# LANGUAGE OverloadedStrings #-}
-- {-# LANGUAGE QuasiQuotes #-}
-- {-# LANGUAGE TemplateHaskell #-}
-- {-# LANGUAGE TypeFamilies #-}
--
-- import Control.Monad.IO.Class (liftIO)
-- import Control.Monad.Logger (runStderrLoggingT)
-- import Database.Persist
-- import Control.Monad.Reader
-- import Data.Text
-- import Database.Persist.Sql
-- import Database.Persist.Postgresql
-- import Database.Persist.TH
--
-- share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
-- Person
-- name String
-- age Int Maybe
-- deriving Show
-- |]
--
-- conn = "host=localhost dbname=new_db user=postgres password=postgres port=5432"
--
-- getPerson :: MonadIO m => ReaderT SqlBackend m [Entity Person]
-- getPerson = rawSql "select ?? from person where name=?" [PersistText "sibi"]
--
-- liftSqlPersistMPool y x = liftIO (runSqlPersistMPool y x)
--
-- main :: IO ()
-- main = runStderrLoggingT $ withPostgresqlPool conn 10 $ liftSqlPersistMPool $ do
-- runMigration migrateAll
-- xs <- getPerson
-- liftIO (print xs)
--
rawSql :: forall a (m :: Type -> Type) backend. (RawSql a, MonadIO m, BackendCompatible SqlBackend backend) => Text -> [PersistValue] -> ReaderT backend m [a]
-- | Get a connection from the pool, run the given action, and then return
-- the connection to the pool.
--
-- This function performs the given action in a transaction. If an
-- exception occurs during the action, then the transaction is rolled
-- back.
--
-- Note: This function previously timed out after 2 seconds, but this
-- behavior was buggy and caused more problems than it solved. Since
-- version 2.1.2, it performs no timeout checks.
runSqlPool :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> Pool backend -> m a
-- | Like runSqlPool, but supports specifying an isolation level.
runSqlPoolWithIsolation :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> Pool backend -> IsolationLevel -> m a
-- | Like runSqlPool, but does not surround the action in a
-- transaction. This action might leave your database in a weird state.
runSqlPoolNoTransaction :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> Pool backend -> Maybe IsolationLevel -> m a
-- | This function is how runSqlPool and
-- runSqlPoolNoTransaction are defined. In addition to the action
-- to be performed and the Pool of conections to use, we give you
-- the opportunity to provide three actions - initialize, afterwards, and
-- onException.
runSqlPoolWithHooks :: forall backend m a before after onException. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> Pool backend -> Maybe IsolationLevel -> (backend -> m before) -> (backend -> m after) -> (backend -> SomeException -> m onException) -> m a
-- | This function is how runSqlPoolWithHooks is defined.
--
-- It's currently the most general function for using a SQL pool.
runSqlPoolWithExtensibleHooks :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> Pool backend -> Maybe IsolationLevel -> SqlPoolHooks m backend -> m a
-- | Starts a new transaction on the connection. When the acquired
-- connection is released the transaction is committed and the connection
-- returned to the pool.
--
-- Upon an exception the transaction is rolled back and the connection
-- destroyed.
--
-- This is equivalent to runSqlConn but does not incur the
-- MonadUnliftIO constraint, meaning it can be used within, for
-- example, a Conduit pipeline.
acquireSqlConn :: (MonadReader backend m, BackendCompatible SqlBackend backend) => m (Acquire backend)
-- | Like acquireSqlConn, but lets you specify an explicit isolation
-- level.
acquireSqlConnWithIsolation :: (MonadReader backend m, BackendCompatible SqlBackend backend) => IsolationLevel -> m (Acquire backend)
runSqlConn :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> backend -> m a
-- | Like runSqlConn, but supports specifying an isolation level.
runSqlConnWithIsolation :: forall backend m a. (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> backend -> IsolationLevel -> m a
runSqlPersistM :: BackendCompatible SqlBackend backend => ReaderT backend (NoLoggingT (ResourceT IO)) a -> backend -> IO a
runSqlPersistMPool :: BackendCompatible SqlBackend backend => ReaderT backend (NoLoggingT (ResourceT IO)) a -> Pool backend -> IO a
liftSqlPersistMPool :: forall backend m a. (MonadIO m, BackendCompatible SqlBackend backend) => ReaderT backend (NoLoggingT (ResourceT IO)) a -> Pool backend -> m a
withSqlPool :: forall backend m a. (MonadLoggerIO m, MonadUnliftIO m, BackendCompatible SqlBackend backend) => (LogFunc -> IO backend) -> Int -> (Pool backend -> m a) -> m a
-- | Creates a pool of connections to a SQL database which can be used by
-- the Pool backend -> m a function. After the function
-- completes, the connections are destroyed.
withSqlPoolWithConfig :: forall backend m a. (MonadLoggerIO m, MonadUnliftIO m, BackendCompatible SqlBackend backend) => (LogFunc -> IO backend) -> ConnectionPoolConfig -> (Pool backend -> m a) -> m a
createSqlPool :: forall backend m. (MonadLoggerIO m, MonadUnliftIO m, BackendCompatible SqlBackend backend) => (LogFunc -> IO backend) -> Int -> m (Pool backend)
-- | Creates a pool of connections to a SQL database.
createSqlPoolWithConfig :: (MonadLoggerIO m, MonadUnliftIO m, BackendCompatible SqlBackend backend) => (LogFunc -> IO backend) -> ConnectionPoolConfig -> m (Pool backend)
-- | Create a connection and run sql queries within it. This function
-- automatically closes the connection on it's completion.
--
--
-- {-# LANGUAGE GADTs #-}
-- {-# LANGUAGE ScopedTypeVariables #-}
-- {-# LANGUAGE OverloadedStrings #-}
-- {-# LANGUAGE MultiParamTypeClasses #-}
-- {-# LANGUAGE TypeFamilies#-}
-- {-# LANGUAGE TemplateHaskell#-}
-- {-# LANGUAGE QuasiQuotes#-}
-- {-# LANGUAGE GeneralizedNewtypeDeriving #-}
--
-- import Control.Monad.IO.Class (liftIO)
-- import Control.Monad.Logger
-- import Conduit
-- import Database.Persist
-- import Database.Sqlite
-- import Database.Persist.Sqlite
-- import Database.Persist.TH
--
-- share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
-- Person
-- name String
-- age Int Maybe
-- deriving Show
-- |]
--
-- openConnection :: LogFunc -> IO SqlBackend
-- openConnection logfn = do
-- conn <- open "/home/sibi/test.db"
-- wrapConnection conn logfn
--
-- main :: IO ()
-- main = do
-- runNoLoggingT $ runResourceT $ withSqlConn openConnection (\backend ->
-- flip runSqlConn backend $ do
-- runMigration migrateAll
-- insert_ $ Person "John doe" $ Just 35
-- insert_ $ Person "Divya" $ Just 36
-- (pers :: [Entity Person]) <- selectList [] []
-- liftIO $ print pers
-- return ()
-- )
--
--
-- On executing it, you get this output:
--
--
-- Migrating: CREATE TABLE "person"("id" INTEGER PRIMARY KEY,"name" VARCHAR NOT NULL,"age" INTEGER NULL)
-- [Entity {entityKey = PersonKey {unPersonKey = SqlBackendKey {unSqlBackendKey = 1}}, entityVal = Person {personName = "John doe", personAge = Just 35}},Entity {entityKey = PersonKey {unPersonKey = SqlBackendKey {unSqlBackendKey = 2}}, entityVal = Person {personName = "Hema", personAge = Just 36}}]
--
withSqlConn :: forall backend m a. (MonadUnliftIO m, MonadLoggerIO m, BackendCompatible SqlBackend backend) => (LogFunc -> IO backend) -> (backend -> m a) -> m a
close' :: BackendCompatible SqlBackend backend => backend -> IO ()
withRawQuery :: forall (m :: Type -> Type) a. MonadIO m => Text -> [PersistValue] -> ConduitM [PersistValue] Void IO a -> ReaderT SqlBackend m a
toSqlKey :: ToBackendKey SqlBackend record => Int64 -> Key record
fromSqlKey :: ToBackendKey SqlBackend record => Key record -> Int64
-- | get the SQL string for the table that a PeristEntity represents Useful
-- for raw SQL queries
--
-- Your backend may provide a more convenient tableName function which
-- does not operate in a Monad
getTableName :: forall record (m :: Type -> Type) backend. (PersistEntity record, BackendCompatible SqlBackend backend, Monad m) => record -> ReaderT backend m Text
-- | useful for a backend to implement tableName by adding escaping
tableDBName :: PersistEntity record => record -> EntityNameDB
-- | get the SQL string for the field that an EntityField represents Useful
-- for raw SQL queries
--
-- Your backend may provide a more convenient fieldName function which
-- does not operate in a Monad
getFieldName :: forall record typ (m :: Type -> Type) backend. (PersistEntity record, PersistEntityBackend record ~ SqlBackend, BackendCompatible SqlBackend backend, Monad m) => EntityField record typ -> ReaderT backend m Text
-- | useful for a backend to implement fieldName by adding escaping
fieldDBName :: PersistEntity record => EntityField record typ -> FieldNameDB
-- | Used when determining how to prefix a column name in a WHERE
-- clause.
data FilterTablePrefix
-- | Prefix the column with the table name. This is useful if the column
-- name might be ambiguous.
PrefixTableName :: FilterTablePrefix
-- | Prefix the column name with the EXCLUDED keyword. This is
-- used with the Postgresql backend when doing ON CONFLICT DO
-- UPDATE clauses - see the documentation on upsertWhere
-- and upsertManyWhere.
PrefixExcluded :: FilterTablePrefix
-- | Render a [Filter record] into a Text value
-- suitable for inclusion into a SQL query.
filterClause :: PersistEntity val => Maybe FilterTablePrefix -> SqlBackend -> [Filter val] -> Text
-- | Render a [Filter record] into a Text value
-- suitable for inclusion into a SQL query, as well as the
-- [PersistValue] to properly fill in the ?
-- place holders.
filterClauseWithVals :: PersistEntity val => Maybe FilterTablePrefix -> SqlBackend -> [Filter val] -> (Text, [PersistValue])
-- | Render a [SelectOpt record] made up *only* of
-- Asc and Desc constructors into a Text value
-- suitable for inclusion into a SQL query.
orderClause :: PersistEntity val => Maybe FilterTablePrefix -> SqlBackend -> [SelectOpt val] -> Text
-- | Generates sql for limit and offset for postgres, sqlite and mysql.
decorateSQLWithLimitOffset :: Text -> (Int, Int) -> Text -> Text
-- | An exception indicating that Persistent refused to run some unsafe
-- migrations. Contains a list of pairs where the Bool tracks whether the
-- migration was unsafe (True means unsafe), and the Sql is the sql
-- statement for the migration.
newtype PersistUnsafeMigrationException
PersistUnsafeMigrationException :: [(Bool, Sql)] -> PersistUnsafeMigrationException
-- | A Migration is a four level monad stack consisting of:
--
-- -- runSqlCommand $ rawExecute "CREATE EXTENSION IF NOT EXISTS "uuid-ossp";" [] --runSqlCommand :: SqlPersistT IO () -> Migration -- | Commit the current transaction and begin a new one. This is used when -- a transaction commit is required within the context of -- runSqlConn (which brackets its provided action with a -- transaction begin/commit pair). transactionSave :: forall (m :: Type -> Type). MonadIO m => ReaderT SqlBackend m () -- | Commit the current transaction and begin a new one with the specified -- isolation level. transactionSaveWithIsolation :: forall (m :: Type -> Type). MonadIO m => IsolationLevel -> ReaderT SqlBackend m () -- | Roll back the current transaction and begin a new one. This rolls back -- to the state of the last call to transactionSave or the -- enclosing runSqlConn call. transactionUndo :: forall (m :: Type -> Type). MonadIO m => ReaderT SqlBackend m () -- | Roll back the current transaction and begin a new one with the -- specified isolation level. transactionUndoWithIsolation :: forall (m :: Type -> Type). MonadIO m => IsolationLevel -> ReaderT SqlBackend m () module Persistent.Event.Source.EventStore.Default defaultStoreMany :: (PersistRecordBackend record SqlBackend, Typeable record, MonadSqlQuery m) => [record] -> m [Key record] defaultGetLastAppliedEventId :: (PersistEntity record, Typeable record, MonadSqlQuery m, PersistEntityBackend record ~ SqlBackend) => EntityField record typ -> (record -> b) -> m (Maybe b) defaultMarkEventsApplied :: (MonadIO m, PersistEntity record, Typeable record, MonadSqlQuery m, PersistEntityBackend record ~ SqlBackend) => (t -> Key record) -> (UTCTime -> t -> record) -> [t] -> m () defaultLoadUnappliedEvents :: (Traversable t, MonadSqlQuery m, PersistEntity val1, PersistEntity val2, PersistField a) => EntityField val1 a -> EntityField val2 a -> t a -> m [Entity val1] module Persistent.Event.Source.Projection -- | Projection is about setting your event sourced table to data in the -- event. class Projection a where { type family Event a = ev | ev -> a; } -- | Apply event to this context Intended to have write access to the -- database for updating views apply :: (Projection a, MonadUnliftIO m, MonadLogger m, MonadSqlQuery m) => Event a -> m () module Persistent.Event.Source.EventStore -- | Determines how events are stored and retrieved. class Projection a => EventStore a storeMany :: (EventStore a, MonadIO m, MonadSqlQuery m) => [Event a] -> m [Key (Event a)] -- | Nothing if no last applied event found. getLastAppliedEventId :: (EventStore a, MonadIO m, MonadSqlQuery m) => m (Maybe (Key (Event a))) markEventsApplied :: (EventStore a, MonadIO m, MonadSqlQuery m) => [Key (Event a)] -> m () -- | Will load all events on nothing loadUnappliedEvents :: (EventStore a, MonadIO m, MonadSqlQuery m) => Maybe (Key (Event a)) -> m [Entity (Event a)] module Persistent.Event.Source.Aggregate -- | Aggregate is an intermediate step, allowing you to specify how your -- changeable commands are stored. stored events should be stable like an -- API. class Projection a => Aggregate a where { -- | The command is a sumtype with all your possible event sourced actions. type family Command a = cmd | cmd -> a; -- | allows you to specify who executed a command. for audit purposes. set -- this to () if you don't care about this. type family Actor a; } -- | Validate action and generate events, if any. act :: (Aggregate a, MonadSqlQuery m, MonadIO m) => Maybe (Actor a) -> Command a -> m [Event a] module Persistent.Event.Source -- | Executes command and applies events, as well as storing them, aka -- transact or actAndApply handleCmdWithAuthor :: (Aggregate a, EventStore a, MonadUnliftIO m, MonadSqlQuery m, MonadLogger m) => Maybe (Actor a) -> Command a -> m [Entity (Event a)] applyEventsSince :: (EventStore a, MonadUnliftIO m, MonadSqlQuery m, MonadLogger m) => Maybe (Key (Event a)) -> m ()