Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'Unpacked' Fields and Rows #256

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions src/Database/PostgreSQL/Simple/FromField.hs
Original file line number Diff line number Diff line change
Expand Up @@ -218,26 +218,27 @@ typename :: Field -> Conversion ByteString
typename field = typname <$> typeInfo field

typeInfo :: Field -> Conversion TypeInfo
typeInfo Field{..} = Conversion $ \conn -> do
Ok <$> (getTypeInfo conn typeOid)
typeInfo f = Conversion $ \conn -> Ok <$> getTypeInfo conn (typeOid f)

typeInfoByOid :: PQ.Oid -> Conversion TypeInfo
typeInfoByOid oid = Conversion $ \conn -> do
Ok <$> (getTypeInfo conn oid)
typeInfoByOid oid = Conversion $ \conn -> Ok <$> getTypeInfo conn oid

-- | Returns the name of the column. This is often determined by a table
-- definition, but it can be set using an @as@ clause.

name :: Field -> Maybe ByteString
name Field{..} = unsafeDupablePerformIO (PQ.fname result column)
name UnpackedField{..} = unpackedFieldColumnName

-- | Returns the name of the object id of the @table@ associated with the
-- column, if any. Returns 'Nothing' when there is no such table;
-- for example a computed column does not have a table associated with it.
-- Analogous to libpq's @PQftable@.

