diff --git a/Database/PostgreSQL/Devel.hs b/Database/PostgreSQL/Devel.hs
--- a/Database/PostgreSQL/Devel.hs
+++ b/Database/PostgreSQL/Devel.hs
@@ -111,8 +111,8 @@
     "pg_ctl -D " ++ showCommandForUser dir' [] ++ " stop -m immediate\n\n"
   version <- readFile (dir </> "PG_VERSION")
   case reads version of
-    [(v, _)] | v < (9.3 :: Double) -> configLocalDB dir $ pgDirectives92 dir
-    _                                -> configLocalDB dir $ pgDirectives dir
+    [(v, _)] | v < (9.3 :: Double) -> configLocalDB dir $ pgDirectives92 dir'
+    _                              -> configLocalDB dir $ pgDirectives dir'
 
 systemNoStdout :: String -> [String] -> IO ExitCode
 systemNoStdout prog args =
diff --git a/Database/PostgreSQL/Escape.hs b/Database/PostgreSQL/Escape.hs
--- a/Database/PostgreSQL/Escape.hs
+++ b/Database/PostgreSQL/Escape.hs
@@ -3,10 +3,13 @@
 {-# LANGUAGE MagicHash #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE UnboxedTuples #-}
+{-# LANGUAGE CPP #-}
 
+#include "MachDeps.h"
+
 -- | This module deals with escaping and sanitizing SQL templates.
 module Database.PostgreSQL.Escape (
-    fmtSql, quoteIdent, Id(..)
+    fmtSql, quoteIdent
   , buildSql, buildSqlFromActions
   , buildAction, buildLiteral, buildByteA, buildIdent
   ) where
@@ -18,7 +21,6 @@
 import qualified Data.ByteString.Internal as S
 import qualified Data.ByteString.Unsafe as S
 import Data.Monoid
-import Data.String
 import Database.PostgreSQL.Simple
 import Database.PostgreSQL.Simple.ToField
 import Database.PostgreSQL.Simple.ToRow
@@ -26,7 +28,7 @@
 import Foreign.Marshal.Alloc (mallocBytes)
 import Foreign.Storable (pokeByteOff)
 import Foreign.Ptr
-import GHC.Prim (Addr#, and#, geAddr#, geWord#, int2Word#
+import GHC.Prim (Addr#, and#, geAddr#, geWord#, Int#, int2Word#
                 , minusAddr#, ord# , plusAddr#, readWord8OffAddr#
                 , State# , uncheckedShiftRL#, word2Int#, writeWord8OffAddr#
                 , Word#)
@@ -35,6 +37,20 @@
 import GHC.Word (Word8(W8#))
 import System.IO.Unsafe (unsafeDupablePerformIO)
 
+{-# INLINE cmpres #-}
+-- | Newer versions of GHC return an Int# instead of a Bool for
+-- primitive comparison functions.  The @cmpres@ function converts the
+-- result of such a comparison to a @Bool@.
+#if __GLASGOW_HASKELL__ >= 707
+cmpres :: Int# -> Bool
+cmpres 0# = False
+cmpres _ = True
+#else /* __GLASGOW_HASKELL__ < 707 */
+cmpres :: Bool -> Bool
+cmpres b = b
+#define cmpres(b) b
+#endif /* __GLASGOW_HASKELL__ < 707 */
+
 c2b :: Char -> Word8
 c2b (C# i) = W8# (int2Word# (ord# i))
 
@@ -48,7 +64,7 @@
     let bse = bsp0 `plusAddr#` bsl0
         check bsp = IO $ \rw -> case readWord8OffAddr# bsp 0# rw of
           (# rw1, w #) -> (# rw1, test w #)
-        go bsp | bsp `geAddr#` bse = return Nothing
+        go bsp | cmpres(bsp `geAddr#` bse) = return Nothing
                | otherwise = do
                  match <- check bsp
                  if match
@@ -136,18 +152,6 @@
 quoteIdent :: S.ByteString -> S.ByteString
 quoteIdent = toByteString . buildIdent
 
--- | An identifier is a table or column name.  When rendered into a
--- SQL query by 'fmtSql', it will be double-quoted, rather than
--- single-quoted.  For example:
---
--- >>> fmtSql "select * from ? where name = ?" (Id "MyTable", "A Name")
--- "select * from \"MyTable\" where name =  E'A Name'"
-newtype Id = Id S.ByteString deriving Show
-instance IsString Id where
-  fromString = Id . fromString
-instance ToField Id where
-  toField (Id name) = Plain $ buildIdent name
-
 hexNibblesPtr :: Ptr Word8
 {-# NOINLINE hexNibblesPtr #-}
 hexNibblesPtr = unsafeDupablePerformIO $ do
@@ -181,7 +185,7 @@
   where isSpecial 39## = True   -- '\''
         isSpecial 63## = True   -- '?'
         isSpecial 92## = True   -- '\\'
-        isSpecial b    = b `geWord#` 128##
+        isSpecial b    = cmpres(b `geWord#` 128##)
         esc b | b == c2b '\'' = copyByteString "''"
               | b == c2b '\\' = copyByteString "\\\\"
               | otherwise     = hexCharEscBuilder b
@@ -199,8 +203,8 @@
   S.unsafeUseAsCStringLen bs $ \(Ptr inptr0, I# inlen0) -> do
   let ine = plusAddr# inptr0 inlen0
       fill oute inp outp
-        | inp `geAddr#` ine = cont (BufRange (Ptr outp) (Ptr oute))
-        | plusAddr# outp 2# `geAddr#` oute = return $
+        | cmpres(inp `geAddr#` ine) = cont (BufRange (Ptr outp) (Ptr oute))
+        | cmpres(plusAddr# outp 2# `geAddr#` oute) = return $
             bufferFull (2 * (I# (ine `minusAddr#` inp)) + 1) (Ptr outp) $
             \(BufRange (Ptr bb) (Ptr be)) -> fill be inp bb
         | otherwise = do copyByteToNibbles inp outp
@@ -211,10 +215,11 @@
 
 
 buildAction :: Action -> Builder
-buildAction (Plain b)        = b
-buildAction (Escape bs)      = buildLiteral bs
-buildAction (EscapeByteA bs) = buildByteA bs
-buildAction (Many bs)        = mconcat $ map buildAction bs
+buildAction (Plain b)             = b
+buildAction (Escape bs)           = buildLiteral bs
+buildAction (EscapeByteA bs)      = buildByteA bs
+buildAction (EscapeIdentifier bs) = buildIdent bs
+buildAction (Many bs)             = mconcat $ map buildAction bs
 
 -- | A lower-level function used by 'buildSql' and 'fmtSql'.  You
 -- probably don't need to call it directly.
diff --git a/Database/PostgreSQL/Migrate.hs b/Database/PostgreSQL/Migrate.hs
--- a/Database/PostgreSQL/Migrate.hs
+++ b/Database/PostgreSQL/Migrate.hs
@@ -23,7 +23,6 @@
 import Database.PostgreSQL.Migrations
 import System.Exit 
 import GHC.IO.Handle
-import System.Cmd
 import System.Process
 import System.Directory
 import System.FilePath
diff --git a/Database/PostgreSQL/ORM/DBSelect.hs b/Database/PostgreSQL/ORM/DBSelect.hs
--- a/Database/PostgreSQL/ORM/DBSelect.hs
+++ b/Database/PostgreSQL/ORM/DBSelect.hs
@@ -13,6 +13,7 @@
   , dbSelectParams, dbSelect
   , Cursor(..), curSelect, curNext
   , dbFold, dbFoldM, dbFoldM_
+  , dbCollect
   , renderDBSelect, buildDBSelect
     -- * Creating DBSelects
   , emptyDBSelect, expressionDBSelect
@@ -283,14 +284,14 @@
 dbSelectParams :: (Model a, ToRow p) => DBSelect a -> Connection -> p -> IO [a]
 {-# INLINE dbSelectParams #-}
 dbSelectParams dbs = \c p -> map lookupRow <$> query c q p
-  where {-# NOINLINE q #-}
+  where -- {-# NOINLINE q #-} (crashes under GHC 7.8)
         q = renderDBSelect dbs
 
 -- | Run a 'DBSelect' query and return the resulting models.
 dbSelect :: (Model a) => Connection -> DBSelect a -> IO [a]
 {-# INLINE dbSelect #-}
 dbSelect c dbs = map lookupRow <$> query_ c q
-  where {-# NOINLINE q #-}
+  where -- {-# NOINLINE q #-} (crashes under GHC 7.8)
         q = renderDBSelect dbs
 
 -- | Datatype that represents a connected cursor
@@ -360,6 +361,18 @@
 dbFoldM_ :: (MonadIO m, Model model)
          => Connection -> (model -> m ()) -> DBSelect model -> m ()
 dbFoldM_ c act dbs = dbFoldM c (const act) () dbs
+
+-- | Group the returned tuples by unique a's. Expects the query to return a's
+-- in sequence -- all rows with the same value for a must be grouped together,
+-- for example, by sorting the result on a's primary key column.
+dbCollect :: (Model a, Model b)
+           => Connection -> DBSelect (a :. b) -> IO [(a, [b])]
+dbCollect c ab = dbFold c group [] ab
+  where
+    group :: (Model a, Model b) => [(a, [b])] -> (a :. b) -> [(a, [b])]
+    group    []     (a :. b) = [(a, [b])]
+    group ls@(l:_)  (a :. b) | primaryKey a /= primaryKey (fst l) = (a, [b]):ls
+    group    (l:ls) (_ :. b) = (fst l, b:(snd l)):ls
 
 -- | Create a join of the 'selFields', 'selFrom', and 'selWhere'
 -- clauses of two 'DBSelect' queries.  Other fields are simply taken
diff --git a/Database/PostgreSQL/ORM/Model.hs b/Database/PostgreSQL/ORM/Model.hs
--- a/Database/PostgreSQL/ORM/Model.hs
+++ b/Database/PostgreSQL/ORM/Model.hs
@@ -805,23 +805,64 @@
 degen_err :: a
 degen_err = error "Attempt to use degenerate ToRow instance as Model"
 #define DEGENERATE(ctx,t)             \
-instance ctx => Model t where         \
+instance ctx => Model t where {       \
   modelInfo = degen_err;              \
   modelIdentifiers = degen_err;       \
   modelRead = fromRow;                \
   modelWrite _ = degen_err;           \
-  modelCreateInfo = degen_err;
+  modelCreateInfo = degen_err; }
 
 DEGENERATE(FromField t, (Only t))
 DEGENERATE(FromField t, [t])
 DEGENERATE((FromField a, FromField b), (a, b))
 DEGENERATE((FromField a, FromField b, FromField c), (a, b, c))
 DEGENERATE((FromField a, FromField b, FromField c, FromField d), (a, b, c, d))
-DEGENERATE((FromField a, FromField b, FromField c, FromField d, FromField e), 
+DEGENERATE((FromField a, FromField b, FromField c, FromField d, FromField e),
            (a, b, c, d, e))
 
 #undef DEGEN_ERR
 #undef DEGENERATE
+
+-- | A degenerate model that lifts any model to a Maybe version. Returns
+-- 'Nothing' on a parse failure. Useful, for example, for performing outer
+-- joins:
+-- @
+-- dbJoin modelDBSelect "LEFT OUTER JOIN"
+--        (addWhere 'foo = 123' $ modelDBSelect)
+--        "USING a.id = b.a_id" :: (A :. Maybe B)
+-- @
+--
+instance forall a. Model a => Model (Maybe a) where
+  modelInfo = mi_a { modelGetPrimaryKey = getPrimaryKey }
+    where mi_a = modelInfo :: ModelInfo a
+          getPrimaryKey Nothing  = NullKey
+          getPrimaryKey (Just a) = modelGetPrimaryKey mi_a a
+
+  modelIdentifiers = mi_a { modelQTable = modelQTable mi_a }
+    where mi_a = modelIdentifiers :: ModelIdentifiers a
+
+  modelQueries = mi_a { modelLookupQuery = modelLookupQuery mi_a }
+    where mi_a = modelQueries :: ModelQueries a
+
+  modelCreateInfo = error
+    "Attempt to use degenerate Maybe (Model a) instance for ModelCreateInfo"
+
+  modelValid = maybe mempty modelValid
+
+  modelWrite = maybe [] modelWrite
+
+  modelRead =
+    Just `fmap` (modelRead :: RowParser a)
+    <|> do
+      let n = length $ modelColumns (modelInfo :: ModelInfo a)
+      replicateM_ n (field :: RowParser AnyField)
+      return Nothing
+
+-- | AnyField parses (simply by consuming) any SQL column.
+data AnyField = AnyField
+
+instance FromField AnyField where
+  fromField _ _ = pure AnyField
 
 joinModelIdentifiers :: forall a b. (Model a, Model b)
                      => ModelIdentifiers (a :. b)
diff --git a/postgresql-orm.cabal b/postgresql-orm.cabal
--- a/postgresql-orm.cabal
+++ b/postgresql-orm.cabal
@@ -1,5 +1,5 @@
 name: postgresql-orm
-version: 0.2.3
+version: 0.3.0
 cabal-version: >= 1.14
 build-type: Simple
 license: GPL
@@ -25,7 +25,7 @@
                , ghc-prim
                , mtl
                , old-locale
-               , postgresql-simple >= 0.3.2.0
+               , postgresql-simple >= 0.4.1.0
                , process
                , time
 
