diff --git a/Database/PostgreSQL/Typed/Array.hs b/Database/PostgreSQL/Typed/Array.hs
--- a/Database/PostgreSQL/Typed/Array.hs
+++ b/Database/PostgreSQL/Typed/Array.hs
@@ -10,9 +10,10 @@
 
 module Database.PostgreSQL.Typed.Array where
 
-import Control.Applicative ((<$>), (<$))
+import Control.Applicative ((<$>))
 import qualified Data.ByteString.Builder as BSB
 import qualified Data.ByteString.Char8 as BSC
+import Data.Char (toLower)
 import Data.List (intersperse)
 import Data.Monoid ((<>), mconcat)
 import qualified Text.Parsec as P
@@ -38,15 +39,14 @@
     el Nothing = BSB.string7 "null"
     el (Just e) = pgDQuote (pgArrayDelim ta : "{}") $ pgEncode (pgArrayElementType ta) e
 instance (PGArrayType ta t, PGColumn t a) => PGColumn ta (PGArray a) where
-  pgDecode ta = either (error . ("pgDecode array: " ++) . show) id . P.parse pa "array" where
+  pgDecode ta a = either (error . ("pgDecode array: " ++) . show) id $ P.parse pa (BSC.unpack a) a where
     pa = do
       l <- P.between (P.char '{') (P.char '}') $
-        P.sepBy nel (P.char (pgArrayDelim ta))
+        P.sepBy el (P.char (pgArrayDelim ta))
       _ <- P.eof
       return l
-    nel = P.between P.spaces P.spaces $ Nothing <$ nul P.<|> Just <$> el
-    nul = P.oneOf "Nn" >> P.oneOf "Uu" >> P.oneOf "Ll" >> P.oneOf "Ll"
-    el = pgDecode (pgArrayElementType ta) . BSC.pack <$> parsePGDQuote (pgArrayDelim ta : "{}")
+    el = P.between P.spaces P.spaces $ fmap (pgDecode (pgArrayElementType ta) . BSC.pack) <$>
+      parsePGDQuote (pgArrayDelim ta : "{}") (("null" ==) . map toLower)
 
 -- Just a dump of pg_type:
 instance PGType "boolean" => PGType "boolean[]"
diff --git a/Database/PostgreSQL/Typed/Dynamic.hs b/Database/PostgreSQL/Typed/Dynamic.hs
--- a/Database/PostgreSQL/Typed/Dynamic.hs
+++ b/Database/PostgreSQL/Typed/Dynamic.hs
@@ -62,17 +62,17 @@
   pgDecodeRep PGNullValue = Nothing
   pgDecodeRep v = Just (pgDecodeRep v)
 
-instance PGRep "boolean" Bool where
-instance PGRep "oid" OID where
-instance PGRep "smallint" Int16 where
-instance PGRep "integer" Int32 where
-instance PGRep "bigint" Int64 where
-instance PGRep "real" Float where
-instance PGRep "double precision" Double where
-instance PGRep "\"char\"" Char where
-instance PGRep "text" String where
+instance PGRep "boolean" Bool
+instance PGRep "oid" OID
+instance PGRep "smallint" Int16
+instance PGRep "integer" Int32
+instance PGRep "bigint" Int64
+instance PGRep "real" Float
+instance PGRep "double precision" Double
+instance PGRep "\"char\"" Char
+instance PGRep "text" String
 #ifdef USE_TEXT
-instance PGRep "text" T.Text where
+instance PGRep "text" T.Text
 #endif
 instance PGRep "date" Time.Day
 instance PGRep "time without time zone" Time.TimeOfDay
@@ -81,10 +81,10 @@
 instance PGRep "interval" Time.DiffTime
 instance PGRep "numeric" Rational
 #ifdef USE_SCIENTIFIC
-instance PGRep "numeric" Scientific where
+instance PGRep "numeric" Scientific
 #endif
 #ifdef USE_UUID
-instance PGRep "uuid" UUID.UUID where
+instance PGRep "uuid" UUID.UUID
 #endif
 
 -- |Create an expression that literally substitutes each instance of @${expr}@ for the result of @pgSafeLiteral expr@.
diff --git a/Database/PostgreSQL/Typed/Enum.hs b/Database/PostgreSQL/Typed/Enum.hs
--- a/Database/PostgreSQL/Typed/Enum.hs
+++ b/Database/PostgreSQL/Typed/Enum.hs
@@ -6,12 +6,16 @@
 -- Support for PostgreSQL enums.
 
 module Database.PostgreSQL.Typed.Enum 
