{-|
Module: Squeal.PostgreSQL.Query.Values
Description: values statements
Copyright: (c) Eitan Chatav, 2019
Maintainer: eitan@morphism.tech
Stability: experimental

values statements
-}

{-# LANGUAGE
    ConstraintKinds
  , DeriveGeneric
  , DerivingStrategies
  , FlexibleContexts
  , FlexibleInstances
  , GADTs
  , GeneralizedNewtypeDeriving
  , LambdaCase
  , MultiParamTypeClasses
  , OverloadedLabels
  , OverloadedStrings
  , QuantifiedConstraints
  , ScopedTypeVariables
  , StandaloneDeriving
  , TypeApplications
  , TypeFamilies
  , DataKinds
  , PolyKinds
  , TypeOperators
  , RankNTypes
  , UndecidableInstances
  #-}

module Squeal.PostgreSQL.Query.Values
  ( -- ** Values
    values
  , values_
  ) where

import Data.ByteString (ByteString)
import Generics.SOP hiding (from)

import Squeal.PostgreSQL.Type.Alias
import Squeal.PostgreSQL.Expression
import Squeal.PostgreSQL.Query
import Squeal.PostgreSQL.Render

-- $setup
-- >>> import Squeal.PostgreSQL

-- | `values` computes a row value or set of row values
-- specified by value expressions. It is most commonly used
-- to generate a “constant table” within a larger command,
-- but it can be used on its own.
--
-- >>> type Row = '["a" ::: 'NotNull 'PGint4, "b" ::: 'NotNull 'PGtext]
-- >>> let query = values (1 `as` #a :* "one" `as` #b) [] :: Query lat with db '[] Row
-- >>> printSQL query
-- SELECT * FROM (VALUES ((1 :: int4), (E'one' :: text))) AS t ("a", "b")
values
  :: SListI cols
  => NP (Aliased (Expression 'Ungrouped lat with db params '[] )) cols
  -> [NP (Aliased (Expression 'Ungrouped lat with db params '[] )) cols]
  -- ^ When more than one row is specified, all the rows must
  -- must have the same number of elements
  -> Query lat with db params cols
values :: forall (cols :: [(Symbol, NullType)]) (lat :: FromType)
       (with :: FromType) (db :: SchemasType) (params :: [NullType]).
SListI cols =>
NP (Aliased (Expression 'Ungrouped lat with db params '[])) cols
-> [NP
      (Aliased (Expression 'Ungrouped lat with db params '[])) cols]
-> Query lat with db params cols
values NP (Aliased (Expression 'Ungrouped lat with db params '[])) cols
rw [NP (Aliased (Expression 'Ungrouped lat with db params '[])) cols]
rws = forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (row :: [(Symbol, NullType)]).
ByteString -> Query lat with db params row
UnsafeQuery forall a b. (a -> b) -> a -> b
$ ByteString
"SELECT * FROM"
  ByteString -> ByteString -> ByteString
<+> ByteString -> ByteString
parenthesized (
    ByteString
"VALUES"
    ByteString -> ByteString -> ByteString
<+> [ByteString] -> ByteString
commaSeparated
        ( ByteString -> ByteString
parenthesized
        forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {k} (xs :: [k]) (expression :: k -> *).
SListI xs =>
(forall (x :: k). expression x -> ByteString)
-> NP expression xs -> ByteString
renderCommaSeparated forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (ty :: (Symbol, NullType)).
Aliased (Expression 'Ungrouped lat with db params '[]) ty
-> ByteString
renderValuePart forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NP (Aliased (Expression 'Ungrouped lat with db params '[])) cols
rwforall a. a -> [a] -> [a]
:[NP (Aliased (Expression 'Ungrouped lat with db params '[])) cols]
rws )
    ) ByteString -> ByteString -> ByteString
<+> ByteString
"AS t"
  ByteString -> ByteString -> ByteString
<+> ByteString -> ByteString
parenthesized (forall {k} (xs :: [k]) (expression :: k -> *).
SListI xs =>
(forall (x :: k). expression x -> ByteString)
-> NP expression xs -> ByteString
renderCommaSeparated forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (ty :: (Symbol, NullType)).
Aliased (Expression 'Ungrouped lat with db params '[]) ty
-> ByteString
renderAliasPart NP (Aliased (Expression 'Ungrouped lat with db params '[])) cols
rw)
  where
    renderAliasPart, renderValuePart
      :: Aliased (Expression 'Ungrouped lat with db params '[] ) ty -> ByteString
    renderAliasPart :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (ty :: (Symbol, NullType)).
Aliased (Expression 'Ungrouped lat with db params '[]) ty
-> ByteString
renderAliasPart (Expression 'Ungrouped lat with db params '[] ty
_ `As` Alias alias
name) = forall sql. RenderSQL sql => sql -> ByteString
renderSQL Alias alias
name
    renderValuePart :: forall (lat :: FromType) (with :: FromType) (db :: SchemasType)
       (params :: [NullType]) (ty :: (Symbol, NullType)).
Aliased (Expression 'Ungrouped lat with db params '[]) ty
-> ByteString
renderValuePart (Expression 'Ungrouped lat with db params '[] ty
value `As` Alias alias
_) = forall sql. RenderSQL sql => sql -> ByteString
renderSQL Expression 'Ungrouped lat with db params '[] ty
value

-- | `values_` computes a row value or set of row values
-- specified by value expressions.
values_
  :: SListI cols
  => NP (Aliased (Expression 'Ungrouped lat with db params '[] )) cols
  -- ^ one row of values
  -> Query lat with db params cols
values_ :: forall (cols :: [(Symbol, NullType)]) (lat :: FromType)
       (with :: FromType) (db :: SchemasType) (params :: [NullType]).
SListI cols =>
NP (Aliased (Expression 'Ungrouped lat with db params '[])) cols
-> Query lat with db params cols
values_ NP (Aliased (Expression 'Ungrouped lat with db params '[])) cols
rw = forall (cols :: [(Symbol, NullType)]) (lat :: FromType)
       (with :: FromType) (db :: SchemasType) (params :: [NullType]).
SListI cols =>
NP (Aliased (Expression 'Ungrouped lat with db params '[])) cols
-> [NP
      (Aliased (Expression 'Ungrouped lat with db params '[])) cols]
-> Query lat with db params cols
values NP (Aliased (Expression 'Ungrouped lat with db params '[])) cols
rw []