packages feed

persistent 2.8.1 → 2.8.2

raw patch · 6 files changed

+150/−14 lines, 6 filesdep ~base

Dependency ranges changed: base

Files

ChangeLog.md view
@@ -1,3 +1,7 @@+## 2.8.2++* Added support for `sql=` to the unique constraints quasi-quoter so that users can specify the database names of the constraints.+ ## 2.8.1  * DRY-ed up and exposed several util functions in `Database.Persist.Sql.Util`.
Database/Persist/Class/PersistField.hs view
@@ -26,7 +26,6 @@ import Data.Time.Clock.POSIX (posixSecondsToUTCTime) #endif import Data.ByteString.Char8 (ByteString, unpack, readInt)-import Control.Applicative as A import Data.Int (Int8, Int16, Int32, Int64) import Data.Word (Word, Word8, Word16, Word32, Word64) import Data.Text (Text)@@ -63,7 +62,52 @@ import Numeric.Natural (Natural) #endif --- | A value which can be marshalled to and from a 'PersistValue'.+-- | This class teaches Persistent how to take a custom type and marshal it to and from a 'PersistValue', allowing it to be stored in a database.+--+-- ==== __Examples__+--+-- ===== Simple Newtype+--+-- You can use @newtype@ to add more type safety/readability to a basis type like 'ByteString'. In these cases, just derive 'PersistField' and @PersistFieldSql@:+--+-- @+-- {-\# LANGUAGE GeneralizedNewtypeDeriving #-}+--+-- newtype HashedPassword = HashedPassword 'ByteString'+--   deriving (Eq, Show, 'PersistField', PersistFieldSql)+-- @+--+-- ===== Smart Constructor Newtype+--+-- In this example, we create a 'PersistField' instance for a newtype following the "Smart Constructor" pattern.+--+-- @+-- {-\# LANGUAGE GeneralizedNewtypeDeriving #-}+-- import qualified "Data.Text" as T+-- import qualified "Data.Char" as C+--+-- -- | An American Social Security Number+-- newtype SSN = SSN 'Text'+--  deriving (Eq, Show, PersistFieldSql)+--+-- mkSSN :: 'Text' -> 'Either' 'Text' SSN+-- mkSSN t = if (T.length t == 9) && (T.all C.isDigit t)+--  then 'Right' $ SSN t+--  else 'Left' $ "Invalid SSN: " <> t+--+-- instance 'PersistField' SSN where+--   'toPersistValue' (SSN t) = 'PersistText' t+--   'fromPersistValue' ('PersistText' t) = mkSSN t+--   -- Handle cases where the database does not give us PersistText+--   'fromPersistValue' x = 'Left' $ "File.hs: When trying to deserialize an SSN: expected PersistText, received: " <> T.pack (show x)+-- @+--+-- Tips:+--+-- * This file contain dozens of 'PersistField' instances you can look at for examples.+-- * Typically custom 'PersistField' instances will only accept a single 'PersistValue' constructor in 'fromPersistValue'. +-- * Internal 'PersistField' instances accept a wide variety of 'PersistValue's to accomodate e.g. storing booleans as integers, booleans or strings.+-- * If you're making a custom instance and using a SQL database, you'll also need @PersistFieldSql@ to specify the type of the database column. class PersistField a where     toPersistValue :: a -> PersistValue     fromPersistValue :: PersistValue -> Either T.Text a@@ -95,7 +139,7 @@ instance PersistField ByteString where     toPersistValue = PersistByteString     fromPersistValue (PersistByteString bs) = Right bs-    fromPersistValue x = TE.encodeUtf8 A.<$> fromPersistValue x+    fromPersistValue x = TE.encodeUtf8 <$> fromPersistValue x  instance PersistField T.Text where     toPersistValue = PersistText
Database/Persist/Class/PersistStore.hs view
@@ -136,6 +136,7 @@         => Key record -> ReaderT backend m (Maybe record)      -- | Get many records by their respective identifiers, if available.+    --     -- @since 2.8.1     getMany         :: (MonadIO m, PersistRecordBackend record backend)
Database/Persist/Quasi.hs view
@@ -23,7 +23,7 @@ import qualified Data.Text as T import Control.Arrow ((&&&)) import qualified Data.Map as M-import Data.List (foldl')+import Data.List (find, foldl') import Data.Monoid (mappend) import Control.Monad (msum, mplus) @@ -462,26 +462,56 @@                 else d         | otherwise = getDef ds t --- Unique UppercaseConstraintName list of lowercasefields+-- Unique UppercaseConstraintName list of lowercasefields terminated+-- by ! or sql= such that a unique constraint can look like:+-- `UniqueTestNull fieldA fieldB sql=ConstraintNameInDatabase !force`+-- Here using sql= sets the name of the constraint. takeUniq :: PersistSettings-          -> Text-          -> [FieldDef]-          -> [Text]-          -> UniqueDef+         -> Text+         -> [FieldDef]+         -> [Text]+         -> UniqueDef takeUniq ps tableName defs (n:rest)     | not (T.null n) && isUpper (T.head n)         = UniqueDef             (HaskellName n)-            (DBName $ psToDBName ps (tableName `T.append` n))+            dbName             (map (HaskellName &&& getDBName defs) fields)             attrs   where-    (fields,attrs) = break ("!" `T.isPrefixOf`) rest-    getDBName [] t = error $ "Unknown column in unique constraint: " ++ show t+    isAttr a =+      "!" `T.isPrefixOf` a+    isSqlName a =+      "sql=" `T.isPrefixOf` a+    isNonField a =+       isAttr a+      || isSqlName a+    (fields, nonFields) =+      break isNonField rest+    attrs = filter isAttr nonFields+    usualDbName =+      DBName $ psToDBName ps (tableName `T.append` n)+    sqlName :: Maybe DBName+    sqlName =+      case find isSqlName nonFields of+        Nothing ->+          Nothing+        (Just t) ->+          case drop 1 $ T.splitOn "=" t of+            (x : _) -> Just (DBName x)+            _ -> Nothing+    dbName = fromMaybe usualDbName sqlName+    getDBName [] t =+      error $ "Unknown column in unique constraint: " ++ show t+              ++ " " ++ show defs ++ show n ++ " " ++ show attrs     getDBName (d:ds) t         | fieldHaskell d == HaskellName t = fieldDB d         | otherwise = getDBName ds t-takeUniq _ tableName _ xs = error $ "invalid unique constraint on table[" ++ show tableName ++ "] expecting an uppercase constraint name xs=" ++ show xs+takeUniq _ tableName _ xs =+  error $ "invalid unique constraint on table["+          ++ show tableName+          ++ "] expecting an uppercase constraint name xs="+          ++ show xs  data UnboundForeignDef = UnboundForeignDef                          { _unboundFields :: [Text] -- ^ fields in other entity
Database/Persist/Sql/Class.hs view
@@ -211,6 +211,63 @@ extractMaybe :: Maybe a -> a extractMaybe = fromMaybe (error "Database.Persist.GenericSql.extractMaybe") +-- | 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' = 'PersistDbSpecific' . toASCIIBytes+--   'fromPersistValue' ('PersistDbSpecific' 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 PersistDbSpecific, 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' <https://www.postgresql.org/docs/current/static/sql-createdomain.html DOMAIN> and <https://www.postgresql.org/docs/current/static/datatype-enum.html 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"+-- @ class PersistField a => PersistFieldSql a where     sqlType :: Proxy a -> SqlType 
persistent.cabal view
@@ -1,5 +1,5 @@ name:            persistent-version:         2.8.1+version:         2.8.2 license:         MIT license-file:    LICENSE author:          Michael Snoyman <michael@snoyman.com>