diff --git a/haskelldb.cabal b/haskelldb.cabal
--- a/haskelldb.cabal
+++ b/haskelldb.cabal
@@ -1,20 +1,38 @@
 Name: haskelldb
-Version: 0.10
+Version: 0.12
+Cabal-version: >= 1.2
+Build-type: Simple
+Homepage: http://haskelldb.sourceforge.net
 Copyright: The authors
 Maintainer: haskelldb-users@lists.sourceforge.net
 Author: Daan Leijen, Conny Andersson, Martin Andersson, Mary Bergman, Victor Blomqvist, Bjorn Bringert, Anders Hockersten, Torbjorn Martin, Jeremy Shaw
 License: BSD3
-build-depends: haskell98, base, mtl
-Extensions: ExistentialQuantification,
-            OverlappingInstances,
-            UndecidableInstances,
-            MultiParamTypeClasses
 Synopsis: SQL unwrapper for Haskell.
-Exposed-Modules:
+
+Flag split-base
+
+Library
+  Build-depends: mtl
+  if flag(split-base)
+    Build-depends: base >= 3.0, pretty, old-time, old-locale, directory
+  else
+    Build-depends: base < 3.0
+  Extensions:
+    ExistentialQuantification,
+    OverlappingInstances,
+    UndecidableInstances,
+    MultiParamTypeClasses,
+    FunctionalDependencies,
+    TypeSynonymInstances,
+    FlexibleInstances,
+    FlexibleContexts,
+    PolymorphicComponents
+  Exposed-Modules:
         Database.HaskellDB,
         Database.HaskellDB.BoundedList,
         Database.HaskellDB.BoundedString,
         Database.HaskellDB.DBLayout,
+        Database.HaskellDB.DBDirect,
         Database.HaskellDB.DBSpec,
         Database.HaskellDB.DBSpec.DBInfo,
         Database.HaskellDB.DBSpec.DBSpecToDBDirect,
@@ -36,5 +54,5 @@
         Database.HaskellDB.Sql.SQLite,
         Database.HaskellDB.Version,
         Database.HaskellDB.DriverAPI
-hs-source-dirs: src
-ghc-options: -O2
+  Hs-source-dirs: src
+  ghc-options: -O2
diff --git a/src/Database/HaskellDB.hs b/src/Database/HaskellDB.hs
--- a/src/Database/HaskellDB.hs
+++ b/src/Database/HaskellDB.hs
@@ -45,7 +45,7 @@
 	, HasField, Record, Select, ( # ), ( << ), (<<-), (!), (!.)
 	
         -- * Relational operators
-	, restrict, table, project
+	, restrict, table, project, unique
 	, union, intersect, divide, minus
 
 	-- * Query expressions
diff --git a/src/Database/HaskellDB/BoundedList.hs b/src/Database/HaskellDB/BoundedList.hs
--- a/src/Database/HaskellDB/BoundedList.hs
+++ b/src/Database/HaskellDB/BoundedList.hs
@@ -1396,8 +1396,10 @@
 
 -- | Returns the length of a 'BoundedList'.
 listBound :: Size n => BoundedList a n -> Int
-listBound (_ :: BoundedList a n) = size (undefined :: n)
+listBound = size . listBoundType
 
+listBoundType :: BoundedList a n -> n
+listBoundType _ = undefined
 
 -- | Takes a list and transforms it to a 'BoundedList'.
 -- If the list doesn\'t fit, Nothing is returned.
diff --git a/src/Database/HaskellDB/DBDirect.hs b/src/Database/HaskellDB/DBDirect.hs
new file mode 100644
--- /dev/null
+++ b/src/Database/HaskellDB/DBDirect.hs
@@ -0,0 +1,150 @@
+-----------------------------------------------------------
+-- |
+-- Module      :  Database.HaskellDB.DBDirect
+-- Copyright   :  Daan Leijen (c) 1999, daan@cs.uu.nl
+--                HWT Group (c) 2003,
+--                Bjorn Bringert (c) 2005-2006, bjorn@bringert.net
+-- License     :  BSD-style
+--
+-- Maintainer  :  haskelldb-users@lists.sourceforge.net
+-- Stability   :  experimental
+-- Portability :  portable
+--
+-- DBDirect generates a Haskell module from a database.
+-- It first reads the system catalog of the database into
+-- a 'Catalog' data type. After that it pretty prints that
+-- data structure in an appropiate Haskell module which
+-- can be used to perform queries on the database.
+--
+-----------------------------------------------------------
+
+module Database.HaskellDB.DBDirect (dbdirect) where
+
+import Database.HaskellDB (Database, )
+import Database.HaskellDB.DriverAPI (DriverInterface, connect, requiredOptions, )
+import Database.HaskellDB.DBSpec (dbToDBSpec, )
+import Database.HaskellDB.DBSpec.DBSpecToDBDirect (dbInfoToModuleFiles, )
+
+import qualified Database.HaskellDB.DBSpec.PPHelpers as PP
+
+import System.Console.GetOpt (getOpt, ArgOrder(..), OptDescr(..), ArgDescr(..), usageInfo, )
+import System.Environment (getArgs, getProgName, )
+import System.Exit (exitFailure, )
+import System.IO (hPutStrLn, stderr, )
+
+import Control.Monad.Error () -- Monad instance for Either
+import Control.Monad (when, )
+import Data.List (intersperse, )
+
+
+createModules :: String -> Bool -> PP.MakeIdentifiers -> Database -> IO ()
+createModules m useBStrT mkIdent db =
+    do
+    putStrLn "Getting database info..."
+    spec <- dbToDBSpec useBStrT mkIdent m db
+    putStrLn "Writing modules..."
+    dbInfoToModuleFiles "." m spec
+
+
+data Flags =
+   Flags {
+      optHelp            :: Bool,
+      optBoundedStrings  :: Bool,
+      optIdentifierStyle :: PP.MakeIdentifiers
+   }
+
+options :: [OptDescr (Flags -> Either String Flags)]
+options =
+   Option ['h'] ["help"]
+      (NoArg (\flags -> Right $ flags{optHelp = True}))
+       "show options" :
+   Option ['b'] ["bounded-strings"]
+      (NoArg (\flags -> Right $ flags{optBoundedStrings = True}))
+       "use bounded string types" :
+   Option []    ["identifier-style"]
+      (ReqArg (\str flags ->
+          case str of
+             "preserve"   -> Right $ flags{optIdentifierStyle = PP.mkIdentPreserving}
+             "camel-case" -> Right $ flags{optIdentifierStyle = PP.mkIdentCamelCase}
+             _ -> Left $ "unknown identifier style: " ++ str)
+        "type")
+       "<type> is one of [preserve, camel-case]" :
+   []
+
+parseOptions ::
+   [Flags -> Either String Flags] -> Either String Flags
+parseOptions =
+   foldr (=<<)
+      (Right $
+       Flags {optHelp = False,
+              optBoundedStrings = False,
+              optIdentifierStyle = PP.mkIdentPreserving})
+
+exitWithError :: String -> IO a
+exitWithError msg =
+   hPutStrLn stderr msg >>
+   hPutStrLn stderr "Try --help option to get detailed info." >>
+   exitFailure
+
+dbdirect :: DriverInterface -> IO ()
+dbdirect driver =
+    do putStrLn "DB/Direct: Daan Leijen (c) 1999, HWT (c) 2003-2004,"
+       putStrLn "           Bjorn Bringert (c) 2005-2007, Henning Thielemann (c) 2008"
+       putStrLn ""
+
+       argv <- getArgs
+       let (opts, modAndDrvOpts, errors) = getOpt RequireOrder options argv
+       when (not (null errors))
+          (ioError . userError . concat $ errors)
+
+       flags <-
+          case parseOptions opts of
+             Left errMsg -> exitWithError errMsg
+             Right flags -> return flags
+
+       when (optHelp flags)
+          (showHelp driver >> exitFailure)
+
+       case modAndDrvOpts of
+          []  -> exitWithError "Missing module and driver options"
+          [_] -> exitWithError "Missing driver options"
+          [moduleName,drvOpts] ->
+              do putStrLn "Connecting to database..."
+                 connect driver
+                    (splitOptions drvOpts)
+                    (createModules moduleName
+                        (optBoundedStrings flags)
+                        (optIdentifierStyle flags))
+                 putStrLn "Done!"
+          (_:_:restArgs) ->
+              exitWithError ("Unnecessary arguments: " ++ show restArgs)
+
+
+
+splitOptions :: String -> [(String,String)]
+splitOptions = map (split2 '=') . split ','
+
+split :: Char -> String -> [String]
+split _ [] = []
+split g xs = y : split g ys
+  where (y,ys) = split2 g xs
+
+split2 :: Char -> String -> (String,String)
+split2 g xs = (ys, drop 1 zs)
+  where (ys,zs) = break (==g) xs
+
+-- | Shows usage information
+showHelp :: DriverInterface -> IO ()
+showHelp driver =
+   do p <- getProgName
+      let header =
+             "Usage: " ++ p ++ " [dbdirect-options] <module> <driver-options>\n"
+          footer = unlines $
+             "" :
+             "module:          Module name without an extension" :
+             ("driver-options:  " ++
+                (concat . intersperse "," .
+                 map (\(name,descr) -> name++"=<"++descr++">") .
+                 requiredOptions) driver) :
+             []
+      hPutStrLn stderr $ (usageInfo header options ++ footer)
diff --git a/src/Database/HaskellDB/DBSpec/DBInfo.hs b/src/Database/HaskellDB/DBSpec/DBInfo.hs
--- a/src/Database/HaskellDB/DBSpec/DBInfo.hs
+++ b/src/Database/HaskellDB/DBSpec/DBInfo.hs
@@ -20,31 +20,40 @@
      dbInfoToDoc,finalizeSpec,constructNonClashingDBInfo)
     where
 
