packages feed

rel8-1.5.0.0: src/Rel8/Query/Materialize.hs

{-# language FlexibleContexts #-}

module Rel8.Query.Materialize
  ( materialize
  )
where

-- base
import Prelude

-- opaleye
import Opaleye.With ( withMaterializedExplicit )

-- rel8
import Rel8.Expr ( Expr )
import Rel8.Query ( Query )
import Rel8.Query.Opaleye ( fromOpaleye, toOpaleye )
import Rel8.Query.Rebind ( rebind )
import Rel8.Table ( Table )
import Rel8.Table.Opaleye ( unpackspec )


-- | 'materialize' takes a 'Query' and fully evaluates it and caches the
-- results thereof, and passes to a continuation a new 'Query' that simply
-- looks up these cached results. It's usually best not to use this and to let
-- the Postgres optimizer decide for itself what's best, but if you know what
-- you're doing this can sometimes help to nudge it in a particular direction.
--
-- 'materialize' is currently implemented in terms of Postgres'
-- [@WITH](https://www.postgresql.org/docs/current/queries-with.html) syntax,
-- specifically the @WITH _ AS MATERIALIZED (_)@ form introduced in PostgreSQL
-- 12. This means that 'materialize' can only be used with PostgreSQL 12 or
-- newer.
materialize :: (Table Expr a, Table Expr b)
  => Query a -> (Query a -> Query b) -> Query b
materialize query f =
  (>>= rebind "with") . fromOpaleye $
    withMaterializedExplicit unpackspec
      (toOpaleye query')
      (toOpaleye . f . fromOpaleye)
  where
    query' = query