1{-# language DuplicateRecordFields #-}
8 -- *** Deriving-via helpers
17 , Composite(..), DBComposite(..), compose, decompose
18 , Enum(..), DBEnum(..), Enumable
20 -- *** @TypeInformation@
23 , parseTypeInformation
25 -- ** The @DBType@ hierarchy
33 -- * Tables and higher-kinded tables
49 , AlternativeTable( emptyTable )
50 , EqTable(..), (==:), (/=:)
51 , OrdTable(..), (<:), (<=:), (>:), (>=:), ascTable, descTable, greatest, least
59 , maybeTable, ($?), nothingTable, justTable
60 , isNothingTable, isJustTable
70 , eitherTable, leftTable, rightTable
71 , isLeftTable, isRightTable
74 , bitraverseEitherTable
75 , aggregateEitherTable
80 , theseTable, thisTable, thatTable, thoseTable
81 , isThisTable, isThatTable, isThoseTable
82 , hasHereTable, hasThereTable
83 , justHereTable, justThereTable
86 , keepHereTable, loseHereTable
87 , keepThereTable, loseThereTable
88 , keepThisTable, loseThisTable
89 , keepThatTable, loseThatTable
90 , keepThoseTable, loseThoseTable
91 , bitraverseTheseTable
104 -- ** @NonEmptyTable@
106 , nonEmptyTable, ($+)
115 , nullableTable, nullTable, nullifyTable
116 , isNullTable, isNonNullTable
119 , toNullTable, toMaybeTable
121 -- ** Algebraic data types / sum types
124 -- *** Naming of ADTs
129 -- *** Deconstruction of ADTs
131 , DeconstructADT, deconstructADT
133 -- *** Construction of ADTs
136 , ConstructADT, constructADT
138 -- *** Other ADT operations
139 , AggregateADT, aggregateADT
141 -- *** Miscellaneous notes
147 , ConstructHKD, constructHKD
148 , DeconstructHKD, deconstructHKD
150 , AggregateHKD, aggregateHKD
156 , namesFromLabelsWith
179 -- ** Boolean operations
184 , (==.), (/=.), (==?), (/=?)
191 , (<.), (<=.), (>.), (>=.)
192 , (<?), (<=?), (>?), (>=?)
193 , leastExpr, greatestExpr
207 , Projectable( project )
208 , Biprojectable( biproject )
226 -- ** @LIMIT@/@OFFSET@
255 , listAgg, listAggExpr
257 , nonEmptyAgg, nonEmptyAggExpr
260 , DBSum, sum, sumWhere, avg
261 , DBString, stringAgg
277 -- ** Window functions
308 -- * Running statements
337 , createOrReplaceView
350import Rel8.Column.ADT
351import Rel8.Column.Either
352import Rel8.Column.Lift
353import Rel8.Column.List
354import Rel8.Column.Maybe
355import Rel8.Column.NonEmpty
356import Rel8.Column.Null
357import Rel8.Column.These
359import Rel8.Expr.Aggregate
361import Rel8.Expr.Default
363import Rel8.Expr.Function
365import Rel8.Expr.Opaleye (unsafeCastExpr, unsafeLiteral)
367import Rel8.Expr.Order
368import Rel8.Expr.Serialize
369import Rel8.Expr.Sequence
370import Rel8.Expr.Text ( like, ilike )
371import Rel8.Expr.Window hiding ( cumulative )
372import Rel8.Generic.Rel8able ( KRel8able, Rel8able )
375import Rel8.Query.Aggregate
376import Rel8.Query.Distinct
377import Rel8.Query.Each
378import Rel8.Query.Either
379import Rel8.Query.Evaluate
380import Rel8.Query.Exists
381import Rel8.Query.Filter
382import Rel8.Query.Indexed
383import Rel8.Query.Limit
384import Rel8.Query.List
385import Rel8.Query.Maybe
386import Rel8.Query.Null
387import Rel8.Query.Order
388import Rel8.Query.Rebind
389import Rel8.Query.SQL (showQuery)
391import Rel8.Query.These
392import Rel8.Query.Values
393import Rel8.Query.Window
394import Rel8.Schema.Field
395import Rel8.Schema.HTable
396import Rel8.Schema.Name
397import Rel8.Schema.Null hiding ( nullable )
398import Rel8.Schema.Result ( Result )
399import Rel8.Schema.Table
400import Rel8.Statement.Delete
401import Rel8.Statement.Insert
402import Rel8.Statement.OnConflict
403import Rel8.Statement.Returning
404import Rel8.Statement.Select
405import Rel8.Statement.SQL
406import Rel8.Statement.Update
407import Rel8.Statement.View
410import Rel8.Table.Aggregate
411import Rel8.Table.Alternative
412import Rel8.Table.Bool
413import Rel8.Table.Either
416import Rel8.Table.List
417import Rel8.Table.Maybe
418import Rel8.Table.Name
419import Rel8.Table.NonEmpty
420import Rel8.Table.Null
421import Rel8.Table.Opaleye ( castTable )
423import Rel8.Table.Order
424import Rel8.Table.Projection
425import Rel8.Table.Rel8able ()
426import Rel8.Table.Serialize
427import Rel8.Table.These
428import Rel8.Table.Transpose
429import Rel8.Table.Window
431import Rel8.Type.Composite
434import Rel8.Type.Information
435import Rel8.Type.JSONBEncoded
436import Rel8.Type.JSONEncoded
437import Rel8.Type.Monoid
440import Rel8.Type.ReadShow
441import Rel8.Type.Semigroup
442import Rel8.Type.String
448-- To run queries and otherwise interact with a PostgreSQL database, Rel8
449-- provides 'select', 'insert', 'update' and 'delete' functions. Note that
450-- 'insert', 'update' and 'delete' will generally need the
451-- `DuplicateRecordFields` language extension enabled.
454-- Algebraic data types can be modelled between Haskell and SQL.
456-- * Your SQL table needs a certain text field that tags which Haskell constructor is in use.
457-- * You have to use a few combinators to specify the sum type's individual constructors.
458-- * If you want to do case analysis at the @Expr@ (SQL) level, you can use 'maybe'/'either'-like eliminators.
460-- The documentation in this section will assume a set of database types like this:
463-- data Thing f = ThingEmployer (Employer f) | ThingPotato (Potato f) | Nullary
464-- deriving stock Generic
466-- data Employer f = Employer { employerId :: f Int32, employerName :: f Text}
467-- deriving stock Generic
468-- deriving anyclass Rel8able
470-- data Potato f = Potato { size :: f Int32, grower :: f Text }
471-- deriving stock Generic
472-- deriving anyclass Rel8able
477-- First, in your 'TableSchema', name your type like this:
480-- thingSchema :: TableSchema (ADT Thing Name)
483-- { schema = Nothing,
489-- { employerName = \"name\",
490-- employerId = \"id\"
492-- Potato {size = \"size\", grower = \"Mary\"}
496-- Note that @nameADT \@Thing "tag"@ is variadic: it accepts one
497-- argument per constructor, except the nullary ones (Nullary) because
498-- there's nothing to do for them.
502-- To deconstruct sum types at the SQL level, use 'deconstructADT',
503-- which is also variadic, and has one argument for each
504-- constructor. Similar to 'maybe'.
507-- query :: Query (ADT Thing Expr)
509-- thingExpr <- each thingSchema
511-- deconstructADT @Thing
512-- (\employer -> employerName employer ==. lit \"Mary\")
513-- (\potato -> grower potato ==. lit \"Mary\")
514-- (lit False) -- Nullary case
523-- CAST("tag0_1" AS text) as "tag",
524-- CAST("id1_1" AS int4) as "ThingEmployer/_1/employerId",
525-- CAST("name2_1" AS text) as "ThingEmployer/_1/employerName",
526-- CAST("size3_1" AS int4) as "ThingPotato/_1/size",
527-- CAST("Mary4_1" AS text) as "ThingPotato/_1/grower"
533-- "name" as "name2_1",
534-- "size" as "size3_1",
535-- "Mary" as "Mary4_1"
536-- FROM "thing" as "T1") as "T1"
537-- WHERE (CASE WHEN ("tag0_1") = (CAST(E'ThingPotato' AS text)) THEN ("Mary4_1") = (CAST(E'Mary' AS text))
538-- WHEN ("tag0_1") = (CAST(E'Nullary' AS text)) THEN CAST(FALSE AS bool) ELSE ("name2_1") = (CAST(E'Mary' AS text)) END)) as "T1"
543-- To construct an ADT, you can use 'buildADT' or 'constructADT'. Consider the following type:
546-- data Task f = Pending | Complete (CompletedTask f)
549-- 'buildADT' is for constructing values of 'Task' in the 'Expr'
550-- context. 'buildADT' needs two type-level arguments before its type
551-- makes any sense. The first argument is the type of the "ADT", which
552-- in our case is 'Task'. The second is the name of the constructor we
553-- want to use. So that means we have the following possible
554-- instantiations of 'buildADT' for 'Task':
557-- > :t buildADT @Task @\"Pending\"
558-- buildADT @Task @\"Pending\" :: ADT Task Expr
559-- > :t buildADT @Task @\"Complete\"
560-- buildADT @Task @\"Complete\" :: CompletedTask Expr -> ADT Task Expr
563-- Note that as the "Pending" constructor has no fields, @buildADT
564-- \@Task \@"Pending"@ is equivalent to @lit Pending@. But @buildADT
565-- \@Task \@"Complete"@ is not the same as @lit . Complete@:
568-- > :t lit . Complete
569-- lit . Complete :: CompletedTask Result -> ADT Task Expr
573-- Note that the former takes a @CompletedTask Expr@ while the latter
574-- takes a @CompletedTask Result@. The former is more powerful because
575-- you can construct @Task@s using dynamic values coming a database
578-- To show what this can look like in SQL, consider:
583-- [ buildADT @Task @\"Pending\"
584-- , buildADT @Task @\"Complete\" CompletedTask {date = Rel8.Expr.Time.now}
589-- This produces the following SQL:
593-- CAST(\"values0_1\" AS text) as \"tag\",
594-- CAST(\"values1_1\" AS timestamptz) as \"Complete/_1/date\"
597-- FROM (SELECT \"column1\" as \"values0_1\",
598-- \"column2\" as \"values1_1\"
601-- (CAST(E'Pending' AS text),CAST(NULL AS timestamptz)),
602-- (CAST(E'Complete' AS text),CAST(now() AS timestamptz))) as \"V\") as \"T1\") as \"T1\"
605-- This is what you get if you run it in @psql@:
609-- tag | Complete/_1/date
610-- ----------+-------------------------------
612-- Complete | 2022-05-19 21:28:23.969065+00
616-- "constructADT" is less convenient but more general alternative to
617-- "buildADT". It requires only one type-level argument for its type
621-- > :t constructADT @Task
623-- :: (forall r. r -> (CompletedTask Expr -> r) -> r) -> ADT Task Expr
626-- This might still seem a bit opaque, but basically it gives you a
627-- Church-encoded constructor for arbitrary algebraic data types. You
628-- might use it as follows:
632-- pending :: ADT Task Expr
633-- pending = constructADT @Task $ \pending _complete -> pending
635-- complete :: ADT Task Expr
636-- complete = constructADT @Task $ \_pending complete -> complete CompletedTask {date = Rel8.Expr.Time.now}
639-- These values are otherwise identical to the ones we saw above with
640-- @buildADT@, it's just a different style of constructing them.
645-- 1. Note that the order of the arguments for all of these functions
646-- is determined by the order of the constructors in the data
647-- definition. If it were @data Task = Complete (CompletedTask f) |
648-- Pending@ then the order of all the invocations of @constructADT@
649-- and @deconstructADT@ would need to change.
651-- 2. Maybe this is obvious, but just to spell it out: once you're in
652-- the @Result@ context, you can of course construct @Task@ values
653-- normally and use standard Haskell pattern-matching. @constructADT@
654-- and @deconstructADT@ are specifically only needed in the @Expr@
655-- context, and they allow you to do the equivalent of pattern
656-- matching in PostgreSQL.
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list