-  ( makePGEnum
+  ( PGEnum
+  , pgEnumValues
+  , makePGEnum
   ) where
 
 import Control.Monad (when)
+import qualified Data.ByteString as BS
 import qualified Data.ByteString.Char8 as BSC
 import qualified Data.ByteString.UTF8 as U
+import Data.Typeable (Typeable)
 import qualified Language.Haskell.TH as TH
 
 import Database.PostgreSQL.Typed.Protocol
@@ -19,17 +23,26 @@
 import Database.PostgreSQL.Typed.Types
 import Database.PostgreSQL.Typed.Dynamic
 
+-- |A type based on a PostgreSQL enum. Automatically instantiated by 'makePGEnum'.
+class (Eq a, Ord a, Enum a, Bounded a, Show a) => PGEnum a
+
+-- |List of all the values in the enum along with their database names.
+pgEnumValues :: PGEnum a => [(a, String)]
+pgEnumValues = map (\e -> (e, show e)) $ enumFromTo minBound maxBound
+
 -- |Create a new enum type corresponding to the given PostgreSQL enum type.
 -- For example, if you have @CREATE TYPE foo AS ENUM (\'abc\', \'DEF\');@, then
 -- @makePGEnum \"foo\" \"Foo\" (\"Foo_\"++)@ will be equivalent to:
 -- 
--- @
--- data Foo = Foo_abc | Foo_DEF deriving (Eq, Ord, Enum, Bounded, Show, Read)
--- instance PGType Foo where ...
--- registerPGType \"foo\" (ConT ''Foo)
--- @
+-- > data Foo = Foo_abc | Foo_DEF deriving (Eq, Ord, Enum, Bounded, Typeable)
+-- > instance Show Foo where show Foo_abc = "abc" ...
+-- > instance PGType "foo"
+-- > instance PGParameter "foo" Foo where ...
+-- > instance PGColumn "foo" Foo where ...
+-- > instance PGRep "foo" Foo
+-- > instance PGEnum Foo where pgEnumValues = [(Foo_abc, "abc"), (Foo_DEF, "DEF")]
 --
--- Requires language extensions: TemplateHaskell, FlexibleInstances, MultiParamTypeClasses, DataKinds
+-- Requires language extensions: TemplateHaskell, FlexibleInstances, MultiParamTypeClasses, DeriveDataTypeable, DataKinds
 makePGEnum :: String -- ^ PostgreSQL enum type name
   -> String -- ^ Haskell type to create
   -> (String -> String) -- ^ How to generate constructor names from enum values, e.g. @(\"Type_\"++)@
@@ -39,24 +52,30 @@
     pgSimpleQuery c $ "SELECT enumlabel FROM pg_catalog.pg_enum JOIN pg_catalog.pg_type t ON enumtypid = t.oid WHERE typtype = 'e' AND format_type(t.oid, -1) = " ++ pgQuote name ++ " ORDER BY enumsortorder"
   when (null vals) $ fail $ "makePGEnum: enum " ++ name ++ " not found"
   let 
-    valn = map (\[PGTextValue v] -> (TH.StringL (BSC.unpack v), TH.mkName $ valnf (U.toString v))) vals
+    valn = map (\[PGTextValue v] -> let u = U.toString v in (TH.mkName $ valnf u, map (TH.IntegerL . fromIntegral) $ BS.unpack v, TH.StringL u)) vals
   dv <- TH.newName "x"
-  ds <- TH.newName "s"
   return
-    [ TH.DataD [] typn [] (map (\(_, n) -> TH.NormalC n []) valn) [''Eq, ''Ord, ''Enum, ''Bounded, ''Show, ''Read]
+    [ TH.DataD [] typn [] (map (\(n, _, _) -> TH.NormalC n []) valn) [''Eq, ''Ord, ''Enum, ''Bounded, ''Typeable]
+    , TH.InstanceD [] (TH.ConT ''Show `TH.AppT` typt)
+      [ TH.FunD 'show $ map (\(n, _, v) -> TH.Clause [TH.ConP n []]
+        (TH.NormalB $ TH.LitE v) []) valn
+      ]
     , TH.InstanceD [] (TH.ConT ''PGType `TH.AppT` typl) []
     , TH.InstanceD [] (TH.ConT ''PGParameter `TH.AppT` typl `TH.AppT` typt)
-      [ TH.FunD 'pgEncode $ map (\(l, n) -> TH.Clause [TH.WildP, TH.ConP n []]
-        (TH.NormalB $ TH.VarE 'BSC.pack `TH.AppE` TH.LitE l) []) valn ]
+      [ TH.FunD 'pgEncode $ map (\(n, l, _) -> TH.Clause [TH.WildP, TH.ConP n []]
+        (TH.NormalB $ TH.VarE 'BS.pack `TH.AppE` TH.ListE (map TH.LitE l)) []) valn
+      ]
     , TH.InstanceD [] (TH.ConT ''PGColumn `TH.AppT` typl `TH.AppT` typt)
       [ TH.FunD 'pgDecode [TH.Clause [TH.WildP, TH.VarP dv]
-        (TH.NormalB $ TH.CaseE (TH.VarE 'BSC.unpack `TH.AppE` TH.VarE dv) $ map (\(l, n) ->
-          TH.Match (TH.LitP l) (TH.NormalB $ TH.ConE n) []) valn ++
-          [TH.Match (TH.VarP ds) (TH.NormalB $ TH.AppE (TH.VarE 'error) $
-            TH.InfixE (Just $ TH.LitE (TH.StringL ("pgDecode " ++ name ++ ": "))) (TH.VarE '(++)) (Just $ TH.VarE ds))
+        (TH.NormalB $ TH.CaseE (TH.VarE 'BS.unpack `TH.AppE` TH.VarE dv) $ map (\(n, l, _) ->
+          TH.Match (TH.ListP (map TH.LitP l)) (TH.NormalB $ TH.ConE n) []) valn ++
+          [TH.Match TH.WildP (TH.NormalB $ TH.AppE (TH.VarE 'error) $
+            TH.InfixE (Just $ TH.LitE (TH.StringL ("pgDecode " ++ name ++ ": "))) (TH.VarE '(++)) (Just $ TH.VarE 'BSC.unpack `TH.AppE` TH.VarE dv))
             []])
-        []] ]
+        []] 
+      ]
     , TH.InstanceD [] (TH.ConT ''PGRep `TH.AppT` typl `TH.AppT` typt) []
+    , TH.InstanceD [] (TH.ConT ''PGEnum `TH.AppT` typt) []
     ]
   where
   typn = TH.mkName typs
diff --git a/Database/PostgreSQL/Typed/Inet.hs b/Database/PostgreSQL/Typed/Inet.hs
new file mode 100644
--- /dev/null
+++ b/Database/PostgreSQL/Typed/Inet.hs
@@ -0,0 +1,47 @@
+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies, DataKinds #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+-- |
+-- Module: Database.PostgreSQL.Typed.Inet
+-- Copyright: 2015 Dylan Simon
+-- 
+-- Representaion of PostgreSQL's inet/cidr types using "Network.Socket".
+-- We don't (yet) supply PGColumn (parsing) instances.
+
+module Database.PostgreSQL.Typed.Inet where
+
+import qualified Data.ByteString.Char8 as BSC
+import Data.Maybe (fromJust)
+import qualified Network.Socket as Net
+import System.IO.Unsafe (unsafeDupablePerformIO)
+
+import Database.PostgreSQL.Typed.Types
+
+data PGInet 
+  = PGInet
+    { pgInetAddr :: !Net.HostAddress
+    , pgInetMask :: !Int
+    }
+  | PGInet6
+    { pgInetAddr6 :: !Net.HostAddress6
+    , pgInetMask :: !Int
+    }
+
+sockAddrPGInet :: Net.SockAddr -> Maybe PGInet
+sockAddrPGInet (Net.SockAddrInet _ a) = Just $ PGInet a 32
+sockAddrPGInet (Net.SockAddrInet6 _ _ a _) = Just $ PGInet6 a 128
+sockAddrPGInet _ = Nothing
+
+instance Show PGInet where
+  -- This is how Network.Socket's Show SockAddr does it:
+  show (PGInet a 32) = unsafeDupablePerformIO $ Net.inet_ntoa a
+  show (PGInet a m) = show (PGInet a 32) ++ '/' : show m
+  show (PGInet6 a 128) = fromJust $ fst $ unsafeDupablePerformIO $
+    Net.getNameInfo [Net.NI_NUMERICHOST] True False (Net.SockAddrInet6 0 0 a 0)
+  show (PGInet6 a m) = show (PGInet6 a 128) ++ '/' : show m
+
+instance PGType "inet"
+instance PGType "cidr"
+instance PGParameter "inet" PGInet where
+  pgEncode _ = BSC.pack . show
+instance PGParameter "cidr" PGInet where
+  pgEncode _ = BSC.pack . show
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
@@ -540,14 +540,14 @@
   pgFlush h
   go start where 
   go = (pgReceive h >>=)
-  start (RowDescription rd) = go (row (map colBinary rd))
-  start (CommandComplete c) = got c
+  start (RowDescription rd) = go $ row (map colBinary rd) id
+  start (CommandComplete c) = got c []
   start EmptyQueryResponse = return (0, [])
   start m = fail $ "pgSimpleQuery: unexpected response: " ++ show m
-  row bc (DataRow fs) = second (fixBinary bc fs :) <$> go (row bc)
-  row _ (CommandComplete c) = got c
-  row _ m = fail $ "pgSimpleQuery: unexpected row: " ++ show m
-  got c = return (rowsAffected c, [])
+  row bc r (DataRow fs) = go $ row bc (r . (fixBinary bc fs :))
+  row _ r (CommandComplete c) = got c (r [])
+  row _ _ m = fail $ "pgSimpleQuery: unexpected row: " ++ show m
+  got c r = return (rowsAffected c, r)
 
 pgPreparedBind :: PGConnection -> String -> [OID] -> PGValues -> [Bool] -> IO (IO ())
 pgPreparedBind c@PGConnection{ connPreparedStatements = psr } sql types bind bc = do
@@ -583,13 +583,13 @@
   pgSend c Sync
   pgFlush c
   start
-  go
+  go id
   where
-  go = pgReceive c >>= row
-  row (DataRow fs) = second (fixBinary bc fs :) <$> go
-  row (CommandComplete r) = return (rowsAffected r, [])
-  row EmptyQueryResponse = return (0, [])
-  row m = fail $ "pgPreparedQuery: unexpected row: " ++ show m
+  go r = pgReceive c >>= row r
+  row r (DataRow fs) = go (r . (fixBinary bc fs :))
+  row r (CommandComplete d) = return (rowsAffected d, r [])
+  row r EmptyQueryResponse = return (0, r [])
+  row _ m = fail $ "pgPreparedQuery: unexpected row: " ++ show m
 
 -- |Like 'pgPreparedQuery' but requests results lazily in chunks of the given size.
 -- Does not use a named portal, so other requests may not intervene.
@@ -600,18 +600,18 @@
   unsafeInterleaveIO $ do
     execute
     start
-    go
+    go id
   where
   execute = do
     pgSend c $ Execute count
     pgSend c $ Flush
     pgFlush c
-  go = pgReceive c >>= row
-  row (DataRow fs) = (fixBinary bc fs :) <$> go
-  row PortalSuspended = unsafeInterleaveIO (execute >> go)
-  row (CommandComplete _) = return []
-  row EmptyQueryResponse = return []
-  row m = fail $ "pgPreparedLazyQuery: unexpected row: " ++ show m
+  go r = pgReceive c >>= row r
+  row r (DataRow fs) = go (r . (fixBinary bc fs :))
+  row r PortalSuspended = r <$> unsafeInterleaveIO (execute >> go id)
+  row r (CommandComplete _) = return (r [])
+  row r EmptyQueryResponse = return (r [])
+  row _ m = fail $ "pgPreparedLazyQuery: unexpected row: " ++ show m
 
 -- |Close a previously prepared query (if necessary).
 pgCloseStatement :: PGConnection -> String -> [OID] -> IO ()
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
@@ -7,6 +7,7 @@
   , rawPGPreparedQuery
   , QueryFlags(..)
   , simpleQueryFlags
+  , parseQueryFlags
   , makePGQuery
   , pgSQL
   , pgExecute
@@ -20,8 +21,10 @@
 import Control.Monad (when, mapAndUnzipM)
 import Data.Array (listArray, (!), inRange)
 import Data.Char (isDigit, isSpace)
+import qualified Data.Foldable as Fold
 import Data.List (dropWhileEnd)
 import Data.Maybe (fromMaybe, isNothing)
+import Data.String (IsString(..))
 import Data.Word (Word32)
 import Language.Haskell.Meta.Parse (parseExp)
 import qualified Language.Haskell.TH as TH
@@ -37,6 +40,10 @@
 class PGQuery q a | q -> a where
   -- |Execute a query and return the number of rows affected (or -1 if not known) and a list of results.
   pgRunQuery :: PGConnection -> q -> IO (Int, [a])
+  -- |Change the raw SQL query stored within this query.
+  -- This is unsafe because the query has already been type-checked, so any change must not change the number or type of results or placeholders (so adding additional static WHERE or ORDER BY clauses is generally safe).
+  -- This is useful in cases where you need to construct some part of the query dynamically, but still want to infer the result types.
+  unsafeModifyQuery :: q -> (String -> String) -> q
 class PGQuery q PGValues => PGRawQuery q
 
 -- |Execute a query that does not return results.
@@ -48,22 +55,27 @@
 pgQuery :: PGQuery q a => PGConnection -> q -> IO [a]
 pgQuery c q = snd <$> pgRunQuery c q
 
+instance PGQuery String PGValues where
+  pgRunQuery c sql = pgSimpleQuery c sql
+  unsafeModifyQuery q f = f q
 
-data SimpleQuery = SimpleQuery String
+newtype SimpleQuery = SimpleQuery String
 instance PGQuery SimpleQuery PGValues where
   pgRunQuery c (SimpleQuery sql) = pgSimpleQuery c sql
-instance PGRawQuery SimpleQuery where
-
+  unsafeModifyQuery (SimpleQuery sql) f = SimpleQuery $ f sql
+instance PGRawQuery SimpleQuery
 
 data PreparedQuery = PreparedQuery String [OID] PGValues [Bool]
 instance PGQuery PreparedQuery PGValues where
   pgRunQuery c (PreparedQuery sql types bind bc) = pgPreparedQuery c sql types bind bc
-instance PGRawQuery PreparedQuery where
+  unsafeModifyQuery (PreparedQuery sql types bind bc) f = PreparedQuery (f sql) types bind bc
+instance PGRawQuery PreparedQuery
 
 
 data QueryParser q a = QueryParser (PGTypeEnv -> q) (PGTypeEnv -> PGValues -> a)
 instance PGRawQuery q => PGQuery (QueryParser q a) a where
   pgRunQuery c (QueryParser q p) = second (fmap $ p e) <$> pgRunQuery c (q e) where e = pgTypeEnv c
+  unsafeModifyQuery (QueryParser q p) f = QueryParser (\e -> unsafeModifyQuery (q e) f) p
 
 instance Functor (QueryParser q) where
   fmap f (QueryParser q p) = QueryParser q (\e -> f . p e)
@@ -80,6 +92,9 @@
 rawPGSimpleQuery :: String -> PGSimpleQuery PGValues
 rawPGSimpleQuery = rawParser . SimpleQuery
 
+instance IsString (PGSimpleQuery PGValues) where
+  fromString = rawPGSimpleQuery
+
 -- |Make a prepared query directly from a query string and bind parameters, with no type inference
 rawPGPreparedQuery :: String -> PGValues -> PGPreparedQuery PGValues
 rawPGPreparedQuery sql bind = rawParser $ PreparedQuery sql [] bind []
@@ -133,20 +148,20 @@
 
 -- |Flags affecting how and what type of query to build with 'makePGQuery'.
 data QueryFlags = QueryFlags
-  { flagQuery :: Bool -- ^ Create a query -- otherwise just call 'pgSubstituteLiterals' to create a string (SQL fragment)
-  , flagNullable :: Bool -- ^ Assume all results are nullable and don't try to guess.
+  { flagQuery :: Bool -- ^ Create a query -- otherwise just call 'pgSubstituteLiterals' to create a string (SQL fragment).
+  , flagNullable :: Maybe Bool -- ^ Disable nullability inference, treating all values as nullable (if 'True') or not (if 'False').
   , flagPrepare :: Maybe [String] -- ^ Prepare and re-use query, binding parameters of the given types (inferring the rest, like PREPARE).
   }
 
 -- |'QueryFlags' for a default (simple) query.
 simpleQueryFlags :: QueryFlags
-simpleQueryFlags = QueryFlags True False Nothing
+simpleQueryFlags = QueryFlags True Nothing Nothing
 
 -- |Construct a 'PGQuery' from a SQL string.
 makePGQuery :: QueryFlags -> String -> TH.ExpQ
 makePGQuery QueryFlags{ flagQuery = False } sqle = pgSubstituteLiterals sqle
 makePGQuery QueryFlags{ flagNullable = nulls, flagPrepare = prep } sqle = do
-  (pt, rt) <- TH.runIO $ tpgDescribe sqlp (fromMaybe [] prep) (not nulls)
+  (pt, rt) <- TH.runIO $ tpgDescribe sqlp (fromMaybe [] prep) (isNothing nulls)
   when (length pt < length exprs) $ fail "Not all expression placeholders were recognized by PostgreSQL; literal occurrences of '${' may need to be escaped with '$${'"
 
   e <- TH.newName "_tenv"
@@ -160,7 +175,7 @@
     v <- TH.newName $ 'c':tpgValueName t
     return 
       ( TH.VarP v
-      , tpgTypeDecoder t e `TH.AppE` TH.VarE v
+      , tpgTypeDecoder (Fold.and nulls) t e `TH.AppE` TH.VarE v
       , tpgTypeBinary t e
       )) rt
   foldl TH.AppE (TH.LamE vars $ TH.ConE 'QueryParser
@@ -184,17 +199,23 @@
   (sqlp, exprs) = sqlPlaceholders sqle
   parse e = either (fail . (++) ("Failed to parse expression {" ++ e ++ "}: ")) return $ parseExp e
 
-qqQuery :: QueryFlags -> String -> TH.ExpQ
-qqQuery f@QueryFlags{ flagQuery = True, flagPrepare = Nothing } ('#':q) = qqQuery f{ flagQuery = False } q
-qqQuery f@QueryFlags{ flagQuery = True, flagNullable = False } ('?':q) = qqQuery f{ flagNullable = True } q
-qqQuery f@QueryFlags{ flagQuery = True, flagPrepare = Nothing } ('$':q) = qqQuery f{ flagPrepare = Just [] } q
-qqQuery f@QueryFlags{ flagQuery = True, flagPrepare = Just [] } ('(':s) = qqQuery f{ flagPrepare = Just args } =<< sql r where
-  args = map trim $ splitCommas arg
-  (arg, r) = break (')' ==) s
-  sql (')':q) = return q
-  sql _ = fail "pgSQL: unterminated argument list" 
-qqQuery f q = makePGQuery f q
+-- |Parse flags off the beginning of a query string, returning the flags and the remaining string.
+parseQueryFlags :: String -> (QueryFlags, String)
+parseQueryFlags = pqf simpleQueryFlags where
+  pqf f@QueryFlags{ flagQuery = True, flagPrepare = Nothing } ('#':q) = pqf f{ flagQuery = False } q
+  pqf f@QueryFlags{ flagQuery = True, flagNullable = Nothing } ('?':q) = pqf f{ flagNullable = Just True } q
+  pqf f@QueryFlags{ flagQuery = True, flagNullable = Nothing } ('!':q) = pqf f{ flagNullable = Just False } q
+  pqf f@QueryFlags{ flagQuery = True, flagPrepare = Nothing } ('$':q) = pqf f{ flagPrepare = Just [] } q
+  pqf f@QueryFlags{ flagQuery = True, flagPrepare = Just [] } ('(':s) = pqf f{ flagPrepare = Just args } (sql r) where
+    args = map trim $ splitCommas arg
+    (arg, r) = break (')' ==) s
+    sql (')':q) = q
+    sql _ = error "pgSQL: unterminated argument list" 
+  pqf f q = (f, q)
 
+qqQuery :: String -> TH.ExpQ
+qqQuery = uncurry makePGQuery . parseQueryFlags
+
 qqTop :: Bool -> String -> TH.DecsQ
 qqTop True ('!':sql) = qqTop False sql
 qqTop err sql = do
@@ -215,7 +236,8 @@
 --
 -- The statement may start with one of more special flags affecting the interpretation:
 --
--- [@?@] To disable nullability inference, treating all result values as nullable, thus returning 'Maybe' values regardless of inferred nullability.
+-- [@?@] To disable nullability inference, treating all result values as nullable, thus returning 'Maybe' values regardless of inferred nullability. This makes unexpected NULL errors impossible.
+-- [@!@] To disable nullability inference, treating all result values as /not/ nullable, thus only returning 'Maybe' where requested. This is makes unexpected NULL errors more likely.
 -- [@$@] To create a 'PGPreparedQuery' rather than a 'PGSimpleQuery', by default inferring parameter types.
 -- [@$(type,...)@] To specify specific types to a prepared query (see <http://www.postgresql.org/docs/current/static/sql-prepare.html> for details).
 -- [@#@] Only do literal @${}@ substitution using 'pgSubstituteLiterals' and return a string, not a query.
@@ -224,7 +246,7 @@
 -- Here the query can only be prefixed with @!@ to make errors non-fatal.
 pgSQL :: QuasiQuoter
 pgSQL = QuasiQuoter
-  { quoteExp = qqQuery simpleQueryFlags
+  { quoteExp = qqQuery
   , quoteType = const $ fail "pgSQL not supported in types"
   , quotePat = const $ fail "pgSQL not supported in patterns"
   , quoteDec = qqTop True
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
@@ -167,12 +167,11 @@
     pb (Just b) = pgDQuote "(),[]" $ pgEncode (pgRangeElementType tr) b
     pc c o b = BSB.char7 $ if boundClosed b then c else o
 instance (PGRangeType tr t, PGColumn t a) => PGColumn tr (Range a) where
-  pgDecode tr = either (error . ("pgDecode range: " ++) . show) id . P.parse per "range" where
+  pgDecode tr a = either (error . ("pgDecode range: " ++) . show) id $ P.parse per (BSC.unpack a) a where
     per = Empty <$ pe P.<|> pr
     pe = P.oneOf "Ee" >> P.oneOf "Mm" >> P.oneOf "Pp" >> P.oneOf "Tt" >> P.oneOf "Yy"
-    pp = pgDecode (pgRangeElementType tr) . BSC.pack <$> parsePGDQuote "(),[]"
+    pb = fmap (pgDecode (pgRangeElementType tr) . BSC.pack) <$> parsePGDQuote "(),[]" null
     pc c o = True <$ P.char c P.<|> False <$ P.char o
-    pb = P.optionMaybe $ P.between P.spaces P.spaces $ pp
     mb = maybe Unbounded . Bounded
     pr = do
       lc <- pc '[' '('
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
@@ -169,9 +169,9 @@
     else 'pgEncodeParameter
 
 -- |TH expression to decode a 'Maybe' 'L.ByteString' to a ('Maybe') 'PGColumn' value.
-tpgTypeDecoder :: TPGValueInfo -> TH.Name -> TH.Exp
-tpgTypeDecoder v = typeApply (tpgValueType v) $
-  if tpgValueNullable v
+tpgTypeDecoder :: Bool -> TPGValueInfo -> TH.Name -> TH.Exp
+tpgTypeDecoder nulls v = typeApply (tpgValueType v) $
+  if nulls && tpgValueNullable v
     then 'pgDecodeColumn
     else 'pgDecodeColumnNotNull
 
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
@@ -14,6 +14,7 @@
   , PGTypeName(..)
   , PGTypeEnv(..)
   , unknownPGTypeEnv
+  , PGRecord(..)
 
   -- * Marshalling classes
   , PGType(..)
@@ -192,11 +193,14 @@
   bs = BSBP.liftFixedToBounded $ ((,) '\\') BSBP.>$< (BSBP.char7 BSBP.>*< BSBP.word8)
 
 -- |Parse double-quoted values ala 'pgDQuote'.
-parsePGDQuote :: P.Stream s m Char => String -> P.ParsecT s u m String
-parsePGDQuote unsafe = (q P.<|> uq) where
+parsePGDQuote :: P.Stream s m Char => String -> (String -> Bool) -> P.ParsecT s u m (Maybe String)
+parsePGDQuote unsafe isnul = (Just <$> q P.<|> mnul <$> uq) where
   q = P.between (P.char '"') (P.char '"') $
     P.many $ (P.char '\\' >> P.anyChar) P.<|> P.noneOf "\\\""
-  uq = P.many1 (P.noneOf ('"':'\\':unsafe))
+  uq = P.many (P.noneOf ('"':'\\':unsafe))
+  mnul s
+    | isnul s = Nothing
+    | otherwise = Just s
 
 #ifdef USE_BINARY
 binDec :: PGType t => BinD.D a -> PGTypeName t -> PGBinaryValue -> a
@@ -455,7 +459,7 @@
 -- PostgreSQL stores months and days separately in intervals, but DiffTime does not.
 -- We collapse all interval fields into seconds
 instance PGColumn "interval" Time.DiffTime where
-  pgDecode _ = either (error . ("pgDecode interval: " ++) . show) id . P.parse ps "interval" where
+  pgDecode _ a = either (error . ("pgDecode interval: " ++) . show) id $ P.parse ps (BSC.unpack a) a where
     ps = do
       _ <- P.char 'P'
       d <- units [('Y', 12*month), ('M', month), ('W', 7*day), ('D', day)]
@@ -540,21 +544,21 @@
 #endif
 
 -- |Generic class of composite (row or record) types.
+newtype PGRecord = PGRecord [Maybe PGTextValue]
 class PGType t => PGRecordType t
-instance PGRecordType t => PGParameter t [Maybe PGTextValue] where
-  pgEncode _ l =
+instance PGRecordType t => PGParameter t PGRecord where
+  pgEncode _ (PGRecord l) =
     buildPGValue $ BSB.char7 '(' <> mconcat (intersperse (BSB.char7 ',') $ map (maybe mempty (pgDQuote "(),")) l) <> BSB.char7 ')' where
-  pgLiteral _ l =
+  pgLiteral _ (PGRecord l) =
     "ROW(" ++ intercalate "," (map (maybe "NULL" (pgQuote . BSU.toString)) l) ++ ")" where
-instance PGRecordType t => PGColumn t [Maybe PGTextValue] where
-  pgDecode _ = either (error . ("pgDecode record: " ++) . show) id . P.parse pa "record" where
+instance PGRecordType t => PGColumn t PGRecord where
+  pgDecode _ a = either (error . ("pgDecode record: " ++) . show) PGRecord $ P.parse pa (BSC.unpack a) a where
     pa = do
       l <- P.between (P.char '(') (P.char ')') $
-        P.sepBy nel (P.char ',')
+        P.sepBy el (P.char ',')
       _ <- P.eof
       return l
-    nel = P.optionMaybe $ P.between P.spaces P.spaces el
-    el = BSC.pack <$> parsePGDQuote "(),"
+    el = fmap BSC.pack <$> parsePGDQuote "()," null
 
 instance PGType "record"
 -- |The generic anonymous record type, as created by @ROW@.
diff --git a/postgresql-typed.cabal b/postgresql-typed.cabal
--- a/postgresql-typed.cabal
+++ b/postgresql-typed.cabal
@@ -1,11 +1,11 @@
 Name:          postgresql-typed
-Version:       0.3.1
+Version:       0.3.2
 Cabal-Version: >= 1.8
 License:       BSD3
 License-File:  COPYING
 Copyright:     2010-2013 Chris Forno, 2014-2015 Dylan Simon
 Author:        Dylan Simon
-Maintainer:    dylan@dylex.net
+Maintainer:    Dylan Simon <dylan-pgtyped@dylex.net>
 Stability:     alpha
 Bug-Reports:   https://github.com/dylex/postgresql-typed/issues
 Homepage:      https://github.com/dylex/postgresql-typed
@@ -46,7 +46,11 @@
 Library
   Build-Depends:
     base >= 4.7 && < 5,
-    array, binary, containers, old-locale, time,
+    array,
+    binary,
+    containers < 0.5.6,
+    old-locale,
+    time < 1.5,
     bytestring >= 0.10.2,
     template-haskell,
     haskell-src-meta,
@@ -62,6 +66,7 @@
     Database.PostgreSQL.Typed.Enum
     Database.PostgreSQL.Typed.Array
     Database.PostgreSQL.Typed.Range
+    Database.PostgreSQL.Typed.Inet
     Database.PostgreSQL.Typed.Dynamic
     Database.PostgreSQL.Typed.TemplatePG
   Other-Modules:
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, DataKinds #-}
+{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, DataKinds, DeriveDataTypeable #-}
 -- {-# OPTIONS_GHC -ddump-splices #-}
 module Main (main) where
 
@@ -45,7 +45,7 @@
       d = Time.localDay t
       p = -34881559 :: Time.DiffTime
       s = "\"hel\\o'"
-      l = [Just "a\\\"b,c", Nothing]
+      l = [Just "a\\\"b,c", Nothing, Just "null", Just "nullish"]
       r = Range.normal (Just (-2 :: Int32)) Nothing
       e = MyEnum_XX_ye
   [(Just b', Just i', Just f', Just s', Just d', Just t', Just z', Just p', Just l', Just r', Just e')] <- pgQuery c
@@ -60,6 +60,8 @@
   ["line"] <- preparedApply c 628
 
   assert $ [pgSQL|#abc${f}def|] == "abc3.14::realdef"
+
+  assert $ pgEnumValues == [(MyEnum_abc, "abc"), (MyEnum_DEF, "DEF"), (MyEnum_XX_ye, "XX_ye")]
 
   pgDisconnect c
   exitSuccess
