module Sqel.Statement where

import qualified Hasql.Decoders as Decoders
import Hasql.Decoders (Row, noResult)
import qualified Hasql.Encoders as Encoders
import Hasql.Encoders (Params)
import Hasql.Statement (Statement (Statement))
import Lens.Micro ((^.))
import Sqel.Data.Codec (Encoder (Encoder))
import qualified Sqel.Data.PgType as PgTable
import Sqel.Data.PgType (
  ColumnType (ColumnPrim),
  PgColumn (PgColumn),
  PgColumnName (PgColumnName),
  PgColumns (PgColumns),
  PgTable (PgTable),
  )
import Sqel.Data.Projection (Projection)
import Sqel.Data.QuerySchema (QuerySchema (QuerySchema))
import Sqel.Data.Selector (Selector (Selector))
import Sqel.Data.Sql (Sql (Sql), sql)
import Sqel.Data.SqlFragment (
  CommaSep (CommaSep),
  Delete (Delete),
  Insert (Insert),
  Returning (Returning),
  Update (Update),
  )
import Sqel.Data.TableSchema (TableSchema (TableSchema))
import Sqel.ResultShape (ResultShape (resultShape))
import qualified Sqel.Sql.Select as Sql
import qualified Sqel.Sql.Type as Sql
import Sqel.Text.Quote (dquote)

statement ::
  ResultShape d result =>
  Bool ->
  Sql ->
  Row d ->
  Params p ->
  Statement p result
statement :: forall d result p.
ResultShape d result =>
Bool -> Sql -> Row d -> Params p -> Statement p result
statement Bool
prep (Sql Text
s) Row d
row Params p
params =
  forall a b.
ByteString -> Params a -> Result b -> Bool -> Statement a b
Statement (forall a b. ConvertUtf8 a b => a -> b
encodeUtf8 Text
s) Params p
params (forall d r. ResultShape d r => Row d -> Result r
resultShape Row d
row) Bool
prep

unprepared ::
   result d p .
  ResultShape d result =>
  Sql ->
  Row d ->
  Params p ->
  Statement p result
unprepared :: forall result d p.
ResultShape d result =>
Sql -> Row d -> Params p -> Statement p result
unprepared =
  forall d result p.
ResultShape d result =>
Bool -> Sql -> Row d -> Params p -> Statement p result
statement Bool
False

prepared ::
  ResultShape d result =>
  Sql ->
  Row d ->
  Params p ->
  Statement p result
prepared :: forall d result p.
ResultShape d result =>
Sql -> Row d -> Params p -> Statement p result
prepared =
  forall d result p.
ResultShape d result =>
Bool -> Sql -> Row d -> Params p -> Statement p result
statement Bool
True

plain :: Sql -> Statement () ()
plain :: Sql -> Statement () ()
plain Sql
s =
  forall a b.
ByteString -> Params a -> Result b -> Bool -> Statement a b
Statement (forall a b. ConvertUtf8 a b => a -> b
encodeUtf8 Sql
s) forall a. Monoid a => a
mempty Result ()
noResult Bool
False

selectWhere ::
   result proj q table .
  ResultShape proj result =>
  QuerySchema q table ->
  Projection proj table ->
  Statement q result
selectWhere :: forall result proj q table.
ResultShape proj result =>
QuerySchema q table -> Projection proj table -> Statement q result
selectWhere q :: QuerySchema q table
q@(QuerySchema [SelectFragment]
_ (Encoder Params q
qp Params ()
_)) Projection proj table
t =
  forall d result p.