tableOid :: Field -> Maybe PQ.Oid
tableOid Field{..} = toMaybeOid (unsafeDupablePerformIO (PQ.ftable result column))
tableOid field = case field of
Field{..} -> toMaybeOid (unsafeDupablePerformIO (PQ.ftable result column))
UnpackedField{..} -> toMaybeOid unpackedFieldTableOid
where
toMaybeOid x
= if x == PQ.invalidOid
Expand All @@ -249,7 +250,9 @@ tableOid Field{..} = toMaybeOid (unsafeDupablePerformIO (PQ.ftable result column
-- to libpq's @PQftablecol@.

tableColumn :: Field -> Int
tableColumn Field{..} = fromCol (unsafeDupablePerformIO (PQ.ftablecol result column))
tableColumn field = case field of
Field{..} -> fromCol (unsafeDupablePerformIO (PQ.ftablecol result column))
UnpackedField{..} -> unpackedFieldColumnNumber
where
fromCol (PQ.Col x) = fromIntegral x

Expand All @@ -258,6 +261,7 @@ tableColumn Field{..} = fromCol (unsafeDupablePerformIO (PQ.ftablecol result col

format :: Field -> PQ.Format
format Field{..} = unsafeDupablePerformIO (PQ.fformat result column)
format UnpackedField{..} = unpackedFieldFormat

-- | void
instance FromField () where
Expand Down Expand Up @@ -523,7 +527,7 @@ fromArray :: FieldParser a -> TypeInfo -> Field -> Parser (Conversion [a])
fromArray fieldParser typeInfo f = sequence . (parseIt <$>) <$> array delim
where
delim = typdelim (typelem typeInfo)
fElem = f{ typeOid = typoid (typelem typeInfo) }
fElem = setTypeOid f $ typoid (typelem typeInfo)

parseIt item =
fieldParser f' $ if item == Arrays.Plain "NULL" then Nothing else Just item'
Expand Down
51 changes: 30 additions & 21 deletions src/Database/PostgreSQL/Simple/FromRow.hs
Original file line number Diff line number Diff line change
Expand Up @@ -88,34 +88,43 @@ getTypeInfoByCol Row{..} col =
Conversion $ \conn -> do
oid <- PQ.ftype rowresult col
Ok <$> getTypeInfo conn oid
getTypeInfoByCol UnpackedRow{..} col = typeInfo $ fst f
where PQ.Col c = col
f = unpackedRowValues !! fromIntegral c


getTypenameByCol :: Row -> PQ.Column -> Conversion ByteString
getTypenameByCol row col = typname <$> getTypeInfoByCol row col

fieldWith :: FieldParser a -> RowParser a
fieldWith fieldP = RP $ do
let unCol (PQ.Col x) = fromIntegral x :: Int
r@Row{..} <- ask
column <- lift get
lift (put (column + 1))
let ncols = nfields rowresult
if (column >= ncols)
then lift $ lift $ do
vals <- mapM (getTypenameByCol r) [0..ncols-1]
let err = ConversionFailed
(show (unCol ncols) ++ " values: " ++ show (map ellipsis vals))
Nothing
""
("at least " ++ show (unCol column + 1)
++ " slots in target type")
"mismatch between number of columns to \
\convert and number in target type"
conversionError err
else do
let !result = rowresult
!typeOid = unsafeDupablePerformIO (PQ.ftype result column)
!field = Field{..}
lift (lift (fieldP field (getvalue result row column)))
ask >>= \r -> case r of
UnpackedRow{..} -> do
column <- lift get
lift (put (column + 1))
lift $ lift $ uncurry fieldP $ unpackedRowValues !! unCol column
Row{..} -> do
column <- lift get
lift (put (column + 1))
let ncols = nfields rowresult
if (column >= ncols)
then lift $ lift $ do
vals <- mapM (getTypenameByCol r) [0..ncols-1]
let err = ConversionFailed
(show (unCol ncols) ++ " values: " ++ show (map ellipsis vals))
Nothing
""
("at least " ++ show (unCol column + 1)
++ " slots in target type")
"mismatch between number of columns to \
\convert and number in target type"
conversionError err
else do
let !result = rowresult
!fieldTypeOid = unsafeDupablePerformIO (PQ.ftype result column)
!field = Field{..}
lift (lift (fieldP field (getvalue result row column)))

field :: FromField a => RowParser a
field = fieldWith fromField
Expand Down
21 changes: 20 additions & 1 deletion src/Database/PostgreSQL/Simple/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,26 @@ import Control.Concurrent(threadWaitRead, threadWaitWrite)
data Field = Field {
result :: !PQ.Result
, column :: {-# UNPACK #-} !PQ.Column
, typeOid :: {-# UNPACK #-} !PQ.Oid
, fieldTypeOid :: {-# UNPACK #-} !PQ.Oid
-- ^ This returns the type oid associated with the column. Analogous
-- to libpq's @PQftype@.
}
| UnpackedField
{ unpackedFieldTypeOid :: PQ.Oid
, unpackedFieldColumnName :: Maybe ByteString
, unpackedFieldTableOid :: PQ.Oid
, unpackedFieldColumnNumber :: Int
, unpackedFieldFormat :: PQ.Format
}

typeOid :: Field -> PQ.Oid
typeOid Field{..} = fieldTypeOid
typeOid UnpackedField{..} = unpackedFieldTypeOid

setTypeOid :: Field -> PQ.Oid -> Field
setTypeOid f oid = case f of
Field {} -> f { fieldTypeOid = oid }
UnpackedField {} -> f { unpackedFieldTypeOid = oid }

type TypeInfoCache = IntMap.IntMap TypeInfo

Expand Down Expand Up @@ -452,6 +468,9 @@ data Row = Row {
row :: {-# UNPACK #-} !PQ.Row
, rowresult :: !PQ.Result
}
| UnpackedRow
{ unpackedRowValues :: [(Field, Maybe ByteString)]
}

newtype RowParser a = RP { unRP :: ReaderT Row (StateT PQ.Column Conversion) a }
deriving ( Functor, Applicative, Alternative, Monad )
Expand Down
3 changes: 2 additions & 1 deletion src/Database/PostgreSQL/Simple/Range.hs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import Data.Word (Word, Word16, Word32,

import Database.PostgreSQL.Simple.Compat (scientificBuilder, (<>), toByteString)
import Database.PostgreSQL.Simple.FromField
import Database.PostgreSQL.Simple.Internal (setTypeOid)
import Database.PostgreSQL.Simple.Time
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I think setTypeOid (and maybe some other things to manipulate the "unpacked" stuff?) should be made part of the public interface.

hiding (PosInfinity, NegInfinity)
-- import qualified Database.PostgreSQL.Simple.Time as Time
Expand Down Expand Up @@ -197,7 +198,7 @@ fromFieldRange fromField' f mdat = do
info <- typeInfo f
case info of
Range{} ->
let f' = f { typeOid = typoid (rngsubtype info) }
let f' = setTypeOid f $ typoid (rngsubtype info)
in case mdat of
Nothing -> returnError UnexpectedNull f ""
Just "empty" -> pure $ empty
Expand Down