{-|
Module: Squeal.PostgreSQL.Definition.Type
Description: create and drop types
Copyright: (c) Eitan Chatav, 2019
Maintainer: eitan@morphism.tech
Stability: experimental

create and drop types
-}

{-# LANGUAGE
    AllowAmbiguousTypes
  , ConstraintKinds
  , DeriveAnyClass
  , DeriveGeneric
  , DerivingStrategies
  , FlexibleContexts
  , FlexibleInstances
  , GADTs
  , LambdaCase
  , MultiParamTypeClasses
  , OverloadedLabels
  , OverloadedStrings
  , RankNTypes
  , ScopedTypeVariables
  , TypeApplications
  , DataKinds
  , PolyKinds
  , TypeOperators
  , UndecidableSuperClasses
#-}

module Squeal.PostgreSQL.Definition.Type
  ( -- * Create
    createTypeEnum
  , createTypeEnumFrom
  , createTypeComposite
  , createTypeCompositeFrom
  , createTypeRange
  , createDomain
    -- * Drop
  , dropType
  , dropTypeIfExists
    -- * Alter
  , alterTypeRename
  , alterTypeSetSchema
  ) where

import Data.ByteString
import Data.Monoid
import GHC.TypeLits
import Prelude hiding ((.), id)

import qualified Generics.SOP as SOP

import Squeal.PostgreSQL.Expression.Logic
import Squeal.PostgreSQL.Expression.Type
import Squeal.PostgreSQL.Definition
import Squeal.PostgreSQL.Render
import Squeal.PostgreSQL.Type
import Squeal.PostgreSQL.Type.Alias
import Squeal.PostgreSQL.Type.List
import Squeal.PostgreSQL.Type.PG
import Squeal.PostgreSQL.Type.Schema

-- $setup
-- >>> import Squeal.PostgreSQL
-- >>> import qualified GHC.Generics as GHC
-- >>> import qualified Generics.SOP as SOP

-- | Enumerated types are created using the `createTypeEnum` command, for example
--
-- >>> printSQL $ (createTypeEnum #mood (label @"sad" :* label @"ok" :* label @"happy") :: Definition (Public '[]) '["public" ::: '["mood" ::: 'Typedef ('PGenum '["sad","ok","happy"])]])
-- CREATE TYPE "mood" AS ENUM ('sad', 'ok', 'happy');
createTypeEnum
  :: (KnownSymbol enum, Has sch db schema, SOP.All KnownSymbol labels)
  => QualifiedAlias sch enum
  -- ^ name of the user defined enumerated type
  -> NP PGlabel labels
  -- ^ labels of the enumerated type
  -> Definition db (Alter sch (Create enum ('Typedef ('PGenum labels)) schema) db)
createTypeEnum :: forall (enum :: Symbol) (sch :: Symbol)
       (db :: [(Symbol, SchemaType)]) (schema :: SchemaType)
       (labels :: [Symbol]).
(KnownSymbol enum, Has sch db schema, All KnownSymbol labels) =>
QualifiedAlias sch enum
-> NP PGlabel labels
-> Definition
     db (Alter sch (Create enum ('Typedef ('PGenum labels)) schema) db)
createTypeEnum QualifiedAlias sch enum
enum NP PGlabel labels
labels = forall (db0 :: [(Symbol, SchemaType)])
       (db1 :: [(Symbol, SchemaType)]).
ByteString -> Definition db0 db1
UnsafeDefinition forall a b. (a -> b) -> a -> b
$
  ByteString
"CREATE" ByteString -> ByteString -> ByteString
<+> ByteString
"TYPE" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL QualifiedAlias sch enum
enum ByteString -> ByteString -> ByteString
<+> ByteString
"AS" ByteString -> ByteString -> ByteString
<+> ByteString
"ENUM" ByteString -> ByteString -> ByteString
<+>
  ByteString -> ByteString
parenthesized (forall sql. RenderSQL sql => sql -> ByteString
renderSQL NP PGlabel labels
labels) forall a. Semigroup a => a -> a -> a
<> ByteString
";"

