{-# 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