opaleye 0.7.4.0 → 0.7.5.0
raw patch · 12 files changed
+229/−21 lines, 12 filesdep +time-compatdep ~timePVP ok
version bump matches the API change (PVP)
Dependencies added: time-compat
Dependency ranges changed: time
API changes (from Hackage documentation)
+ Opaleye.Experimental.Enum: enumMapperWithSchema :: String -> String -> (String -> Maybe haskellSum) -> (haskellSum -> String) -> EnumMapper sqlEnum haskellSum
+ Opaleye.Internal.Constant: instance Data.Profunctor.Product.Default.Class.Default Opaleye.Internal.Constant.ToFields Data.Time.LocalTime.Internal.CalendarDiffTime.CalendarDiffTime (Opaleye.Internal.Column.Column Opaleye.Internal.PGTypesExternal.SqlInterval)
+ Opaleye.Internal.Inferrable: instance (Opaleye.Internal.Column.Column Opaleye.Internal.PGTypesExternal.SqlInterval GHC.Types.~ cSqlInterval) => Data.Profunctor.Product.Default.Class.Default (Opaleye.Internal.Inferrable.Inferrable Opaleye.Internal.Constant.ToFields) Data.Time.LocalTime.Internal.CalendarDiffTime.CalendarDiffTime cSqlInterval
+ Opaleye.Internal.Inferrable: instance (calendardifftime GHC.Types.~ Data.Time.LocalTime.Internal.CalendarDiffTime.CalendarDiffTime) => Data.Profunctor.Product.Default.Class.Default (Opaleye.Internal.Inferrable.Inferrable Opaleye.Internal.RunQuery.FromField) Opaleye.Internal.PGTypesExternal.SqlInterval calendardifftime
+ Opaleye.Internal.PGTypesExternal: data SqlInterval
+ Opaleye.Internal.PGTypesExternal: instance Opaleye.Internal.PGTypes.IsSqlType Opaleye.Internal.PGTypesExternal.SqlInterval
+ Opaleye.Internal.PGTypesExternal: sqlInterval :: CalendarDiffTime -> Column PGInterval
+ Opaleye.Internal.PGTypesExternal: type PGInterval = SqlInterval
+ Opaleye.Internal.RunQuery: instance Opaleye.Internal.RunQuery.DefaultFromField Opaleye.Internal.PGTypesExternal.SqlInterval Data.Time.LocalTime.Internal.CalendarDiffTime.CalendarDiffTime
+ Opaleye.Operators: addInterval :: IntervalNum from to => Field from -> Field SqlInterval -> Field to
+ Opaleye.Operators: class IntervalNum from to | from -> to
+ Opaleye.Operators: instance Opaleye.Operators.IntervalNum Opaleye.Internal.PGTypesExternal.SqlDate Opaleye.Internal.PGTypesExternal.SqlTimestamp
+ Opaleye.Operators: instance Opaleye.Operators.IntervalNum Opaleye.Internal.PGTypesExternal.SqlInterval Opaleye.Internal.PGTypesExternal.SqlInterval
+ Opaleye.Operators: instance Opaleye.Operators.IntervalNum Opaleye.Internal.PGTypesExternal.SqlTime Opaleye.Internal.PGTypesExternal.SqlTime
+ Opaleye.Operators: instance Opaleye.Operators.IntervalNum Opaleye.Internal.PGTypesExternal.SqlTimestamp Opaleye.Internal.PGTypesExternal.SqlTimestamp
+ Opaleye.Operators: instance Opaleye.Operators.IntervalNum Opaleye.Internal.PGTypesExternal.SqlTimestamptz Opaleye.Internal.PGTypesExternal.SqlTimestamptz
+ Opaleye.Operators: minusInterval :: IntervalNum from to => Field from -> Field SqlInterval -> Field to
+ Opaleye.SqlTypes: data SqlInterval
+ Opaleye.SqlTypes: sqlInterval :: CalendarDiffTime -> Column PGInterval
Files
- CHANGELOG.md +6/−0
- Doc/Tutorial/TutorialBasic.lhs +9/−9
- Test/Test.hs +114/−1
- opaleye.cabal +3/−3
- src/Opaleye/Experimental/Enum.hs +34/−1
- src/Opaleye/Internal/Constant.hs +4/−1
- src/Opaleye/Internal/Inferrable.hs +9/−1
- src/Opaleye/Internal/PGTypes.hs +1/−1
- src/Opaleye/Internal/PGTypesExternal.hs +15/−1
- src/Opaleye/Internal/RunQuery.hs +4/−1
- src/Opaleye/Operators.hs +26/−0
- src/Opaleye/SqlTypes.hs +4/−2
CHANGELOG.md view
@@ -1,3 +1,9 @@+## 0.7.5.0++* Added `enumMapperWithSchema`. Thanks to Steve Mao.++* Added `SqlInterval`. Thanks to Bas van Dijk.+ ## 0.7.4.0 * Added `distinctOnCorrect` and `distinctOnByCorrect` which will
Doc/Tutorial/TutorialBasic.lhs view
@@ -9,11 +9,11 @@ > > import Opaleye (Field, FieldNullable, matchNullable, isNull, > Table, table, tableField, selectTable,-> Select, restrict, (.==), (.<=), (.&&), (.<),+> Select, (.==), (.<=), (.&&), (.<), > (.===), > (.++), ifThenElse, sqlString, aggregate, groupBy, > count, avg, sum, leftJoin, runSelect,-> showSql, viaLateral, Unpackspec,+> showSql, where_, Unpackspec, > SqlInt4, SqlInt8, SqlText, SqlDate, SqlFloat8, SqlBool) > > import Data.Profunctor.Product (p2, p3)@@ -253,7 +253,7 @@ > youngPeople :: Select (Field SqlText, Field SqlInt4, Field SqlText) > youngPeople = do > row@(_, age, _) <- personSelect-> viaLateral restrict (age .<= 18)+> where_ (age .<= 18) > > pure row @@ -284,8 +284,8 @@ > twentiesAtAddress = do > row@(_, age, address) <- personSelect >-> viaLateral restrict $ (20 .<= age) .&& (age .< 30)-> viaLateral restrict $ address .== sqlString "1 My Street, My Town"+> where_ $ (20 .<= age) .&& (age .< 30)+> where_ $ address .== sqlString "1 My Street, My Town" > > pure row @@ -326,7 +326,7 @@ > (name, age, address) <- personSelect > birthday <- birthdaySelect >-> viaLateral restrict $ name .== bdName birthday+> where_ $ name .== bdName birthday > > pure (name, age, address, bdDay birthday) @@ -470,11 +470,11 @@ > restrictIsTwenties :: Field SqlInt4 -> Select () > restrictIsTwenties age = do-> viaLateral restrict $ (20 .<= age) .&& (age .< 30)+> where_ $ (20 .<= age) .&& (age .< 30) > > restrictAddressIs1MyStreet :: Field SqlText -> Select () > restrictAddressIs1MyStreet address = do-> viaLateral restrict $ address .== sqlString "1 My Street, My Town"+> where_ $ address .== sqlString "1 My Street, My Town" We can't generate "the SQL of" these combinators. They are not `Select`s so they don't have any SQL! (This corresponds to the@@ -517,7 +517,7 @@ > birthdayOfPerson name = do > birthday <- birthdaySelect >-> viaLateral restrict $ name .== bdName birthday+> where_ $ name .== bdName birthday > > pure (bdDay birthday)
Test/Test.hs view
@@ -21,7 +21,8 @@ import qualified Data.String as String import qualified Data.ByteString as SBS import qualified Data.Text as T-import qualified Data.Time as Time+import qualified Data.Time.Compat as Time+import qualified Data.Time.Clock.POSIX.Compat as Time import qualified Database.PostgreSQL.Simple as PGS import qualified Database.PostgreSQL.Simple.Range as R import GHC.Int (Int64)@@ -1255,6 +1256,8 @@ testH (pure (O.sqlZonedTime value)) (\r -> map Time.zonedTimeToUTC r `shouldBe` [Time.zonedTimeToUTC value]) + it "sqlInterval" $ testLiteral O.sqlInterval (Time.calendarTimeTime 1)+ -- Check that MaybeFields's "Nothings" are not distinct, even if we -- fmap different values over their inner fields. testMaybeFieldsDistinct :: Test@@ -1274,7 +1277,108 @@ it "Returns same rows from a table" $ testH (OL.forUpdate table1Q) (`shouldBe` table1data) +testAddIntervalFromDateToTimestamptz :: Test+testAddIntervalFromDateToTimestamptz = do+ it "date + interval = timestamptz" $ testH query (`shouldBe` [expectation])+ where query :: Select (Field O.SqlTimestamp)+ query = pure $ (O.toFields d :: Field O.SqlDate)+ `O.addInterval`+ (O.toFields c :: Field O.SqlInterval) + d :: Time.Day+ d = Time.ModifiedJulianDay 0 -- = 1858-11-17++ -- 1 second+ c :: Time.CalendarDiffTime+ c = Time.calendarTimeTime 1++ expectation :: Time.LocalTime+ expectation = Time.ctTime c `Time.addLocalTime`+ Time.LocalTime+ { Time.localDay = d+ , Time.localTimeOfDay = Time.TimeOfDay 0 0 0+ }++testAddIntervalFromIntervalToInterval :: Test+testAddIntervalFromIntervalToInterval = do+ it "interval + interval = interval" $ testH query (`shouldBe` [expectation])+ where query :: Select (Field O.SqlInterval)+ query = pure $ (O.toFields c1 :: Field O.SqlInterval)+ `O.addInterval`+ (O.toFields c2 :: Field O.SqlInterval)++ -- 1 second+ c1 :: Time.CalendarDiffTime+ c1 = Time.calendarTimeTime 1++ -- 2 second+ c2 :: Time.CalendarDiffTime+ c2 = Time.calendarTimeTime 2++ expectation :: Time.CalendarDiffTime+ expectation = Time.calendarTimeTime $ Time.ctTime c1 + Time.ctTime c2++testAddIntervalFromTimestampToTimestamp :: Test+testAddIntervalFromTimestampToTimestamp = do+ it "timestamp + interval = timestamp" $ testH query (`shouldBe` [expectation])+ where query :: Select (Field O.SqlTimestamp)+ query = pure $ (O.toFields t :: Field O.SqlTimestamp)+ `O.addInterval`+ (O.toFields c :: Field O.SqlInterval)++ t :: Time.LocalTime+ t = Time.LocalTime+ { Time.localDay = Time.ModifiedJulianDay 0 -- = 1858-11-17+ , Time.localTimeOfDay = Time.TimeOfDay 0 0 0 -- midnight+ }++ -- 1 second+ c :: Time.CalendarDiffTime+ c = Time.calendarTimeTime 1++ expectation :: Time.LocalTime+ expectation = Time.ctTime c `Time.addLocalTime` t++testAddIntervalFromTimestamptzToTimestamptz :: Test+testAddIntervalFromTimestamptzToTimestamptz = do+ it "timestamptz + interval = timestamptz" $ testH query (`shouldBe` [expectation])+ where query :: Select (Field O.SqlTimestamptz)+ query = pure $ (O.toFields t :: Field O.SqlTimestamptz)+ `O.addInterval`+ (O.toFields c :: Field O.SqlInterval)++ -- UNIX epoch+ t :: Time.UTCTime+ t = Time.posixSecondsToUTCTime 0++ -- 1 second+ c :: Time.CalendarDiffTime+ c = Time.calendarTimeTime 1++ expectation :: Time.UTCTime+ expectation = Time.ctTime c `Time.addUTCTime` t++testAddIntervalFromTimeToTime :: Test+testAddIntervalFromTimeToTime = do+ it "time + interval = time" $ testH query (`shouldBe` [expectation])+ where query :: Select (Field O.SqlTime)+ query = pure $ (O.toFields t :: Field O.SqlTime)+ `O.addInterval`+ (O.toFields c :: Field O.SqlInterval)++ -- midnight+ t :: Time.TimeOfDay+ t = Time.TimeOfDay 0 0 0++ -- 1 second+ c :: Time.CalendarDiffTime+ c = Time.calendarTimeTime 1++ expectation :: Time.TimeOfDay+ expectation = Time.timeToTimeOfDay $+ (realToFrac (Time.ctTime c :: Time.NominalDiffTime) :: Time.DiffTime)+ + Time.timeOfDayToTime t+ main :: IO () main = do let envVarName = "POSTGRES_CONNSTRING"@@ -1323,6 +1427,9 @@ Connection.close conn2 conn3 <- PGS.connectPostgreSQL connectString+ -- intervals can only be decoded to CalendarDiffTimes+ -- when the interval rendering style is set to ISO-8601.+ _nrOfAffectedRows <- PGS.execute_ conn3 "SET intervalstyle TO iso_8601;" hspec $ do before (return conn3) $ do describe "core dsl?" $ do@@ -1428,3 +1535,9 @@ testMaybeFieldsDistinct describe "Locking" $ do testForUpdate+ describe "Interval" $ do+ testAddIntervalFromDateToTimestamptz+ testAddIntervalFromIntervalToInterval+ testAddIntervalFromTimestampToTimestamp+ testAddIntervalFromTimestamptzToTimestamptz+ testAddIntervalFromTimeToTime
opaleye.cabal view
@@ -1,6 +1,6 @@ name: opaleye copyright: Copyright (c) 2014-2018 Purely Agile Limited; 2019-2021 Tom Ellis-version: 0.7.4.0+version: 0.7.5.0 synopsis: An SQL-generating DSL targeting PostgreSQL description: An SQL-generating DSL targeting PostgreSQL. Allows Postgres queries to be written within Haskell in a@@ -41,7 +41,7 @@ , semigroups >= 0.13 && < 0.20 , text >= 0.11 && < 1.3 , transformers >= 0.3 && < 0.6- , time >= 1.4 && < 1.10+ , time-compat >= 1.9.5 && < 1.12 , time-locale-compat >= 0.1 && < 0.2 , uuid >= 1.3 && < 1.4 , void >= 0.4 && < 0.8@@ -141,7 +141,7 @@ QuickCheck, semigroups, text >= 0.11 && < 1.3,- time,+ time-compat, uuid, transformers, hspec,
src/Opaleye/Experimental/Enum.hs view
@@ -5,6 +5,7 @@ ( enumMapper, EnumMapper,+ enumMapperWithSchema, enumFromField, enumToFields, fromFieldToFieldsEnum,@@ -17,6 +18,8 @@ import Data.ByteString.Char8 (unpack) import qualified Data.Profunctor.Product.Default as D+import Text.PrettyPrint.HughesPJ ((<>), doubleQuotes, render, text)+import Prelude hiding ((<>)) data EnumMapper sqlEnum haskellSum = EnumMapper { enumFromField :: RQ.FromField sqlEnum haskellSum@@ -97,7 +100,37 @@ -- ^ The @sqlEnum@ type variable is phantom. To protect -- yourself against type mismatches you should set it to -- the Haskell type that you use to represent the @ENUM@.-enumMapper type_ from to_ = EnumMapper {+enumMapper type_ = enumMapper' (render (doubleQuotes (text type_)))++enumMapperWithSchema :: String+ -- ^ The schema of the @ENUM@ type+ -> String+ -- ^ The name of the @ENUM@ type+ -> (String -> Maybe haskellSum)+ -- ^ A function which converts from the string+ -- representation of the ENUM field+ -> (haskellSum -> String)+ -- ^ A function which converts to the string representation+ -- of the ENUM field+ -> EnumMapper sqlEnum haskellSum+ -- ^ The @sqlEnum@ type variable is phantom. To protect+ -- yourself against type mismatches you should set it to+ -- the Haskell type that you use to represent the @ENUM@.+enumMapperWithSchema schema type_ = enumMapper' (render (doubleQuotes (text schema) <> text "." <> doubleQuotes (text type_)))++enumMapper' :: String+ -- ^ The name of the @ENUM@ type+ -> (String -> Maybe haskellSum)+ -- ^ A function which converts from the string+ -- representation of the ENUM field+ -> (haskellSum -> String)+ -- ^ A function which converts to the string representation+ -- of the ENUM field+ -> EnumMapper sqlEnum haskellSum+ -- ^ The @sqlEnum@ type variable is phantom. To protect+ -- yourself against type mismatches you should set it to+ -- the Haskell type that you use to represent the @ENUM@.+enumMapper' type_ from to_ = EnumMapper { enumFromField = fromFieldEnum , enumToFields = toFieldsEnum }
src/Opaleye/Internal/Constant.hs view
@@ -14,7 +14,7 @@ import qualified Data.ByteString as SBS import qualified Data.ByteString.Lazy as LBS import qualified Data.Scientific as Sci-import qualified Data.Time as Time+import qualified Data.Time.Compat as Time import qualified Data.UUID as UUID import qualified Data.Profunctor.Product as PP@@ -103,6 +103,9 @@ instance D.Default ToFields Time.TimeOfDay (Column T.SqlTime) where def = toToFields T.sqlTimeOfDay++instance D.Default ToFields Time.CalendarDiffTime (Column T.SqlInterval) where+ def = toToFields T.sqlInterval instance D.Default ToFields (CI.CI ST.Text) (Column T.SqlCitext) where def = toToFields T.sqlCiStrictText
src/Opaleye/Internal/Inferrable.hs view
@@ -21,7 +21,7 @@ import qualified Data.Scientific as Sci import qualified Data.Text.Lazy as LT import qualified Data.Text as ST-import qualified Data.Time as Time+import qualified Data.Time.Compat as Time import Data.Typeable (Typeable) import Data.UUID (UUID) import qualified Database.PostgreSQL.Simple.Range as R@@ -98,6 +98,10 @@ => D.Default (Inferrable FromField) T.SqlTime timeofday where def = Inferrable D.def +instance calendardifftime ~ Time.CalendarDiffTime+ => D.Default (Inferrable FromField) T.SqlInterval calendardifftime where+ def = Inferrable D.def+ instance cttext ~ CI.CI ST.Text => D.Default (Inferrable FromField) T.SqlCitext cttext where def = Inferrable D.def@@ -204,6 +208,10 @@ instance C.Column T.SqlTime ~ cSqlTime => D.Default (Inferrable ToFields) Time.TimeOfDay cSqlTime where+ def = Inferrable D.def++instance C.Column T.SqlInterval ~ cSqlInterval+ => D.Default (Inferrable ToFields) Time.CalendarDiffTime cSqlInterval where def = Inferrable D.def instance C.Column T.SqlCitext ~ cSqlCitext
src/Opaleye/Internal/PGTypes.hs view
@@ -13,7 +13,7 @@ import qualified Data.Text.Lazy.Encoding as LTextEncoding import qualified Data.ByteString as SByteString import qualified Data.ByteString.Lazy as LByteString-import qualified Data.Time as Time+import qualified Data.Time.Compat as Time import qualified Data.Time.Locale.Compat as Locale unsafePgFormatTime :: Time.FormatTime t => HPQ.Name -> String -> t -> Column c
src/Opaleye/Internal/PGTypesExternal.hs view
@@ -20,7 +20,8 @@ import qualified Data.ByteString as SByteString import qualified Data.ByteString.Lazy as LByteString import Data.Scientific as Sci-import qualified Data.Time as Time+import qualified Data.Time.Compat as Time+import qualified Data.Time.Format.ISO8601.Compat as Time.Format.ISO8601 import qualified Data.UUID as UUID import Data.Int (Int64)@@ -106,6 +107,10 @@ -- "We recommend not using the type time with time zone" -- http://www.postgresql.org/docs/8.3/static/datatype-datetime.html +sqlInterval :: Time.CalendarDiffTime -> Column PGInterval+sqlInterval = IPT.castToType "interval" . quote . Time.Format.ISO8601.iso8601Show+ where+ quote s = "'" ++ s ++ "'" pgCiStrictText :: CI.CI SText.Text -> Column PGCitext pgCiStrictText = IPT.literalColumn . HPQ.StringLit . SText.unpack . CI.original@@ -179,6 +184,8 @@ showSqlType _ = "integer" instance IsSqlType SqlInt2 where showSqlType _ = "smallint"+instance IsSqlType SqlInterval where+ showSqlType _ = "interval" instance IsSqlType SqlNumeric where showSqlType _ = "numeric" instance IsSqlType SqlText where@@ -234,6 +241,12 @@ data SqlInt8 data SqlInt4 data SqlInt2+-- | Requires you to configure @intervalstyle@ as @iso_8601@.+--+-- You can configure @intervalstyle@ on every connection with a @SET@ command,+-- but for better performance you may want to configure it permanently in the+-- file found with @SHOW config_file;@.+data SqlInterval data SqlNumeric data SqlText data SqlTime@@ -264,6 +277,7 @@ type PGInt8 = SqlInt8 type PGInt4 = SqlInt4 type PGInt2 = SqlInt2+type PGInterval = SqlInterval type PGNumeric = SqlNumeric type PGText = SqlText type PGTime = SqlTime
src/Opaleye/Internal/RunQuery.hs view
@@ -39,7 +39,7 @@ import qualified Data.Text.Lazy.Encoding as LTE import qualified Data.ByteString as SBS import qualified Data.ByteString.Lazy as LBS-import qualified Data.Time as Time+import qualified Data.Time.Compat as Time import qualified Data.Scientific as Sci import qualified Data.String as String import Data.UUID (UUID)@@ -242,6 +242,9 @@ defaultFromField = fromPGSFromField instance DefaultFromField T.SqlTimestamptz Time.ZonedTime where+ defaultFromField = fromPGSFromField++instance DefaultFromField T.SqlInterval Time.CalendarDiffTime where defaultFromField = fromPGSFromField instance DefaultFromField T.SqlTime Time.TimeOfDay where
src/Opaleye/Operators.hs view
@@ -2,6 +2,8 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FunctionalDependencies #-} -- We can probably disable ConstraintKinds and TypeSynonymInstances -- when we move to Sql... instead of PG..@@ -100,6 +102,9 @@ , timestamptzAtTimeZone , dateOfTimestamp , now+ , IntervalNum+ , addInterval+ , minusInterval -- * Deprecated , exists , notExists@@ -468,6 +473,27 @@ dateOfTimestamp :: F.Field T.SqlTimestamp -> F.Field T.SqlDate dateOfTimestamp (Column e) = Column (HPQ.FunExpr "date" [e])++-- | @IntervalNum from to@ determines from which date or time types an interval+-- can be added ('addInterval') or subtracted ('minusInterval`) and which is the+-- resulting type.+--+-- The instances should correspond to the interval + and - operations listed in:+--+-- https://www.postgresql.org/docs/current/functions-datetime.html#OPERATORS-DATETIME-TABLE+class IntervalNum from to | from -> to++instance IntervalNum T.SqlDate T.SqlTimestamp+instance IntervalNum T.SqlInterval T.SqlInterval+instance IntervalNum T.SqlTimestamp T.SqlTimestamp+instance IntervalNum T.SqlTimestamptz T.SqlTimestamptz+instance IntervalNum T.SqlTime T.SqlTime++addInterval :: IntervalNum from to => F.Field from -> F.Field T.SqlInterval -> F.Field to+addInterval = C.binOp (HPQ.:+)++minusInterval :: IntervalNum from to => F.Field from -> F.Field T.SqlInterval -> F.Field to+minusInterval = C.binOp (HPQ.:-) {-# DEPRECATED exists "Identical to 'restrictExists'. Will be removed in version 0.8." #-} exists :: QueryArr a b -> QueryArr a ()
src/Opaleye/SqlTypes.hs view
@@ -23,11 +23,13 @@ sqlLocalTime, sqlZonedTime, sqlTimeOfDay,+ P.sqlInterval, -- ** Types SqlDate, SqlTime, SqlTimestamp, SqlTimestamptz,+ SqlInterval, -- * JSON -- ** Creating values sqlJSON,@@ -96,6 +98,7 @@ SqlTime, SqlTimestamp, SqlTimestamptz,+ SqlInterval, SqlUuid, SqlCitext, SqlArray,@@ -112,7 +115,7 @@ import Data.Scientific as Sci import qualified Data.Text as SText import qualified Data.Text.Lazy as LText-import qualified Data.Time as Time+import qualified Data.Time.Compat as Time import qualified Data.UUID as UUID import qualified Database.PostgreSQL.Simple.Range as R@@ -169,7 +172,6 @@ -- "We recommend not using the type time with time zone" -- http://www.postgresql.org/docs/8.3/static/datatype-datetime.html- sqlCiStrictText :: CI.CI SText.Text -> F.Field SqlCitext sqlCiStrictText = P.pgCiStrictText