ResultShape d result =>
Sql -> Row d -> Params p -> Statement p result
prepared (forall {k1} {k2} (f :: k1 -> k2 -> *) (proj :: k1) q (table :: k2).
ToSql (Select (f proj table)) =>
QuerySchema q table -> f proj table -> Sql
Sql.selectWhereGen QuerySchema q table
q Projection proj table
t) (Projection proj table
t forall s a. s -> Getting a s a -> a
^. forall a. IsLabel "decoder" a => a
#decoder) Params q
qp

delete ::
  ResultShape a result =>
  QuerySchema q a ->
  TableSchema a ->
  Statement q result
delete :: forall a result q.
ResultShape a result =>
QuerySchema q a -> TableSchema a -> Statement q result
delete (QuerySchema [SelectFragment]
query (Encoder Params q
qp Params ()
_)) (TableSchema PgTable a
col Row a
row Params a
_) =
  forall d result p.
ResultShape d result =>
Sql -> Row d -> Params p -> Statement p result
prepared [sql|##{Delete col} ##{query} ##{Returning col}|] Row a
row Params q
qp

insert ::
  TableSchema a ->
  Statement a ()
insert :: forall a. TableSchema a -> Statement a ()
insert (TableSchema PgTable a
col Row a
_ Params a
params) =
  forall d result p.
ResultShape d result =>
Sql -> Row d -> Params p -> Statement p result
prepared [sql|##{Insert col}|] forall (f :: * -> *). Applicative f => f ()
unit Params a
params

uniqueColumn :: PgColumn -> Maybe Selector
uniqueColumn :: PgColumn -> Maybe Selector
uniqueColumn = \case
  PgColumn (PgColumnName Text
name) (ColumnPrim PgPrimName
_ Bool
True [Sql]
_) ->
    forall a. a -> Maybe a
Just (Sql -> Selector
Selector (Text -> Sql
Sql (forall a. Exon a => a -> a
dquote Text
name)))
  PgColumn
_ ->
    forall a. Maybe a
Nothing

pattern UniqueName :: Selector -> PgColumn
pattern $mUniqueName :: forall {r}. PgColumn -> (Selector -> r) -> ((# #) -> r) -> r
UniqueName sel <- (uniqueColumn -> Just sel)

conflictFragment ::
  PgTable a ->
  Sql
conflictFragment :: forall {k} (a :: k). PgTable a -> Sql
conflictFragment table :: PgTable a
table@PgTable {$sel:columns:PgTable :: forall {k} (a :: k). PgTable a -> PgColumns
columns = PgColumns [PgColumn]
columns} =
  Maybe (NonEmpty Sql) -> Sql
format Maybe (NonEmpty Sql)
uniques
  where
    format :: Maybe (NonEmpty Sql) -> Sql
format Maybe (NonEmpty Sql)
Nothing =
      Sql
""
    format (Just NonEmpty Sql
cols) =
      [sql|on conflict (##{CommaSep (toList cols)}) do ##{Update table}|]
    uniques :: Maybe (NonEmpty Sql)
uniques =
      forall a. [a] -> Maybe (NonEmpty a)
nonEmpty [Sql
n | UniqueName (Selector Sql
n) <- [PgColumn]
columns]

upsertSql :: PgTable a -> Sql
upsertSql :: forall {k} (a :: k). PgTable a -> Sql
upsertSql PgTable a
tab =
  [sql|##{Insert tab} #{conflict}|]
  where
    conflict :: Sql
conflict = forall {k} (a :: k). PgTable a -> Sql
conflictFragment PgTable a
tab

upsert ::
  TableSchema a ->
  Statement a ()
upsert :: forall a. TableSchema a -> Statement a ()
upsert (TableSchema PgTable a
tab Row a
_ Params a
params) =
  forall d result p.
ResultShape d result =>
Sql -> Row d -> Params p -> Statement p result
prepared (forall {k} (a :: k). PgTable a -> Sql
upsertSql PgTable a
tab) forall (f :: * -> *). Applicative f => f ()
unit Params a
params

dbColumns ::
  Sql ->
  Statement Text [(Text, Text, Text, Maybe Text)]
dbColumns :: Sql -> Statement Text [(Text, Text, Text, Maybe Text)]
dbColumns Sql
code =
  forall d result p.
ResultShape d result =>
Sql -> Row d -> Params p -> Statement p result
prepared Sql
code Row (Text, Text, Text, Maybe Text)
decoder Params Text
encoder
  where
    decoder :: Row (Text, Text, Text, Maybe Text)
decoder =
      (,,,) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Row Text
text' forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Row Text
text' forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Row Text
text' forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall a. NullableOrNot Value a -> Row a
Decoders.column (forall (decoder :: * -> *) a.
decoder a -> NullableOrNot decoder (Maybe a)
Decoders.nullable Value Text
Decoders.text)
    text' :: Row Text
text' =
      forall a. NullableOrNot Value a -> Row a
Decoders.column (forall (decoder :: * -> *) a. decoder a -> NullableOrNot decoder a
Decoders.nonNullable Value Text
Decoders.text)
    encoder :: Params Text
encoder =
      forall a. NullableOrNot Value a -> Params a
Encoders.param (forall (encoder :: * -> *) a. encoder a -> NullableOrNot encoder a
Encoders.nonNullable Value Text
Encoders.text)

columnsSql :: Sql -> Sql -> Sql -> Sql
columnsSql :: Sql -> Sql -> Sql -> Sql
columnsSql Sql
entity Sql
container Sql
namePrefix =
  [sql|select c.#{entity}_name, c.data_type, c.#{namePrefix}udt_name, e.data_type
       from information_schema.#{entity}s c left join information_schema.element_types e
       on ((c.#{container}_catalog, c.#{container}_schema, c.#{container}_name, 'TABLE', c.dtd_identifier)
       = (e.object_catalog, e.object_schema, e.object_name, e.object_type, e.collection_type_identifier))
       where c.#{container}_name = $1|]

tableColumnsSql :: Sql
tableColumnsSql :: Sql
tableColumnsSql =
  Sql -> Sql -> Sql -> Sql
columnsSql Sql
"column" Sql
"table" Sql
""

typeColumnsSql :: Sql
typeColumnsSql :: Sql
typeColumnsSql =
  Sql -> Sql -> Sql -> Sql
columnsSql Sql
"attribute" Sql
"udt" Sql
"attribute_"

createTable :: PgTable a -> Statement () ()
createTable :: forall {k} (a :: k). PgTable a -> Statement () ()
createTable PgTable a
table =
  forall result d p.
ResultShape d result =>
Sql -> Row d -> Params p -> Statement p result
unprepared (forall {k} (a :: k). PgTable a -> Sql
Sql.createTable PgTable a
table) forall (f :: * -> *). Applicative f => f ()
unit forall a. Monoid a => a
mempty