diff --git a/Database/PostgreSQL/Typed/Protocol.hs b/Database/PostgreSQL/Typed/Protocol.hs
--- a/Database/PostgreSQL/Typed/Protocol.hs
+++ b/Database/PostgreSQL/Typed/Protocol.hs
@@ -11,7 +11,7 @@
   , defaultPGDatabase
   , PGConnection
   , PGError(..)
-  , pgMessageCode
+  , pgErrorCode
   , pgTypeEnv
   , pgConnect
   , pgDisconnect
@@ -170,7 +170,7 @@
 
 -- |PGException is thrown upon encountering an 'ErrorResponse' with severity of
 --  ERROR, FATAL, or PANIC. It holds the message of the error.
-data PGError = PGError MessageFields
+data PGError = PGError { pgErrorFields :: MessageFields }
   deriving (Typeable)
 
 instance Show PGError where
@@ -188,8 +188,8 @@
 
 -- |Message SQLState code.
 --  See <http://www.postgresql.org/docs/current/static/errcodes-appendix.html>.
-pgMessageCode :: MessageFields -> String
-pgMessageCode = Map.findWithDefault "" 'C'
+pgErrorCode :: PGError -> String
+pgErrorCode (PGError e) = Map.findWithDefault "" 'C' e
 
 defaultLogMessage :: MessageFields -> IO ()
 defaultLogMessage = hPutStrLn stderr . displayMessage
diff --git a/Database/PostgreSQL/Typed/Query.hs b/Database/PostgreSQL/Typed/Query.hs
--- a/Database/PostgreSQL/Typed/Query.hs
+++ b/Database/PostgreSQL/Typed/Query.hs
@@ -94,6 +94,8 @@
 
 instance IsString (PGSimpleQuery PGValues) where
   fromString = rawPGSimpleQuery
+instance IsString (PGSimpleQuery ()) where
+  fromString = fmap (const ()) . rawPGSimpleQuery
 
 -- |Make a prepared query directly from a query string and bind parameters, with no type inference
 rawPGPreparedQuery :: String -> PGValues -> PGPreparedQuery PGValues
diff --git a/Database/PostgreSQL/Typed/Range.hs b/Database/PostgreSQL/Typed/Range.hs
--- a/Database/PostgreSQL/Typed/Range.hs
+++ b/Database/PostgreSQL/Typed/Range.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, UndecidableInstances, FunctionalDependencies, DataKinds #-}
+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, UndecidableInstances, FunctionalDependencies, DataKinds, GeneralizedNewtypeDeriving, PatternGuards #-}
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 -- |
 -- Module: Database.PostgreSQL.Typed.Range
@@ -14,45 +14,66 @@
 import Control.Monad (guard)
 import qualified Data.ByteString.Builder as BSB
 import qualified Data.ByteString.Char8 as BSC
-import Data.Monoid ((<>), mempty)
+import Data.Monoid (Monoid(..), (<>))
 import qualified Text.Parsec as P
 
 import Database.PostgreSQL.Typed.Types
 
+-- |A end-point for a range, which may be nothing (infinity, NULL in PostgreSQL), open (inclusive), or closed (exclusive)
 data Bound a
-  = Unbounded
-  | Bounded Bool a
+  = Unbounded -- ^ Equivalent to @Bounded False ±Infinity@
+  | Bounded
+    { _boundClosed :: Bool -- ^ @True@ if the range includes this bound
+    , _bound :: a
+    }
   deriving (Eq)
 
 instance Functor Bound where
   fmap _ Unbounded = Unbounded
   fmap f (Bounded c a) = Bounded c (f a)
 
-newtype LowerBound a = Lower (Bound a) deriving (Eq)
-
-instance Functor LowerBound where
-  fmap f (Lower b) = Lower (fmap f b)
+newtype LowerBound a = Lower { boundLower :: Bound a } deriving (Eq, Functor)
 
