sqel-0.0.1.0: lib/Sqel/Statement.hs
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 prep (Sql s) row params =
Statement (encodeUtf8 s) params (resultShape row) prep
unprepared ::
∀ result d p .
ResultShape d result =>
Sql ->
Row d ->
Params p ->
Statement p result
unprepared =
statement False
prepared ::
ResultShape d result =>
Sql ->
Row d ->
Params p ->
Statement p result
prepared =
statement True
plain :: Sql -> Statement () ()
plain s =
Statement (encodeUtf8 s) mempty noResult False
selectWhere ::
∀ result proj q table .
ResultShape proj result =>
QuerySchema q table ->
Projection proj table ->
Statement q result
selectWhere q@(QuerySchema _ (Encoder qp _)) t =
prepared (Sql.selectWhereGen q t) (t ^. #decoder) qp
delete ::
ResultShape a result =>
QuerySchema q a ->
TableSchema a ->
Statement q result
delete (QuerySchema query (Encoder qp _)) (TableSchema col row _) =
prepared [sql|##{Delete col} ##{query} ##{Returning col}|] row qp
insert ::
TableSchema a ->
Statement a ()
insert (TableSchema col _ params) =
prepared [sql|##{Insert col}|] unit params
uniqueColumn :: PgColumn -> Maybe Selector
uniqueColumn = \case
PgColumn (PgColumnName name) (ColumnPrim _ True _) ->
Just (Selector (Sql (dquote name)))
_ ->
Nothing
pattern UniqueName :: Selector -> PgColumn
pattern UniqueName sel <- (uniqueColumn -> Just sel)
conflictFragment ::
PgTable a ->
Sql
conflictFragment table@PgTable {columns = PgColumns columns} =
format uniques
where
format Nothing =
""
format (Just cols) =
[sql|on conflict (##{CommaSep (toList cols)}) do ##{Update table}|]
uniques =
nonEmpty [n | UniqueName (Selector n) <- columns]
upsertSql :: PgTable a -> Sql
upsertSql tab =
[sql|##{Insert tab} #{conflict}|]
where
conflict = conflictFragment tab
upsert ::
TableSchema a ->
Statement a ()
upsert (TableSchema tab _ params) =
prepared (upsertSql tab) unit params
dbColumns ::
Sql ->
Statement Text [(Text, Text, Text, Maybe Text)]
dbColumns code =
prepared code decoder encoder
where
decoder =
(,,,) <$> text' <*> text' <*> text' <*> Decoders.column (Decoders.nullable Decoders.text)
text' =
Decoders.column (Decoders.nonNullable Decoders.text)
encoder =
Encoders.param (Encoders.nonNullable Encoders.text)
columnsSql :: Sql -> Sql -> Sql -> Sql
columnsSql entity container 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 =
columnsSql "column" "table" ""
typeColumnsSql :: Sql
typeColumnsSql =
columnsSql "attribute" "udt" "attribute_"
createTable :: PgTable a -> Statement () ()
createTable table =
unprepared (Sql.createTable table) unit mempty