packages feed

hpgsql-simple-compat-0.1.0.0: src/Database/PostgreSQL/Simple/ToField.hs

{-# LANGUAGE UndecidableInstances #-}

------------------------------------------------------------------------------

------------------------------------------------------------------------------

-- |
-- Module:      Database.PostgreSQL.Simple.ToField
-- Copyright:   (c) 2011 MailRank, Inc.
--              (c) 2011-2012 Leon P Smith
-- License:     BSD3
-- Maintainer:  Leon P Smith <leon@melding-monads.com>
-- Stability:   experimental
--
-- The 'ToField' typeclass, for rendering a parameter to a SQL query.
module Database.PostgreSQL.Simple.ToField
  ( Action (..),
    ToField (..),
    ToPgField (..),
    toJSONField,
  )
where

import qualified Data.Aeson as Aeson
import Data.ByteString (ByteString)
import qualified Data.ByteString.Lazy as LB
import Data.CaseInsensitive (CI)
import Data.Int (Int16, Int32, Int64)
import Data.Proxy (Proxy (..))
import Data.Scientific (Scientific)
import Data.Text (Text)
import qualified Data.Text.Lazy as LT
import Data.Time.Calendar.Compat (Day)
import Data.Time.Compat (CalendarDiffTime, NominalDiffTime, UTCTime, ZonedTime)
import Data.Time.LocalTime.Compat (LocalTime, TimeOfDay)
import Data.Typeable (Typeable)
import Data.UUID.Types (UUID)
import Data.Vector (Vector)
import Hpgsql.Builder (BinaryField (..))
import Hpgsql.Encoding (EncodingContext, FieldEncoder (toTypeOid), ToPgField (..))
import Hpgsql.Query (encodeParam)
import Hpgsql.Time (Unbounded (..))
import Hpgsql.TypeInfo (Oid)
import Hpgsql.Types (Aeson, PGArray)

-- | How to render an element when substituting it into a query.
data Action
  = QueryArgument (EncodingContext -> (Maybe Oid, BinaryField))
  | -- | Escape before substituting. Use for all sql identifiers like
    -- table, column names, etc. This is used by the 'Identifier' newtype
    -- wrapper.
    EscapeIdentifier ByteString
  | -- | Concatenate a series of rendering actions.
    Many [Action]
  | -- | Just a static SQL fragment to render
    Plain LB.ByteString
  deriving (Typeable)

instance Show Action where
  show (QueryArgument _) = "QueryArgument"
  show (EscapeIdentifier b) = "EscapeIdentifier " ++ show b
  show (Many b) = "Many " ++ show b
  show (Plain b) = "Plain " ++ show b

-- | A type that may be used as a single parameter to a SQL query.
class ToField a where
  toField :: a -> Action
  default toField :: (ToPgField a) => a -> Action
  toField v = QueryArgument $ encodeParam v

instance ToField Action where
  toField v = v

instance ToField Int

instance ToField Int16

instance ToField Int32

instance ToField Int64

instance ToField Integer

instance ToField Oid

instance ToField Scientific

instance ToField Rational

instance ToField Float

instance ToField Double

instance ToField Bool

instance ToField Day

instance ToField CalendarDiffTime

instance ToField NominalDiffTime

instance ToField UTCTime

instance ToField ZonedTime

instance ToField LocalTime

instance ToField TimeOfDay

instance ToField Char

instance ToField ByteString

instance ToField LB.ByteString

instance ToField Text

instance ToField LT.Text

instance ToField String

instance ToField Aeson.Value

instance ToField UUID

instance ToField (CI Text)

instance ToField (CI LT.Text)

instance ToField (CI String)

instance ToField (Unbounded Day)

instance ToField (Unbounded UTCTime)

instance ToField (Unbounded ZonedTime)

class HasFieldType a where
  fieldTypeOid :: Proxy a -> EncodingContext -> Maybe Oid

instance (ToPgField a) => HasFieldType a where
  fieldTypeOid _ = toTypeOid (fieldEncoder @a)

instance forall a. (ToField a, HasFieldType a) => ToField (Maybe a) where
  toField = \case
    Nothing -> QueryArgument $ \encCtx -> (fieldTypeOid (Proxy @a) encCtx, SqlNull)
    Just v -> case toField v of
      QueryArgument enc -> QueryArgument enc
      _ -> error "hpgsql-simple-compat does not support (ToField (Maybe a)) instances that aren't simple query arguments"

instance (ToPgField a) => ToField (Vector a)

instance (ToPgField a) => ToField (PGArray a)

instance (Aeson.ToJSON a) => ToField (Aeson a)

toJSONField :: (Aeson.ToJSON a) => a -> Action
toJSONField v = QueryArgument $ encodeParam $ Aeson.toJSON v