+-- |Takes into account open vs. closed (but does not understand equivalent discrete bounds)
 instance Ord a => Ord (LowerBound a) where
   compare (Lower Unbounded) (Lower Unbounded) = EQ
   compare (Lower Unbounded) _ = LT
   compare _ (Lower Unbounded) = GT
   compare (Lower (Bounded ac a)) (Lower (Bounded bc b)) = compare a b <> compare bc ac
 
-newtype UpperBound a = Upper (Bound a) deriving (Eq)
+-- |The constraint is only necessary for @maxBound@, unfortunately
+instance Bounded a => Bounded (LowerBound a) where
+  minBound = Lower Unbounded
+  maxBound = Lower (Bounded False maxBound)
 
-instance Functor UpperBound where
-  fmap f (Upper b) = Upper (fmap f b)
+newtype UpperBound a = Upper { boundUpper :: Bound a } deriving (Eq, Functor)
 
+-- |Takes into account open vs. closed (but does not understand equivalent discrete bounds)
 instance Ord a => Ord (UpperBound a) where
   compare (Upper Unbounded) (Upper Unbounded) = EQ
   compare (Upper Unbounded) _ = GT
   compare _ (Upper Unbounded) = LT
   compare (Upper (Bounded ac a)) (Upper (Bounded bc b)) = compare a b <> compare ac bc
 
+-- |The constraint is only necessary for @minBound@, unfortunately
+instance Bounded a => Bounded (UpperBound a) where
+  minBound = Upper (Bounded False minBound)
+  maxBound = Upper Unbounded
+
+compareBounds :: Ord a => LowerBound a -> UpperBound a -> Bound Bool
+compareBounds (Lower (Bounded lc l)) (Upper (Bounded uc u)) =
+  case compare l u of
+    LT -> Bounded True True
+    EQ -> Bounded (lc /= uc) (lc && uc)
+    GT -> Bounded False False
+compareBounds _ _ = Unbounded
+
 data Range a
   = Empty
-  | Range (LowerBound a) (UpperBound a)
+  | Range
+    { lower :: LowerBound a
+    , upper :: UpperBound a
+    }
   deriving (Eq)
 
 instance Functor Range where
@@ -70,28 +91,45 @@
 bound Unbounded = Nothing
 bound (Bounded _ b) = Just b
 
+-- |Unbounded endpoints are always open.
 boundClosed :: Bound a -> Bool
 boundClosed Unbounded = False
 boundClosed (Bounded c _) = c
 
+-- |Construct from parts: @makeBound (boundClosed b) (bound b) == b@
 makeBound :: Bool -> Maybe a -> Bound a
 makeBound c (Just a) = Bounded c a
 makeBound False Nothing = Unbounded
 makeBound True Nothing = error "makeBound: unbounded may not be closed"
 
+-- |Empty ranges treated as 'Unbounded'
+lowerBound :: Range a -> Bound a
+lowerBound Empty = Unbounded
+lowerBound (Range (Lower b) _) = b
+
+-- |Empty ranges treated as 'Unbounded'
+upperBound :: Range a -> Bound a
+upperBound Empty = Unbounded
+upperBound (Range _ (Upper b)) = b
+
+-- |Equivalent to @boundClosed . lowerBound@
 lowerClosed :: Range a -> Bool
 lowerClosed Empty = False
 lowerClosed (Range (Lower b) _) = boundClosed b
 
+-- |Equivalent to @boundClosed . upperBound@
 upperClosed :: Range a -> Bool
 upperClosed Empty = False
 upperClosed (Range _ (Upper b)) = boundClosed b
 
+empty :: Range a
+empty = Empty
+
 isEmpty :: Ord a => Range a -> Bool
 isEmpty Empty = True
-isEmpty (Range (Lower (Bounded True l)) (Upper (Bounded True u))) = l > u
-isEmpty (Range (Lower (Bounded _ l)) (Upper (Bounded _ u))) = l >= u
-isEmpty _ = False
+isEmpty (Range l u)
+  | Bounded _ n <- compareBounds l u = not n
+  | otherwise = False
 
 full :: Range a
 full = Range (Lower Unbounded) (Upper Unbounded)