-import Database.HaskellDB.FieldType
-import Data.Char
+import qualified Database.HaskellDB.DBSpec.PPHelpers as PP
+import Database.HaskellDB.FieldType (FieldDesc, FieldType(BStrT, StringT), )
+import Data.Char (toLower, )
 import Text.PrettyPrint.HughesPJ
 
 -- | Defines a database layout, top level
-data DBInfo = DBInfo {dbname :: String, -- ^ The name of the database
-		      opts :: DBOptions, -- ^ Any options (i.e whether to use
+data DBInfo = DBInfo {dbname :: String -- ^ The name of the database
+		     ,opts :: DBOptions  -- ^ Any options (i.e whether to use
 					 --   Bounded Strings)
-		      tbls :: [TInfo]    -- ^ Tables this database contains
+		     ,tbls :: [TInfo]    -- ^ Tables this database contains
 		     }
-	      deriving (Eq,Show)
+	      deriving (Show)
 
-data TInfo = TInfo {tname :: String, -- ^ The name of the table
-		    cols :: [CInfo]  -- ^ The columns in this table
+data TInfo = TInfo {tname :: String  -- ^ The name of the table
+		   ,cols :: [CInfo]  -- ^ The columns in this table
 		   }
 	      deriving (Eq,Show)
-data CInfo = CInfo {cname :: String, -- ^ The name of this column
-		    descr :: FieldDesc -- ^ The description of this column
+data CInfo = CInfo {cname :: String    -- ^ The name of this column
+		   ,descr :: FieldDesc -- ^ The description of this column
 		   }
 	     deriving (Eq,Show)
 
-data DBOptions = DBOptions {useBString :: Bool -- ^ Use Bounded Strings?
-			   }
-		 deriving (Eq,Show)
+data DBOptions = DBOptions
+		   {useBString :: Bool -- ^ Use Bounded Strings?
+		   ,makeIdent  :: PP.MakeIdentifiers -- ^ Conversion routines from Database identifiers to Haskell identifiers
+		   }
 
+instance Show DBOptions where
+   showsPrec p opts =
+      showString "DBOptions {useBString = " .
+      shows (useBString opts) .
+      showString "}"
+
+
 -- | Creates a valid declaration of a DBInfo. The variable name will be the
 --   same as the database name
 dbInfoToDoc :: DBInfo -> Doc
@@ -130,9 +139,15 @@
 -- | Constructs a DBInfo that doesn't cause nameclashes
 constructNonClashingDBInfo :: DBInfo -> DBInfo
 constructNonClashingDBInfo dbinfo = 
-    let db' = makeDBNameUnique dbinfo in 
-	if db' == makeDBNameUnique db' then db'
-	   else constructNonClashingDBInfo db'
+    let db' = makeDBNameUnique dbinfo
+    in  if equalObjectNames db' (makeDBNameUnique db')
+          then db'
+	  else constructNonClashingDBInfo db'
+
+equalObjectNames :: DBInfo -> DBInfo -> Bool
+equalObjectNames db1 db2 =
+   dbname db1 == dbname db2 &&
+   tbls db1 == tbls db2
 
 -- | Makes a table name unique among all other table names
 makeTblNamesUnique :: [TInfo] -> [TInfo]
diff --git a/src/Database/HaskellDB/DBSpec/DBSpecToDBDirect.hs b/src/Database/HaskellDB/DBSpec/DBSpecToDBDirect.hs
--- a/src/Database/HaskellDB/DBSpec/DBSpecToDBDirect.hs
+++ b/src/Database/HaskellDB/DBSpec/DBSpecToDBDirect.hs
@@ -3,29 +3,32 @@
 -- Module      :  DBSpecToDBDirect
 -- Copyright   :  HWT Group (c) 2004, haskelldb-users@lists.sourceforge.net
 -- License     :  BSD-style
--- 
+--
 -- Maintainer  :  haskelldb-users@lists.sourceforge.net
 -- Stability   :  experimental
 -- Portability :  non-portable
 --
 -- Converts a DBSpec-generated database to a set of
--- (FilePath,Doc), that can be used to generate definition 
--- files usable in HaskellDB (the generation itself is done 
+-- (FilePath,Doc), that can be used to generate definition
+-- files usable in HaskellDB (the generation itself is done
 -- in DBDirect)
 --
--- 
+--
 -----------------------------------------------------------
 module Database.HaskellDB.DBSpec.DBSpecToDBDirect
-    (specToHDB, dbInfoToModuleFiles) 
+    (specToHDB, dbInfoToModuleFiles)
     where
-import Database.HaskellDB.BoundedString
-import Database.HaskellDB.FieldType
 
-import Database.HaskellDB
-import Database.HaskellDB.PrimQuery
+import Database.HaskellDB.FieldType (toHaskellType, )
+
 import Database.HaskellDB.DBSpec.DBInfo
+   (TInfo(TInfo), CInfo(CInfo), DBInfo,
+    descr, cols, tname, cname, tbls, dbInfoToDoc, opts, makeIdent,
+    finalizeSpec, constructNonClashingDBInfo, )
 
 import Database.HaskellDB.DBSpec.PPHelpers
+   (MakeIdentifiers, moduleName, toType, identifier, checkChars,
+    ppComment, newline, )
 
 import Control.Monad (unless)
 import Data.List (isPrefixOf)
@@ -37,15 +40,14 @@
 header = ppComment ["Generated by DB/Direct"]
 
 -- | Adds an appropriate -fcontext-stackXX OPTIONS pragma at the top
---   of the generated file. Not currently in use since -fcontext-stackXX
---   is a static option, and thus can't be used in options pragmas.
+--   of the generated file.
 contextStackPragma :: TInfo -> Doc
 contextStackPragma ti
-      = text "-- NOTE: use GHC flag" <+> text flag <+> text "with this module"
---    = text "{-# OPTIONS" <+> text flag <+> text "#-}"
+    = text "{-# OPTIONS_GHC" <+> text flag <+> text "#-}" $$
+      text "-- NOTE: use GHC flag" <+> text flag <+> text "with this module if GHC < 6.8.1"
   where flag = "-fcontext-stack" ++ (show (40 + length (cols ti)))
 
--- | All imports generated files have dependencies on. Nowadays, this 
+-- | All imports generated files have dependencies on. Nowadays, this
 --   should only be Database.HaskellDB.DBLayout
 imports :: Doc
 imports = text "import Database.HaskellDB.DBLayout"
@@ -54,16 +56,16 @@
 dbInfoToModuleFiles :: FilePath -- ^ base directory
 		    -> String -- ^ top-level module name
 		    -> DBInfo -> IO ()
-dbInfoToModuleFiles d name = 
+dbInfoToModuleFiles d name =
     createModules d name . specToHDB name . finalizeSpec
 
--- | Creates modules 
+-- | Creates modules
 createModules :: FilePath -- ^ Base directory
 	      -> String   -- ^ Name of directory and top-level module for the database modules
 	      -> [(String,Doc)] -- ^ Module names and module contents
 	      -> IO ()
 createModules basedir dbname files
-    = do 
+    = do
       let dir = withPrefix basedir (replace '.' '/' dbname)
       createPath dir
       mapM_ (\ (name,doc) -> writeFile (moduleNameToFile basedir name)
@@ -99,7 +101,7 @@
 			 exists <- doesDirectoryExist p
 			 unless exists (createDirectory p)
 
--- | Converts a database specification to a set of module names 
+-- | Converts a database specification to a set of module names
 --   and module contents. The first element of the returned list
 --   is the top-level module.
 specToHDB :: String -- ^ Top level module name
@@ -108,9 +110,9 @@
 
 -- | Does the actual conversion work
 genDocs :: String -- ^ Top-level module name
-	-> DBInfo 
+	-> DBInfo
 	-> [(String,Doc)] -- ^ list of module name, module contents pairs
-genDocs name dbinfo 
+genDocs name dbinfo
     = (name,
        header
        $$ text "module" <+> text name <+> text "where"
@@ -122,15 +124,17 @@
        $$ dbInfoToDoc dbinfo)
         : rest
     where
-    rest = [tInfoToModule name t | t <- tbls dbinfo, hasName t]
+    rest = map (tInfoToModule (makeIdent (opts dbinfo)) name) $
+           filter hasName $ tbls dbinfo
     hasName TInfo{tname=name} = name /= ""
     tbnames = map fst rest
-      
+
 -- | Makes a module from a TInfo
-tInfoToModule :: String -- ^ The name of our main module
-	      -> TInfo 
+tInfoToModule :: MakeIdentifiers
+              -> String -- ^ The name of our main module
+	      -> TInfo
 	      -> (String,Doc) -- ^ Module name and module contents
-tInfoToModule dbname tinfo@TInfo{tname=name,cols=col}
+tInfoToModule mi dbname tinfo@TInfo{tname=name,cols=col}
     = (modname,
        contextStackPragma tinfo $$
        header
@@ -138,67 +142,78 @@
        <> newline
        $$ imports
        <> newline
+       $$ ppComment ["Table type"]
+       <> newline
+       $$ ppTableType mi tinfo
+       <> newline
        $$ ppComment ["Table"]
-       $$ ppTable tinfo       
+       $$ ppTable mi tinfo
        $$ ppComment ["Fields"]
-       $$ if col == [] then empty -- no fields, don't do any weird shit
-             else vcat (map ppField (columnNamesTypes tinfo)))
-    where modname = dbname ++ "." ++ moduleName name
+       $$ if null col
+             then empty -- no fields, don't do anything weird
+             else vcat (map (ppField mi) (columnNamesTypes tinfo)))
+    where modname = dbname ++ "." ++ moduleName mi name
 
+ppTableType :: MakeIdentifiers -> TInfo -> Doc
+ppTableType mi (TInfo { tname = tiName, cols = tiColumns }) =
+    hang decl 4 types
+  where
+    decl = text "type" <+> text (toType mi tiName) <+> text "="
+    types = ppColumns mi tiColumns
+
 -- | Pretty prints a TableInfo
-ppTable :: TInfo -> Doc
-ppTable (TInfo tiName tiColumns) =  
-    hang (text (identifier tiName) <+> text "::" <+> text "Table") 4 
-	 (parens (ppColumns tiColumns)
-	 <>  newline)
-    $$  
-    text (identifier tiName) <+> text "=" <+> 
-    hang (text "baseTable" <+> 
-	  doubleQuotes (text (checkChars tiName)) <+> 
+ppTable :: MakeIdentifiers -> TInfo -> Doc
+ppTable mi (TInfo tiName tiColumns) =
+    hang (text (identifier mi tiName) <+> text "::" <+> text "Table") 4
+	 (text (toType mi tiName) <> newline)
+    $$
+    text (identifier mi tiName) <+> text "=" <+>
+    hang (text "baseTable" <+>
+	  doubleQuotes (text (checkChars tiName)) <+>
 	  text "$") 0
-	     (vcat $ punctuate (text " #") (map ppColumnValue tiColumns))
+	     (vcat $ punctuate (text " #") (map (ppColumnValue mi) tiColumns))
 	     <>  newline
 
 -- | Pretty prints a list of ColumnInfo
-ppColumns :: [CInfo] -> Doc
-ppColumns []      = text ""
-ppColumns [c]     = parens (ppColumnType c <+> text "RecNil")
-ppColumns (c:cs)  = parens (ppColumnType c $$ ppColumns cs)
+ppColumns :: MakeIdentifiers -> [CInfo] -> Doc
+ppColumns _  []      = text ""
+ppColumns mi [c]     = parens (ppColumnType mi c <+> text "RecNil")
+ppColumns mi (c:cs)  = parens (ppColumnType mi c $$ ppColumns mi cs)
 
--- | Pretty prints the type field in a ColumnInfo	
-ppColumnType :: CInfo -> Doc 
-ppColumnType (CInfo ciName (ciType,ciAllowNull))
-	=   text "RecCons" <+> 
-	    ((text $ toType ciName) <+> parens (text "Expr"
+-- | Pretty prints the type field in a ColumnInfo
+ppColumnType :: MakeIdentifiers -> CInfo -> Doc
+ppColumnType mi (CInfo ciName (ciType,ciAllowNull))
+	=   text "RecCons" <+>
+	    ((text $ toType mi ciName) <+> parens (text "Expr"
 	    <+> (if (ciAllowNull)
 	      then parens (text "Maybe" <+> text (toHaskellType ciType))
 	      else text (toHaskellType ciType)
 	    )))
 
 -- | Pretty prints the value field in a ColumnInfo
-ppColumnValue :: CInfo -> Doc 
-ppColumnValue (CInfo ciName _)
-	=   text "hdbMakeEntry" <+> text (toType ciName)
+ppColumnValue :: MakeIdentifiers -> CInfo -> Doc
+ppColumnValue mi (CInfo ciName _)
+	=   text "hdbMakeEntry" <+> text (toType mi ciName)
 
 -- | Pretty prints Field definitions
-ppField :: (String,String) -> Doc
-ppField (name,typeof) = 
-    ppComment [toType name ++ " Field"]
+ppField :: MakeIdentifiers -> (String,String) -> Doc
+ppField mi (name,typeof) =
+    ppComment [toType mi name ++ " Field"]
     <> newline $$
     text "data" <+> bname <+> equals <+> bname -- <+> text "deriving Show"
     <> newline $$
-    hang (text "instance FieldTag" <+> bname <+> text "where") 4 
-         (text "fieldName _" <+> equals <+> doubleQuotes 
+    hang (text "instance FieldTag" <+> bname <+> text "where") 4
+         (text "fieldName _" <+> equals <+> doubleQuotes
 	         (text (checkChars name)))
     <> newline $$
-    
+
     iname <+> text "::" <+> text "Attr" <+> bname <+> text typeof
-    $$ 
+    $$
     iname <+> equals <+> text "mkAttr" <+> bname
     <> newline
 	where
-	bname = text (toType name)
-	iname = text (identifier name)
+	bname = text (toType mi name)
+	iname = text (identifier mi name)
 
 -- | Extracts all the column names from a TableInfo
 columnNames :: TInfo -> [String]
@@ -206,7 +221,7 @@
 
 -- | Extracts all the column types from a TableInfo
 columnTypes :: TInfo -> [String]
-columnTypes table = 
+columnTypes table =
     [if b then ("(Maybe " ++ t ++ ")") else t | (t,b) <- zippedlist]
     where
     zippedlist = zip typelist null_list
@@ -215,5 +230,5 @@
 
 -- | Combines the results of columnNames and columnTypes
 columnNamesTypes :: TInfo -> [(String,String)]
-columnNamesTypes table@(TInfo tname fields) 
+columnNamesTypes table@(TInfo tname fields)
     = zip (columnNames table) (columnTypes table)
diff --git a/src/Database/HaskellDB/DBSpec/DatabaseToDBSpec.hs b/src/Database/HaskellDB/DBSpec/DatabaseToDBSpec.hs
--- a/src/Database/HaskellDB/DBSpec/DatabaseToDBSpec.hs
+++ b/src/Database/HaskellDB/DBSpec/DatabaseToDBSpec.hs
@@ -17,18 +17,23 @@
     (dbToDBSpec)
     where
 
-import Database.HaskellDB
-import Database.HaskellDB.FieldType
+import Database.HaskellDB (Database, tables, describe, )
 import Database.HaskellDB.DBSpec.DBInfo
+   (DBInfo, makeCInfo, makeTInfo, makeDBSpec, 
+    DBOptions(DBOptions), useBString, makeIdent, )
 
+import qualified Database.HaskellDB.DBSpec.PPHelpers as PP
+
+
 -- | Connects to a database and generates a specification from it
 dbToDBSpec :: Bool -- ^ Use bounded strings?
+           -> PP.MakeIdentifiers -- ^ style of generated Haskell identifiers, cOLUMN_NAME vs. columnName
 	   -> String  -- ^ the name our database should have
 	   -> Database -- ^ the database connection
 	   -> IO DBInfo    -- ^ return a DBInfo
-dbToDBSpec useBStr name dbconn
+dbToDBSpec useBStr mkIdent name dbconn
     = do ts <- tables dbconn
 	 descs <- mapM (describe dbconn) ts
          let cinfos = map (map $ uncurry makeCInfo) descs
 	 let tinfos = map (uncurry makeTInfo) (zip ts cinfos)
-	 return $ makeDBSpec name (DBOptions {useBString = useBStr}) tinfos
+	 return $ makeDBSpec name (DBOptions {useBString = useBStr, makeIdent = mkIdent }) tinfos
diff --git a/src/Database/HaskellDB/DBSpec/PPHelpers.hs b/src/Database/HaskellDB/DBSpec/PPHelpers.hs
--- a/src/Database/HaskellDB/DBSpec/PPHelpers.hs
+++ b/src/Database/HaskellDB/DBSpec/PPHelpers.hs
@@ -3,19 +3,19 @@
 -- Module      :  PPHelpers
 -- Copyright   :  HWT Group (c) 2004, haskelldb-users@lists.sourceforge.net
 -- License     :  BSD-style
--- 
+--
 -- Maintainer  :  haskelldb-users@lists.sourceforge.net
 -- Stability   :  experimental
 -- Portability :  non-portable
 --
 -- Various functions used when pretty printing stuff
 --
--- 
+--
 -----------------------------------------------------------
 module Database.HaskellDB.DBSpec.PPHelpers where
 -- no explicit export, we want ALL of it
 
-import Data.Char
+import Data.Char (toLower, toUpper, isAlpha, isAlphaNum, )
 import Text.PrettyPrint.HughesPJ
 
 newline = char '\n'
@@ -28,7 +28,7 @@
 	where
 	  commentLine	= text (replicate 75 '-')
 	  commentText s	= text ("-- " ++ s)
-	  
+
 -----------------------------------------------------------
 -- Create valid Names
 -----------------------------------------------------------
@@ -36,12 +36,48 @@
 		| otherwise		   = name
 		where
 	          baseName = reverse (takeWhile (/='\\') (reverse name))
-		  
-		  
-moduleName 	= checkChars . checkUpper
-identifier	= checkChars . checkKeyword . checkLower
-toType          = checkChars . checkKeyword . checkUpper
 
+
+data MakeIdentifiers =
+   MakeIdentifiers
+      { moduleName, identifier, toType :: String -> String }
+
+mkIdentPreserving =
+   MakeIdentifiers
+      {
+         moduleName = checkChars . checkUpper,
+         identifier = checkChars . checkKeyword . checkLower,
+         toType     = checkChars . checkKeyword . checkUpper
+      }
+
+mkIdentCamelCase =
+   MakeIdentifiers
+      {
+         moduleName = checkChars . toUpperCamelCase,
+         identifier = checkChars . checkKeyword . toLowerCamelCase,
+         toType     = checkChars . checkKeyword . toUpperCamelCase
+      }
+
+
+toLowerCamelCase s@(_:_) =
+   let (h : rest) = split ('_'==) $ dropWhile ('_'==) $ map toLower s
+   in  concat $ checkLower h : map (checkUpperDef '_') rest
+toLowerCamelCase [] =
+   error "toLowerCamelCase: identifier must be non-empty"
+
+toUpperCamelCase s@(_:_) =
+   let (h : rest) = split ('_'==) $ dropWhile ('_'==) $ map toLower s
+   in  concat $ checkUpper h : map (checkUpperDef '_') rest
+toUpperCamelCase [] =
+   error "toUpperCamelCase: identifier must be non-empty"
+
+{- |
+Generalization of 'words' and 'lines' to any separating character set.
+-}
+split :: Eq a => (a -> Bool) -> [a] -> [[a]]
+split p =
+   foldr (\ x yt@ ~(y:ys) -> (if p x then ([]:yt) else ((x:y):ys)) ) [[]]
+
 checkChars s	= map replace s
 		where
 		  replace c	| isAlphaNum c	= c
@@ -58,16 +94,22 @@
 		  		  , "do", "return"
 		  		  , "let", "in"
 		  		  , "case", "of"
-		  		  , "if", "then", "else" 
+		  		  , "if", "then", "else"
 		  		  , "id", "zip","baseTable"
 		  		  ]
 
-checkUpper ""           = error "Empty name from database?"
-checkUpper s@(x:xs)	| isUpper x	= s
-			| isLower x	= toUpper x : xs
-			| otherwise	= 'X' : s -- isNumeric?
+checkUpper "" = error "Empty name from database?"
+checkUpper s = checkUpperDef 'X' s
 
-checkLower ""           = error "Empty name from database?"	
-checkLower s@(x:xs)	| isLower x	= s
-			| isUpper x	= toLower x : xs
-			| otherwise	= 'x' : s -- isNumeric?
+checkLower "" = error "Empty name from database?"
+checkLower s = checkLowerDef 'x' s
+
+checkUpperDef _ ""      = ""
+checkUpperDef d s@(x:xs)
+			| isAlpha x	= toUpper x : xs
+			| otherwise	= d : s -- isDigit?
+
+checkLowerDef _ ""      = ""
+checkLowerDef d s@(x:xs)
+			| isAlpha x	= toLower x : xs
+			| otherwise	= d : s -- isDigit?
diff --git a/src/Database/HaskellDB/Database.hs b/src/Database/HaskellDB/Database.hs
--- a/src/Database/HaskellDB/Database.hs
+++ b/src/Database/HaskellDB/Database.hs
@@ -119,11 +119,14 @@
     => GetRec (RecCons f (Expr a) er) (RecCons f a vr) where
 
     getRec _ _ [] _ = fail $ "Wanted non-empty record, but scheme is empty"
-    getRec vfs (_::Rel (RecCons f (Expr a) er)) (f:fs) stmt = 
+    getRec vfs c (f:fs) stmt = 
 	do
 	x <- getValue vfs stmt f
-	r <- getRec vfs (undefined :: Rel er) fs stmt
+	r <- getRec vfs (recTailType c) fs stmt
 	return (RecCons x . r)
+
+recTailType :: Rel (RecCons f (Expr a) er) -> Rel er
+recTailType _ = undefined
 
 class GetValue a where
     getValue :: GetInstances s -> s -> String -> IO a
diff --git a/src/Database/HaskellDB/DriverAPI.hs b/src/Database/HaskellDB/DriverAPI.hs
--- a/src/Database/HaskellDB/DriverAPI.hs
+++ b/src/Database/HaskellDB/DriverAPI.hs
@@ -18,6 +18,7 @@
                                      MonadIO, 
 				     defaultdriver,
                                      getOptions,
+                                     getAnnotatedOptions,
                                      getGenerator
 				    ) where
 
@@ -36,12 +37,19 @@
 -- | Interface which drivers should implement.
 --   The 'connect' function takes some driver specific name, value pairs
 --   use to setup the database connection, and a database action to run.
+--   'requiredOptions' lists all required options with a short description,
+--   that is printed as help in the DBDirect program.
 data DriverInterface = DriverInterface
-    { connect :: forall m a. MonadIO m => [(String,String)] -> (Database -> m a) -> m a }
+    { connect :: forall m a. MonadIO m => [(String,String)] -> (Database -> m a) -> m a,
+      requiredOptions :: [(String, String)]
+    }
 
 -- | Default dummy driver, real drivers should overload this
 defaultdriver :: DriverInterface 
-defaultdriver = DriverInterface {connect = undefined}
+defaultdriver =
+    DriverInterface {
+        connect = error "DriverAPI.connect: not implemented",
+        requiredOptions = error "DriverAPI.requiredOptions: not implemented"}
 
 -- | Can be used by drivers to get option values from the given
 --   list of name, value pairs.
@@ -55,6 +63,17 @@
     case lookup x ys of
                      Nothing -> fail $ "Missing field " ++ x
                      Just v -> liftM (v:) $ getOptions xs ys
+
+-- | Can be used by drivers to get option values from the given
+--   list of name, value pairs.
+--   It is intended for use with the 'requiredOptions' value of the driver.
+getAnnotatedOptions :: Monad m =>
+              [(String,String)] -- ^ names and descriptions of options to get
+           -> [(String,String)] -- ^ options given
+           -> m [String] -- ^ a list of the same length as the first argument
+                         --   with the values of each option. Fails in the given
+                         --   monad if any options is not found.
+getAnnotatedOptions opts = getOptions (map fst opts)
 
 -- | Gets an 'SqlGenerator' from the "generator" option in the given list.
 --   Currently available generators: "mysql", "postgresql", "sqlite", "default"
diff --git a/src/Database/HaskellDB/FieldType.hs b/src/Database/HaskellDB/FieldType.hs
--- a/src/Database/HaskellDB/FieldType.hs
+++ b/src/Database/HaskellDB/FieldType.hs
@@ -14,12 +14,16 @@
 -- 
 -----------------------------------------------------------
 module Database.HaskellDB.FieldType 
-    (FieldDesc, FieldType(..), toHaskellType) where
+    (FieldDesc, FieldType(..), toHaskellType, ExprType(..)
+      , ExprTypes(..), queryFields) where
 
 import Data.Dynamic
 import System.Time
 
 import Database.HaskellDB.BoundedString
+import Database.HaskellDB.HDBRec (RecCons(..), Record, RecNil(..), ShowLabels)
+import Database.HaskellDB.BoundedList (listBound, Size)
+import Database.HaskellDB.Query (Expr, Rel, runQueryRel, Query, labels)
 
 -- | The type and @nullable@ flag of a database column
 type FieldDesc = (FieldType, Bool)
@@ -35,6 +39,22 @@
     | BStrT Int
     deriving (Eq,Ord,Show,Read)
 
+-- | Class which retrieves a field description from a given type.
+-- Instances are provided for most concrete types. Instances
+-- for Maybe automatically make the field nullable, and instances
+-- for all (Expr a) types where a has an ExprType instance allows
+-- type information to be recovered from a given column expression.
+class ExprType e where
+  fromHaskellType :: e -> FieldDesc
+
+-- | Class which returns a list of field descriptions. Gets the
+-- descriptions of all columns in a Record/query. Most useful when
+-- the columns associated with each field in a (Rel r) type must be
+-- recovered. Note that this occurs at the type level only and no
+-- values are inspected.
+class ExprTypes r where
+  fromHaskellTypes :: r -> [FieldDesc]
+
 toHaskellType :: FieldType -> String
 toHaskellType StringT = "String"
 toHaskellType IntT = "Int"
@@ -44,8 +64,73 @@
 toHaskellType CalendarTimeT = "CalendarTime"
 toHaskellType (BStrT a) = "BStr" ++ show a
 
+-- | Given a query, returns a list of the field names and their
+-- types used by the query. Useful for recovering field information
+-- once a query has been built up. 
+queryFields :: (ShowLabels r, ExprTypes r) => Query (Rel r) -> [(String, FieldDesc)]
+queryFields def = zip (labels query) types
+  where
+    query = unRel . snd . runQueryRel $ def
+    types = fromHaskellTypes query 
+    unRel :: (Rel r) -> r
+    unRel r = undefined -- Only used to get to type-level information.
+
 instance Typeable CalendarTime where -- not available in standard libraries
     typeOf _ = mkTyConApp (mkTyCon "System.Time.CalendarTime") []
 
 instance Typeable (BoundedString n) where
     typeOf _ = mkTyConApp (mkTyCon "Database.HaskellDB.BoundedString") []
+
+instance (ExprType a) => ExprType (Maybe a) where
+  fromHaskellType ~(Just e) = ((fst . fromHaskellType $ e), True)
+
+instance (ExprType a) => ExprType (Expr a) where
+  fromHaskellType e =
+    let unExpr :: Expr a -> a
+        unExpr _ = undefined
+    in fromHaskellType . unExpr $ e
+
+instance (ExprType a) => ExprType (Rel a) where
+  fromHaskellType e =
+    let unRel :: Rel a -> a
+        unRel _ = undefined
+    in fromHaskellType . unRel $ e
+    
+instance ExprType Bool where
+  fromHaskellType _ = (BoolT, False)
+
+instance ExprType String where
+  fromHaskellType _ = (StringT, False)
+  
+instance ExprType Int where
+  fromHaskellType _ = (IntT, False)
+
+instance ExprType Integer where
+  fromHaskellType _ = (IntegerT, False)
+
+instance ExprType Double where
+  fromHaskellType _ = (DoubleT, False)
+
+instance ExprType CalendarTime where
+  fromHaskellType _ = (CalendarTimeT, False)
+
+instance (Size n) => ExprType (BoundedString n) where
+  fromHaskellType b = (BStrT (listBound b), False)
+  
+instance ExprTypes RecNil where
+  fromHaskellTypes _ = []
+
+instance (ExprType e, ExprTypes r) => ExprTypes (RecCons f e r) where
+  fromHaskellTypes ~f@(RecCons e r) =
+    let getFieldType :: RecCons f a r -> a
+        getFieldType = undefined
+    in (fromHaskellType . getFieldType $ f) : fromHaskellTypes r
+
+instance (ExprTypes r) => ExprTypes (Record r) where
+  fromHaskellTypes r = fromHaskellTypes (r RecNil)
+
+instance (ExprTypes r) => ExprTypes (Rel r) where
+  fromHaskellTypes r =
+    let unRel :: Rel a -> a
+        unRel _ = undefined
+    in fromHaskellTypes . unRel $ r
diff --git a/src/Database/HaskellDB/HDBRec.hs b/src/Database/HaskellDB/HDBRec.hs
--- a/src/Database/HaskellDB/HDBRec.hs
+++ b/src/Database/HaskellDB/HDBRec.hs
@@ -87,7 +87,7 @@
     cat RecNil r = r
 
 instance RecCat r1 r2 r3 => RecCat (RecCons f a r1) r2 (RecCons f a r3) where
-    cat (RecCons x r1) r2 = RecCons x (cat r1 r2)
+    cat ~(RecCons x r1) r2 = RecCons x (cat r1 r2)
 
 instance RecCat r1 r2 r3 => RecCat (Record r1) (Record r2) (Record r3) where
     cat r1 r2 = \n -> cat (r1 n) (r2 n)
@@ -103,8 +103,11 @@
     (!) :: r -> f -> a
 
 instance SelectField f r a => Select (l f a) (Record r) a where
-    (!) r (_::l f a) = selectField (undefined::f) r
+    (!) r l = selectField (labelType l) r
 
+labelType :: l f a -> f
+labelType _ = undefined
+
 -- | Class which does the actual work of 
 --   getting the value of a field from a record.
 -- FIXME: would like the dependency f r -> a here, but
@@ -116,10 +119,10 @@
 		-> a -- ^ Field value
 
 instance SelectField f (RecCons f a r) a where
-    selectField _ (RecCons x _) = x
+    selectField _ ~(RecCons x _) = x
 
 instance SelectField f r a => SelectField f (RecCons g b r) a where
-    selectField f (RecCons _ r) = selectField f r
+    selectField f ~(RecCons _ r) = selectField f r
 
 instance SelectField f r a => SelectField f (Record r) a where
     selectField f r = selectField f (r RecNil)
@@ -127,7 +130,7 @@
 -- * Field update
 
 setField :: SetField f r a => l f a -> a -> r -> r
-setField (_::l f a) = setField_ (undefined::f)
+setField l = setField_ (labelType l)
 
 class SetField f r a where
     -- | Sets the value of a field in a record.
@@ -137,10 +140,10 @@
 	     -> r -- ^ New record
 
 instance SetField f (RecCons f a r) a where
-    setField_  _ y (RecCons _ r) = RecCons y r
+    setField_  _ y ~(RecCons _ r) = RecCons y r
 
 instance SetField f r a => SetField f (RecCons g b r) a where
-    setField_ l y (RecCons f r) = RecCons f (setField_ l y r)
+    setField_ l y ~(RecCons f r) = RecCons f (setField_ l y r)
 
 instance SetField f r a => SetField f (Record r) a where
     setField_ f y r = \e -> setField_ f y (r e)
@@ -157,14 +160,17 @@
 
 -- | Get the label name of a record entry.
 consFieldName :: FieldTag f => RecCons f a r -> String
-consFieldName (_::RecCons f a r) = fieldName (undefined::f)
+consFieldName = fieldName . consFieldType
 
+consFieldType ::  RecCons f a r -> f
+consFieldType _ = undefined
+
 class ShowLabels r where
     recordLabels :: r -> [String]
 instance ShowLabels RecNil where
     recordLabels _ = []
 instance (FieldTag f,ShowLabels r) => ShowLabels (RecCons f a r) where
-    recordLabels x@(RecCons _ r) = consFieldName x : recordLabels r
+    recordLabels ~x@(RecCons _ r) = consFieldName x : recordLabels r
 instance ShowLabels r => ShowLabels (Record r) where
     recordLabels r = recordLabels (r RecNil)
 
@@ -182,7 +188,7 @@
 instance (FieldTag a, 
 	  Show b, 
 	  ShowRecRow c) => ShowRecRow (RecCons a b c) where
-    showRecRow r@(RecCons x fs) = (consFieldName r, shows x) : showRecRow fs
+    showRecRow ~r@(RecCons x fs) = (consFieldName r, shows x) : showRecRow fs
 
 instance ShowRecRow r => ShowRecRow (Record r) where
     showRecRow r = showRecRow (r RecNil)
diff --git a/src/Database/HaskellDB/Optimize.hs b/src/Database/HaskellDB/Optimize.hs
--- a/src/Database/HaskellDB/Optimize.hs
+++ b/src/Database/HaskellDB/Optimize.hs
@@ -44,13 +44,13 @@
 --   PostgreSQL, since we use SELECT DISTINCT.
 includeOrderFieldsInSelect :: PrimQuery -> PrimQuery
 includeOrderFieldsInSelect = 
-    foldPrimQuery (Empty, BaseTable, proj, Restrict, Binary, Special)
+    foldPrimQuery (Empty, BaseTable, proj, Restrict, Binary, Group, Special)
     where 
       proj ass p = Project (ass++ass') p
           where ass' = [(a, AttrExpr a) | a <- new ]
                 new = orderedBy p \\ concatMap (attrInExpr . snd) ass
                 orderedBy = foldPrimQuery ([], \_ _ -> [], \_ _ -> [],
-                                           \_ _ -> [], \_ _ _ -> [], special)
+                                           \_ _ -> [], \_ _ _ -> [], \_ _ -> [], special)
                 special (Order es) p = attrInOrder es `union` p
                 special _ p = p
 
@@ -119,6 +119,14 @@
 removeD live (Special (Order xs) query)
 	= Special (Order xs) (removeD (live ++ attrInOrder xs) query)
 
+-- Filter dead columns from group expression, as they are not used. Note
+-- live columns are NOT just those that are in the select, but also those
+-- used in restrictions.
+removeD live (Group cols query)
+    = Group liveCols (removeD (live ++ (map fst liveCols)) query)
+  where
+    liveCols = filter ((`elem` live) . fst) cols
+  
 removeD live query
         = query
 
@@ -126,7 +134,7 @@
 -- | Remove unused parts of the query
 removeEmpty :: PrimQuery -> PrimQuery
 removeEmpty
-        = foldPrimQuery (Empty, BaseTable, project, restrict, binary, special)
+        = foldPrimQuery (Empty, BaseTable, project, restrict, binary, group, special)
         where
           -- Messes up queries without a table, e.g. constant queries
 	  -- disabled by Bjorn Bringert 2004-04-08
@@ -147,12 +155,14 @@
                                                Difference -> query
                                                _          -> Empty
           binary op query1 query2 = Binary op query1 query2
+          group _ Empty = Empty
+          group cols query = Group cols query
 
 
 -- | Collapse adjacent projections
 mergeProject :: PrimQuery -> PrimQuery
 mergeProject
-        = foldPrimQuery (Empty,BaseTable,project,Restrict,Binary,Special)
+        = foldPrimQuery (Empty,BaseTable,project,Restrict,Binary,Group, Special)
         where
           project assoc1 (Project assoc2 query)
              	| safe newAssoc	  = Project newAssoc query
@@ -261,7 +271,7 @@
 
 
 optimizeExprs :: PrimQuery -> PrimQuery
-optimizeExprs = foldPrimQuery (Empty, BaseTable, Project, restr, Binary, Special)
+optimizeExprs = foldPrimQuery (Empty, BaseTable, Project, restr, Binary, Group, Special)
     where 
       restr e q | exprIsTrue e' = q
                 | otherwise = Restrict e' q
diff --git a/src/Database/HaskellDB/PrimQuery.hs b/src/Database/HaskellDB/PrimQuery.hs
--- a/src/Database/HaskellDB/PrimQuery.hs
+++ b/src/Database/HaskellDB/PrimQuery.hs
@@ -56,6 +56,7 @@
 data PrimQuery  = BaseTable TableName Scheme
                 | Project   Assoc PrimQuery
                 | Restrict  PrimExpr PrimQuery
+                | Group Assoc PrimQuery
                 | Binary    RelOp PrimQuery PrimQuery
                 | Special   SpecialOp PrimQuery
                 | Empty
@@ -153,6 +154,7 @@
                                 where
                                   attr1         = attributes q1
                                   attr2         = attributes q2
+attributes (Group _ qry) = attributes qry
 
 -- | Returns a one-to-one association of a
 --   schema. ie. @assocFromScheme ["name","city"]@ becomes:
@@ -203,8 +205,8 @@
 -- | Fold on 'PrimQuery'
 foldPrimQuery :: (t, TableName -> Scheme -> t, Assoc -> t -> t,
                   PrimExpr -> t -> t, RelOp -> t -> t -> t,
-                  SpecialOp -> t -> t) -> PrimQuery -> t
-foldPrimQuery (empty,table,project,restrict,binary,special) 
+                  Assoc -> t -> t, SpecialOp -> t -> t) -> PrimQuery -> t
+foldPrimQuery (empty,table,project,restrict,binary,group,special) 
         = fold
         where
           fold (Empty)  = empty
@@ -216,6 +218,8 @@
                         = restrict expr (fold query)
           fold (Binary op query1 query2)
                         = binary op (fold query1) (fold query2)
+          fold (Group assocs query)
+                        = group assocs (fold query)
           fold (Special op query)
           		= special op (fold query)
 -- | Fold on 'PrimExpr'
diff --git a/src/Database/HaskellDB/Query.hs b/src/Database/HaskellDB/Query.hs
--- a/src/Database/HaskellDB/Query.hs
+++ b/src/Database/HaskellDB/Query.hs
@@ -27,7 +27,7 @@
 	     , (.*.) , (./.), (.%.), (.+.), (.-.), (.++.)
              , (<<), (<<-)
 	      -- * Function declarations
-	     , project, restrict, table
+	     , project, restrict, table, unique
 	     , union, intersect, divide, minus
 	     , _not, like, _in, cat, _length
 	     , isNull, notNull
@@ -116,9 +116,9 @@
 class ExprC e where
     -- | Get the underlying untyped 'PrimExpr'.
     primExpr :: e a -> PrimExpr
-instance ExprC Expr where primExpr (Expr e) = e
-instance ExprC ExprAggr where primExpr (ExprAggr e) = e
-instance ExprC ExprDefault where primExpr (ExprDefault e) = e
+instance ExprC Expr where primExpr ~(Expr e) = e
+instance ExprC ExprAggr where primExpr ~(ExprAggr e) = e
+instance ExprC ExprDefault where primExpr ~(ExprDefault e) = e
 
 -- | Class of expressions that can be used with 'insert'.
 class ExprC e => InsertExpr e
@@ -199,6 +199,29 @@
 restrict :: Expr Bool -> Query ()
 restrict (Expr primExpr) = updatePrimQuery_ (Restrict primExpr)
 
+-- | Restricts the relation given to only return unique records. Upshot
+-- is all projected attributes will be 'grouped'
+unique :: Query ()
+unique = Query (\(i, primQ) ->
+    -- Add all non-aggregate expressions in the query
+    -- to a groupby association list. This list holds the name
+    -- of the expression and the expression itself. Those expressions
+    -- will later by added to the groupby list in the SqlSelect built.
+    case findNonAggr primQ of
+      [] -> ((), (i + 1, primQ)) -- No non-aggregate expressions - no-op.
+      newCols -> ((), (i + 1, Group newCols primQ)))
+  where
+    -- Find all non-aggregate expressions.
+    findNonAggr :: PrimQuery -> Assoc
+    findNonAggr (Project cols q) = filter (not . isAggregate . snd) cols
+    findNonAggr (Restrict _ q) = findNonAggr q
+    findNonAggr (Binary _ q1 q2) = findNonAggr q1 ++ findNonAggr q2
+    findNonAggr (BaseTable tblName cols) = zip cols (map AttrExpr cols)
+    findNonAggr (Special _ q) = findNonAggr q
+    -- Group and Empty are no-ops
+    findNonAggr (Group _ _) = []
+    findNonAggr Empty  = []
+  
 -----------------------------------------------------------
 -- Binary operations
 -----------------------------------------------------------
@@ -470,7 +493,7 @@
 
 instance (ShowConstant a, ConstantRecord r cr) 
     => ConstantRecord (RecCons f a r) (RecCons f (Expr a) cr) where
-    constantRecord (RecCons x rs) = RecCons (constant x) (constantRecord rs)
+    constantRecord ~(RecCons x rs) = RecCons (constant x) (constantRecord rs)
 
 -----------------------------------------------------------
 -- Aggregate operators
@@ -630,10 +653,10 @@
     toPrimExprs :: r -> [PrimExpr]
 
 instance ToPrimExprs RecNil where
-    toPrimExprs RecNil = []
+    toPrimExprs ~RecNil = []
 
 instance (ExprC e, ToPrimExprs r) => ToPrimExprs (RecCons l (e a) r) where
-    toPrimExprs (RecCons e r) = primExpr e : toPrimExprs r
+    toPrimExprs ~(RecCons e r) = primExpr e : toPrimExprs r
 
 {-
 
diff --git a/src/Database/HaskellDB/Sql.hs b/src/Database/HaskellDB/Sql.hs
--- a/src/Database/HaskellDB/Sql.hs
+++ b/src/Database/HaskellDB/Sql.hs
@@ -40,10 +40,12 @@
 type SqlColumn = String
 
 data SqlOrder = SqlAsc | SqlDesc
+  deriving Show
 
 data SqlType = SqlType String
              | SqlType1 String Int
              | SqlType2 String Int Int
+  deriving Show
 
 -- | Data type for SQL SELECT statements.
 data SqlSelect  = SqlSelect { 
@@ -51,14 +53,15 @@
 			     attrs     :: [(SqlColumn,SqlExpr)],   -- ^ result
                              tables    :: [(SqlTable,SqlSelect)],  -- ^ FROM
                              criteria  :: [SqlExpr],               -- ^ WHERE
-                             groupby   :: [SqlExpr],               -- ^ GROUP BY
+                             groupby   :: [(SqlColumn,SqlExpr)],   -- ^ GROUP BY
                              orderby   :: [(SqlExpr,SqlOrder)],    -- ^ ORDER BY
 			     extra     :: [String]                 -- ^ TOP n, etc.
                             }
                 | SqlBin   String SqlSelect SqlSelect -- ^ Binary relational operator
                 | SqlTable SqlTable -- ^ Select a whole table.
                 | SqlEmpty -- ^ Empty select.
-
+  deriving Show
+  
 -- | Expressions in SQL statements.
 data SqlExpr = ColumnSqlExpr  SqlColumn
              | BinSqlExpr     String SqlExpr SqlExpr
@@ -68,6 +71,8 @@
              | ConstSqlExpr   String
 	     | CaseSqlExpr    [(SqlExpr,SqlExpr)] SqlExpr
              | ListSqlExpr    [SqlExpr]
+             | ExistsSqlExpr  SqlSelect
+  deriving Show
 
 -- | Data type for SQL UPDATE statements.
 data SqlUpdate  = SqlUpdate SqlTable [(SqlColumn,SqlExpr)] [SqlExpr]
@@ -89,12 +94,12 @@
 
 newSelect :: SqlSelect
 newSelect = SqlSelect { 
-                       options   = ["DISTINCT"],
-		       attrs     = [],
-		       tables    = [],
-		       criteria  = [],
-		       groupby	 = [],
-		       orderby	 = [],
-		       extra     = []
+                       options   = [],
+                       attrs     = [],
+                       tables    = [],
+                       criteria  = [],
+                       groupby	 = [],
+                       orderby	 = [],
+                       extra     = []
                       }
 
diff --git a/src/Database/HaskellDB/Sql/Default.hs b/src/Database/HaskellDB/Sql/Default.hs
--- a/src/Database/HaskellDB/Sql/Default.hs
+++ b/src/Database/HaskellDB/Sql/Default.hs
@@ -31,6 +31,7 @@
                                         defaultSqlProject,
                                         defaultSqlRestrict,
                                         defaultSqlBinary,
+                                        defaultSqlGroup,
                                         defaultSqlSpecial,
 
                                         defaultSqlExpr,
@@ -68,6 +69,7 @@
      sqlProject     = defaultSqlProject     gen,
      sqlRestrict    = defaultSqlRestrict    gen,
      sqlBinary      = defaultSqlBinary      gen,
+     sqlGroup       = defaultSqlGroup       gen,
      sqlSpecial     = defaultSqlSpecial     gen,
 
      sqlExpr        = defaultSqlExpr        gen,
@@ -105,6 +107,7 @@
                                      sqlProject gen,
                                      sqlRestrict gen,
                                      sqlBinary gen,
+                                     sqlGroup gen,
                                      sqlSpecial gen)
 
 defaultSqlEmpty :: SqlGenerator -> SqlSelect
@@ -115,19 +118,20 @@
 
 defaultSqlProject :: SqlGenerator -> Assoc -> SqlSelect -> SqlSelect
 defaultSqlProject gen assoc q
-          	| hasAggr    = select { groupby = map (sqlExpr gen) nonAggrs }
+          	| hasAggr    = select { groupby = toSqlAssoc gen nonAggrs }
           	| otherwise  = select 
                 where
                   select   = sql { attrs = toSqlAssoc gen assoc }
                   sql      = toSqlSelect q
+                  hasAggr  = (not . null  . filter (isAggregate . snd)) assoc
+                  nonAggrs = filter (not . isAggregate . snd) assoc
 
-                  hasAggr  = any isAggregate exprs
-                  
-                  -- TODO: we should make sure that every non-aggregate 
-                  -- is only a simple attribute expression
-                  nonAggrs = filter (not.isAggregate) exprs
-                  
-                  exprs    = map snd assoc
+
+-- | Takes all non-aggregate expressions in the select and adds them to
+-- the 'group by' clause.
+defaultSqlGroup :: SqlGenerator -> Assoc -> SqlSelect -> SqlSelect
+defaultSqlGroup gen cols select = select { groupby = toSqlAssoc gen cols }
+    
 
 defaultSqlRestrict :: SqlGenerator -> PrimExpr -> SqlSelect -> SqlSelect
 defaultSqlRestrict gen expr q
diff --git a/src/Database/HaskellDB/Sql/Generate.hs b/src/Database/HaskellDB/Sql/Generate.hs
--- a/src/Database/HaskellDB/Sql/Generate.hs
+++ b/src/Database/HaskellDB/Sql/Generate.hs
@@ -34,6 +34,9 @@
      sqlEmpty       :: SqlSelect,
      sqlTable       :: TableName -> Scheme -> SqlSelect,
      sqlProject     :: Assoc -> SqlSelect -> SqlSelect,
+     -- | Ensures non-aggregate expressions in the select are included in
+     -- group by clause.
+     sqlGroup       :: Assoc -> SqlSelect -> SqlSelect,
      sqlRestrict    :: PrimExpr -> SqlSelect -> SqlSelect,
      sqlBinary      :: RelOp -> SqlSelect -> SqlSelect -> SqlSelect,
      sqlSpecial     :: SpecialOp -> SqlSelect -> SqlSelect,
diff --git a/src/Database/HaskellDB/Sql/MySQL.hs b/src/Database/HaskellDB/Sql/MySQL.hs
--- a/src/Database/HaskellDB/Sql/MySQL.hs
+++ b/src/Database/HaskellDB/Sql/MySQL.hs
@@ -13,9 +13,28 @@
 -----------------------------------------------------------
 module Database.HaskellDB.Sql.MySQL (generator) where
 
+import Database.HaskellDB.Sql
 import Database.HaskellDB.Sql.Default
 import Database.HaskellDB.Sql.Generate
 import Database.HaskellDB.PrimQuery
 
 generator :: SqlGenerator
-generator = mkSqlGenerator generator 
+generator = (mkSqlGenerator generator) {
+              sqlBinary = mySqlBinary
+            }
+
+mySqlBinary :: RelOp -> SqlSelect -> SqlSelect -> SqlSelect
+mySqlBinary Difference = mySqlDifference
+mySqlBinary op = defaultSqlBinary generator op
+
+{- Hack around the lack of "EXCEPT" in MySql -}
+mySqlDifference :: SqlSelect -> SqlSelect -> SqlSelect
+mySqlDifference sel1 sel2
+ = (toSqlSelect sel1) { criteria = [PrefixSqlExpr "NOT" $ ExistsSqlExpr existsSql] }
+     where existsSql = (toSqlSelect ((toSqlSelect sel2) { attrs = zipWith mkAttr names renames }))
+                            { criteria = zipWith mkCond names renames }
+           names = map fst $ attrs sel2 -- attrs sel1 should be the same, but it turned out to
+                                        -- be undefined in the case I tried
+           renames = map (++"_local") names
+           mkAttr name rename = (rename, ColumnSqlExpr name)
+           mkCond name rename = BinSqlExpr "=" (ColumnSqlExpr name) (ColumnSqlExpr rename)
diff --git a/src/Database/HaskellDB/Sql/PostgreSQL.hs b/src/Database/HaskellDB/Sql/PostgreSQL.hs
--- a/src/Database/HaskellDB/Sql/PostgreSQL.hs
+++ b/src/Database/HaskellDB/Sql/PostgreSQL.hs
@@ -21,7 +21,7 @@
 
 
 generator :: SqlGenerator
-generator = mkSqlGenerator generator 
+generator = (mkSqlGenerator generator)
             {
              sqlSpecial = postgresqlSpecial,
              sqlType = postgresqlType
diff --git a/src/Database/HaskellDB/Sql/Print.hs b/src/Database/HaskellDB/Sql/Print.hs
--- a/src/Database/HaskellDB/Sql/Print.hs
+++ b/src/Database/HaskellDB/Sql/Print.hs
@@ -73,10 +73,16 @@
 ppWhere es = text "WHERE" 
              <+> hsep (intersperse (text "AND") (map ppSqlExpr es))
 
-ppGroupBy :: [SqlExpr] -> Doc
+ppGroupBy :: [(SqlColumn, SqlExpr)] -> Doc
 ppGroupBy [] = empty
-ppGroupBy es = text "GROUP BY" <+> commaV ppSqlExpr es
-
+ppGroupBy es = text "GROUP BY" <+> ppGroupAttrs es
+  where
+    ppGroupAttrs :: [(SqlColumn, SqlExpr)] -> Doc
+    ppGroupAttrs cs = commaV nameOrExpr cs
+    nameOrExpr :: (SqlColumn, SqlExpr) -> Doc
+    nameOrExpr (name, ColumnSqlExpr _) = text name
+    nameOrExpr (_, expr) = parens (ppSqlExpr expr)
+    
 ppOrderBy :: [(SqlExpr,SqlOrder)] -> Doc
 ppOrderBy [] = empty
 ppOrderBy ord = text "ORDER BY" <+> commaV ppOrd ord
@@ -138,8 +144,7 @@
     ppF (fname,t) = text fname <+> ppSqlTypeNull t
 
 ppSqlTypeNull :: (SqlType,Bool) -> Doc
-ppSqlTypeNull (t,nullable) = if nullable then t' else t' <+> text " not null"
-    where t' = ppSqlType t
+ppSqlTypeNull (t,nullable) = ppSqlType t <+> text (if nullable then " null" else " not null")
 
 ppSqlType :: SqlType -> Doc
 ppSqlType (SqlType t) = text t
@@ -172,6 +177,7 @@
           where ppWhen (w,t) = text "WHEN" <+> ppSqlExpr w 
                                <+> text "THEN" <+> ppSqlExpr t
       ListSqlExpr es      -> parens (commaH ppSqlExpr es)
+      ExistsSqlExpr s     -> text "EXISTS" <+> parens (ppSql s)
 
 commaH :: (a -> Doc) -> [a] -> Doc
 commaH f = hcat . punctuate comma . map f
diff --git a/src/Database/HaskellDB/Sql/SQLite.hs b/src/Database/HaskellDB/Sql/SQLite.hs
--- a/src/Database/HaskellDB/Sql/SQLite.hs
+++ b/src/Database/HaskellDB/Sql/SQLite.hs
@@ -19,7 +19,7 @@
 import Database.HaskellDB.PrimQuery
 
 generator :: SqlGenerator
-generator = mkSqlGenerator generator 
+generator = (mkSqlGenerator generator)
             {
              sqlLiteral = literal
             }
