git.iko.soy/typeable/postgresql-simple
on master help sign in
Add preliminary support for converting SQL arrays
commitd7475b8d6c14b6f2fd1c0d1a1dc77b9101cce2d5 authorBas van Dijk <v.dijk.bas@gmail.com> dateMon 30 Jul 2012, 21:14 (14 years ago) parentfeadd8d
4 files changed+56−28
src/Database/PostgreSQL/Simple.hs+35−21 src/Database/PostgreSQL/Simple/FromField.hs+1−1 src/Database/PostgreSQL/Simple/FromRow.hs+3−3 src/Database/PostgreSQL/Simple/Internal.hs+17−3
src/Database/PostgreSQL/Simple.hs+35−21
@@ -4,6 +4,7 @@
44{-# LANGUAGE PatternGuards #-}
55{-# LANGUAGE RecordWildCards #-}
66{-# LANGUAGE ViewPatterns #-}
7+{-# LANGUAGE QuasiQuotes #-}
78
89------------------------------------------------------------------------------
910-- |
@@ -140,6 +141,7 @@ import Database.PostgreSQL.Simple.ToRow (ToRow(..))
140141import Database.PostgreSQL.Simple.Types
141142 ( Binary(..), In(..), Only(..), Query(..), (:.)(..) )
142143import Database.PostgreSQL.Simple.Internal as Base
144+import Database.PostgreSQL.Simple.SqlQQ (sql)
143145import qualified Database.PostgreSQL.LibPQ as PQ
144146import qualified Data.ByteString.Char8 as B
145147import qualified Data.Text as T
@@ -549,19 +551,19 @@ finishQuery conn q result = do
549551 PQ.TuplesOk -> do
550552 ncols <- PQ.nfields result
551553 let unCol (PQ.Col x) = fromIntegral x :: Int
552− typenames <- V.generateM (unCol ncols)
554+ typeinfos <- V.generateM (unCol ncols)
553555 (\(PQ.Col . fromIntegral -> col) -> do
554− getTypename conn =<< PQ.ftype result col)
556+ getTypeInfo conn =<< PQ.ftype result col)
555557 nrows <- PQ.ntuples result
556558 ncols <- PQ.nfields result
557559 forM' 0 (nrows-1) $ \row -> do
558− let rw = Row row typenames result
560+ let rw = Row row typeinfos result
559561 case runStateT (runReaderT (unRP fromRow) rw) 0 of
560562 Ok (val,col) | col == ncols -> return val
561563 | otherwise -> do
562564 vals <- forM' 0 (ncols-1) $ \c -> do
563565 v <- PQ.getvalue result row c
564− return ( typenames V.! unCol c
566+ return ( typeinfos V.! unCol c
565567 , fmap ellipsis v )
566568 throw (ConversionFailed
567569 (show (unCol ncols) ++ " values: " ++ show vals)
@@ -963,24 +965,36 @@ fmtError msg q xs = throw FormatError {
963965-- wrong results. In such cases, write a @newtype@ wrapper and a
964966-- custom 'Result' instance to handle your encoding.
965967
966−getTypename :: Connection -> PQ.Oid -> IO ByteString
967−getTypename conn@Connection{..} oid =
968+getTypeInfo :: Connection -> PQ.Oid -> IO TypeInfo
969+getTypeInfo conn@Connection{..} oid =
968970 case oid2typname oid of
969− Just name -> return name
971+ Just name -> return $! TypeInfo { typ = NamedOid oid name
972+ , typelem = Nothing
973+ }
970974 Nothing -> modifyMVar connectionObjects $ \oidmap -> do
971975 case IntMap.lookup (oid2int oid) oidmap of
972− Just name -> return (oidmap, name)
976+ Just typeinfo -> return (oidmap, typeinfo)
973977 Nothing -> do
974− names <- query conn "SELECT typname FROM pg_type WHERE oid=?"
975− (Only oid)
976− name <- case names of
977− [] -> return $ throw SqlError {
978− sqlNativeError = -1,
979− sqlErrorMsg = "invalid type oid",
980− sqlState = ""
981− }
982− [Only x] -> return x
983− _ -> fail "typename query returned more than one result"
984− -- oid is a primary key, so the query should
985− -- never return more than one result
986− return (IntMap.insert (oid2int oid) name oidmap, name)
978+ names <- query conn
979+ [sql| SELECT p.oid, p.typname, c.oid, c.typname
980+ FROM pg_type AS p LEFT OUTER JOIN pg_type AS c
981+ ON c.oid = p.typelem
982+ WHERE p.oid = ?
983+ |] (Only oid)
984+ typinf <- case names of
985+ [] -> return $ throw SqlError {
986+ sqlNativeError = -1,
987+ sqlErrorMsg = "invalid type oid",
988+ sqlState = ""
989+ }
990+ [(pOid, pTypName, mbCOid, mbCTypName)] ->
991+ return $! TypeInfo { typ = NamedOid pOid pTypName
992+ , typelem = do
993+ cOid <- mbCOid
994+ cTypName <- mbCTypName
995+ return $ NamedOid cOid cTypName
996+ }
997+ _ -> fail "typename query returned more than one result"
998+ -- oid is a primary key, so the query should
999+ -- never return more than one result
1000+ return (IntMap.insert (oid2int oid) typinf oidmap, typinf)
src/Database/PostgreSQL/Simple/FromField.hs+1−1
@@ -282,7 +282,7 @@ doFromField :: forall a . (Typeable a)
282282 => Field -> Compat -> (ByteString -> Ok a)
283283 -> Maybe ByteString -> Ok a
284284doFromField f types cvt (Just bs)
285− | Just typ <- oid2builtin (typeOid f)
285+ | Just typ <- oid2builtin (typoid $ typ $ typeinfo f)
286286 , mkCompat typ `compat` types = cvt bs
287287 | otherwise = returnError Incompatible f "types incompatible"
288288doFromField f _ _ _ = returnError UnexpectedNull f ""
src/Database/PostgreSQL/Simple/FromRow.hs+3−3
@@ -68,13 +68,13 @@ class FromRow a where
6868fieldWith :: FieldParser a -> RowParser a
6969fieldWith fieldP = RP $ do
7070 let unCol (PQ.Col x) = fromIntegral x :: Int
71− Row{..} <- ask
71+ r@Row{..} <- ask
7272 column <- lift get
7373 lift (put (column + 1))
7474 let ncols = nfields rowresult
7575 if (column >= ncols)
7676 then do
77− let vals = map (\c -> ( typenames ! (unCol c)
77+ let vals = map (\c -> ( typenames r ! (unCol c)
7878 , fmap ellipsis (getvalue rowresult row c) ))
7979 [0..ncols-1]
8080 convertError = ConversionFailed
@@ -85,7 +85,7 @@ fieldWith fieldP = RP $ do
8585 \convert and number in target type"
8686 lift (lift (Errors [SomeException convertError]))
8787 else do
88− let typename = typenames ! unCol column
88+ let typeinfo = typeinfos ! unCol column
8989 result = rowresult
9090 field = Field{..}
9191 lift (lift (fieldP field (getvalue result row column)))
src/Database/PostgreSQL/Simple/Internal.hs+17−3
@@ -52,9 +52,20 @@ import System.IO.Unsafe (unsafePerformIO)
5252data Field = Field {
5353 result :: !PQ.Result
5454 , column :: {-# UNPACK #-} !PQ.Column
55− , typename :: !ByteString
55+ , typeinfo :: !TypeInfo
5656 }
5757
58+data NamedOid = NamedOid { typoid :: !PQ.Oid
59+ , typname :: !ByteString
60+ } deriving Show
61+
62+data TypeInfo = TypeInfo { typ :: !NamedOid
63+ , typelem :: !(Maybe NamedOid)
64+ } deriving Show
65+
66+typename :: Field -> ByteString
67+typename = typname . typ . typeinfo
68+
5869name :: Field -> Maybe ByteString
5970name Field{..} = unsafePerformIO (PQ.fname result column)
6071
@@ -76,7 +87,7 @@ typeOid Field{..} = unsafePerformIO (PQ.ftype result column)
7687
7788data Connection = Connection {
7889 connectionHandle :: {-# UNPACK #-} !(MVar PQ.Connection)
79− , connectionObjects :: {-# UNPACK #-} !(MVar (IntMap.IntMap ByteString))
90+ , connectionObjects :: {-# UNPACK #-} !(MVar (IntMap.IntMap TypeInfo))
8091 }
8192
8293data SqlType
@@ -301,10 +312,13 @@ newNullConnection = do
301312
302313data Row = Row {
303314 row :: {-# UNPACK #-} !PQ.Row
304− , typenames :: !(V.Vector ByteString)
315+ , typeinfos :: !(V.Vector TypeInfo)
305316 , rowresult :: !PQ.Result
306317 }
307318
319+typenames :: Row -> V.Vector ByteString
320+typenames = V.map (typname . typ) . typeinfos
321+
308322newtype RowParser a = RP { unRP :: ReaderT Row (StateT PQ.Column Ok) a }
309323 deriving ( Functor, Applicative, Alternative, Monad )
310324