packages feed

groundhog-postgresql 0.2.0 → 0.3.0

raw patch · 5 files changed

+605/−272 lines, 5 filesdep +attoparsecdep +blaze-builderdep +resource-pooldep −pool-conduitdep ~groundhogPVP ok

version bump matches the API change (PVP)

Dependencies added: attoparsec, blaze-builder, resource-pool

Dependencies removed: pool-conduit

Dependency ranges changed: groundhog

API changes (from Hackage documentation)

- Database.Groundhog.Postgresql: runPostgresqlConn :: (MonadBaseControl IO m, MonadIO m) => DbPersist Postgresql m a -> Postgresql -> m a
- Database.Groundhog.Postgresql: runPostgresqlPool :: (MonadBaseControl IO m, MonadIO m) => DbPersist Postgresql m a -> Pool Postgresql -> m a
+ Database.Groundhog.Postgresql: instance (MonadBaseControl IO m, MonadIO m) => SchemaAnalyzer (DbPersist Postgresql m)
+ Database.Groundhog.Postgresql: instance ConnectionManager (Pool Postgresql) Postgresql
+ Database.Groundhog.Postgresql: instance ConnectionManager Postgresql Postgresql
+ Database.Groundhog.Postgresql: instance Savepoint Postgresql
+ Database.Groundhog.Postgresql: instance SingleConnectionManager Postgresql Postgresql
+ Database.Groundhog.Postgresql: instance SqlDb Postgresql
+ Database.Groundhog.Postgresql: runDbConn :: (MonadBaseControl IO m, MonadIO m, ConnectionManager cm conn) => DbPersist conn m a -> cm -> m a
+ Database.Groundhog.Postgresql.Array: (!) :: (ExpressionOf Postgresql r a (Array elem), ExpressionOf Postgresql r b Int) => a -> b -> Expr Postgresql r elem
+ Database.Groundhog.Postgresql.Array: (!:) :: (ExpressionOf Postgresql r a (Array elem), ExpressionOf Postgresql r i1 Int, ExpressionOf Postgresql r i2 Int) => a -> (i1, i2) -> Expr Postgresql r (Array elem)
+ Database.Groundhog.Postgresql.Array: (<@) :: (ExpressionOf Postgresql r a (Array elem), ExpressionOf Postgresql r b (Array elem)) => a -> b -> Cond Postgresql r
+ Database.Groundhog.Postgresql.Array: (@>) :: (ExpressionOf Postgresql r a (Array elem), ExpressionOf Postgresql r b (Array elem)) => a -> b -> Cond Postgresql r
+ Database.Groundhog.Postgresql.Array: Array :: [a] -> Array a
+ Database.Groundhog.Postgresql.Array: all :: (ExpressionOf Postgresql r a elem, ExpressionOf Postgresql r b (Array elem)) => a -> b -> Cond Postgresql r
+ Database.Groundhog.Postgresql.Array: any :: (ExpressionOf Postgresql r a elem, ExpressionOf Postgresql r b (Array elem)) => a -> b -> Cond Postgresql r
+ Database.Groundhog.Postgresql.Array: append :: (ExpressionOf Postgresql r a (Array elem), ExpressionOf Postgresql r b elem) => a -> b -> Expr Postgresql r (Array elem)
+ Database.Groundhog.Postgresql.Array: arrayCat :: (ExpressionOf Postgresql r a (Array elem), ExpressionOf Postgresql r b (Array elem)) => a -> b -> Expr Postgresql r (Array elem)
+ Database.Groundhog.Postgresql.Array: arrayDims :: ExpressionOf Postgresql r a (Array elem) => a -> Expr Postgresql r String
+ Database.Groundhog.Postgresql.Array: arrayLength :: ExpressionOf Postgresql r a (Array elem) => a -> Int -> Expr Postgresql r Int
+ Database.Groundhog.Postgresql.Array: arrayLower :: ExpressionOf Postgresql r a (Array elem) => a -> Int -> Expr Postgresql r Int
+ Database.Groundhog.Postgresql.Array: arrayNDims :: ExpressionOf Postgresql r a (Array elem) => a -> Expr Postgresql r Int
+ Database.Groundhog.Postgresql.Array: arrayToString :: ExpressionOf Postgresql r a (Array elem) => a -> String -> Expr Postgresql r String
+ Database.Groundhog.Postgresql.Array: arrayUpper :: ExpressionOf Postgresql r a (Array elem) => a -> Int -> Expr Postgresql r Int
+ Database.Groundhog.Postgresql.Array: instance [overlap ok] (ArrayElem a, PersistField a) => PersistField (Array a)
+ Database.Groundhog.Postgresql.Array: instance [overlap ok] (ArrayElem a, PersistField a) => PrimitivePersistField (Array a)
+ Database.Groundhog.Postgresql.Array: instance [overlap ok] ArrayElem a => ArrayElem (Array a)
+ Database.Groundhog.Postgresql.Array: instance [overlap ok] Eq a => Eq (Array a)
+ Database.Groundhog.Postgresql.Array: instance [overlap ok] PrimitivePersistField a => ArrayElem a
+ Database.Groundhog.Postgresql.Array: instance [overlap ok] Show a => Show (Array a)
+ Database.Groundhog.Postgresql.Array: newtype Array a
+ Database.Groundhog.Postgresql.Array: overlaps :: (ExpressionOf Postgresql r a (Array elem), ExpressionOf Postgresql r b (Array elem)) => a -> b -> Cond Postgresql r
+ Database.Groundhog.Postgresql.Array: prepend :: (ExpressionOf Postgresql r a elem, ExpressionOf Postgresql r b (Array elem)) => a -> b -> Expr Postgresql r (Array elem)
+ Database.Groundhog.Postgresql.Array: stringToArray :: ExpressionOf Postgresql r a String => a -> String -> Expr Postgresql r (Array String)
+ Database.Groundhog.Postgresql.Geometry: Box :: Point -> Point -> Box
+ Database.Groundhog.Postgresql.Geometry: Circle :: Point -> Double -> Circle
+ Database.Groundhog.Postgresql.Geometry: ClosedPath :: [Point] -> Path
+ Database.Groundhog.Postgresql.Geometry: Line :: Point -> Point -> Line
+ Database.Groundhog.Postgresql.Geometry: Lseg :: Point -> Point -> Lseg
+ Database.Groundhog.Postgresql.Geometry: OpenPath :: [Point] -> Path
+ Database.Groundhog.Postgresql.Geometry: Point :: Double -> Double -> Point
+ Database.Groundhog.Postgresql.Geometry: Polygon :: [Point] -> Polygon
+ Database.Groundhog.Postgresql.Geometry: data Box
+ Database.Groundhog.Postgresql.Geometry: data Circle
+ Database.Groundhog.Postgresql.Geometry: data Line
+ Database.Groundhog.Postgresql.Geometry: data Lseg
+ Database.Groundhog.Postgresql.Geometry: data Path
+ Database.Groundhog.Postgresql.Geometry: data Point
+ Database.Groundhog.Postgresql.Geometry: data Polygon
+ Database.Groundhog.Postgresql.Geometry: instance Eq Box
+ Database.Groundhog.Postgresql.Geometry: instance Eq Circle
+ Database.Groundhog.Postgresql.Geometry: instance Eq Line
+ Database.Groundhog.Postgresql.Geometry: instance Eq Lseg
+ Database.Groundhog.Postgresql.Geometry: instance Eq Path
+ Database.Groundhog.Postgresql.Geometry: instance Eq Point
+ Database.Groundhog.Postgresql.Geometry: instance Eq Polygon
+ Database.Groundhog.Postgresql.Geometry: instance PersistField Box
+ Database.Groundhog.Postgresql.Geometry: instance PersistField Circle
+ Database.Groundhog.Postgresql.Geometry: instance PersistField Line
+ Database.Groundhog.Postgresql.Geometry: instance PersistField Lseg
+ Database.Groundhog.Postgresql.Geometry: instance PersistField Path
+ Database.Groundhog.Postgresql.Geometry: instance PersistField Point
+ Database.Groundhog.Postgresql.Geometry: instance PersistField Polygon
+ Database.Groundhog.Postgresql.Geometry: instance PrimitivePersistField Box
+ Database.Groundhog.Postgresql.Geometry: instance PrimitivePersistField Circle
+ Database.Groundhog.Postgresql.Geometry: instance PrimitivePersistField Line
+ Database.Groundhog.Postgresql.Geometry: instance PrimitivePersistField Lseg
+ Database.Groundhog.Postgresql.Geometry: instance PrimitivePersistField Path
+ Database.Groundhog.Postgresql.Geometry: instance PrimitivePersistField Point
+ Database.Groundhog.Postgresql.Geometry: instance PrimitivePersistField Polygon
+ Database.Groundhog.Postgresql.Geometry: instance Show Box
+ Database.Groundhog.Postgresql.Geometry: instance Show Circle
+ Database.Groundhog.Postgresql.Geometry: instance Show Line
+ Database.Groundhog.Postgresql.Geometry: instance Show Lseg
+ Database.Groundhog.Postgresql.Geometry: instance Show Path
+ Database.Groundhog.Postgresql.Geometry: instance Show Point
+ Database.Groundhog.Postgresql.Geometry: instance Show Polygon

Files

