persistent-2.10.5.2: Type-safe, multi-backend data serialization.

Safe HaskellNone
LanguageHaskell2010

Database.Persist.Quasi

Description

This module defines the Persistent entity syntax used in the quasiquoter to generate persistent entities.

The basic structure of the syntax looks like this:

TableName
    fieldName      FieldType
    otherField     String
    nullableField  Int       Maybe

You start an entity definition with the table name, in this case, TableName. It's followed by a list of fields on the entity, which have the basic form fieldName FieldType. You can indicate that a field is nullable with Maybe at the end of the type.

persistent automatically generates an ID column for you, if you don't specify one, so the above table definition corresponds to the following SQL:

CREATE TABLE table_name (
    id                SERIAL PRIMARY KEY,
    field_name        field_type NOT NULL,
    other_field       varchar    NOT NULL,
    nullable_field    int NULL
);

Note that the exact SQL that is generated can be customized using the PersistSettings that are passed to the parse function.

It generates a Haskell datatype with the following form:

data TableName = TableName
    { tableNameFieldName :: FieldType
    , tableNameOtherField :: String
    , tableNameNullableField :: Maybe Int
    }

As with the SQL generated, the specifics of this are customizable. See the persistent-template package for details.

Deriving

You can add a deriving clause to a table, and the generated Haskell type will have a deriving clause with that. Unlike normal Haskell syntax, you don't need parentheses or commas to separate the classes, and you can even have multiple deriving clauses.

User
    name String
    age  Int
    deriving Eq Show
    deriving Ord

Unique Keys

You can define a uniqueness key on a table with the following format:

User
   name String
   age  Int

   UniqueUserName name

This will put a unique index on the user table and the name field.

Setting defaults

You can use a default=${sql expression} clause to set a default for a field. The thing following the `=` is interpreted as SQL that will be put directly into the table definition.

User
    name    Text
    admin   Bool default=false

This creates a SQL definition like this:

CREATE TABLE user (
  id      SERIAL PRIMARY KEY,
  name    VARCHAR NOT NULL,
  admin   BOOL DEFAULT=false
);

A restriction here is that you still need to provide a value when performing an insert, because the generated Haskell type has the form:

data User = User
    { userName :: Text
    , userAdmin :: Bool
    }

You can work around this by using a 'Maybe Bool' and supplying Nothing by default.

Custom ID column

If you don't want to use the default ID column type of Int64, you can set a custom type with an Id field. This User has a Text ID.

User
    Id   Text
    name Text
    age  Int

If you do this, it's a good idea to set a default for the ID. Otherwise, you will need to use insertKey instead of insert when performing inserts.

insertKey (UserKey "Hello world!") (User Bob 32)

If you attempt to do insert (User Bob 32), then you will receive a runtime error because the SQL database doesn't know how to make an ID for you anymore. So instead just use a default expression, like this:

User
    Id      Text default=generate_user_id()
    name    Text
    age     Int

Custom Primary Keys

Sometimes you don't want to have an ID column, and you want a different sort of primary key. This is a table that stores unique email addresses, and the email is the primary key. We store the first and second part (eg first@second) separately.

Email
    firstPart   Text
    secondPart  Text

    Primary firstPart secondPart

This creates a table with the following form:

CREATE TABLE email (
    first_part  varchar,
    second_part varchar,

    PRIMARY KEY (first_part, second_part)

You can specify 1 or more columns in the primary key.

Overriding SQL

You can use a sql=custom annotation to provide some customization on the entity and field. For example, you might prefer to name a table differently than what persistent will do by default. You may also prefer to name a field differently.

User sql=big_user_table
    fullName    String sql=name
    age         Int

This will alter the generated SQL to be:

CREATE TABEL big_user_table (
    id      SERIAL PRIMARY KEY,
    name    VARCHAR,
    age     INT
);

Attributes

The QuasiQuoter allows you to provide arbitrary attributes to an entity or field. This can be used to extend the code in ways that the library hasn't anticipated. If you use this feature, we'd definitely appreciate hearing about it and potentially supporting your use case directly!

User !funny
    field   String  !sad
    good    Dog     !sogood

We can see the attributes using the entityAttrs field and the fieldAttrs field.

userAttrs = do
    let userDefinition = entityDef (Proxy :: Proxy User)
    let userAttributes = entityAttrs userDefinition
    let fieldAttributes = map fieldAttrs (entityFields userDefinition)
    print userAttributes
-- ["funny"]
    print fieldAttributes
-- [["sad"],["sogood"]]

Documentation Comments

The quasiquoter supports ordinary comments with -- and #. Since persistent-2.10.5.1, it also supports documentation comments. The grammar for documentation comments is similar to Haskell's Haddock syntax, with a few restrictions:

  1. Only the -- | form is allowed.
  2. You must put a space before and after the | pipe character.
  3. The comment must be indented at the same level as the entity or field it documents.

An example of the field documentation is:

-- | I am a doc comment for a User. Users are important
-- | to the application, and should be treasured.
User
    -- | Users have names. Call them by names.
    name String
    -- | A user can be old, or young, and we care about
    -- | this for some reason.
    age Int

The documentation is present on the entityComments field on the EntityDef for the entity:

>>> let userDefinition = entityDef (Proxy :: Proxy User)
>>> entityComments userDefinition
"I am a doc comment for a User. Users are importantnto the application, and should be treasured.n"

Likewise, the field documentation is present in the fieldComments field on the FieldDef present in the EntityDef:

>>> let userFields = entityFields userDefinition
>>> let comments = map fieldComments userFields
>>> mapM_ putStrLn comments
"Users have names. Call them by names."
"A user can be old, or young, and we care aboutnthis for some reason."

Unfortunately, we can't use this to create Haddocks for you, because Template Haskell does not support Haddock yet. persistent backends *can* use this to generate SQL COMMENTs, which are useful for a database perspective, and you can use the @persistent-documentation@ library to render a Markdown document of the entity definitions.

Synopsis

Documentation

parse :: PersistSettings -> Text -> [EntityDef] Source #

Parses a quasi-quoted syntax into a list of entity definitions.

data PersistSettings Source #

Constructors

PersistSettings 

Fields