-- | Enumerated types can also be generated from a Haskell type, for example
--
-- >>> data Schwarma = Beef | Lamb | Chicken deriving GHC.Generic
-- >>> instance SOP.Generic Schwarma
-- >>> instance SOP.HasDatatypeInfo Schwarma
-- >>> :{
-- let
--   createSchwarma :: Definition (Public '[]) '["public" ::: '["schwarma" ::: 'Typedef (PG (Enumerated Schwarma))]]
--   createSchwarma = createTypeEnumFrom @Schwarma #schwarma
-- in
--   printSQL createSchwarma
-- :}
-- CREATE TYPE "schwarma" AS ENUM ('Beef', 'Lamb', 'Chicken');
createTypeEnumFrom
  :: forall hask sch enum db schema.
  ( SOP.Generic hask
  , SOP.All KnownSymbol (LabelsPG hask)
  , KnownSymbol enum
  , Has sch db schema )
  => QualifiedAlias sch enum
  -- ^ name of the user defined enumerated type
  -> Definition db (Alter sch (Create enum ('Typedef (PG (Enumerated hask))) schema) db)
createTypeEnumFrom :: forall hask (sch :: Symbol) (enum :: Symbol)
       (db :: [(Symbol, SchemaType)]) (schema :: SchemaType).
(Generic hask, All KnownSymbol (LabelsPG hask), KnownSymbol enum,
 Has sch db schema) =>
QualifiedAlias sch enum
-> Definition
     db
     (Alter
        sch (Create enum ('Typedef (PG (Enumerated hask))) schema) db)
createTypeEnumFrom QualifiedAlias sch enum
enum = forall (enum :: Symbol) (sch :: Symbol)
       (db :: [(Symbol, SchemaType)]) (schema :: SchemaType)
       (labels :: [Symbol]).
(KnownSymbol enum, Has sch db schema, All KnownSymbol labels) =>
QualifiedAlias sch enum
-> NP PGlabel labels
-> Definition
     db (Alter sch (Create enum ('Typedef ('PGenum labels)) schema) db)
createTypeEnum QualifiedAlias sch enum
enum
  (forall k l (h :: (k -> *) -> l -> *) (xs :: l) (f :: k -> *).
(HPure h, SListIN h xs) =>
(forall (a :: k). f a) -> h f xs
SOP.hpure forall (label :: Symbol) expr. IsPGlabel label expr => expr
label :: NP PGlabel (LabelsPG hask))

{- | `createTypeComposite` creates a composite type. The composite type is
specified by a list of attribute names and data types.

>>> :{
type PGcomplex = 'PGcomposite
  '[ "real"      ::: 'NotNull 'PGfloat8
   , "imaginary" ::: 'NotNull 'PGfloat8 ]
:}

>>> :{
let
  setup :: Definition (Public '[]) '["public" ::: '["complex" ::: 'Typedef PGcomplex]]
  setup = createTypeComposite #complex
    (float8 `as` #real :* float8 `as` #imaginary)
in printSQL setup
:}
CREATE TYPE "complex" AS ("real" float8, "imaginary" float8);
-}
createTypeComposite
  :: (KnownSymbol ty, Has sch db schema, SOP.SListI fields)
  => QualifiedAlias sch ty
  -- ^ name of the user defined composite type
  -> NP (Aliased (TypeExpression db)) fields
  -- ^ list of attribute names and data types
  -> Definition db (Alter sch (Create ty ('Typedef ('PGcomposite fields)) schema) db)
createTypeComposite :: forall (ty :: Symbol) (sch :: Symbol)
       (db :: [(Symbol, SchemaType)]) (schema :: SchemaType)
       (fields :: [(Symbol, NullType)]).
(KnownSymbol ty, Has sch db schema, SListI fields) =>
QualifiedAlias sch ty
-> NP (Aliased (TypeExpression db)) fields
-> Definition
     db
     (Alter sch (Create ty ('Typedef ('PGcomposite fields)) schema) db)
createTypeComposite QualifiedAlias sch ty
ty NP (Aliased (TypeExpression db)) fields
fields = forall (db0 :: [(Symbol, SchemaType)])
       (db1 :: [(Symbol, SchemaType)]).
ByteString -> Definition db0 db1
UnsafeDefinition forall a b. (a -> b) -> a -> b
$
  ByteString
"CREATE" ByteString -> ByteString -> ByteString
<+> ByteString
"TYPE" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL QualifiedAlias sch ty
ty ByteString -> ByteString -> ByteString
<+> ByteString
"AS" 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 (db :: [(Symbol, SchemaType)]) (x :: (Symbol, NullType)).
Aliased (TypeExpression db) x -> ByteString
renderField NP (Aliased (TypeExpression db)) fields
fields) forall a. Semigroup a => a -> a -> a
<> ByteString
";"
  where
    renderField :: Aliased (TypeExpression db) x -> ByteString
    renderField :: forall (db :: [(Symbol, SchemaType)]) (x :: (Symbol, NullType)).
Aliased (TypeExpression db) x -> ByteString
renderField (TypeExpression db ty
typ `As` Alias alias
alias) =
      forall sql. RenderSQL sql => sql -> ByteString
renderSQL Alias alias
alias ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL TypeExpression db ty
typ

-- | Composite types can also be generated from a Haskell type, for example
--
-- >>> data Complex = Complex {real :: Double, imaginary :: Double} deriving GHC.Generic
-- >>> instance SOP.Generic Complex
-- >>> instance SOP.HasDatatypeInfo Complex
-- >>> type Schema = '["complex" ::: 'Typedef (PG (Composite Complex))]
-- >>> :{
-- let
--   createComplex :: Definition (Public '[]) (Public Schema)
--   createComplex = createTypeCompositeFrom @Complex #complex
-- in
--   printSQL createComplex
-- :}
-- CREATE TYPE "complex" AS ("real" float8, "imaginary" float8);
createTypeCompositeFrom
  :: forall hask sch ty db schema.
  ( SOP.All (FieldTyped db) (RowPG hask)
  , KnownSymbol ty
  , Has sch db schema )
  => QualifiedAlias sch ty
  -- ^ name of the user defined composite type
  -> Definition db (Alter sch (Create ty ( 'Typedef (PG (Composite hask))) schema) db)
createTypeCompositeFrom :: forall hask (sch :: Symbol) (ty :: Symbol)
       (db :: [(Symbol, SchemaType)]) (schema :: SchemaType).
(All (FieldTyped db) (RowPG hask), KnownSymbol ty,
 Has sch db schema) =>
QualifiedAlias sch ty
-> Definition
     db
     (Alter sch (Create ty ('Typedef (PG (Composite hask))) schema) db)
createTypeCompositeFrom QualifiedAlias sch ty
ty = forall (ty :: Symbol) (sch :: Symbol)
       (db :: [(Symbol, SchemaType)]) (schema :: SchemaType)
       (fields :: [(Symbol, NullType)]).
(KnownSymbol ty, Has sch db schema, SListI fields) =>
QualifiedAlias sch ty
-> NP (Aliased (TypeExpression db)) fields
-> Definition
     db
     (Alter sch (Create ty ('Typedef ('PGcomposite fields)) schema) db)
createTypeComposite QualifiedAlias sch ty
ty
  (forall k l (h :: (k -> *) -> l -> *) (c :: k -> Constraint)
       (xs :: l) (proxy :: (k -> Constraint) -> *) (f :: k -> *).
(HPure h, AllN h c xs) =>
proxy c -> (forall (a :: k). c a => f a) -> h f xs
SOP.hcpure (forall {k} (t :: k). Proxy t
SOP.Proxy :: SOP.Proxy (FieldTyped db)) forall (db :: [(Symbol, SchemaType)]) (ty :: (Symbol, NullType)).
FieldTyped db ty =>
Aliased (TypeExpression db) ty
fieldtype
    :: NP (Aliased (TypeExpression db)) (RowPG hask))

{-|
`createDomain` creates a new domain. A domain is essentially a data type
with constraints (restrictions on the allowed set of values).

Domains are useful for abstracting common constraints on fields
into a single location for maintenance. For example, several tables might
contain email address columns, all requiring the same
`Squeal.PostgreSQL.Definition.Table.Constraint.check` constraint
to verify the address syntax. Define a domain rather than setting up
each table's constraint individually.

>>> :{
let
  createPositive :: Definition (Public '[]) (Public '["positive" ::: 'Typedef 'PGfloat4])
  createPositive = createDomain #positive real (#value .> 0)
in printSQL createPositive
:}
CREATE DOMAIN "positive" AS real CHECK (("value" > (0.0 :: float4)));
-}
createDomain
  :: (Has sch db schema, KnownSymbol dom)
  => QualifiedAlias sch dom -- ^ domain alias
  -> (forall null. TypeExpression db (null ty)) -- ^ underlying type
  -> (forall tab. Condition 'Ungrouped '[] '[] db '[] '[tab ::: '["value" ::: 'Null ty]])
    -- ^ constraint on type
  -> Definition db (Alter sch (Create dom ('Typedef ty) schema) db)
createDomain :: forall (sch :: Symbol) (db :: [(Symbol, SchemaType)])
       (schema :: SchemaType) (dom :: Symbol) (ty :: PGType).
(Has sch db schema, KnownSymbol dom) =>
QualifiedAlias sch dom
-> (forall (null :: PGType -> NullType).
    TypeExpression db (null ty))
-> (forall (tab :: Symbol).
    Condition
      'Ungrouped '[] '[] db '[] '[tab ::: '["value" ::: 'Null ty]])
-> Definition db (Alter sch (Create dom ('Typedef ty) schema) db)
createDomain QualifiedAlias sch dom
dom forall (null :: PGType -> NullType). TypeExpression db (null ty)
ty forall (tab :: Symbol).
Condition
  'Ungrouped '[] '[] db '[] '[tab ::: '["value" ::: 'Null ty]]
condition =
  forall (db0 :: [(Symbol, SchemaType)])
       (db1 :: [(Symbol, SchemaType)]).
ByteString -> Definition db0 db1
UnsafeDefinition forall a b. (a -> b) -> a -> b
$ ByteString
"CREATE DOMAIN" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL QualifiedAlias sch dom
dom
    ByteString -> ByteString -> ByteString
<+> ByteString
"AS" ByteString -> ByteString -> ByteString
<+> forall (db :: [(Symbol, SchemaType)]) (ty :: NullType).
TypeExpression db ty -> ByteString
renderTypeExpression forall (null :: PGType -> NullType). TypeExpression db (null ty)
ty
    ByteString -> ByteString -> ByteString
<+> ByteString
"CHECK" ByteString -> ByteString -> ByteString
<+> ByteString -> ByteString
parenthesized (forall sql. RenderSQL sql => sql -> ByteString
renderSQL forall (tab :: Symbol).
Condition
  'Ungrouped '[] '[] db '[] '[tab ::: '["value" ::: 'Null ty]]
condition) forall a. Semigroup a => a -> a -> a
<> ByteString
";"

{- | Range types are data types representing a range of values
of some element type (called the range's subtype).
The subtype must have a total order so that it is well-defined
whether element values are within, before, or after a range of values.

Range types are useful because they represent many element values
in a single range value, and because concepts such as overlapping ranges
can be expressed clearly.
The use of time and date ranges for scheduling purposes
is the clearest example; but price ranges,
measurement ranges from an instrument, and so forth can also be useful.

>>> :{
let
  createSmallIntRange :: Definition (Public '[]) (Public '["int2range" ::: 'Typedef ('PGrange 'PGint2)])
  createSmallIntRange = createTypeRange #int2range int2
in printSQL createSmallIntRange
:}
CREATE TYPE "int2range" AS RANGE (subtype = int2);
-}
createTypeRange
  :: (Has sch db schema, KnownSymbol range)
  => QualifiedAlias sch range -- ^ range alias
  -> (forall null. TypeExpression db (null ty)) -- ^ underlying type
  -> Definition db (Alter sch (Create range ('Typedef ('PGrange ty)) schema) db)
createTypeRange :: forall (sch :: Symbol) (db :: [(Symbol, SchemaType)])
       (schema :: SchemaType) (range :: Symbol) (ty :: PGType).
(Has sch db schema, KnownSymbol range) =>
QualifiedAlias sch range
-> (forall (null :: PGType -> NullType).
    TypeExpression db (null ty))
-> Definition
     db (Alter sch (Create range ('Typedef ('PGrange ty)) schema) db)
createTypeRange QualifiedAlias sch range
range forall (null :: PGType -> NullType). TypeExpression db (null ty)
ty = forall (db0 :: [(Symbol, SchemaType)])
       (db1 :: [(Symbol, SchemaType)]).
ByteString -> Definition db0 db1
UnsafeDefinition forall a b. (a -> b) -> a -> b
$
  ByteString
"CREATE" ByteString -> ByteString -> ByteString
<+> ByteString
"TYPE" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL QualifiedAlias sch range
range ByteString -> ByteString -> ByteString
<+> ByteString
"AS" ByteString -> ByteString -> ByteString
<+> ByteString
"RANGE" ByteString -> ByteString -> ByteString
<+>
  ByteString -> ByteString
parenthesized (ByteString
"subtype" ByteString -> ByteString -> ByteString
<+> ByteString
"=" ByteString -> ByteString -> ByteString
<+> forall (db :: [(Symbol, SchemaType)]) (ty :: NullType).
TypeExpression db ty -> ByteString
renderTypeExpression forall (null :: PGType -> NullType). TypeExpression db (null ty)
ty) forall a. Semigroup a => a -> a -> a
<> ByteString
";"

-- | Drop a type.
--
-- >>> data Schwarma = Beef | Lamb | Chicken deriving GHC.Generic
-- >>> instance SOP.Generic Schwarma
-- >>> instance SOP.HasDatatypeInfo Schwarma
-- >>> printSQL (dropType #schwarma :: Definition '["public" ::: '["schwarma" ::: 'Typedef (PG (Enumerated Schwarma))]] (Public '[]))
-- DROP TYPE "schwarma";
dropType
  :: (Has sch db schema, KnownSymbol td)
  => QualifiedAlias sch td
  -- ^ name of the user defined type
  -> Definition db (Alter sch (DropSchemum td 'Typedef schema) db)
dropType :: forall (sch :: Symbol) (db :: [(Symbol, SchemaType)])
       (schema :: SchemaType) (td :: Symbol).
(Has sch db schema, KnownSymbol td) =>
QualifiedAlias sch td
-> Definition db (Alter sch (DropSchemum td 'Typedef schema) db)
dropType QualifiedAlias sch td
tydef = forall (db0 :: [(Symbol, SchemaType)])
       (db1 :: [(Symbol, SchemaType)]).
ByteString -> Definition db0 db1
UnsafeDefinition forall a b. (a -> b) -> a -> b
$ ByteString
"DROP TYPE" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL QualifiedAlias sch td
tydef forall a. Semigroup a => a -> a -> a
<> ByteString
";"

-- | Drop a type if it exists.
dropTypeIfExists
  :: (Has sch db schema, KnownSymbol td)
  => QualifiedAlias sch td
  -- ^ name of the user defined type
  -> Definition db (Alter sch (DropSchemumIfExists td 'Typedef schema) db)
dropTypeIfExists :: forall (sch :: Symbol) (db :: [(Symbol, SchemaType)])
       (schema :: SchemaType) (td :: Symbol).
(Has sch db schema, KnownSymbol td) =>
QualifiedAlias sch td
-> Definition
     db (Alter sch (DropSchemumIfExists td 'Typedef schema) db)
dropTypeIfExists QualifiedAlias sch td
tydef = forall (db0 :: [(Symbol, SchemaType)])
       (db1 :: [(Symbol, SchemaType)]).
ByteString -> Definition db0 db1
UnsafeDefinition forall a b. (a -> b) -> a -> b
$
  ByteString
"DROP TYPE IF EXISTS" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL QualifiedAlias sch td
tydef forall a. Semigroup a => a -> a -> a
<> ByteString
";"

-- | `alterTypeRename` changes the name of a type from the schema.
--
-- >>> type DB = '[ "public" ::: '[ "foo" ::: 'Typedef 'PGbool ] ]
-- >>> :{
--  let def :: Definition DB '["public" ::: '["bar" ::: 'Typedef 'PGbool ] ]
--      def = alterTypeRename #foo #bar
--  in printSQL def
-- :}
-- ALTER TYPE "foo" RENAME TO "bar";
alterTypeRename
  :: ( Has sch db schema
     , KnownSymbol ty1
     , Has ty0 schema ('Typedef ty))
  => QualifiedAlias sch ty0 -- ^ type to rename
  -> Alias ty1 -- ^ what to rename it
  -> Definition db (Alter sch (Rename ty0 ty1 schema) db )
alterTypeRename :: forall (sch :: Symbol) (db :: [(Symbol, SchemaType)])
       (schema :: SchemaType) (ty1 :: Symbol) (ty0 :: Symbol)
       (ty :: PGType).
(Has sch db schema, KnownSymbol ty1,
 Has ty0 schema ('Typedef ty)) =>
QualifiedAlias sch ty0
-> Alias ty1
-> Definition db (Alter sch (Rename ty0 ty1 schema) db)
alterTypeRename QualifiedAlias sch ty0
ty0 Alias ty1
ty1 = forall (db0 :: [(Symbol, SchemaType)])
       (db1 :: [(Symbol, SchemaType)]).
ByteString -> Definition db0 db1
UnsafeDefinition forall a b. (a -> b) -> a -> b
$
  ByteString
"ALTER TYPE" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL QualifiedAlias sch ty0
ty0
  ByteString -> ByteString -> ByteString
<+> ByteString
"RENAME TO" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL Alias ty1
ty1 forall a. Semigroup a => a -> a -> a
<> ByteString
";"

{- | This form moves the type into another schema.

>>> type DB0 = '[ "sch0" ::: '[ "ty" ::: 'Typedef 'PGfloat8 ], "sch1" ::: '[] ]
>>> type DB1 = '[ "sch0" ::: '[], "sch1" ::: '[ "ty" ::: 'Typedef 'PGfloat8 ] ]
>>> :{
let def :: Definition DB0 DB1
    def = alterTypeSetSchema (#sch0 ! #ty) #sch1
in printSQL def
:}
ALTER TYPE "sch0"."ty" SET SCHEMA "sch1";
-}
alterTypeSetSchema
  :: ( Has sch0 db schema0
     , Has ty schema0 ('Typedef td)
     , Has sch1 db schema1 )
  => QualifiedAlias sch0 ty -- ^ type to move
  -> Alias sch1 -- ^ where to move it
  -> Definition db (SetSchema sch0 sch1 schema0 schema1 ty 'Typedef td db)
alterTypeSetSchema :: forall (sch0 :: Symbol) (db :: [(Symbol, SchemaType)])
       (schema0 :: SchemaType) (ty :: Symbol) (td :: PGType)
       (sch1 :: Symbol) (schema1 :: SchemaType).
(Has sch0 db schema0, Has ty schema0 ('Typedef td),
 Has sch1 db schema1) =>
QualifiedAlias sch0 ty
-> Alias sch1
-> Definition
     db (SetSchema sch0 sch1 schema0 schema1 ty 'Typedef td db)
alterTypeSetSchema QualifiedAlias sch0 ty
ty Alias sch1
sch = forall (db0 :: [(Symbol, SchemaType)])
       (db1 :: [(Symbol, SchemaType)]).
ByteString -> Definition db0 db1
UnsafeDefinition forall a b. (a -> b) -> a -> b
$
  ByteString
"ALTER TYPE" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL QualifiedAlias sch0 ty
ty ByteString -> ByteString -> ByteString
<+> ByteString
"SET SCHEMA" ByteString -> ByteString -> ByteString
<+> forall sql. RenderSQL sql => sql -> ByteString
renderSQL Alias sch1
sch forall a. Semigroup a => a -> a -> a
<> ByteString
";"