− Database/Groundhog/Generic/Sql/String.hs
@@ -1,40 +0,0 @@-module Database.Groundhog.Generic.Sql.String-    ( module Database.Groundhog.Generic.Sql-    , StringS (..)-    ) where--import Database.Groundhog.Core-import Database.Groundhog.Generic.Sql-import Data.Monoid-import Data.String--newtype StringS = StringS { fromStringS :: ShowS }--instance Monoid StringS where-  mempty = StringS id-  (StringS s1) `mappend` (StringS s2) = StringS (s1 . s2)--instance IsString StringS where-  fromString s = StringS (s++)--instance StringLike StringS where-  fromChar c = StringS (c:)--{-# SPECIALIZE (<>) :: RenderS StringS -> RenderS StringS -> RenderS StringS #-}--{-# SPECIALIZE renderArith :: (PersistEntity v, Constructor c, DbDescriptor db) => Proxy db -> (StringS -> StringS) -> Arith v c a -> RenderS StringS #-}--{-# SPECIALIZE renderCond :: (PersistEntity v, Constructor c, DbDescriptor db)-  => Proxy db-  -> (StringS -> StringS)-  -> (StringS -> StringS -> StringS)-  -> (StringS -> StringS -> StringS)-  -> Cond v c -> Maybe (RenderS StringS) #-}--{-# SPECIALIZE renderOrders :: (PersistEntity v, Constructor c) => (StringS -> StringS) -> [Order v c] -> StringS #-}--{-# SPECIALIZE renderUpdates :: (PersistEntity v, Constructor c, DbDescriptor db) => Proxy db -> (StringS -> StringS) -> [Update v c] -> Maybe (RenderS StringS) #-}--{-# SPECIALIZE renderFields :: (StringS -> StringS) -> [(String, DbType)] -> StringS #-}--{-# SPECIALIZE renderChain :: (StringS -> StringS) -> FieldChain -> [StringS] -> [StringS] #-}
Database/Groundhog/Postgresql.hs view
@@ -1,11 +1,11 @@-{-# LANGUAGE ScopedTypeVariables, FlexibleInstances, FlexibleContexts, OverloadedStrings, TypeFamilies #-}+{-# LANGUAGE ScopedTypeVariables, FlexibleInstances, FlexibleContexts, OverloadedStrings, TypeFamilies, MultiParamTypeClasses #-} module Database.Groundhog.Postgresql     ( withPostgresqlPool     , withPostgresqlConn-    , runPostgresqlPool-    , runPostgresqlConn+    , runDbConn     , Postgresql     , module Database.Groundhog+    , module Database.Groundhog.Generic.Sql.Functions     ) where  import Database.Groundhog@@ -13,7 +13,8 @@ import Database.Groundhog.Generic import Database.Groundhog.Generic.Migration hiding (MigrationPack(..)) import qualified Database.Groundhog.Generic.Migration as GM-import Database.Groundhog.Generic.Sql.String+import Database.Groundhog.Generic.Sql+import Database.Groundhog.Generic.Sql.Functions import qualified Database.Groundhog.Generic.PersistBackendHelpers as H  import qualified Database.PostgreSQL.Simple as PG@@ -29,34 +30,40 @@ import Control.Exception (throw) import Control.Monad (forM, liftM, liftM2) import Control.Monad.IO.Class (MonadIO(..))+import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Control (MonadBaseControl) import Control.Monad.Trans.Reader (ask) import Data.ByteString.Char8 (ByteString, pack, unpack, copy)+import Data.Char (toUpper) import Data.Either (partitionEithers) import Data.Function (on) import Data.Int (Int64) import Data.IORef import Data.List (groupBy, intercalate)+import Data.Maybe (fromJust, fromMaybe) import Data.Monoid-import Data.Conduit.Pool-import qualified Data.Text as T-import qualified Data.Text.Encoding as T+import Data.Pool import Data.Time.LocalTime (localTimeToUTC, utc)-import System.IO.Unsafe (unsafePerformIO)  -- typical operations for connection: OPEN, BEGIN, COMMIT, ROLLBACK, CLOSE newtype Postgresql = Postgresql PG.Connection  instance DbDescriptor Postgresql where   type AutoKeyType Postgresql = Int64+  type QueryRaw Postgresql = Snippet Postgresql+  backendName _ = "postgresql" +instance SqlDb Postgresql where+  append a b = Expr $ operator 50 "||" a b+ instance (MonadBaseControl IO m, MonadIO m) => PersistBackend (DbPersist Postgresql m) where   {-# SPECIALIZE instance PersistBackend (DbPersist Postgresql IO) #-}   type PhantomDb (DbPersist Postgresql m) = Postgresql   insert v = insert' v+  insert_ v = insert_' v   insertBy u v = H.insertBy escapeS queryRawTyped' u v   insertByAll v = H.insertByAll escapeS queryRawTyped' v-  replace k v = H.replace escapeS queryRawTyped' executeRaw' insertIntoConstructorTable k v+  replace k v = H.replace escapeS queryRawTyped' executeRaw' (insertIntoConstructorTable False) k v   select options = H.select escapeS queryRawTyped' "" renderCond' options   selectAll = H.selectAll escapeS queryRawTyped'   get k = H.get escapeS queryRawTyped' k@@ -75,35 +82,58 @@   insertList l = insertList' l   getList k = getList' k +instance (MonadBaseControl IO m, MonadIO m) => SchemaAnalyzer (DbPersist Postgresql m) where+  listTables schema = queryRaw' "SELECT table_name FROM information_schema.tables WHERE table_schema=coalesce(?,current_schema())" [toPrimitivePersistValue proxy schema] (mapAllRows $ return . fst . fromPurePersistValues proxy)+  listTableTriggers schema name = queryRaw' "SELECT trigger_name FROM information_schema.triggers WHERE event_object_schema=coalesce(?,current_schema()) AND event_object_table=?" [toPrimitivePersistValue proxy schema, toPrimitivePersistValue proxy name] (mapAllRows $ return . fst . fromPurePersistValues proxy)+  analyzeTable = analyzeTable'+  analyzeTrigger schema name = do+    x <- queryRaw' "SELECT action_statement FROM information_schema.triggers WHERE trigger_schema=coalesce(?,current_schema()) AND trigger_name=?" [toPrimitivePersistValue proxy schema, toPrimitivePersistValue proxy name] id+    case x of+      Nothing  -> return Nothing+      Just src -> return (fst $ fromPurePersistValues proxy src)+  analyzeFunction schema name = do+    x <- queryRaw' "SELECT p.prosrc FROM pg_catalog.pg_namespace n INNER JOIN pg_catalog.pg_proc p ON p.pronamespace = n.oid WHERE n.nspname = coalesce(?,current_schema()) AND p.proname = ?" [toPrimitivePersistValue proxy schema, toPrimitivePersistValue proxy name] id+    case x of+      Nothing  -> return Nothing+      Just src -> return (fst $ fromPurePersistValues proxy src)+ --{-# SPECIALIZE withPostgresqlPool :: String -> Int -> (Pool Postgresql -> IO a) -> IO a #-} withPostgresqlPool :: (MonadBaseControl IO m, MonadIO m)-               => String+               => String -- ^ connection string                -> Int -- ^ number of connections to open                -> (Pool Postgresql -> m a)                -> m a withPostgresqlPool s connCount f = liftIO (createPool (open' s) close' 1 20 connCount) >>= f  {-# SPECIALIZE withPostgresqlConn :: String -> (Postgresql -> IO a) -> IO a #-}-{-# INLINE withPostgresqlConn #-} withPostgresqlConn :: (MonadBaseControl IO m, MonadIO m)-               => String+               => String -- ^ connection string                -> (Postgresql -> m a)                -> m a withPostgresqlConn s = bracket (liftIO $ open' s) (liftIO . close') -{-# SPECIALIZE runPostgresqlPool :: DbPersist Postgresql IO a -> Pool Postgresql -> IO a #-}-runPostgresqlPool :: (MonadBaseControl IO m, MonadIO m) => DbPersist Postgresql m a -> Pool Postgresql -> m a-runPostgresqlPool f pconn = withResource pconn $ runPostgresqlConn f+instance Savepoint Postgresql where+  withConnSavepoint name m (Postgresql c) = do+    let name' = fromString name+    liftIO $ PG.execute_ c $ "SAVEPOINT " <> name'+    x <- onException m (liftIO $ PG.execute_ c $ "ROLLBACK TO SAVEPOINT " <> name')+    liftIO $ PG.execute_ c $ "RELEASE SAVEPOINT" <> name'+    return x -{-# SPECIALIZE runPostgresqlConn :: DbPersist Postgresql IO a -> Postgresql -> IO a #-}-{-# INLINE runPostgresqlConn #-}-runPostgresqlConn :: (MonadBaseControl IO m, MonadIO m) => DbPersist Postgresql m a -> Postgresql -> m a-runPostgresqlConn f conn@(Postgresql c) = do-  liftIO $ PG.begin c-  x <- onException (runDbPersist f conn) (liftIO $ PG.rollback c)-  liftIO $ PG.commit c-  return x+instance ConnectionManager Postgresql Postgresql where+  withConn f conn@(Postgresql c) = do+    liftIO $ PG.begin c+    x <- onException (f conn) (liftIO $ PG.rollback c)+    liftIO $ PG.commit c+    return x+  withConnNoTransaction f conn = f conn +instance ConnectionManager (Pool Postgresql) Postgresql where+  withConn f pconn = withResource pconn (withConn f)+  withConnNoTransaction f pconn = withResource pconn (withConnNoTransaction f)++instance SingleConnectionManager Postgresql Postgresql+ open' :: String -> IO Postgresql open' s = do   conn <- PG.connectPostgreSQL $ pack s@@ -114,43 +144,62 @@ close' (Postgresql conn) = PG.close conn  {-# SPECIALIZE insert' :: PersistEntity v => v -> DbPersist Postgresql IO (AutoKey v) #-}-{-# INLINE insert' #-} insert' :: (PersistEntity v, MonadBaseControl IO m, MonadIO m) => v -> DbPersist Postgresql m (AutoKey v) insert' v = do   -- constructor number and the rest of the field values   vals <- toEntityPersistValues' v   let e = entityDef v-  let name = persistName v   let constructorNum = fromPrimitivePersistValue proxy (head vals)    liftM fst $ if isSimple (constructors e)     then do       let constr = head $ constructors e-      let query = insertIntoConstructorTable False name constr+      let RenderS query vals' = insertIntoConstructorTable True False (tableName escapeS e constr) constr (tail vals)       case constrAutoKeyName constr of-        Nothing -> executeRaw' query (tail vals) >> pureFromPersistValue []+        Nothing -> executeRaw' query (vals' []) >> pureFromPersistValue []         Just _  -> do-          x <- queryRaw' query (tail vals) id+          x <- queryRaw' query (vals' []) id           case x of             Just xs -> pureFromPersistValue xs             Nothing -> pureFromPersistValue []     else do       let constr = constructors e !! constructorNum-      let cName = name ++ [delim] ++ constrName constr-      let query = "INSERT INTO " <> escapeS (fromString name) <> "(discr)VALUES(?)RETURNING(id)"+      let query = "INSERT INTO " <> mainTableName escapeS e <> "(discr)VALUES(?)RETURNING(id)"       rowid <- queryRaw' query (take 1 vals) getKey-      let cQuery = insertIntoConstructorTable True cName constr-      executeRaw' cQuery $ rowid:(tail vals)+      let RenderS cQuery vals' = insertIntoConstructorTable False True (tableName escapeS e constr) constr (rowid:tail vals)+      executeRaw' cQuery (vals' [])       pureFromPersistValue [rowid] -insertIntoConstructorTable :: Bool -> String -> ConstructorDef -> StringS-insertIntoConstructorTable withId tName c = "INSERT INTO " <> escapeS (fromString tName) <> "(" <> fieldNames <> ")VALUES(" <> placeholders <> ")" <> returning where+{-# SPECIALIZE insert_' :: PersistEntity v => v -> DbPersist Postgresql IO () #-}+insert_' :: (PersistEntity v, MonadBaseControl IO m, MonadIO m) => v -> DbPersist Postgresql m ()+insert_' v = do+  -- constructor number and the rest of the field values+  vals <- toEntityPersistValues' v+  let e = entityDef v+  let constructorNum = fromPrimitivePersistValue proxy (head vals)++  if isSimple (constructors e)+    then do+      let constr = head $ constructors e+      let RenderS query vals' = insertIntoConstructorTable False False (tableName escapeS e constr) constr (tail vals)+      executeRaw' query (vals' [])+    else do+      let constr = constructors e !! constructorNum+      let query = "INSERT INTO " <> mainTableName escapeS e <> "(discr)VALUES(?)RETURNING(id)"+      rowid <- queryRaw' query (take 1 vals) getKey+      let RenderS cQuery vals' = insertIntoConstructorTable False True (tableName escapeS e constr) constr (rowid:tail vals)+      executeRaw' cQuery (vals' [])++insertIntoConstructorTable :: Bool -> Bool -> Utf8 -> ConstructorDef -> [PersistValue] -> RenderS db r+insertIntoConstructorTable withRet withId tName c vals = RenderS query vals' where+  query = "INSERT INTO " <> tName <> "(" <> fieldNames <> ")VALUES(" <> placeholders <> ")" <> returning   (fields, returning) = case constrAutoKeyName c of-    Just idName | withId    -> ((idName, dbType (0 :: Int64)):constrParams c, mempty)-                | otherwise -> (constrParams c, "RETURNING(" <> escapeS (fromString idName) <> ")")-    _                       -> (constrParams c, mempty)+    Just idName -> (fields', returning') where+      fields' = if withId then (idName, dbType (0 :: Int64)):constrParams c else constrParams c+      returning' = if withRet then "RETURNING(" <> escapeS (fromString idName) <> ")" else mempty+    _           -> (constrParams c, mempty)   fieldNames   = renderFields escapeS fields-  placeholders = renderFields (const $ fromChar '?') fields+  RenderS placeholders vals' = commasJoin $ map renderPersistValue vals  insertList' :: forall m a.(MonadBaseControl IO m, MonadIO m, PersistField a) => [a] -> DbPersist Postgresql m Int64 insertList' (l :: [a]) = do@@ -183,24 +232,24 @@  ---------- -executeRaw' :: MonadIO m => StringS -> [PersistValue] -> DbPersist Postgresql m ()+executeRaw' :: MonadIO m => Utf8 -> [PersistValue] -> DbPersist Postgresql m () executeRaw' query vals = do-  --liftIO $ print $ fromStringS query ""+  --liftIO $ print $ fromUtf8 query ""   Postgresql conn <- DbPersist ask   let stmt = getStatement query   liftIO $ do     _ <- PG.execute conn stmt (map P vals)     return () -renderCond' :: (PersistEntity v, Constructor c) => Cond v c -> Maybe (RenderS StringS)-renderCond' = renderCond proxy escapeS renderEquals renderNotEquals where+renderCond' :: Cond Postgresql r -> Maybe (RenderS Postgresql r)+renderCond' = renderCond escapeS renderEquals renderNotEquals where   renderEquals a b = a <> " IS NOT DISTINCT FROM " <> b   renderNotEquals a b = a <> " IS DISTINCT FROM " <> b -escapeS :: StringS -> StringS+escapeS :: Utf8 -> Utf8 escapeS a = let q = fromChar '"' in q <> a <> q -delim' :: StringS+delim' :: Utf8 delim' = fromChar delim  toEntityPersistValues' :: (MonadBaseControl IO m, MonadIO m, PersistEntity v) => v -> DbPersist Postgresql m [PersistValue]@@ -209,18 +258,21 @@ --- MIGRATION  migrate' :: (PersistEntity v, MonadBaseControl IO m, MonadIO m) => v -> Migration (DbPersist Postgresql m)-migrate' = migrateRecursively (migrateEntity migrationPack) (migrateList migrationPack)+migrate' v = do+  x <- lift $ queryRaw' "SELECT current_schema()" [] id+  let schema = fst $ fromPurePersistValues proxy $ fromJust x+  migrateRecursively (migrateEntity $ migrationPack schema) (migrateList $ migrationPack schema) v -migrationPack :: (MonadBaseControl IO m, MonadIO m) => GM.MigrationPack (DbPersist Postgresql m)-migrationPack = GM.MigrationPack+migrationPack :: (MonadBaseControl IO m, MonadIO m) => String -> GM.MigrationPack (DbPersist Postgresql m)+migrationPack currentSchema = GM.MigrationPack   compareTypes-  compareRefs+  (compareRefs currentSchema)   compareUniqs-  checkTable   migTriggerOnDelete   migTriggerOnUpdate   GM.defaultMigConstr   escape+  DbInt64   "SERIAL PRIMARY KEY UNIQUE"   "INT8"   mainTableId@@ -241,240 +293,249 @@         Just s  -> " DEFAULT " ++ s     ] -checkFunction :: (MonadBaseControl IO m, MonadIO m) => String -> DbPersist Postgresql m (Maybe String)-checkFunction name = do-  x <- queryRaw' "SELECT p.prosrc FROM pg_catalog.pg_namespace n INNER JOIN pg_catalog.pg_proc p ON p.pronamespace = n.oid WHERE n.nspname = 'public' AND p.proname = ?" [toPrimitivePersistValue proxy name] id-  case x of-    Nothing  -> return Nothing-    Just src -> return (fst $ fromPurePersistValues proxy src)--checkTrigger :: (MonadBaseControl IO m, MonadIO m) => String -> DbPersist Postgresql m (Maybe String)-checkTrigger name = do-  x <- queryRaw' "SELECT action_statement FROM information_schema.triggers WHERE trigger_name = ?" [toPrimitivePersistValue proxy name] id-  case x of-    Nothing  -> return Nothing-    Just src -> return (fst $ fromPurePersistValues proxy src)---- it handles only delete operations. So far when list or tuple replace is not allowed, it is ok-migTriggerOnDelete :: (MonadBaseControl IO m, MonadIO m) => String -> [(String, String)] -> DbPersist Postgresql m (Bool, [AlterDB])-migTriggerOnDelete name deletes = do+migTriggerOnDelete :: (MonadBaseControl IO m, MonadIO m) => Maybe String -> String -> [(String, String)] -> DbPersist Postgresql m (Bool, [AlterDB])+migTriggerOnDelete schema name deletes = do   let funcName = name   let trigName = name-  func <- checkFunction funcName-  trig <- checkTrigger trigName+  func <- analyzeFunction schema funcName+  trig <- analyzeTrigger schema trigName   let funcBody = "BEGIN " ++ concatMap snd deletes ++ "RETURN NEW;END;"-      addFunction = CreateOrReplaceFunction $ "CREATE OR REPLACE FUNCTION " ++ escape funcName ++ "() RETURNS trigger AS $$" ++ funcBody ++ "$$ LANGUAGE plpgsql"+      addFunction = CreateOrReplaceFunction $ "CREATE OR REPLACE FUNCTION " ++ withSchema schema funcName ++ "() RETURNS trigger AS $$" ++ funcBody ++ "$$ LANGUAGE plpgsql"       funcMig = case func of         Nothing | null deletes -> []         Nothing   -> [addFunction]         Just body -> if null deletes -- remove old trigger if a datatype earlier had fields of ephemeral types-          then [DropFunction funcName]+          then [DropFunction schema funcName]           else if body == funcBody             then []             -- this can happen when an ephemeral field was added or removed.-            else [DropFunction funcName, addFunction]+            else [DropFunction schema funcName, addFunction] -      trigBody = "EXECUTE PROCEDURE " ++ escape funcName ++ "()"-      addTrigger = AddTriggerOnDelete trigName name trigBody+      trigBody = "EXECUTE PROCEDURE " ++ withSchema schema funcName ++ "()"+      addTrigger = AddTriggerOnDelete schema trigName schema name trigBody       (trigExisted, trigMig) = case trig of         Nothing | null deletes -> (False, [])         Nothing   -> (False, [addTrigger])         Just body -> (True, if null deletes -- remove old trigger if a datatype earlier had fields of ephemeral types-          then [DropTrigger trigName name]+          then [DropTrigger schema trigName schema name]           else if body == trigBody             then []             -- this can happen when an ephemeral field was added or removed.-            else [DropTrigger trigName name, addTrigger])+            else [DropTrigger schema trigName schema name, addTrigger])   return (trigExisted, funcMig ++ trigMig)        -- | Table name and a  list of field names and according delete statements -- assume that this function is called only for ephemeral fields-migTriggerOnUpdate :: (MonadBaseControl IO m, MonadIO m) => String -> String -> String -> DbPersist Postgresql m (Bool, [AlterDB])-migTriggerOnUpdate name fieldName del = do+migTriggerOnUpdate :: (MonadBaseControl IO m, MonadIO m) => Maybe String -> String -> [(String, String)] -> DbPersist Postgresql m [(Bool, [AlterDB])]+migTriggerOnUpdate schema name dels = forM dels $ \(fieldName, del) -> do   let funcName = name ++ delim : fieldName   let trigName = name ++ delim : fieldName-  func <- checkFunction funcName-  trig <- checkTrigger trigName+  func <- analyzeFunction schema funcName+  trig <- analyzeTrigger schema trigName   let funcBody = "BEGIN " ++ del ++ "RETURN NEW;END;"-      addFunction = CreateOrReplaceFunction $ "CREATE OR REPLACE FUNCTION " ++ escape funcName ++ "() RETURNS trigger AS $$" ++ funcBody ++ "$$ LANGUAGE plpgsql"+      addFunction = CreateOrReplaceFunction $ "CREATE OR REPLACE FUNCTION " ++ withSchema schema funcName ++ "() RETURNS trigger AS $$" ++ funcBody ++ "$$ LANGUAGE plpgsql"       funcMig = case func of         Nothing   -> [addFunction]         Just body -> if body == funcBody             then []             -- this can happen when an ephemeral field was added or removed.-            else [DropFunction funcName, addFunction]+            else [DropFunction schema funcName, addFunction] -      trigBody = "EXECUTE PROCEDURE " ++ escape funcName ++ "()"-      addTrigger = AddTriggerOnUpdate trigName name fieldName trigBody+      trigBody = "EXECUTE PROCEDURE " ++ withSchema schema funcName ++ "()"+      addTrigger = AddTriggerOnUpdate schema trigName schema name (Just fieldName) trigBody       (trigExisted, trigMig) = case trig of         Nothing   -> (False, [addTrigger])         Just body -> (True, if body == trigBody             then []             -- this can happen when an ephemeral field was added or removed.-            else [DropTrigger trigName name, addTrigger])+            else [DropTrigger schema trigName schema name, addTrigger])   return (trigExisted, funcMig ++ trigMig)   -checkTable :: (MonadBaseControl IO m, MonadIO m) => String -> DbPersist Postgresql m (Maybe (Either [String] TableInfo))-checkTable name = do-  table <- queryRaw' "SELECT * FROM information_schema.tables WHERE table_name=?" [toPrimitivePersistValue proxy name] id+analyzeTable' :: (MonadBaseControl IO m, MonadIO m) => Maybe String -> String -> DbPersist Postgresql m (Either [String] (Maybe TableInfo))+analyzeTable' schema name = do+  table <- queryRaw' "SELECT * FROM information_schema.tables WHERE table_schema = coalesce(?, current_schema()) AND table_name = ?" [toPrimitivePersistValue proxy schema, toPrimitivePersistValue proxy name] id   case table of     Just _ -> do       -- omit primary keys-      cols <- queryRaw' "SELECT c.column_name, c.is_nullable, c.udt_name, c.character_maximum_length, c.numeric_precision, c.numeric_scale, c.datetime_precision, c.interval_type, c.column_default FROM information_schema.columns c WHERE c.table_name=? AND c.column_name NOT IN (SELECT c.column_name FROM information_schema.table_constraints tc INNER JOIN information_schema.constraint_column_usage u ON tc.constraint_catalog = u.constraint_catalog AND tc.constraint_schema=u.constraint_schema AND tc.constraint_name=u.constraint_name INNER JOIN information_schema.columns c ON u.table_catalog=c.table_catalog AND u.table_schema=c.table_schema AND u.table_name=c.table_name AND u.column_name=c.column_name WHERE tc.constraint_type='PRIMARY KEY' AND tc.table_name=?) ORDER BY c.ordinal_position" [toPrimitivePersistValue proxy name, toPrimitivePersistValue proxy name] (mapAllRows $ return . getColumn name . fst . fromPurePersistValues proxy)+      let colQuery = "SELECT c.column_name, c.is_nullable, c.udt_name, c.column_default, c.character_maximum_length, c.numeric_precision, c.numeric_scale, c.datetime_precision, c.interval_type, a.attndims AS array_dims, te.typname AS array_elem\+\  FROM pg_catalog.pg_attribute a\+\  INNER JOIN pg_catalog.pg_class cl ON cl.oid = a.attrelid\+\  INNER JOIN pg_catalog.pg_namespace n ON n.oid = cl.relnamespace\+\  INNER JOIN information_schema.columns c ON c.column_name = a.attname AND c.table_name = cl.relname AND c.table_schema = n.nspname\+\  INNER JOIN pg_catalog.pg_type t ON t.oid = a.atttypid\+\  LEFT JOIN pg_catalog.pg_type te ON te.oid = t.typelem\+\  WHERE c.table_schema = coalesce(?, current_schema()) AND c.table_name=?\+\  ORDER BY c.ordinal_position"++      cols <- queryRaw' colQuery [toPrimitivePersistValue proxy schema, toPrimitivePersistValue proxy name] (mapAllRows $ return . getColumn name . fst . fromPurePersistValues proxy)       let (col_errs, cols') = partitionEithers cols       -      uniqConstraints <- queryRaw' "SELECT u.constraint_name, u.column_name FROM information_schema.table_constraints tc INNER JOIN information_schema.constraint_column_usage u ON tc.constraint_catalog=u.constraint_catalog AND tc.constraint_schema=u.constraint_schema AND tc.constraint_name=u.constraint_name WHERE u.table_name=? AND tc.constraint_type='UNIQUE' ORDER BY u.constraint_name, u.column_name" [toPrimitivePersistValue proxy name] (mapAllRows $ return . fst . fromPurePersistValues proxy)-      uniqIndexes <- queryRaw' "SELECT ic.relname, a.attname FROM pg_catalog.pg_attribute a INNER JOIN pg_catalog.pg_class ic ON ic.oid = a.attrelid INNER JOIN pg_catalog.pg_index i ON i.indexrelid = ic.oid INNER JOIN pg_catalog.pg_class tc ON i.indrelid = tc.oid WHERE tc.relname = ? AND a.attnum > 0 AND NOT a.attisdropped AND ic.oid NOT IN (SELECT conindid FROM pg_catalog.pg_constraint) AND NOT i.indisprimary AND i.indisunique ORDER BY ic.relname, a.attnum" [toPrimitivePersistValue proxy name] (mapAllRows $ return . fst . fromPurePersistValues proxy)+      let constraintQuery = "SELECT u.constraint_name, u.column_name FROM information_schema.table_constraints tc INNER JOIN information_schema.constraint_column_usage u ON tc.constraint_catalog=u.constraint_catalog AND tc.constraint_schema=u.constraint_schema AND tc.constraint_name=u.constraint_name WHERE tc.constraint_type=? AND tc.table_schema=coalesce(?,current_schema()) AND u.table_name=? ORDER BY u.constraint_name, u.column_name"+      +      uniqConstraints <- queryRaw' constraintQuery [toPrimitivePersistValue proxy ("UNIQUE" :: String), toPrimitivePersistValue proxy schema, toPrimitivePersistValue proxy name] (mapAllRows $ return . fst . fromPurePersistValues proxy)+      uniqPrimary <- queryRaw' constraintQuery [toPrimitivePersistValue proxy ("PRIMARY KEY" :: String), toPrimitivePersistValue proxy schema, toPrimitivePersistValue proxy name] (mapAllRows $ return . fst . fromPurePersistValues proxy)+      uniqIndexes <- queryRaw' "SELECT ic.relname, a.attname FROM pg_catalog.pg_attribute a INNER JOIN pg_catalog.pg_class ic ON ic.oid = a.attrelid INNER JOIN pg_catalog.pg_index i ON i.indexrelid = ic.oid INNER JOIN pg_catalog.pg_class tc ON i.indrelid = tc.oid INNER JOIN pg_namespace sch ON sch.oid = tc.relnamespace WHERE sch.nspname = coalesce(?, current_schema()) AND tc.relname = ? AND a.attnum > 0 AND NOT a.attisdropped AND ic.oid NOT IN (SELECT conindid FROM pg_catalog.pg_constraint) AND NOT i.indisprimary AND i.indisunique ORDER BY ic.relname, a.attnum" [toPrimitivePersistValue proxy schema, toPrimitivePersistValue proxy name] (mapAllRows $ return . fst . fromPurePersistValues proxy)       let mkUniqs typ = map (\us -> UniqueDef' (fst $ head us) typ (map snd us)) . groupBy ((==) `on` fst)-      let uniqs = mkUniqs UniqueConstraint uniqConstraints ++ mkUniqs UniqueIndex uniqIndexes-      references <- checkTableReferences name-      primaryKeyResult <- checkPrimaryKey name-      let (primaryKey, uniqs') = case primaryKeyResult of-            (Left primaryKeyName) -> (primaryKeyName, uniqs)-            (Right u) -> (Nothing, u:uniqs)-      return $ Just $ case col_errs of-        []   -> Right $ TableInfo primaryKey cols' uniqs' references+      let uniqs = mkUniqs UniqueConstraint uniqConstraints ++ mkUniqs UniqueIndex uniqIndexes ++ mkUniqs UniquePrimary uniqPrimary+      references <- analyzeTableReferences schema name+      return $ case col_errs of+        []   -> Right $ Just $ TableInfo cols' uniqs references         errs -> Left errs-    Nothing -> return Nothing--checkPrimaryKey :: (MonadBaseControl IO m, MonadIO m) => String -> DbPersist Postgresql m (Either (Maybe String) UniqueDef')-checkPrimaryKey name = do-  uniqRows <- queryRaw' "SELECT u.constraint_name, u.column_name FROM information_schema.table_constraints tc INNER JOIN information_schema.constraint_column_usage u ON tc.constraint_catalog = u.constraint_catalog AND tc.constraint_schema=u.constraint_schema AND tc.constraint_name=u.constraint_name WHERE tc.constraint_type='PRIMARY KEY' AND tc.table_name=?" [toPrimitivePersistValue proxy name] (mapAllRows $ return . fst . fromPurePersistValues proxy)-  let mkUniq us = UniqueDef' (fst $ head us) UniqueConstraint (map snd us)-  return $ case uniqRows of-    [] -> Left Nothing-    [(_, primaryKeyName)] -> Left $ Just primaryKeyName-    us -> Right $ mkUniq us+    Nothing -> return $ Right Nothing -getColumn :: String -> (String, String, String, (Maybe Int, Maybe Int, Maybe Int, Maybe Int, Maybe String), Maybe String) -> Either String Column-getColumn _ (column_name, is_nullable, udt_name, modifiers, d) = Right $ Column column_name (is_nullable == "YES") t d where-  t = readSqlType udt_name modifiers+getColumn :: String -> ((String, String, String, Maybe String), (Maybe Int, Maybe Int, Maybe Int, Maybe Int, Maybe String), (Int, Maybe String)) -> Either String Column+getColumn _ ((column_name, is_nullable, udt_name, d), modifiers, arr_info) = Right $ Column column_name (is_nullable == "YES") t d where+  t = readSqlType udt_name modifiers arr_info -checkTableReferences :: (MonadBaseControl IO m, MonadIO m) => String -> DbPersist Postgresql m [(Maybe String, Reference)]-checkTableReferences tableName = do-  let sql = "SELECT c.conname, c.foreign_table || '', a_child.attname AS child, a_parent.attname AS parent FROM (SELECT r.confrelid::regclass AS foreign_table, r.conrelid, r.confrelid, unnest(r.conkey) AS conkey, unnest(r.confkey) AS confkey, r.conname FROM pg_catalog.pg_constraint r WHERE r.conrelid = ?::regclass AND r.contype = 'f') AS c INNER JOIN pg_attribute a_parent ON a_parent.attnum = c.confkey AND a_parent.attrelid = c.confrelid INNER JOIN pg_attribute a_child ON a_child.attnum = c.conkey AND a_child.attrelid = c.conrelid ORDER BY c.conname"-  x <- queryRaw' sql [toPrimitivePersistValue proxy $ escape tableName] $ mapAllRows (return . fst . fromPurePersistValues proxy)-  -- (refName, (parentTable, (childColumn, parentColumn)))-  let mkReference xs = (Just refName, (parentTable, map (snd . snd) xs)) where-        (refName, (parentTable, _)) = head xs+analyzeTableReferences :: (MonadBaseControl IO m, MonadIO m) => Maybe String -> String -> DbPersist Postgresql m [(Maybe String, Reference)]+analyzeTableReferences schema tName = do+  let sql = "SELECT c.conname, sch_parent.nspname, cl_parent.relname, c. confdeltype, c.confupdtype, a_child.attname AS child, a_parent.attname AS parent FROM\+\  (SELECT r.conrelid, r.confrelid, unnest(r.conkey) AS conkey, unnest(r.confkey) AS confkey, r.conname, r.confupdtype, r.confdeltype\+\    FROM pg_catalog.pg_constraint r WHERE r.contype = 'f'\+\  ) AS c\+\  INNER JOIN pg_attribute a_parent ON a_parent.attnum = c.confkey AND a_parent.attrelid = c.confrelid\+\  INNER JOIN pg_class cl_parent ON cl_parent.oid = c.confrelid\+\  INNER JOIN pg_namespace sch_parent ON sch_parent.oid = cl_parent.relnamespace\+\  INNER JOIN pg_attribute a_child ON a_child.attnum = c.conkey AND a_child.attrelid = c.conrelid\+\  INNER JOIN pg_class cl_child ON cl_child.oid = c.conrelid AND cl_child.relname = ?\+\  INNER JOIN pg_namespace sch_child ON sch_child.oid = cl_child.relnamespace AND sch_child.nspname = coalesce(?, current_schema())\+\  ORDER BY c.conname"+  x <- queryRaw' sql [toPrimitivePersistValue proxy tName, toPrimitivePersistValue proxy schema] $ mapAllRows (return . fst . fromPurePersistValues proxy)+  -- (refName, ((parentTableSchema, parentTable, onDelete, onUpdate), (childColumn, parentColumn)))+  let mkReference xs = (Just refName, Reference parentSchema parentTable pairs (mkAction onDelete) (mkAction onUpdate)) where+        pairs = map (snd . snd) xs+        (refName, ((parentSchema, parentTable, onDelete, onUpdate), _)) = head xs+        mkAction c = Just $ case c of+          "a" -> NoAction+          "r" -> Restrict+          "c" -> Cascade+          "n" -> SetNull+          "d" -> SetDefault+          _ -> error $ "unknown reference action type: " ++ c       references = map mkReference $ groupBy ((==) `on` fst) x   return references  showAlterDb :: AlterDB -> SingleMigration showAlterDb (AddTable s) = Right [(False, defaultPriority, s)]-showAlterDb (AlterTable t _ _ _ alts) = Right $ map (showAlterTable t) alts-showAlterDb (DropTrigger trigName tName) = Right [(False, triggerPriority, "DROP TRIGGER " ++ escape trigName ++ " ON " ++ escape tName)]-showAlterDb (AddTriggerOnDelete trigName tName body) = Right [(False, triggerPriority, "CREATE TRIGGER " ++ escape trigName ++ " AFTER DELETE ON " ++ escape tName ++ " FOR EACH ROW " ++ body)]-showAlterDb (AddTriggerOnUpdate trigName tName fName body) = Right [(False, triggerPriority, "CREATE TRIGGER " ++ escape trigName ++ " AFTER UPDATE OF " ++ escape fName ++ " ON " ++ escape tName ++ " FOR EACH ROW " ++ body)]+showAlterDb (AlterTable sch t _ _ _ alts) = Right $ concatMap (showAlterTable $ withSchema sch t) alts+showAlterDb (DropTrigger schTrg trigName schTbl tName) = Right [(False, triggerPriority, "DROP TRIGGER " ++ withSchema schTrg trigName ++ " ON " ++ withSchema schTbl tName)]+showAlterDb (AddTriggerOnDelete schTrg trigName schTbl tName body) = Right [(False, triggerPriority, "CREATE TRIGGER " ++ withSchema schTrg trigName ++ " AFTER DELETE ON " ++ withSchema schTbl tName ++ " FOR EACH ROW " ++ body)]+showAlterDb (AddTriggerOnUpdate schTrg trigName schTbl tName fName body) = Right [(False, triggerPriority, "CREATE TRIGGER " ++ withSchema schTrg trigName ++ " AFTER UPDATE OF " ++ fName' ++ " ON " ++ withSchema schTbl tName ++ " FOR EACH ROW " ++ body)] where+    fName' = maybe (error $ "showAlterDb: AddTriggerOnUpdate does not have fieldName for trigger " ++ trigName) escape fName showAlterDb (CreateOrReplaceFunction s) = Right [(False, functionPriority, s)]-showAlterDb (DropFunction funcName) = Right [(False, functionPriority, "DROP FUNCTION " ++ escape funcName ++ "()")]-                 -showAlterTable :: String -> AlterTable -> (Bool, Int, String)-showAlterTable table (AlterColumn alt) = showAlterColumn table alt-showAlterTable table (AddUnique (UniqueDef' uName UniqueConstraint cols)) = (False, defaultPriority, concat+showAlterDb (DropFunction sch funcName) = Right [(False, functionPriority, "DROP FUNCTION " ++ withSchema sch funcName ++ "()")]++showAlterTable :: String -> AlterTable -> [(Bool, Int, String)]+showAlterTable table (AddColumn col) = [(False, defaultPriority, concat   [ "ALTER TABLE "-  , escape table-  , " ADD CONSTRAINT "-  , escape uName+  , table+  , " ADD COLUMN "+  , showColumn col+  ])]+showAlterTable table (DropColumn name) = [(True, defaultPriority, concat+  [ "ALTER TABLE "+  , table+  , " DROP COLUMN "+  , escape name+  ])]+showAlterTable table (AlterColumn col alts) = map (showAlterColumn table $ colName col) alts+showAlterTable table (AddUnique (UniqueDef' uName UniqueConstraint cols)) = [(False, defaultPriority, concat+  [ "ALTER TABLE "+  , table+  , " ADD"+  , maybe "" ((" CONSTRAINT " ++) . escape) uName   , " UNIQUE("   , intercalate "," $ map escape cols   , ")"-  ])-showAlterTable table (AddUnique (UniqueDef' uName UniqueIndex cols)) = (False, defaultPriority, concat+  ])]+showAlterTable table (AddUnique (UniqueDef' uName UniqueIndex cols)) = [(False, defaultPriority, concat   [ "CREATE UNIQUE INDEX "-  , escape uName+  , maybe (error $ "showAlterTable: index for table " ++ table ++ " does not have a name") escape uName   , " ON "-  , escape table+  , table   , "("   , intercalate "," $ map escape cols   , ")"-  ])-showAlterTable table (DropConstraint uName) = (False, defaultPriority, concat+  ])]+showAlterTable table (AddUnique (UniqueDef' uName UniquePrimary cols)) = [(False, defaultPriority, concat   [ "ALTER TABLE "-  , escape table+  , table+  , " ADD"+  , maybe "" ((" CONSTRAINT " ++) . escape) uName+  , " PRIMARY KEY("+  , intercalate "," $ map escape cols+  , ")"+  ])]+showAlterTable table (DropConstraint uName) = [(False, defaultPriority, concat+  [ "ALTER TABLE "+  , table   , " DROP CONSTRAINT "   , escape uName-  ])-showAlterTable _ (DropIndex uName) = (False, defaultPriority, concat+  ])]+showAlterTable _ (DropIndex uName) = [(False, defaultPriority, concat   [ "DROP INDEX "   , escape uName-  ])-showAlterTable table (AddReference (tName, columns)) = (False, referencePriority, concat+  ])]+showAlterTable table (AddReference (Reference schema tName columns onDelete onUpdate)) = [(False, referencePriority, concat   [ "ALTER TABLE "-  , escape table+  , table   , " ADD FOREIGN KEY("   , our   , ") REFERENCES "-  , escape tName+  , maybe "" (\x -> escape x ++ ".") schema ++ escape tName   , "("   , foreign   , ")"-  ]) where+  , maybe "" ((" ON DELETE " ++) . showReferenceAction) onDelete+  , maybe "" ((" ON UPDATE " ++) . showReferenceAction) onUpdate+  ])] where   (our, foreign) = f *** f $ unzip columns   f = intercalate ", " . map escape-showAlterTable table (DropReference name) = (False, defaultPriority,-    "ALTER TABLE " ++ escape table ++ " DROP CONSTRAINT " ++ name)+showAlterTable table (DropReference name) = [(False, defaultPriority,+    "ALTER TABLE " ++ table ++ " DROP CONSTRAINT " ++ name)] -showAlterColumn :: String -> AlterColumn' -> (Bool, Int, String)-showAlterColumn table (n, Type t) = (False, defaultPriority, concat+showAlterColumn :: String -> String -> AlterColumn -> (Bool, Int, String)+showAlterColumn table n (Type t) = (False, defaultPriority, concat   [ "ALTER TABLE "-  , escape table+  , table   , " ALTER COLUMN "   , escape n   , " TYPE "   , showSqlType t   ])-showAlterColumn table (n, IsNull) = (False, defaultPriority, concat+showAlterColumn table n IsNull = (False, defaultPriority, concat   [ "ALTER TABLE "-  , escape table+  , table   , " ALTER COLUMN "   , escape n   , " DROP NOT NULL"   ])-showAlterColumn table (n, NotNull) = (False, defaultPriority, concat+showAlterColumn table n NotNull = (False, defaultPriority, concat   [ "ALTER TABLE "-  , escape table+  , table   , " ALTER COLUMN "   , escape n   , " SET NOT NULL"   ])-showAlterColumn table (_, Add col) = (False, defaultPriority, concat-  [ "ALTER TABLE "-  , escape table-  , " ADD COLUMN "-  , showColumn col-  ])-showAlterColumn table (n, Drop) = (True, defaultPriority, concat-  [ "ALTER TABLE "-  , escape table-  , " DROP COLUMN "-  , escape n-  ])-showAlterColumn table (n, AddPrimaryKey) = (False, defaultPriority, concat-  [ "ALTER TABLE "-  , escape table-  , " ADD COLUMN "-  , escape n-  , " SERIAL PRIMARY KEY UNIQUE"-  ])-showAlterColumn table (n, Default s) = (False, defaultPriority, concat++showAlterColumn table n (Default s) = (False, defaultPriority, concat   [ "ALTER TABLE "-  , escape table+  , table   , " ALTER COLUMN "   , escape n   , " SET DEFAULT "   , s   ])-showAlterColumn table (n, NoDefault) = (False, defaultPriority, concat+showAlterColumn table n NoDefault = (False, defaultPriority, concat   [ "ALTER TABLE "-  , escape table+  , table   , " ALTER COLUMN "   , escape n   , " DROP DEFAULT"   ])-showAlterColumn table (n, UpdateValue s) = (False, defaultPriority, concat+showAlterColumn table n (UpdateValue s) = (False, defaultPriority, concat   [ "UPDATE "-  , escape table+  , table   , " SET "   , escape n   , "="@@ -485,12 +546,12 @@   ])  -- | udt_name, character_maximum_length, numeric_precision, numeric_scale, datetime_precision, interval_type-readSqlType :: String -> (Maybe Int, Maybe Int, Maybe Int, Maybe Int, Maybe String) -> DbType-readSqlType typ (character_maximum_length, numeric_precision, numeric_scale, datetime_precision, _) = (case typ of+readSqlType :: String -> (Maybe Int, Maybe Int, Maybe Int, Maybe Int, Maybe String) -> (Int, Maybe String) -> DbType+readSqlType typ (character_maximum_length, numeric_precision, numeric_scale, datetime_precision, _) (array_ndims, array_elem) = (case typ of   "int4" -> DbInt32   "int8" -> DbInt64-  "varchar" -> maybe DbString (DbOther . ("varchar"++) . wrap . show) character_maximum_length-  "numeric" -> DbOther $ "numeric" ++ maybe "" wrap attrs where+  "varchar" -> maybe DbString (dbOther . ("varchar"++) . wrap . show) character_maximum_length+  "numeric" -> dbOther $ "numeric" ++ maybe "" wrap attrs where     attrs = liftM2 (\a b -> if b == 0 then show a else show a ++ ", " ++ show b) numeric_precision numeric_scale   "date" -> DbDay   "bool" -> DbBool@@ -500,9 +561,12 @@   "float4" -> DbReal   "float8" -> DbReal   "bytea" -> DbBlob-  a -> DbOther a) where+  _ | array_ndims > 0 -> dbOther $ arr ++ concat (replicate array_ndims "[]") where+    arr = fromMaybe (error "readSqlType: array with elem type Nothing") array_elem+  a -> dbOther a) where+    dbOther = DbOther . OtherTypeDef . const     wrap x = "(" ++ x ++ ")"-    mkDate t name = maybe t (DbOther . (name++) . wrap . show) datetime_precision'+    mkDate t name = maybe t (dbOther . (name++) . wrap . show) datetime_precision'     defDateTimePrec = 6     datetime_precision' = datetime_precision >>= \p -> if p == defDateTimePrec then Nothing else Just p @@ -517,38 +581,35 @@ showSqlType DbDayTime = "TIMESTAMP" showSqlType DbDayTimeZoned = "TIMESTAMP WITH TIME ZONE" showSqlType DbBlob = "BYTEA"-showSqlType (DbOther name) = name+showSqlType (DbOther (OtherTypeDef f)) = f showSqlType showSqlType (DbMaybe t) = showSqlType t showSqlType (DbList _ _) = showSqlType DbInt64-showSqlType (DbEntity Nothing _) = showSqlType DbInt64+showSqlType (DbEntity Nothing _ _ _) = showSqlType DbInt64 showSqlType t = error $ "showSqlType: DbType does not have corresponding database type: " ++ show t  compareUniqs :: UniqueDef' -> UniqueDef' -> Bool-compareUniqs (UniqueDef' name1 typ1 cols1) (UniqueDef' name2 typ2 cols2) = name1 == name2 && typ1 == typ2 && haveSameElems (==) cols1 cols2+compareUniqs (UniqueDef' _ UniquePrimary cols1) (UniqueDef' _ UniquePrimary cols2) = haveSameElems (==) cols1 cols2+-- only one of the uniques is primary+compareUniqs (UniqueDef' _ type1 _) (UniqueDef' _ type2 _) | UniquePrimary `elem` [type1, type2] = False+compareUniqs (UniqueDef' name1 type1 cols1) (UniqueDef' name2 type2 cols2) = fromMaybe True (liftM2 (==) name1 name2) && type1 == type2 && haveSameElems (==) cols1 cols2 -compareRefs :: (Maybe String, Reference) -> (Maybe String, Reference) -> Bool-compareRefs (_, (tbl1, pairs1)) (_, (tbl2, pairs2)) = unescape tbl1 == unescape tbl2 && haveSameElems (==) pairs1 pairs2 where-  unescape name = if head name == '"' && last name == '"' then tail $ init name else name+compareRefs :: String -> (Maybe String, Reference) -> (Maybe String, Reference) -> Bool+compareRefs currentSchema (_, Reference sch1 tbl1 pairs1 onDel1 onUpd1) (_, Reference sch2 tbl2 pairs2 onDel2 onUpd2) =+     fromMaybe currentSchema sch1 == fromMaybe currentSchema sch2+  && unescape tbl1 == unescape tbl2+  && haveSameElems (==) pairs1 pairs2+  && fromMaybe NoAction onDel1 == fromMaybe NoAction onDel2+  && fromMaybe NoAction onUpd1 == fromMaybe NoAction onUpd2 where+    unescape name = if head name == '"' && last name == '"' then tail $ init name else name  compareTypes :: DbType -> DbType -> Bool-compareTypes type1 type2 = showSqlType (simplifyType type1) == showSqlType (simplifyType type2)---- | Converts complex datatypes that reference other data to id type DbInt64. Does not handle DbEmbedded-simplifyType :: DbType -> DbType-simplifyType (DbEntity Nothing _) = DbInt64-simplifyType (DbList _ _) = DbInt64-simplifyType x = x+compareTypes type1 type2 = f type1 == f type2 where+  f = map toUpper . showSqlType -defaultPriority :: Int+defaultPriority, referencePriority, functionPriority, triggerPriority :: Int defaultPriority = 0--referencePriority :: Int referencePriority = 1--functionPriority :: Int functionPriority = 2--triggerPriority :: Int triggerPriority = 3  mainTableId :: String@@ -560,13 +621,13 @@ escape :: String -> String escape s = '\"' : s ++ "\""   -getStatement :: StringS -> PG.Query-getStatement sql = PG.Query $ T.encodeUtf8 $ T.pack $ fromStringS sql ""+getStatement :: Utf8 -> PG.Query+getStatement sql = PG.Query $ fromUtf8 sql -queryRawTyped' :: (MonadBaseControl IO m, MonadIO m) => StringS -> [DbType] -> [PersistValue] -> (RowPopper (DbPersist Postgresql m) -> DbPersist Postgresql m a) -> DbPersist Postgresql m a+queryRawTyped' :: (MonadBaseControl IO m, MonadIO m) => Utf8 -> [DbType] -> [PersistValue] -> (RowPopper (DbPersist Postgresql m) -> DbPersist Postgresql m a) -> DbPersist Postgresql m a queryRawTyped' query _ vals f = queryRaw' query vals f -queryRaw' :: (MonadBaseControl IO m, MonadIO m) => StringS -> [PersistValue] -> (RowPopper (DbPersist Postgresql m) -> DbPersist Postgresql m a) -> DbPersist Postgresql m a+queryRaw' :: (MonadBaseControl IO m, MonadIO m) => Utf8 -> [PersistValue] -> (RowPopper (DbPersist Postgresql m) -> DbPersist Postgresql m a) -> DbPersist Postgresql m a queryRaw' query vals f = do   Postgresql conn <- DbPersist ask   rawquery <- liftIO $ PG.formatQuery conn (getStatement query) (map P vals)@@ -590,17 +651,20 @@             merr <- LibPQ.errorMessage rawconn             fail $ "Postgresql.withStmt': bad result status " ++                    show status ++ " (" ++ show msg ++ ")" ++-                   maybe "" ((". Error message: "++) . unpack) merr+                   maybe "" ((". Error message: " ++) . unpack) merr          -- Get number and type of columns         cols <- LibPQ.nfields ret         getters <- forM [0..cols-1] $ \col -> do           oid <- LibPQ.ftype ret col           case PG.oid2builtin oid of-            Nothing -> fail $ "Postgresql.withStmt': could not " ++-                              "recognize Oid of column " +++            -- TODO: this is a temporary hack until postgresql-simple supports arrays and has more builtin types. Restore fail clause then.+            Nothing -> return $ getGetter PG.Unknown $+                       PG.Field ret col $ PG.builtin2typname PG.Unknown+             {- fail $ "Postgresql.withStmt': could not " +++                              "recognize " ++ show oid ++ " of column " ++                               show (let LibPQ.Col i = col in i) ++-                              " (counting from zero)"+                              " (counting from zero)" -}             Just bt -> return $ getGetter bt $                        PG.Field ret col $                        PG.builtin2typname bt@@ -621,21 +685,22 @@             Errors (exc:_) -> throw exc             Errors [] -> error "Got an Errors, but no exceptions"             Ok v  -> return v-    + -- | Avoid orphan instances. newtype P = P PersistValue  instance PGTF.ToField P where-    toField (P (PersistString t))        = PGTF.toField t-    toField (P (PersistByteString bs)) = PGTF.toField (PG.Binary bs)-    toField (P (PersistInt64 i))       = PGTF.toField i-    toField (P (PersistDouble d))      = PGTF.toField d-    toField (P (PersistBool b))        = PGTF.toField b-    toField (P (PersistDay d))         = PGTF.toField d-    toField (P (PersistTimeOfDay t))   = PGTF.toField t-    toField (P (PersistUTCTime t))     = PGTF.toField t-    toField (P (PersistZonedTime (ZT t))) = PGTF.toField t-    toField (P PersistNull)            = PGTF.toField PG.Null+  toField (P (PersistString t))         = PGTF.toField t+  toField (P (PersistByteString bs))    = PGTF.toField (PG.Binary bs)+  toField (P (PersistInt64 i))          = PGTF.toField i+  toField (P (PersistDouble d))         = PGTF.toField d+  toField (P (PersistBool b))           = PGTF.toField b+  toField (P (PersistDay d))            = PGTF.toField d+  toField (P (PersistTimeOfDay t))      = PGTF.toField t+  toField (P (PersistUTCTime t))        = PGTF.toField t+  toField (P (PersistZonedTime (ZT t))) = PGTF.toField t+  toField (P PersistNull)               = PGTF.toField PG.Null+  toField (P (PersistCustom _ _))       = error "toField: unexpected PersistCustom"  type Getter a = PG.Field -> Maybe ByteString -> Ok a @@ -670,14 +735,13 @@ getGetter PG.Void                  = \_ _ -> Ok PersistNull getGetter _ = \f dat -> fmap (PersistByteString . unBinary) $ case dat of   Nothing -> PGFF.returnError PGFF.UnexpectedNull f ""-  Just str -> case PGFF.format f of-    LibPQ.Text -> case unsafePerformIO (LibPQ.unescapeBytea str) of-      Nothing  -> PGFF.returnError PGFF.ConversionFailed f "unescapeBytea failed"-      Just str' -> return $ PG.Binary str'-    LibPQ.Binary -> return $ PG.Binary $ copy $ str+  Just str -> return $ PG.Binary $ copy $ str  unBinary :: PG.Binary a -> a unBinary (PG.Binary x) = x  proxy :: Proxy Postgresql proxy = error "Proxy Postgresql"++withSchema :: Maybe String -> String -> String+withSchema sch name = maybe "" (\x -> escape x ++ ".") sch ++ escape name
+ Database/Groundhog/Postgresql/Array.hs view
@@ -0,0 +1,181 @@+{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, OverloadedStrings, UndecidableInstances, OverlappingInstances, BangPatterns #-}
+
+-- | See detailed documentation for PostgreSQL arrays at http://www.postgresql.org/docs/9.2/static/arrays.html and http://www.postgresql.org/docs/9.2/static/functions-array.html
+module Database.Groundhog.Postgresql.Array
+  (
+    Array(..)
+  , (!)
+  , (!:)
+  , append
+  , prepend
+  , arrayCat
+  , arrayDims
+  , arrayNDims
+  , arrayLower
+  , arrayUpper
+  , arrayLength
+  , arrayToString
+  , stringToArray
+  , any
+  , all
+  , (@>)
+  , (<@)
+  , overlaps
+  ) where
+
+import Database.Groundhog.Core
+import Database.Groundhog.Generic
+import Database.Groundhog.Generic.Sql hiding (append)
+import Database.Groundhog.Postgresql hiding (append)
+
+import Blaze.ByteString.Builder (fromByteString, toByteString)
+import Blaze.ByteString.Builder.Word (fromWord8)
+import Control.Applicative
+import Data.Attoparsec.Char8
+import qualified Data.Attoparsec as A
+import qualified Data.Attoparsec.Zepto as Z
+import Data.ByteString.Char8 (ByteString)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Unsafe as B
+import Data.Monoid
+import Data.Word
+import Prelude hiding (all, any)
+
+-- | Represents PostgreSQL arrays
+newtype Array a = Array [a] deriving (Eq, Show)
+
+instance (ArrayElem a, PersistField a) => PersistField (Array a) where
+  persistName a = "Array" ++ delim : persistName ((undefined :: Array a -> a) a)
+  toPersistValues = primToPersistValue
+  fromPersistValues = primFromPersistValue
+  dbType a = DbOther $ OtherTypeDef $ \f -> f elemType ++ "[]" where
+    elemType = dbType ((undefined :: Array a -> a) a)
+
+class ArrayElem a where
+  -- this function is added to avoid GHC bug 7126 which appears when PrimitivePersistField is added as class constraint
+  toElem :: DbDescriptor db => Proxy db -> a -> PersistValue
+  parseElem :: DbDescriptor db => Proxy db -> Parser a
+
+instance ArrayElem a => ArrayElem (Array a) where
+  toElem p (Array xs) = PersistCustom ("ARRAY[" <> query <> fromChar ']') (vals []) where
+    RenderS query vals = commasJoin $ map (renderPersistValue . toElem p) xs
+  parseElem = parseArr
+
+instance PrimitivePersistField a => ArrayElem a where
+  toElem = toPrimitivePersistValue
+  parseElem p = parseString >>= (return . fromPrimitivePersistValue p . PersistByteString)
+
+instance (ArrayElem a, PersistField a) => PrimitivePersistField (Array a) where
+  toPrimitivePersistValue = toElem
+  fromPrimitivePersistValue p a = parseHelper parser a where
+    dimensions = char '[' *> takeWhile1 (/= '=') *> char '='
+    parser = optional dimensions *> parseArr p
+
+parseString :: Parser ByteString
+parseString = (char '"' *> jstring_)
+          <|> takeWhile1 (\c -> c /= ',' && c /= '}')
+          
+-- Borrowed from aeson
+jstring_ :: Parser ByteString
+jstring_ = {-# SCC "jstring_" #-} do
+  s <- A.scan False $ \s c -> if s then Just False
+                                   else if c == doubleQuote
+                                        then Nothing
+                                        else Just (c == backslash)
+  _ <- A.word8 doubleQuote
+  if backslash `B.elem` s
+    then case Z.parse unescape s of
+           Right r  -> return r
+           Left err -> fail err
+    else return s
+{-# INLINE jstring_ #-}
+
+-- Borrowed from aeson
+unescape :: Z.Parser ByteString
+unescape = toByteString <$> go mempty where
+  go acc = do
+    h <- Z.takeWhile (/=backslash)
+    let rest = do
+          start <- Z.take 2
+          let !slash = B.unsafeHead start
+              !t = B.unsafeIndex start 1
+              escape = if t == doubleQuote || t == backslash
+                then t
+                else 255
+          if slash /= backslash || escape == 255
+            then fail "invalid array escape sequence"
+            else do
+            let cont m = go (acc `mappend` fromByteString h `mappend` m)
+                {-# INLINE cont #-}
+            cont (fromWord8 escape)
+    done <- Z.atEnd
+    if done
+      then return (acc `mappend` fromByteString h)
+      else rest
+
+doubleQuote, backslash :: Word8
+doubleQuote = 34
+backslash = 92
+  
+parseArr :: (DbDescriptor db, ArrayElem a) => Proxy db -> Parser (Array a)
+parseArr p = Array <$> (char '{' *> parseElem p `sepBy` char ',' <* char '}')
+
+(!) :: (ExpressionOf Postgresql r a (Array elem), ExpressionOf Postgresql r b Int) => a -> b -> Expr Postgresql r elem
+(!) arr i = Expr $ Snippet $ \esc _ -> [renderExpr esc (toExpr arr) <> "[" <> renderExpr esc (toExpr i) <> "]"]
+
+(!:) :: (ExpressionOf Postgresql r a (Array elem), ExpressionOf Postgresql r i1 Int, ExpressionOf Postgresql r i2 Int) => a -> (i1, i2) -> Expr Postgresql r (Array elem)
+(!:) arr (i1, i2) = Expr $ Snippet $ \esc _ -> [renderExpr esc (toExpr arr) <> "[" <> renderExpr esc (toExpr i1) <> ":" <> renderExpr esc (toExpr i2) <> "]"]
+
+prepend :: (ExpressionOf Postgresql r a elem, ExpressionOf Postgresql r b (Array elem)) => a -> b -> Expr Postgresql r (Array elem)
+prepend a b = Expr $ operator 50 "||" a b
+
+append :: (ExpressionOf Postgresql r a (Array elem), ExpressionOf Postgresql r b elem) => a -> b -> Expr Postgresql r (Array elem)
+append a b = Expr $ operator 50 "||" a b
+
+arrayCat :: (ExpressionOf Postgresql r a (Array elem), ExpressionOf Postgresql r b (Array elem)) => a -> b -> Expr Postgresql r (Array elem)
+arrayCat a b = Expr $ operator 50 "||" a b
+
+arrayDims :: (ExpressionOf Postgresql r a (Array elem)) => a -> Expr Postgresql r String
+arrayDims arr = Expr $ function "array_dims" [toExpr arr]
+
+arrayNDims :: (ExpressionOf Postgresql r a (Array elem)) => a -> Expr Postgresql r Int
+arrayNDims arr = Expr $ function "array_ndims" [toExpr arr]
+
+arrayLower :: (ExpressionOf Postgresql r a (Array elem)) => a -> Int -> Expr Postgresql r Int
+arrayLower arr dim = Expr $ function "array_lower" [toExpr arr, toExpr dim]
+
+arrayUpper :: (ExpressionOf Postgresql r a (Array elem)) => a -> Int -> Expr Postgresql r Int
+arrayUpper arr dim = Expr $ function "array_upper" [toExpr arr, toExpr dim]
+
+arrayLength :: (ExpressionOf Postgresql r a (Array elem)) => a -> Int -> Expr Postgresql r Int
+arrayLength arr dim = Expr $ function "array_length" [toExpr arr, toExpr dim]
+
+-- | Concatenates array elements using supplied delimiter. array_to_string(ARRAY[1, 2, 3], '~^~') = 1~^~2~^~3
+arrayToString :: (ExpressionOf Postgresql r a (Array elem)) => a -> String -> Expr Postgresql r String
+arrayToString arr sep = Expr $ function "array_to_string" [toExpr arr, toExpr sep]
+
+-- | Splits string into array elements using supplied delimiter. string_to_array('xx~^~yy~^~zz', '~^~') = {xx,yy,zz}
+stringToArray :: (ExpressionOf Postgresql r a String) => a -> String -> Expr Postgresql r (Array String)
+stringToArray arr sep = Expr $ function "string_to_array" [toExpr arr, toExpr sep]
+
+any :: (ExpressionOf Postgresql r a elem, ExpressionOf Postgresql r b (Array elem)) => a -> b -> Cond Postgresql r
+any a arr = CondRaw $ Snippet $ \esc _ -> [renderExprPriority esc 37 (toExpr a) <> "=ANY" <> fromChar '(' <> renderExpr esc (toExpr arr) <> fromChar ')']
+
+all :: (ExpressionOf Postgresql r a elem, ExpressionOf Postgresql r b (Array elem)) => a -> b -> Cond Postgresql r
+all a arr = CondRaw $ Snippet $ \esc _ -> [renderExprPriority esc 37 (toExpr a) <> "=ALL" <> fromChar '(' <> renderExpr esc (toExpr arr) <> fromChar ')']
+
+-- | Contains. ARRAY[1,4,3] @> ARRAY[3,1] = t
+(@>) :: (ExpressionOf Postgresql r a (Array elem), ExpressionOf Postgresql r b (Array elem)) => a -> b -> Cond Postgresql r
+(@>) a b = CondRaw $ operator 50 "@>" a b
+
+-- | Is contained by. ARRAY[2,7] <@ ARRAY[1,7,4,2,6] = t
+(<@) :: (ExpressionOf Postgresql r a (Array elem), ExpressionOf Postgresql r b (Array elem)) => a -> b -> Cond Postgresql r
+(<@) a b = CondRaw $ operator 50 "<@" a b
+
+-- | Overlap (have elements in common). ARRAY[1,4,3] && ARRAY[2,1] = t
+overlaps :: (ExpressionOf Postgresql r a (Array elem), ExpressionOf Postgresql r b (Array elem)) => a -> b -> Cond Postgresql r
+overlaps a b = CondRaw $ operator 50 "&&" a b
+
+parseHelper :: Parser a -> PersistValue -> a
+parseHelper p (PersistByteString bs) = either error id $ parseOnly p bs
+parseHelper _ a = error $ "parseHelper: expected PersistByteString, got " ++ show a
+ Database/Groundhog/Postgresql/Geometry.hs view
@@ -0,0 +1,125 @@+module Database.Groundhog.Postgresql.Geometry
+  (
+    Point(..)
+  , Line(..)
+  , Lseg(..)
+  , Box(..)
+  , Path(..)
+  , Polygon(..)
+  , Circle(..)
+  ) where
+
+import Database.Groundhog.Core
+import Database.Groundhog.Generic
+import Database.Groundhog.Instances ()
+
+import Control.Applicative
+import Data.Attoparsec.Char8
+
+data Point = Point Double Double deriving (Eq, Show)
+-- | It is not fully implemented in PostgreSQL yet. It is kept just to match all geometric types.
+data Line = Line Point Point deriving (Eq, Show)
+data Lseg = Lseg Point Point deriving (Eq, Show)
+data Box = Box Point Point deriving (Eq, Show)
+data Path = ClosedPath [Point]
+          | OpenPath [Point] deriving (Eq, Show)
+data Polygon = Polygon [Point] deriving (Eq, Show)
+data Circle = Circle Point Double deriving (Eq, Show)
+
+-- select o.oprname, o.oprkind, tl.typname as oprleft, tr.typname as oprright, tres.typname as oprresult, o.oprcode, ocom.oprname as oprcom, oneg.oprname as oprnegate from pg_operator o inner join pg_type tl on o.oprleft = tl.oid inner join pg_type tr on o.oprright = tr.oid inner join pg_type tres on o.oprresult = tres.oid left join pg_operator ocom on o.oprcom = ocom.oid left join pg_operator oneg on o.oprnegate = oneg.oid where tl.typname in ('point', 'line', 'lseg', 'box', 'path', 'polygon', 'circle') order by o.oprname, oprleft;
+
+parseHelper :: Parser a -> PersistValue -> a
+parseHelper p (PersistByteString bs) = either error id $ parseOnly p bs
+parseHelper _ a = error $ "parseHelper: expected PersistByteString, got " ++ show a
+
+pair :: (a -> a -> b) -> Char -> Char -> Parser a -> Parser b
+pair f open close p = f <$> (char open *> p <* char ',') <*> p <* char close
+
+point :: Parser Point
+point = pair Point '(' ')' double
+
+points :: Parser [Point]
+points = point `sepBy1` char ','
+
+instance PrimitivePersistField Point where
+  toPrimitivePersistValue _ (Point x y) = PersistString $ show (x, y)
+  fromPrimitivePersistValue _ = parseHelper point
+
+instance PersistField Point where
+  persistName _ = "Point"
+  toPersistValues = primToPersistValue
+  fromPersistValues = primFromPersistValue
+  dbType _ = DbOther $ OtherTypeDef $ const "point"
+
+instance PrimitivePersistField Line where
+  toPrimitivePersistValue _ (Line (Point x1 y1) (Point x2 y2)) = PersistString $ show ((x1, y1), (x2, y2))
+  fromPrimitivePersistValue _ = error "fromPrimitivePersistValue Line is not supported yet"
+
+instance PersistField Line where
+  persistName _ = "Line"
+  toPersistValues = primToPersistValue
+  fromPersistValues = primFromPersistValue
+  dbType _ = DbOther $ OtherTypeDef $ const "line"
+
+instance PrimitivePersistField Lseg where
+  toPrimitivePersistValue _ (Lseg (Point x1 y1) (Point x2 y2)) = PersistString $ show ((x1, y1), (x2, y2))
+  fromPrimitivePersistValue _ = parseHelper $ pair Lseg '[' ']' point
+
+instance PersistField Lseg where
+  persistName _ = "Lseg"
+  toPersistValues = primToPersistValue
+  fromPersistValues = primFromPersistValue
+  dbType _ = DbOther $ OtherTypeDef $ const "lseg"
+
+instance PrimitivePersistField Box where
+  toPrimitivePersistValue _ (Box (Point x1 y1) (Point x2 y2)) = PersistString $ show ((x1, y1), (x2, y2))
+  fromPrimitivePersistValue _ = parseHelper $ Box <$> (point <* char ',') <*> point
+
+instance PersistField Box where
+  persistName _ = "Box"
+  toPersistValues = primToPersistValue
+  fromPersistValues = primFromPersistValue
+  dbType _ = DbOther $ OtherTypeDef $ const "box"
+
+showPath :: Char -> Char -> [Point] -> ShowS
+showPath open close []     s = open : close : s
+showPath open close (x:xs) s = open : showPoint x (showl xs)
+  where
+    showl []     = close : s
+    showl (y:ys) = ',' : showPoint y (showl ys)
+
+showPoint :: Point -> ShowS
+showPoint (Point x y) = shows (x, y)
+
+instance PrimitivePersistField Path where
+  toPrimitivePersistValue _ path = PersistString $ case path of
+    ClosedPath ps -> showPath '(' ')' ps ""
+    OpenPath ps -> showPath '[' ']' ps ""
+  fromPrimitivePersistValue _ = parseHelper $ path' ClosedPath '(' ')' <|> path' OpenPath '[' ']' where
+    path' f open close = f <$> (char open *> points <* char close)
+
+instance PersistField Path where
+  persistName _ = "Path"
+  toPersistValues = primToPersistValue
+  fromPersistValues = primFromPersistValue
+  dbType _ = DbOther $ OtherTypeDef $ const "path"
+
+instance PrimitivePersistField Polygon where
+  toPrimitivePersistValue _ (Polygon ps) = PersistString $ showPath '(' ')' ps ""
+  fromPrimitivePersistValue _ = parseHelper $ Polygon <$> (char '(' *> points <* char ')')
+
+instance PersistField Polygon where
+  persistName _ = "Polygon"
+  toPersistValues = primToPersistValue
+  fromPersistValues = primFromPersistValue
+  dbType _ = DbOther $ OtherTypeDef $ const "polygon"
+
+instance PrimitivePersistField Circle where
+  toPrimitivePersistValue _ (Circle (Point x1 y1) r) = PersistString $ show ((x1, y1), r)
+  fromPrimitivePersistValue _ = parseHelper $ Circle <$> (char '<' *> point) <* char ',' <*> double <* char '>'
+
+instance PersistField Circle where
+  persistName _ = "Circle"
+  toPersistValues = primToPersistValue
+  fromPersistValues = primFromPersistValue
+  dbType _ = DbOther $ OtherTypeDef $ const "circle"
groundhog-postgresql.cabal view
@@ -1,5 +1,5 @@ name:            groundhog-postgresql-version:         0.2.0+version:         0.3.0 license:         BSD3 license-file:    LICENSE author:          Boris Lykah <lykahb@gmail.com>@@ -16,13 +16,16 @@                    , postgresql-simple       >= 0.2       && < 0.3                    , postgresql-libpq        >= 0.6.1     && < 0.9                    , bytestring              >= 0.9+                   , blaze-builder           >= 0.3.0.0   && < 0.4                    , transformers            >= 0.2.1     && < 0.4-                   , groundhog               >= 0.2.0     && < 0.3.0+                   , groundhog               >= 0.3.0     && < 0.4.0                    , monad-control           >= 0.3       && < 0.4                    , containers              >= 0.2                    , text                    >= 0.8       && < 0.12-                   , pool-conduit            >= 0.1       && < 0.2+                   , attoparsec              >= 0.8.5.3+                   , resource-pool           >= 0.2.1     && < 0.3                    , time                    >= 1.1     exposed-modules: Database.Groundhog.Postgresql-    other-modules:   Database.Groundhog.Generic.Sql.String+                     Database.Groundhog.Postgresql.Array+                     Database.Groundhog.Postgresql.Geometry     ghc-options:     -Wall -fno-warn-unused-do-bind