@@ -100,23 +138,29 @@
 isFull (Range (Lower Unbounded) (Upper Unbounded)) = True
 isFull _ = False
 
+-- |Create a point range @[x,x]@
 point :: Eq a => a -> Range a
 point a = Range (Lower (Bounded True a)) (Upper (Bounded True a))
 
+-- |Extract a point: @getPoint (point x) == Just x@
 getPoint :: Eq a => Range a -> Maybe a
 getPoint (Range (Lower (Bounded True l)) (Upper (Bounded True u))) = u <$ guard (u == l)
 getPoint _ = Nothing
 
+-- Construct a range from endpoints and normalize it.
 range :: Ord a => Bound a -> Bound a -> Range a
 range l u = normalize $ Range (Lower l) (Upper u)
 
+-- Construct a standard range (@[l,u)@ or 'point') from bounds (like 'bound') and normalize it.
 normal :: Ord a => Maybe a -> Maybe a -> Range a
-normal l u = range (mb True l) (mb False u) where
+normal l u = range (mb True l) (mb (l == u) u) where
   mb = maybe Unbounded . Bounded
 
+-- Construct a bounded range like 'normal'.
 bounded :: Ord a => a -> a -> Range a
-bounded l u = range (Bounded True l) (Bounded False u)
+bounded l u = normal (Just l) (Just u)
 
+-- Fold empty ranges to 'Empty'.
 normalize :: Ord a => Range a -> Range a
 normalize r
   | isEmpty r = Empty
@@ -125,7 +169,7 @@
 -- |'normalize' for discrete (non-continuous) range types, using the 'Enum' instance
 normalize' :: (Ord a, Enum a) => Range a -> Range a
 normalize' Empty = Empty
-normalize' (Range (Lower l) (Upper u)) = range l' u'
+normalize' (Range (Lower l) (Upper u)) = normalize $ range l' u'
   where
   l' = case l of
     Bounded False b -> Bounded True (succ b)
@@ -134,18 +178,35 @@
     Bounded True b -> Bounded False (succ b)
     _ -> l
 
+-- |Contains range
 (@>), (<@) :: Ord a => Range a -> Range a -> Bool
 _ @> Empty = True
 Empty @> r = isEmpty r
 Range la ua @> Range lb ub = la <= lb && ua >= ub
 a <@ b = b @> a
 
+-- |Contains element
 (@>.) :: Ord a => Range a -> a -> Bool
 r @>. a = r @> point a
 
+overlaps :: Ord a => Range a -> Range a -> Bool
+overlaps a b = intersect a b /= Empty
+
 intersect :: Ord a => Range a -> Range a -> Range a
 intersect (Range la ua) (Range lb ub) = normalize $ Range (max la lb) (min ua ub)
 intersect _ _ = Empty
+
+instance Ord a => Monoid (Range a) where
+  mempty = Empty
+  -- |Union ranges.  Fails if ranges are disjoint.
+  mappend Empty r = r
+  mappend r Empty = r
+  mappend _ra@(Range la ua) _rb@(Range lb ub)
+    -- isEmpty _ra = _rb
+    -- isEmpty _rb = _ra
+    | Bounded False False <- compareBounds lb ua = error "mappend: disjoint Ranges"
+    | Bounded False False <- compareBounds la ub = error "mappend: disjoint Ranges"
+    | otherwise = Range (min la lb) (max ua ub)
 
 
 -- |Class indicating that the first PostgreSQL type is a range of the second.
diff --git a/Database/PostgreSQL/Typed/TH.hs b/Database/PostgreSQL/Typed/TH.hs
--- a/Database/PostgreSQL/Typed/TH.hs
+++ b/Database/PostgreSQL/Typed/TH.hs
@@ -151,7 +151,7 @@
       { tpgValueName = c
       , tpgValueTypeOID = o
       , tpgValueType = tpgType tpg o
-      , tpgValueNullable = n
+      , tpgValueNullable = n && o /= 2278 -- "void"
       }) rt
     )
 
