Skip to content

0.3#33

Merged
echatav merged 93 commits intomasterfrom
0.3
Jun 26, 2018
Merged

0.3#33
echatav merged 93 commits intomasterfrom
0.3

Conversation

@echatav
Copy link
Copy Markdown
Contributor

@echatav echatav commented Jun 23, 2018

Version 0.3 of Squeal adds views as well as composite and enumerated types to Squeal.
To support these features, a new kind SchemumType was added.

data SchemumType
  = Table TableType
  | View RelationType
  | Typedef PGType

As a consequence, you will have to update your schema definitions like so:

-- Squeal 0.2
type Schema =
  '[ "users" :::
      '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
      '[ "id"   :::   'Def :=> 'NotNull 'PGint4
       , "name" ::: 'NoDef :=> 'NotNull 'PGtext
       ]
   ]

-- Squeal 0.3
type Schema =
  '[ "users" ::: 'Table (
      '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=>
      '[ "id"   :::   'Def :=> 'NotNull 'PGint4
       , "name" ::: 'NoDef :=> 'NotNull 'PGtext
       ])
   ]

Views

You can now create, drop, and query views.

>>> :{
type ABC =
  ('[] :: TableConstraints) :=>
  '[ "a" ::: 'NoDef :=> 'Null 'PGint4
   , "b" ::: 'NoDef :=> 'Null 'PGint4
   , "c" ::: 'NoDef :=> 'Null 'PGint4
   ]
type BC =
  '[ "b" ::: 'Null 'PGint4
   , "c" ::: 'Null 'PGint4
   ]
:}

>>> :{
let
  definition :: Definition '["abc" ::: 'Table ABC ] '["abc" ::: 'Table ABC, "bc"  ::: 'View BC]
  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";

>>> :{
let
  definition :: Definition '["abc" ::: 'Table ABC, "bc"  ::: 'View BC] '["abc" ::: 'Table ABC]
  definition = dropView #bc
in printSQL definition
:}
DROP VIEW "bc";

>>> :{
let
  query :: Query '["abc" ::: 'Table ABC, "bc"  ::: 'View BC] '[] BC
  query = selectStar (from (view #bc))
in printSQL query
:}
SELECT * FROM "bc" AS "bc"

Enumerated Types

PostgreSQL has a powerful type system. It even allows for user defined types.
For instance, you can define enumerated types which 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.

Enumerated types are created using the createTypeEnum command, for example:

>>> :{
let
  definition :: Definition '[] '["mood" ::: 'Typedef ('PGenum '["sad", "ok", "happy"])]
  definition = createTypeEnum #mood (label @"sad" :* label @"ok" :* label @"happy" :* Nil)
:}
>>> printSQL definition
CREATE TYPE "mood" AS ENUM ('sad', 'ok', 'happy');

Enumerated types can also be generated from a Haskell algbraic data type with nullary constructors, for example:

>>> data Schwarma = Beef | Lamb | Chicken deriving GHC.Generic
>>> instance SOP.Generic Schwarma
>>> instance SOP.HasDatatypeInfo Schwarma

>>> :kind! EnumFrom Schwarma
EnumFrom Schwarma :: PGType
= 'PGenum '["Beef", "Lamb", "Chicken"]

>>> :{
let
  definition :: Definition '[] '["schwarma" ::: 'Typedef (EnumFrom Schwarma)]
  definition = createTypeEnumFrom @Schwarma #schwarma
:}
>>> printSQL definition
CREATE TYPE "schwarma" AS ENUM ('Beef', 'Lamb', 'Chicken');

You can express values of an enum type using label, which is an overloaded method
of the IsPGlabel typeclass.

>>> :{
let
  expression :: Expression sch rels grp params ('NotNull (EnumFrom Schwarma))
  expression = label @"Chicken"
in printSQL expression
:}
'Chicken'

Composite Types

In addition to enum types, you can define composite types.
A composite type represents the structure of a row or record;
it is essentially just a list of field names and their data types.

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

>>> :{
let
  definition :: Definition '[] '["complex" ::: 'Typedef ('PGcomposite '["real" ::: 'PGfloat8, "imaginary" ::: 'PGfloat8])]
  definition = createTypeComposite #complex (float8 `As` #real :* float8 `As` #imaginary :* Nil)
:}
>>> printSQL definition
CREATE TYPE "complex" AS ("real" float8, "imaginary" float8);

Composite types 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.
Composite types can be generated from a Haskell record type, for example:

>>> data Complex = Complex {real :: Maybe Double, imaginary :: Maybe Double} deriving GHC.Generic
>>> instance SOP.Generic Complex
>>> instance SOP.HasDatatypeInfo Complex

>>> :kind! CompositeFrom Complex
CompositeFrom Complex :: PGType
= 'PGcomposite '['("real", 'PGfloat8), '("imaginary", 'PGfloat8)]

>>> :{
let
  definition :: Definition '[] '["complex" ::: 'Typedef (CompositeFrom Complex)]
  definition = createTypeCompositeFrom @Complex #complex
in printSQL definition
:}
CREATE TYPE "complex" AS ("real" float8, "imaginary" float8);

A row constructor is an expression that builds a row value
(also called a composite value) using values for its member fields.

>>> :{
let
  i :: Expression '[] '[] 'Ungrouped '[] ('NotNull (CompositeFrom Complex))
  i = row (0 `As` #real :* 1 `As` #imaginary :* Nil)
:}
>>>  printSQL i
ROW(0, 1)

You can also use (&) to apply a field label to a composite value.

>>> :{
let
  expr :: Expression '[] '[] 'Ungrouped '[] ('Null 'PGfloat8)
  expr = i & #imaginary
in printSQL expr
:}
(ROW(0, 1)).imaginary

Both composite and enum types can be automatically encoded from and decoded to their equivalent Haskell types.
And they can be dropped.

>>> :{
let
  definition :: Definition '["mood" ::: 'Typedef ('PGenum '["sad", "ok", "happy"])] '[]
  definition = dropType #mood
:}
>>> printSQL definition
DROP TYPE "mood";

Additional Changes

Squeal 0.3 also introduces a typeclass HasAll similar to Has but for a list of aliases.
This makes it possible to clean up some unfortunately messy Squeal 0.2 definitions.

-- Squeal 0.2
>>> unique (Column #a :* Column #b :* Nil)

-- Squeal 0.3
>>> unique (#a :* #b :* Nil)

Squeal 0.3 also adds IsLabel instances for Aliased expressions and tables as well as
heterogeneous lists, allowing for some more economy of code.

-- Squeal 0.2
>>> select (#a `As` #a :* Nil) (from (table (#t `As` #t)))

-- Squeal 0.3
>>> select #a (from (table #t))

The above changes required major and minor changes to Squeal DSL functions.
Please consult the documentation.

echatav added 30 commits April 8, 2018 21:53
Attempt to resolve #27
* introduce new typeclass `HasAll` which proves a subfield relation
* remove `Column` datatype!
* remove `ForeignKeyed` constraint synonym
* change the kind of `Alter` type family to be more consistent
* add `SamePGType` constraint
* Change the kind of `TableConstraintExpression`
* check primary keys are all not null
* check foreign keys reference all not null and either primary keys or unique columns
types
* Add `PGenum` and `PGcomposite` types
* Add `Typedef` schemum
* `createTypeEnum` statements
* Split `ColumnTypeExpression` from `TypeExpression`
* label - enum construction
* row - composite construction
* composite access
* enum encoding
just like aliases but  instead of `Label`, `PGLabel` see #27 discussion
createTypeEnumFromHaskell
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant