-- 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.3.0.0 -- | 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 -- | Add double quotes around a ByteString. doubleQuoted :: 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 class for rendering SQL class RenderSQL sql renderSQL :: RenderSQL sql => sql -> ByteString -- | Print SQL. printSQL :: (RenderSQL sql, MonadBase IO io) => sql -> io () -- | This module provides a type-level DSL for kinds of Postgres types, -- tables, schema, constraints, aliases, enumerated labels, and -- groupings. It also defines useful type families to operate on these. -- Finally, it defines an embedding of Haskell types into Postgres types. 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 PGenum :: [Symbol] -> PGType PGcomposite :: [(Symbol, 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)
-- | A SchemumType is a user-defined type, either a Table,
-- View or Typedef.
data SchemumType
Table :: TableType -> SchemumType
View :: RelationType -> SchemumType
Typedef :: PGType -> SchemumType
-- | The schema of a database consists of a list of aliased, user-defined
-- SchemumTypes.
type SchemaType = [(Symbol, SchemumType)]
-- | 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.
-- | A ForeignKey must reference columns that either are a
-- PrimaryKey or form a Unique constraint.
-- | The alias operator ::: is like a promoted version of As,
-- a type level pair between an alias and some type.
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 -- |
-- >>> import Generics.SOP (NP(..)) -- -- >>> renderAliases (#jimbob :* #kandi :* Nil) -- ["\"jimbob\"","\"kandi\""] --renderAliases :: All KnownSymbol aliases => NP Alias aliases -> [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. -- | The ZipAliased class provides a type family for zipping -- Symbol lists together with arbitrary lists of the same size, -- with an associated type family ZipAs, together with a method -- zipAs for zipping heterogeneous lists of Aliases -- together with a heterogeneous list of expressions into a heterogeneous -- list of Aliased expressions. class (SListI (ZipAs ns xs), All KnownSymbol ns) => ZipAliased ns xs where { type family ZipAs (ns :: [Symbol]) (xs :: [k]) = (zs :: [(Symbol, k)]) | zs -> ns xs; } zipAs :: ZipAliased ns xs => NP Alias ns -> NP expr xs -> NP (Aliased expr) (ZipAs ns xs) -- | Has alias fields field is a constraint that proves that -- fields has a field of alias ::: field, inferring -- field from alias and fields. 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] -- | HasAll extends Has to take lists of aliases and -- fields and infer a list of subfields. class (All KnownSymbol aliases) => HasAll (aliases :: [Symbol]) (fields :: [(Symbol, kind)]) (subfields :: [(Symbol, kind)]) | aliases fields -> subfields 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 -- | IsPGlabel looks very much like the IsLabel class. -- Whereas the overloaded label, fromLabel is used for column -- references, labels are used for enum terms. A label is -- called with type application like `label @"beef"`. class IsPGlabel (label :: Symbol) expr label :: IsPGlabel label expr => expr -- | A PGlabel unit type with an IsPGlabel instance data PGlabel (label :: Symbol) PGlabel :: PGlabel -- | Renders a label renderLabel :: KnownSymbol label => proxy label -> ByteString -- | Renders a list of labels renderLabels :: All KnownSymbol labels => NP PGlabel labels -> [ByteString] -- | 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 -- | Join is simply promoted ++ and is used in JOINs -- in FromClauses. -- | Used in with. -- | 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 x xs 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. -- | Equality constraint on the underlying PGType of two columns. class SamePGType (ty0 :: (Symbol, ColumnType)) (ty1 :: (Symbol, ColumnType)) -- | 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. -- | TableToColumns removes table constraints. -- | Convert a table to a relation. -- | Check if a TableConstraint involves a column -- | Drop all TableConstraints that involve a column -- | The PG type family embeds a subset of Haskell types as Postgres -- basic types. -- --
-- >>> :kind! PG LocalTime -- PG LocalTime :: PGType -- = 'PGtimestamp ---- | The EnumFrom type family embeds Haskell enum types, ADTs with -- nullary constructors, as Postgres enum types -- --
-- >>> data Schwarma = Beef | Lamb | Chicken deriving GHC.Generic -- -- >>> instance Generic Schwarma -- -- >>> instance HasDatatypeInfo Schwarma -- -- >>> :kind! EnumFrom Schwarma -- EnumFrom Schwarma :: PGType -- = 'PGenum '["Beef", "Lamb", "Chicken"] ---- | The LabelsFrom type family calculates the constructors of a -- Haskell enum type. -- --
-- >>> data Schwarma = Beef | Lamb | Chicken deriving GHC.Generic -- -- >>> instance Generic Schwarma -- -- >>> instance HasDatatypeInfo Schwarma -- -- >>> :kind! LabelsFrom Schwarma -- LabelsFrom Schwarma :: [Type.ConstructorName] -- = '["Beef", "Lamb", "Chicken"] ---- | The CompositeFrom type family embeds Haskell record types as -- Postgres composite types, as long as the record fields are -- Maybes of Haskell types that can be embedded as basic types -- with the PG type family. -- --
-- >>> data Row = Row { a :: Maybe Int16, b :: Maybe LocalTime } deriving GHC.Generic
--
-- >>> instance Generic Row
--
-- >>> instance HasDatatypeInfo Row
--
-- >>> :kind! CompositeFrom Row
-- CompositeFrom Row :: PGType
-- = 'PGcomposite '['("a", 'PGint2), '("b", 'PGtimestamp)]
--
-- |
-- >>> data Row = Row { a :: Maybe Int16, b :: Maybe LocalTime } deriving GHC.Generic
--
-- >>> instance Generic Row
--
-- >>> instance HasDatatypeInfo Row
--
-- >>> :kind! FieldNamesFrom Row
-- FieldNamesFrom Row :: [Type.FieldName]
-- = '["a", "b"]
--
-- |
-- >>> data Row = Row { a :: Maybe Int16, b :: Maybe LocalTime } deriving GHC.Generic
--
-- >>> instance Generic Row
--
-- >>> instance HasDatatypeInfo Row
--
-- >>> :kind! FieldTypesFrom Row
-- FieldTypesFrom Row :: [PGType]
-- = '['PGint2, 'PGtimestamp]
--
-- | Calculates constructors of a datatype.
-- | Calculates the name of a nullary constructor, otherwise generates a
-- type error.
-- | Calculate the names of nullary constructors.
-- | Calculate the fields of a datatype.
-- | Calculate the name of a field.
-- | Calculate the names of fields.
-- | -- >>> :kind! FieldTypeOf (Maybe Int16) -- FieldTypeOf (Maybe Int16) :: PGType -- = 'PGint2 ---- | Calculate the types of fields. -- | Inspect the code of an algebraic datatype and ensure it's a product, -- otherwise generate a type error -- | MapMaybes is used in the binary instances of composite types. class MapMaybes xs where { type family Maybes (xs :: [Type]) = (mxs :: [Type]) | mxs -> xs; } maybes :: MapMaybes xs => NP Maybe xs -> NP I (Maybes xs) unMaybes :: MapMaybes xs => NP I (Maybes xs) -> NP Maybe xs -- | Nulls is used to construct a row of a composite type. 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 expression (alias Squeal.PostgreSQL.Schema.::: ty)) instance forall k (expression :: k -> *) (ty :: k) (alias :: GHC.Types.Symbol). GHC.Classes.Eq (expression ty) => GHC.Classes.Eq (Squeal.PostgreSQL.Schema.Aliased expression (alias Squeal.PostgreSQL.Schema.::: ty)) instance forall k (expression :: k -> *) (ty :: k) (alias :: GHC.Types.Symbol). GHC.Classes.Ord (expression ty) => GHC.Classes.Ord (Squeal.PostgreSQL.Schema.Aliased expression (alias Squeal.PostgreSQL.Schema.::: ty)) instance label ~ label' => Squeal.PostgreSQL.Schema.IsPGlabel label (Squeal.PostgreSQL.Schema.PGlabel label') instance Squeal.PostgreSQL.Schema.MapMaybes '[] instance Squeal.PostgreSQL.Schema.MapMaybes xs => Squeal.PostgreSQL.Schema.MapMaybes (x : xs) instance forall k (ty0 :: k) (ty1 :: k) (alias0 :: GHC.Types.Symbol) (def0 :: Squeal.PostgreSQL.Schema.ColumnConstraint) (nullity0 :: k -> Squeal.PostgreSQL.Schema.NullityType) (alias1 :: GHC.Types.Symbol) (def1 :: Squeal.PostgreSQL.Schema.ColumnConstraint) (nullity1 :: k -> Squeal.PostgreSQL.Schema.NullityType). ty0 ~ ty1 => Squeal.PostgreSQL.Schema.SamePGType (alias0 Squeal.PostgreSQL.Schema.::: (def0 Squeal.PostgreSQL.Schema.:=> nullity0 ty0)) (alias1 Squeal.PostgreSQL.Schema.::: (def1 Squeal.PostgreSQL.Schema.:=> nullity1 ty1)) instance Squeal.PostgreSQL.Schema.IsQualified table column (Squeal.PostgreSQL.Schema.Alias table, Squeal.PostgreSQL.Schema.Alias column) instance forall kind (fields :: [(GHC.Types.Symbol, kind)]). Squeal.PostgreSQL.Schema.HasAll '[] fields '[] instance forall k (alias :: GHC.Types.Symbol) (fields :: [(GHC.Types.Symbol, k)]) (field :: k) (aliases :: [GHC.Types.Symbol]) (subfields :: [(GHC.Types.Symbol, k)]). (Squeal.PostgreSQL.Schema.Has alias fields field, Squeal.PostgreSQL.Schema.HasAll aliases fields subfields) => Squeal.PostgreSQL.Schema.HasAll (alias : aliases) fields ((alias Squeal.PostgreSQL.Schema.::: field) : subfields) instance forall kind (alias :: GHC.Types.Symbol) (field :: kind) (fields :: [(GHC.Types.Symbol, kind)]). GHC.TypeLits.KnownSymbol alias => Squeal.PostgreSQL.Schema.Has alias ((alias Squeal.PostgreSQL.Schema.::: 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 alias fields field) => Squeal.PostgreSQL.Schema.Has alias (field' : fields) field instance Squeal.PostgreSQL.Schema.ZipAliased '[] '[] instance forall k (n :: GHC.Types.Symbol) (ns :: [GHC.Types.Symbol]) (xs :: [k]) (x :: k). (GHC.TypeLits.KnownSymbol n, Squeal.PostgreSQL.Schema.ZipAliased ns xs) => Squeal.PostgreSQL.Schema.ZipAliased (n : ns) (x : xs) instance (alias0 ~ alias1, alias0 ~ alias2, GHC.TypeLits.KnownSymbol alias2) => GHC.OverloadedLabels.IsLabel alias0 (Squeal.PostgreSQL.Schema.Aliased Squeal.PostgreSQL.Schema.Alias (alias1 Squeal.PostgreSQL.Schema.::: alias2)) instance alias1 ~ alias2 => GHC.OverloadedLabels.IsLabel alias1 (Squeal.PostgreSQL.Schema.Alias alias2) instance aliases ~ '[alias] => GHC.OverloadedLabels.IsLabel alias (Generics.SOP.NP.NP Squeal.PostgreSQL.Schema.Alias aliases) instance GHC.TypeLits.KnownSymbol alias => Squeal.PostgreSQL.Render.RenderSQL (Squeal.PostgreSQL.Schema.Alias alias) 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 relation column ('(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 relation column bys) => Squeal.PostgreSQL.Schema.GroupedBy relation column (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 -- insertRow 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 (schema :: SchemaType) (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 KnownNat n => HasParameter (n :: Nat) (schema :: SchemaType) (params :: [NullityType]) (ty :: NullityType) | n params -> ty -- | parameter takes a Nat using type application and a -- TypeExpression. -- --
-- >>> let expr = parameter @1 int4 :: Expression sch rels grp '[ 'Null 'PGint4] ('Null 'PGint4)
--
-- >>> printSQL expr
-- ($1 :: int4)
--
parameter :: HasParameter n schema params ty => TypeExpression schema (PGTypeOf ty) -> Expression schema relations grouping params ty
-- | param takes a Nat using type application and for basic
-- types, infers a TypeExpression.
--
--
-- >>> let expr = param @1 :: Expression sch rels grp '[ 'Null 'PGint4] ('Null 'PGint4)
--
-- >>> printSQL expr
-- ($1 :: int4)
--
param :: forall n schema params relations grouping ty. (PGTyped schema (PGTypeOf ty), HasParameter n schema params ty) => Expression schema relations grouping params ty
-- | analagous to Nothing
--
-- -- >>> printSQL null_ -- NULL --null_ :: Expression schema rels grouping params ( 'Null ty) -- | analagous to Just -- --
-- >>> printSQL $ notNull true -- TRUE --notNull :: Expression schema rels grouping params ( 'NotNull ty) -> Expression schema rels grouping params ( 'Null ty) -- | return the leftmost value which is not NULL -- --
-- >>> printSQL $ coalesce [null_, notNull true] false -- COALESCE(NULL, TRUE, FALSE) --coalesce :: [Expression schema relations grouping params ( 'Null ty)] -> Expression schema relations grouping params ( 'NotNull ty) -> Expression schema relations grouping params ( 'NotNull ty) -- | analagous to fromMaybe using COALESCE -- --
-- >>> printSQL $ fromNull true null_ -- COALESCE(NULL, TRUE) --fromNull :: Expression schema relations grouping params ( 'NotNull ty) -> Expression schema relations grouping params ( 'Null ty) -> Expression schema relations grouping params ( 'NotNull ty) -- |
-- >>> printSQL $ null_ & isNull -- NULL IS NULL --isNull :: Expression schema relations grouping params ( 'Null ty) -> Condition schema relations grouping params -- |
-- >>> printSQL $ null_ & isNotNull -- NULL IS NOT NULL --isNotNull :: Expression schema relations grouping params ( 'Null ty) -> Condition schema relations grouping params -- | analagous to maybe using IS NULL -- --
-- >>> printSQL $ matchNull true not_ null_ -- CASE WHEN NULL IS NULL THEN TRUE ELSE (NOT NULL) END --matchNull :: Expression schema relations grouping params (nullty) -> (Expression schema relations grouping params ( 'NotNull ty) -> Expression schema relations grouping params (nullty)) -> Expression schema relations grouping params ( 'Null ty) -> Expression schema relations grouping params (nullty) -- | right inverse to fromNull, if its arguments are equal then -- nullIf gives NULL. -- --
-- >>> :set -XTypeApplications -XDataKinds
--
-- >>> let expr = nullIf false (param @1) :: Expression schema rels grp '[ 'NotNull 'PGbool] ('Null 'PGbool)
--
-- >>> printSQL expr
-- NULL IF (FALSE, ($1 :: bool))
--
nullIf :: Expression schema relations grouping params ( 'NotNull ty) -> Expression schema relations grouping params ( 'NotNull ty) -> Expression schema relations grouping params ( 'Null ty)
-- | -- >>> printSQL $ array [null_, notNull false, notNull true] -- ARRAY[NULL, FALSE, TRUE] --array :: [Expression schema relations grouping params ( 'Null ty)] -> Expression schema relations grouping params (nullity ( 'PGvararray ty)) -- | A row constructor is an expression that builds a row value (also -- called a composite value) using values for its member fields. -- --
-- >>> type Complex = PGcomposite '["real" ::: 'PGfloat8, "imaginary" ::: 'PGfloat8]
--
-- >>> let i = row (0 `As` #real :* 1 `As` #imaginary :* Nil) :: Expression '[] '[] 'Ungrouped '[] ('NotNull Complex)
--
-- >>> printSQL i
-- ROW(0, 1)
--
row :: SListI (Nulls fields) => NP (Aliased (Expression schema relations grouping params)) (Nulls fields) -> Expression schema relations grouping params (nullity ( 'PGcomposite fields))
-- | -- >>> printSQL $ unsafeBinaryOp "OR" true false -- (TRUE OR FALSE) --unsafeBinaryOp :: ByteString -> Expression schema relations grouping params (ty0) -> Expression schema relations grouping params (ty1) -> Expression schema relations grouping params (ty2) -- |
-- >>> printSQL $ unsafeUnaryOp "NOT" true -- (NOT TRUE) --unsafeUnaryOp :: ByteString -> Expression schema relations grouping params (ty0) -> Expression schema relations grouping params (ty1) -- |
-- >>> printSQL $ unsafeFunction "f" true -- f(TRUE) --unsafeFunction :: ByteString -> Expression schema relations grouping params (xty) -> Expression schema relations grouping params (yty) -- |
-- >>> :{
-- let
-- expression :: Expression schema relations grouping params (nullity 'PGfloat4)
-- expression = atan2_ pi 2
-- in printSQL expression
-- :}
-- atan2(pi(), 2)
--
atan2_ :: PGFloating float => Expression schema relations grouping params (nullity float) -> Expression schema relations grouping params (nullity float) -> Expression schema relations grouping params (nullity float)
-- | -- >>> printSQL $ true & cast int4 -- (TRUE :: int4) --cast :: TypeExpression schema ty1 -> Expression schema relations grouping params (nullity ty0) -> Expression schema relations grouping params (nullity ty1) -- | integer division, truncates the result -- --
-- >>> :{
-- let
-- expression :: Expression schema relations grouping params (nullity 'PGint2)
-- expression = 5 `quot_` 2
-- in printSQL expression
-- :}
-- (5 / 2)
--
quot_ :: PGIntegral int => Expression schema relations grouping params (nullity int) -> Expression schema relations grouping params (nullity int) -> Expression schema relations grouping params (nullity int)
-- | remainder upon integer division
--
--
-- >>> :{
-- let
-- expression :: Expression schema relations grouping params (nullity 'PGint2)
-- expression = 5 `rem_` 2
-- in printSQL expression
-- :}
-- (5 % 2)
--
rem_ :: PGIntegral int => Expression schema relations grouping params (nullity int) -> Expression schema relations grouping params (nullity int) -> Expression schema relations grouping params (nullity int)
-- |
-- >>> :{
-- let
-- expression :: Expression schema relations grouping params (nullity 'PGfloat4)
-- expression = trunc pi
-- in printSQL expression
-- :}
-- trunc(pi())
--
trunc :: PGFloating frac => Expression schema relations grouping params (nullity frac) -> Expression schema relations grouping params (nullity frac)
-- |
-- >>> :{
-- let
-- expression :: Expression schema relations grouping params (nullity 'PGfloat4)
-- expression = round_ pi
-- in printSQL expression
-- :}
-- round(pi())
--
round_ :: PGFloating frac => Expression schema relations grouping params (nullity frac) -> Expression schema relations grouping params (nullity frac)
-- |
-- >>> :{
-- let
-- expression :: Expression schema relations grouping params (nullity 'PGfloat4)
-- expression = ceiling_ pi
-- in printSQL expression
-- :}
-- ceiling(pi())
--
ceiling_ :: PGFloating frac => Expression schema relations grouping params (nullity frac) -> Expression schema relations grouping params (nullity frac)
-- |
-- >>> let expr = greatest currentTimestamp [param @1] :: Expression sch rels grp '[ 'NotNull 'PGtimestamptz] ('NotNull 'PGtimestamptz)
--
-- >>> printSQL expr
-- GREATEST(CURRENT_TIMESTAMP, ($1 :: timestamp with time zone))
--
greatest :: Expression schema relations grouping params (nullty) -> [Expression schema relations grouping params (nullty)] -> Expression schema relations grouping params (nullty)
-- | -- >>> printSQL $ least currentTimestamp [null_] -- LEAST(CURRENT_TIMESTAMP, NULL) --least :: Expression schema relations grouping params (nullty) -> [Expression schema relations grouping params (nullty)] -> Expression schema 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 schema relations grouping params = Expression schema relations grouping params ( 'NotNull 'PGbool) -- |
-- >>> printSQL true -- TRUE --true :: Condition schema relations grouping params -- |
-- >>> printSQL false -- FALSE --false :: Condition schema relations grouping params -- |
-- >>> printSQL $ not_ true -- (NOT TRUE) --not_ :: Condition schema relations grouping params -> Condition schema relations grouping params -- |
-- >>> printSQL $ true .&& false -- (TRUE AND FALSE) --(.&&) :: Condition schema relations grouping params -> Condition schema relations grouping params -> Condition schema relations grouping params -- |
-- >>> printSQL $ true .|| false -- (TRUE OR FALSE) --(.||) :: Condition schema relations grouping params -> Condition schema relations grouping params -> Condition schema relations grouping params -- |
-- >>> :{
-- let
-- expression :: Expression schema relations grouping params (nullity 'PGint2)
-- expression = caseWhenThenElse [(true, 1), (false, 2)] 3
-- in printSQL expression
-- :}
-- CASE WHEN TRUE THEN 1 WHEN FALSE THEN 2 ELSE 3 END
--
caseWhenThenElse :: [(Condition schema relations grouping params, Expression schema relations grouping params (ty))] -> Expression schema relations grouping params (ty) -> Expression schema relations grouping params (ty)
-- |
-- >>> :{
-- let
-- expression :: Expression schema relations grouping params (nullity 'PGint2)
-- expression = ifThenElse true 1 0
-- in printSQL expression
-- :}
-- CASE WHEN TRUE THEN 1 ELSE 0 END
--
ifThenElse :: Condition schema relations grouping params -> Expression schema relations grouping params (ty) -> Expression schema relations grouping params (ty) -> Expression schema relations grouping params (ty)
-- | Comparison operations like .==, ./=, .>,
-- .>=, .< and .<= will produce
-- NULLs if one of their arguments is NULL.
--
-- -- >>> printSQL $ notNull true .== null_ -- (TRUE = NULL) --(.==) :: Expression schema relations grouping params (nullity ty) -> Expression schema relations grouping params (nullity ty) -> Expression schema relations grouping params (nullity 'PGbool) infix 4 .== -- |
-- >>> printSQL $ notNull true ./= null_ -- (TRUE <> NULL) --(./=) :: Expression schema relations grouping params (nullity ty) -> Expression schema relations grouping params (nullity ty) -> Expression schema relations grouping params (nullity 'PGbool) infix 4 ./= -- |
-- >>> printSQL $ notNull true .>= null_ -- (TRUE >= NULL) --(.>=) :: Expression schema relations grouping params (nullity ty) -> Expression schema relations grouping params (nullity ty) -> Expression schema relations grouping params (nullity 'PGbool) infix 4 .>= -- |
-- >>> printSQL $ notNull true .< null_ -- (TRUE < NULL) --(.<) :: Expression schema relations grouping params (nullity ty) -> Expression schema relations grouping params (nullity ty) -> Expression schema relations grouping params (nullity 'PGbool) infix 4 .< -- |
-- >>> printSQL $ notNull true .<= null_ -- (TRUE <= NULL) --(.<=) :: Expression schema relations grouping params (nullity ty) -> Expression schema relations grouping params (nullity ty) -> Expression schema relations grouping params (nullity 'PGbool) infix 4 .<= -- |
-- >>> printSQL $ notNull true .> null_ -- (TRUE > NULL) --(.>) :: Expression schema relations grouping params (nullity ty) -> Expression schema relations grouping params (nullity ty) -> Expression schema relations grouping params (nullity 'PGbool) infix 4 .> -- |
-- >>> printSQL currentDate -- CURRENT_DATE --currentDate :: Expression schema relations grouping params (nullity 'PGdate) -- |
-- >>> printSQL currentTime -- CURRENT_TIME --currentTime :: Expression schema relations grouping params (nullity 'PGtimetz) -- |
-- >>> printSQL currentTimestamp -- CURRENT_TIMESTAMP --currentTimestamp :: Expression schema relations grouping params (nullity 'PGtimestamptz) -- |
-- >>> printSQL localTime -- LOCALTIME --localTime :: Expression schema relations grouping params (nullity 'PGtime) -- |
-- >>> printSQL localTimestamp -- LOCALTIMESTAMP --localTimestamp :: Expression schema relations grouping params (nullity 'PGtimestamp) -- |
-- >>> printSQL $ lower "ARRRGGG" -- lower(E'ARRRGGG') --lower :: Expression schema relations grouping params (nullity 'PGtext) -> Expression schema relations grouping params (nullity 'PGtext) -- |
-- >>> printSQL $ upper "eeee" -- upper(E'eeee') --upper :: Expression schema relations grouping params (nullity 'PGtext) -> Expression schema relations grouping params (nullity 'PGtext) -- |
-- >>> printSQL $ charLength "four" -- char_length(E'four') --charLength :: Expression schema relations grouping params (nullity 'PGtext) -> Expression schema 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. -- --
-- >>> printSQL $ "abc" `like` "a%" -- (E'abc' LIKE E'a%') --like :: Expression schema relations grouping params (nullity 'PGtext) -> Expression schema relations grouping params (nullity 'PGtext) -> Expression schema relations grouping params (nullity 'PGbool) -- | escape hatch to define aggregate functions unsafeAggregate :: ByteString -> Expression schema relations 'Ungrouped params (xty) -> Expression schema relations ( 'Grouped bys) params (yty) -- | escape hatch to define aggregate functions over distinct values unsafeAggregateDistinct :: ByteString -> Expression schema relations 'Ungrouped params (xty) -> Expression schema relations ( 'Grouped bys) params (yty) -- |
-- >>> :{
-- let
-- expression :: Expression schema '[tab ::: '["col" ::: 'Null 'PGnumeric]] ('Grouped bys) params ('Null 'PGnumeric)
-- expression = sum_ #col
-- in printSQL expression
-- :}
-- sum("col")
--
sum_ :: PGNum ty => Expression schema relations 'Ungrouped params (nullity ty) -> Expression schema relations ( 'Grouped bys) params (nullity ty)
-- |
-- >>> :{
-- let
-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGnumeric]] ('Grouped bys) params (nullity 'PGnumeric)
-- expression = sumDistinct #col
-- in printSQL expression
-- :}
-- sum(DISTINCT "col")
--
sumDistinct :: PGNum ty => Expression schema relations 'Ungrouped params (nullity ty) -> Expression schema 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 schema relations 'Ungrouped params (nullity ty) -> Expression schema relations ( 'Grouped bys) params (nullity avg)
avg, avgDistinct :: PGAvg ty avg => Expression schema relations 'Ungrouped params (nullity ty) -> Expression schema relations ( 'Grouped bys) params (nullity avg)
-- |
-- >>> :{
-- let
-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
-- expression = bitAnd #col
-- in printSQL expression
-- :}
-- bit_and("col")
--
bitAnd :: PGIntegral int => Expression schema relations 'Ungrouped params (nullity int) -> Expression schema relations ( 'Grouped bys) params (nullity int)
-- |
-- >>> :{
-- let
-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
-- expression = bitOr #col
-- in printSQL expression
-- :}
-- bit_or("col")
--
bitOr :: PGIntegral int => Expression schema relations 'Ungrouped params (nullity int) -> Expression schema relations ( 'Grouped bys) params (nullity int)
-- |
-- >>> :{
-- let
-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
-- expression = boolAnd #col
-- in printSQL expression
-- :}
-- bool_and("col")
--
boolAnd :: Expression schema relations 'Ungrouped params (nullity 'PGbool) -> Expression schema relations ( 'Grouped bys) params (nullity 'PGbool)
-- |
-- >>> :{
-- let
-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
-- expression = boolOr #col
-- in printSQL expression
-- :}
-- bool_or("col")
--
boolOr :: Expression schema relations 'Ungrouped params (nullity 'PGbool) -> Expression schema relations ( 'Grouped bys) params (nullity 'PGbool)
-- |
-- >>> :{
-- let
-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
-- expression = bitAndDistinct #col
-- in printSQL expression
-- :}
-- bit_and(DISTINCT "col")
--
bitAndDistinct :: PGIntegral int => Expression schema relations 'Ungrouped params (nullity int) -> Expression schema relations ( 'Grouped bys) params (nullity int)
-- |
-- >>> :{
-- let
-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
-- expression = bitOrDistinct #col
-- in printSQL expression
-- :}
-- bit_or(DISTINCT "col")
--
bitOrDistinct :: PGIntegral int => Expression schema relations 'Ungrouped params (nullity int) -> Expression schema relations ( 'Grouped bys) params (nullity int)
-- |
-- >>> :{
-- let
-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
-- expression = boolAndDistinct #col
-- in printSQL expression
-- :}
-- bool_and(DISTINCT "col")
--
boolAndDistinct :: Expression schema relations 'Ungrouped params (nullity 'PGbool) -> Expression schema relations ( 'Grouped bys) params (nullity 'PGbool)
-- |
-- >>> :{
-- let
-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
-- expression = boolOrDistinct #col
-- in printSQL expression
-- :}
-- bool_or(DISTINCT "col")
--
boolOrDistinct :: Expression schema relations 'Ungrouped params (nullity 'PGbool) -> Expression schema relations ( 'Grouped bys) params (nullity 'PGbool)
-- | A special aggregation that does not require an input
--
-- -- >>> printSQL countStar -- count(*) --countStar :: Expression schema relations ( 'Grouped bys) params ( 'NotNull 'PGint8) -- |
-- >>> :{
-- let
-- expression :: Expression schema '[tab ::: '["col" ::: nullity ty]] (Grouped bys) params ('NotNull 'PGint8)
-- expression = count #col
-- in printSQL expression
-- :}
-- count("col")
--
count :: Expression schema relations 'Ungrouped params ty -> Expression schema relations ( 'Grouped bys) params ( 'NotNull 'PGint8)
-- |
-- >>> :{
-- let
-- expression :: Expression schema '[tab ::: '["col" ::: nullity ty]] (Grouped bys) params ('NotNull 'PGint8)
-- expression = countDistinct #col
-- in printSQL expression
-- :}
-- count(DISTINCT "col")
--
countDistinct :: Expression schema relations 'Ungrouped params ty -> Expression schema relations ( 'Grouped bys) params ( 'NotNull 'PGint8)
-- | synonym for boolAnd
--
--
-- >>> :{
-- let
-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
-- expression = every #col
-- in printSQL expression
-- :}
-- every("col")
--
every :: Expression schema relations 'Ungrouped params (nullity 'PGbool) -> Expression schema relations ( 'Grouped bys) params (nullity 'PGbool)
-- | synonym for boolAndDistinct
--
--
-- >>> :{
-- let
-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
-- expression = everyDistinct #col
-- in printSQL expression
-- :}
-- every(DISTINCT "col")
--
everyDistinct :: Expression schema relations 'Ungrouped params (nullity 'PGbool) -> Expression schema relations ( 'Grouped bys) params (nullity 'PGbool)
-- | minimum and maximum aggregation
max_ :: Expression schema relations 'Ungrouped params (nullity ty) -> Expression schema relations ( 'Grouped bys) params (nullity ty)
-- | minimum and maximum aggregation
maxDistinct :: Expression schema relations 'Ungrouped params (nullity ty) -> Expression schema relations ( 'Grouped bys) params (nullity ty)
-- | minimum and maximum aggregation
min_ :: Expression schema relations 'Ungrouped params (nullity ty) -> Expression schema relations ( 'Grouped bys) params (nullity ty)
-- | minimum and maximum aggregation
minDistinct :: Expression schema relations 'Ungrouped params (nullity ty) -> Expression schema relations ( 'Grouped bys) params (nullity ty)
-- | TypeExpressions are used in casts and
-- createTable commands.
newtype TypeExpression (schema :: SchemaType) (ty :: PGType)
UnsafeTypeExpression :: ByteString -> TypeExpression
[renderTypeExpression] :: TypeExpression -> ByteString
-- | pgtype is a demoted version of a PGType
class PGTyped schema (ty :: PGType)
pgtype :: PGTyped schema ty => TypeExpression schema ty
-- | logical Boolean (true/false)
bool :: TypeExpression schema 'PGbool
-- | signed two-byte integer
int2 :: TypeExpression schema 'PGint2
-- | signed two-byte integer
smallint :: TypeExpression schema 'PGint2
-- | signed four-byte integer
int4 :: TypeExpression schema 'PGint4
-- | signed four-byte integer
int :: TypeExpression schema 'PGint4
-- | signed four-byte integer
integer :: TypeExpression schema 'PGint4
-- | signed eight-byte integer
int8 :: TypeExpression schema 'PGint8
-- | signed eight-byte integer
bigint :: TypeExpression schema 'PGint8
-- | arbitrary precision numeric type
numeric :: TypeExpression schema 'PGnumeric
-- | single precision floating-point number (4 bytes)
float4 :: TypeExpression schema 'PGfloat4
-- | single precision floating-point number (4 bytes)
real :: TypeExpression schema 'PGfloat4
-- | double precision floating-point number (8 bytes)
float8 :: TypeExpression schema 'PGfloat8
-- | double precision floating-point number (8 bytes)
doublePrecision :: TypeExpression schema 'PGfloat8
-- | variable-length character string
text :: TypeExpression schema 'PGtext
-- | fixed-length character string
char :: (KnownNat n, 1 <= n) => proxy n -> TypeExpression schema ( 'PGchar n)
-- | fixed-length character string
character :: (KnownNat n, 1 <= n) => proxy n -> TypeExpression schema ( 'PGchar n)
-- | variable-length character string
varchar :: (KnownNat n, 1 <= n) => proxy n -> TypeExpression schema ( 'PGvarchar n)
-- | variable-length character string
characterVarying :: (KnownNat n, 1 <= n) => proxy n -> TypeExpression schema ( 'PGvarchar n)
-- | binary data ("byte array")
bytea :: TypeExpression schema 'PGbytea
-- | date and time (no time zone)
timestamp :: TypeExpression schema 'PGtimestamp
-- | date and time, including time zone
timestampWithTimeZone :: TypeExpression schema 'PGtimestamptz
-- | calendar date (year, month, day)
date :: TypeExpression schema 'PGdate
-- | time of day (no time zone)
time :: TypeExpression schema 'PGtime
-- | time of day, including time zone
timeWithTimeZone :: TypeExpression schema 'PGtimetz
-- | time span
interval :: TypeExpression schema 'PGinterval
-- | universally unique identifier
uuid :: TypeExpression schema 'PGuuid
-- | IPv4 or IPv6 host address
inet :: TypeExpression schema 'PGinet
-- | textual JSON data
json :: TypeExpression schema 'PGjson
-- | binary JSON data, decomposed
jsonb :: TypeExpression schema 'PGjsonb
-- | variable length array
vararray :: TypeExpression schema pg -> TypeExpression schema ( 'PGvararray pg)
-- | fixed length array
--
-- -- >>> renderTypeExpression (fixarray (Proxy @2) json) -- "json[2]" --fixarray :: KnownNat n => proxy n -> TypeExpression schema pg -> TypeExpression schema ( 'PGfixarray n pg) -- | & 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 schema ty) instance GHC.Classes.Ord (Squeal.PostgreSQL.Expression.TypeExpression schema ty) instance GHC.Classes.Eq (Squeal.PostgreSQL.Expression.TypeExpression schema ty) instance GHC.Show.Show (Squeal.PostgreSQL.Expression.TypeExpression schema ty) instance GHC.Generics.Generic (Squeal.PostgreSQL.Expression.TypeExpression schema ty) instance Control.DeepSeq.NFData (Squeal.PostgreSQL.Expression.Expression schema relations grouping params ty) instance GHC.Classes.Ord (Squeal.PostgreSQL.Expression.Expression schema relations grouping params ty) instance GHC.Classes.Eq (Squeal.PostgreSQL.Expression.Expression schema relations grouping params ty) instance GHC.Show.Show (Squeal.PostgreSQL.Expression.Expression schema relations grouping params ty) instance GHC.Generics.Generic (Squeal.PostgreSQL.Expression.Expression schema relations grouping params ty) instance Squeal.PostgreSQL.Expression.PGTyped schema 'Squeal.PostgreSQL.Schema.PGbool instance Squeal.PostgreSQL.Expression.PGTyped schema 'Squeal.PostgreSQL.Schema.PGint2 instance Squeal.PostgreSQL.Expression.PGTyped schema 'Squeal.PostgreSQL.Schema.PGint4 instance Squeal.PostgreSQL.Expression.PGTyped schema 'Squeal.PostgreSQL.Schema.PGint8 instance Squeal.PostgreSQL.Expression.PGTyped schema 'Squeal.PostgreSQL.Schema.PGnumeric instance Squeal.PostgreSQL.Expression.PGTyped schema 'Squeal.PostgreSQL.Schema.PGfloat4 instance Squeal.PostgreSQL.Expression.PGTyped schema 'Squeal.PostgreSQL.Schema.PGfloat8 instance Squeal.PostgreSQL.Expression.PGTyped schema 'Squeal.PostgreSQL.Schema.PGtext instance (GHC.TypeNats.KnownNat n, 1 GHC.TypeNats.<= n) => Squeal.PostgreSQL.Expression.PGTyped schema ('Squeal.PostgreSQL.Schema.PGchar n) instance (GHC.TypeNats.KnownNat n, 1 GHC.TypeNats.<= n) => Squeal.PostgreSQL.Expression.PGTyped schema ('Squeal.PostgreSQL.Schema.PGvarchar n) instance Squeal.PostgreSQL.Expression.PGTyped schema 'Squeal.PostgreSQL.Schema.PGbytea instance Squeal.PostgreSQL.Expression.PGTyped schema 'Squeal.PostgreSQL.Schema.PGtimestamp instance Squeal.PostgreSQL.Expression.PGTyped schema 'Squeal.PostgreSQL.Schema.PGtimestamptz instance Squeal.PostgreSQL.Expression.PGTyped schema 'Squeal.PostgreSQL.Schema.PGdate instance Squeal.PostgreSQL.Expression.PGTyped schema 'Squeal.PostgreSQL.Schema.PGtime instance Squeal.PostgreSQL.Expression.PGTyped schema 'Squeal.PostgreSQL.Schema.PGtimetz instance Squeal.PostgreSQL.Expression.PGTyped schema 'Squeal.PostgreSQL.Schema.PGinterval instance Squeal.PostgreSQL.Expression.PGTyped schema 'Squeal.PostgreSQL.Schema.PGuuid instance Squeal.PostgreSQL.Expression.PGTyped schema 'Squeal.PostgreSQL.Schema.PGjson instance Squeal.PostgreSQL.Expression.PGTyped schema 'Squeal.PostgreSQL.Schema.PGjsonb instance Squeal.PostgreSQL.Expression.PGTyped schema ty => Squeal.PostgreSQL.Expression.PGTyped schema ('Squeal.PostgreSQL.Schema.PGvararray ty) instance (GHC.TypeNats.KnownNat n, Squeal.PostgreSQL.Expression.PGTyped schema ty) => Squeal.PostgreSQL.Expression.PGTyped schema ('Squeal.PostgreSQL.Schema.PGfixarray n ty) instance Squeal.PostgreSQL.Expression.HasParameter 1 schema (ty1 : tys) ty1 instance (GHC.TypeNats.KnownNat n, Squeal.PostgreSQL.Expression.HasParameter (n GHC.TypeNats.- 1) schema params ty) => Squeal.PostgreSQL.Expression.HasParameter n schema (ty' : params) ty instance Squeal.PostgreSQL.Schema.Has alias schema ('Squeal.PostgreSQL.Schema.Typedef ty) => GHC.OverloadedLabels.IsLabel alias (Squeal.PostgreSQL.Expression.TypeExpression schema ty) instance Squeal.PostgreSQL.Expression.PGAvg 'Squeal.PostgreSQL.Schema.PGint2 'Squeal.PostgreSQL.Schema.PGnumeric instance Squeal.PostgreSQL.Expression.PGAvg 'Squeal.PostgreSQL.Schema.PGint4 'Squeal.PostgreSQL.Schema.PGnumeric instance Squeal.PostgreSQL.Expression.PGAvg 'Squeal.PostgreSQL.Schema.PGint8 'Squeal.PostgreSQL.Schema.PGnumeric instance Squeal.PostgreSQL.Expression.PGAvg 'Squeal.PostgreSQL.Schema.PGnumeric 'Squeal.PostgreSQL.Schema.PGnumeric instance Squeal.PostgreSQL.Expression.PGAvg 'Squeal.PostgreSQL.Schema.PGfloat4 'Squeal.PostgreSQL.Schema.PGfloat8 instance Squeal.PostgreSQL.Expression.PGAvg 'Squeal.PostgreSQL.Schema.PGfloat8 'Squeal.PostgreSQL.Schema.PGfloat8 instance Squeal.PostgreSQL.Expression.PGAvg 'Squeal.PostgreSQL.Schema.PGinterval 'Squeal.PostgreSQL.Schema.PGinterval instance Squeal.PostgreSQL.Render.RenderSQL (Squeal.PostgreSQL.Expression.Expression schema relations grouping params ty) instance (Squeal.PostgreSQL.Schema.HasUnique relation relations columns, Squeal.PostgreSQL.Schema.Has column columns ty) => GHC.OverloadedLabels.IsLabel column (Squeal.PostgreSQL.Expression.Expression schema relations 'Squeal.PostgreSQL.Schema.Ungrouped params ty) instance (Squeal.PostgreSQL.Schema.HasUnique relation relations columns, Squeal.PostgreSQL.Schema.Has column columns ty) => GHC.OverloadedLabels.IsLabel column (Squeal.PostgreSQL.Schema.Aliased (Squeal.PostgreSQL.Expression.Expression schema relations 'Squeal.PostgreSQL.Schema.Ungrouped params) (column Squeal.PostgreSQL.Schema.::: ty)) instance (Squeal.PostgreSQL.Schema.HasUnique relation relations columns, Squeal.PostgreSQL.Schema.Has column columns ty) => GHC.OverloadedLabels.IsLabel column (Generics.SOP.NP.NP (Squeal.PostgreSQL.Schema.Aliased (Squeal.PostgreSQL.Expression.Expression schema relations 'Squeal.PostgreSQL.Schema.Ungrouped params)) '[column Squeal.PostgreSQL.Schema.::: ty]) instance (Squeal.PostgreSQL.Schema.Has relation relations columns, Squeal.PostgreSQL.Schema.Has column columns ty) => Squeal.PostgreSQL.Schema.IsQualified relation column (Squeal.PostgreSQL.Expression.Expression schema relations 'Squeal.PostgreSQL.Schema.Ungrouped params ty) instance (Squeal.PostgreSQL.Schema.Has relation relations columns, Squeal.PostgreSQL.Schema.Has column columns ty) => Squeal.PostgreSQL.Schema.IsQualified relation column (Squeal.PostgreSQL.Schema.Aliased (Squeal.PostgreSQL.Expression.Expression schema relations 'Squeal.PostgreSQL.Schema.Ungrouped params) (column Squeal.PostgreSQL.Schema.::: ty)) instance (Squeal.PostgreSQL.Schema.Has relation relations columns, Squeal.PostgreSQL.Schema.Has column columns ty) => Squeal.PostgreSQL.Schema.IsQualified relation column (Generics.SOP.NP.NP (Squeal.PostgreSQL.Schema.Aliased (Squeal.PostgreSQL.Expression.Expression schema relations 'Squeal.PostgreSQL.Schema.Ungrouped params)) '[column Squeal.PostgreSQL.Schema.::: ty]) instance (Squeal.PostgreSQL.Schema.HasUnique relation relations columns, Squeal.PostgreSQL.Schema.Has column columns ty, Squeal.PostgreSQL.Schema.GroupedBy relation column bys) => GHC.OverloadedLabels.IsLabel column (Squeal.PostgreSQL.Expression.Expression schema relations ('Squeal.PostgreSQL.Schema.Grouped bys) params ty) instance (Squeal.PostgreSQL.Schema.HasUnique relation relations columns, Squeal.PostgreSQL.Schema.Has column columns ty, Squeal.PostgreSQL.Schema.GroupedBy relation column bys) => GHC.OverloadedLabels.IsLabel column (Squeal.PostgreSQL.Schema.Aliased (Squeal.PostgreSQL.Expression.Expression schema relations ('Squeal.PostgreSQL.Schema.Grouped bys) params) (column Squeal.PostgreSQL.Schema.::: ty)) instance (Squeal.PostgreSQL.Schema.HasUnique relation relations columns, Squeal.PostgreSQL.Schema.Has column columns ty, Squeal.PostgreSQL.Schema.GroupedBy relation column bys) => GHC.OverloadedLabels.IsLabel column (Generics.SOP.NP.NP (Squeal.PostgreSQL.Schema.Aliased (Squeal.PostgreSQL.Expression.Expression schema relations ('Squeal.PostgreSQL.Schema.Grouped bys) params)) '[column Squeal.PostgreSQL.Schema.::: ty]) instance (Squeal.PostgreSQL.Schema.Has relation relations columns, Squeal.PostgreSQL.Schema.Has column columns ty, Squeal.PostgreSQL.Schema.GroupedBy relation column bys) => Squeal.PostgreSQL.Schema.IsQualified relation column (Squeal.PostgreSQL.Expression.Expression schema relations ('Squeal.PostgreSQL.Schema.Grouped bys) params ty) instance (Squeal.PostgreSQL.Schema.Has relation relations columns, Squeal.PostgreSQL.Schema.Has column columns ty, Squeal.PostgreSQL.Schema.GroupedBy relation column bys) => Squeal.PostgreSQL.Schema.IsQualified relation column (Squeal.PostgreSQL.Schema.Aliased (Squeal.PostgreSQL.Expression.Expression schema relations ('Squeal.PostgreSQL.Schema.Grouped bys) params) (column Squeal.PostgreSQL.Schema.::: ty)) instance (Squeal.PostgreSQL.Schema.Has relation relations columns, Squeal.PostgreSQL.Schema.Has column columns ty, Squeal.PostgreSQL.Schema.GroupedBy relation column bys) => Squeal.PostgreSQL.Schema.IsQualified relation column (Generics.SOP.NP.NP (Squeal.PostgreSQL.Schema.Aliased (Squeal.PostgreSQL.Expression.Expression schema relations ('Squeal.PostgreSQL.Schema.Grouped bys) params)) '[column Squeal.PostgreSQL.Schema.::: ty]) instance (GHC.TypeLits.KnownSymbol label, Squeal.PostgreSQL.Schema.In label labels) => Squeal.PostgreSQL.Schema.IsPGlabel label (Squeal.PostgreSQL.Expression.Expression schema relations grouping params (nullity ('Squeal.PostgreSQL.Schema.PGenum labels))) instance Squeal.PostgreSQL.Schema.Has field fields ty => GHC.OverloadedLabels.IsLabel field (Squeal.PostgreSQL.Expression.Expression schema relation grouping params (nullity ('Squeal.PostgreSQL.Schema.PGcomposite fields)) -> Squeal.PostgreSQL.Expression.Expression schema relation grouping params ('Squeal.PostgreSQL.Schema.Null ty)) instance Data.Semigroup.Semigroup (Squeal.PostgreSQL.Expression.Expression schema relations grouping params (nullity ('Squeal.PostgreSQL.Schema.PGvararray ty))) instance GHC.Base.Monoid (Squeal.PostgreSQL.Expression.Expression schema relations grouping params (nullity ('Squeal.PostgreSQL.Schema.PGvararray ty))) instance Squeal.PostgreSQL.Schema.PGNum ty => GHC.Num.Num (Squeal.PostgreSQL.Expression.Expression schema relations grouping params (nullity ty)) instance (Squeal.PostgreSQL.Schema.PGNum ty, Squeal.PostgreSQL.Schema.PGFloating ty) => GHC.Real.Fractional (Squeal.PostgreSQL.Expression.Expression schema relations grouping params (nullity ty)) instance (Squeal.PostgreSQL.Schema.PGNum ty, Squeal.PostgreSQL.Schema.PGFloating ty) => GHC.Float.Floating (Squeal.PostgreSQL.Expression.Expression schema relations grouping params (nullity ty)) instance Data.String.IsString (Squeal.PostgreSQL.Expression.Expression schema relations grouping params (nullity 'Squeal.PostgreSQL.Schema.PGtext)) instance Data.Semigroup.Semigroup (Squeal.PostgreSQL.Expression.Expression schema relations grouping params (nullity 'Squeal.PostgreSQL.Schema.PGtext)) instance GHC.Base.Monoid (Squeal.PostgreSQL.Expression.Expression schema 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" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
-- '[]
-- '["col" ::: 'Null 'PGint4]
-- query = selectStar (from (table #tab))
-- in printSQL query
-- :}
-- SELECT * FROM "tab" AS "tab"
--
--
-- restricted query:
--
--
-- >>> :{
-- let
-- query :: Query
-- '[ "tab" ::: 'Table ('[] :=>
-- '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "col2" ::: 'NoDef :=> 'NotNull 'PGint4 ])]
-- '[]
-- '[ "sum" ::: 'NotNull 'PGint4
-- , "col1" ::: 'NotNull 'PGint4 ]
-- query =
-- select
-- ((#col1 + #col2) `As` #sum :* #col1 :* Nil)
-- ( from (table #tab)
-- & where_ (#col1 .> #col2)
-- & where_ (#col2 .> 0) )
-- in printSQL query
-- :}
-- SELECT ("col1" + "col2") AS "sum", "col1" AS "col1" FROM "tab" AS "tab" WHERE (("col1" > "col2") AND ("col2" > 0))
--
--
-- subquery:
--
--
-- >>> :{
-- let
-- query :: Query
-- '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
-- '[]
-- '["col" ::: 'Null 'PGint4]
-- query =
-- selectStar
-- (from (subquery (selectStar (from (table #tab)) `As` #sub)))
-- in printSQL query
-- :}
-- SELECT * FROM (SELECT * FROM "tab" AS "tab") AS "sub"
--
--
-- limits and offsets:
--
--
-- >>> :{
-- let
-- query :: Query
-- '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
-- '[]
-- '["col" ::: 'Null 'PGint4]
-- query = selectStar
-- (from (table #tab) & limit 100 & offset 2 & limit 50 & offset 2)
-- in printSQL query
-- :}
-- SELECT * FROM "tab" AS "tab" LIMIT 50 OFFSET 4
--
--
-- parameterized query:
--
--
-- >>> :{
-- let
-- query :: Query
-- '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGfloat8])]
-- '[ 'NotNull 'PGfloat8]
-- '["col" ::: 'NotNull 'PGfloat8]
-- query = selectStar
-- (from (table #tab) & where_ (#col .> param @1))
-- in printSQL query
-- :}
-- SELECT * FROM "tab" AS "tab" WHERE ("col" > ($1 :: float8))
--
--
-- aggregation query:
--
--
-- >>> :{
-- let
-- query :: Query
-- '[ "tab" ::: 'Table ('[] :=>
-- '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "col2" ::: 'NoDef :=> 'NotNull 'PGint4 ])]
-- '[]
-- '[ "sum" ::: 'NotNull 'PGint4
-- , "col1" ::: 'NotNull 'PGint4 ]
-- query =
-- select (sum_ #col2 `As` #sum :* #col1 :* Nil)
-- ( from (table (#tab `As` #table1))
-- & group (By #col1 :* Nil)
-- & having (#col1 + sum_ #col2 .> 1) )
-- in printSQL 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" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
-- '[]
-- '["col" ::: 'Null 'PGint4]
-- query = selectStar
-- (from (table #tab) & orderBy [#col & AscNullsFirst])
-- in printSQL query
-- :}
-- SELECT * FROM "tab" AS "tab" ORDER BY "col" ASC NULLS FIRST
--
--
-- joins:
--
--
-- >>> :set -XFlexibleContexts
--
-- >>> :{
-- let
-- query :: Query
-- '[ "orders" ::: 'Table (
-- '["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" ::: 'Table (
-- '["pk_customers" ::: PrimaryKey '["id"]] :=>
-- '[ "id" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "name" ::: 'NoDef :=> 'NotNull 'PGtext
-- ])
-- , "shippers" ::: 'Table (
-- '["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 printSQL 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" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
-- '[]
-- '["col" ::: 'Null 'PGint4]
-- query = selectDotStar #t1
-- (from (table (#tab `As` #t1) & crossJoin (table (#tab `As` #t2))))
-- in printSQL query
-- :}
-- SELECT "t1".* FROM "tab" AS "t1" CROSS JOIN "tab" AS "t2"
--
--
-- set operations:
--
--
-- >>> :{
-- let
-- query :: Query
-- '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
-- '[]
-- '["col" ::: 'Null 'PGint4]
-- query =
-- selectStar (from (table #tab))
-- `unionAll`
-- selectStar (from (table #tab))
-- in printSQL query
-- :}
-- (SELECT * FROM "tab" AS "tab") UNION ALL (SELECT * FROM "tab" AS "tab")
--
newtype Query (schema :: SchemaType) (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 schema 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 schema 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
-- | values computes a row value or set of row values specified by
-- value expressions. It is most commonly used to generate a “constant
-- table” within a larger command, but it can be used on its own.
--
--
-- >>> type Row = '["a" ::: 'NotNull 'PGint4, "b" ::: 'NotNull 'PGtext]
--
-- >>> let query = values (1 `As` #a :* "one" `As` #b :* Nil) [] :: Query '[] '[] Row
--
-- >>> printSQL query
-- SELECT * FROM (VALUES (1, E'one')) AS t ("a", "b")
--
values :: SListI cols => NP (Aliased (Expression schema '[] 'Ungrouped params)) cols -> [NP (Aliased (Expression schema '[] 'Ungrouped params)) cols] -> Query schema params cols
-- | values_ computes a row value or set of row values specified by
-- value expressions.
values_ :: SListI cols => NP (Aliased (Expression schema '[] 'Ungrouped params)) cols -> Query schema params cols
-- | 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 :: SchemaType) (params :: [NullityType]) (relations :: RelationsType) (grouping :: Grouping)
TableExpression :: FromClause schema params relations -> [Condition schema relations 'Ungrouped params] -> GroupByClause relations grouping -> HavingClause schema relations grouping params -> [SortExpression schema 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 schema 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 schema 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 schema 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 schema 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 schema 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 schema 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 :: Has tab schema ( 'Table table) => Aliased Alias (alias ::: tab) -> FromClause schema params '[alias ::: TableToRelation table]
-- | subquery derives a table from a Query.
subquery :: Aliased (Query schema params) rel -> FromClause schema params '[rel]
-- | view derives a table from a View.
view :: Has view schema ( 'View rel) => Aliased Alias (alias ::: view) -> FromClause schema params '[alias ::: rel]
-- | 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 schema (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 schema (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 schema (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 schema (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 schema relations grouping params
[NoHaving] :: HavingClause schema relations 'Ungrouped params
[Having] :: [Condition schema relations ( 'Grouped bys) params] -> HavingClause schema relations ( 'Grouped bys) params
-- | Render a HavingClause.
renderHavingClause :: HavingClause schema 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 schema relations grouping params
[Asc] :: Expression schema relations grouping params ( 'NotNull ty) -> SortExpression schema relations grouping params
[Desc] :: Expression schema relations grouping params ( 'NotNull ty) -> SortExpression schema relations grouping params
[AscNullsFirst] :: Expression schema relations grouping params ( 'Null ty) -> SortExpression schema relations grouping params
[AscNullsLast] :: Expression schema relations grouping params ( 'Null ty) -> SortExpression schema relations grouping params
[DescNullsFirst] :: Expression schema relations grouping params ( 'Null ty) -> SortExpression schema relations grouping params
[DescNullsLast] :: Expression schema relations grouping params ( 'Null ty) -> SortExpression schema relations grouping params
-- | Render a SortExpression.
renderSortExpression :: SortExpression schema relations grouping params -> ByteString
instance forall k1 (schema :: k1) k2 (params :: k2) k3 (relations :: k3). Control.DeepSeq.NFData (Squeal.PostgreSQL.Query.FromClause schema params relations)
instance forall k1 (schema :: k1) k2 (params :: k2) k3 (relations :: k3). GHC.Classes.Ord (Squeal.PostgreSQL.Query.FromClause schema params relations)
instance forall k1 (schema :: k1) k2 (params :: k2) k3 (relations :: k3). GHC.Classes.Eq (Squeal.PostgreSQL.Query.FromClause schema params relations)
instance forall k1 (schema :: k1) k2 (params :: k2) k3 (relations :: k3). GHC.Show.Show (Squeal.PostgreSQL.Query.FromClause schema params relations)
instance forall k1 (schema :: k1) k2 (params :: k2) k3 (relations :: k3). GHC.Generics.Generic (Squeal.PostgreSQL.Query.FromClause 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 schema relations grouping params)
instance GHC.Classes.Eq (Squeal.PostgreSQL.Query.HavingClause schema relations grouping params)
instance GHC.Classes.Ord (Squeal.PostgreSQL.Query.HavingClause schema relations grouping params)
instance GHC.Show.Show (Squeal.PostgreSQL.Query.SortExpression schema relations grouping params)
instance Squeal.PostgreSQL.Render.RenderSQL (Squeal.PostgreSQL.Query.Query schema params columns)
-- | 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" ::: 'Table ('[] :=>
-- '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "col2" ::: 'Def :=> 'NotNull 'PGint4 ])] '[] '[]
-- manipulation =
-- insertRow_ #tab (Set 2 `As` #col1 :* Default `As` #col2 :* Nil)
-- in printSQL manipulation
-- :}
-- INSERT INTO "tab" ("col1", "col2") VALUES (2, DEFAULT)
--
--
-- parameterized insert:
--
--
-- >>> :{
-- let
-- manipulation :: Manipulation
-- '[ "tab" ::: 'Table ('[] :=>
-- '[ "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 printSQL manipulation
-- :}
-- INSERT INTO "tab" ("col1", "col2") VALUES (($1 :: int4), ($2 :: int4))
--
--
-- returning insert:
--
--
-- >>> :{
-- let
-- manipulation :: Manipulation
-- '[ "tab" ::: 'Table ('[] :=>
-- '[ "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 printSQL manipulation
-- :}
-- INSERT INTO "tab" ("col1", "col2") VALUES (2, DEFAULT) RETURNING "col1" AS "fromOnly"
--
--
-- upsert:
--
--
-- >>> :{
-- let
-- manipulation :: Manipulation
-- '[ "tab" ::: 'Table ('[] :=>
-- '[ "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 printSQL 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" ::: 'Table ('[] :=>
-- '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "col2" ::: 'NoDef :=> 'NotNull 'PGint4
-- ])
-- , "other_tab" ::: 'Table ('[] :=>
-- '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "col2" ::: 'NoDef :=> 'NotNull 'PGint4
-- ])
-- ] '[] '[]
-- manipulation =
-- insertQuery_ #tab
-- (selectStar (from (table (#other_tab `As` #t))))
-- in printSQL manipulation
-- :}
-- INSERT INTO "tab" SELECT * FROM "other_tab" AS "t"
--
--
-- update:
--
--
-- >>> :{
-- let
-- manipulation :: Manipulation
-- '[ "tab" ::: 'Table ('[] :=>
-- '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "col2" ::: 'NoDef :=> 'NotNull 'PGint4 ])] '[] '[]
-- manipulation =
-- update_ #tab (Set 2 `As` #col1 :* Same `As` #col2 :* Nil)
-- (#col1 ./= #col2)
-- in printSQL manipulation
-- :}
-- UPDATE "tab" SET "col1" = 2 WHERE ("col1" <> "col2")
--
--
-- delete:
--
--
-- >>> :{
-- let
-- manipulation :: Manipulation
-- '[ "tab" ::: 'Table ('[] :=>
-- '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
-- , "col2" ::: 'NoDef :=> 'NotNull 'PGint4 ])] '[]
-- '[ "col1" ::: 'NotNull 'PGint4
-- , "col2" ::: 'NotNull 'PGint4 ]
-- manipulation = deleteFrom #tab (#col1 .== #col2) ReturningStar
-- in printSQL manipulation
-- :}
-- DELETE FROM "tab" WHERE ("col1" = "col2") RETURNING *
--
newtype Manipulation (schema :: SchemaType) (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 sets a value to be
-- an Expression, which can refer to existing value in the row for
-- an update.
data ColumnValue (schema :: SchemaType) (columns :: RelationType) (params :: [NullityType]) (ty :: ColumnType)
[Same] :: ColumnValue schema (column : columns) params ty
[Default] :: ColumnValue schema columns params ( 'Def :=> ty)
[Set] :: (forall table. Expression schema '[table ::: columns] 'Ungrouped params ty) -> ColumnValue schema 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 (schema :: SchemaType) (columns :: ColumnsType) (params :: [NullityType]) (results :: RelationType)
[ReturningStar] :: results ~ ColumnsToRelation columns => ReturningClause schema columns params results
[Returning] :: rel ~ ColumnsToRelation columns => NP (Aliased (Expression schema '[table ::: rel] 'Ungrouped params)) results -> ReturningClause schema 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 (schema :: SchemaType) (columns :: ColumnsType) params
[OnConflictDoRaise] :: ConflictClause schema columns params
[OnConflictDoNothing] :: ConflictClause schema columns params
[OnConflictDoUpdate] :: NP (Aliased (ColumnValue schema (ColumnsToRelation columns) params)) columns -> [Condition schema '[table ::: ColumnsToRelation columns] 'Ungrouped params] -> ConflictClause schema 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 table), columns ~ TableToColumns table) => Alias tab -> NP (Aliased (ColumnValue schema '[] params)) columns -> [NP (Aliased (ColumnValue schema '[] params)) columns] -> ConflictClause schema columns params -> ReturningClause schema columns params results -> Manipulation schema params results
-- | Insert a single row.
insertRow :: (SListI columns, SListI results, Has tab schema ( 'Table table), columns ~ TableToColumns table) => Alias tab -> NP (Aliased (ColumnValue schema '[] params)) columns -> ConflictClause schema columns params -> ReturningClause schema 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 table), columns ~ TableToColumns table) => Alias tab -> NP (Aliased (ColumnValue schema '[] params)) columns -> [NP (Aliased (ColumnValue schema '[] params)) columns] -> Manipulation schema params '[]
-- | Insert a single row returning Nil and raising an error on
-- conflicts.
insertRow_ :: (SListI columns, Has tab schema ( 'Table table), columns ~ TableToColumns table) => Alias tab -> NP (Aliased (ColumnValue schema '[] params)) columns -> Manipulation schema params '[]
-- | Insert a Query.
insertQuery :: (SListI columns, SListI results, Has tab schema ( 'Table table), columns ~ TableToColumns table) => Alias tab -> Query schema params (ColumnsToRelation columns) -> ConflictClause schema columns params -> ReturningClause schema 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 table), columns ~ TableToColumns table) => Alias tab -> Query schema params (ColumnsToRelation columns) -> Manipulation schema params '[]
-- | Render a ReturningClause.
renderReturningClause :: SListI results => ReturningClause schema params columns results -> ByteString
-- | Render a ConflictClause.
renderConflictClause :: SListI columns => ConflictClause schema 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 table), columns ~ TableToColumns table) => Alias tab -> NP (Aliased (ColumnValue schema (ColumnsToRelation columns) params)) columns -> Condition schema '[tab ::: ColumnsToRelation columns] 'Ungrouped params -> ReturningClause schema columns params results -> Manipulation schema params results
-- | Update a row returning Nil.
update_ :: (SListI columns, Has tab schema ( 'Table table), columns ~ TableToColumns table) => Alias tab -> NP (Aliased (ColumnValue schema (ColumnsToRelation columns) params)) columns -> Condition schema '[tab ::: ColumnsToRelation columns] 'Ungrouped params -> Manipulation schema params '[]
-- | Delete rows of a table.
deleteFrom :: (SListI results, Has tab schema ( 'Table table), columns ~ TableToColumns table) => Alias tab -> Condition schema '[tab ::: ColumnsToRelation columns] 'Ungrouped params -> ReturningClause schema columns params results -> Manipulation schema params results
-- | Delete rows returning Nil.
deleteFrom_ :: (Has tab schema ( 'Table table), columns ~ TableToColumns table) => Alias tab -> Condition schema '[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" ::: 'Table ProductsTable, "products_deleted" ::: 'Table ProductsTable] '[ 'NotNull 'PGdate] '[]
-- manipulation = with
-- (deleteFrom #products (#date .< param @1) ReturningStar `As` #deleted_rows :* Nil)
-- (insertQuery_ #products_deleted (selectStar (from (view (#deleted_rows `As` #t)))))
-- in printSQL 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 (With 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)
instance Squeal.PostgreSQL.Render.RenderSQL (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 :: SchemaType) (schema1 :: SchemaType)
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
--
-- >>> :{
-- printSQL $
-- createTable #tab ((int & nullable) `As` #a :* (real & nullable) `As` #b :* Nil) Nil
-- :}
-- CREATE TABLE "tab" ("a" int NULL, "b" real NULL);
--
createTable :: (KnownSymbol table, columns ~ (col : cols), SListI columns, SListI constraints, schema1 ~ Create table ( 'Table (constraints :=> columns)) schema0) => Alias table -> NP (Aliased (ColumnTypeExpression schema0)) columns -> NP (Aliased (TableConstraintExpression schema1 table)) constraints -> Definition schema0 schema1
-- | 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 of Definitions.
--
--
-- >>> :set -XOverloadedLabels -XTypeApplications
--
-- >>> type Table = '[] :=> '["a" ::: 'NoDef :=> 'Null 'PGint4, "b" ::: 'NoDef :=> 'Null 'PGfloat4]
--
-- >>> type Schema = '["tab" ::: 'Table Table]
--
-- >>> :{
-- let
-- setup :: Definition Schema Schema
-- setup = createTableIfNotExists #tab ((int & nullable) `As` #a :* (real & nullable) `As` #b :* Nil) Nil
-- in printSQL setup
-- :}
-- CREATE TABLE IF NOT EXISTS "tab" ("a" int NULL, "b" real NULL);
--
createTableIfNotExists :: (Has table schema ( 'Table (constraints :=> columns)), SListI columns, SListI constraints) => Alias table -> NP (Aliased (ColumnTypeExpression schema)) columns -> NP (Aliased (TableConstraintExpression schema table)) 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 :: SchemaType) (table :: Symbol) (tableConstraint :: TableConstraint)
UnsafeTableConstraintExpression :: ByteString -> TableConstraintExpression
[renderTableConstraintExpression] :: TableConstraintExpression -> ByteString
-- | 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.
--
--
-- >>> :{
-- type Schema = '[
-- "tab" ::: 'Table ('[ "inequality" ::: 'Check '["a","b"]] :=> '[
-- "a" ::: 'NoDef :=> 'NotNull 'PGint4,
-- "b" ::: 'NoDef :=> 'NotNull 'PGint4
-- ])]
-- :}
--
--
--
-- >>> :{
-- let
-- definition :: Definition '[] Schema
-- definition = createTable #tab
-- ( (int & notNullable) `As` #a :*
-- (int & notNullable) `As` #b :* Nil )
-- ( check (#a :* #b :* Nil) (#a .> #b) `As` #inequality :* Nil )
-- :}
--
--
--
-- >>> printSQL definition
-- CREATE TABLE "tab" ("a" int NOT NULL, "b" int NOT NULL, CONSTRAINT "inequality" CHECK (("a" > "b")));
--
check :: (Has alias schema ( 'Table table), HasAll aliases (TableToRelation table) subcolumns) => NP Alias aliases -> (forall tab. Condition schema '[tab ::: subcolumns] 'Ungrouped '[]) -> TableConstraintExpression schema alias ( 'Check aliases)
-- | 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.
--
--
-- >>> :{
-- type Schema = '[
-- "tab" ::: 'Table( '[ "uq_a_b" ::: 'Unique '["a","b"]] :=> '[
-- "a" ::: 'NoDef :=> 'Null 'PGint4,
-- "b" ::: 'NoDef :=> 'Null 'PGint4
-- ])]
-- :}
--
--
--
-- >>> :{
-- let
-- definition :: Definition '[] Schema
-- definition = createTable #tab
-- ( (int & nullable) `As` #a :*
-- (int & nullable) `As` #b :* Nil )
-- ( unique (#a :* #b :* Nil) `As` #uq_a_b :* Nil )
-- :}
--
--
--
-- >>> printSQL definition
-- CREATE TABLE "tab" ("a" int NULL, "b" int NULL, CONSTRAINT "uq_a_b" UNIQUE ("a", "b"));
--
unique :: (Has alias schema ( 'Table table), HasAll aliases (TableToRelation table) subcolumns) => NP Alias aliases -> TableConstraintExpression schema alias ( 'Unique aliases)
-- | 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.
--
--
-- >>> :{
-- type Schema = '[
-- "tab" ::: 'Table ('[ "pk_id" ::: 'PrimaryKey '["id"]] :=> '[
-- "id" ::: 'Def :=> 'NotNull 'PGint4,
-- "name" ::: 'NoDef :=> 'NotNull 'PGtext
-- ])]
-- :}
--
--
--
-- >>> :{
-- let
-- definition :: Definition '[] Schema
-- definition = createTable #tab
-- ( serial `As` #id :*
-- (text & notNullable) `As` #name :* Nil )
-- ( primaryKey #id `As` #pk_id :* Nil )
-- :}
--
--
--
-- >>> printSQL definition
-- CREATE TABLE "tab" ("id" serial, "name" text NOT NULL, CONSTRAINT "pk_id" PRIMARY KEY ("id"));
--
primaryKey :: (Has alias schema ( 'Table table), HasAll aliases (TableToColumns table) subcolumns, AllNotNull subcolumns) => NP Alias aliases -> TableConstraintExpression schema alias ( 'PrimaryKey aliases)
-- | 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" ::: 'Table (
-- '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
-- '[ "id" ::: 'Def :=> 'NotNull 'PGint4
-- , "name" ::: 'NoDef :=> 'NotNull 'PGtext
-- ])
-- , "emails" ::: 'Table (
-- '[ "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 & notNullable) `As` #name :* Nil )
-- ( primaryKey #id `As` #pk_users :* Nil ) >>>
-- createTable #emails
-- ( serial `As` #id :*
-- (int & notNullable) `As` #user_id :*
-- (text & nullable) `As` #email :* Nil )
-- ( primaryKey #id `As` #pk_emails :*
-- foreignKey #user_id #users #id
-- OnDeleteCascade OnUpdateCascade `As` #fk_user_id :* Nil )
-- in printSQL 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 NULL, CONSTRAINT "pk_emails" PRIMARY KEY ("id"), CONSTRAINT "fk_user_id" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE);
--
--
-- A foreignKey can even be a table self-reference.
--
--
-- >>> :{
-- type Schema =
-- '[ "employees" ::: 'Table (
-- '[ "employees_pk" ::: 'PrimaryKey '["id"]
-- , "employees_employer_fk" ::: 'ForeignKey '["employer_id"] "employees" '["id"]
-- ] :=>
-- '[ "id" ::: 'Def :=> 'NotNull 'PGint4
-- , "name" ::: 'NoDef :=> 'NotNull 'PGtext
-- , "employer_id" ::: 'NoDef :=> 'Null 'PGint4
-- ])
-- ]
-- :}
--
--
--
-- >>> :{
-- let
-- setup :: Definition '[] Schema
-- setup =
-- createTable #employees
-- ( serial `As` #id :*
-- (text & notNullable) `As` #name :*
-- (integer & nullable) `As` #employer_id :* Nil )
-- ( primaryKey #id `As` #employees_pk :*
-- foreignKey #employer_id #employees #id
-- OnDeleteCascade OnUpdateCascade `As` #employees_employer_fk :* Nil )
-- in printSQL setup
-- :}
-- CREATE TABLE "employees" ("id" serial, "name" text NOT NULL, "employer_id" integer NULL, CONSTRAINT "employees_pk" PRIMARY KEY ("id"), CONSTRAINT "employees_employer_fk" FOREIGN KEY ("employer_id") REFERENCES "employees" ("id") ON DELETE CASCADE ON UPDATE CASCADE);
--
foreignKey :: (ForeignKeyed schema child parent table reftable columns refcolumns constraints cols reftys tys) => NP Alias columns -> Alias parent -> NP Alias refcolumns -> OnDeleteClause -> OnUpdateClause -> TableConstraintExpression schema child ( 'ForeignKey columns parent refcolumns)
-- | A constraint synonym between types involved in a foreign key
-- constraint.
type ForeignKeyed schema child parent table reftable columns refcolumns constraints cols reftys tys = (Has child schema ( 'Table table), Has parent schema ( 'Table reftable), HasAll columns (TableToColumns table) tys, reftable ~ (constraints :=> cols), HasAll refcolumns cols reftys, AllZip SamePGType tys reftys, Uniquely refcolumns constraints)
-- | 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.
--
--
-- >>> :{
-- let
-- definition :: Definition '["muh_table" ::: 'Table t] '[]
-- definition = dropTable #muh_table
-- :}
--
--
-- -- >>> printSQL definition -- DROP TABLE "muh_table"; --dropTable :: Has table schema ( 'Table t) => Alias table -> Definition schema (Drop table schema) -- | alterTable changes the definition of a table from the schema. alterTable :: KnownSymbol alias => Alias alias -> AlterTable alias table schema -> Definition schema (Alter alias ( 'Table table) schema) -- | alterTableRename changes the name of a table from the schema. -- --
-- >>> printSQL $ 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 (alias :: Symbol) (table :: TableType) (schema :: SchemaType) UnsafeAlterTable :: ByteString -> AlterTable [renderAlterTable] :: AlterTable -> ByteString -- | An addConstraint adds a table constraint. -- --
-- >>> :{
-- let
-- definition :: Definition
-- '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGint4])]
-- '["tab" ::: 'Table ('["positive" ::: Check '["col"]] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGint4])]
-- definition = alterTable #tab (addConstraint #positive (check (#col :* Nil) (#col .> 0)))
-- in printSQL definition
-- :}
-- ALTER TABLE "tab" ADD CONSTRAINT "positive" CHECK (("col" > 0));
--
addConstraint :: (KnownSymbol alias, Has tab schema ( 'Table table0), table0 ~ (constraints :=> columns), table1 ~ (Create alias constraint constraints :=> columns)) => Alias alias -> TableConstraintExpression schema tab constraint -> AlterTable tab table1 schema
-- | A dropConstraint drops a table constraint.
--
--
-- >>> :{
-- let
-- definition :: Definition
-- '["tab" ::: 'Table ('["positive" ::: Check '["col"]] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGint4])]
-- '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGint4])]
-- definition = alterTable #tab (dropConstraint #positive)
-- in printSQL definition
-- :}
-- ALTER TABLE "tab" DROP CONSTRAINT "positive";
--
dropConstraint :: (KnownSymbol constraint, Has tab schema ( 'Table table0), table0 ~ (constraints :=> columns), table1 ~ (Drop constraint constraints :=> columns)) => Alias constraint -> AlterTable tab table1 schema
-- | 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" ::: 'Table ('[] :=> '["col1" ::: 'NoDef :=> 'Null 'PGint4])]
-- '["tab" ::: 'Table ('[] :=>
-- '[ "col1" ::: 'NoDef :=> 'Null 'PGint4
-- , "col2" ::: 'Def :=> 'Null 'PGtext ])]
-- definition = alterTable #tab (addColumn #col2 (text & nullable & default_ "foo"))
-- in printSQL definition
-- :}
-- ALTER TABLE "tab" ADD COLUMN "col2" text NULL DEFAULT E'foo';
--
--
--
-- >>> :{
-- let
-- definition :: Definition
-- '["tab" ::: 'Table ('[] :=> '["col1" ::: 'NoDef :=> 'Null 'PGint4])]
-- '["tab" ::: 'Table ('[] :=>
-- '[ "col1" ::: 'NoDef :=> 'Null 'PGint4
-- , "col2" ::: 'NoDef :=> 'Null 'PGtext ])]
-- definition = alterTable #tab (addColumn #col2 (text & nullable))
-- in printSQL definition
-- :}
-- ALTER TABLE "tab" ADD COLUMN "col2" text NULL;
--
addColumn :: (AddColumn ty, KnownSymbol column, Has tab schema ( 'Table table0), table0 ~ (constraints :=> columns), table1 ~ (constraints :=> Create column ty columns)) => Alias column -> ColumnTypeExpression schema ty -> AlterTable tab table1 schema
-- | 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" ::: 'Table ('[] :=>
-- '[ "col1" ::: 'NoDef :=> 'Null 'PGint4
-- , "col2" ::: 'NoDef :=> 'Null 'PGtext ])]
-- '["tab" ::: 'Table ('[] :=> '["col1" ::: 'NoDef :=> 'Null 'PGint4])]
-- definition = alterTable #tab (dropColumn #col2)
-- in printSQL definition
-- :}
-- ALTER TABLE "tab" DROP COLUMN "col2";
--
dropColumn :: (KnownSymbol column, Has tab schema ( 'Table table0), table0 ~ (constraints :=> columns), table1 ~ (constraints :=> Drop column columns)) => Alias column -> AlterTable tab table1 schema
-- | A renameColumn renames a column.
--
--
-- >>> :{
-- let
-- definition :: Definition
-- '["tab" ::: 'Table ('[] :=> '["foo" ::: 'NoDef :=> 'Null 'PGint4])]
-- '["tab" ::: 'Table ('[] :=> '["bar" ::: 'NoDef :=> 'Null 'PGint4])]
-- definition = alterTable #tab (renameColumn #foo #bar)
-- in printSQL definition
-- :}
-- ALTER TABLE "tab" RENAME COLUMN "foo" TO "bar";
--
renameColumn :: (KnownSymbol column0, KnownSymbol column1, Has tab schema ( 'Table table0), table0 ~ (constraints :=> columns), table1 ~ (constraints :=> Rename column0 column1 columns)) => Alias column0 -> Alias column1 -> AlterTable tab table1 schema
-- | An alterColumn alters a single column.
alterColumn :: (KnownSymbol column, Has tab schema ( 'Table table0), table0 ~ (constraints :=> columns), Has column columns ty0, tables1 ~ (constraints :=> Alter column ty1 columns)) => Alias column -> AlterColumn schema ty0 ty1 -> AlterTable tab table1 schema
-- | An AlterColumn describes the alteration to perform on a single
-- column.
newtype AlterColumn (schema :: SchemaType) (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" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
-- '["tab" ::: 'Table ('[] :=> '["col" ::: 'Def :=> 'Null 'PGint4])]
-- definition = alterTable #tab (alterColumn #col (setDefault 5))
-- in printSQL definition
-- :}
-- ALTER TABLE "tab" ALTER COLUMN "col" SET DEFAULT 5;
--
setDefault :: Expression schema '[] 'Ungrouped '[] ty -> AlterColumn schema (constraint :=> ty) ( 'Def :=> ty)
-- | A dropDefault removes any default value for a column.
--
--
-- >>> :{
-- let
-- definition :: Definition
-- '["tab" ::: 'Table ('[] :=> '["col" ::: 'Def :=> 'Null 'PGint4])]
-- '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
-- definition = alterTable #tab (alterColumn #col dropDefault)
-- in printSQL definition
-- :}
-- ALTER TABLE "tab" ALTER COLUMN "col" DROP DEFAULT;
--
dropDefault :: AlterColumn schema ( '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" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
-- '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGint4])]
-- definition = alterTable #tab (alterColumn #col setNotNull)
-- in printSQL definition
-- :}
-- ALTER TABLE "tab" ALTER COLUMN "col" SET NOT NULL;
--
setNotNull :: AlterColumn schema (constraint :=> 'Null ty) (constraint :=> 'NotNull ty)
-- | A dropNotNull drops a NOT NULL constraint from a
-- column.
--
--
-- >>> :{
-- let
-- definition :: Definition
-- '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGint4])]
-- '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
-- definition = alterTable #tab (alterColumn #col dropNotNull)
-- in printSQL definition
-- :}
-- ALTER TABLE "tab" ALTER COLUMN "col" DROP NOT NULL;
--
dropNotNull :: AlterColumn schema (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" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGint4])]
-- '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGnumeric])]
-- definition =
-- alterTable #tab (alterColumn #col (alterType (numeric & notNullable)))
-- in printSQL definition
-- :}
-- ALTER TABLE "tab" ALTER COLUMN "col" TYPE numeric NOT NULL;
--
alterType :: ColumnTypeExpression schema ty -> AlterColumn schema ty0 ty
-- | Create a view.
--
--
-- >>> :{
-- let
-- definition :: Definition
-- '[ "abc" ::: 'Table ('[] :=> '["a" ::: 'NoDef :=> 'Null 'PGint4, "b" ::: 'NoDef :=> 'Null 'PGint4, "c" ::: 'NoDef :=> 'Null 'PGint4])]
-- '[ "abc" ::: 'Table ('[] :=> '["a" ::: 'NoDef :=> 'Null 'PGint4, "b" ::: 'NoDef :=> 'Null 'PGint4, "c" ::: 'NoDef :=> 'Null 'PGint4])
-- , "bc" ::: 'View ('["b" ::: 'Null 'PGint4, "c" ::: 'Null 'PGint4])]
-- definition =
-- createView #bc (select (#b :* #c :* Nil) (from (table #abc)))
-- in printSQL definition
-- :}
-- CREATE VIEW "bc" AS SELECT "b" AS "b", "c" AS "c" FROM "abc" AS "abc";
--
createView :: KnownSymbol view => Alias view -> Query schema '[] relation -> Definition schema (Create view ( 'View relation) schema)
-- | Drop a view.
--
--
-- >>> :{
-- let
-- definition :: Definition
-- '[ "abc" ::: 'Table ('[] :=> '["a" ::: 'NoDef :=> 'Null 'PGint4, "b" ::: 'NoDef :=> 'Null 'PGint4, "c" ::: 'NoDef :=> 'Null 'PGint4])
-- , "bc" ::: 'View ('["b" ::: 'Null 'PGint4, "c" ::: 'Null 'PGint4])]
-- '[ "abc" ::: 'Table ('[] :=> '["a" ::: 'NoDef :=> 'Null 'PGint4, "b" ::: 'NoDef :=> 'Null 'PGint4, "c" ::: 'NoDef :=> 'Null 'PGint4])]
-- definition = dropView #bc
-- in printSQL definition
-- :}
-- DROP VIEW "bc";
--
dropView :: Has view schema ( 'View v) => Alias view -> Definition schema (Drop view schema)
-- | Enumerated types are created using the createTypeEnum command,
-- for example
--
--
-- >>> printSQL $ createTypeEnum #mood (label @"sad" :* label @"ok" :* label @"happy" :* Nil)
-- CREATE TYPE "mood" AS ENUM ('sad', 'ok', 'happy');
--
createTypeEnum :: (KnownSymbol enum, All KnownSymbol labels) => Alias enum -> NP PGlabel labels -> Definition schema (Create enum ( 'Typedef ( 'PGenum labels)) schema)
-- | Enumerated types can also be generated from a Haskell type, for
-- example
--
--
-- >>> data Schwarma = Beef | Lamb | Chicken deriving GHC.Generic
--
-- >>> instance SOP.Generic Schwarma
--
-- >>> instance SOP.HasDatatypeInfo Schwarma
--
-- >>> printSQL $ createTypeEnumFrom @Schwarma #schwarma
-- CREATE TYPE "schwarma" AS ENUM ('Beef', 'Lamb', 'Chicken');
--
createTypeEnumFrom :: forall hask enum schema. (Generic hask, All KnownSymbol (LabelsFrom hask), KnownSymbol enum) => Alias enum -> Definition schema (Create enum ( 'Typedef (EnumFrom hask)) schema)
-- | createTypeComposite creates a composite type. The composite
-- type is specified by a list of attribute names and data types.
--
--
-- >>> printSQL $ createTypeComposite #complex (float8 `As` #real :* float8 `As` #imaginary :* Nil)
-- CREATE TYPE "complex" AS ("real" float8, "imaginary" float8);
--
createTypeComposite :: (KnownSymbol ty, SListI fields) => Alias ty -> NP (Aliased (TypeExpression schema)) fields -> Definition schema (Create ty ( 'Typedef ( 'PGcomposite fields)) schema)
-- | Composite types can also be generated from a Haskell type, for example
--
--
-- >>> data Complex = Complex {real :: Maybe Double, imaginary :: Maybe Double} deriving GHC.Generic
--
-- >>> instance SOP.Generic Complex
--
-- >>> instance SOP.HasDatatypeInfo Complex
--
-- >>> printSQL $ createTypeCompositeFrom @Complex #complex
-- CREATE TYPE "complex" AS ("real" float8, "imaginary" float8);
--
createTypeCompositeFrom :: forall hask ty schema. (ZipAliased (FieldNamesFrom hask) (FieldTypesFrom hask), All (PGTyped schema) (FieldTypesFrom hask), KnownSymbol ty) => Alias ty -> Definition schema (Create ty ( 'Typedef (CompositeFrom hask)) schema)
-- | Drop a type.
--
-- -- >>> data Schwarma = Beef | Lamb | Chicken deriving GHC.Generic -- -- >>> instance SOP.Generic Schwarma -- -- >>> instance SOP.HasDatatypeInfo Schwarma -- -- >>> printSQL (dropType #schwarma :: Definition '["schwarma" ::: 'Typedef (EnumFrom Schwarma)] '[]) -- DROP TYPE "schwarma"; --dropType :: Has tydef schema ( 'Typedef ty) => Alias tydef -> Definition schema (Drop tydef schema) -- | ColumnTypeExpressions are used in createTable commands. newtype ColumnTypeExpression (schema :: SchemaType) (ty :: ColumnType) UnsafeColumnTypeExpression :: ByteString -> ColumnTypeExpression [renderColumnTypeExpression] :: ColumnTypeExpression -> ByteString -- | used in createTable commands as a column constraint to note -- that NULL may be present in a column nullable :: TypeExpression schema ty -> ColumnTypeExpression schema ( 'NoDef :=> 'Null ty) -- | used in createTable commands as a column constraint to ensure -- NULL is not present in a column notNullable :: TypeExpression schema ty -> ColumnTypeExpression schema (def :=> 'NotNull ty) -- | used in createTable commands as a column constraint to give a -- default default_ :: Expression schema '[] 'Ungrouped '[] ty -> ColumnTypeExpression schema ( 'NoDef :=> ty) -> ColumnTypeExpression schema ( 'Def :=> ty) -- | not a true type, but merely a notational convenience for creating -- unique identifier columns with type PGint2 serial2 :: ColumnTypeExpression schema ( 'Def :=> 'NotNull 'PGint2) -- | not a true type, but merely a notational convenience for creating -- unique identifier columns with type PGint2 smallserial :: ColumnTypeExpression schema ( 'Def :=> 'NotNull 'PGint2) -- | not a true type, but merely a notational convenience for creating -- unique identifier columns with type PGint4 serial4 :: ColumnTypeExpression schema ( 'Def :=> 'NotNull 'PGint4) -- | not a true type, but merely a notational convenience for creating -- unique identifier columns with type PGint4 serial :: ColumnTypeExpression schema ( 'Def :=> 'NotNull 'PGint4) -- | not a true type, but merely a notational convenience for creating -- unique identifier columns with type PGint8 serial8 :: ColumnTypeExpression schema ( 'Def :=> 'NotNull 'PGint8) -- | not a true type, but merely a notational convenience for creating -- unique identifier columns with type PGint8 bigserial :: ColumnTypeExpression schema ( 'Def :=> 'NotNull 'PGint8) instance Control.DeepSeq.NFData (Squeal.PostgreSQL.Definition.ColumnTypeExpression schema ty) instance GHC.Classes.Ord (Squeal.PostgreSQL.Definition.ColumnTypeExpression schema ty) instance GHC.Classes.Eq (Squeal.PostgreSQL.Definition.ColumnTypeExpression schema ty) instance GHC.Show.Show (Squeal.PostgreSQL.Definition.ColumnTypeExpression schema ty) instance GHC.Generics.Generic (Squeal.PostgreSQL.Definition.ColumnTypeExpression schema ty) instance Control.DeepSeq.NFData (Squeal.PostgreSQL.Definition.AlterColumn schema ty0 ty1) instance GHC.Classes.Ord (Squeal.PostgreSQL.Definition.AlterColumn schema ty0 ty1) instance GHC.Classes.Eq (Squeal.PostgreSQL.Definition.AlterColumn schema ty0 ty1) instance GHC.Show.Show (Squeal.PostgreSQL.Definition.AlterColumn schema ty0 ty1) instance GHC.Generics.Generic (Squeal.PostgreSQL.Definition.AlterColumn schema ty0 ty1) instance Control.DeepSeq.NFData (Squeal.PostgreSQL.Definition.AlterTable alias table schema) instance GHC.Classes.Ord (Squeal.PostgreSQL.Definition.AlterTable alias table schema) instance GHC.Classes.Eq (Squeal.PostgreSQL.Definition.AlterTable alias table schema) instance GHC.Show.Show (Squeal.PostgreSQL.Definition.AlterTable alias table schema) instance GHC.Generics.Generic (Squeal.PostgreSQL.Definition.AlterTable alias table schema) 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 table tableConstraint) instance GHC.Classes.Ord (Squeal.PostgreSQL.Definition.TableConstraintExpression schema table tableConstraint) instance GHC.Classes.Eq (Squeal.PostgreSQL.Definition.TableConstraintExpression schema table tableConstraint) instance GHC.Show.Show (Squeal.PostgreSQL.Definition.TableConstraintExpression schema table tableConstraint) instance GHC.Generics.Generic (Squeal.PostgreSQL.Definition.TableConstraintExpression schema table 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.Def Squeal.PostgreSQL.Schema.:=> ty) instance Squeal.PostgreSQL.Definition.AddColumn ('Squeal.PostgreSQL.Schema.NoDef Squeal.PostgreSQL.Schema.:=> 'Squeal.PostgreSQL.Schema.Null ty) instance Control.DeepSeq.NFData Squeal.PostgreSQL.Definition.OnUpdateClause instance Control.DeepSeq.NFData Squeal.PostgreSQL.Definition.OnDeleteClause instance Squeal.PostgreSQL.Render.RenderSQL (Squeal.PostgreSQL.Definition.Definition schema0 schema1) instance Control.Category.Category Squeal.PostgreSQL.Definition.Definition -- | This module provides binary encoding and decoding between Haskell and -- PostgreSQL types. -- -- Instances are governed by the Generic and -- HasDatatypeInfo typeclasses, so you absolutely do not need to -- define your own instances to decode retrieved rows into Haskell values -- or to encode Haskell values into statement parameters. -- --
-- >>> import Data.Int (Int16) -- -- >>> import Data.Text (Text) ---- --
-- >>> data Row = Row { col1 :: Int16, col2 :: Text } deriving (Eq, GHC.Generic)
--
-- >>> instance Generic Row
--
-- >>> instance HasDatatypeInfo Row
--
--
-- -- >>> import Control.Monad (void) -- -- >>> import Control.Monad.Base (liftBase) -- -- >>> import Squeal.PostgreSQL ---- --
-- >>> :{
-- let
-- query :: Query '[]
-- '[ 'NotNull 'PGint2, 'NotNull 'PGtext]
-- '["col1" ::: 'NotNull 'PGint2, "col2" ::: 'NotNull 'PGtext]
-- query = values_ (param @1 `As` #col1 :* param @2 `As` #col2 :* Nil)
-- :}
--
--
--
-- >>> :{
-- let
-- roundtrip :: IO ()
-- roundtrip = void . withConnection "host=localhost port=5432 dbname=exampledb" $ do
-- result <- runQueryParams query (2 :: Int16, "hi" :: Text)
-- Just row <- firstRow result
-- liftBase . print $ row == Row 2 "hi"
-- :}
--
--
-- -- >>> roundtrip -- True ---- -- In addition to being able to encode and decode basic Haskell types -- like Int16 and Text, Squeal permits you to encode and -- decode Haskell types which are equivalent to Postgres enumerated and -- composite types. -- -- Enumerated (enum) types are data types that comprise a static, ordered -- set of values. They are equivalent to Haskell algebraic data types -- whose constructors are nullary. An example of an enum type might be -- the days of the week, or a set of status values for a piece of data. -- --
-- >>> data Schwarma = Beef | Lamb | Chicken deriving (Show, GHC.Generic) -- -- >>> instance Generic Schwarma -- -- >>> instance HasDatatypeInfo Schwarma ---- -- A composite type represents the structure of a row or record; it is -- essentially just a list of field names and their data types. They are -- almost equivalent to Haskell record types. However, because of the -- potential presence of NULL all the record fields must be -- Maybes of basic types. -- --
-- >>> data Person = Person {name :: Maybe Text, age :: Maybe Int32} deriving (Show, GHC.Generic)
--
-- >>> instance Generic Person
--
-- >>> instance HasDatatypeInfo Person
--
--
-- We can create the equivalent Postgres types directly from their
-- Haskell types.
--
--
-- >>> :{
-- type Schema =
-- '[ "schwarma" ::: 'Typedef (EnumFrom Schwarma)
-- , "person" ::: 'Typedef (CompositeFrom Person)
-- ]
-- :}
--
--
--
-- >>> :{
-- let
-- setup :: Definition '[] Schema
-- setup =
-- createTypeEnumFrom @Schwarma #schwarma >>>
-- createTypeCompositeFrom @Person #person
-- :}
--
--
-- Then we can perform roundtrip queries;
--
--
-- >>> :{
-- let
-- querySchwarma :: Query Schema
-- '[ 'NotNull (EnumFrom Schwarma)]
-- '["fromOnly" ::: 'NotNull (EnumFrom Schwarma)]
-- querySchwarma = values_ (parameter @1 #schwarma `As` #fromOnly :* Nil)
-- :}
--
--
--
-- >>> :{
-- let
-- queryPerson :: Query Schema
-- '[ 'NotNull (CompositeFrom Person)]
-- '["fromOnly" ::: 'NotNull (CompositeFrom Person)]
-- queryPerson = values_ (parameter @1 #person `As` #fromOnly :* Nil)
-- :}
--
--
-- And finally drop the types.
--
--
-- >>> :{
-- let
-- teardown :: Definition Schema '[]
-- teardown = dropType #schwarma >>> dropType #person
-- :}
--
--
-- Now let's run it.
--
--
-- >>> :{
-- let
-- session = do
-- result1 <- runQueryParams querySchwarma (Only Chicken)
-- Just (Only schwarma) <- firstRow result1
-- liftBase $ print (schwarma :: Schwarma)
-- result2 <- runQueryParams queryPerson (Only (Person (Just "Faisal") (Just 24)))
-- Just (Only person) <- firstRow result2
-- liftBase $ print (person :: Person)
-- in
-- void . withConnection "host=localhost port=5432 dbname=exampledb" $
-- define setup
-- & pqThen session
-- & pqThen (define teardown)
-- :}
-- Chicken
-- Person {name = Just "Faisal", age = Just 24}
--
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 NullityType, 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, NullityType) 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 results, Generics.SOP.Universe.IsProductType y ys, Generics.SOP.Constraint.AllZip Squeal.PostgreSQL.Binary.FromColumnValue results ys, Squeal.PostgreSQL.Schema.FieldNamesFrom y ~ Squeal.PostgreSQL.Schema.AliasesOf results) => Squeal.PostgreSQL.Binary.FromRow results y
instance Squeal.PostgreSQL.Binary.FromValue pg y => Squeal.PostgreSQL.Binary.FromColumnValue (column Squeal.PostgreSQL.Schema.::: 'Squeal.PostgreSQL.Schema.NotNull pg) y
instance Squeal.PostgreSQL.Binary.FromValue pg y => Squeal.PostgreSQL.Binary.FromColumnValue (column Squeal.PostgreSQL.Schema.::: 'Squeal.PostgreSQL.Schema.Null pg) (GHC.Base.Maybe y)
instance (Generics.SOP.Sing.SListI fields, Squeal.PostgreSQL.Schema.MapMaybes ys, Generics.SOP.Universe.IsProductType y (Squeal.PostgreSQL.Schema.Maybes ys), Generics.SOP.Constraint.AllZip Squeal.PostgreSQL.Binary.FromAliasedValue fields ys, Squeal.PostgreSQL.Schema.FieldNamesFrom y ~ Squeal.PostgreSQL.Schema.AliasesOf fields) => Squeal.PostgreSQL.Binary.FromValue ('Squeal.PostgreSQL.Schema.PGcomposite fields) y
instance Squeal.PostgreSQL.Binary.FromValue pg y => Squeal.PostgreSQL.Binary.FromAliasedValue (alias Squeal.PostgreSQL.Schema.::: pg) 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.Universe.IsEnumType y, Generics.SOP.Universe.HasDatatypeInfo y, Squeal.PostgreSQL.Schema.LabelsFrom y ~ labels) => Squeal.PostgreSQL.Binary.FromValue ('Squeal.PostgreSQL.Schema.PGenum labels) y
instance (Generics.SOP.Sing.SListI tys, Generics.SOP.Universe.IsProductType x xs, Generics.SOP.Constraint.AllZip 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 (Generics.SOP.Sing.SListI fields, Squeal.PostgreSQL.Schema.MapMaybes xs, Generics.SOP.Universe.IsProductType x (Squeal.PostgreSQL.Schema.Maybes xs), Generics.SOP.Constraint.AllZip Squeal.PostgreSQL.Binary.ToAliasedParam xs fields, Squeal.PostgreSQL.Schema.FieldNamesFrom x ~ Squeal.PostgreSQL.Schema.AliasesOf fields, Generics.SOP.Constraint.All Squeal.PostgreSQL.Binary.HasAliasedOid fields) => Squeal.PostgreSQL.Binary.ToParam x ('Squeal.PostgreSQL.Schema.PGcomposite fields)
instance Squeal.PostgreSQL.Binary.ToParam x ty => Squeal.PostgreSQL.Binary.ToAliasedParam x (alias Squeal.PostgreSQL.Schema.::: ty)
instance Squeal.PostgreSQL.Schema.HasOid ty => Squeal.PostgreSQL.Binary.HasAliasedOid (alias Squeal.PostgreSQL.Schema.::: ty)
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)
instance (Generics.SOP.Universe.IsEnumType x, Generics.SOP.Universe.HasDatatypeInfo x, Squeal.PostgreSQL.Schema.LabelsFrom x ~ labels) => Squeal.PostgreSQL.Binary.ToParam x ('Squeal.PostgreSQL.Schema.PGenum labels)
-- | 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 :: SchemaType) (schema1 :: SchemaType) (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 -- | indexed analog of <=< pqAndThen :: (IndexedMonadTransPQ pq, Monad m) => (y -> pq schema1 schema2 m z) -> (x -> pq schema0 schema1 m y) -> x -> pq schema0 schema2 m z -- | 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 -- -- >>> import Squeal.PostgreSQL.Render ---- -- 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" ::: 'Table (
-- '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
-- '[ "id" ::: 'Def :=> 'NotNull 'PGint4
-- , "name" ::: 'NoDef :=> 'NotNull 'PGtext
-- ])
-- , "emails" ::: 'Table (
-- '[ "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 a
-- SchemumType, a TableConstraint or a ColumnType.
-- It is intended to connote Haskell's :: operator.
--
-- :=> is used to pair TableConstraints with a
-- ColumnsType, yielding a TableType, or to pair a
-- ColumnConstraint with a NullityType, yielding a
-- ColumnType. It is intended to connote Haskell's =>
-- operator
--
-- Next, we'll write Definitions to set up and tear down the
-- schema. In Squeal, a Definition like createTable,
-- alterTable or dropTable 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 & notNullable) `As` #name :* Nil )
-- ( primaryKey #id `As` #pk_users :* Nil ) >>>
-- createTable #emails
-- ( serial `As` #id :*
-- (int & notNullable) `As` #user_id :*
-- (text & nullable) `As` #email :* Nil )
-- ( primaryKey #id `As` #pk_emails :*
-- foreignKey #user_id #users #id
-- OnDeleteCascade OnUpdateCascade `As` #fk_user_id :* Nil )
-- :}
--
--
-- We can easily see the generated SQL is unsurprising looking.
--
--
-- >>> printSQL 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 NULL, 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 teardown Definition
-- is simpler.
--
--
-- >>> :{
-- let
-- teardown :: Definition Schema '[]
-- teardown = dropTable #emails >>> dropTable #users
-- :}
--
--
-- -- >>> printSQL teardown -- DROP TABLE "emails"; -- DROP TABLE "users"; ---- -- Next, we'll write Manipulations to insert data into our two -- tables. A Manipulation like insertRow, update or -- deleteFrom 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 serial, 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)
-- :}
--
--
--
-- >>> printSQL insertUser
-- INSERT INTO "users" ("id", "name") VALUES (DEFAULT, ($1 :: text)) ON CONFLICT DO NOTHING RETURNING "id" AS "fromOnly"
--
-- >>> printSQL 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)) )
-- :}
--
--
--
-- >>> printSQL 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])
-- in
-- 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" ::: 'Table UsersTable]
-- makeUsers = Migration
-- { name = "make users table"
-- , up = void . define $
-- createTable #users
-- ( serial `As` #id :*
-- (text & notNullable) `As` #name :* Nil )
-- ( primaryKey #id `As` #pk_users :* Nil )
-- , down = void . define $ dropTable #users
-- }
-- :}
--
--
--
-- >>> :{
-- let
-- makeEmails :: Migration IO '["users" ::: 'Table UsersTable]
-- '["users" ::: 'Table UsersTable, "emails" ::: 'Table EmailsTable]
-- makeEmails = Migration
-- { name = "make emails table"
-- , up = void . define $
-- createTable #emails
-- ( serial `As` #id :*
-- (int & notNullable) `As` #user_id :*
-- (text & nullable) `As` #email :* Nil )
-- ( primaryKey #id `As` #pk_emails :*
-- foreignKey #user_id #users #id
-- 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 ('Table 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" ::: 'Table MigrationsTable) : schema0) (("schema_migrations" ::: 'Table 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" ::: 'Table MigrationsTable) : schema1) (("schema_migrations" ::: 'Table 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 ( 'Table MigrationsTable) => Definition schema schema
-- | Inserts a Migration into the MigrationsTable
insertMigration :: Has "schema_migrations" schema ( 'Table MigrationsTable) => Manipulation schema '[ 'NotNull 'PGtext] '[]
-- | Deletes a Migration from the MigrationsTable
deleteMigration :: Has "schema_migrations" schema ( 'Table MigrationsTable) => Manipulation schema '[ 'NotNull 'PGtext] '[]
-- | Selects a Migration from the MigrationsTable, returning
-- the time at which it was executed.
selectMigration :: Has "schema_migrations" schema ( 'Table MigrationsTable) => Query schema '[ 'NotNull 'PGtext] '["executed_at" ::: 'NotNull 'PGtimestamptz]
instance forall k (p :: k -> k -> *). Control.Category.Category (Squeal.PostgreSQL.Migration.AlignedList p)