{-# LANGUAGE CPP #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE UndecidableInstances #-}

-- |
-- 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 "Database.Persist.TH" module 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.
--
-- __Note__: Persistent determines whether or not to migrate a column's default
-- value by comparing the exact string found in your @models@ file with the one
-- returned by the database. If a database canonicalizes the SQL @FALSE@ from your
-- @models@ file to @false@ in the database, Persistent will think the default
-- value needs to be migrated and
-- <https://github.com/yesodweb/persistent/issues/241 attempt a migration each time you start your app>.
--
-- To workaround this, find the exact SQL your DBMS uses for the default value. For example, using postgres:
--
-- @
-- psql database_name # Open postgres
--
-- \\d+ table_name -- describe the table schema
-- @
--
-- @
-- ...
-- created       | timestamp without time zone | not null default now()
-- @
--
-- Then use the listed default value SQL inside your @models@ file.
--
-- = 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)
-- @
--
-- Since the primary key for this table is part of the record, it's called a "natural key" in the SQL lingo.
-- As a key with multiple fields, it is also a "composite key."
--
-- You can specify a @Primary@ key with a single field, too.
--
-- = 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 TABLE big_user_table (
--     id      SERIAL PRIMARY KEY,
--     name    VARCHAR,
--     age     INT
-- );
-- @
--
-- = Customizing Types/Tables
--
-- == JSON instances
--
-- You can automatically get ToJSON and FromJSON instances for any entity by adding @json@ to the entity line:
--
-- @
-- Person json
--     name Text
-- @
-- Requires @\{\-\# LANGUAGE FlexibleInstances \#\-\}@
--
-- Customizable by using mpsEntityJSON
-- * http://hackage.haskell.org/package/persistent-template/docs/Database-Persist-TH.html#v:EntityJSON
-- * http://hackage.haskell.org/package/persistent/docs/Database-Persist-Class.html#v:keyValueEntityToJSON
--
-- == Changing table/collection name
--
-- @
-- Person sql=peoples
--     name Text
-- @
--
-- == Change table/collection key definition (field name and\/or type, persistent >= 2.1)
--
-- @Id@ defines the column to use to define the key of the entity.
-- Without type, the default backend key type will be used. You can change its
-- database name using the @sql@ attributes :
--
-- @
-- Person
--    Id         sql=my_id_name
--    phone Text
-- @
--
-- With a Haskell type, the corresponding type is used. Note that you'll need to
-- use @default=@ to tell it what to do on insertion.
--
-- @
-- Person
--    Id    Day default=CURRENT_DATE
--    phone Text
-- @
--
-- @default=@ works for SQL databases, and is backend specific.
-- For MongoDB currently one always needs to create the key on the application
-- side and use @insertKey@. @insert@ will not work correctly. Sql backends can
-- also do this if default does not work.
--
-- @sqltype@ can also be used to specify a different database type
--
-- @
-- Currency
--     Id String sqltype=varchar(3) sql=code
-- @
--
-- Composite key (using multiple columns) can also be defined using @Primary@.
--
-- @sql=@ also works for setting the names of unique indexes.
--
-- @
-- Person
--   name Text
--   phone Text
--   UniquePersonPhone phone sql=UniqPerPhone
-- @
--
-- This makes a unique index requiring @phone@ to be unique across @Person@ rows.
-- Ordinarily Persistent will generate a snake-case index name from the
-- capitalized name provided such that @UniquePersonPhone@ becomes
-- @unique_person_phone@. However, we provided a @sql=@ so the index name in the
-- database will instead be @UniqPerPhone@. Keep in mind @sql=@ and @!@ attrs must
-- come after the list of fields in front of the index name in the quasi-quoter.
--
--
--
-- = Customizing Fields
--
-- == Nullable Fields
--
-- As illustrated in the example at the beginning of this page, we are able to represent nullable
-- fields by including 'Maybe' at the end of the type declaration:
--
-- > TableName
-- >     fieldName      FieldType
-- >     otherField     String
-- >     nullableField  Int       Maybe
--
-- Alternatively we can specify the keyword nullable:
--
-- > TableName
-- >     fieldName      FieldType
-- >     otherField     String
-- >     nullableField  Int       nullable
--
-- However the difference here is in the first instance the Haskell type will be 'Maybe Int',
-- but in the second it will be 'Int'. Be aware that this will cause runtime errors if the
-- database returns @NULL@ and the @PersistField@ instance does not handle @PersistNull@.
--
-- If you wish to define your Maybe types in a way that is similar to the actual Haskell
-- definition, you can define 'Maybe Int' like so:
--
-- > TableName
-- >     fieldName      FieldType
-- >     otherField     String
-- >     nullableField  (Maybe Int)
--
-- However, note, the field _must_ be enclosed in parenthesis.
--
-- == @sqltype=@
--
-- By default, Persistent maps the Haskell types you specify in the Models DSL to
-- an appropriate SQL type in the database (refer to the section "Conversion table
-- (migrations)" for the default mappings). Using the
-- @sqltype=@ option, you can  customize the SQL type Persistent uses for your
-- column. Use cases include:
--
-- * Interacting with an existing database whose column types don't match Persistent's defaults.
-- * Taking advantage of a specific SQL type's features
--     * e.g. Using an equivalent type that has better space or performance characteristics
--
-- To use this setting, add the @sqltype=@ option after declaring your field name and type:
--
-- @
-- User
--     username Text sqltype=varchar(255)
-- @
--
-- == Laziness
--
-- By default the records created by persistent have strict fields. You can prefix
-- a field name with @~@ to make it lazy (or @!@ to make it strict).
--
-- == 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"]]
-- @
--
-- == @!no-migrate@
--
-- To prevent @migrateModels@ from generating _any_ migrations for an entity, add
-- the @!no-migrate@ attribute to it's definition:
--
-- @
-- User !no-migrate
--     field   String
--     good    Dog
-- @
--
-- == @MigrationOnly@
--
-- Introduced with @persistent-template@ 1.2.0. The purpose of this attribute is
-- to mark a field which will be entirely ignored by the normal processing, but
-- retained in the database definition for purposes of migration. This means, in
-- SQL, a column will not be flagged for removal by the migration scripts, even
-- though it is not used in your code. This is useful for phasing out usage of a
-- column before entirely removing it, or having columns which are needed by other
-- tools but not by Persistent.
--
-- @
-- Person
--     name Text
--     age Int
--     unusedField ByteString Maybe MigrationOnly
-- @
--
-- Note that you almost certainly want to either mark the field as @Maybe@ or
-- provide a default value, otherwise insertions will fail.
--
--
-- == @SafeToRemove@
--
-- This is intended to be used as part of a deprecation of a field, after
-- @MigrationOnly@ has been used usually. This works somewhat as a superset of the
-- functionality of @MigrationOnly@. In addition, the field will be removed from
-- the database if it is present. Note that this is a destructive change which you
-- are marking as safe.
--
-- == Constraints
--
-- Migration will remove any manual constraints from your tables. Exception: constraints whose names begin with the string @__manual_@ (which starts with two underscores) will be preserved.
--
--
-- = Foreign Keys
--
-- If you define an entity and want to refer to it in another table, you can use the entity's Id type in a column directly.
--
-- @
-- Person
--     name    Text
--
-- Dog
--     name    Text
--     owner   PersonId
-- @
--
-- This automatically creates a foreign key reference from @Dog@ to @Person@.
-- The foreign key constraint means that, if you have a @PersonId@ on the @Dog@, the database guarantees that the corresponding @Person@ exists in the database.
-- If you try to delete a @Person@ out of the database that has a @Dog@, you'll receive an exception that a foreign key violation has occurred.
--
-- == @constraint=@
--
-- You can use the @constraint=@ attribute to override the constraint name used in
-- migrations. This is useful particularly when the automatically generated
-- constraint names exceed database limits (e.g. MySQL does not allow constraint
-- names longer than 64 characters).
--
-- @
-- VeryLongTableName
--   name Text
--
-- AnotherVeryLongTableName
--   veryLongTableNameId VeryLongTableNameId constraint=short_foreign_key
-- @
--
-- == OnUpdate and OnDelete
--
-- These options affects how a referring record behaves when the target record is changed.
-- There are several options:
--
-- * 'Restrict' - This is the default. It prevents the action from occurring.
-- * 'Cascade' - this copies the change to the child record. If a parent record is deleted, then the child record will be deleted too.
-- * 'SetNull' - If the parent record is modified, then this sets the reference to @NULL@. This only works on @Maybe@ foreign keys.
-- * 'SetDefault' - This will set the column's value to the @default@ for the column, if specified.
--
-- To specify the behavior for a reference, write @OnUpdate@ or @OnDelete@ followed by the action.
--
-- @
-- Record
--     -- If the referred Foo is deleted or updated, then this record will
--     -- also be deleted or updated.
--     fooId   FooId   OnDeleteCascade OnUpdateCascade
--
--     -- If the referred Bar is deleted, then we'll set the reference to
--     -- 'Nothing'. If the referred Bar is updated, then we'll cascade the
--     -- update.
--     barId   BarId Maybe     OnDeleteSetNull OnUpdateCascade
--
--     -- If the referred Baz is deleted, then we set to the default ID.
--     bazId   BazId   OnDeleteSetDefault  default=1
-- @
--
-- Let's demonstrate this with a shopping cart example.
--
-- @
-- User
--     name    Text
--
-- Cart
--     user    UserId Maybe
--
-- CartItem
--     cartId  CartId
--     itemId  ItemId
--
-- Item
--     name    Text
--     price   Int
-- @
--
-- Let's consider how we want to handle deletions and updates.
-- If a @User@ is deleted or update, then we want to cascade the action to the associated @Cart@.
--
-- @
-- Cart
--     user    UserId Maybe OnDeleteCascade OnUpdateCascade
-- @
--
-- If an @Item@ is deleted, then we want to set the @CartItem@ to refer to a special "deleted item" in the database.
-- If a @Cart@ is deleted, though, then we just want to delete the @CartItem@.
--
-- @
-- CartItem
--     cartId CartId   OnDeleteCascade
--     itemId ItemId   OnDeleteSetDefault default=1
-- @
--
-- == @Foreign@ keyword
--
-- The above example is a "simple" foreign key. It refers directly to the Id column, and it only works with a non-composite primary key. We can define more complicated foreign keys using the @Foreign@ keyword.
--
-- A pseudo formal syntax for @Foreign@ is:
--
-- @
-- Foreign $(TargetEntity) [$(cascade-actions)] $(constraint-name) $(columns) [ $(references) ]
--
-- columns := column0 [column1 column2 .. columnX]
-- references := References $(target-columns)
-- target-columns := target-column0 [target-column1 target-columns2 .. target-columnX]
-- @
--
-- Columns are the columns as defined on this entity.
-- @target-columns@ are the columns as defined on the target entity.
--
-- Let's look at some examples.
--
-- === Composite Primary Key References
--
-- The most common use for this is to refer to a composite primary key.
-- Since composite primary keys take up more than one column, we can't refer to them with a single @persistent@ column.
--
-- @
-- Email
--     firstPart   Text
--     secondPart  Text
--     Primary firstPart secondPart
--
-- User
--     name            Text
--     emailFirstPart  Text
--     emailSecondPart Text
--
--     Foreign Email fk_user_email emailFirstPart emailSecondPart
-- @
--
-- If you omit the @References@ keyword, then it assumes that the foreign key reference is for the target table's primary key.
-- If we wanted to be fully redundant, we could specify the @References@ keyword.
--
-- @
--     Foreign Email fk_user_email emailFirstPart emailSecondPart References firstPart secondPart
-- @
--
-- We can specify delete/cascade behavior directly after the target table.
--
-- @
--     Foreign Email OnDeleteCascade OnUpdateCascade fk_user_email emailFirstPart emailSecondPart
-- @
--
-- Now, if the email is deleted or updated, the user will be deleted or updated to match.
--
-- === Non-Primary Key References
--
-- SQL database backends allow you to create a foreign key to any column(s) with a Unique constraint.
-- Persistent does not check this, because you might be defining your uniqueness constraints outside of Persistent.
-- To do this, we must use the @References@ keyword.
--
-- @
-- User
--     name    Text
--     email   Text
--
--     UniqueEmail email
--
-- Notification
--     content Text
--     sentTo  Text
--
--     Foreign User fk_noti_user sentTo References email
-- @
--
-- If the target uniqueness constraint has multiple columns, then you must specify them independently.
--
-- @
-- User
--     name            Text
--     emailFirst      Text
--     emailSecond     Text
--
--     UniqueEmail emailFirst emailSecond
--
-- Notification
--     content         Text
--     sentToFirst     Text
--     sentToSecond    Text
--
--     Foreign User fk_noti_user sentToFirst sentToSecond References emailFirst emailSecond
-- @
--
-- = 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 important\nto 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 about\nthis for some reason."
-- @
--
-- Since @persistent-2.14.6.0@, documentation comments are included in documentation generated using Haddock if `mpsEntityHaddocks` is enabled (defaults to False).
-- @persistent@ backends can also use this to generate SQL @COMMENT@s, which are useful for a database perspective, and you can use the <https://hackage.haskell.org/package/persistent-documentation @persistent-documentation@> library to render a Markdown document of the entity definitions.
--
-- = Sum types
--
-- == Field level
--
-- You'll frequently want to store an enum of values in your database. For
-- example, you might describe a @Person@'s employment status as being @Employed@,
-- @Unemployed@, or @Retired@. In Haskell this is represented with a sum type, and
-- Persistent provides a Template Haskell function to marshall these values to and
-- from the database:
--
-- @
-- -- @Employment.hs
-- {-# LANGUAGE TemplateHaskell #-}
-- module Employment where
--
-- import Database.Persist.TH
-- import Prelude
--
-- data Employment = Employed | Unemployed | Retired
--     deriving (Show, Read, Eq)
-- derivePersistField "Employment"
-- @
--
-- @derivePersistField@ stores sum type values as strins in the database. While not as efficient as using integers, this approach simplifies adding and removing values from your enumeration.
--
-- Due to the GHC Stage Restriction, the call to the Template Haskell function @derivePersistField@ must be in a separate module than where the generated code is used.
--
-- Note: If you created a new module, make sure add it to the @exposed-modules@ section of your Cabal file.
--
-- Use the module by importing it into your @Model.hs@ file:
--
-- @
-- -- @Model.hs
-- import Employment
-- @
--
-- and use it in the @models@ DSL:
--
-- @
-- Person
--     employment Employment
-- @
--
-- You can export the Employment module from Import to use it across your app:
--
-- @
-- -- @Import.hs
-- import Employment as Import
-- @
--
-- === Entity-level
--
-- NOTE: This feature is deprecated as of version 2.14 and will be removed in 2.15 (unless there are many complaints).
--
-- The
-- <https://github.com/yesodweb/persistent/blob/master/persistent-test/src/SumTypeTest.hs#L35 tests for this feature>
-- demonstrate their usage. Note the use of the sign @+@ in front of the entity
-- name.
--
-- The schema in the test is reproduced here:
--
-- @
-- share [mkPersist persistSettings, mkMigrate "sumTypeMigrate"] [persistLowerCase|
-- Bicycle
--     brand T.Text
-- Car
--     make T.Text
--     model T.Text
-- +Vehicle
--     bicycle BicycleId
--     car CarId
-- |]
-- @
--
-- Let's check out the definition of the Haskell type @Vehicle@.
-- Using @ghci@, we can query for @:info Vehicle@:
--
-- >>> :i Vehicle
-- type Vehicle = VehicleGeneric SqlBackend
--         -- Defined at .../Projects/persistent/persistent-test/src/SumTypeTest.hs:26:1
--
-- >>> :i VehicleGeneric
-- type role VehicleGeneric nominal
-- data VehicleGeneric backend
--   = VehicleBicycleSum (Key (BicycleGeneric backend))
--   | VehicleCarSum (Key (CarGeneric backend))
--         -- Defined at .../persistent/persistent-test/src/SumTypeTest.hs:26:1
-- -- lots of instances follow...
--
-- A @VehicleGeneric@ has two constructors:
--
-- - @VehicleBicycleSum@ with a @Key (BicycleGeneric backend)@ field
-- - @VehicleCarSum@ with a @Key (CarGeneric backend)@ field
--
-- The @Bicycle@ and @Car@ are typical @persistent@ entities.
--
-- This generates the following SQL migrations (formatted for readability):
--
-- @
-- CREATE TABLE "bicycle" (
--     "id"        INTEGER PRIMARY KEY,
--     "brand"     VARCHAR NOT NULL
-- );
--
-- CREATE TABLE "car"(
--     "id"        INTEGER PRIMARY KEY,
--     "make"      VARCHAR NOT NULL,
--     "model"     VARCHAR NOT NULL
-- );
--
-- CREATE TABLE "vehicle"(
--     "id"        INTEGER PRIMARY KEY,
--     "bicycle"   INTEGER NULL REFERENCES "bicycle",
--     "car"       INTEGER NULL REFERENCES "car"
-- );
-- @
--
-- The @vehicle@ table contains a nullable foreign key reference to both the bicycle and the car tables.
--
-- A SQL query that grabs all the vehicles from the database looks like this (note the @??@ is for the @persistent@ raw SQL query functions):
--
-- @
-- SELECT ??, ??, ??
-- FROM vehicle
-- LEFT JOIN car
--     ON vehicle.car = car.id
-- LEFT JOIN bicycle
--     ON vehicle.bicycle = bicycle.id
-- @
--
-- If we use the above query with @rawSql@, we'd get the following result:
--
-- @
-- getVehicles
--     :: SqlPersistM
--         [ ( Entity Vehicle
--           , Maybe (Entity Bicycle)
--           , Maybe (Entity Car)
--           )
--         ]
-- @
--
-- This result has some post-conditions that are not guaranteed by the types *or* the schema.
-- The constructor for @Entity Vehicle@ is going to determine which of the other members of the tuple is @Nothing@.
-- We can convert this to a friendlier domain model like this:
--
-- @
-- data Vehicle'
--     = Car' Text Text
--     | Bike Text
--
-- check = do
--     result <- getVehicles
--     pure (map convert result)
--
-- convert
--     :: (Entity Vehicle, Maybe (Entity Bicycle), Maybe (Entity Car))
--     -> Vehicle'
-- convert (Entity _ (VehicleBicycleSum _), Just (Entity _ (Bicycle brand)), _) =
--     Bike brand
-- convert (Entity _ (VehicleCarSum _), _, Just (Entity _ (Car make model))) =
--     Car make model
-- convert _ =
--     error "The database preconditions have been violated!"
-- @
--
-- == Times with timezones
--
-- Storing times with timezones in one type in databases is not possible, although
-- it seems that it should be possible (@timezone@ and @timezonetz@ in
-- PostgreSQL). That's why starting with persistent 2.0, all times will be mapped
-- to @UTCTime@. If you need to store timezone information along with times in a
-- database, store the timezone in a second field. Here are some links about the
-- topic with further information:
--
-- * https://github.com/yesodweb/persistent/issues/290
-- * https://groups.google.com/forum/#!msg/yesodweb/MIfcV2bwM80/8QLFpgp1LykJ
-- * http://stackoverflow.com/questions/14615271/postgres-timestamp/14616640#14616640
-- * http://justatheory.com/computers/databases/postgresql/use-timestamptz.html
-- * https://github.com/lpsmith/postgresql-simple/issues/69
-- * https://github.com/nikita-volkov/hasql-postgres/issues/1
--
-- = Conversion table (migrations)
--
-- Here are the conversions between Haskell types and database types:
--
-- +------------+----------------------+-------------------+---------------+----------------+
-- | Haskell    | PostgreSQL           | MySQL             | MongoDB       |  SQLite        |
-- +============+======================+===================+===============+================+
-- | Text       |  VARCHAR             |  TEXT             | String        |  VARCHAR       |
-- +------------+----------------------+-------------------+---------------+----------------+
-- | ByteString |  BYTEA               |  BLOB             | BinData       |  BLOB          |
-- +------------+----------------------+-------------------+---------------+----------------+
-- | Int        |  INT8                |  BIGINT(20)       | NumberLong    |  INTEGER       |
-- +------------+----------------------+-------------------+---------------+----------------+
-- | Double     |  DOUBLE PRECISION    |  DOUBLE           | Double        |  REAL          |
-- +------------+----------------------+-------------------+---------------+----------------+
-- | Rational   |  NUMERIC(22, 12)     |  DECIMAL(32,20)   | *Unsupported* |  NUMERIC(32,20)|
-- +------------+----------------------+-------------------+---------------+----------------+
-- | Bool       |  BOOLEAN             |  TINYINT(1)       | Boolean       |  BOOLEAN       |
-- +------------+----------------------+-------------------+---------------+----------------+
-- | Day        |  DATE                |  DATE             | NumberLong    |  DATE          |
-- +------------+----------------------+-------------------+---------------+----------------+
-- | TimeOfDay  |  TIME                |  TIME\*\*         | *Unsupported* |  TIME          |
-- +------------+----------------------+-------------------+---------------+----------------+
-- | UTCTime\*  |  TIMESTAMP           |  DATETIME\*\*     | Date          |  TIMESTAMP     |
-- +------------+----------------------+-------------------+---------------+----------------+
--
-- Notes:
--
-- \* Support for @ZonedTime@ was dropped in persistent 2.0. @UTCTime@ can be used
-- with @timestamp without timezone@ and @timestamp with timezone@ in PostgreSQL.
-- See also the section "Times with timezones".
--
-- \*\* The default resolution for @TIME@ and @DATETIME@ in MySQL is one second.
-- As of MySQL version 5.6.4, and persistent-mysql-2.6.2, fractional seconds are
-- handled correctly if you declare an explicit precision by using @sqltype@. For
-- example, appending @sqltype=TIME(6)@ to a @TimeOfDay@ field definition will
-- give microsecond resolution.
--
-- = Compatibility tables
--
-- MySQL:
--
-- +-------------------+-----------------------------------------------------------------------+
-- |Haskell type       | Compatible MySQL types                                                |
-- +===================+=======================================================================+
-- | Bool              | Tiny                                                                  |
-- +-------------------+-----------------------------------------------------------------------+
-- | Int8              | Tiny                                                                  |
-- +-------------------+-----------------------------------------------------------------------+
-- | Int16             | Tiny,Short                                                            |
-- +-------------------+-----------------------------------------------------------------------+
-- | Int32             | Tiny,Short,Int24,Long                                                 |
-- +-------------------+-----------------------------------------------------------------------+
-- | Int               | Tiny,Short,Int24,Long,LongLong\*                                      |
-- +-------------------+-----------------------------------------------------------------------+
-- | Int64             | Tiny,Short,Int24,Long,LongLong                                        |
-- +-------------------+-----------------------------------------------------------------------+
-- | Integer           | Tiny,Short,Int24,Long,LongLong                                        |
-- +-------------------+-----------------------------------------------------------------------+
-- | Word8             | Tiny                                                                  |
-- +-------------------+-----------------------------------------------------------------------+
-- | Word16            | Tiny,Short                                                            |
-- +-------------------+-----------------------------------------------------------------------+
-- | Word32            | Tiny,Short,Int24,Long                                                 |
-- +-------------------+-----------------------------------------------------------------------+
-- | Word64            | Tiny,Short,Int24,Long,LongLong                                        |
-- | Double            | Float,Double,Decimal,NewDecimal,Tiny,Short,Int24,Long                 |
-- +-------------------+-----------------------------------------------------------------------+
-- | Ratio Integer     | Float,Double,Decimal,NewDecimal,Tiny,Short,Int24,Long,LongLong        |
-- +-------------------+-----------------------------------------------------------------------+
-- | ByteString        | VarChar,TinyBlob,MediumBlob,LongBlob,Blob,VarString,String,Set,Enum   |
-- +-------------------+-----------------------------------------------------------------------+
-- | Lazy.ByteString   | VarChar,TinyBlob,MediumBlob,LongBlob,Blob,VarString,String,Set,Enum   |
-- +-------------------+-----------------------------------------------------------------------+
-- | Encoding.Text\*\* | VarChar,TinyBlob,MediumBlob,LongBlob,Blob,VarString,String,Set,Enum   |
-- +-------------------+-----------------------------------------------------------------------+
-- | Lazy.Text         | VarChar,TinyBlob,MediumBlob,LongBlob,Blob,VarString,String,Set,Enum   |
-- +-------------------+-----------------------------------------------------------------------+
-- | [Char]/String     | VarChar,TinyBlob,MediumBlob,LongBlob,Blob,VarString,String,Set,Enum   |
-- +-------------------+-----------------------------------------------------------------------+
-- | UTCTime           | DateTime,Timestamp                                                    |
-- +-------------------+-----------------------------------------------------------------------+
-- | Day               | Year,Date,NewDate                                                     |
-- +-------------------+-----------------------------------------------------------------------+
-- | TimeOfDay         | Time                                                                  |
-- +-------------------+-----------------------------------------------------------------------+
--
-- \* When @Word@ size is 64bit
--
-- \*\* Utf8 only
--
-- Unsupported types:
--
-- +--------------------------------------------------------------------+
-- | Not currently supported                                            |
-- +====================================================================+
-- | Word                                                               |
-- +--------------------------------------------------------------------+
-- | Float                                                              |
-- +--------------------------------------------------------------------+
-- | Scientific <https://github.com/yesodweb/persistent/issues/225 #225>|
-- +--------------------------------------------------------------------+
--
-- See <http://hackage.haskell.org/package/mysql-simple/docs/Database-MySQL-Simple-Result.html MySQL.Simple.Result>.
module Database.Persist.Quasi
    ( parse

      -- * 'PersistSettings'
    , PersistSettings
    , upperCaseSettings
    , lowerCaseSettings

      -- ** Getters and Setters
    , getPsToDBName
    , setPsToDBName
    , setPsToFKName
    , setPsUseSnakeCaseForeignKeys
    , setPsUseSnakeCaseForiegnKeys
    , getPsStrictFields
    , setPsStrictFields
    , getPsIdName
    , setPsIdName
    , getPsTabErrorLevel
    , setPsTabErrorLevel
    , getPsQuotedArgumentErrorLevel
    , setPsQuotedArgumentErrorLevel
    ) where

import Database.Persist.Quasi.Internal
import Database.Persist.Quasi.PersistSettings
