module Database.Beam.Query.Extract
    ( -- * SQL @EXTRACT@ support

      ExtractField(..),

      extract_,

      -- ** SQL92 fields
      hour_, minutes_, seconds_,
      year_, month_, day_,

      HasSqlTime, HasSqlDate
    ) where

import Database.Beam.Query.Internal ( QGenExpr(..) )

import Database.Beam.Backend.SQL ( BeamSqlBackend, BeamSqlBackendSyntax )
import Database.Beam.Backend.SQL.SQL92 ( Sql92ExtractFieldSyntax
                                       , IsSql92ExpressionSyntax(..)
                                       , IsSql92ExtractFieldSyntax(..) )

import Data.Time (LocalTime, UTCTime, TimeOfDay, Day)

-- | A field that can be extracted from SQL expressions of type 'tgt'
-- that results in a type 'a', in backend 'be'.
newtype ExtractField be tgt a
    = ExtractField (Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be))

-- | Extracts the given field from the target expression
extract_ :: BeamSqlBackend be
         => ExtractField be tgt a -> QGenExpr ctxt be s tgt -> QGenExpr cxt be s a
extract_ :: forall be tgt a ctxt s cxt.
BeamSqlBackend be =>
ExtractField be tgt a
-> QGenExpr ctxt be s tgt -> QGenExpr cxt be s a
extract_ (ExtractField Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
field) (QExpr TablePrefix
-> Sql92SelectTableExpressionSyntax
     (Sql92SelectSelectTableSyntax
        (Sql92SelectSyntax (BeamSqlBackendSyntax be)))
expr) =
    (TablePrefix
 -> Sql92SelectTableExpressionSyntax
      (Sql92SelectSelectTableSyntax
         (Sql92SelectSyntax (BeamSqlBackendSyntax be))))
-> QGenExpr cxt be s a
forall context be s t.
(TablePrefix -> BeamSqlBackendExpressionSyntax be)
-> QGenExpr context be s t
QExpr (Sql92ExpressionExtractFieldSyntax
  (Sql92UpdateExpressionSyntax
     (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
-> Sql92UpdateExpressionSyntax
     (Sql92UpdateSyntax (BeamSqlBackendSyntax be))
-> Sql92UpdateExpressionSyntax
     (Sql92UpdateSyntax (BeamSqlBackendSyntax be))
forall expr.
IsSql92ExpressionSyntax expr =>
Sql92ExpressionExtractFieldSyntax expr -> expr -> expr
extractE Sql92ExpressionExtractFieldSyntax
  (Sql92UpdateExpressionSyntax
     (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
field (Sql92UpdateExpressionSyntax
   (Sql92UpdateSyntax (BeamSqlBackendSyntax be))
 -> Sql92UpdateExpressionSyntax
      (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
-> (TablePrefix
    -> Sql92UpdateExpressionSyntax
         (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
-> TablePrefix
-> Sql92UpdateExpressionSyntax
     (Sql92UpdateSyntax (BeamSqlBackendSyntax be))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TablePrefix
-> Sql92UpdateExpressionSyntax
     (Sql92UpdateSyntax (BeamSqlBackendSyntax be))
TablePrefix
-> Sql92SelectTableExpressionSyntax
     (Sql92SelectSelectTableSyntax
        (Sql92SelectSyntax (BeamSqlBackendSyntax be)))
expr)

-- | Type-class for types that contain a time component
class HasSqlTime tgt
instance HasSqlTime LocalTime
instance HasSqlTime UTCTime
instance HasSqlTime TimeOfDay

-- | Extracts the hours, minutes, or seconds from any timestamp or
-- time field
hour_, minutes_, seconds_
    :: ( BeamSqlBackend be, HasSqlTime tgt )
    => ExtractField be tgt Double
hour_ :: forall be tgt.
(BeamSqlBackend be, HasSqlTime tgt) =>
ExtractField be tgt Double
hour_    = Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
-> ExtractField be tgt Double
forall be tgt a.
Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
-> ExtractField be tgt a
ExtractField Sql92ExpressionExtractFieldSyntax
  (Sql92UpdateExpressionSyntax
     (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
forall extractField.
IsSql92ExtractFieldSyntax extractField =>
extractField
hourField
minutes_ :: forall be tgt.
(BeamSqlBackend be, HasSqlTime tgt) =>
ExtractField be tgt Double
minutes_ = Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
-> ExtractField be tgt Double
forall be tgt a.
Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
-> ExtractField be tgt a
ExtractField Sql92ExpressionExtractFieldSyntax
  (Sql92UpdateExpressionSyntax
     (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
forall extractField.
IsSql92ExtractFieldSyntax extractField =>
extractField
minutesField
seconds_ :: forall be tgt.
(BeamSqlBackend be, HasSqlTime tgt) =>
ExtractField be tgt Double
seconds_ = Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
-> ExtractField be tgt Double
forall be tgt a.
Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
-> ExtractField be tgt a
ExtractField Sql92ExpressionExtractFieldSyntax
  (Sql92UpdateExpressionSyntax
     (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
forall extractField.
IsSql92ExtractFieldSyntax extractField =>
extractField
secondsField

-- | Type-class for types that contain a date component
class HasSqlDate tgt
instance HasSqlDate LocalTime
instance HasSqlDate UTCTime
instance HasSqlDate Day

year_, month_, day_
    :: ( BeamSqlBackend be, HasSqlDate tgt )
    => ExtractField be tgt Double
year_ :: forall be tgt.
(BeamSqlBackend be, HasSqlDate tgt) =>
ExtractField be tgt Double
year_  = Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
-> ExtractField be tgt Double
forall be tgt a.
Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
-> ExtractField be tgt a
ExtractField Sql92ExpressionExtractFieldSyntax
  (Sql92UpdateExpressionSyntax
     (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
forall extractField.
IsSql92ExtractFieldSyntax extractField =>
extractField
yearField
month_ :: forall be tgt.
(BeamSqlBackend be, HasSqlDate tgt) =>
ExtractField be tgt Double
month_ = Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
-> ExtractField be tgt Double
forall be tgt a.
Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
-> ExtractField be tgt a
ExtractField Sql92ExpressionExtractFieldSyntax
  (Sql92UpdateExpressionSyntax
     (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
forall extractField.
IsSql92ExtractFieldSyntax extractField =>
extractField
monthField
day_ :: forall be tgt.
(BeamSqlBackend be, HasSqlDate tgt) =>
ExtractField be tgt Double
day_   = Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
-> ExtractField be tgt Double
forall be tgt a.
Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
-> ExtractField be tgt a
ExtractField Sql92ExpressionExtractFieldSyntax
  (Sql92UpdateExpressionSyntax
     (Sql92UpdateSyntax (BeamSqlBackendSyntax be)))
Sql92ExtractFieldSyntax (BeamSqlBackendSyntax be)
forall extractField.
IsSql92ExtractFieldSyntax extractField =>
extractField
dayField