diff --git a/Database/PostgreSQL/Typed/TemplatePG.hs b/Database/PostgreSQL/Typed/TemplatePG.hs
--- a/Database/PostgreSQL/Typed/TemplatePG.hs
+++ b/Database/PostgreSQL/Typed/TemplatePG.hs
@@ -87,7 +87,7 @@
 -- |Ignore duplicate key errors. This is also limited to the 'IO' Monad.
 insertIgnore :: IO () -> IO ()
 insertIgnore q = catchJust uniquenessError q (\ _ -> return ()) where
-  uniquenessError (PG.PGError m) = guard (PG.pgMessageCode m == "24505")
+  uniquenessError e = guard (PG.pgErrorCode e == "23505")
 
 type PGException = PG.PGError
 
diff --git a/Database/PostgreSQL/Typed/Types.hs b/Database/PostgreSQL/Typed/Types.hs
--- a/Database/PostgreSQL/Typed/Types.hs
+++ b/Database/PostgreSQL/Typed/Types.hs
@@ -36,6 +36,10 @@
 
 import Control.Applicative ((<$>), (<$))
 import Control.Monad (mzero)
+#ifdef USE_AESON
+import qualified Data.Aeson as JSON
+import qualified Data.Attoparsec.ByteString as JSONP
+#endif
 import Data.Bits (shiftL, (.|.))
 import Data.ByteString.Internal (w2c)
 import qualified Data.ByteString as BS
@@ -215,6 +219,12 @@
 #define BIN_DEC(F)
 #endif
 
+instance PGType "void"
+instance PGColumn "void" () where
+  pgDecode _ _ = ()
+  pgDecodeBinary _ _ _ = ()
+  pgDecodeValue _ _ _ = ()
+
 instance PGType "boolean" where BIN_COL
 instance PGParameter "boolean" Bool where
   pgEncode _ False = BSC.singleton 'f'
@@ -565,8 +575,21 @@
 -- In this case we can not know the types, and in fact, PostgreSQL does not accept values of this type regardless (except as literals).
 instance PGRecordType "record"
 
+#ifdef USE_AESON
+instance PGType "json"
+instance PGParameter "json" JSON.Value where
+  pgEncode _ = BSL.toStrict . JSON.encode
+instance PGColumn "json" JSON.Value where
+  pgDecode _ j = either (error . ("pgDecode json: " ++)) id $ JSONP.parseOnly JSON.json j
+
+instance PGType "jsonb"
+instance PGParameter "jsonb" JSON.Value where
+  pgEncode _ = BSL.toStrict . JSON.encode
+instance PGColumn "jsonb" JSON.Value where
+  pgDecode _ j = either (error . ("pgDecode json: " ++)) id $ JSONP.parseOnly JSON.json j
+#endif
+
 {-
---, ( 114,  199, "json",        ?)
 --, ( 142,  143, "xml",         ?)
 --, ( 600, 1017, "point",       ?)
 --, ( 650,  651, "cidr",        ?)
diff --git a/postgresql-typed.cabal b/postgresql-typed.cabal
--- a/postgresql-typed.cabal
+++ b/postgresql-typed.cabal
@@ -1,5 +1,5 @@
 Name:          postgresql-typed
-Version:       0.3.2
+Version:       0.3.3
 Cabal-Version: >= 1.8
 License:       BSD3
 License-File:  COPYING
@@ -43,6 +43,10 @@
   Description: Support decoding numeric via scientific (implied by binary).
   Default: True
 
+Flag aeson
+  Description: Support decoding json via aeson.
+  Default: True
+
 Library
   Build-Depends:
     base >= 4.7 && < 5,
@@ -88,6 +92,9 @@
     if flag(scientific)
       Build-Depends: scientific >= 0.3
       CPP-options: -DUSE_SCIENTIFIC
+  if flag(aeson)
+    Build-Depends: aeson >= 0.7, attoparsec >= 0.10
+    CPP-options: -DUSE_AESON
 
 test-suite test
   build-depends: base, network, time, postgresql-typed
