Add direct codec interface to persistent core#1
Conversation
New modules: - DirectDecode: RowReader, FieldDecode, FromRow with prepare-once via prepareRow/RowDecoder, DirectEntity for SqlBackend bridge - DirectEncode: FieldEncode, ParamBuilder (vector-builder monoid), ToRow - Sql.DirectRaw: HasDirectQuery, rawQueryDirect, rawSqlDirect, rawSqlDirectCompat (SqlBackend bridge) - Sql.Experimental: drop-in replacement for Sql that re-exports the direct codec API SqlBackend gains connDirectQueryCap (DirectQueryCap existential) for the Typeable-based SqlBackend bridge. ISRSingleCustom added to InsertSqlResult for UNNEST bulk inserts.
| Database.Persist.DirectDecode | ||
| Database.Persist.DirectEncode |
There was a problem hiding this comment.
Restyled by cabal-fmt
| Database.Persist.DirectDecode | |
| Database.Persist.DirectEncode |
| -- Returns the 'FieldRunner' as a value so it can be captured in a | ||
| -- 'RowDecoder' closure and reused across rows. Used inside | ||
| -- 'prepareRow' implementations. | ||
| prepareNextField :: FieldDecode env a => FieldNameDB -> RowReader env (FieldRunner env a) |
There was a problem hiding this comment.
Restyled by fourmolu
| prepareNextField :: FieldDecode env a => FieldNameDB -> RowReader env (FieldRunner env a) | |
| prepareNextField | |
| :: (FieldDecode env a) => FieldNameDB -> RowReader env (FieldRunner env a) |
| import GHC.Exts (MutableByteArray#, RealWorld) | ||
| import GHC.IO (IO (..)) | ||
| import GHC.Int (Int (..)) | ||
| import GHC.Prim (readIntArray#, writeIntArray#, newByteArray#, (+#)) |
There was a problem hiding this comment.
Restyled by fourmolu
| import GHC.Prim (readIntArray#, writeIntArray#, newByteArray#, (+#)) | |
| import GHC.Prim (newByteArray#, readIntArray#, writeIntArray#, (+#)) |
| a <> b = ParamBuilder | ||
| { pbLength = pbLength a + pbLength b | ||
| , pbWrite = \mv off -> pbWrite a mv off >> pbWrite b mv (off + pbLength a) | ||
| } |
There was a problem hiding this comment.
Restyled by fourmolu
| a <> b = ParamBuilder | |
| { pbLength = pbLength a + pbLength b | |
| , pbWrite = \mv off -> pbWrite a mv off >> pbWrite b mv (off + pbLength a) | |
| } | |
| a <> b = | |
| ParamBuilder | |
| { pbLength = pbLength a + pbLength b | |
| , pbWrite = \mv off -> pbWrite a mv off >> pbWrite b mv (off + pbLength a) | |
| } |
| {-# LANGUAGE FlexibleInstances #-} | ||
| {-# LANGUAGE FlexibleContexts #-} | ||
| {-# LANGUAGE MultiParamTypeClasses #-} | ||
| {-# LANGUAGE FunctionalDependencies #-} | ||
| {-# LANGUAGE DefaultSignatures #-} |
There was a problem hiding this comment.
Restyled by fourmolu
| {-# LANGUAGE FlexibleInstances #-} | |
| {-# LANGUAGE FlexibleContexts #-} | |
| {-# LANGUAGE MultiParamTypeClasses #-} | |
| {-# LANGUAGE FunctionalDependencies #-} | |
| {-# LANGUAGE DefaultSignatures #-} |
| :: env -> FieldNameDB -> Int | ||
| -> (Text -> IO r) -- ^ on error | ||
| -> (FieldRunner env a -> IO r) -- ^ on success (receives the runner) |
There was a problem hiding this comment.
Restyled by fourmolu
| :: env -> FieldNameDB -> Int | |
| -> (Text -> IO r) -- ^ on error | |
| -> (FieldRunner env a -> IO r) -- ^ on success (receives the runner) | |
| :: env | |
| -> FieldNameDB | |
| -> Int | |
| -> (Text -> IO r) | |
| -- ^ on error | |
| -> (FieldRunner env a -> IO r) | |
| -- ^ on success (receives the runner) |
| :: RowReader env a -> env -> Counter | ||
| -> (Text -> IO r) -- ^ on error | ||
| -> (a -> IO r) -- ^ on success |
There was a problem hiding this comment.
Restyled by fourmolu
| :: RowReader env a -> env -> Counter | |
| -> (Text -> IO r) -- ^ on error | |
| -> (a -> IO r) -- ^ on success | |
| :: RowReader env a | |
| -> env | |
| -> Counter | |
| -> (Text -> IO r) | |
| -- ^ on error | |
| -> (a -> IO r) | |
| -- ^ on success |
| runRowDecoder dd env' onErr' $ \vd -> | ||
| onOk' (va, vb, vc, vd) | ||
|
|
||
| instance (FromRow env a, FromRow env b, FromRow env c, FromRow env d, FromRow env e) => FromRow env (a, b, c, d, e) where |
There was a problem hiding this comment.
Restyled by fourmolu
| instance (FromRow env a, FromRow env b, FromRow env c, FromRow env d, FromRow env e) => FromRow env (a, b, c, d, e) where | |
| instance | |
| (FromRow env a, FromRow env b, FromRow env c, FromRow env d, FromRow env e) | |
| => FromRow env (a, b, c, d, e) | |
| where |
| -- | ||
| -- Calls 'prepareField' and immediately applies the resulting 'FieldRunner'. | ||
| -- For the prepare-once path, use 'prepareRow' instead. | ||
| nextField :: FieldDecode env a => FieldNameDB -> RowReader env a |
There was a problem hiding this comment.
Restyled by fourmolu
| nextField :: FieldDecode env a => FieldNameDB -> RowReader env a | |
| nextField :: (FieldDecode env a) => FieldNameDB -> RowReader env a |
| runRowDecoder dc env' onErr' $ \vc -> | ||
| onOk' (va, vb, vc) | ||
|
|
||
| instance (FromRow env a, FromRow env b, FromRow env c, FromRow env d) => FromRow env (a, b, c, d) where |
There was a problem hiding this comment.
Restyled by fourmolu
| instance (FromRow env a, FromRow env b, FromRow env c, FromRow env d) => FromRow env (a, b, c, d) where | |
| instance | |
| (FromRow env a, FromRow env b, FromRow env c, FromRow env d) | |
| => FromRow env (a, b, c, d) | |
| where |
| -- one allocation (no intermediate list). | ||
| data ParamBuilder param = ParamBuilder | ||
| { pbLength :: {-# UNPACK #-} !Int | ||
| , pbWrite :: forall s. MV.MVector s param -> Int -> ST s () |
There was a problem hiding this comment.
Restyled by fourmolu
| , pbWrite :: forall s. MV.MVector s param -> Int -> ST s () | |
| , pbWrite :: forall s. MV.MVector s param -> Int -> ST s () |
| type Env backend | ||
| type Param backend | ||
| directQuerySource | ||
| :: MonadIO m |
There was a problem hiding this comment.
Restyled by fourmolu
| :: MonadIO m | |
| :: (MonadIO m) |
| decoder <- liftIO $ prepareRow firstEnv ctr | ||
| (throwIO . PersistMarshalError) | ||
| pure | ||
| val <- liftIO $ runRowDecoderCPS decoder firstEnv | ||
| (throwIO . PersistMarshalError) | ||
| pure |
There was a problem hiding this comment.
Restyled by fourmolu
| decoder <- liftIO $ prepareRow firstEnv ctr | |
| (throwIO . PersistMarshalError) | |
| pure | |
| val <- liftIO $ runRowDecoderCPS decoder firstEnv | |
| (throwIO . PersistMarshalError) | |
| pure | |
| decoder <- | |
| liftIO $ | |
| prepareRow | |
| firstEnv | |
| ctr | |
| (throwIO . PersistMarshalError) | |
| pure | |
| val <- | |
| liftIO $ | |
| runRowDecoderCPS | |
| decoder | |
| firstEnv | |
| (throwIO . PersistMarshalError) | |
| pure |
| :: forall r. env | ||
| -> (Text -> IO r) -- on error | ||
| -> (a -> IO r) -- on success |
There was a problem hiding this comment.
Restyled by fourmolu
| :: forall r. env | |
| -> (Text -> IO r) -- on error | |
| -> (a -> IO r) -- on success | |
| :: forall r | |
| . env | |
| -> (Text -> IO r) -- on error | |
| -> (a -> IO r) -- on success |
| -> ReaderT backend m1 (Acquire (ConduitM () a m2 ())) | ||
| rawQueryDirect sql paramBuilder = do | ||
| backend <- ask | ||
| let params = buildParams paramBuilder |
There was a problem hiding this comment.
Restyled by fourmolu
| let params = buildParams paramBuilder | |
| let | |
| params = buildParams paramBuilder |
| -> ReaderT backend m [a] | ||
| rawSqlDirect sql paramBuilder = do | ||
| backend <- ask | ||
| let params = buildParams paramBuilder |
There was a problem hiding this comment.
Restyled by fourmolu
| let params = buildParams paramBuilder | |
| let | |
| params = buildParams paramBuilder |
| rowReader :: RowReader env a | ||
|
|
||
| prepareRow | ||
| :: env -> Counter |
There was a problem hiding this comment.
Restyled by fourmolu
| :: env -> Counter | |
| :: env | |
| -> Counter |
| :: forall r. env -> Counter | ||
| -> (Text -> IO r) -- on error | ||
| -> (a -> IO r) -- on success |
There was a problem hiding this comment.
Restyled by fourmolu
| :: forall r. env -> Counter | |
| -> (Text -> IO r) -- on error | |
| -> (a -> IO r) -- on success | |
| :: forall r | |
| . env | |
| -> Counter | |
| -> (Text -> IO r) -- on error | |
| -> (a -> IO r) -- on success |
| v <- liftIO $ runRowDecoderCPS decoder env | ||
| (throwIO . PersistMarshalError) | ||
| pure |
There was a problem hiding this comment.
Restyled by fourmolu
| v <- liftIO $ runRowDecoderCPS decoder env | |
| (throwIO . PersistMarshalError) | |
| pure | |
| v <- | |
| liftIO $ | |
| runRowDecoderCPS | |
| decoder | |
| env | |
| (throwIO . PersistMarshalError) | |
| pure |
| ( FromRow (..), DirectEntity (..), RowDecoder (..) | ||
| , newCounter, runRowReaderCPS, runRowDecoderCPS ) | ||
| import Database.Persist.DirectEncode (FieldEncode, ParamBuilder, buildParams, writeParam) | ||
| import Database.Persist.SqlBackend.Internal (SqlBackend (..), DirectQueryCap (..)) |
There was a problem hiding this comment.
Restyled by fourmolu
| ( FromRow (..), DirectEntity (..), RowDecoder (..) | |
| , newCounter, runRowReaderCPS, runRowDecoderCPS ) | |
| import Database.Persist.DirectEncode (FieldEncode, ParamBuilder, buildParams, writeParam) | |
| import Database.Persist.SqlBackend.Internal (SqlBackend (..), DirectQueryCap (..)) | |
| ( DirectEntity (..) | |
| , FromRow (..) | |
| , RowDecoder (..) | |
| , newCounter | |
| , runRowDecoderCPS | |
| , runRowReaderCPS | |
| ) | |
| import Database.Persist.DirectEncode | |
| ( FieldEncode | |
| , ParamBuilder | |
| , buildParams | |
| , writeParam | |
| ) | |
| import Database.Persist.SqlBackend.Internal | |
| ( DirectQueryCap (..) | |
| , SqlBackend (..) | |
| ) |
| {-# INLINE mempty #-} | ||
|
|
||
| -- | Encode a value and append it to the parameter builder. | ||
| writeParam :: FieldEncode param a => a -> ParamBuilder param |
There was a problem hiding this comment.
Restyled by fourmolu
| writeParam :: FieldEncode param a => a -> ParamBuilder param | |
| writeParam :: (FieldEncode param a) => a -> ParamBuilder param |
| , rawQueryDirect | ||
| , rawSqlDirect | ||
| , rawQueryDirectPV | ||
| , rawSqlDirectPV |
There was a problem hiding this comment.
Restyled by fourmolu
| , rawSqlDirectPV | |
| , rawSqlDirect |
| -- encoded via the backend's 'FieldEncode' instance for 'PersistValue'. | ||
| rawQueryDirectPV | ||
| :: forall a backend m1 m2 | ||
| . ( FromRow (Env backend) a, HasDirectQuery backend |
There was a problem hiding this comment.
Restyled by fourmolu
| . ( FromRow (Env backend) a, HasDirectQuery backend | |
| . ( FromRow (Env backend) a | |
| , HasDirectQuery backend |
| -- 'SqlBackend'). | ||
| class DirectEntity a where | ||
| lookupDirectDecoder | ||
| :: Typeable env |
There was a problem hiding this comment.
Restyled by fourmolu
| :: Typeable env | |
| :: (Typeable env) |
| acq = directQuerySource backend sql params | ||
| pure $ fmap (.| decodeWithPrepare) acq | ||
| where | ||
| decodeWithPrepare :: MonadIO m => ConduitM (Env backend) a m () |
There was a problem hiding this comment.
Restyled by fourmolu
| decodeWithPrepare :: MonadIO m => ConduitM (Env backend) a m () | |
| decodeWithPrepare :: (MonadIO m) => ConduitM (Env backend) a m () |
| ( FieldEncode (..) | ||
| , ParamBuilder (..) | ||
| , ToRow (..) | ||
| , writeParam |
There was a problem hiding this comment.
Restyled by fourmolu
| , writeParam |
| | ISRSingleCustom Text [PersistValue] | ||
| -- ^ Like 'ISRSingle' but carries a custom parameter list instead of | ||
| -- using the default flattened row-major values. This allows backends | ||
| -- to rearrange parameters (e.g. column-major arrays for @UNNEST@). | ||
| -- | ||
| -- @since 2.18.2 |
There was a problem hiding this comment.
Restyled by fourmolu
| | ISRSingleCustom Text [PersistValue] | |
| -- ^ Like 'ISRSingle' but carries a custom parameter list instead of | |
| -- using the default flattened row-major values. This allows backends | |
| -- to rearrange parameters (e.g. column-major arrays for @UNNEST@). | |
| -- | |
| -- @since 2.18.2 | |
| | -- | Like 'ISRSingle' but carries a custom parameter list instead of | |
| -- using the default flattened row-major values. This allows backends | |
| -- to rearrange parameters (e.g. column-major arrays for @UNNEST@). | |
| -- | |
| -- @since 2.18.2 | |
| ISRSingleCustom Text [PersistValue] |
| :: forall r. env | ||
| -> (Text -> IO r) -- on error | ||
| -> (a -> IO r) -- on success |
There was a problem hiding this comment.
Restyled by fourmolu
| :: forall r. env | |
| -> (Text -> IO r) -- on error | |
| -> (a -> IO r) -- on success | |
| :: forall r | |
| . env | |
| -> (Text -> IO r) -- on error | |
| -> (a -> IO r) -- on success |
| import Database.Persist.Sql.DirectRaw | ||
| ( HasDirectQuery (..) | ||
| , rawQueryDirect | ||
| , rawSqlDirect |
There was a problem hiding this comment.
Restyled by fourmolu
| , rawSqlDirect |
| , prepareNextField | ||
| , runRowReader | ||
| , runRowReaderCPS | ||
| , runRowDecoderCPS |
There was a problem hiding this comment.
Restyled by fourmolu
| , runRowDecoderCPS |
| ISRSingle sql -> rawSql sql (concat valss) | ||
| _ -> error "ISRSingle is expected from the connInsertManySql function" | ||
| ISRSingleCustom sql params -> rawSql sql params | ||
| _ -> error "ISRSingle or ISRSingleCustom is expected from the connInsertManySql function" |
There was a problem hiding this comment.
Restyled by fourmolu
| _ -> error "ISRSingle or ISRSingleCustom is expected from the connInsertManySql function" | |
| _ -> | |
| error | |
| "ISRSingle or ISRSingleCustom is expected from the connInsertManySql function" |
|
|
||
| -- | Run a 'RowDecoder' in CPS. | ||
| runRowDecoderCPS | ||
| :: RowDecoder env a -> env |
There was a problem hiding this comment.
Restyled by fourmolu
| :: RowDecoder env a -> env | |
| :: RowDecoder env a | |
| -> env |
| , DirectEntity (..) | ||
| , Counter |
There was a problem hiding this comment.
Restyled by fourmolu
| , DirectEntity (..) | |
| , Counter | |
| , RowDecoder (..) | |
| , RowReader (..) |
| decoder <- liftIO $ prepareRow firstEnv ctr | ||
| (throwIO . PersistMarshalError) | ||
| pure | ||
| val <- liftIO $ runRowDecoderCPS decoder firstEnv | ||
| (throwIO . PersistMarshalError) | ||
| pure |
There was a problem hiding this comment.
Restyled by fourmolu
| decoder <- liftIO $ prepareRow firstEnv ctr | |
| (throwIO . PersistMarshalError) | |
| pure | |
| val <- liftIO $ runRowDecoderCPS decoder firstEnv | |
| (throwIO . PersistMarshalError) | |
| pure | |
| decoder <- | |
| liftIO $ | |
| prepareRow | |
| firstEnv | |
| ctr | |
| (throwIO . PersistMarshalError) | |
| pure | |
| val <- | |
| liftIO $ | |
| runRowDecoderCPS | |
| decoder | |
| firstEnv | |
| (throwIO . PersistMarshalError) | |
| pure |
| -- encoded via the backend's 'FieldEncode' instance for 'PersistValue'. | ||
| rawSqlDirectPV | ||
| :: forall a backend m | ||
| . ( FromRow (Env backend) a, HasDirectQuery backend |
There was a problem hiding this comment.
Restyled by fourmolu
| . ( FromRow (Env backend) a, HasDirectQuery backend | |
| . ( FromRow (Env backend) a | |
| , HasDirectQuery backend |
| v <- liftIO $ runRowDecoderCPS decoder env | ||
| (throwIO . PersistMarshalError) | ||
| pure |
There was a problem hiding this comment.
Restyled by fourmolu
| v <- liftIO $ runRowDecoderCPS decoder env | |
| (throwIO . PersistMarshalError) | |
| pure | |
| v <- | |
| liftIO $ | |
| runRowDecoderCPS | |
| decoder | |
| env | |
| (throwIO . PersistMarshalError) | |
| pure |
| -- decodes directly. Otherwise it returns 'Nothing' and the caller | ||
| -- should fall back to the 'PersistValue' path. | ||
| rawSqlDirectCompat | ||
| :: forall a m. (DirectEntity a, MonadIO m) |
There was a problem hiding this comment.
Restyled by fourmolu
| :: forall a m. (DirectEntity a, MonadIO m) | |
| :: forall a m | |
| . (DirectEntity a, MonadIO m) |
| :: forall a backend m1 m2 | ||
| . ( FromRow (Env backend) a, HasDirectQuery backend | ||
| , FieldEncode (Param backend) PersistValue | ||
| , MonadIO m1, MonadIO m2 ) |
There was a problem hiding this comment.
Restyled by fourmolu
| , MonadIO m1, MonadIO m2 ) | |
| , MonadIO m1 | |
| , MonadIO m2 | |
| ) |
| :: forall a backend m | ||
| . ( FromRow (Env backend) a, HasDirectQuery backend | ||
| , FieldEncode (Param backend) PersistValue | ||
| , MonadIO m ) |
There was a problem hiding this comment.
Restyled by fourmolu
| , MonadIO m ) | |
| , MonadIO m | |
| ) |
| -> Text -- ^ SQL | ||
| -> V.Vector (Param backend) -- ^ Encoded parameters |
There was a problem hiding this comment.
Restyled by fourmolu
| -> Text -- ^ SQL | |
| -> V.Vector (Param backend) -- ^ Encoded parameters | |
| -> Text | |
| -- ^ SQL | |
| -> V.Vector (Param backend) | |
| -- ^ Encoded parameters |
| ( RowReader (..) | ||
| , RowDecoder (..) |
There was a problem hiding this comment.
Restyled by fourmolu
| ( RowReader (..) | |
| , RowDecoder (..) | |
| ( Counter | |
| , DirectEntity (..) |
| -- 'Typeable' and @eqTypeRep@. | ||
| data DirectQueryCap where | ||
| MkDirectQueryCap | ||
| :: forall env. Typeable env |
There was a problem hiding this comment.
Restyled by fourmolu
| :: forall env. Typeable env | |
| :: forall env | |
| . (Typeable env) |
Summary
DirectDecodemodule:RowReader,FieldDecode,FromRowwith prepare-once viaprepareRow/RowDecoder,DirectEntityclass for SqlBackend bridgeDirectEncodemodule:FieldEncode,ParamBuilder(zero-copy vector-builder monoid),ToRowSql.DirectRaw:HasDirectQuery,rawQueryDirect,rawSqlDirect,rawSqlDirectCompatSql.Experimental: drop-in replacement forDatabase.Persist.Sqlthat re-exports the direct codec APIDirectQueryCapexistential toSqlBackendfor Typeable-basedSqlBackendbridgeISRSingleCustomtoInsertSqlResultfor UNNEST bulk inserts