@@ -17,8 +17,8 @@ module Database.PostgreSQL.Simple.Range
1717 ( RangeBound(..)
1818 , PGRange(..)
1919 , empty
20− , isEmpty
21− , isEmptyBy
20+ , isEmpty, isEmptyBy
21+ , contains, containsBy
2222 ) where
2323
2424import Control.Applicative hiding (empty)
@@ -83,9 +83,46 @@ isEmptyBy cmp v =
8383 (PGRange (Exclusive x) (Inclusive y)) -> cmp x y /= LT
8484 (PGRange (Exclusive x) (Exclusive y)) -> cmp x y /= LT
8585
86+-- | Is a range empty? If this returns 'True', then the 'contains'
87+-- predicate will always return 'False'. However, if this returns
88+-- 'False', it is not necessarily true that there exists a point for
89+-- which 'contains' returns 'True'.
90+-- Consider @'PGRange' ('Excludes' 2) ('Excludes' 3) :: PGRange Int@,
91+-- for example.
8692isEmpty :: Ord a => PGRange a -> Bool
8793isEmpty = isEmptyBy compare
8894
95+
96+-- | Does a range contain a given point? Note that in some cases, this may
97+-- not correspond exactly with a server-side computation. Consider @UTCTime@
98+-- for example, which has a resolution of a picosecond, whereas postgresql's
99+-- @timestamptz@ types have a resolution of a microsecond. Putting such
100+-- Haskell values into the database will result in them being rounded, which
101+-- can change the value of the containment predicate.
102+
103+contains :: Ord a => PGRange a -> (a -> Bool)
104+contains = containsBy compare
105+
106+containsBy :: (a -> a -> Ordering) -> PGRange a -> (a -> Bool)
107+containsBy cmp rng x =
108+ case rng of
109+ PGRange _lb NegInfinity -> False
110+ PGRange lb ub -> checkLB lb x && checkUB ub x
111+ where
112+ checkLB lb x =
113+ case lb of
114+ NegInfinity -> True
115+ PosInfinity -> False
116+ Inclusive a -> cmp a x /= GT
117+ Exclusive a -> cmp a x == LT
118+
119+ checkUB ub x =
120+ case ub of
121+ NegInfinity -> False
122+ PosInfinity -> True
123+ Inclusive z -> cmp x z /= GT
124+ Exclusive z -> cmp x z == LT
125+
89126lowerBound :: Parser (a -> RangeBound a)
90127lowerBound = (A.char '(' *> pure Exclusive) <|> (A.char '[' *> pure Inclusive)
91128{-# INLINE lowerBound #-}