HORIZON HASKELLDocslts/ghc-9.10.x248f8f02026-10-05Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · 248f8f0 · 2026-10-05

Modulepersistent-2.14.6.3Haskell2010

Database.Persist.Sql

This module is the primary entry point if you're working with persistent on a SQL database.

Getting Started

First, you'll want to define your database entities. You can do that with "Database.Persist.Quasi."

Then, you'll use the operations

  • 24 types
  • 2 classes
  • 54 values

RawSql and PersistFieldSql

4 declarations
classclass PersistField a => PersistFieldSql a where
#

Tells Persistent what database column type should be used to store a Haskell type.

Examples
Simple Boolean Alternative
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
Non-Standard Database Types

If your database supports non-standard types, such as Postgres' uuid, you can use SqlOther to use them:

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"
User Created Database Types

Similarly, some databases support creating custom types, e.g. Postgres' DOMAIN and ENUM features. You can use SqlOther to specify a custom type:

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"

Methods

Instances37PersistFieldSql, …
classclass RawSql a where
#

Class for data types that may be retrived from a rawSql query.

Methods

Instances66RawSql, …
newtypenewtype EntityWithPrefix (prefix :: Symbol) record
#

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.

Instances1RawSql
valueunPrefix :: EntityWithPrefix prefix record -> Entity record
#

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"

Running actions

18 declarations

Run actions in a transaction with runSqlPool.

valuerunSqlPool
  1. :: (MonadUnliftIO m, BackendCompatible SqlBackend backend)
  2. => ReaderT backend m a
  3. -> Pool backend
  4. -> 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.

valuerunSqlPoolWithHooks
  1. :: (MonadUnliftIO m, BackendCompatible SqlBackend backend)
  2. => ReaderT backend m a
  3. -> Pool backend
  4. -> Maybe IsolationLevel
  5. -> (backend -> m before)

    Run this action immediately before the action is performed.

  6. -> (backend -> m after)

    Run this action immediately after the action is completed.

  7. -> (backend -> SomeException -> m onException)

    This action is performed when an exception is received. The exception is provided as a convenience - it is rethrown once this cleanup function is complete.

  8. -> 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.

valueacquireSqlConn :: (MonadReader backend m, BackendCompatible SqlBackend backend) => m (Acquire backend)
#

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.

valuewithSqlConn
  1. :: (MonadUnliftIO m, MonadLoggerIO m, BackendCompatible SqlBackend backend)
  2. => LogFunc -> IO backend
  3. -> backend -> m a
  4. -> m a
#

Create a connection and run sql queries within it. This function automatically closes the connection on it's completion.

Example usage
{-# 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}}]

Migrations

0 declarations

persistent combinators

15 declarations

We re-export Database.Persist here, to make it easier to use query and update combinators. Check out that module for documentation.

familydata family BackendKey backend
#
Instances71Bounded, Enum, Eq, Integral, Num, Ord, …
familydata family BackendKey backend
#
Instances71Bounded, Enum, Eq, Integral, Num, Ord, …

The Escape Hatch

5 declarations

persistent offers a set of functions that are useful for operating directly on the underlying SQL database. This can allow you to use whatever SQL features you want.

Consider going to <https://hackage.haskell.org/package/esqueleto esqueleto> for a more powerful SQL query library built on persistent.

valuerawSql
  1. :: (RawSql a, MonadIO m, BackendCompatible SqlBackend backend)
  2. => Text

    SQL statement, possibly with placeholders.

  3. -> [PersistValue]

    Values to fill the placeholders.

  4. -> ReaderT backend m [a]
#

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)

SQL helpers

6 declarations
datadata FilterTablePrefix
#

Used when determining how to prefix a column name in a WHERE clause.

Constructors

  • PrefixTableName

    Prefix the column with the table name. This is useful if the column name might be ambiguous.

  • PrefixExcluded

    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.

Transactions

4 declarations

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).

Other utilities

7 declarations

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.

Internal

26 declarations
datadata IsolationLevel
#

Please refer to the documentation for the database in question for a full overview of the semantics of the varying isloation levels

Instances5Bounded, Enum, Eq, Ord, Show
  • Bounded IsolationLevelDefined in persistent-2.14.6.3 · Database.Persist.SqlBackend.Internal.IsolationLevel
  • Enum IsolationLevelDefined in persistent-2.14.6.3 · Database.Persist.SqlBackend.Internal.IsolationLevel
  • Eq IsolationLevelDefined in persistent-2.14.6.3 · Database.Persist.SqlBackend.Internal.IsolationLevel
  • Ord IsolationLevelDefined in persistent-2.14.6.3 · Database.Persist.SqlBackend.Internal.IsolationLevel
  • Show IsolationLevelDefined in persistent-2.14.6.3 · Database.Persist.SqlBackend.Internal.IsolationLevel
newtypenewtype OverflowNatural
#

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.

Instances6Eq, Num, Ord, Show, PersistField, PersistFieldSql
  • Eq OverflowNaturalDefined in persistent-2.14.6.3 · Database.Persist.Class.PersistField
  • Num OverflowNaturalDefined in persistent-2.14.6.3 · Database.Persist.Class.PersistField
  • Ord OverflowNaturalDefined in persistent-2.14.6.3 · Database.Persist.Class.PersistField
  • Show OverflowNaturalDefined in persistent-2.14.6.3 · Database.Persist.Class.PersistField
  • PersistField OverflowNaturalDefined in persistent-2.14.6.3 · Database.Persist.Class.PersistField
  • PersistFieldSql OverflowNaturalDefined in persistent-2.14.6.3 · Database.Persist.Sql.Class

    This type uses the SqlInt64 version, which will exhibit overflow and underflow behavior. Additionally, it permits negative values in the database, which isn't ideal.

datadata SqlBackend
#

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.

Instances32PersistQueryRead, PersistQueryWrite, HasPersistBackend, IsPersistBackend, PersistCore, PersistStoreRead, …
newtypenewtype SqlReadBackend
#

An SQL backend which can only handle read queries

The constructor was exposed in 2.10.0.

Instances27PersistQueryRead, HasPersistBackend, IsPersistBackend, PersistCore, PersistStoreRead, PersistUniqueRead, …
newtypenewtype SqlWriteBackend
#

An SQL backend which can handle read or write queries

The constructor was exposed in 2.10.0

Instances30PersistQueryRead, PersistQueryWrite, HasPersistBackend, IsPersistBackend, PersistCore, PersistStoreRead, …
datadata ConnectionPoolConfig
#

Values to configure a pool of database connections. See Data.Pool for details.

Constructors

Instances1Show
newtypenewtype Single a
#

A single column (see rawSql). Any PersistField may be used here, including PersistValue (which does not do any processing).

Constructors

Instances5Eq, Ord, Read, Show, RawSql
  • Eq a => Eq (Single a)Defined in persistent-2.14.6.3 · Database.Persist.Sql.Types
  • Ord a => Ord (Single a)Defined in persistent-2.14.6.3 · Database.Persist.Sql.Types
  • Read a => Read (Single a)Defined in persistent-2.14.6.3 · Database.Persist.Sql.Types
  • Show a => Show (Single a)Defined in persistent-2.14.6.3 · Database.Persist.Sql.Types
  • PersistField a => RawSql (Single a)Defined in persistent-2.14.6.3 · Database.Persist.Sql.Class
datadata ColumnReference
#

This value specifies how a field references another table.

Constructors

Instances3Eq, Ord, Show