{-# LANGUAGE CPP                  #-}
{-# LANGUAGE DataKinds            #-}
{-# LANGUAGE FlexibleInstances    #-}
{-# LANGUAGE OverloadedStrings    #-}
{-# LANGUAGE PatternSynonyms      #-}
{-# LANGUAGE TypeFamilies         #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE UndecidableInstances #-}

{-# OPTIONS_GHC -Wno-orphans #-}

#include "MachDeps.h"

module Database.PostgreSQL.Pure.Internal.Builder
  ( startup
  , password
  , terminate
  , query
  , parse
  , bind
  , execute
  , describePreparedStatement
  , describePortal
  , flush
  , sync
  , closePreparedStatement
  , closePortal
  ) where

import           Database.PostgreSQL.Pure.Internal.Data           (BindParameterFormatCodes (BindParameterFormatCodesAll, BindParameterFormatCodesAllDefault, BindParameterFormatCodesEach),
                                                                   BindResultFormatCodes (BindResultFormatCodesAllDefault, BindResultFormatCodesEach, BindResultFormatCodesNothing),
                                                                   FormatCode (BinaryFormat, TextFormat), Oid (Oid),
                                                                   PortalName (PortalName),
                                                                   PreparedStatementName (PreparedStatementName),
                                                                   Query (Query), ToField (toField),
                                                                   ToRecord (toRecord))
import           Database.PostgreSQL.Pure.Internal.Exception      (cantReachHere)
import qualified Database.PostgreSQL.Pure.Internal.MonadFail      as MonadFail
import qualified Database.PostgreSQL.Pure.Oid                     as Oid
import qualified Database.PostgreSQL.Simple.Time.Internal.Printer as Time

import           Control.Exception.Safe                           (assert)
import qualified Data.Bool                                        as B
import qualified Data.ByteString                                  as BS
import qualified Data.ByteString.Builder                          as BSB
import qualified Data.ByteString.Builder.Prim                     as BSBP
import qualified Data.ByteString.Lazy                             as BSL
import qualified Data.Double.Conversion.ByteString                as DC
import           Data.Fixed                                       (Fixed (MkFixed), HasResolution, Pico, resolution)
import           Data.Int                                         (Int16, Int32, Int64)
import qualified Data.Map.Strict                                  as M
import           Data.Scientific                                  (FPFormat (Exponent), Scientific, formatScientific,
                                                                   scientific)
import           Data.Time                                        (Day, DiffTime, NominalDiffTime, TimeOfDay, TimeZone,
                                                                   UTCTime)
import           Data.Time.LocalTime                              (LocalTime)
import           Data.Tuple.Single                                (Single, pattern Single)
import qualified PostgreSQL.Binary.Encoding                       as BE

startup
  :: String -- ^ user name. ASCII text.
  -> String -- ^ database name. ASCII text.
  -> BSB.Builder
startup user database =
  let
    len =
      4 -- length field
      + 2 -- protocol major version
      + 2 -- protocol minor version
      + 5 -- "user\0"
      + length user
      + 1 -- '\0'
      + 9 -- "database\0"
      + length database
      + 1 -- '\0'
      + 1 -- '\0'
  in
    BSB.int32BE (fromIntegral len)
    <> BSB.int16BE 3 -- protocol major version
    <> BSB.int16BE 0 -- protocol minor version
    <> BSB.string7 "user\0"
    <> BSB.string7 user
    <> BSB.char7 '\0'
    <> BSB.string7 "database\0" <> BSB.string7 database <> BSB.char7 '\0'
    <> BSB.char7 '\0'

password
  :: BS.ByteString -- ^ password, which may be hashed. ASCII text.
  -> BSB.Builder
password password =
  let
    len = 4 + BS.length password + 1
  in
    BSB.char7 'p'
    <> BSB.int32BE (fromIntegral len)
    <> BSB.byteString password
    <> BSB.char7 '\0'

query :: Query -> BSB.Builder
query (Query q) =
  let
    len = 4 + BS.length q + 1
  in
    BSB.char7 'Q'
    <> BSB.int32BE (fromIntegral len)
    <> BSB.byteString q
    <> BSB.char7 '\0'

terminate :: BS.ByteString
terminate = BS.pack [0x58, 0, 0, 0, 4]

parse
  :: PreparedStatementName
  -> Query
  -> [Oid] -- ^ OIDs of data types of parameters
  -> BSB.Builder
parse (PreparedStatementName name) (Query q) oids =
  let
    len = 4 + BS.length name + 1 + BS.length q + 1 + 2 + noids * 4
    noids = length oids
  in
    BSB.char7 'P'
    <> BSB.int32BE (fromIntegral len)
    <> BSB.byteString name
    <> BSB.char7 '\0'
    <> BSB.byteString q
    <> BSB.char7 '\0'
    <> BSB.int16BE (fromIntegral noids)
    <> mconcat (BSB.int32BE . (\(Oid n) -> n) <$> oids)

bind
  :: PortalName
  -> PreparedStatementName
  -> BindParameterFormatCodes
  -> [Maybe BS.ByteString]
  -> BindResultFormatCodes
  -> BSB.Builder
bind (PortalName portalName) (PreparedStatementName preparedStatementName) parameterFormatCodes parameters resultFormatCodes =
  let
    len =
      4 -- length field
      + BS.length portalName
      + 1 -- '\0'
      + BS.length preparedStatementName
      + 1 -- '\0'
      + 2 -- the number of parameter format codes
      + ( case parameterFormatCodes of -- format codes
            BindParameterFormatCodesAllDefault -> 0
            BindParameterFormatCodesAll _      -> 2
            BindParameterFormatCodesEach cs    -> 2 * length cs
        )
      + 2 -- the number of parameters
      + 4 * length parameters -- length field of each parameters
      + sum ((\p -> case p of Just bs -> BS.length bs; Nothing -> 0) <$> parameters) -- parameters themselves
      + 2 -- the number of result format codes
      + ( case resultFormatCodes of -- format codes
            BindResultFormatCodesNothing    -> 0
            BindResultFormatCodesAllDefault -> 0
            BindResultFormatCodesEach cs    -> 2 * length cs
        )
  in
    BSB.char7 'B'
    <> BSB.int32BE (fromIntegral len)
    <> BSB.byteString portalName
    <> BSB.char7 '\0'
    <> BSB.byteString preparedStatementName
    <> BSB.char7 '\0'
    <> ( case parameterFormatCodes of
           BindParameterFormatCodesAllDefault -> BSB.int16BE 0
           BindParameterFormatCodesAll c -> BSB.int16BE 1 <> BSB.int16BE (fromIntegral $ fromEnum c)
           BindParameterFormatCodesEach cs -> BSB.int16BE (fromIntegral $ length cs) <> mconcat (BSB.int16BE . fromIntegral . fromEnum <$> cs)
       )
    <> BSB.int16BE (fromIntegral $ length parameters)
    <> mconcat
         ( ( \p ->
               case p of
                 Just bs -> BSB.int32BE (fromIntegral $ BS.length bs) <> BSB.byteString bs
                 Nothing -> BSB.int32BE (-1)
           ) <$> parameters
         )
    <> ( case resultFormatCodes of
           BindResultFormatCodesNothing -> BSB.int16BE 0
           BindResultFormatCodesAllDefault -> BSB.int16BE 1
           BindResultFormatCodesEach cs -> BSB.int16BE (fromIntegral $ length cs) <> mconcat (BSB.int16BE . fromIntegral . fromEnum <$> cs)
       )

execute
  :: PortalName
  -> Int -- ^ limit of the number of rows
  -> BSB.Builder
execute (PortalName name) limitRows =
  let
    len = 4 + BS.length name + 1 + 4
  in
    BSB.char7 'E'
    <> BSB.int32BE (fromIntegral len)
    <> BSB.byteString name
    <> BSB.char7 '\0'
    <> BSB.int32BE (fromIntegral limitRows)

flush :: BS.ByteString
flush = BS.pack [0x48, 0, 0, 0, 4]

sync :: BS.ByteString
sync = BS.pack [0x53, 0, 0, 0, 4]

describePreparedStatement :: PreparedStatementName -> BSB.Builder
describePreparedStatement (PreparedStatementName name) = doDescribe 'S' name

closePreparedStatement :: PreparedStatementName -> BSB.Builder
closePreparedStatement (PreparedStatementName name) = doClose 'S' name

describePortal :: PortalName -> BSB.Builder
describePortal (PortalName name) = doDescribe 'P' name

closePortal :: PortalName -> BSB.Builder
closePortal (PortalName name) = doClose 'P' name

doDescribe :: Char -> BS.ByteString -> BSB.Builder
doDescribe typ name =
  let
    len = 4 + 1 + BS.length name + 1
  in
    BSB.char7 'D'
    <> BSB.int32BE (fromIntegral len)
    <> BSB.char7 typ
    <> BSB.byteString name
    <> BSB.char7 '\0'

doClose :: Char -> BS.ByteString -> BSB.Builder
doClose typ name =
  let
    len = 4 + 1 + BS.length name + 1
  in
    BSB.char7 'C'
    <> BSB.int32BE (fromIntegral len)
    <> BSB.char7 typ
    <> BSB.byteString name
    <> BSB.char7 '\0'

instance ToField () where
  toField _ _ _ _ _ = fail "no values for units"

instance ToField Bool where
  toField _ _ Nothing TextFormat = pure . Just . B.bool "TRUE" "FALSE"
  toField _ _ Nothing BinaryFormat = pure . Just . BE.encodingBytes . BE.bool
  toField backendParams encode (Just o) f | o == Oid.bool = toField backendParams encode Nothing f
                                          | otherwise = const $ fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: Bool"

instance ToField Int where
#if WORD_SIZE_IN_BITS > 64
  toField _ _ _ _ _ = fail "the Int's size is too large, larger then 64 bits"
#else
  toField _ _ Nothing TextFormat = pure . Just. BSL.toStrict . BSB.toLazyByteString . BSB.intDec
#if WORD_SIZE_IN_BITS > 32
  toField _ _ Nothing BinaryFormat = pure . Just . BE.encodingBytes . BE.int8_int64 . fromIntegral
  toField backendParams encode (Just o) TextFormat | o == Oid.int8 = toField backendParams encode Nothing TextFormat
  toField backendParams encode (Just o) BinaryFormat | o == Oid.int8 = toField backendParams encode Nothing BinaryFormat
#else /* the width of Int is wider than 30 bits */
  toField _ _ Nothing BinaryFormat = pure . Just . BE.encodingBytes . BE.int4_int32 . fromIntegral
  toField backendParams encode (Just o) TextFormat | o `elem` [Oid.int4, Oid.int8] = toField backendParams encode Nothing TextFormat
  toField backendParams encode (Just o) BinaryFormat | o == Oid.int4 = toField backendParams encode Nothing BinaryFormat
                                                     | o == Oid.int8 = pure . Just . BE.encodingBytes . BE.int8_int64 . fromIntegral
#endif
  toField _ _ (Just o) _ = const $ fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: Int"
#endif

instance ToField Int16 where
  toField _ _ Nothing TextFormat = pure . Just . BSL.toStrict . BSB.toLazyByteString . BSB.int16Dec
  toField _ _ Nothing BinaryFormat = pure . Just . BE.encodingBytes . BE.int2_int16
  toField backendParams encode (Just o) f | o == Oid.int2 = toField backendParams encode Nothing f
                                          | otherwise = const $ fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: Int16"

instance ToField Int32 where
  toField _ _ Nothing TextFormat = pure . Just . BSL.toStrict . BSB.toLazyByteString . BSB.int32Dec
  toField _ _ Nothing BinaryFormat = pure . Just . BE.encodingBytes . BE.int4_int32
  toField backendParams encode (Just o) TextFormat | o `elem` [Oid.int4, Oid.int8] = toField backendParams encode Nothing TextFormat
  toField backendParams encode (Just o) BinaryFormat | o == Oid.int4 = toField backendParams encode Nothing BinaryFormat
                                                     | o == Oid.int8 = pure . Just . BE.encodingBytes . BE.int8_int64 . fromIntegral
  toField _ _ (Just o) _ = const $ fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: Int32"

instance ToField Int64 where
  toField _ _ Nothing TextFormat = pure . Just . BSL.toStrict . BSB.toLazyByteString . BSB.int64Dec
  toField _ _ Nothing BinaryFormat = pure . Just . BE.encodingBytes . BE.int8_int64
  toField backendParams encode (Just o) f | o == Oid.int8 = toField backendParams encode Nothing f
                                          | otherwise = const $ fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: Int64"

instance ToField Float where
  toField _ _ Nothing TextFormat = pure . Just . DC.toShortest . realToFrac
  toField _ _ Nothing BinaryFormat = pure . Just . BE.encodingBytes . BE.float4
  toField backendParams encode (Just o) TextFormat | o `elem` [Oid.float4, Oid.float8] = toField backendParams encode Nothing TextFormat
  toField backendParams encode (Just o) BinaryFormat | o == Oid.float4 = toField backendParams encode Nothing BinaryFormat
                                                     | o == Oid.float8 = pure . Just . BE.encodingBytes . BE.float8 . realToFrac
  toField _ _ (Just o) _ = const $ fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: Float"

instance ToField Double where
  toField _ _ Nothing TextFormat = pure . Just . DC.toShortest
  toField _ _ Nothing BinaryFormat = pure . Just . BE.encodingBytes . BE.float8
  toField backendParams encode (Just o) f | o == Oid.float8 = toField backendParams encode Nothing f
                                          | otherwise = const $ fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: Double"

instance ToField Scientific where
  toField _ encode Nothing TextFormat = (Just <$>) . MonadFail.fromEither . encode . formatScientific Exponent Nothing
  toField _ _ Nothing BinaryFormat = pure . Just . BE.encodingBytes . BE.numeric
  toField backendParams encode (Just o) f | o == Oid.numeric = toField backendParams encode Nothing f
                                          | otherwise = const $ fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: Scientific"

instance HasResolution a => ToField (Fixed a) where
  toField _ encode Nothing TextFormat v = Just <$> MonadFail.fromEither (encode (show v)) -- XXX maybe slow
  toField _ _ Nothing BinaryFormat v@(MkFixed i) = pure $ Just $ BE.encodingBytes $ BE.numeric $ scientific i (fromInteger $ resolution v)
  toField backendParams encode (Just o) f v | o == Oid.numeric = toField backendParams encode Nothing f v
                                            | otherwise = fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: Fixed a (" <> show (resolution v) <> ")"

instance ToField Char where
  toField _ encode Nothing _ v = Just <$> MonadFail.fromEither (encode [v])
  toField backendParams encode (Just o) f v | o == Oid.char = toField backendParams encode Nothing f v
                                            | otherwise = fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: Char"

instance ToField String where
  toField _ encode Nothing _ = (Just <$>) . MonadFail.fromEither . encode
  toField backendParams encode (Just _) TextFormat = toField backendParams encode Nothing TextFormat
  toField backendParams encode (Just o) BinaryFormat | o `elem` [Oid.text, Oid.bpchar, Oid.varchar, Oid.name] = toField backendParams encode Nothing BinaryFormat
                                                     | otherwise = const $ fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: String"

instance ToField BS.ByteString where
  toField _ _ Nothing _ = pure . Just
  toField backendParams encode (Just o) f | o `elem` [Oid.text, Oid.bpchar, Oid.varchar, Oid.name, Oid.bytea] = toField backendParams encode Nothing f
                                          | otherwise = const $ fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: ByteString (strict)"

instance ToField Day where -- TODO infinity/-infinity
  toField _ _ Nothing TextFormat = pure . Just . BSL.toStrict . BSB.toLazyByteString . BSBP.primBounded Time.day
  toField _ _ Nothing BinaryFormat = pure . Just . BE.encodingBytes . BE.date
  toField backendParams encode (Just o) f | o == Oid.date = toField backendParams encode Nothing f
                                          | otherwise = const $ fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: Day"

instance ToField TimeOfDay where
  toField _ _ Nothing TextFormat = pure . Just . BSL.toStrict . BSB.toLazyByteString . BSBP.primBounded Time.timeOfDay
  toField backendParams _ Nothing BinaryFormat =
    case M.lookup "integer_datetimes" backendParams of
      Nothing    -> const $ fail "not found \"integer_datetimes\" backend parameter"
      Just "on"  -> pure . Just . BE.encodingBytes . BE.time_int
      Just "off" -> pure . Just . BE.encodingBytes . BE.time_float
      Just v     -> const $ fail $ "\"integer_datetimes\" has unrecognized value: " <> show v
  toField backendParams encode (Just o) f | o == Oid.time = toField backendParams encode Nothing f
                                          | otherwise = const $ fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: TimeOfDay"

instance ToField (TimeOfDay, TimeZone) where
  toField _ _ Nothing TextFormat = pure . Just . BSL.toStrict . BSB.toLazyByteString . BSBP.primBounded (Time.timeOfDay BSBP.>*< Time.timeZone)
  toField backendParams _ Nothing BinaryFormat =
    case M.lookup "integer_datetimes" backendParams of
      Nothing    -> const $ fail "not found \"integer_datetimes\" backend parameter"
      Just "on"  -> pure . Just . BE.encodingBytes . BE.timetz_int
      Just "off" -> pure . Just . BE.encodingBytes . BE.timetz_float
      Just v     -> const $ fail $ "\"integer_datetimes\" has unrecognized value: " <> show v
  toField backendParams encode (Just o) f | o == Oid.timetz = toField backendParams encode Nothing f
                                          | otherwise = const $ fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: (TimeOfDay, TimeZone)"

instance ToField LocalTime where
  toField _ _ Nothing TextFormat = pure . Just . BSL.toStrict . BSB.toLazyByteString . BSBP.primBounded Time.localTime
  toField _ _ Nothing BinaryFormat = pure . Just . BE.encodingBytes . BE.timestamp_int
  toField backendParams encode (Just o) f | o == Oid.timestamp = toField backendParams encode Nothing f
                                          | otherwise = const $ fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: LocalTime"

instance ToField UTCTime where
  toField _ _ Nothing TextFormat = pure . Just . BSL.toStrict . BSB.toLazyByteString . BSBP.primBounded Time.utcTime
  toField _ _ Nothing BinaryFormat = pure . Just . BE.encodingBytes . BE.timestamptz_int
  toField backendParams encode (Just o) f | o == Oid.timestamptz = toField backendParams encode Nothing f
                                          | otherwise = const $ fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: UTCTime"

instance ToField DiffTime where
  toField _ encode Nothing TextFormat = (Just <$>) . MonadFail.fromEither . encode . show -- XXX maybe slow
  toField _ _ Nothing BinaryFormat = pure . Just . BE.encodingBytes . BE.interval_int
  toField backendParams encode (Just o) f | o == Oid.interval = toField backendParams encode Nothing f
                                          | otherwise = const $ fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: DiffTime"

instance ToField NominalDiffTime where
  toField backendParams encode Nothing f = toField backendParams encode Nothing f . (realToFrac :: NominalDiffTime -> Pico)
  toField backendParams encode (Just o) f | o == Oid.numeric = toField backendParams encode Nothing f
                                          | otherwise = const $ fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: NominalDiffTime"

instance ToField Oid where
  toField _ _ Nothing TextFormat (Oid v) = pure $ Just $ BSL.toStrict $ BSB.toLazyByteString $ BSB.int32Dec v
  toField _ _ Nothing BinaryFormat (Oid v) = pure $ Just $ BE.encodingBytes $ BE.int4_int32 v
  toField backendParams encode (Just o) f v | o == Oid.oid = toField backendParams encode Nothing f v
                                            | otherwise = fail $ "type mismatch (ToField): OID: " <> show o <> ", Haskell: Oid"

-- 0 tuple
instance ToRecord () where
  toRecord _ _ Nothing [] _ =
    pure []
  toRecord _ _ Nothing fs _ =
    fail $ "the number of format codes must be 0, actually " <> show (length fs)
  toRecord _ _ (Just []) [] _ =
    pure []
  toRecord _ _ (Just os) [] _ =
    fail $ "the number of OIDs must be 0, actually " <> show (length os)
  toRecord _ _ _ fs _ =
    fail $ "the number of format codes must be 0, actually " <> show (length fs)

-- 1 tuple
instance
  {-# OVERLAPPABLE #-}
  (ToField a, Single c, t ~ c a)
  => ToRecord t where
  toRecord backendParams encode Nothing [format] (Single v) =
    sequence [toField backendParams encode Nothing format v]
  toRecord _ _ Nothing [_] _ =
    cantReachHere
  toRecord backendParams encode (Just [o]) [format] (Single v) =
    sequence [toField backendParams encode (Just o) format v]
  toRecord _ _ (Just os) [_] _ =
    fail $ "the number of OIDs must be 1, actually " <> show (length os)
  toRecord _ _ _ fs _ =
    fail $ "the number of format codes must be 1, actually " <> show (length fs)

-- 2 tuple
instance
  (ToField a, ToField b)
  => ToRecord (a, b) where
  toRecord backendParams encode Nothing [f0, f1] (v0, v1) =
    sequence [toField backendParams encode Nothing f0 v0, toField backendParams encode Nothing f1 v1]
  toRecord backendParams encode (Just [o0, o1]) [f0, f1] (v0, v1) =
    sequence [toField backendParams encode (Just o0) f0 v0, toField backendParams encode (Just o1) f1 v1]
  toRecord _ _ (Just os) _ _ =
    fail $ "the number of OIDs must be 2, actually " <> show (length os)
  toRecord _ _ _ fs _ =
    fail $ "the number of format codes must be 2, actually " <> show (length fs)

-- 3 tuple
instance
  (ToField a, ToField b, ToField c)
  => ToRecord (a, b, c) where
  toRecord backendParams encode Nothing [f0, f1, f2] (v0, v1, v2) =
    sequence [toField backendParams encode Nothing f0 v0, toField backendParams encode Nothing f1 v1, toField backendParams encode Nothing f2 v2]
  toRecord backendParams encode (Just [o0, o1, o2]) [f0, f1, f2] (v0, v1, v2) =
    sequence [toField backendParams encode (Just o0) f0 v0, toField backendParams encode (Just o1) f1 v1, toField backendParams encode (Just o2) f2 v2]
  toRecord _ _ (Just os) _ _ =
    fail $ "the number of OIDs must be 3, actually " <> show (length os)
  toRecord _ _ _ fs _ =
    fail $ "the number of format codes must be 3, actually " <> show (length fs)

-- 4 tuple
instance
  (ToField a, ToField b, ToField c, ToField d)
  => ToRecord (a, b, c, d) where
  toRecord backendParams encode Nothing [f0, f1, f2, f3] (v0, v1, v2, v3) =
    sequence [toField backendParams encode Nothing f0 v0, toField backendParams encode Nothing f1 v1, toField backendParams encode Nothing f2 v2, toField backendParams encode Nothing f3 v3]
  toRecord backendParams encode (Just [o0, o1, o2, o3]) [f0, f1, f2, f3] (v0, v1, v2, v3) =
    sequence [toField backendParams encode (Just o0) f0 v0, toField backendParams encode (Just o1) f1 v1, toField backendParams encode (Just o2) f2 v2, toField backendParams encode (Just o3) f3 v3]
  toRecord _ _ (Just os) _ _ =
    fail $ "the number of OIDs must be 4, actually " <> show (length os)
  toRecord _ _ _ fs _ =
    fail $ "the number of format codes must be 4, actually " <> show (length fs)

-- 5 tuple
instance
  (ToField a, ToField b, ToField c, ToField d, ToField e)
  => ToRecord (a, b, c, d, e) where
  toRecord backendParams encode Nothing [f0, f1, f2, f3, f4] (v0, v1, v2, v3, v4) =
    sequence [toField backendParams encode Nothing f0 v0, toField backendParams encode Nothing f1 v1, toField backendParams encode Nothing f2 v2, toField backendParams encode Nothing f3 v3, toField backendParams encode Nothing f4 v4]
  toRecord backendParams encode (Just [o0, o1, o2, o3, o4]) [f0, f1, f2, f3, f4] (v0, v1, v2, v3, v4) =
    sequence [toField backendParams encode (Just o0) f0 v0, toField backendParams encode (Just o1) f1 v1, toField backendParams encode (Just o2) f2 v2, toField backendParams encode (Just o3) f3 v3, toField backendParams encode (Just o4) f4 v4]
  toRecord _ _ (Just os) _ _ =
    fail $ "the number of OIDs must be 5, actually " <> show (length os)
  toRecord _ _ _ fs _ =
    fail $ "the number of format codes must be 5, actually " <> show (length fs)

-- 6 tuple
instance
  (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f)
  => ToRecord (a, b, c, d, e, f) where
  toRecord backendParams encode Nothing [f0, f1, f2, f3, f4, f5] (v0, v1, v2, v3, v4, v5) =
    sequence [toField backendParams encode Nothing f0 v0, toField backendParams encode Nothing f1 v1, toField backendParams encode Nothing f2 v2, toField backendParams encode Nothing f3 v3, toField backendParams encode Nothing f4 v4, toField backendParams encode Nothing f5 v5]
  toRecord backendParams encode (Just [o0, o1, o2, o3, o4, o5]) [f0, f1, f2, f3, f4, f5] (v0, v1, v2, v3, v4, v5) =
    sequence [toField backendParams encode (Just o0) f0 v0, toField backendParams encode (Just o1) f1 v1, toField backendParams encode (Just o2) f2 v2, toField backendParams encode (Just o3) f3 v3, toField backendParams encode (Just o4) f4 v4, toField backendParams encode (Just o5) f5 v5]
  toRecord _ _ (Just os) _ _ =
    fail $ "the number of OIDs must be 6, actually " <> show (length os)
  toRecord _ _ _ fs _ =
    fail $ "the number of format codes must be 6, actually " <> show (length fs)

-- 7 tuple
instance
  (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f, ToField g)
  => ToRecord (a, b, c, d, e, f, g) where
  toRecord backendParams encode Nothing [f0, f1, f2, f3, f4, f5, f6] (v0, v1, v2, v3, v4, v5, v6) =
    sequence [toField backendParams encode Nothing f0 v0, toField backendParams encode Nothing f1 v1, toField backendParams encode Nothing f2 v2, toField backendParams encode Nothing f3 v3, toField backendParams encode Nothing f4 v4, toField backendParams encode Nothing f5 v5, toField backendParams encode Nothing f6 v6]
  toRecord backendParams encode (Just [o0, o1, o2, o3, o4, o5, o6]) [f0, f1, f2, f3, f4, f5, f6] (v0, v1, v2, v3, v4, v5, v6) =
    sequence [toField backendParams encode (Just o0) f0 v0, toField backendParams encode (Just o1) f1 v1, toField backendParams encode (Just o2) f2 v2, toField backendParams encode (Just o3) f3 v3, toField backendParams encode (Just o4) f4 v4, toField backendParams encode (Just o5) f5 v5, toField backendParams encode (Just o6) f6 v6]
  toRecord _ _ (Just os) _ _ =
    fail $ "the number of OIDs must be 7, actually " <> show (length os)
  toRecord _ _ _ fs _ =
    fail $ "the number of format codes must be 7, actually " <> show (length fs)

-- 8 tuple
instance
  (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f, ToField g, ToField h)
  => ToRecord (a, b, c, d, e, f, g, h) where
  toRecord backendParams encode Nothing [f0, f1, f2, f3, f4, f5, f6, f7] (v0, v1, v2, v3, v4, v5, v6, v7) =
    sequence [toField backendParams encode Nothing f0 v0, toField backendParams encode Nothing f1 v1, toField backendParams encode Nothing f2 v2, toField backendParams encode Nothing f3 v3, toField backendParams encode Nothing f4 v4, toField backendParams encode Nothing f5 v5, toField backendParams encode Nothing f6 v6, toField backendParams encode Nothing f7 v7]
  toRecord backendParams encode (Just [o0, o1, o2, o3, o4, o5, o6, o7]) [f0, f1, f2, f3, f4, f5, f6, f7] (v0, v1, v2, v3, v4, v5, v6, v7) =
    sequence [toField backendParams encode (Just o0) f0 v0, toField backendParams encode (Just o1) f1 v1, toField backendParams encode (Just o2) f2 v2, toField backendParams encode (Just o3) f3 v3, toField backendParams encode (Just o4) f4 v4, toField backendParams encode (Just o5) f5 v5, toField backendParams encode (Just o6) f6 v6, toField backendParams encode (Just o7) f7 v7]
  toRecord _ _ (Just os) _ _ =
    fail $ "the number of OIDs must be 8, actually " <> show (length os)
  toRecord _ _ _ fs _ =
    fail $ "the number of format codes must be 8, actually " <> show (length fs)

-- 9 tuple
instance
  (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f, ToField g, ToField h, ToField i)
  => ToRecord (a, b, c, d, e, f, g, h, i) where
  toRecord backendParams encode Nothing [f0, f1, f2, f3, f4, f5, f6, f7, f8] (v0, v1, v2, v3, v4, v5, v6, v7, v8) =
    sequence [toField backendParams encode Nothing f0 v0, toField backendParams encode Nothing f1 v1, toField backendParams encode Nothing f2 v2, toField backendParams encode Nothing f3 v3, toField backendParams encode Nothing f4 v4, toField backendParams encode Nothing f5 v5, toField backendParams encode Nothing f6 v6, toField backendParams encode Nothing f7 v7, toField backendParams encode Nothing f8 v8]
  toRecord backendParams encode (Just [o0, o1, o2, o3, o4, o5, o6, o7, o8]) [f0, f1, f2, f3, f4, f5, f6, f7, f8] (v0, v1, v2, v3, v4, v5, v6, v7, v8) =
    sequence [toField backendParams encode (Just o0) f0 v0, toField backendParams encode (Just o1) f1 v1, toField backendParams encode (Just o2) f2 v2, toField backendParams encode (Just o3) f3 v3, toField backendParams encode (Just o4) f4 v4, toField backendParams encode (Just o5) f5 v5, toField backendParams encode (Just o6) f6 v6, toField backendParams encode (Just o7) f7 v7, toField backendParams encode (Just o8) f8 v8]
  toRecord _ _ (Just os) _ _ =
    fail $ "the number of OIDs must be 9, actually " <> show (length os)
  toRecord _ _ _ fs _ =
    fail $ "the number of format codes must be 9, actually " <> show (length fs)

-- 10 tuple
instance
  (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f, ToField g, ToField h, ToField i, ToField j)
  => ToRecord (a, b, c, d, e, f, g, h, i, j) where
  toRecord backendParams encode Nothing [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9] (v0, v1, v2, v3, v4, v5, v6, v7, v8, v9) =
    sequence [toField backendParams encode Nothing f0 v0, toField backendParams encode Nothing f1 v1, toField backendParams encode Nothing f2 v2, toField backendParams encode Nothing f3 v3, toField backendParams encode Nothing f4 v4, toField backendParams encode Nothing f5 v5, toField backendParams encode Nothing f6 v6, toField backendParams encode Nothing f7 v7, toField backendParams encode Nothing f8 v8, toField backendParams encode Nothing f9 v9]
  toRecord backendParams encode (Just [o0, o1, o2, o3, o4, o5, o6, o7, o8, o9]) [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9] (v0, v1, v2, v3, v4, v5, v6, v7, v8, v9) =
    sequence [toField backendParams encode (Just o0) f0 v0, toField backendParams encode (Just o1) f1 v1, toField backendParams encode (Just o2) f2 v2, toField backendParams encode (Just o3) f3 v3, toField backendParams encode (Just o4) f4 v4, toField backendParams encode (Just o5) f5 v5, toField backendParams encode (Just o6) f6 v6, toField backendParams encode (Just o7) f7 v7, toField backendParams encode (Just o8) f8 v8, toField backendParams encode (Just o9) f9 v9]
  toRecord _ _ (Just os) _ _ =
    fail $ "the number of OIDs must be 10, actually " <> show (length os)
  toRecord _ _ _ fs _ =
    fail $ "the number of format codes must be 10, actually " <> show (length fs)

-- 11 tuple
instance
  (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f, ToField g, ToField h, ToField i, ToField j, ToField k)
  => ToRecord (a, b, c, d, e, f, g, h, i, j, k) where
  toRecord backendParams encode Nothing [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10] (v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) =
    sequence [toField backendParams encode Nothing f0 v0, toField backendParams encode Nothing f1 v1, toField backendParams encode Nothing f2 v2, toField backendParams encode Nothing f3 v3, toField backendParams encode Nothing f4 v4, toField backendParams encode Nothing f5 v5, toField backendParams encode Nothing f6 v6, toField backendParams encode Nothing f7 v7, toField backendParams encode Nothing f8 v8, toField backendParams encode Nothing f9 v9, toField backendParams encode Nothing f10 v10]
  toRecord backendParams encode (Just [o0, o1, o2, o3, o4, o5, o6, o7, o8, o9, o10]) [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10] (v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) =
    sequence [toField backendParams encode (Just o0) f0 v0, toField backendParams encode (Just o1) f1 v1, toField backendParams encode (Just o2) f2 v2, toField backendParams encode (Just o3) f3 v3, toField backendParams encode (Just o4) f4 v4, toField backendParams encode (Just o5) f5 v5, toField backendParams encode (Just o6) f6 v6, toField backendParams encode (Just o7) f7 v7, toField backendParams encode (Just o8) f8 v8, toField backendParams encode (Just o9) f9 v9, toField backendParams encode (Just o10) f10 v10]
  toRecord _ _ (Just os) _ _ =
    fail $ "the number of OIDs must be 11, actually " <> show (length os)
  toRecord _ _ _ fs _ =
    fail $ "the number of format codes must be 11, actually " <> show (length fs)

-- 12 tuple
instance
  (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f, ToField g, ToField h, ToField i, ToField j, ToField k, ToField l)
  => ToRecord (a, b, c, d, e, f, g, h, i, j, k, l) where
  toRecord backendParams encode Nothing [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11] (v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) =
    sequence [toField backendParams encode Nothing f0 v0, toField backendParams encode Nothing f1 v1, toField backendParams encode Nothing f2 v2, toField backendParams encode Nothing f3 v3, toField backendParams encode Nothing f4 v4, toField backendParams encode Nothing f5 v5, toField backendParams encode Nothing f6 v6, toField backendParams encode Nothing f7 v7, toField backendParams encode Nothing f8 v8, toField backendParams encode Nothing f9 v9, toField backendParams encode Nothing f10 v10, toField backendParams encode Nothing f11 v11]
  toRecord backendParams encode (Just [o0, o1, o2, o3, o4, o5, o6, o7, o8, o9, o10, o11]) [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11] (v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) =
    sequence [toField backendParams encode (Just o0) f0 v0, toField backendParams encode (Just o1) f1 v1, toField backendParams encode (Just o2) f2 v2, toField backendParams encode (Just o3) f3 v3, toField backendParams encode (Just o4) f4 v4, toField backendParams encode (Just o5) f5 v5, toField backendParams encode (Just o6) f6 v6, toField backendParams encode (Just o7) f7 v7, toField backendParams encode (Just o8) f8 v8, toField backendParams encode (Just o9) f9 v9, toField backendParams encode (Just o10) f10 v10, toField backendParams encode (Just o11) f11 v11]
  toRecord _ _ (Just os) _ _ =
    fail $ "the number of OIDs must be 12, actually " <> show (length os)
  toRecord _ _ _ fs _ =
    fail $ "the number of format codes must be 12, actually " <> show (length fs)

-- 13 tuple
instance
  (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f, ToField g, ToField h, ToField i, ToField j, ToField k, ToField l, ToField m)
  => ToRecord (a, b, c, d, e, f, g, h, i, j, k, l, m) where
  toRecord backendParams encode Nothing [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12] (v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) =
    sequence [toField backendParams encode Nothing f0 v0, toField backendParams encode Nothing f1 v1, toField backendParams encode Nothing f2 v2, toField backendParams encode Nothing f3 v3, toField backendParams encode Nothing f4 v4, toField backendParams encode Nothing f5 v5, toField backendParams encode Nothing f6 v6, toField backendParams encode Nothing f7 v7, toField backendParams encode Nothing f8 v8, toField backendParams encode Nothing f9 v9, toField backendParams encode Nothing f10 v10, toField backendParams encode Nothing f11 v11, toField backendParams encode Nothing f12 v12]
  toRecord backendParams encode (Just [o0, o1, o2, o3, o4, o5, o6, o7, o8, o9, o10, o11, o12]) [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12] (v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) =
    sequence [toField backendParams encode (Just o0) f0 v0, toField backendParams encode (Just o1) f1 v1, toField backendParams encode (Just o2) f2 v2, toField backendParams encode (Just o3) f3 v3, toField backendParams encode (Just o4) f4 v4, toField backendParams encode (Just o5) f5 v5, toField backendParams encode (Just o6) f6 v6, toField backendParams encode (Just o7) f7 v7, toField backendParams encode (Just o8) f8 v8, toField backendParams encode (Just o9) f9 v9, toField backendParams encode (Just o10) f10 v10, toField backendParams encode (Just o11) f11 v11, toField backendParams encode (Just o12) f12 v12]
  toRecord _ _ (Just os) _ _ =
    fail $ "the number of OIDs must be 13, actually " <> show (length os)
  toRecord _ _ _ fs _ =
    fail $ "the number of format codes must be 13, actually " <> show (length fs)

-- 14 tuple
instance
  (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f, ToField g, ToField h, ToField i, ToField j, ToField k, ToField l, ToField m, ToField n)
  => ToRecord (a, b, c, d, e, f, g, h, i, j, k, l, m, n) where
  toRecord backendParams encode Nothing [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13] (v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) =
    sequence [toField backendParams encode Nothing f0 v0, toField backendParams encode Nothing f1 v1, toField backendParams encode Nothing f2 v2, toField backendParams encode Nothing f3 v3, toField backendParams encode Nothing f4 v4, toField backendParams encode Nothing f5 v5, toField backendParams encode Nothing f6 v6, toField backendParams encode Nothing f7 v7, toField backendParams encode Nothing f8 v8, toField backendParams encode Nothing f9 v9, toField backendParams encode Nothing f10 v10, toField backendParams encode Nothing f11 v11, toField backendParams encode Nothing f12 v12, toField backendParams encode Nothing f13 v13]
  toRecord backendParams encode (Just [o0, o1, o2, o3, o4, o5, o6, o7, o8, o9, o10, o11, o12, o13]) [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13] (v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) =
    sequence [toField backendParams encode (Just o0) f0 v0, toField backendParams encode (Just o1) f1 v1, toField backendParams encode (Just o2) f2 v2, toField backendParams encode (Just o3) f3 v3, toField backendParams encode (Just o4) f4 v4, toField backendParams encode (Just o5) f5 v5, toField backendParams encode (Just o6) f6 v6, toField backendParams encode (Just o7) f7 v7, toField backendParams encode (Just o8) f8 v8, toField backendParams encode (Just o9) f9 v9, toField backendParams encode (Just o10) f10 v10, toField backendParams encode (Just o11) f11 v11, toField backendParams encode (Just o12) f12 v12, toField backendParams encode (Just o13) f13 v13]
  toRecord _ _ (Just os) _ _ =
    fail $ "the number of OIDs must be 14, actually " <> show (length os)
  toRecord _ _ _ fs _ =
    fail $ "the number of format codes must be 14, actually " <> show (length fs)

-- 15 tuple
instance
  (ToField a, ToField b, ToField c, ToField d, ToField e, ToField f, ToField g, ToField h, ToField i, ToField j, ToField k, ToField l, ToField m, ToField n, ToField o)
  => ToRecord (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) where
  toRecord backendParams encode Nothing [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14] (v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) =
    sequence [toField backendParams encode Nothing f0 v0, toField backendParams encode Nothing f1 v1, toField backendParams encode Nothing f2 v2, toField backendParams encode Nothing f3 v3, toField backendParams encode Nothing f4 v4, toField backendParams encode Nothing f5 v5, toField backendParams encode Nothing f6 v6, toField backendParams encode Nothing f7 v7, toField backendParams encode Nothing f8 v8, toField backendParams encode Nothing f9 v9, toField backendParams encode Nothing f10 v10, toField backendParams encode Nothing f11 v11, toField backendParams encode Nothing f12 v12, toField backendParams encode Nothing f13 v13, toField backendParams encode Nothing f14 v14]
  toRecord backendParams encode (Just [o0, o1, o2, o3, o4, o5, o6, o7, o8, o9, o10, o11, o12, o13, o14]) [f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14] (v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) =
    sequence [toField backendParams encode (Just o0) f0 v0, toField backendParams encode (Just o1) f1 v1, toField backendParams encode (Just o2) f2 v2, toField backendParams encode (Just o3) f3 v3, toField backendParams encode (Just o4) f4 v4, toField backendParams encode (Just o5) f5 v5, toField backendParams encode (Just o6) f6 v6, toField backendParams encode (Just o7) f7 v7, toField backendParams encode (Just o8) f8 v8, toField backendParams encode (Just o9) f9 v9, toField backendParams encode (Just o10) f10 v10, toField backendParams encode (Just o11) f11 v11, toField backendParams encode (Just o12) f12 v12, toField backendParams encode (Just o13) f13 v13, toField backendParams encode (Just o14) f14 v14]
  toRecord _ _ (Just os) _ _ =
    fail $ "the number of OIDs must be 15, actually " <> show (length os)
  toRecord _ _ _ fs _ =
    fail $ "the number of format codes must be 15, actually " <> show (length fs)

-- list
instance
  {-# OVERLAPPING #-}
  ToField a
  => ToRecord [a] where
  toRecord backendParams encode Nothing fs vs =
    sequence $ uncurry (toField backendParams encode Nothing) <$> zip fs vs
  toRecord backendParams encode (Just os) fs vs =
    assert (length os == length fs && length fs == length vs) $ sequence $ uncurry3 (toField backendParams encode) <$> zip3 (Just <$> os) fs vs

uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
uncurry3 f (a, b, c) = f a b c