-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Squeal PostgreSQL Library -- -- Squeal is a type-safe embedding of PostgreSQL in Haskell @package squeal-postgresql @version 0.2 -- | Rendering helper functions. module Squeal.PostgreSQL.Render -- | Parenthesize a ByteString. parenthesized :: ByteString -> ByteString -- | Concatenate two ByteStrings with a space between. (<+>) :: ByteString -> ByteString -> ByteString -- | Comma separate a list of ByteStrings. commaSeparated :: [ByteString] -> ByteString -- | Comma separate the renderings of a heterogeneous list. renderCommaSeparated :: SListI xs => (forall x. expression x -> ByteString) -> NP expression xs -> ByteString -- | Comma separate the Maybe renderings of a heterogeneous list, -- dropping Nothings. renderCommaSeparatedMaybe :: SListI xs => (forall x. expression x -> Maybe ByteString) -> NP expression xs -> ByteString -- | Render a promoted Nat. renderNat :: KnownNat n => proxy n -> ByteString -- | A type-level DSL for kinds of PostgreSQL types, constraints, and -- aliases. module Squeal.PostgreSQL.Schema -- | PGType is the promoted datakind of PostgreSQL types. -- --
-- >>> import Squeal.PostgreSQL.Schema -- -- >>> :kind 'PGbool -- 'PGbool :: PGType --data PGType -- | logical Boolean (true/false) PGbool :: PGType -- | signed two-byte integer PGint2 :: PGType -- | signed four-byte integer PGint4 :: PGType -- | signed eight-byte integer PGint8 :: PGType -- | arbitrary precision numeric type PGnumeric :: PGType -- | single precision floating-point number (4 bytes) PGfloat4 :: PGType -- | double precision floating-point number (8 bytes) PGfloat8 :: PGType -- | fixed-length character string PGchar :: Nat -> PGType -- | variable-length character string PGvarchar :: Nat -> PGType -- | variable-length character string PGtext :: PGType -- | binary data ("byte array") PGbytea :: PGType -- | date and time (no time zone) PGtimestamp :: PGType -- | date and time, including time zone PGtimestamptz :: PGType -- | calendar date (year, month, day) PGdate :: PGType -- | time of day (no time zone) PGtime :: PGType -- | time of day, including time zone PGtimetz :: PGType -- | time span PGinterval :: PGType -- | universally unique identifier PGuuid :: PGType -- | IPv4 or IPv6 host address PGinet :: PGType -- | textual JSON data PGjson :: PGType -- | binary JSON data, decomposed PGjsonb :: PGType -- | variable length array PGvararray :: PGType -> PGType -- | fixed length array PGfixarray :: Nat -> PGType -> PGType -- | an escape hatch for unsupported PostgreSQL types UnsafePGType :: Symbol -> PGType -- | The object identifier of a PGType. -- --
-- >>> :set -XTypeApplications -- -- >>> oid @'PGbool -- 16 --class HasOid (ty :: PGType) oid :: HasOid ty => Word32 -- | NullityType encodes the potential presence or definite absence -- of a NULL allowing operations which are sensitive to such to -- be well typed. -- --
-- >>> :kind 'Null 'PGint4
-- 'Null 'PGint4 :: NullityType
--
-- >>> :kind 'NotNull ('PGvarchar 50)
-- 'NotNull ('PGvarchar 50) :: NullityType
--
data NullityType
-- | NULL may be present
Null :: PGType -> NullityType
-- | NULL is absent
NotNull :: PGType -> NullityType
-- | ColumnType encodes the allowance of DEFAULT and
-- NULL and the base PGType for a column.
--
-- -- >>> :set -XTypeFamilies -XTypeInType -- -- >>> import GHC.TypeLits -- -- >>> type family IdColumn :: ColumnType where IdColumn = 'Def :=> 'NotNull 'PGint4 -- -- >>> type family EmailColumn :: ColumnType where EmailColumn = 'NoDef :=> 'Null 'PGtext --type ColumnType = (ColumnConstraint, NullityType) -- | ColumnsType is a row of ColumnTypes. -- --
-- >>> :{
-- type family UsersColumns :: ColumnsType where
-- UsersColumns =
-- '[ "name" ::: 'NoDef :=> 'NotNull 'PGtext
-- , "id" ::: 'Def :=> 'NotNull 'PGint4
-- ]
-- :}
--
type ColumnsType = [(Symbol, ColumnType)]
-- | RelationType is a row of NullityType
--
--
-- >>> :{
-- type family PersonRelation :: RelationType where
-- PersonRelation =
-- '[ "name" ::: 'NotNull 'PGtext
-- , "age" ::: 'NotNull 'PGint4
-- , "dateOfBirth" ::: 'Null 'PGdate
-- ]
-- :}
--
type RelationType = [(Symbol, NullityType)]
-- | A monokinded empty RelationType.
-- | RelationsType is a row of RelationTypes, thought of as a
-- product.
type RelationsType = [(Symbol, RelationType)]
-- | TableType encodes a row of constraints on a table as well as
-- the types of its columns.
--
--
-- >>> :{
-- type family UsersTable :: TableType where
-- UsersTable =
-- '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
-- '[ "id" ::: 'Def :=> 'NotNull 'PGint4
-- , "name" ::: 'NoDef :=> 'NotNull 'PGtext
-- ]
-- :}
--
type TableType = (TableConstraints, ColumnsType)
-- | TablesType is a row of TableTypes, thought of as a
-- union.
--
--
-- >>> :{
-- type family Schema :: TablesType where
-- Schema =
-- '[ "users" :::
-- '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
-- '[ "id" ::: 'Def :=> 'NotNull 'PGint4
-- , "name" ::: 'NoDef :=> 'NotNull 'PGtext
-- , "vec" ::: 'NoDef :=> 'NotNull ('PGvararray 'PGint2)
-- ]
-- , "emails" :::
-- '[ "pk_emails" ::: 'PrimaryKey '["id"]
-- , "fk_user_id" ::: 'ForeignKey '["user_id"] "users" '["id"]
-- ] :=>
-- '[ "id" ::: 'Def :=> 'NotNull 'PGint4
-- , "user_id" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "email" ::: 'NoDef :=> 'Null 'PGtext
-- ]
-- ]
-- :}
--
type TablesType = [(Symbol, TableType)]
-- | A monokinded empty TablesType.
-- | Grouping is an auxiliary namespace, created by GROUP
-- BY clauses (group), and used for typesafe aggregation
data Grouping
-- | no aggregation permitted
Ungrouped :: Grouping
-- | aggregation required for any column which is not grouped
Grouped :: [(Symbol, Symbol)] -> Grouping
-- | A GroupedBy constraint indicates that a table qualified column
-- is a member of the auxiliary namespace created by GROUP BY
-- clauses and thus, may be called in an output Expression without
-- aggregating.
class (KnownSymbol relation, KnownSymbol column) => GroupedBy relation column bys
-- | The constraint operator, :=> is a type level pair between a
-- "constraint" and some type, for use in pairing a
-- ColumnConstraint with a NullityType to produce a
-- ColumnType or a TableConstraints and a
-- ColumnsType to produce a TableType.
type (:=>) constraint ty = '(constraint, ty)
-- | ColumnConstraint encodes the availability of DEFAULT
-- for inserts and updates. A column can be assigned a default value. A
-- data Manipulation command can also request explicitly that a
-- column be set to its default value, without having to know what that
-- value is.
data ColumnConstraint
-- | DEFAULT is available for inserts and updates
Def :: ColumnConstraint
-- | DEFAULT is unavailable for inserts and updates
NoDef :: ColumnConstraint
-- | TableConstraint encodes various forms of data constraints of
-- columns in a table. TableConstraints give you as much control
-- over the data in your tables as you wish. If a user attempts to store
-- data in a column that would violate a constraint, an error is raised.
-- This applies even if the value came from the default value definition.
data TableConstraint
Check :: [Symbol] -> TableConstraint
Unique :: [Symbol] -> TableConstraint
PrimaryKey :: [Symbol] -> TableConstraint
ForeignKey :: [Symbol] -> Symbol -> [Symbol] -> TableConstraint
-- | A TableConstraints is a row of TableConstraints.
type TableConstraints = [(Symbol, TableConstraint)]
-- | A monokinded empty TableConstraints.
-- | The alias operator ::: is like a promoted version of As,
-- a type level pair between an alias and some type, like a column alias
-- and either a ColumnType or NullityType or a table alias
-- and either a TableType or a RelationType or a constraint
-- alias and a TableConstraint.
type (:::) (alias :: Symbol) ty = '(alias, ty)
-- | Aliases are proxies for a type level string or Symbol
-- and have an IsLabel instance so that with
-- -XOverloadedLabels
--
-- -- >>> :set -XOverloadedLabels -- -- >>> #foobar :: Alias "foobar" -- Alias --data Alias (alias :: Symbol) Alias :: Alias -- |
-- >>> renderAlias #jimbob -- "jimbob" --renderAlias :: KnownSymbol alias => Alias alias -> ByteString -- | The As operator is used to name an expression. As is -- like a demoted version of :::. -- --
-- >>> Just "hello" `As` #hi :: Aliased Maybe ("hi" ::: String)
-- As (Just "hello") Alias
--
data Aliased expression aliased
[As] :: KnownSymbol alias => expression ty -> Alias alias -> Aliased expression (alias ::: ty)
-- | -- >>> let renderMaybe = fromString . maybe "Nothing" (const "Just") -- -- >>> renderAliasedAs renderMaybe (Just (3::Int) `As` #an_int) -- "Just AS an_int" --renderAliasedAs :: (forall ty. expression ty -> ByteString) -> Aliased expression aliased -> ByteString -- | AliasesOf retains the AliasesOf in a row. -- | Has alias fields field is a constraint that proves that -- fields has a field of alias ::: field. class KnownSymbol alias => Has (alias :: Symbol) (fields :: [(Symbol, kind)]) (field :: kind) | alias fields -> field -- | HasUnique alias fields field is a constraint that proves that -- fields is a singleton of alias ::: field. type HasUnique alias fields field = fields ~ '[alias ::: field] class IsLabel (x :: Symbol) a fromLabel :: IsLabel x a => a -- | Analagous to IsLabel, the constraint IsQualified defines -- ! for a column alias qualified by a table alias. class IsQualified table column expression (!) :: IsQualified table column expression => Alias table -> Alias column -> expression -- | Join is simply promoted ++ and is used in JOINs -- in FromClauses. -- | Create alias x xs adds alias ::: x to the end of -- xs and is used in createTable statements and in -- ALTER TABLE addColumn. -- | Drop alias xs removes the type associated with alias -- in xs and is used in dropTable statements and in -- ALTER TABLE dropColumn statements. -- | Alter alias xs x replaces the type associated with an -- alias in xs with the type x and is used in -- alterTable and alterColumn. -- | Rename alias0 alias1 xs replaces the alias alias0 by -- alias1 in xs and is used in alterTableRename -- and renameColumn. -- | Elem is a promoted elem. -- | In x xs is a constraint that proves that x is in -- xs. -- | PGNum is a constraint on PGType whose Expressions -- have a Num constraint. type PGNum ty = In ty '[ 'PGint2, 'PGint4, 'PGint8, 'PGnumeric, 'PGfloat4, 'PGfloat8] -- | PGIntegral is a constraint on PGType whose -- Expressions have div_ and mod_ functions. type PGIntegral ty = In ty '[ 'PGint2, 'PGint4, 'PGint8] -- | PGFloating is a constraint on PGType whose -- Expressions have Fractional and Floating -- constraints. type PGFloating ty = In ty '[ 'PGfloat4, 'PGfloat8, 'PGnumeric] -- | PGTypeOf forgets about NULL and any column -- constraints. -- | SameTypes is a constraint that proves two ColumnsTypes -- have the same length and the same ColumnTypes. -- | AllNotNull is a constraint that proves a ColumnsType has -- no NULLs. -- | NotAllNull is a constraint that proves a ColumnsType has -- some NOT NULL. -- | NullifyType is an idempotent that nullifies a -- ColumnType. -- | NullifyRelation is an idempotent that nullifies a -- ColumnsType. -- | NullifyRelations is an idempotent that nullifies a -- RelationsType used to nullify the left or right hand side of an -- outer join in a FromClause. -- | ColumnsToRelation removes column constraints. -- | RelationToColumns adds `NoDef column constraints. -- | TableToColumns removes table constraints. -- | TablesToRelations removes both table and column constraints. -- | RelationsToTables adds both trivial table and column -- constraints. -- | Check if a TableConstraint involves a column -- | Drop all TableConstraints that involve a column -- | A SameField constraint is an equality constraint on a -- FieldInfo and the column alias in a ::: pair. class SameField (fieldInfo :: FieldInfo) (fieldty :: (Symbol, NullityType)) -- | A SameFields constraint proves that a DatatypeInfo of a -- record type has the same field names as the column AliasesOf of a -- ColumnsType. instance Control.DeepSeq.NFData (Squeal.PostgreSQL.Schema.Alias alias) instance GHC.Show.Show (Squeal.PostgreSQL.Schema.Alias alias) instance GHC.Classes.Ord (Squeal.PostgreSQL.Schema.Alias alias) instance GHC.Generics.Generic (Squeal.PostgreSQL.Schema.Alias alias) instance GHC.Classes.Eq (Squeal.PostgreSQL.Schema.Alias alias) instance forall k (expression :: k -> *) (ty :: k) (alias :: GHC.Types.Symbol). GHC.Show.Show (expression ty) => GHC.Show.Show (Squeal.PostgreSQL.Schema.Aliased k expression ((Squeal.PostgreSQL.Schema.:::) k alias ty)) instance forall k (expression :: k -> *) (ty :: k) (alias :: GHC.Types.Symbol). GHC.Classes.Eq (expression ty) => GHC.Classes.Eq (Squeal.PostgreSQL.Schema.Aliased k expression ((Squeal.PostgreSQL.Schema.:::) k alias ty)) instance forall k (expression :: k -> *) (ty :: k) (alias :: GHC.Types.Symbol). GHC.Classes.Ord (expression ty) => GHC.Classes.Ord (Squeal.PostgreSQL.Schema.Aliased k expression ((Squeal.PostgreSQL.Schema.:::) k alias ty)) instance field ~ column => Squeal.PostgreSQL.Schema.SameField ('Generics.SOP.Type.Metadata.FieldInfo field) ((Squeal.PostgreSQL.Schema.:::) Squeal.PostgreSQL.Schema.NullityType column ty) instance Squeal.PostgreSQL.Schema.IsQualified table column (Squeal.PostgreSQL.Schema.Alias table, Squeal.PostgreSQL.Schema.Alias column) instance forall kind (alias :: GHC.Types.Symbol) (field :: kind) (fields :: [(GHC.Types.Symbol, kind)]). GHC.TypeLits.KnownSymbol alias => Squeal.PostgreSQL.Schema.Has kind alias ((':) (GHC.Types.Symbol, kind) ((Squeal.PostgreSQL.Schema.:::) kind alias field) fields) field instance forall kind (alias :: GHC.Types.Symbol) (fields :: [(GHC.Types.Symbol, kind)]) (field :: kind) (field' :: (GHC.Types.Symbol, kind)). (GHC.TypeLits.KnownSymbol alias, Squeal.PostgreSQL.Schema.Has kind alias fields field) => Squeal.PostgreSQL.Schema.Has kind alias ((':) (GHC.Types.Symbol, kind) field' fields) field instance alias1 ~ alias2 => GHC.OverloadedLabels.IsLabel alias1 (Squeal.PostgreSQL.Schema.Alias alias2) instance forall k (relation :: GHC.Types.Symbol) (column :: GHC.Types.Symbol) (table :: k) (bys :: [(k, GHC.Types.Symbol)]). (GHC.TypeLits.KnownSymbol relation, GHC.TypeLits.KnownSymbol column) => Squeal.PostgreSQL.Schema.GroupedBy [(k, GHC.Types.Symbol)] relation column ((':) (k, GHC.Types.Symbol) '(table, column) bys) instance forall a (relation :: GHC.Types.Symbol) (column :: GHC.Types.Symbol) (bys :: [a]) (tabcol :: a). (GHC.TypeLits.KnownSymbol relation, GHC.TypeLits.KnownSymbol column, Squeal.PostgreSQL.Schema.GroupedBy [a] relation column bys) => Squeal.PostgreSQL.Schema.GroupedBy [a] relation column ((':) a tabcol bys) instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGbool instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGint2 instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGint4 instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGint8 instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGnumeric instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGfloat4 instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGfloat8 instance Squeal.PostgreSQL.Schema.HasOid ('Squeal.PostgreSQL.Schema.PGchar n) instance Squeal.PostgreSQL.Schema.HasOid ('Squeal.PostgreSQL.Schema.PGvarchar n) instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGtext instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGbytea instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGtimestamp instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGtimestamptz instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGdate instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGtime instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGtimetz instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGinterval instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGuuid instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGinet instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGjson instance Squeal.PostgreSQL.Schema.HasOid 'Squeal.PostgreSQL.Schema.PGjsonb -- | Squeal expressions are the atoms used to build statements. module Squeal.PostgreSQL.Expression -- | Expressions are used in a variety of contexts, such as in the -- target list of the select command, as new column values in -- insertInto or update, or in search Conditions -- in a number of commands. -- -- The expression syntax allows the calculation of values from primitive -- expression using arithmetic, logical, and other operations. newtype Expression (relations :: RelationsType) (grouping :: Grouping) (params :: [NullityType]) (ty :: NullityType) UnsafeExpression :: ByteString -> Expression [renderExpression] :: Expression -> ByteString -- | A HasParameter constraint is used to indicate a value that is -- supplied externally to a SQL statement. manipulateParams, -- queryParams and traversePrepared support specifying data -- values separately from the SQL command string, in which case -- params are used to refer to the out-of-line data values. class (PGTyped (PGTypeOf ty), KnownNat n) => HasParameter (n :: Nat) (params :: [NullityType]) (ty :: NullityType) | n params -> ty param :: HasParameter n params ty => Expression relations grouping params ty -- | analagous to Nothing -- --
-- >>> renderExpression $ null_ -- "NULL" --null_ :: Expression relations grouping params ( 'Null ty) -- | analagous to Just -- --
-- >>> renderExpression $ unNull true -- "TRUE" --unNull :: Expression relations grouping params ( 'NotNull ty) -> Expression relations grouping params ( 'Null ty) -- | return the leftmost value which is not NULL -- --
-- >>> renderExpression $ coalesce [null_, unNull true] false -- "COALESCE(NULL, TRUE, FALSE)" --coalesce :: [Expression relations grouping params ( 'Null ty)] -> Expression relations grouping params ( 'NotNull ty) -> Expression relations grouping params ( 'NotNull ty) -- | analagous to fromMaybe using COALESCE -- --
-- >>> renderExpression $ fromNull true null_ -- "COALESCE(NULL, TRUE)" --fromNull :: Expression relations grouping params ( 'NotNull ty) -> Expression relations grouping params ( 'Null ty) -> Expression relations grouping params ( 'NotNull ty) -- |
-- >>> renderExpression $ null_ & isNull -- "NULL IS NULL" --isNull :: Expression relations grouping params ( 'Null ty) -> Condition relations grouping params -- |
-- >>> renderExpression $ null_ & isn'tNull -- "NULL IS NOT NULL" --isn'tNull :: Expression relations grouping params ( 'Null ty) -> Condition relations grouping params -- | analagous to maybe using IS NULL -- --
-- >>> renderExpression $ matchNull true not_ null_ -- "CASE WHEN NULL IS NULL THEN TRUE ELSE (NOT NULL) END" --matchNull :: Expression relations grouping params (nullty) -> (Expression relations grouping params ( 'NotNull ty) -> Expression relations grouping params (nullty)) -> Expression relations grouping params ( 'Null ty) -> Expression relations grouping params (nullty) -- | right inverse to fromNull, if its arguments are equal then -- nullIf gives NULL. -- --
-- >>> :set -XTypeApplications -XDataKinds -- -- >>> renderExpression @_ @_ @'[_] $ fromNull false (nullIf false (param @1)) -- "COALESCE(NULL IF (FALSE, ($1 :: bool)), FALSE)" --nullIf :: Expression relations grouping params ( 'NotNull ty) -> Expression relations grouping params ( 'NotNull ty) -> Expression relations grouping params ( 'Null ty) -- |
-- >>> renderExpression $ array [null_, unNull false, unNull true] -- "ARRAY[NULL, FALSE, TRUE]" --array :: [Expression relations grouping params ( 'Null ty)] -> Expression relations grouping params (nullity ( 'PGvararray ty)) -- |
-- >>> renderExpression $ unsafeBinaryOp "OR" true false -- "(TRUE OR FALSE)" --unsafeBinaryOp :: ByteString -> Expression relations grouping params (ty0) -> Expression relations grouping params (ty1) -> Expression relations grouping params (ty2) -- |
-- >>> renderExpression $ unsafeUnaryOp "NOT" true -- "(NOT TRUE)" --unsafeUnaryOp :: ByteString -> Expression relations grouping params (ty0) -> Expression relations grouping params (ty1) -- |
-- >>> renderExpression $ unsafeFunction "f" true -- "f(TRUE)" --unsafeFunction :: ByteString -> Expression relations grouping params (xty) -> Expression relations grouping params (yty) -- |
-- >>> :{
-- let
-- expression :: Expression relations grouping params (nullity 'PGfloat4)
-- expression = atan2_ pi 2
-- in renderExpression expression
-- :}
-- "atan2(pi(), 2)"
--
atan2_ :: PGFloating float => Expression relations grouping params (nullity float) -> Expression relations grouping params (nullity float) -> Expression relations grouping params (nullity float)
-- | -- >>> renderExpression $ true & cast int4 -- "(TRUE :: int4)" --cast :: TypeExpression ( 'NoDef :=> 'Null ty1) -> Expression relations grouping params (nullity ty0) -> Expression relations grouping params (nullity ty1) -- | integer division, truncates the result -- --
-- >>> :{
-- let
-- expression :: Expression relations grouping params (nullity 'PGint2)
-- expression = 5 `quot_` 2
-- in renderExpression expression
-- :}
-- "(5 / 2)"
--
quot_ :: PGIntegral int => Expression relations grouping params (nullity int) -> Expression relations grouping params (nullity int) -> Expression relations grouping params (nullity int)
-- | remainder upon integer division
--
--
-- >>> :{
-- let
-- expression :: Expression relations grouping params (nullity 'PGint2)
-- expression = 5 `rem_` 2
-- in renderExpression expression
-- :}
-- "(5 % 2)"
--
rem_ :: PGIntegral int => Expression relations grouping params (nullity int) -> Expression relations grouping params (nullity int) -> Expression relations grouping params (nullity int)
-- |
-- >>> :{
-- let
-- expression :: Expression relations grouping params (nullity 'PGfloat4)
-- expression = trunc pi
-- in renderExpression expression
-- :}
-- "trunc(pi())"
--
trunc :: PGFloating frac => Expression relations grouping params (nullity frac) -> Expression relations grouping params (nullity frac)
-- |
-- >>> :{
-- let
-- expression :: Expression relations grouping params (nullity 'PGfloat4)
-- expression = round_ pi
-- in renderExpression expression
-- :}
-- "round(pi())"
--
round_ :: PGFloating frac => Expression relations grouping params (nullity frac) -> Expression relations grouping params (nullity frac)
-- |
-- >>> :{
-- let
-- expression :: Expression relations grouping params (nullity 'PGfloat4)
-- expression = ceiling_ pi
-- in renderExpression expression
-- :}
-- "ceiling(pi())"
--
ceiling_ :: PGFloating frac => Expression relations grouping params (nullity frac) -> Expression relations grouping params (nullity frac)
-- | -- >>> renderExpression @_ @_ @'[_] $ greatest currentTimestamp [param @1] -- "GREATEST(CURRENT_TIMESTAMP, ($1 :: timestamp with time zone))" --greatest :: Expression relations grouping params (nullty) -> [Expression relations grouping params (nullty)] -> Expression relations grouping params (nullty) -- |
-- >>> renderExpression $ least currentTimestamp [null_] -- "LEAST(CURRENT_TIMESTAMP, NULL)" --least :: Expression relations grouping params (nullty) -> [Expression relations grouping params (nullty)] -> Expression relations grouping params (nullty) -- | A Condition is a boolean valued Expression. While SQL -- allows conditions to have NULL, Squeal instead chooses to -- disallow NULL, forcing one to handle the case of -- NULL explicitly to produce a Condition. type Condition relations grouping params = Expression relations grouping params ( 'NotNull 'PGbool) -- |
-- >>> renderExpression true -- "TRUE" --true :: Condition relations grouping params -- |
-- >>> renderExpression false -- "FALSE" --false :: Condition relations grouping params -- |
-- >>> renderExpression $ not_ true -- "(NOT TRUE)" --not_ :: Condition relations grouping params -> Condition relations grouping params -- |
-- >>> renderExpression $ true .&& false -- "(TRUE AND FALSE)" --(.&&) :: Condition relations grouping params -> Condition relations grouping params -> Condition relations grouping params -- |
-- >>> renderExpression $ true .|| false -- "(TRUE OR FALSE)" --(.||) :: Condition relations grouping params -> Condition relations grouping params -> Condition relations grouping params -- |
-- >>> :{
-- let
-- expression :: Expression relations grouping params (nullity 'PGint2)
-- expression = caseWhenThenElse [(true, 1), (false, 2)] 3
-- in renderExpression expression
-- :}
-- "CASE WHEN TRUE THEN 1 WHEN FALSE THEN 2 ELSE 3 END"
--
caseWhenThenElse :: [(Condition relations grouping params, Expression relations grouping params (ty))] -> Expression relations grouping params (ty) -> Expression relations grouping params (ty)
-- |
-- >>> :{
-- let
-- expression :: Expression relations grouping params (nullity 'PGint2)
-- expression = ifThenElse true 1 0
-- in renderExpression expression
-- :}
-- "CASE WHEN TRUE THEN 1 ELSE 0 END"
--
ifThenElse :: Condition relations grouping params -> Expression relations grouping params (ty) -> Expression relations grouping params (ty) -> Expression relations grouping params (ty)
-- | Comparison operations like .==, ./=, .>,
-- .>=, .< and .<= will produce
-- NULLs if one of their arguments is NULL.
--
-- -- >>> renderExpression $ unNull true .== null_ -- "(TRUE = NULL)" --(.==) :: Expression relations grouping params (nullity ty) -> Expression relations grouping params (nullity ty) -> Expression relations grouping params (nullity 'PGbool) infix 4 .== -- |
-- >>> renderExpression $ unNull true ./= null_ -- "(TRUE <> NULL)" --(./=) :: Expression relations grouping params (nullity ty) -> Expression relations grouping params (nullity ty) -> Expression relations grouping params (nullity 'PGbool) infix 4 ./= -- |
-- >>> renderExpression $ unNull true .>= null_ -- "(TRUE >= NULL)" --(.>=) :: Expression relations grouping params (nullity ty) -> Expression relations grouping params (nullity ty) -> Expression relations grouping params (nullity 'PGbool) infix 4 .>= -- |
-- >>> renderExpression $ unNull true .< null_ -- "(TRUE < NULL)" --(.<) :: Expression relations grouping params (nullity ty) -> Expression relations grouping params (nullity ty) -> Expression relations grouping params (nullity 'PGbool) infix 4 .< -- |
-- >>> renderExpression $ unNull true .<= null_ -- "(TRUE <= NULL)" --(.<=) :: Expression relations grouping params (nullity ty) -> Expression relations grouping params (nullity ty) -> Expression relations grouping params (nullity 'PGbool) infix 4 .<= -- |
-- >>> renderExpression $ unNull true .> null_ -- "(TRUE > NULL)" --(.>) :: Expression relations grouping params (nullity ty) -> Expression relations grouping params (nullity ty) -> Expression relations grouping params (nullity 'PGbool) infix 4 .> -- |
-- >>> renderExpression currentDate -- "CURRENT_DATE" --currentDate :: Expression relations grouping params (nullity 'PGdate) -- |
-- >>> renderExpression currentTime -- "CURRENT_TIME" --currentTime :: Expression relations grouping params (nullity 'PGtimetz) -- |
-- >>> renderExpression currentTimestamp -- "CURRENT_TIMESTAMP" --currentTimestamp :: Expression relations grouping params (nullity 'PGtimestamptz) -- |
-- >>> renderExpression localTime -- "LOCALTIME" --localTime :: Expression relations grouping params (nullity 'PGtime) -- |
-- >>> renderExpression localTimestamp -- "LOCALTIMESTAMP" --localTimestamp :: Expression relations grouping params (nullity 'PGtimestamp) -- |
-- >>> renderExpression $ lower "ARRRGGG" -- "lower(E'ARRRGGG')" --lower :: Expression relations grouping params (nullity 'PGtext) -> Expression relations grouping params (nullity 'PGtext) -- |
-- >>> renderExpression $ upper "eeee" -- "upper(E'eeee')" --upper :: Expression relations grouping params (nullity 'PGtext) -> Expression relations grouping params (nullity 'PGtext) -- |
-- >>> renderExpression $ charLength "four" -- "char_length(E'four')" --charLength :: Expression relations grouping params (nullity 'PGtext) -> Expression relations grouping params (nullity 'PGint4) -- | The like expression returns true if the string matches -- the supplied pattern. If pattern does not contain -- percent signs or underscores, then the pattern only represents the -- string itself; in that case like acts like the equals operator. -- An underscore (_) in pattern stands for (matches) any single -- character; a percent sign (%) matches any sequence of zero or more -- characters. -- --
-- >>> renderExpression $ "abc" `like` "a%" -- "(E'abc' LIKE E'a%')" --like :: Expression relations grouping params (nullity 'PGtext) -> Expression relations grouping params (nullity 'PGtext) -> Expression relations grouping params (nullity 'PGbool) -- | escape hatch to define aggregate functions unsafeAggregate :: ByteString -> Expression relations 'Ungrouped params (xty) -> Expression relations ( 'Grouped bys) params (yty) -- | escape hatch to define aggregate functions over distinct values unsafeAggregateDistinct :: ByteString -> Expression relations 'Ungrouped params (xty) -> Expression relations ( 'Grouped bys) params (yty) -- |
-- >>> :{
-- let
-- expression :: Expression '[tab ::: '["col" ::: 'Null 'PGnumeric]] ('Grouped bys) params ('Null 'PGnumeric)
-- expression = sum_ #col
-- in renderExpression expression
-- :}
-- "sum(col)"
--
sum_ :: PGNum ty => Expression relations 'Ungrouped params (nullity ty) -> Expression relations ( 'Grouped bys) params (nullity ty)
-- |
-- >>> :{
-- let
-- expression :: Expression '[tab ::: '["col" ::: nullity 'PGnumeric]] ('Grouped bys) params (nullity 'PGnumeric)
-- expression = sumDistinct #col
-- in renderExpression expression
-- :}
-- "sum(DISTINCT col)"
--
sumDistinct :: PGNum ty => Expression relations 'Ungrouped params (nullity ty) -> Expression relations ( 'Grouped bys) params (nullity ty)
-- | A constraint for PGTypes that you can take averages of and the
-- resulting PGType.
class PGAvg ty avg | ty -> avg
avg, avgDistinct :: PGAvg ty avg => Expression relations 'Ungrouped params (nullity ty) -> Expression relations ( 'Grouped bys) params (nullity avg)
avg, avgDistinct :: PGAvg ty avg => Expression relations 'Ungrouped params (nullity ty) -> Expression relations ( 'Grouped bys) params (nullity avg)
-- |
-- >>> :{
-- let
-- expression :: Expression '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
-- expression = bitAnd #col
-- in renderExpression expression
-- :}
-- "bit_and(col)"
--
bitAnd :: PGIntegral int => Expression relations 'Ungrouped params (nullity int) -> Expression relations ( 'Grouped bys) params (nullity int)
-- |
-- >>> :{
-- let
-- expression :: Expression '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
-- expression = bitOr #col
-- in renderExpression expression
-- :}
-- "bit_or(col)"
--
bitOr :: PGIntegral int => Expression relations 'Ungrouped params (nullity int) -> Expression relations ( 'Grouped bys) params (nullity int)
-- |
-- >>> :{
-- let
-- expression :: Expression '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
-- expression = boolAnd #col
-- in renderExpression expression
-- :}
-- "bool_and(col)"
--
boolAnd :: Expression relations 'Ungrouped params (nullity 'PGbool) -> Expression relations ( 'Grouped bys) params (nullity 'PGbool)
-- |
-- >>> :{
-- let
-- expression :: Expression '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
-- expression = boolOr #col
-- in renderExpression expression
-- :}
-- "bool_or(col)"
--
boolOr :: Expression relations 'Ungrouped params (nullity 'PGbool) -> Expression relations ( 'Grouped bys) params (nullity 'PGbool)
-- |
-- >>> :{
-- let
-- expression :: Expression '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
-- expression = bitAndDistinct #col
-- in renderExpression expression
-- :}
-- "bit_and(DISTINCT col)"
--
bitAndDistinct :: PGIntegral int => Expression relations 'Ungrouped params (nullity int) -> Expression relations ( 'Grouped bys) params (nullity int)
-- |
-- >>> :{
-- let
-- expression :: Expression '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
-- expression = bitOrDistinct #col
-- in renderExpression expression
-- :}
-- "bit_or(DISTINCT col)"
--
bitOrDistinct :: PGIntegral int => Expression relations 'Ungrouped params (nullity int) -> Expression relations ( 'Grouped bys) params (nullity int)
-- |
-- >>> :{
-- let
-- expression :: Expression '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
-- expression = boolAndDistinct #col
-- in renderExpression expression
-- :}
-- "bool_and(DISTINCT col)"
--
boolAndDistinct :: Expression relations 'Ungrouped params (nullity 'PGbool) -> Expression relations ( 'Grouped bys) params (nullity 'PGbool)
-- |
-- >>> :{
-- let
-- expression :: Expression '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
-- expression = boolOrDistinct #col
-- in renderExpression expression
-- :}
-- "bool_or(DISTINCT col)"
--
boolOrDistinct :: Expression relations 'Ungrouped params (nullity 'PGbool) -> Expression relations ( 'Grouped bys) params (nullity 'PGbool)
-- | A special aggregation that does not require an input
--
-- -- >>> renderExpression countStar -- "count(*)" --countStar :: Expression relations ( 'Grouped bys) params ( 'NotNull 'PGint8) -- |
-- >>> :{
-- let
-- expression :: Expression '[tab ::: '["col" ::: nullity ty]] (Grouped bys) params ('NotNull 'PGint8)
-- expression = count #col
-- in renderExpression expression
-- :}
-- "count(col)"
--
count :: Expression relations 'Ungrouped params ty -> Expression relations ( 'Grouped bys) params ( 'NotNull 'PGint8)
-- |
-- >>> :{
-- let
-- expression :: Expression '[tab ::: '["col" ::: nullity ty]] (Grouped bys) params ('NotNull 'PGint8)
-- expression = countDistinct #col
-- in renderExpression expression
-- :}
-- "count(DISTINCT col)"
--
countDistinct :: Expression relations 'Ungrouped params ty -> Expression relations ( 'Grouped bys) params ( 'NotNull 'PGint8)
-- | synonym for boolAnd
--
--
-- >>> :{
-- let
-- expression :: Expression '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
-- expression = every #col
-- in renderExpression expression
-- :}
-- "every(col)"
--
every :: Expression relations 'Ungrouped params (nullity 'PGbool) -> Expression relations ( 'Grouped bys) params (nullity 'PGbool)
-- | synonym for boolAndDistinct
--
--
-- >>> :{
-- let
-- expression :: Expression '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
-- expression = everyDistinct #col
-- in renderExpression expression
-- :}
-- "every(DISTINCT col)"
--
everyDistinct :: Expression relations 'Ungrouped params (nullity 'PGbool) -> Expression relations ( 'Grouped bys) params (nullity 'PGbool)
-- | minimum and maximum aggregation
max_ :: Expression relations 'Ungrouped params (nullity ty) -> Expression relations ( 'Grouped bys) params (nullity ty)
-- | minimum and maximum aggregation
maxDistinct :: Expression relations 'Ungrouped params (nullity ty) -> Expression relations ( 'Grouped bys) params (nullity ty)
-- | minimum and maximum aggregation
min_ :: Expression relations 'Ungrouped params (nullity ty) -> Expression relations ( 'Grouped bys) params (nullity ty)
-- | minimum and maximum aggregation
minDistinct :: Expression relations 'Ungrouped params (nullity ty) -> Expression relations ( 'Grouped bys) params (nullity ty)
-- | A Table from a table expression is a way to call a table
-- reference by its alias.
newtype Table (schema :: TablesType) (columns :: RelationType)
UnsafeTable :: ByteString -> Table
[renderTable] :: Table -> ByteString
-- | TypeExpressions are used in casts and
-- createTable commands.
newtype TypeExpression (ty :: ColumnType)
UnsafeTypeExpression :: ByteString -> TypeExpression
[renderTypeExpression] :: TypeExpression -> ByteString
-- | pgtype is a demoted version of a PGType
class PGTyped (ty :: PGType)
pgtype :: PGTyped ty => TypeExpression ( 'NoDef :=> 'Null ty)
-- | logical Boolean (true/false)
bool :: TypeExpression ( 'NoDef :=> 'Null 'PGbool)
-- | signed two-byte integer
int2 :: TypeExpression ( 'NoDef :=> 'Null 'PGint2)
-- | signed two-byte integer
smallint :: TypeExpression ( 'NoDef :=> 'Null 'PGint2)
-- | signed four-byte integer
int4 :: TypeExpression ( 'NoDef :=> 'Null 'PGint4)
-- | signed four-byte integer
int :: TypeExpression ( 'NoDef :=> 'Null 'PGint4)
-- | signed four-byte integer
integer :: TypeExpression ( 'NoDef :=> 'Null 'PGint4)
-- | signed eight-byte integer
int8 :: TypeExpression ( 'NoDef :=> 'Null 'PGint8)
-- | signed eight-byte integer
bigint :: TypeExpression ( 'NoDef :=> 'Null 'PGint8)
-- | arbitrary precision numeric type
numeric :: TypeExpression ( 'NoDef :=> 'Null 'PGnumeric)
-- | single precision floating-point number (4 bytes)
float4 :: TypeExpression ( 'NoDef :=> 'Null 'PGfloat4)
-- | single precision floating-point number (4 bytes)
real :: TypeExpression ( 'NoDef :=> 'Null 'PGfloat4)
-- | double precision floating-point number (8 bytes)
float8 :: TypeExpression ( 'NoDef :=> 'Null 'PGfloat8)
-- | double precision floating-point number (8 bytes)
doublePrecision :: TypeExpression ( 'NoDef :=> 'Null 'PGfloat8)
-- | not a true type, but merely a notational convenience for creating
-- unique identifier columns with type `PGint2
serial2 :: TypeExpression ( 'Def :=> 'NotNull 'PGint2)
-- | not a true type, but merely a notational convenience for creating
-- unique identifier columns with type `PGint2
smallserial :: TypeExpression ( 'Def :=> 'NotNull 'PGint2)
-- | not a true type, but merely a notational convenience for creating
-- unique identifier columns with type `PGint4
serial4 :: TypeExpression ( 'Def :=> 'NotNull 'PGint4)
-- | not a true type, but merely a notational convenience for creating
-- unique identifier columns with type `PGint4
serial :: TypeExpression ( 'Def :=> 'NotNull 'PGint4)
-- | not a true type, but merely a notational convenience for creating
-- unique identifier columns with type `PGint8
serial8 :: TypeExpression ( 'Def :=> 'NotNull 'PGint8)
-- | not a true type, but merely a notational convenience for creating
-- unique identifier columns with type `PGint8
bigserial :: TypeExpression ( 'Def :=> 'NotNull 'PGint8)
-- | variable-length character string
text :: TypeExpression ( 'NoDef :=> 'Null 'PGtext)
-- | fixed-length character string
char :: (KnownNat n, 1 <= n) => proxy n -> TypeExpression ( 'NoDef :=> 'Null ( 'PGchar n))
-- | fixed-length character string
character :: (KnownNat n, 1 <= n) => proxy n -> TypeExpression ( 'NoDef :=> 'Null ( 'PGchar n))
-- | variable-length character string
varchar :: (KnownNat n, 1 <= n) => proxy n -> TypeExpression ( 'NoDef :=> 'Null ( 'PGvarchar n))
-- | variable-length character string
characterVarying :: (KnownNat n, 1 <= n) => proxy n -> TypeExpression ( 'NoDef :=> 'Null ( 'PGvarchar n))
-- | binary data ("byte array")
bytea :: TypeExpression ( 'NoDef :=> 'Null 'PGbytea)
-- | date and time (no time zone)
timestamp :: TypeExpression ( 'NoDef :=> 'Null 'PGtimestamp)
-- | date and time, including time zone
timestampWithTimeZone :: TypeExpression ( 'NoDef :=> 'Null 'PGtimestamptz)
-- | calendar date (year, month, day)
date :: TypeExpression ( 'NoDef :=> 'Null 'PGdate)
-- | time of day (no time zone)
time :: TypeExpression ( 'NoDef :=> 'Null 'PGtime)
-- | time of day, including time zone
timeWithTimeZone :: TypeExpression ( 'NoDef :=> 'Null 'PGtimetz)
-- | time span
interval :: TypeExpression ( 'NoDef :=> 'Null 'PGinterval)
-- | universally unique identifier
uuid :: TypeExpression ( 'NoDef :=> 'Null 'PGuuid)
-- | IPv4 or IPv6 host address
inet :: TypeExpression ( 'NoDef :=> 'Null 'PGinet)
-- | textual JSON data
json :: TypeExpression ( 'NoDef :=> 'Null 'PGjson)
-- | binary JSON data, decomposed
jsonb :: TypeExpression ( 'NoDef :=> 'Null 'PGjsonb)
-- | variable length array
vararray :: TypeExpression ( 'NoDef :=> 'Null pg) -> TypeExpression ( 'NoDef :=> 'Null ( 'PGvararray pg))
-- | fixed length array
--
-- -- >>> renderTypeExpression (fixarray (Proxy @2) json) -- "json[2]" --fixarray :: KnownNat n => proxy n -> TypeExpression ( 'NoDef :=> 'Null pg) -> TypeExpression ( 'NoDef :=> 'Null ( 'PGfixarray n pg)) -- | used in createTable commands as a column constraint to ensure -- NULL is not present notNull :: TypeExpression (def :=> 'Null ty) -> TypeExpression (def :=> 'NotNull ty) -- | used in createTable commands as a column constraint to give a -- default default_ :: Expression '[] 'Ungrouped '[] ty -> TypeExpression ( 'NoDef :=> ty) -> TypeExpression ( 'Def :=> ty) -- | & is a reverse application operator. This provides -- notational convenience. Its precedence is one higher than that of the -- forward application operator $, which allows & to be -- nested in $. (&) :: () => a -> (a -> b) -> b infixl 1 & -- | An n-ary product. -- -- The product is parameterized by a type constructor f and -- indexed by a type-level list xs. The length of the list -- determines the number of elements in the product, and if the -- i-th element of the list is of type x, then the -- i-th element of the product is of type f x. -- -- The constructor names are chosen to resemble the names of the list -- constructors. -- -- Two common instantiations of f are the identity functor -- I and the constant functor K. For I, the product -- becomes a heterogeneous list, where the type-level list describes the -- types of its components. For K a, the product becomes -- a homogeneous list, where the contents of the type-level list are -- ignored, but its length still specifies the number of elements. -- -- In the context of the SOP approach to generic programming, an n-ary -- product describes the structure of the arguments of a single data -- constructor. -- -- Examples: -- --
-- I 'x' :* I True :* Nil :: NP I '[ Char, Bool ] -- K 0 :* K 1 :* Nil :: NP (K Int) '[ Char, Bool ] -- Just 'x' :* Nothing :* Nil :: NP Maybe '[ Char, Bool ] --data NP k (a :: k -> *) (b :: [k]) :: forall k. () => (k -> *) -> [k] -> * [Nil] :: NP k a [] k [:*] :: NP k a (:) k x xs instance Control.DeepSeq.NFData (Squeal.PostgreSQL.Expression.TypeExpression ty) instance GHC.Classes.Ord (Squeal.PostgreSQL.Expression.TypeExpression ty) instance GHC.Classes.Eq (Squeal.PostgreSQL.Expression.TypeExpression ty) instance GHC.Show.Show (Squeal.PostgreSQL.Expression.TypeExpression ty) instance GHC.Generics.Generic (Squeal.PostgreSQL.Expression.TypeExpression ty) instance Control.DeepSeq.NFData (Squeal.PostgreSQL.Expression.Table schema columns) instance GHC.Classes.Ord (Squeal.PostgreSQL.Expression.Table schema columns) instance GHC.Classes.Eq (Squeal.PostgreSQL.Expression.Table schema columns) instance GHC.Show.Show (Squeal.PostgreSQL.Expression.Table schema columns) instance GHC.Generics.Generic (Squeal.PostgreSQL.Expression.Table schema columns) instance Control.DeepSeq.NFData (Squeal.PostgreSQL.Expression.Expression relations grouping params ty) instance GHC.Classes.Ord (Squeal.PostgreSQL.Expression.Expression relations grouping params ty) instance GHC.Classes.Eq (Squeal.PostgreSQL.Expression.Expression relations grouping params ty) instance GHC.Show.Show (Squeal.PostgreSQL.Expression.Expression relations grouping params ty) instance GHC.Generics.Generic (Squeal.PostgreSQL.Expression.Expression relations grouping params ty) instance Squeal.PostgreSQL.Expression.PGTyped (Squeal.PostgreSQL.Schema.PGTypeOf ty1) => Squeal.PostgreSQL.Expression.HasParameter 1 ((':) Squeal.PostgreSQL.Schema.NullityType ty1 tys) ty1 instance (GHC.TypeNats.KnownNat n, Squeal.PostgreSQL.Expression.HasParameter (n GHC.TypeNats.- 1) params ty) => Squeal.PostgreSQL.Expression.HasParameter n ((':) Squeal.PostgreSQL.Schema.NullityType ty' params) ty instance Squeal.PostgreSQL.Expression.PGTyped 'Squeal.PostgreSQL.Schema.PGbool instance Squeal.PostgreSQL.Expression.PGTyped 'Squeal.PostgreSQL.Schema.PGint2 instance Squeal.PostgreSQL.Expression.PGTyped 'Squeal.PostgreSQL.Schema.PGint4 instance Squeal.PostgreSQL.Expression.PGTyped 'Squeal.PostgreSQL.Schema.PGint8 instance Squeal.PostgreSQL.Expression.PGTyped 'Squeal.PostgreSQL.Schema.PGnumeric instance Squeal.PostgreSQL.Expression.PGTyped 'Squeal.PostgreSQL.Schema.PGfloat4 instance Squeal.PostgreSQL.Expression.PGTyped 'Squeal.PostgreSQL.Schema.PGfloat8 instance Squeal.PostgreSQL.Expression.PGTyped 'Squeal.PostgreSQL.Schema.PGtext instance (GHC.TypeNats.KnownNat n, 1 GHC.TypeNats.<= n) => Squeal.PostgreSQL.Expression.PGTyped ('Squeal.PostgreSQL.Schema.PGchar n) instance (GHC.TypeNats.KnownNat n, 1 GHC.TypeNats.<= n) => Squeal.PostgreSQL.Expression.PGTyped ('Squeal.PostgreSQL.Schema.PGvarchar n) instance Squeal.PostgreSQL.Expression.PGTyped 'Squeal.PostgreSQL.Schema.PGbytea instance Squeal.PostgreSQL.Expression.PGTyped 'Squeal.PostgreSQL.Schema.PGtimestamp instance Squeal.PostgreSQL.Expression.PGTyped 'Squeal.PostgreSQL.Schema.PGtimestamptz instance Squeal.PostgreSQL.Expression.PGTyped 'Squeal.PostgreSQL.Schema.PGdate instance Squeal.PostgreSQL.Expression.PGTyped 'Squeal.PostgreSQL.Schema.PGtime instance Squeal.PostgreSQL.Expression.PGTyped 'Squeal.PostgreSQL.Schema.PGtimetz instance Squeal.PostgreSQL.Expression.PGTyped 'Squeal.PostgreSQL.Schema.PGinterval instance Squeal.PostgreSQL.Expression.PGTyped 'Squeal.PostgreSQL.Schema.PGuuid instance Squeal.PostgreSQL.Expression.PGTyped 'Squeal.PostgreSQL.Schema.PGjson instance Squeal.PostgreSQL.Expression.PGTyped 'Squeal.PostgreSQL.Schema.PGjsonb instance Squeal.PostgreSQL.Expression.PGTyped ty => Squeal.PostgreSQL.Expression.PGTyped ('Squeal.PostgreSQL.Schema.PGvararray ty) instance (GHC.TypeNats.KnownNat n, Squeal.PostgreSQL.Expression.PGTyped ty) => Squeal.PostgreSQL.Expression.PGTyped ('Squeal.PostgreSQL.Schema.PGfixarray n ty) instance (Squeal.PostgreSQL.Schema.Has Squeal.PostgreSQL.Schema.TableType alias schema table, relation ~ Squeal.PostgreSQL.Schema.ColumnsToRelation (Squeal.PostgreSQL.Schema.TableToColumns table)) => GHC.OverloadedLabels.IsLabel alias (Squeal.PostgreSQL.Expression.Table schema relation) instance Squeal.PostgreSQL.Expression.PGAvg Squeal.PostgreSQL.Schema.PGType 'Squeal.PostgreSQL.Schema.PGint2 'Squeal.PostgreSQL.Schema.PGnumeric instance Squeal.PostgreSQL.Expression.PGAvg Squeal.PostgreSQL.Schema.PGType 'Squeal.PostgreSQL.Schema.PGint4 'Squeal.PostgreSQL.Schema.PGnumeric instance Squeal.PostgreSQL.Expression.PGAvg Squeal.PostgreSQL.Schema.PGType 'Squeal.PostgreSQL.Schema.PGint8 'Squeal.PostgreSQL.Schema.PGnumeric instance Squeal.PostgreSQL.Expression.PGAvg Squeal.PostgreSQL.Schema.PGType 'Squeal.PostgreSQL.Schema.PGnumeric 'Squeal.PostgreSQL.Schema.PGnumeric instance Squeal.PostgreSQL.Expression.PGAvg Squeal.PostgreSQL.Schema.PGType 'Squeal.PostgreSQL.Schema.PGfloat4 'Squeal.PostgreSQL.Schema.PGfloat8 instance Squeal.PostgreSQL.Expression.PGAvg Squeal.PostgreSQL.Schema.PGType 'Squeal.PostgreSQL.Schema.PGfloat8 'Squeal.PostgreSQL.Schema.PGfloat8 instance Squeal.PostgreSQL.Expression.PGAvg Squeal.PostgreSQL.Schema.PGType 'Squeal.PostgreSQL.Schema.PGinterval 'Squeal.PostgreSQL.Schema.PGinterval instance (Squeal.PostgreSQL.Schema.HasUnique [(GHC.Types.Symbol, Squeal.PostgreSQL.Schema.NullityType)] relation relations columns, Squeal.PostgreSQL.Schema.Has Squeal.PostgreSQL.Schema.NullityType column columns ty) => GHC.OverloadedLabels.IsLabel column (Squeal.PostgreSQL.Expression.Expression relations 'Squeal.PostgreSQL.Schema.Ungrouped params ty) instance (Squeal.PostgreSQL.Schema.Has [(GHC.Types.Symbol, Squeal.PostgreSQL.Schema.NullityType)] relation relations columns, Squeal.PostgreSQL.Schema.Has Squeal.PostgreSQL.Schema.NullityType column columns ty) => Squeal.PostgreSQL.Schema.IsQualified relation column (Squeal.PostgreSQL.Expression.Expression relations 'Squeal.PostgreSQL.Schema.Ungrouped params ty) instance (Squeal.PostgreSQL.Schema.HasUnique [(GHC.Types.Symbol, Squeal.PostgreSQL.Schema.NullityType)] relation relations columns, Squeal.PostgreSQL.Schema.Has Squeal.PostgreSQL.Schema.NullityType column columns ty, Squeal.PostgreSQL.Schema.GroupedBy [(GHC.Types.Symbol, GHC.Types.Symbol)] relation column bys) => GHC.OverloadedLabels.IsLabel column (Squeal.PostgreSQL.Expression.Expression relations ('Squeal.PostgreSQL.Schema.Grouped bys) params ty) instance (Squeal.PostgreSQL.Schema.Has [(GHC.Types.Symbol, Squeal.PostgreSQL.Schema.NullityType)] relation relations columns, Squeal.PostgreSQL.Schema.Has Squeal.PostgreSQL.Schema.NullityType column columns ty, Squeal.PostgreSQL.Schema.GroupedBy [(GHC.Types.Symbol, GHC.Types.Symbol)] relation column bys) => Squeal.PostgreSQL.Schema.IsQualified relation column (Squeal.PostgreSQL.Expression.Expression relations ('Squeal.PostgreSQL.Schema.Grouped bys) params ty) instance GHC.Base.Monoid (Squeal.PostgreSQL.Expression.Expression relations grouping params (nullity ('Squeal.PostgreSQL.Schema.PGvararray ty))) instance Squeal.PostgreSQL.Schema.PGNum ty => GHC.Num.Num (Squeal.PostgreSQL.Expression.Expression relations grouping params (nullity ty)) instance (Squeal.PostgreSQL.Schema.PGNum ty, Squeal.PostgreSQL.Schema.PGFloating ty) => GHC.Real.Fractional (Squeal.PostgreSQL.Expression.Expression relations grouping params (nullity ty)) instance (Squeal.PostgreSQL.Schema.PGNum ty, Squeal.PostgreSQL.Schema.PGFloating ty) => GHC.Float.Floating (Squeal.PostgreSQL.Expression.Expression relations grouping params (nullity ty)) instance Data.String.IsString (Squeal.PostgreSQL.Expression.Expression relations grouping params (nullity 'Squeal.PostgreSQL.Schema.PGtext)) instance GHC.Base.Monoid (Squeal.PostgreSQL.Expression.Expression relations grouping params (nullity 'Squeal.PostgreSQL.Schema.PGtext)) -- | Squeal queries. module Squeal.PostgreSQL.Query -- | The process of retrieving or the command to retrieve data from a -- database is called a Query. The select, -- selectStar, selectDotStar, selectDistinct, -- selectDistinctStar and selectDistinctDotStar commands -- are used to specify queries. -- -- simple query: -- --
-- >>> :{
-- let
-- query :: Query
-- '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4]]
-- '[]
-- '["col" ::: 'Null 'PGint4]
-- query = selectStar (from (table (#tab `As` #t)))
-- in renderQuery query
-- :}
-- "SELECT * FROM tab AS t"
--
--
-- restricted query:
--
--
-- >>> :{
-- let
-- query :: Query
-- '[ "tab" ::: '[] :=>
-- '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "col2" ::: 'NoDef :=> 'NotNull 'PGint4 ]]
-- '[]
-- '[ "sum" ::: 'NotNull 'PGint4
-- , "col1" ::: 'NotNull 'PGint4 ]
-- query =
-- select
-- ((#col1 + #col2) `As` #sum :* #col1 `As` #col1 :* Nil)
-- ( from (table (#tab `As` #t))
-- & where_ (#col1 .> #col2)
-- & where_ (#col2 .> 0) )
-- in renderQuery query
-- :}
-- "SELECT (col1 + col2) AS sum, col1 AS col1 FROM tab AS t WHERE ((col1 > col2) AND (col2 > 0))"
--
--
-- subquery:
--
--
-- >>> :{
-- let
-- query :: Query
-- '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4]]
-- '[]
-- '["col" ::: 'Null 'PGint4]
-- query =
-- selectStar
-- (from (subquery (selectStar (from (table (#tab `As` #t))) `As` #sub)))
-- in renderQuery query
-- :}
-- "SELECT * FROM (SELECT * FROM tab AS t) AS sub"
--
--
-- limits and offsets:
--
--
-- >>> :{
-- let
-- query :: Query
-- '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4]]
-- '[]
-- '["col" ::: 'Null 'PGint4]
-- query = selectStar
-- (from (table (#tab `As` #t)) & limit 100 & offset 2 & limit 50 & offset 2)
-- in renderQuery query
-- :}
-- "SELECT * FROM tab AS t LIMIT 50 OFFSET 4"
--
--
-- parameterized query:
--
--
-- >>> :{
-- let
-- query :: Query
-- '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGfloat8]]
-- '[ 'NotNull 'PGfloat8]
-- '["col" ::: 'NotNull 'PGfloat8]
-- query = selectStar
-- (from (table (#tab `As` #t)) & where_ (#col .> param @1))
-- in renderQuery query
-- :}
-- "SELECT * FROM tab AS t WHERE (col > ($1 :: float8))"
--
--
-- aggregation query:
--
--
-- >>> :{
-- let
-- query :: Query
-- '[ "tab" ::: '[] :=>
-- '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "col2" ::: 'NoDef :=> 'NotNull 'PGint4 ]]
-- '[]
-- '[ "sum" ::: 'NotNull 'PGint4
-- , "col1" ::: 'NotNull 'PGint4 ]
-- query =
-- select (sum_ #col2 `As` #sum :* #col1 `As` #col1 :* Nil)
-- ( from (table (#tab `As` #table1))
-- & group (By #col1 :* Nil)
-- & having (#col1 + sum_ #col2 .> 1) )
-- in renderQuery query
-- :}
-- "SELECT sum(col2) AS sum, col1 AS col1 FROM tab AS table1 GROUP BY col1 HAVING ((col1 + sum(col2)) > 1)"
--
--
-- sorted query:
--
--
-- >>> :{
-- let
-- query :: Query
-- '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4]]
-- '[]
-- '["col" ::: 'Null 'PGint4]
-- query = selectStar
-- (from (table (#tab `As` #t)) & orderBy [#col & AscNullsFirst])
-- in renderQuery query
-- :}
-- "SELECT * FROM tab AS t ORDER BY col ASC NULLS FIRST"
--
--
-- joins:
--
--
-- >>> :set -XFlexibleContexts
--
-- >>> :{
-- let
-- query :: Query
-- '[ "orders" :::
-- '["pk_orders" ::: PrimaryKey '["id"]
-- ,"fk_customers" ::: ForeignKey '["customer_id"] "customers" '["id"]
-- ,"fk_shippers" ::: ForeignKey '["shipper_id"] "shippers" '["id"]] :=>
-- '[ "id" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "price" ::: 'NoDef :=> 'NotNull 'PGfloat4
-- , "customer_id" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "shipper_id" ::: 'NoDef :=> 'NotNull 'PGint4
-- ]
-- , "customers" :::
-- '["pk_customers" ::: PrimaryKey '["id"]] :=>
-- '[ "id" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "name" ::: 'NoDef :=> 'NotNull 'PGtext
-- ]
-- , "shippers" :::
-- '["pk_shippers" ::: PrimaryKey '["id"]] :=>
-- '[ "id" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "name" ::: 'NoDef :=> 'NotNull 'PGtext
-- ]
-- ]
-- '[]
-- '[ "order_price" ::: 'NotNull 'PGfloat4
-- , "customer_name" ::: 'NotNull 'PGtext
-- , "shipper_name" ::: 'NotNull 'PGtext
-- ]
-- query = select
-- ( #o ! #price `As` #order_price :*
-- #c ! #name `As` #customer_name :*
-- #s ! #name `As` #shipper_name :* Nil )
-- ( from (table (#orders `As` #o)
-- & innerJoin (table (#customers `As` #c))
-- (#o ! #customer_id .== #c ! #id)
-- & innerJoin (table (#shippers `As` #s))
-- (#o ! #shipper_id .== #s ! #id)) )
-- in renderQuery query
-- :}
-- "SELECT o.price AS order_price, c.name AS customer_name, s.name AS shipper_name FROM orders AS o INNER JOIN customers AS c ON (o.customer_id = c.id) INNER JOIN shippers AS s ON (o.shipper_id = s.id)"
--
--
-- self-join:
--
--
-- >>> :{
-- let
-- query :: Query
-- '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4]]
-- '[]
-- '["col" ::: 'Null 'PGint4]
-- query = selectDotStar #t1
-- (from (table (#tab `As` #t1) & crossJoin (table (#tab `As` #t2))))
-- in renderQuery query
-- :}
-- "SELECT t1.* FROM tab AS t1 CROSS JOIN tab AS t2"
--
--
-- set operations:
--
--
-- >>> :{
-- let
-- query :: Query
-- '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4]]
-- '[]
-- '["col" ::: 'Null 'PGint4]
-- query =
-- selectStar (from (table (#tab `As` #t)))
-- `unionAll`
-- selectStar (from (table (#tab `As` #t)))
-- in renderQuery query
-- :}
-- "(SELECT * FROM tab AS t) UNION ALL (SELECT * FROM tab AS t)"
--
newtype Query (schema :: TablesType) (params :: [NullityType]) (columns :: RelationType)
UnsafeQuery :: ByteString -> Query
[renderQuery] :: Query -> ByteString
-- | The results of two queries can be combined using the set operation
-- union. Duplicate rows are eliminated.
union :: Query schema params columns -> Query schema params columns -> Query schema params columns
-- | The results of two queries can be combined using the set operation
-- unionAll, the disjoint union. Duplicate rows are retained.
unionAll :: Query schema params columns -> Query schema params columns -> Query schema params columns
-- | The results of two queries can be combined using the set operation
-- intersect, the intersection. Duplicate rows are eliminated.
intersect :: Query schema params columns -> Query schema params columns -> Query schema params columns
-- | The results of two queries can be combined using the set operation
-- intersectAll, the intersection. Duplicate rows are retained.
intersectAll :: Query schema params columns -> Query schema params columns -> Query schema params columns
-- | The results of two queries can be combined using the set operation
-- except, the set difference. Duplicate rows are eliminated.
except :: Query schema params columns -> Query schema params columns -> Query schema params columns
-- | The results of two queries can be combined using the set operation
-- exceptAll, the set difference. Duplicate rows are retained.
exceptAll :: Query schema params columns -> Query schema params columns -> Query schema params columns
-- | the TableExpression in the select command constructs an
-- intermediate virtual table by possibly combining tables, views,
-- eliminating rows, grouping, etc. This table is finally passed on to
-- processing by the select list. The select list determines which
-- columns of the intermediate table are actually output.
select :: SListI columns => NP (Aliased (Expression relations grouping params)) (column : columns) -> TableExpression schema params relations grouping -> Query schema params (column : columns)
-- | After the select list has been processed, the result table can be
-- subject to the elimination of duplicate rows using
-- selectDistinct.
selectDistinct :: SListI columns => NP (Aliased (Expression relations 'Ungrouped params)) (column : columns) -> TableExpression schema params relations 'Ungrouped -> Query schema params (column : columns)
-- | The simplest kind of query is selectStar which emits all
-- columns that the table expression produces.
selectStar :: HasUnique relation relations columns => TableExpression schema params relations 'Ungrouped -> Query schema params columns
-- | A selectDistinctStar emits all columns that the table
-- expression produces and eliminates duplicate rows.
selectDistinctStar :: HasUnique relation relations columns => TableExpression schema params relations 'Ungrouped -> Query schema params columns
-- | When working with multiple tables, it can also be useful to ask for
-- all the columns of a particular table, using selectDotStar.
selectDotStar :: Has relation relations columns => Alias relation -> TableExpression schema params relations 'Ungrouped -> Query schema params columns
-- | A selectDistinctDotStar asks for all the columns of a
-- particular table, and eliminates duplicate rows.
selectDistinctDotStar :: Has relation relations columns => Alias relation -> TableExpression schema params relations 'Ungrouped -> Query schema params columns
-- | A TableExpression computes a table. The table expression
-- contains a fromClause that is optionally followed by a
-- whereClause, groupByClause, havingClause,
-- orderByClause, limitClause and offsetClauses.
-- Trivial table expressions simply refer to a table on disk, a so-called
-- base table, but more complex expressions can be used to modify or
-- combine base tables in various ways.
data TableExpression (schema :: TablesType) (params :: [NullityType]) (relations :: RelationsType) (grouping :: Grouping)
TableExpression :: FromClause schema params relations -> [Condition relations 'Ungrouped params] -> GroupByClause relations grouping -> HavingClause relations grouping params -> [SortExpression relations grouping params] -> [Word64] -> [Word64] -> TableExpression
-- | A table reference that can be a table name, or a derived table such as
-- a subquery, a JOIN construct, or complex combinations of
-- these.
[fromClause] :: TableExpression -> FromClause schema params relations
-- | optional search coditions, combined with .&&. After the
-- processing of the fromClause is done, each row of the derived
-- virtual table is checked against the search condition. If the result
-- of the condition is true, the row is kept in the output table,
-- otherwise it is discarded. The search condition typically references
-- at least one column of the table generated in the fromClause;
-- this is not required, but otherwise the WHERE clause will be fairly
-- useless.
[whereClause] :: TableExpression -> [Condition relations 'Ungrouped params]
-- | The groupByClause is used to group together those rows in a
-- table that have the same values in all the columns listed. The order
-- in which the columns are listed does not matter. The effect is to
-- combine each set of rows having common values into one group row that
-- represents all rows in the group. This is done to eliminate redundancy
-- in the output and/or compute aggregates that apply to these groups.
[groupByClause] :: TableExpression -> GroupByClause relations grouping
-- | If a table has been grouped using groupBy, but only certain
-- groups are of interest, the havingClause can be used, much like
-- a whereClause, to eliminate groups from the result. Expressions
-- in the havingClause can refer both to grouped expressions and
-- to ungrouped expressions (which necessarily involve an aggregate
-- function).
[havingClause] :: TableExpression -> HavingClause relations grouping params
-- | The orderByClause is for optional sorting. When more than one
-- SortExpression is specified, the later (right) values are used
-- to sort rows that are equal according to the earlier (left) values.
[orderByClause] :: TableExpression -> [SortExpression relations grouping params]
-- | The limitClause is combined with min to give a limit
-- count if nonempty. If a limit count is given, no more than that many
-- rows will be returned (but possibly fewer, if the query itself yields
-- fewer rows).
[limitClause] :: TableExpression -> [Word64]
-- | The offsetClause is combined with + to give an offset
-- count if nonempty. The offset count says to skip that many rows before
-- beginning to return rows. The rows are skipped before the limit count
-- is applied.
[offsetClause] :: TableExpression -> [Word64]
-- | Render a TableExpression
renderTableExpression :: TableExpression schema params relations grouping -> ByteString
-- | A from generates a TableExpression from a table
-- reference that can be a table name, or a derived table such as a
-- subquery, a JOIN construct, or complex combinations of these. A
-- from may be transformed by where_, group,
-- having, orderBy, limit and offset, using
-- the & operator to match the left-to-right sequencing of
-- their placement in SQL.
from :: FromClause schema params relations -> TableExpression schema params relations 'Ungrouped
-- | A where_ is an endomorphism of TableExpressions which
-- adds a search condition to the whereClause.
where_ :: Condition relations 'Ungrouped params -> TableExpression schema params relations grouping -> TableExpression schema params relations grouping
-- | A group is a transformation of TableExpressions which
-- switches its Grouping from Ungrouped to Grouped.
-- Use group Nil to perform a "grand total" aggregation query.
group :: SListI bys => NP (By relations) bys -> TableExpression schema params relations 'Ungrouped -> TableExpression schema params relations ( 'Grouped bys)
-- | A having is an endomorphism of TableExpressions which
-- adds a search condition to the havingClause.
having :: Condition relations ( 'Grouped bys) params -> TableExpression schema params relations ( 'Grouped bys) -> TableExpression schema params relations ( 'Grouped bys)
-- | An orderBy is an endomorphism of TableExpressions which
-- appends an ordering to the right of the orderByClause.
orderBy :: [SortExpression relations grouping params] -> TableExpression schema params relations grouping -> TableExpression schema params relations grouping
-- | A limit is an endomorphism of TableExpressions which
-- adds to the limitClause.
limit :: Word64 -> TableExpression schema params relations grouping -> TableExpression schema params relations grouping
-- | An offset is an endomorphism of TableExpressions which
-- adds to the offsetClause.
offset :: Word64 -> TableExpression schema params relations grouping -> TableExpression schema params relations grouping
-- | A FromClause can be a table name, or a derived table such as a
-- subquery, a JOIN construct, or complex combinations of these.
newtype FromClause schema params relations
UnsafeFromClause :: ByteString -> FromClause schema params relations
[renderFromClause] :: FromClause schema params relations -> ByteString
-- | A real table is a table from the schema.
table :: Aliased (Table schema) table -> FromClause schema params '[table]
-- | subquery derives a table from a Query.
subquery :: Aliased (Query schema params) table -> FromClause schema params '[table]
-- | left & crossJoin right. For every possible combination of
-- rows from left and right (i.e., a Cartesian
-- product), the joined table will contain a row consisting of all
-- columns in left followed by all columns in right. If
-- the tables have n and m rows respectively, the
-- joined table will have n * m rows.
crossJoin :: FromClause schema params right -> FromClause schema params left -> FromClause schema params (Join left right)
-- | left & innerJoin right on. The joined table is filtered
-- by the on condition.
innerJoin :: FromClause schema params right -> Condition (Join left right) 'Ungrouped params -> FromClause schema params left -> FromClause schema params (Join left right)
-- | left & leftOuterJoin right on. First, an inner join is
-- performed. Then, for each row in left that does not satisfy
-- the on condition with any row in right, a joined row
-- is added with null values in columns of right. Thus, the
-- joined table always has at least one row for each row in
-- left.
leftOuterJoin :: FromClause schema params right -> Condition (Join left right) 'Ungrouped params -> FromClause schema params left -> FromClause schema params (Join left (NullifyRelations right))
-- | left & rightOuterJoin right on. First, an inner join is
-- performed. Then, for each row in right that does not satisfy
-- the on condition with any row in left, a joined row
-- is added with null values in columns of left. This is the
-- converse of a left join: the result table will always have a row for
-- each row in right.
rightOuterJoin :: FromClause schema params right -> Condition (Join left right) 'Ungrouped params -> FromClause schema params left -> FromClause schema params (Join (NullifyRelations left) right)
-- | left & fullOuterJoin right on. First, an inner join is
-- performed. Then, for each row in left that does not satisfy
-- the on condition with any row in right, a joined row
-- is added with null values in columns of right. Also, for each
-- row of right that does not satisfy the join condition with
-- any row in left, a joined row with null values in the columns
-- of left is added.
fullOuterJoin :: FromClause schema params right -> Condition (Join left right) 'Ungrouped params -> FromClause schema params left -> FromClause schema params (Join (NullifyRelations left) (NullifyRelations right))
-- | Bys are used in group to reference a list of columns
-- which are then used to group together those rows in a table that have
-- the same values in all the columns listed. By #col will
-- reference an unambiguous column col; otherwise By2 (#tab
-- ! #col) will reference a table qualified column tab.col.
data By (relations :: RelationsType) (by :: (Symbol, Symbol))
[By] :: (HasUnique relation relations columns, Has column columns ty) => Alias column -> By relations '(relation, column)
[By2] :: (Has relation relations columns, Has column columns ty) => (Alias relation, Alias column) -> By relations '(relation, column)
-- | Renders a By.
renderBy :: By relations by -> ByteString
-- | A GroupByClause indicates the Grouping of a
-- TableExpression. A NoGroups indicates Ungrouped
-- while a Group indicates Grouped. NoGroups is
-- distinguised from Group Nil since no aggregation can be done
-- on NoGroups while all output Expressions must be
-- aggregated in Group Nil. In general, all output
-- Expressions in the complement of bys must be
-- aggregated in Group bys.
data GroupByClause relations grouping
[NoGroups] :: GroupByClause relations 'Ungrouped
[Group] :: SListI bys => NP (By relations) bys -> GroupByClause relations ( 'Grouped bys)
-- | Renders a GroupByClause.
renderGroupByClause :: GroupByClause relations grouping -> ByteString
-- | A HavingClause is used to eliminate groups that are not of
-- interest. An Ungrouped TableExpression may only use
-- NoHaving while a Grouped TableExpression must use
-- Having whose conditions are combined with .&&.
data HavingClause relations grouping params
[NoHaving] :: HavingClause relations 'Ungrouped params
[Having] :: [Condition relations ( 'Grouped bys) params] -> HavingClause relations ( 'Grouped bys) params
-- | Render a HavingClause.
renderHavingClause :: HavingClause relations grouping params -> ByteString
-- | SortExpressions are used by sortBy to optionally sort
-- the results of a Query. Asc or Desc set the sort
-- direction of a NotNull result column to ascending or
-- descending. Ascending order puts smaller values first, where "smaller"
-- is defined in terms of the .< operator. Similarly,
-- descending order is determined with the .> operator.
-- AscNullsFirst, AscNullsLast, DescNullsFirst and
-- DescNullsLast options are used to determine whether nulls
-- appear before or after non-null values in the sort ordering of a
-- Null result column.
data SortExpression relations grouping params
[Asc] :: Expression relations grouping params ( 'NotNull ty) -> SortExpression relations grouping params
[Desc] :: Expression relations grouping params ( 'NotNull ty) -> SortExpression relations grouping params
[AscNullsFirst] :: Expression relations grouping params ( 'Null ty) -> SortExpression relations grouping params
[AscNullsLast] :: Expression relations grouping params ( 'Null ty) -> SortExpression relations grouping params
[DescNullsFirst] :: Expression relations grouping params ( 'Null ty) -> SortExpression relations grouping params
[DescNullsLast] :: Expression relations grouping params ( 'Null ty) -> SortExpression relations grouping params
-- | Render a SortExpression.
renderSortExpression :: SortExpression relations grouping params -> ByteString
instance forall k1 (schema :: k1) k2 (params :: k2) k3 (relations :: k3). Control.DeepSeq.NFData (Squeal.PostgreSQL.Query.FromClause k1 k2 k3 schema params relations)
instance forall k1 (schema :: k1) k2 (params :: k2) k3 (relations :: k3). GHC.Classes.Ord (Squeal.PostgreSQL.Query.FromClause k1 k2 k3 schema params relations)
instance forall k1 (schema :: k1) k2 (params :: k2) k3 (relations :: k3). GHC.Classes.Eq (Squeal.PostgreSQL.Query.FromClause k1 k2 k3 schema params relations)
instance forall k1 (schema :: k1) k2 (params :: k2) k3 (relations :: k3). GHC.Show.Show (Squeal.PostgreSQL.Query.FromClause k1 k2 k3 schema params relations)
instance forall k1 (schema :: k1) k2 (params :: k2) k3 (relations :: k3). GHC.Generics.Generic (Squeal.PostgreSQL.Query.FromClause k1 k2 k3 schema params relations)
instance Control.DeepSeq.NFData (Squeal.PostgreSQL.Query.Query schema params columns)
instance GHC.Classes.Ord (Squeal.PostgreSQL.Query.Query schema params columns)
instance GHC.Classes.Eq (Squeal.PostgreSQL.Query.Query schema params columns)
instance GHC.Show.Show (Squeal.PostgreSQL.Query.Query schema params columns)
instance GHC.Generics.Generic (Squeal.PostgreSQL.Query.Query schema params columns)
instance GHC.Show.Show (Squeal.PostgreSQL.Query.By relations by)
instance GHC.Classes.Eq (Squeal.PostgreSQL.Query.By relations by)
instance GHC.Classes.Ord (Squeal.PostgreSQL.Query.By relations by)
instance GHC.Show.Show (Squeal.PostgreSQL.Query.HavingClause relations grouping params)
instance GHC.Classes.Eq (Squeal.PostgreSQL.Query.HavingClause relations grouping params)
instance GHC.Classes.Ord (Squeal.PostgreSQL.Query.HavingClause relations grouping params)
instance GHC.Show.Show (Squeal.PostgreSQL.Query.SortExpression relations grouping params)
-- | Squeal data manipulation language.
module Squeal.PostgreSQL.Manipulation
-- | A Manipulation is a statement which may modify data in the
-- database, but does not alter the schema. Examples are inserts, updates
-- and deletes. A Query is also considered a Manipulation
-- even though it does not modify data.
--
-- simple insert:
--
--
-- >>> :{
-- let
-- manipulation :: Manipulation
-- '[ "tab" ::: '[] :=>
-- '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "col2" ::: 'Def :=> 'NotNull 'PGint4 ]] '[] '[]
-- manipulation =
-- insertRow_ #tab (Set 2 `As` #col1 :* Default `As` #col2 :* Nil)
-- in renderManipulation manipulation
-- :}
-- "INSERT INTO tab (col1, col2) VALUES (2, DEFAULT);"
--
--
-- parameterized insert:
--
--
-- >>> :{
-- let
-- manipulation :: Manipulation
-- '[ "tab" ::: '[] :=>
-- '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "col2" ::: 'NoDef :=> 'NotNull 'PGint4 ]]
-- '[ 'NotNull 'PGint4, 'NotNull 'PGint4 ] '[]
-- manipulation =
-- insertRow_ #tab
-- (Set (param @1) `As` #col1 :* Set (param @2) `As` #col2 :* Nil)
-- in renderManipulation manipulation
-- :}
-- "INSERT INTO tab (col1, col2) VALUES (($1 :: int4), ($2 :: int4));"
--
--
-- returning insert:
--
--
-- >>> :{
-- let
-- manipulation :: Manipulation
-- '[ "tab" ::: '[] :=>
-- '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "col2" ::: 'Def :=> 'NotNull 'PGint4 ]] '[]
-- '["fromOnly" ::: 'NotNull 'PGint4]
-- manipulation =
-- insertRow #tab (Set 2 `As` #col1 :* Default `As` #col2 :* Nil)
-- OnConflictDoRaise (Returning (#col1 `As` #fromOnly :* Nil))
-- in renderManipulation manipulation
-- :}
-- "INSERT INTO tab (col1, col2) VALUES (2, DEFAULT) RETURNING col1 AS fromOnly;"
--
--
-- upsert:
--
--
-- >>> :{
-- let
-- manipulation :: Manipulation
-- '[ "tab" ::: '[] :=>
-- '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "col2" ::: 'NoDef :=> 'NotNull 'PGint4 ]]
-- '[] '[ "sum" ::: 'NotNull 'PGint4]
-- manipulation =
-- insertRows #tab
-- (Set 2 `As` #col1 :* Set 4 `As` #col2 :* Nil)
-- [Set 6 `As` #col1 :* Set 8 `As` #col2 :* Nil]
-- (OnConflictDoUpdate
-- (Set 2 `As` #col1 :* Same `As` #col2 :* Nil)
-- [#col1 .== #col2])
-- (Returning $ (#col1 + #col2) `As` #sum :* Nil)
-- in renderManipulation manipulation
-- :}
-- "INSERT INTO tab (col1, col2) VALUES (2, 4), (6, 8) ON CONFLICT DO UPDATE SET col1 = 2 WHERE (col1 = col2) RETURNING (col1 + col2) AS sum;"
--
--
-- query insert:
--
--
-- >>> :{
-- let
-- manipulation :: Manipulation
-- '[ "tab" ::: '[] :=>
-- '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "col2" ::: 'NoDef :=> 'NotNull 'PGint4
-- ]
-- , "other_tab" ::: '[] :=>
-- '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "col2" ::: 'NoDef :=> 'NotNull 'PGint4
-- ]
-- ] '[] '[]
-- manipulation =
-- insertQuery_ #tab
-- (selectStar (from (table (#other_tab `As` #t))))
-- in renderManipulation manipulation
-- :}
-- "INSERT INTO tab SELECT * FROM other_tab AS t;"
--
--
-- update:
--
--
-- >>> :{
-- let
-- manipulation :: Manipulation
-- '[ "tab" ::: '[] :=>
-- '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "col2" ::: 'NoDef :=> 'NotNull 'PGint4 ]] '[] '[]
-- manipulation =
-- update_ #tab (Set 2 `As` #col1 :* Same `As` #col2 :* Nil)
-- (#col1 ./= #col2)
-- in renderManipulation manipulation
-- :}
-- "UPDATE tab SET col1 = 2 WHERE (col1 <> col2);"
--
--
-- delete:
--
--
-- >>> :{
-- let
-- manipulation :: Manipulation
-- '[ "tab" ::: '[] :=>
-- '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "col2" ::: 'NoDef :=> 'NotNull 'PGint4 ]] '[]
-- '[ "col1" ::: 'NotNull 'PGint4
-- , "col2" ::: 'NotNull 'PGint4 ]
-- manipulation = deleteFrom #tab (#col1 .== #col2) ReturningStar
-- in renderManipulation manipulation
-- :}
-- "DELETE FROM tab WHERE (col1 = col2) RETURNING *;"
--
newtype Manipulation (schema :: TablesType) (params :: [NullityType]) (columns :: RelationType)
UnsafeManipulation :: ByteString -> Manipulation
[renderManipulation] :: Manipulation -> ByteString
-- | Convert a Query into a Manipulation.
queryStatement :: Query schema params columns -> Manipulation schema params columns
-- | ColumnValues are values to insert or update in a row
-- Same updates with the same value. Default inserts or
-- updates with the DEFAULT value Set a value to be an
-- Expression, relative to the given row for an update, and closed
-- for an insert.
data ColumnValue (columns :: RelationType) (params :: [NullityType]) (ty :: ColumnType)
[Same] :: ColumnValue (column : columns) params ty
[Default] :: ColumnValue columns params ( 'Def :=> ty)
[Set] :: (forall table. Expression '[table ::: columns] 'Ungrouped params ty) -> ColumnValue columns params (constraint :=> ty)
-- | A ReturningClause computes and return value(s) based on each
-- row actually inserted, updated or deleted. This is primarily useful
-- for obtaining values that were supplied by defaults, such as a serial
-- sequence number. However, any expression using the table's columns is
-- allowed. Only rows that were successfully inserted or updated or
-- deleted will be returned. For example, if a row was locked but not
-- updated because an OnConflictDoUpdate condition was not
-- satisfied, the row will not be returned. ReturningStar will
-- return all columns in the row. Use Returning Nil in the
-- common case where no return values are desired.
data ReturningClause (columns :: ColumnsType) (params :: [NullityType]) (results :: RelationType)
[ReturningStar] :: results ~ ColumnsToRelation columns => ReturningClause columns params results
[Returning] :: rel ~ ColumnsToRelation columns => NP (Aliased (Expression '[table ::: rel] 'Ungrouped params)) results -> ReturningClause columns params results
-- | A ConflictClause specifies an action to perform upon a
-- constraint violation. OnConflictDoRaise will raise an error.
-- OnConflictDoNothing simply avoids inserting a row.
-- OnConflictDoUpdate updates the existing row that conflicts with
-- the row proposed for insertion.
data ConflictClause (columns :: ColumnsType) params
[OnConflictDoRaise] :: ConflictClause columns params
[OnConflictDoNothing] :: ConflictClause columns params
[OnConflictDoUpdate] :: NP (Aliased (ColumnValue (ColumnsToRelation columns) params)) columns -> [Condition '[table ::: ColumnsToRelation columns] 'Ungrouped params] -> ConflictClause columns params
-- | Insert multiple rows.
--
-- When a table is created, it contains no data. The first thing to do
-- before a database can be of much use is to insert data. Data is
-- conceptually inserted one row at a time. Of course you can also insert
-- more than one row, but there is no way to insert less than one row.
-- Even if you know only some column values, a complete row must be
-- created.
insertRows :: (SListI columns, SListI results, Has tab schema table, columns ~ TableToColumns table) => Alias tab -> NP (Aliased (ColumnValue '[] params)) columns -> [NP (Aliased (ColumnValue '[] params)) columns] -> ConflictClause columns params -> ReturningClause columns params results -> Manipulation schema params results
-- | Insert a single row.
insertRow :: (SListI columns, SListI results, Has tab schema table, columns ~ TableToColumns table) => Alias tab -> NP (Aliased (ColumnValue '[] params)) columns -> ConflictClause columns params -> ReturningClause columns params results -> Manipulation schema params results
-- | Insert multiple rows returning Nil and raising an error on
-- conflicts.
insertRows_ :: (SListI columns, Has tab schema table, columns ~ TableToColumns table) => Alias tab -> NP (Aliased (ColumnValue '[] params)) columns -> [NP (Aliased (ColumnValue '[] params)) columns] -> Manipulation schema params '[]
-- | Insert a single row returning Nil and raising an error on
-- conflicts.
insertRow_ :: (SListI columns, Has tab schema table, columns ~ TableToColumns table) => Alias tab -> NP (Aliased (ColumnValue '[] params)) columns -> Manipulation schema params '[]
-- | Insert a Query.
insertQuery :: (SListI columns, SListI results, Has tab schema table, columns ~ TableToColumns table) => Alias tab -> Query schema params (ColumnsToRelation columns) -> ConflictClause columns params -> ReturningClause columns params results -> Manipulation schema params results
-- | Insert a Query returning Nil and raising an error on
-- conflicts.
insertQuery_ :: (SListI columns, Has tab schema table, columns ~ TableToColumns table) => Alias tab -> Query schema params (ColumnsToRelation columns) -> Manipulation schema params '[]
-- | Render a ReturningClause.
renderReturningClause :: SListI results => ReturningClause params columns results -> ByteString
-- | Render a ConflictClause.
renderConflictClause :: SListI columns => ConflictClause columns params -> ByteString
-- | An update command changes the values of the specified columns
-- in all rows that satisfy the condition.
update :: (SListI columns, SListI results, Has tab schema table, columns ~ TableToColumns table) => Alias tab -> NP (Aliased (ColumnValue (ColumnsToRelation columns) params)) columns -> Condition '[tab ::: ColumnsToRelation columns] 'Ungrouped params -> ReturningClause columns params results -> Manipulation schema params results
-- | Update a row returning Nil.
update_ :: (SListI columns, Has tab schema table, columns ~ TableToColumns table) => Alias tab -> NP (Aliased (ColumnValue (ColumnsToRelation columns) params)) columns -> Condition '[tab ::: ColumnsToRelation columns] 'Ungrouped params -> Manipulation schema params '[]
-- | Delete rows of a table.
deleteFrom :: (SListI results, Has tab schema table, columns ~ TableToColumns table) => Alias tab -> Condition '[tab ::: ColumnsToRelation columns] 'Ungrouped params -> ReturningClause columns params results -> Manipulation schema params results
-- | Delete rows returning Nil.
deleteFrom_ :: (Has tab schema table, columns ~ TableToColumns table) => Alias tab -> Condition '[tab ::: ColumnsToRelation columns] 'Ungrouped params -> Manipulation schema params '[]
-- | with provides a way to write auxiliary statements for use in a
-- larger statement. These statements, which are often referred to as
-- Common Table Expressions or CTEs, can be thought of as defining
-- temporary tables that exist just for one statement.
--
-- -- >>> type ProductsTable = '[] :=> '["product" ::: 'NoDef :=> 'NotNull 'PGtext, "date" ::: 'Def :=> 'NotNull 'PGdate] ---- --
-- >>> :{
-- let
-- manipulation :: Manipulation '["products" ::: ProductsTable, "products_deleted" ::: ProductsTable] '[ 'NotNull 'PGdate] '[]
-- manipulation = with
-- (deleteFrom #products (#date .< param @1) ReturningStar `As` #deleted_rows :* Nil)
-- (insertQuery_ #products_deleted (selectStar (from (table (#deleted_rows `As` #t)))))
-- in renderManipulation manipulation
-- :}
-- "WITH deleted_rows AS (DELETE FROM products WHERE (date < ($1 :: date)) RETURNING *) INSERT INTO products_deleted SELECT * FROM deleted_rows AS t;"
--
with :: SListI commons => NP (Aliased (Manipulation schema params)) commons -> Manipulation (Join (RelationsToTables commons) schema) params results -> Manipulation schema params results
instance Control.DeepSeq.NFData (Squeal.PostgreSQL.Manipulation.Manipulation schema params columns)
instance GHC.Classes.Ord (Squeal.PostgreSQL.Manipulation.Manipulation schema params columns)
instance GHC.Classes.Eq (Squeal.PostgreSQL.Manipulation.Manipulation schema params columns)
instance GHC.Show.Show (Squeal.PostgreSQL.Manipulation.Manipulation schema params columns)
instance GHC.Generics.Generic (Squeal.PostgreSQL.Manipulation.Manipulation schema params columns)
-- | Squeal data definition language.
module Squeal.PostgreSQL.Definition
-- | A Definition is a statement that changes the schema of the
-- database, like a createTable, dropTable, or
-- alterTable command. Definitions may be composed using
-- the >>> operator.
newtype Definition (schema0 :: TablesType) (schema1 :: TablesType)
UnsafeDefinition :: ByteString -> Definition
[renderDefinition] :: Definition -> ByteString
-- | Left-to-right composition
(>>>) :: Category k cat => cat a b -> cat b c -> cat a c
infixr 1 >>>
-- | createTable adds a table to the schema.
--
--
-- >>> :set -XOverloadedLabels
--
-- >>> :{
-- renderDefinition $
-- createTable #tab (int `As` #a :* real `As` #b :* Nil) Nil
-- :}
-- "CREATE TABLE tab (a int, b real);"
--
createTable :: (KnownSymbol table, columns ~ (col : cols), SListI columns, SListI constraints) => Alias table -> NP (Aliased TypeExpression) columns -> NP (Aliased (TableConstraintExpression schema columns)) constraints -> Definition schema (Create table (constraints :=> columns) schema)
-- | createTableIfNotExists creates a table if it doesn't exist, but
-- does not add it to the schema. Instead, the schema already has the
-- table so if the table did not yet exist, the schema was wrong.
-- createTableIfNotExists fixes this. Interestingly, this property
-- makes it an idempotent in the Category Definition.
--
--
-- >>> :set -XOverloadedLabels -XTypeApplications
--
-- >>> type Table = '[] :=> '["a" ::: 'NoDef :=> 'Null 'PGint4, "b" ::: 'NoDef :=> 'Null 'PGfloat4]
--
-- >>> type Schema = '["tab" ::: Table]
--
-- >>> :{
-- renderDefinition
-- (createTableIfNotExists #tab (int `As` #a :* real `As` #b :* Nil) Nil :: Definition Schema Schema)
-- :}
-- "CREATE TABLE IF NOT EXISTS tab (a int, b real);"
--
createTableIfNotExists :: (Has table schema (constraints :=> columns), SListI columns, SListI constraints) => Alias table -> NP (Aliased TypeExpression) columns -> NP (Aliased (TableConstraintExpression schema columns)) constraints -> Definition schema schema
-- | Data types are a way to limit the kind of data that can be stored in a
-- table. For many applications, however, the constraint they provide is
-- too coarse. For example, a column containing a product price should
-- probably only accept positive values. But there is no standard data
-- type that accepts only positive numbers. Another issue is that you
-- might want to constrain column data with respect to other columns or
-- rows. For example, in a table containing product information, there
-- should be only one row for each product number.
-- TableConstraints give you as much control over the data in your
-- tables as you wish. If a user attempts to store data in a column that
-- would violate a constraint, an error is raised. This applies even if
-- the value came from the default value definition.
newtype TableConstraintExpression (schema :: TablesType) (columns :: ColumnsType) (tableConstraint :: TableConstraint)
UnsafeTableConstraintExpression :: ByteString -> TableConstraintExpression
[renderTableConstraintExpression] :: TableConstraintExpression -> ByteString
-- | Column columns column is a witness that column is in
-- columns.
data Column (columns :: ColumnsType) (column :: (Symbol, ColumnType))
[Column] :: Has column columns ty => Alias column -> Column columns (column ::: ty)
-- | A check constraint is the most generic TableConstraint
-- type. It allows you to specify that the value in a certain column must
-- satisfy a Boolean (truth-value) expression.
--
--
-- >>> :{
-- renderDefinition $
-- createTable #tab
-- ( (int & notNull) `As` #a :*
-- (int & notNull) `As` #b :* Nil )
-- ( check (Column #a :* Column #b :* Nil) (#a .> #b) `As` #inequality :* Nil )
-- :}
-- "CREATE TABLE tab (a int NOT NULL, b int NOT NULL, CONSTRAINT inequality CHECK ((a > b)));"
--
check :: NP (Column columns) subcolumns -> Condition '[table ::: ColumnsToRelation subcolumns] 'Ungrouped '[] -> TableConstraintExpression schema columns ( 'Check (AliasesOf subcolumns))
-- | A unique constraint ensure that the data contained in a column,
-- or a group of columns, is unique among all the rows in the table.
--
--
-- >>> :{
-- renderDefinition $
-- createTable #tab
-- ( int `As` #a :*
-- int `As` #b :* Nil )
-- ( unique (Column #a :* Column #b :* Nil) `As` #uq_a_b :* Nil )
-- :}
-- "CREATE TABLE tab (a int, b int, CONSTRAINT uq_a_b UNIQUE (a, b));"
--
unique :: SListI subcolumns => NP (Column columns) subcolumns -> TableConstraintExpression schema columns ( 'Unique (AliasesOf subcolumns))
-- | A primaryKey constraint indicates that a column, or group of
-- columns, can be used as a unique identifier for rows in the table.
-- This requires that the values be both unique and not null.
--
--
-- >>> :{
-- renderDefinition $
-- createTable #tab
-- ( serial `As` #id :*
-- (text & notNull) `As` #name :* Nil )
-- ( primaryKey (Column #id :* Nil) `As` #pk_id :* Nil )
-- :}
-- "CREATE TABLE tab (id serial, name text NOT NULL, CONSTRAINT pk_id PRIMARY KEY (id));"
--
primaryKey :: (SListI subcolumns, AllNotNull subcolumns) => NP (Column columns) subcolumns -> TableConstraintExpression schema columns ( 'PrimaryKey (AliasesOf subcolumns))
-- | A foreignKey specifies that the values in a column (or a group
-- of columns) must match the values appearing in some row of another
-- table. We say this maintains the referential integrity between two
-- related tables.
--
--
-- >>> :{
-- type Schema =
-- '[ "users" :::
-- '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
-- '[ "id" ::: 'Def :=> 'NotNull 'PGint4
-- , "name" ::: 'NoDef :=> 'NotNull 'PGtext
-- ]
-- , "emails" :::
-- '[ "pk_emails" ::: 'PrimaryKey '["id"]
-- , "fk_user_id" ::: 'ForeignKey '["user_id"] "users" '["id"]
-- ] :=>
-- '[ "id" ::: 'Def :=> 'NotNull 'PGint4
-- , "user_id" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "email" ::: 'NoDef :=> 'Null 'PGtext
-- ]
-- ]
-- :}
--
--
--
-- >>> :{
-- let
-- setup :: Definition '[] Schema
-- setup =
-- createTable #users
-- ( serial `As` #id :*
-- (text & notNull) `As` #name :* Nil )
-- ( primaryKey (Column #id :* Nil) `As` #pk_users :* Nil ) >>>
-- createTable #emails
-- ( serial `As` #id :*
-- (int & notNull) `As` #user_id :*
-- text `As` #email :* Nil )
-- ( primaryKey (Column #id :* Nil) `As` #pk_emails :*
-- foreignKey (Column #user_id :* Nil) #users (Column #id :* Nil)
-- OnDeleteCascade OnUpdateCascade `As` #fk_user_id :* Nil )
-- in renderDefinition setup
-- :}
-- "CREATE TABLE users (id serial, name text NOT NULL, CONSTRAINT pk_users PRIMARY KEY (id)); CREATE TABLE emails (id serial, user_id int NOT NULL, email text, CONSTRAINT pk_emails PRIMARY KEY (id), CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE);"
--
foreignKey :: ForeignKeyed schema table reftable subcolumns refsubcolumns => NP (Column columns) subcolumns -> Alias table -> NP (Column (TableToColumns reftable)) refsubcolumns -> OnDeleteClause -> OnUpdateClause -> TableConstraintExpression schema columns ( 'ForeignKey (AliasesOf subcolumns) table (AliasesOf refsubcolumns))
-- | A type synonym for constraints on a table with a foreign key.
type ForeignKeyed schema table reftable subcolumns refsubcolumns = (Has table schema reftable, SameTypes subcolumns refsubcolumns, AllNotNull subcolumns, SListI subcolumns, SListI refsubcolumns)
-- | OnDeleteClause indicates what to do with rows that reference a
-- deleted row.
data OnDeleteClause
-- | if any referencing rows still exist when the constraint is checked, an
-- error is raised
OnDeleteNoAction :: OnDeleteClause
-- | prevents deletion of a referenced row
OnDeleteRestrict :: OnDeleteClause
-- | specifies that when a referenced row is deleted, row(s) referencing it
-- should be automatically deleted as well
OnDeleteCascade :: OnDeleteClause
-- | Render OnDeleteClause.
renderOnDeleteClause :: OnDeleteClause -> ByteString
-- | Analagous to OnDeleteClause there is also OnUpdateClause
-- which is invoked when a referenced column is changed (updated).
data OnUpdateClause
-- | if any referencing rows has not changed when the constraint is
-- checked, an error is raised
OnUpdateNoAction :: OnUpdateClause
-- | prevents update of a referenced row
OnUpdateRestrict :: OnUpdateClause
-- | the updated values of the referenced column(s) should be copied into
-- the referencing row(s)
OnUpdateCascade :: OnUpdateClause
-- | Render OnUpdateClause.
renderOnUpdateClause :: OnUpdateClause -> ByteString
-- | dropTable removes a table from the schema.
--
-- -- >>> renderDefinition $ dropTable #muh_table -- "DROP TABLE muh_table;" --dropTable :: KnownSymbol table => Alias table -> Definition schema (Drop table schema) -- | alterTable changes the definition of a table from the schema. alterTable :: Has tab schema table0 => Alias tab -> AlterTable schema table0 table1 -> Definition schema (Alter tab schema table1) -- | alterTableRename changes the name of a table from the schema. -- --
-- >>> renderDefinition $ alterTableRename #foo #bar -- "ALTER TABLE foo RENAME TO bar;" --alterTableRename :: (KnownSymbol table0, KnownSymbol table1) => Alias table0 -> Alias table1 -> Definition schema (Rename table0 table1 schema) -- | An AlterTable describes the alteration to perform on the -- columns of a table. newtype AlterTable (schema :: TablesType) (table0 :: TableType) (table1 :: TableType) UnsafeAlterTable :: ByteString -> AlterTable [renderAlterTable] :: AlterTable -> ByteString -- | An addConstraint adds a table constraint. -- --
-- >>> :{
-- let
-- definition :: Definition
-- '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGint4]]
-- '["tab" ::: '["positive" ::: Check '["col"]] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGint4]]
-- definition = alterTable #tab (addConstraint #positive (check (Column #col :* Nil) (#col .> 0)))
-- in renderDefinition definition
-- :}
-- "ALTER TABLE tab ADD CONSTRAINT positive CHECK ((col > 0));"
--
addConstraint :: KnownSymbol alias => Alias alias -> TableConstraintExpression schema columns constraint -> AlterTable schema (constraints :=> columns) (Create alias constraint constraints :=> columns)
-- | A dropConstraint drops a table constraint.
--
--
-- >>> :{
-- let
-- definition :: Definition
-- '["tab" ::: '["positive" ::: Check '["col"]] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGint4]]
-- '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGint4]]
-- definition = alterTable #tab (dropConstraint #positive)
-- in renderDefinition definition
-- :}
-- "ALTER TABLE tab DROP CONSTRAINT positive;"
--
dropConstraint :: KnownSymbol constraint => Alias constraint -> AlterTable schema (constraints :=> columns) (Drop constraint constraints :=> columns)
-- | An AddColumn is either NULL or has DEFAULT.
class AddColumn ty
-- | addColumn adds a new column, initially filled with whatever
-- default value is given or with NULL.
--
--
-- >>> :{
-- let
-- definition :: Definition
-- '["tab" ::: '[] :=> '["col1" ::: 'NoDef :=> 'Null 'PGint4]]
-- '["tab" ::: '[] :=>
-- '[ "col1" ::: 'NoDef :=> 'Null 'PGint4
-- , "col2" ::: 'Def :=> 'Null 'PGtext ]]
-- definition = alterTable #tab (addColumn #col2 (text & default_ "foo"))
-- in renderDefinition definition
-- :}
-- "ALTER TABLE tab ADD COLUMN col2 text DEFAULT E'foo';"
--
--
--
-- >>> :{
-- let
-- definition :: Definition
-- '["tab" ::: '[] :=> '["col1" ::: 'NoDef :=> 'Null 'PGint4]]
-- '["tab" ::: '[] :=>
-- '[ "col1" ::: 'NoDef :=> 'Null 'PGint4
-- , "col2" ::: 'NoDef :=> 'Null 'PGtext ]]
-- definition = alterTable #tab (addColumn #col2 text)
-- in renderDefinition definition
-- :}
-- "ALTER TABLE tab ADD COLUMN col2 text;"
--
addColumn :: (AddColumn ty, KnownSymbol column) => Alias column -> TypeExpression ty -> AlterTable schema (constraints :=> columns) (constraints :=> Create column ty columns)
-- | A dropColumn removes a column. Whatever data was in the column
-- disappears. Table constraints involving the column are dropped, too.
-- However, if the column is referenced by a foreign key constraint of
-- another table, PostgreSQL will not silently drop that constraint.
--
--
-- >>> :{
-- let
-- definition :: Definition
-- '["tab" ::: '[] :=>
-- '[ "col1" ::: 'NoDef :=> 'Null 'PGint4
-- , "col2" ::: 'NoDef :=> 'Null 'PGtext ]]
-- '["tab" ::: '[] :=> '["col1" ::: 'NoDef :=> 'Null 'PGint4]]
-- definition = alterTable #tab (dropColumn #col2)
-- in renderDefinition definition
-- :}
-- "ALTER TABLE tab DROP COLUMN col2;"
--
dropColumn :: KnownSymbol column => Alias column -> AlterTable schema (constraints :=> columns) (DropIfConstraintsInvolve column constraints :=> Drop column columns)
-- | A renameColumn renames a column.
--
--
-- >>> :{
-- let
-- definition :: Definition
-- '["tab" ::: '[] :=> '["foo" ::: 'NoDef :=> 'Null 'PGint4]]
-- '["tab" ::: '[] :=> '["bar" ::: 'NoDef :=> 'Null 'PGint4]]
-- definition = alterTable #tab (renameColumn #foo #bar)
-- in renderDefinition definition
-- :}
-- "ALTER TABLE tab RENAME COLUMN foo TO bar;"
--
renameColumn :: (KnownSymbol column0, KnownSymbol column1) => Alias column0 -> Alias column1 -> AlterTable schema (constraints :=> columns) (constraints :=> Rename column0 column1 columns)
-- | An alterColumn alters a single column.
alterColumn :: (KnownSymbol column, Has column columns ty0) => Alias column -> AlterColumn ty0 ty1 -> AlterTable schema (constraints :=> columns) (constraints :=> Alter column columns ty1)
-- | An AlterColumn describes the alteration to perform on a single
-- column.
newtype AlterColumn (ty0 :: ColumnType) (ty1 :: ColumnType)
UnsafeAlterColumn :: ByteString -> AlterColumn
[renderAlterColumn] :: AlterColumn -> ByteString
-- | A setDefault sets a new default for a column. Note that this
-- doesn't affect any existing rows in the table, it just changes the
-- default for future insert and update commands.
--
--
-- >>> :{
-- let
-- definition :: Definition
-- '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4]]
-- '["tab" ::: '[] :=> '["col" ::: 'Def :=> 'Null 'PGint4]]
-- definition = alterTable #tab (alterColumn #col (setDefault 5))
-- in renderDefinition definition
-- :}
-- "ALTER TABLE tab ALTER COLUMN col SET DEFAULT 5;"
--
setDefault :: Expression '[] 'Ungrouped '[] ty -> AlterColumn (constraint :=> ty) ( 'Def :=> ty)
-- | A dropDefault removes any default value for a column.
--
--
-- >>> :{
-- let
-- definition :: Definition
-- '["tab" ::: '[] :=> '["col" ::: 'Def :=> 'Null 'PGint4]]
-- '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4]]
-- definition = alterTable #tab (alterColumn #col dropDefault)
-- in renderDefinition definition
-- :}
-- "ALTER TABLE tab ALTER COLUMN col DROP DEFAULT;"
--
dropDefault :: AlterColumn ( 'Def :=> ty) ( 'NoDef :=> ty)
-- | A setNotNull adds a NOT NULL constraint to a column.
-- The constraint will be checked immediately, so the table data must
-- satisfy the constraint before it can be added.
--
--
-- >>> :{
-- let
-- definition :: Definition
-- '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4]]
-- '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGint4]]
-- definition = alterTable #tab (alterColumn #col setNotNull)
-- in renderDefinition definition
-- :}
-- "ALTER TABLE tab ALTER COLUMN col SET NOT NULL;"
--
setNotNull :: AlterColumn (constraint :=> 'Null ty) (constraint :=> 'NotNull ty)
-- | A dropNotNull drops a NOT NULL constraint from a
-- column.
--
--
-- >>> :{
-- let
-- definition :: Definition
-- '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGint4]]
-- '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4]]
-- definition = alterTable #tab (alterColumn #col dropNotNull)
-- in renderDefinition definition
-- :}
-- "ALTER TABLE tab ALTER COLUMN col DROP NOT NULL;"
--
dropNotNull :: AlterColumn (constraint :=> 'NotNull ty) (constraint :=> 'Null ty)
-- | An alterType converts a column to a different data type. This
-- will succeed only if each existing entry in the column can be
-- converted to the new type by an implicit cast.
--
--
-- >>> :{
-- let
-- definition :: Definition
-- '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGint4]]
-- '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGnumeric]]
-- definition =
-- alterTable #tab (alterColumn #col (alterType (numeric & notNull)))
-- in renderDefinition definition
-- :}
-- "ALTER TABLE tab ALTER COLUMN col TYPE numeric NOT NULL;"
--
alterType :: TypeExpression ty -> AlterColumn ty0 ty
instance Control.DeepSeq.NFData (Squeal.PostgreSQL.Definition.AlterColumn ty0 ty1)
instance GHC.Classes.Ord (Squeal.PostgreSQL.Definition.AlterColumn ty0 ty1)
instance GHC.Classes.Eq (Squeal.PostgreSQL.Definition.AlterColumn ty0 ty1)
instance GHC.Show.Show (Squeal.PostgreSQL.Definition.AlterColumn ty0 ty1)
instance GHC.Generics.Generic (Squeal.PostgreSQL.Definition.AlterColumn ty0 ty1)
instance Control.DeepSeq.NFData (Squeal.PostgreSQL.Definition.AlterTable schema table0 table1)
instance GHC.Classes.Ord (Squeal.PostgreSQL.Definition.AlterTable schema table0 table1)
instance GHC.Classes.Eq (Squeal.PostgreSQL.Definition.AlterTable schema table0 table1)
instance GHC.Show.Show (Squeal.PostgreSQL.Definition.AlterTable schema table0 table1)
instance GHC.Generics.Generic (Squeal.PostgreSQL.Definition.AlterTable schema table0 table1)
instance GHC.Classes.Ord Squeal.PostgreSQL.Definition.OnUpdateClause
instance GHC.Classes.Eq Squeal.PostgreSQL.Definition.OnUpdateClause
instance GHC.Show.Show Squeal.PostgreSQL.Definition.OnUpdateClause
instance GHC.Generics.Generic Squeal.PostgreSQL.Definition.OnUpdateClause
instance GHC.Classes.Ord Squeal.PostgreSQL.Definition.OnDeleteClause
instance GHC.Classes.Eq Squeal.PostgreSQL.Definition.OnDeleteClause
instance GHC.Show.Show Squeal.PostgreSQL.Definition.OnDeleteClause
instance GHC.Generics.Generic Squeal.PostgreSQL.Definition.OnDeleteClause
instance Control.DeepSeq.NFData (Squeal.PostgreSQL.Definition.TableConstraintExpression schema columns tableConstraint)
instance GHC.Classes.Ord (Squeal.PostgreSQL.Definition.TableConstraintExpression schema columns tableConstraint)
instance GHC.Classes.Eq (Squeal.PostgreSQL.Definition.TableConstraintExpression schema columns tableConstraint)
instance GHC.Show.Show (Squeal.PostgreSQL.Definition.TableConstraintExpression schema columns tableConstraint)
instance GHC.Generics.Generic (Squeal.PostgreSQL.Definition.TableConstraintExpression schema columns tableConstraint)
instance Control.DeepSeq.NFData (Squeal.PostgreSQL.Definition.Definition schema0 schema1)
instance GHC.Classes.Ord (Squeal.PostgreSQL.Definition.Definition schema0 schema1)
instance GHC.Classes.Eq (Squeal.PostgreSQL.Definition.Definition schema0 schema1)
instance GHC.Show.Show (Squeal.PostgreSQL.Definition.Definition schema0 schema1)
instance GHC.Generics.Generic (Squeal.PostgreSQL.Definition.Definition schema0 schema1)
instance Squeal.PostgreSQL.Definition.AddColumn ((Squeal.PostgreSQL.Schema.:=>) Squeal.PostgreSQL.Schema.ColumnConstraint Squeal.PostgreSQL.Schema.NullityType 'Squeal.PostgreSQL.Schema.Def ty)
instance Squeal.PostgreSQL.Definition.AddColumn ((Squeal.PostgreSQL.Schema.:=>) Squeal.PostgreSQL.Schema.ColumnConstraint Squeal.PostgreSQL.Schema.NullityType 'Squeal.PostgreSQL.Schema.NoDef ('Squeal.PostgreSQL.Schema.Null ty))
instance Control.DeepSeq.NFData Squeal.PostgreSQL.Definition.OnUpdateClause
instance Control.DeepSeq.NFData Squeal.PostgreSQL.Definition.OnDeleteClause
instance Control.Category.Category Squeal.PostgreSQL.Schema.TablesType Squeal.PostgreSQL.Definition.Definition
-- | Binary encoding and decoding between Haskell and PostgreSQL types.
module Squeal.PostgreSQL.Binary
-- | A ToParam constraint gives an encoding of a Haskell Type
-- into into the binary format of a PostgreSQL PGType.
class ToParam (x :: Type) (pg :: PGType)
-- | -- >>> :set -XTypeApplications -XDataKinds -- -- >>> toParam @Bool @'PGbool False -- K "\NUL" ---- --
-- >>> toParam @Int16 @'PGint2 0 -- K "\NUL\NUL" ---- --
-- >>> toParam @Int32 @'PGint4 0 -- K "\NUL\NUL\NUL\NUL" ---- --
-- >>> :set -XMultiParamTypeClasses
--
-- >>> newtype Id = Id { getId :: Int16 } deriving Show
--
-- >>> instance ToParam Id 'PGint2 where toParam = toParam . getId
--
-- >>> toParam @Id @'PGint2 (Id 1)
-- K "\NUL\SOH"
--
toParam :: ToParam x pg => x -> K Encoding pg
-- | A ToColumnParam constraint lifts the ToParam encoding of
-- a Type to a ColumnType, encoding Maybes to
-- Nulls. You should not define instances of ToColumnParam,
-- just use the provided instances.
class ToColumnParam (x :: Type) (ty :: NullityType)
-- |
-- >>> toColumnParam @Int16 @('NotNull 'PGint2) 0
-- K (Just "\NUL\NUL")
--
--
--
-- >>> toColumnParam @(Maybe Int16) @('Null 'PGint2) (Just 0)
-- K (Just "\NUL\NUL")
--
--
--
-- >>> toColumnParam @(Maybe Int16) @('Null 'PGint2) Nothing
-- K Nothing
--
toColumnParam :: ToColumnParam x ty => x -> K (Maybe ByteString) ty
-- | A ToParams constraint generically sequences the encodings of
-- Types of the fields of a tuple or record to a row of
-- ColumnTypes. You should not define instances of
-- ToParams. Instead define Generic instances which in turn
-- provide ToParams instances.
class SListI tys => ToParams (x :: Type) (tys :: [NullityType])
-- | -- >>> type Params = '[ 'NotNull 'PGbool, 'Null 'PGint2] -- -- >>> toParams @(Bool, Maybe Int16) @'[ 'NotNull 'PGbool, 'Null 'PGint2] (False, Just 0) -- K (Just "\NUL") :* K (Just "\NUL\NUL") :* Nil ---- --
-- >>> :set -XDeriveGeneric
--
-- >>> data Tuple = Tuple { p1 :: Bool, p2 :: Maybe Int16} deriving GHC.Generic
--
-- >>> instance Generic Tuple
--
-- >>> toParams @Tuple @Params (Tuple False (Just 0))
-- K (Just "\NUL") :* K (Just "\NUL\NUL") :* Nil
--
toParams :: ToParams x tys => x -> NP (K (Maybe ByteString)) tys
-- | A FromValue constraint gives a parser from the binary format of
-- a PostgreSQL PGType into a Haskell Type.
class FromValue (pg :: PGType) (y :: Type)
-- |
-- >>> newtype Id = Id { getId :: Int16 } deriving Show
--
-- >>> instance FromValue 'PGint2 Id where fromValue = fmap Id . fromValue
--
fromValue :: FromValue pg y => proxy pg -> Value y
-- | A FromColumnValue constraint lifts the FromValue parser
-- to a decoding of a (Symbol, ColumnType) to a Type,
-- decoding Nulls to Maybes. You should not define
-- instances for FromColumnValue, just use the provided instances.
class FromColumnValue (colty :: (Symbol, NullityType)) (y :: Type)
-- |
-- >>> :set -XTypeOperators -XOverloadedStrings
--
-- >>> newtype Id = Id { getId :: Int16 } deriving Show
--
-- >>> instance FromValue 'PGint2 Id where fromValue = fmap Id . fromValue
--
-- >>> fromColumnValue @("col" ::: 'NotNull 'PGint2) @Id (K (Just "\NUL\SOH"))
-- Id {getId = 1}
--
--
--
-- >>> fromColumnValue @("col" ::: 'Null 'PGint2) @(Maybe Id) (K (Just "\NUL\SOH"))
-- Just (Id {getId = 1})
--
fromColumnValue :: FromColumnValue colty y => K (Maybe ByteString) colty -> y
-- | A FromRow constraint generically sequences the parsings of the
-- columns of a RelationType into the fields of a record
-- Type provided they have the same field names. You should not
-- define instances of FromRow. Instead define Generic and
-- HasDatatypeInfo instances which in turn provide FromRow
-- instances.
class SListI results => FromRow (results :: RelationType) y
-- |
-- >>> :set -XOverloadedStrings
--
-- >>> import Data.Text
--
-- >>> newtype UserId = UserId { getUserId :: Int16 } deriving Show
--
-- >>> instance FromValue 'PGint2 UserId where fromValue = fmap UserId . fromValue
--
-- >>> data UserRow = UserRow { userId :: UserId, userName :: Maybe Text } deriving (Show, GHC.Generic)
--
-- >>> instance Generic UserRow
--
-- >>> instance HasDatatypeInfo UserRow
--
-- >>> type User = '["userId" ::: 'NotNull 'PGint2, "userName" ::: 'Null 'PGtext]
--
-- >>> fromRow @User @UserRow (K (Just "\NUL\SOH") :* K (Just "bloodninja") :* Nil)
-- UserRow {userId = UserId {getUserId = 1}, userName = Just "bloodninja"}
--
fromRow :: FromRow results y => NP (K (Maybe ByteString)) results -> y
-- | Only is a 1-tuple type, useful for encoding a single parameter
-- with toParams or decoding a single value with fromRow.
--
-- -- >>> import Data.Text -- -- >>> toParams @(Only (Maybe Text)) @'[ 'Null 'PGtext] (Only (Just "foo")) -- K (Just "foo") :* Nil ---- --
-- >>> fromRow @'["fromOnly" ::: 'Null 'PGtext] @(Only (Maybe Text)) (K (Just "bar") :* Nil)
-- Only {fromOnly = Just "bar"}
--
newtype Only x
Only :: x -> Only x
[fromOnly] :: Only x -> x
instance GHC.Generics.Generic (Squeal.PostgreSQL.Binary.Only x)
instance GHC.Show.Show x => GHC.Show.Show (Squeal.PostgreSQL.Binary.Only x)
instance GHC.Read.Read x => GHC.Read.Read (Squeal.PostgreSQL.Binary.Only x)
instance GHC.Classes.Ord x => GHC.Classes.Ord (Squeal.PostgreSQL.Binary.Only x)
instance GHC.Classes.Eq x => GHC.Classes.Eq (Squeal.PostgreSQL.Binary.Only x)
instance Data.Traversable.Traversable Squeal.PostgreSQL.Binary.Only
instance Data.Foldable.Foldable Squeal.PostgreSQL.Binary.Only
instance GHC.Base.Functor Squeal.PostgreSQL.Binary.Only
instance Generics.SOP.Universe.Generic (Squeal.PostgreSQL.Binary.Only x)
instance Generics.SOP.Universe.HasDatatypeInfo (Squeal.PostgreSQL.Binary.Only x)
instance (Generics.SOP.Sing.SListI (GHC.Types.Symbol, Squeal.PostgreSQL.Schema.NullityType) results, Generics.SOP.Universe.IsProductType y ys, Generics.SOP.Constraint.AllZip (GHC.Types.Symbol, Squeal.PostgreSQL.Schema.NullityType) * Squeal.PostgreSQL.Binary.FromColumnValue results ys, Squeal.PostgreSQL.Schema.SameFields (Generics.SOP.Universe.DatatypeInfoOf y) results) => Squeal.PostgreSQL.Binary.FromRow results y
instance Squeal.PostgreSQL.Binary.FromValue pg y => Squeal.PostgreSQL.Binary.FromColumnValue ((Squeal.PostgreSQL.Schema.:::) Squeal.PostgreSQL.Schema.NullityType column ('Squeal.PostgreSQL.Schema.NotNull pg)) y
instance Squeal.PostgreSQL.Binary.FromValue pg y => Squeal.PostgreSQL.Binary.FromColumnValue ((Squeal.PostgreSQL.Schema.:::) Squeal.PostgreSQL.Schema.NullityType column ('Squeal.PostgreSQL.Schema.Null pg)) (GHC.Base.Maybe y)
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGbool GHC.Types.Bool
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGint2 GHC.Int.Int16
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGint4 GHC.Int.Int32
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGint8 GHC.Int.Int64
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGfloat4 GHC.Types.Float
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGfloat8 GHC.Types.Double
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGnumeric Data.Scientific.Scientific
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGuuid Data.UUID.Types.Internal.UUID
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGinet (Network.IP.Addr.NetAddr Network.IP.Addr.IP)
instance Squeal.PostgreSQL.Binary.FromValue ('Squeal.PostgreSQL.Schema.PGchar 1) GHC.Types.Char
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGtext Data.Text.Internal.Text
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGtext Data.Text.Internal.Lazy.Text
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGbytea Data.ByteString.Internal.ByteString
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGbytea Data.ByteString.Lazy.Internal.ByteString
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGdate Data.Time.Calendar.Days.Day
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGtime Data.Time.LocalTime.Internal.TimeOfDay.TimeOfDay
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGtimetz (Data.Time.LocalTime.Internal.TimeOfDay.TimeOfDay, Data.Time.LocalTime.Internal.TimeZone.TimeZone)
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGtimestamp Data.Time.LocalTime.Internal.LocalTime.LocalTime
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGtimestamptz Data.Time.Clock.Internal.UTCTime.UTCTime
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGinterval Data.Time.Clock.Internal.DiffTime.DiffTime
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGjson Data.Aeson.Types.Internal.Value
instance Squeal.PostgreSQL.Binary.FromValue 'Squeal.PostgreSQL.Schema.PGjsonb Data.Aeson.Types.Internal.Value
instance Squeal.PostgreSQL.Binary.FromValue pg y => Squeal.PostgreSQL.Binary.FromValue ('Squeal.PostgreSQL.Schema.PGvararray pg) (Data.Vector.Vector (GHC.Base.Maybe y))
instance Squeal.PostgreSQL.Binary.FromValue pg y => Squeal.PostgreSQL.Binary.FromValue ('Squeal.PostgreSQL.Schema.PGfixarray n pg) (Data.Vector.Vector (GHC.Base.Maybe y))
instance (Generics.SOP.Sing.SListI Squeal.PostgreSQL.Schema.NullityType tys, Generics.SOP.Universe.IsProductType x xs, Generics.SOP.Constraint.AllZip * Squeal.PostgreSQL.Schema.NullityType Squeal.PostgreSQL.Binary.ToColumnParam xs tys) => Squeal.PostgreSQL.Binary.ToParams x tys
instance Squeal.PostgreSQL.Binary.ToParam x pg => Squeal.PostgreSQL.Binary.ToColumnParam x ('Squeal.PostgreSQL.Schema.NotNull pg)
instance Squeal.PostgreSQL.Binary.ToParam x pg => Squeal.PostgreSQL.Binary.ToColumnParam (GHC.Base.Maybe x) ('Squeal.PostgreSQL.Schema.Null pg)
instance Squeal.PostgreSQL.Binary.ToParam GHC.Types.Bool 'Squeal.PostgreSQL.Schema.PGbool
instance Squeal.PostgreSQL.Binary.ToParam GHC.Int.Int16 'Squeal.PostgreSQL.Schema.PGint2
instance Squeal.PostgreSQL.Binary.ToParam GHC.Word.Word16 'Squeal.PostgreSQL.Schema.PGint2
instance Squeal.PostgreSQL.Binary.ToParam GHC.Int.Int32 'Squeal.PostgreSQL.Schema.PGint4
instance Squeal.PostgreSQL.Binary.ToParam GHC.Word.Word32 'Squeal.PostgreSQL.Schema.PGint4
instance Squeal.PostgreSQL.Binary.ToParam GHC.Int.Int64 'Squeal.PostgreSQL.Schema.PGint8
instance Squeal.PostgreSQL.Binary.ToParam GHC.Word.Word64 'Squeal.PostgreSQL.Schema.PGint8
instance Squeal.PostgreSQL.Binary.ToParam GHC.Types.Float 'Squeal.PostgreSQL.Schema.PGfloat4
instance Squeal.PostgreSQL.Binary.ToParam GHC.Types.Double 'Squeal.PostgreSQL.Schema.PGfloat8
instance Squeal.PostgreSQL.Binary.ToParam Data.Scientific.Scientific 'Squeal.PostgreSQL.Schema.PGnumeric
instance Squeal.PostgreSQL.Binary.ToParam Data.UUID.Types.Internal.UUID 'Squeal.PostgreSQL.Schema.PGuuid
instance Squeal.PostgreSQL.Binary.ToParam (Network.IP.Addr.NetAddr Network.IP.Addr.IP) 'Squeal.PostgreSQL.Schema.PGinet
instance Squeal.PostgreSQL.Binary.ToParam GHC.Types.Char ('Squeal.PostgreSQL.Schema.PGchar 1)
instance Squeal.PostgreSQL.Binary.ToParam Data.Text.Internal.Text 'Squeal.PostgreSQL.Schema.PGtext
instance Squeal.PostgreSQL.Binary.ToParam Data.Text.Internal.Lazy.Text 'Squeal.PostgreSQL.Schema.PGtext
instance Squeal.PostgreSQL.Binary.ToParam Data.ByteString.Internal.ByteString 'Squeal.PostgreSQL.Schema.PGbytea
instance Squeal.PostgreSQL.Binary.ToParam Data.ByteString.Lazy.Internal.ByteString 'Squeal.PostgreSQL.Schema.PGbytea
instance Squeal.PostgreSQL.Binary.ToParam Data.Time.Calendar.Days.Day 'Squeal.PostgreSQL.Schema.PGdate
instance Squeal.PostgreSQL.Binary.ToParam Data.Time.LocalTime.Internal.TimeOfDay.TimeOfDay 'Squeal.PostgreSQL.Schema.PGtime
instance Squeal.PostgreSQL.Binary.ToParam (Data.Time.LocalTime.Internal.TimeOfDay.TimeOfDay, Data.Time.LocalTime.Internal.TimeZone.TimeZone) 'Squeal.PostgreSQL.Schema.PGtimetz
instance Squeal.PostgreSQL.Binary.ToParam Data.Time.LocalTime.Internal.LocalTime.LocalTime 'Squeal.PostgreSQL.Schema.PGtimestamp
instance Squeal.PostgreSQL.Binary.ToParam Data.Time.Clock.Internal.UTCTime.UTCTime 'Squeal.PostgreSQL.Schema.PGtimestamptz
instance Squeal.PostgreSQL.Binary.ToParam Data.Time.Clock.Internal.DiffTime.DiffTime 'Squeal.PostgreSQL.Schema.PGinterval
instance Squeal.PostgreSQL.Binary.ToParam Data.Aeson.Types.Internal.Value 'Squeal.PostgreSQL.Schema.PGjson
instance Squeal.PostgreSQL.Binary.ToParam Data.Aeson.Types.Internal.Value 'Squeal.PostgreSQL.Schema.PGjsonb
instance (Squeal.PostgreSQL.Schema.HasOid pg, Squeal.PostgreSQL.Binary.ToParam x pg) => Squeal.PostgreSQL.Binary.ToParam (Data.Vector.Vector (GHC.Base.Maybe x)) ('Squeal.PostgreSQL.Schema.PGvararray pg)
-- | This module is where Squeal commands actually get executed by
-- LibPQ. It containts two typeclasses,
-- IndexedMonadTransPQ for executing a Definition and
-- MonadPQ for executing a Manipulation or Query,
-- and a PQ type with instances for them.
--
-- Using Squeal in your application will come down to defining the
-- schema of your database and including PQ schema
-- schema in your application's monad transformer stack, giving it
-- an instance of MonadPQ.
--
-- This module also provides functions for retrieving rows from the
-- Result of executing Squeal commands.
module Squeal.PostgreSQL.PQ
-- | Connection encapsulates a connection to the backend.
data Connection :: *
-- | Makes a new connection to the database server.
--
-- This function opens a new database connection using the parameters
-- taken from the string conninfo.
--
-- The passed string can be empty to use all default parameters, or it
-- can contain one or more parameter settings separated by whitespace.
-- Each parameter setting is in the form keyword = value. Spaces around
-- the equal sign are optional. To write an empty value or a value
-- containing spaces, surround it with single quotes, e.g., keyword = 'a
-- value'. Single quotes and backslashes within the value must be escaped
-- with a backslash, i.e., ' and .
--
-- To specify the schema you wish to connect with, use type application.
--
-- -- >>> :set -XDataKinds -- -- >>> :set -XPolyKinds -- -- >>> :set -XTypeOperators -- -- >>> type Schema = '["tab" ::: '[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint2]] -- -- >>> :set -XTypeApplications -- -- >>> :set -XOverloadedStrings -- -- >>> conn <- connectdb @Schema "host=localhost port=5432 dbname=exampledb" ---- -- Note that, for now, squeal doesn't offer any protection from -- connecting with the wrong schema! connectdb :: forall schema io. MonadBase IO io => ByteString -> io (K Connection schema) -- | Closes the connection to the server. finish :: MonadBase IO io => K Connection schema -> io () -- | Do connectdb and finish before and after a computation. withConnection :: forall schema0 schema1 io x. MonadBaseControl IO io => ByteString -> PQ schema0 schema1 io x -> io x -- | Safely lowerConnection to a smaller schema. lowerConnection :: K Connection (table : schema) -> K Connection schema -- | We keep track of the schema via an Atkey indexed state monad -- transformer, PQ. newtype PQ (schema0 :: TablesType) (schema1 :: TablesType) (m :: Type -> Type) (x :: Type) PQ :: (K Connection schema0 -> m (K x schema1)) -> PQ [unPQ] :: PQ -> K Connection schema0 -> m (K x schema1) -- | Run a PQ and keep the result and the Connection. runPQ :: Functor m => PQ schema0 schema1 m x -> K Connection schema0 -> m (x, K Connection schema1) -- | Execute a PQ and discard the result but keep the -- Connection. execPQ :: Functor m => PQ schema0 schema1 m x -> K Connection schema0 -> m (K Connection schema1) -- | Evaluate a PQ and discard the Connection but keep the -- result. evalPQ :: Functor m => PQ schema0 schema1 m x -> K Connection schema0 -> m x -- | An Atkey indexed monad is a Functor enriched -- category. An indexed monad transformer transforms a Monad -- into an indexed monad. And, IndexedMonadTransPQ is a class for -- indexed monad transformers that support running Definitions -- using define and embedding a computation in a larger schema -- using pqEmbed. class IndexedMonadTransPQ pq -- | indexed analog of <*> pqAp :: (IndexedMonadTransPQ pq, Monad m) => pq schema0 schema1 m (x -> y) -> pq schema1 schema2 m x -> pq schema0 schema2 m y -- | indexed analog of join pqJoin :: (IndexedMonadTransPQ pq, Monad m) => pq schema0 schema1 m (pq schema1 schema2 m y) -> pq schema0 schema2 m y -- | indexed analog of =<< pqBind :: (IndexedMonadTransPQ pq, Monad m) => (x -> pq schema1 schema2 m y) -> pq schema0 schema1 m x -> pq schema0 schema2 m y -- | indexed analog of flipped >> pqThen :: (IndexedMonadTransPQ pq, Monad m) => pq schema1 schema2 m y -> pq schema0 schema1 m x -> pq schema0 schema2 m y -- | Safely embed a computation in a larger schema. pqEmbed :: (IndexedMonadTransPQ pq, Monad m) => pq schema0 schema1 m x -> pq (table : schema0) (table : schema1) m x -- | Run a Definition with exec, we expect that libpq obeys -- the law -- --
-- define statement1 & pqThen (define statement2) = define (statement1 >>> statement2) --define :: (IndexedMonadTransPQ pq, MonadBase IO io) => Definition schema0 schema1 -> pq schema0 schema1 io (K Result '[]) -- | MonadPQ is an mtl style constraint, similar to -- MonadState, for using LibPQ to -- --
-- >>> :set -XDataKinds -XDeriveGeneric -XOverloadedLabels -- -- >>> :set -XOverloadedStrings -XTypeApplications -XTypeOperators ---- -- We'll need some imports. -- --
-- >>> import Control.Monad (void) -- -- >>> import Control.Monad.Base (liftBase) -- -- >>> import Data.Int (Int32) -- -- >>> import Data.Text (Text) -- -- >>> import Squeal.PostgreSQL ---- -- We'll use generics to easily convert between Haskell and PostgreSQL -- values. -- --
-- >>> import qualified Generics.SOP as SOP -- -- >>> import qualified GHC.Generics as GHC ---- -- The first step is to define the schema of our database. This is where -- we use DataKinds and TypeOperators. -- --
-- >>> :{
-- type Schema =
-- '[ "users" :::
-- '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
-- '[ "id" ::: 'Def :=> 'NotNull 'PGint4
-- , "name" ::: 'NoDef :=> 'NotNull 'PGtext
-- ]
-- , "emails" :::
-- '[ "pk_emails" ::: 'PrimaryKey '["id"]
-- , "fk_user_id" ::: 'ForeignKey '["user_id"] "users" '["id"]
-- ] :=>
-- '[ "id" ::: 'Def :=> 'NotNull 'PGint4
-- , "user_id" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "email" ::: 'NoDef :=> 'Null 'PGtext
-- ]
-- ]
-- :}
--
--
-- Notice the use of type operators. ::: is used to pair an alias
-- Symbol with either a TableType or a ColumnType.
-- :=> is used to pair a TableConstraints with a
-- ColumnsType, yielding a TableType, or to pair a
-- ColumnConstraint with a NullityType, yielding a
-- ColumnType.
--
-- Next, we'll write Definitions to set up and tear down the
-- schema. In Squeal, a Definition is a createTable,
-- alterTable or dropTable command and has two type
-- parameters, corresponding to the schema before being run and the
-- schema after. We can compose definitions using >>>.
-- Here and in the rest of our commands we make use of overloaded labels
-- to refer to named tables and columns in our schema.
--
--
-- >>> :{
-- let
-- setup :: Definition '[] Schema
-- setup =
-- createTable #users
-- ( serial `As` #id :*
-- (text & notNull) `As` #name :* Nil )
-- ( primaryKey (Column #id :* Nil) `As` #pk_users :* Nil ) >>>
-- createTable #emails
-- ( serial `As` #id :*
-- (int & notNull) `As` #user_id :*
-- text `As` #email :* Nil )
-- ( primaryKey (Column #id :* Nil) `As` #pk_emails :*
-- foreignKey (Column #user_id :* Nil) #users (Column #id :* Nil)
-- OnDeleteCascade OnUpdateCascade `As` #fk_user_id :* Nil )
-- :}
--
--
-- We can easily see the generated SQL is unsuprising looking.
--
-- -- >>> renderDefinition setup -- "CREATE TABLE users (id serial, name text NOT NULL, CONSTRAINT pk_users PRIMARY KEY (id)); CREATE TABLE emails (id serial, user_id int NOT NULL, email text, CONSTRAINT pk_emails PRIMARY KEY (id), CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE);" ---- -- Notice that setup starts with an empty schema '[] -- and produces Schema. In our createTable commands we -- included TableConstraints to define primary and foreign keys, -- making them somewhat complex. Our tear down Definition is -- simpler. -- --
-- >>> :{
-- let
-- teardown :: Definition Schema '[]
-- teardown = dropTable #emails >>> dropTable #users
-- :}
--
--
-- -- >>> renderDefinition teardown -- "DROP TABLE emails; DROP TABLE users;" ---- -- Next, we'll write Manipulations to insert data into our two -- tables. A Manipulation is an insertRow (or other -- inserts), update or deleteFrom command and has three -- type parameters, the schema it refers to, a list of parameters it can -- take as input, and a list of columns it produces as output. When we -- insert into the users table, we will need a parameter for the -- name field but not for the id field. Since it's -- optional, we can use a default value. However, since the emails table -- refers to the users table, we will need to retrieve the user id that -- the insert generates and insert it into the emails table. Take a -- careful look at the type and definition of both of our inserts. -- --
-- >>> :{
-- let
-- insertUser :: Manipulation Schema '[ 'NotNull 'PGtext ] '[ "fromOnly" ::: 'NotNull 'PGint4 ]
-- insertUser = insertRow #users
-- (Default `As` #id :* Set (param @1) `As` #name :* Nil)
-- OnConflictDoNothing (Returning (#id `As` #fromOnly :* Nil))
-- :}
--
--
--
-- >>> :{
-- let
-- insertEmail :: Manipulation Schema '[ 'NotNull 'PGint4, 'Null 'PGtext] '[]
-- insertEmail = insertRow #emails
-- ( Default `As` #id :*
-- Set (param @1) `As` #user_id :*
-- Set (param @2) `As` #email :* Nil )
-- OnConflictDoNothing (Returning Nil)
-- :}
--
--
-- -- >>> renderManipulation insertUser -- "INSERT INTO users (id, name) VALUES (DEFAULT, ($1 :: text)) ON CONFLICT DO NOTHING RETURNING id AS fromOnly;" -- -- >>> renderManipulation insertEmail -- "INSERT INTO emails (id, user_id, email) VALUES (DEFAULT, ($1 :: int4), ($2 :: text)) ON CONFLICT DO NOTHING;" ---- -- Next we write a Query to retrieve users from the database. -- We're not interested in the ids here, just the usernames and email -- addresses. We need to use an inner join to get the right result. A -- Query is like a Manipulation with the same kind of type -- parameters. -- --
-- >>> :{
-- let
-- getUsers :: Query Schema '[]
-- '[ "userName" ::: 'NotNull 'PGtext
-- , "userEmail" ::: 'Null 'PGtext ]
-- getUsers = select
-- (#u ! #name `As` #userName :* #e ! #email `As` #userEmail :* Nil)
-- ( from (table (#users `As` #u)
-- & innerJoin (table (#emails `As` #e))
-- (#u ! #id .== #e ! #user_id)) )
-- :}
--
--
-- -- >>> renderQuery getUsers -- "SELECT u.name AS userName, e.email AS userEmail FROM users AS u INNER JOIN emails AS e ON (u.id = e.user_id)" ---- -- Now that we've defined the SQL side of things, we'll need a Haskell -- type for users. We give the type Generic and -- HasDatatypeInfo instances so that we can decode the rows we -- receive when we run getUsers. Notice that the record fields -- of the User type match the column names of getUsers. -- --
-- >>> data User = User { userName :: Text, userEmail :: Maybe Text } deriving (Show, GHC.Generic)
--
-- >>> instance SOP.Generic User
--
-- >>> instance SOP.HasDatatypeInfo User
--
--
-- Let's also create some users to add to the database.
--
--
-- >>> :{
-- let
-- users :: [User]
-- users =
-- [ User "Alice" (Just "alice@gmail.com")
-- , User "Bob" Nothing
-- , User "Carole" (Just "carole@hotmail.com")
-- ]
-- :}
--
--
-- Now we can put together all the pieces into a program. The program
-- connects to the database, sets up the schema, inserts the user data
-- (using prepared statements as an optimization), queries the user data
-- and prints it out and finally closes the connection. We can thread the
-- changing schema information through by using the indexed PQ
-- monad transformer and when the schema doesn't change we can use
-- Monad and MonadPQ functionality.
--
--
-- >>> :{
-- let
-- session :: PQ Schema Schema IO ()
-- session = do
-- idResults <- traversePrepared insertUser (Only . userName <$> users)
-- ids <- traverse (fmap fromOnly . getRow 0) idResults
-- traversePrepared_ insertEmail (zip (ids :: [Int32]) (userEmail <$> users))
-- usersResult <- runQuery getUsers
-- usersRows <- getRows usersResult
-- liftBase $ print (usersRows :: [User])
-- :}
--
--
--
-- >>> :{
-- void . withConnection "host=localhost port=5432 dbname=exampledb" $
-- define setup
-- & pqThen session
-- & pqThen (define teardown)
-- :}
-- [User {userName = "Alice", userEmail = Just "alice@gmail.com"},User {userName = "Bob", userEmail = Nothing},User {userName = "Carole", userEmail = Just "carole@hotmail.com"}]
--
module Squeal.PostgreSQL
-- | This module defines a Migration type to safely change the
-- schema of your database over time. Let's see an example!
--
--
-- >>> :set -XDataKinds -XOverloadedLabels
--
-- >>> :set -XOverloadedStrings -XFlexibleContexts -XTypeOperators
--
-- >>> :{
-- type UsersTable =
-- '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
-- '[ "id" ::: 'Def :=> 'NotNull 'PGint4
-- , "name" ::: 'NoDef :=> 'NotNull 'PGtext
-- ]
-- :}
--
--
--
-- >>> :{
-- type EmailsTable =
-- '[ "pk_emails" ::: 'PrimaryKey '["id"]
-- , "fk_user_id" ::: 'ForeignKey '["user_id"] "users" '["id"]
-- ] :=>
-- '[ "id" ::: 'Def :=> 'NotNull 'PGint4
-- , "user_id" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "email" ::: 'NoDef :=> 'Null 'PGtext
-- ]
-- :}
--
--
--
-- >>> :{
-- let
-- makeUsers :: Migration IO '[] '["users" ::: UsersTable]
-- makeUsers = Migration
-- { name = "make users table"
-- , up = void . define $
-- createTable #users
-- ( serial `As` #id :*
-- (text & notNull) `As` #name :* Nil )
-- ( primaryKey (Column #id :* Nil) `As` #pk_users :* Nil )
-- , down = void . define $ dropTable #users
-- }
-- :}
--
--
--
-- >>> :{
-- let
-- makeEmails :: Migration IO '["users" ::: UsersTable]
-- '["users" ::: UsersTable, "emails" ::: EmailsTable]
-- makeEmails = Migration
-- { name = "make emails table"
-- , up = void . define $
-- createTable #emails
-- ( serial `As` #id :*
-- (int & notNull) `As` #user_id :*
-- text `As` #email :* Nil )
-- ( primaryKey (Column #id :* Nil) `As` #pk_emails :*
-- foreignKey (Column #user_id :* Nil) #users (Column #id :* Nil)
-- OnDeleteCascade OnUpdateCascade `As` #fk_user_id :* Nil )
-- , down = void . define $ dropTable #emails
-- }
-- :}
--
--
-- Now that we have a couple migrations we can chain them together.
--
-- -- >>> let migrations = makeUsers :>> makeEmails :>> Done ---- --
-- >>> :{
-- let
-- numMigrations
-- :: Has "schema_migrations" schema MigrationsTable
-- => PQ schema schema IO ()
-- numMigrations = do
-- result <- runQuery (selectStar (from (table (#schema_migrations `As` #m))))
-- num <- ntuples result
-- liftBase $ print num
-- :}
--
--
--
-- >>> :{
-- withConnection "host=localhost port=5432 dbname=exampledb" $
-- manipulate (UnsafeManipulation "SET client_min_messages TO WARNING;")
-- -- suppress notices
-- & pqThen (migrateUp migrations)
-- & pqThen numMigrations
-- & pqThen (migrateDown migrations)
-- & pqThen numMigrations
-- :}
-- Row 2
-- Row 0
--
module Squeal.PostgreSQL.Migration
-- | A Migration should contain an inverse pair of up and
-- down instructions and a unique name.
data Migration io schema0 schema1
Migration :: Text -> PQ schema0 schema1 io () -> PQ schema1 schema0 io () -> Migration io schema0 schema1
-- | The name of a Migration. Each name in a
-- Migration should be unique.
[name] :: Migration io schema0 schema1 -> Text
-- | The up instruction of a Migration.
[up] :: Migration io schema0 schema1 -> PQ schema0 schema1 io ()
-- | The down instruction of a Migration.
[down] :: Migration io schema0 schema1 -> PQ schema1 schema0 io ()
-- | Run Migrations by creating the MigrationsTable if it
-- does not exist and then in a transaction, for each each
-- Migration query to see if the Migration is executed. If
-- not, then execute the Migration and insert its row in the
-- MigrationsTable.
migrateUp :: MonadBaseControl IO io => AlignedList (Migration io) schema0 schema1 -> PQ (("schema_migrations" ::: MigrationsTable) : schema0) (("schema_migrations" ::: MigrationsTable) : schema1) io ()
-- | Rewind Migrations by creating the MigrationsTable if it
-- does not exist and then in a transaction, for each each
-- Migration query to see if the Migration is executed. If
-- it is, then rewind the Migration and delete its row in the
-- MigrationsTable.
migrateDown :: MonadBaseControl IO io => AlignedList (Migration io) schema0 schema1 -> PQ (("schema_migrations" ::: MigrationsTable) : schema1) (("schema_migrations" ::: MigrationsTable) : schema0) io ()
-- | An AlignedList is a type-aligned list or free category.
data AlignedList p x0 x1
[Done] :: AlignedList p x x
[:>>] :: p x0 x1 -> AlignedList p x1 x2 -> AlignedList p x0 x2
-- | A single step.
single :: p x0 x1 -> AlignedList p x0 x1
-- | The TableType for a Squeal migration.
type MigrationsTable = '["migrations_unique_name" ::: 'Unique '["name"]] :=> '["name" ::: ( 'NoDef :=> 'NotNull 'PGtext), "executed_at" ::: ( 'Def :=> 'NotNull 'PGtimestamptz)]
-- | Creates a MigrationsTable if it does not already exist.
createMigrations :: Has "schema_migrations" schema MigrationsTable => Definition schema schema
-- | Inserts a Migration into the MigrationsTable
insertMigration :: Has "schema_migrations" schema MigrationsTable => Manipulation schema '[ 'NotNull 'PGtext] '[]
-- | Deletes a Migration from the MigrationsTable
deleteMigration :: Has "schema_migrations" schema MigrationsTable => Manipulation schema '[ 'NotNull 'PGtext] '[]
-- | Selects a Migration from the MigrationsTable, returning
-- the time at which it was executed.
selectMigration :: Has "schema_migrations" schema MigrationsTable => Query schema '[ 'NotNull 'PGtext] '["executed_at" ::: 'NotNull 'PGtimestamptz]
instance forall k (p :: k -> k -> *). Control.Category.Category k (Squeal.PostgreSQL.Migration.AlignedList k p)