rel8
Hey! Hey! Can u rel8?
https://github.com/circuithub/rel8
| LTS Haskell 21.25: | 1.4.1.0 | 
| Stackage Nightly 2025-10-31: | 1.7.0.0 | 
| Latest on Hackage: | 1.7.0.0 | 
rel8-1.7.0.0@sha256:8007f7aa1173fee39c3dc54b45c3f22bcfaf0b1db8c09176c96b24dce232ac06,6346Module documentation for 1.7.0.0
Welcome!
Welcome to Rel8! Rel8 is a Haskell library for interacting with PostgreSQL databases, built on top of the fantastic Opaleye library.
The main objectives of Rel8 are:
- 
Conciseness: Users using Rel8 should not need to write boiler-plate code. By using expressive types, we can provide sufficient information for the compiler to infer code whenever possible. 
- 
Inferrable: Despite using a lot of type level magic, Rel8 aims to have excellent and predictable type inference. 
- 
Familiar: writing Rel8 queries should feel like normal Haskell programming. 
Rel8 was presented at ZuriHac 2021. If you want to have a brief overview of what Rel8 is, and a tour of the API - check out the video below:
For more details, check out the official documentation.
Changes
1.7.0.0 — 2025-07-31
Removed
- Removed support for network-ip. We still supportiproute.
Added
- 
Add support for prepared statements. To use prepared statements, simply use prepare runinstead ofrunwith a function that passes the parameters to your statement.
- 
Added new Encodertype with three members:binary, which is the Hasql binary encoder,textwhich encodes a type in PostgreSQL’s text format (needed for nested arrays) andquote, which is the does the thing that the function we previously calledencodedoes (i.e.,a -> Opaleye.PrimExpr).
- 
Support hasql-1.9 
- 
Add elemandelem1toRel8.Arrayfor testing if an element is contained in[]andNonEmptyExprs.
Changed
- 
Several changes to TypeInformation:- 
Changed the encodefield ofTypeInformationto beEncoder ainstead ofa -> Opaleye.PrimExpr.
- 
Moved the delimiterfield ofDecoderinto the top level ofTypeInformation, as it’s not “decoding” specific, it’s also used when “encoding”.
- 
Renamed the parserfield ofDecodertotext, to mirror thetextfield of the newEncodertype.
 All of this will break any downstream code that uses a completely custom DBTypeimplementation, but anything that usesReadShow,Enum,Composite,JSONBEncodedorparseTypeInformationwill continue working as before (which should cover all common cases).
- 
- 
Stop exporting DecoderandEncoderfrom theRel8module. These can now be found inRel8.DecoderandRel8.Encoder.
- 
Some changes were made to the DBEnumtype class:- Enumablewas removed as a superclass constraint. It is still used to provide the default implementation of the- DBEnumclass.
- A new method, enumerate, was added to theDBEnumclass (with the default implementation provided byEnumable).
 This is unlikely to break any existing DBEnuminstances, it just allows some instances that weren’t possible before (e.g., for types that are notGeneric).
1.6.0.0 — 2024-12-13
Removed
- Remove Table Expr bconstraint frommaterialize. (#334)
Added
- 
Support GHC-9.10. (#340) 
- 
Support hasql-1.8 (#345) 
- 
Add aggregateJustTable,aggregateJustTableaggregator functions. These provide another way to do aggregation ofMaybeTables than the existingaggregateMaybeTablefunction. (#333)
- 
Add aggregateLeftTable,aggregateLeftTable1,aggregateRightTableandaggregateRightTable1aggregator functions. These provide another way to do aggregation ofEitherTables than the existingaggregateEitherTablefunction. (#333)
- 
Add aggregateThisTable,aggregateThisTable1,aggregateThatTable,aggregateThatTable1,aggregateThoseTable,aggregateThoseTable1,aggregateHereTable,aggregateHereTable1,aggregateThereTableandaggregateThereTable1aggregation functions. These provide another way to do aggregation ofTheseTables than the existingaggregateTheseTablefunction. (#333)
- 
Add rawFunction,rawBinaryOperator,rawAggregateFunction,unsafeCoerceExpr,unsafePrimExpr,unsafeSubscript,unsafeSubscripts— these give more options for generating SQL expressions that Rel8 does not support natively. (#331)
- 
Expose unsafeUnnullifyandunsafeUnnullifyTablefromRel8. (#343)
- 
Expose listOfandnonEmptyOf. (#330)
- 
Add NOINLINEpragmas toGenericderived default methods ofRel8able. This should speed up compilation times. If users wish for these methods to beINLINEd, they can override with a pragma in their own code. (#346)
Fixed
- 
JSONEncodedshould be encoded asjsonnotjsonb. (#347)
- 
Disallow NULL characters in Hedgehog generated text values. (#339) 
- 
Fix fromRational bug. (#338) 
- 
Fix regex match operator. (#336) 
- 
Fix some documentation formatting issues. (#332), (#329), (#327), and (#318) 
1.5.0.0 — 2024-03-19
Removed
- Removed nullaryFunction. Insteadfunctioncan be called with(). (#258)
Added
- 
Support PostgreSQL’s inettype (which maps to the HaskellNetAddr IPtype). (#227)
- 
Rel8.materializeandRel8.Tabulate.materialize, which add a materialization/optimisation fence toSELECTstatements by binding a query to aWITHsubquery. Note that explicitly materialized common table expressions are only supported in PostgreSQL 12 an higher. (#180) (#284)
- 
Rel8.head,Rel8.headExpr,Rel8.last,Rel8.lastExprfor accessing the first/last elements ofListTables and arrays. We have also added variants forNonEmptyTables/non-empty arrays with the1suffix (e.g.,head1). (#245)
- 
Rel8 now has extensive support for WITHstatements and data-modifying statements (https://www.postgresql.org/docs/current/queries-with.html#QUERIES-WITH-MODIFYING).This work offers a lot of new power to Rel8. One new possibility is “moving” rows between tables, for example to archive rows in one table into a log table: import Rel8 archive :: Statement () archive = do deleted <- delete Delete { from = mainTable , using = pure () , deleteWhere = \foo -> fooId foo ==. lit 123 , returning = Returning id } insert Insert { into = archiveTable , rows = deleted , onConflict = DoNothing , returning = NoReturninvg }This Statementwill compile to a single SQL statement - essentially:WITH deleted_rows (DELETE FROM main_table WHERE id = 123 RETURNING *) INSERT INTO archive_table SELECT * FROM deleted_rowsThis feature is a significant performant improvement, as it avoids an entire roundtrip. This change has necessitated a change to how a SELECTstatement is ran:selectnow will now produce aRel8.Statement, which you have torunto turn it into a HasqlStatement. Rel8 offers a variety ofrunfunctions depending on how many rows need to be returned - see the various family ofrunfunctions in Rel8’s documentation for more.
- 
Rel8.loopandRel8.loopDistinct, which allow writingWITH .. RECURSIVEqueries. (#180)
- 
Added the QualifiedNametype for named PostgreSQL objects (tables, views, functions, operators, sequences, etc.) that can optionally be qualified by a schema, including anIsStringinstance. (#257) (#263)
- 
Added queryFunctionforSELECTing from table-returning functions such asjsonb_to_recordset. (#241)
- 
TypeNamerecord, which gives a richer representation of the components of a PostgreSQL type name (name, schema, modifiers, scalar/array). (#263)
- 
Rel8.lengthandRel8.lengthExprfor getting the lengthListTables and arrays. We have also added variants forNonEmptyTables/non-empty arrays with the1suffix (e.g.,length1). (#268)
- 
Added aggregators listCatandnonEmptyCatfor folding a collection of lists into a single list by concatenation. (#270)
- 
DBTypeinstance forFixedthat would map (e.g.)Microtonumeric(1000, 6)andPicotonumeric(1000, 12). (#280)
- 
aggregationFunction, which allows custom aggregation functions to be used. (#283)
- 
Add support for ordered-set aggregation functions, including mode,percentile,percentileContinuous,hypotheticalRank,hypotheticalDenseRank,hypotheticalPercentRankandhypotheticalCumeDist. (#282)
- 
Added index,index1,indexExpr, andindex1Exprfunctions for extracting individual elements fromListTables andNonEmptyTables. (#285)
- 
Rel8 now supports GHC 9.8. (#299) 
Changed
- 
Rel8’s API regarding aggregation has changed significantly, and is now a closer match to Opaleye. The previous aggregation API had aggregatetransform aTablefrom theAggregatecontext back into theExprcontext:myQuery = aggregate do a <- each tableA return $ liftF2 (,) (sum (foo a)) (countDistinct (bar a))This API seemed convenient, but has some significant shortcomings. The new API requires an explicit Aggregatorbe passed toaggregate:myQuery = aggregate (liftA2 (,) (sumOn foo) (countDistinctOn bar)) do each tableAFor more details, see #235 
- 
TypeInformation‘sdecoderfield has changed. Instead of taking aHasql.Decoder, it now takes aRel8.Decoder, which itself is comprised of aHasql.Decoderand anattoparsecParser. This is necessitated by the fix for #168; we generally decode things in PostgreSQL’s binary format (using aHasql.Decoder), but for nested arrays we now get things in PostgreSQL’s text format (for which we need anattoparsecParser), so must have both. MostDBTypeinstances that usemapTypeInformationorParseTypeInformation, orDerivingViahelpers likeReadShow,JSONBEncoded,EnumandCompositeare unaffected by this change. (#243)
- 
The schemafield fromTableSchemahas been removed and the name field changed fromStringtoQualifiedName. (#257)
- 
nextval,functionandbinaryOperatornow take aQualifiedNameinstead of aString. (#262)
- 
functionhas been changed to accept a single argument (as opposed to variadic arguments). (#258)
- 
TypeInformation’stypeNameparameter fromStringtoTypeName. (#263)
- 
DBEnum’senumTypeNamemethod fromStringtoQualifiedName. (#263)
- 
DBComposite’scompositeTypeNamemethod fromStringtoQualifiedName. (#263)
- 
Changed Upsertby adding apredicatefield, which allows partial indexes to be specified as conflict targets. (#264)
- 
The window functions lag,lead,firstValue,lastValueandnthValuecan now operate on entire rows at once as opposed to just single columns. (#281)
Fixed
- 
Fixed a bug with catListTableandcatNonEmptyTablewhere invalid SQL could be produced. (#240)
- 
A fix for #168, which prevented using catListTableon arrays of arrays. To achieve this we had to coerce arrays of arrays to text internally, which unfortunately isn’t completely transparent; you can oberve it if you write something likelistTable [listTable [10]] > listTable [listTable [9]]: previously that would befalse, but now it’strue. Arrays of non-arrays are unaffected by this.
- 
Fixes #228 where it was impossible to call nextvalwith a qualified sequence name.
- 
Fixes #71. 
- 
Fixed a typo in the documentation for /=.. (#312)
- 
Fixed a bug where fromRationalcould crash with repeating fractions. (#309)
- 
Fixed a typo in the documentation for min. (#306)
1.4.1.0 (2023-01-19)
New features
- Rel8 now supports window functions. See the “Window functions” section of the Rel8module documentation for more details. (#182)
- Querynow has- Monoidand- Semigroupinstances. (#207)
- createOrReplaceViewhas been added (to run- CREATE OR REPLACE VIEW). (#209 and #212)
- deriving Rel8ablenow supports more polymorphism. (#215)
- Support GHC 9.4 (#199)
Bug fixes
- Insertion of DEFAULTvalues has been fixed. (#206)
- Avoid some exponential SQL generation in Rel8.Tabulate.alignWith. (#213)
- nextValhas been fixed to work with case-sensitive sequence names. (#217)
Other
- Correct the documentation for “Supplying Rel8ableinstances” (#200)
- Removed some redundant internal code (#202)
- Rel8 is now less dependant on the internal Opaleye API. (#204)
1.4.0.0 (2022-08-17)
Breaking changes
- The behavior of greatest/leasthas been corrected, and was previously flipped. (#183)
New features
- NullTable/- HNullhave been added. This is an alternative to- MaybeTablethat doesn’t use a tag columns. It’s less flexible (no- Functoror- Applicativeinstance) and is meaningless when used with a table that has no non-nullable columns (so nesting- NullTableis redundant). But in situations where the underlying- Tabledoes have non-nullable columns, it can losslessly converted to and from- MaybeTable. It is useful for embedding into a base table when you don’t want to store the extra tag column in your schema. (#173)
- Add fromMaybeTable. (#179)
- Add alignMaybeTable. (#196)
Improvements
- Optimize implementation of AltTableforTabulation(#178)
Other
- Documentation improvements for HADT. (#177)
- Document example usage of groupBy. (#184)
- Build with and require Opaleye >= 0.9.3.3. (#190)
- Build with hasql1.6. (#195)
1.3.1.0 (2022-01-20)
Other
- Rel8 now requires Opaleye >= 0.9.1. (#165)
1.3.0.0 (2022-01-31)
Breaking changes
- divand- modhave been changed to match Haskell semantics. If you need the PostgreSQL- div()and- mod()functions, use- quotand- rem. While this is not an API change, we feel this is a breaking change in semantics and have bumped the major version number. (#155)
New features
- divModand- quotRemfunctions have been added, matching Haskell’s- Preludefunctions. (#155)
- avgand- modeaggregation functions to find the mean value of an expression, or the most common row in a query, respectively. (#152)
- The full EqTableandOrdTableclasses have been exported, allowing for instances to be manually created. (#157)
- Added likeandilike(for theLIKEandILIKEoperators). (#146)
Other
- Rel8 now requires Opaleye 0.9. (#158)
- Rel8’s test suite supports Hedgehog 1.1. (#160)
- The documentation for binary operations has been corrected. (#162)
1.2.2.0 (2021-11-21)
Other
1.2.1.0 (2021-11-21)
New features
- 
castTablehas been added, which casts all columns selected from inQueryaccording to the types of columns. This can occasionally be useful when usingmany/someon older PostgreSQL versions. (#137)
- 
Added <:,<=:,>:,>=:,greatestandleast, which sort all columns in a table lexicographically. These operators are like the<.operators which operate onExprs, but the<:operate on entire tables. (#139)
Other
- Support opaleye-0.8 (#142)
1.2.0.0 (2021-10-22)
New features
- New $*and$+operators for projecting out ofListTableandNonEmptyTablerespectively (analogous to the existing$?forMaybeTable). (#125)
Bug fixes
- 
Fix UPSERTsupport (and add tests), which was broken due to a typo inside Rel8 that made it impossible to constructProjections which are needed byUPSERT. (#134)
- 
Remove DBMinandDBMaxinstances forBool, which are invalid as far as Postgres is concerned.andandorcan be used to achieve the same thing. (#126)
- 
Fix aggregateMaybeTable,aggregateEitherTableandaggregateTheseTable, which generated invalid SQL previously. (#127)
Breaking changes
- rebindnow takes an additional argument for the “name” of the binding. (#128)
1.1.0.0 (2021-07-16)
New features
- 
You can derive Rel8ablefor “vanilla” higher-kinded data types - data types that don’t use theColumntype family. For example, the following is now possible:data MyTable f = MyTable { myTableX :: f Int32 , myTableY :: f Text } deriving (Generic, Rel8able)
- 
The ProjectableandBiprojectabletype classes have been introduced. These classes model a type of functors and bifunctors (respectively) on tables, where the mapping function is only able to rearrange columns. For example, the following is now possible:x <- project myTableX <$> many ...
- 
Insert‘sonConflictfield now has a new option -DoUpdate. This maps to PostgreSQL’sON CONFLICT DO UPDATEsyntax, which provides upsert support. (#85)
- 
Insert,UpdateandDeletehas all been expanded to work withQuerys. In particular:- Deletenow supports a- USINGsub-query
- Updatenow supports a- FROMsub-query
- Insertcan delete the result of any arbitrary- Query(providing the types match)
 
- 
The new Rel8.Tabulatemodule has been added, which gives aMap-esque interface to writing and composing queries. (#70)
- 
The new indexedQuery -> Queryfunction was added. This function augments each row in a query with it’s 0-based index. (#117)
Breaking changes
- 
Insert,DeleteandUpdatehave all changed. In particular, forInsertusers should now replacerows = xswithrows = values xs. (#85)
- 
Rename whereExistsandwhereNotExiststopresentandabsentrespectively. (#57)
- 
Simplify evaluateto run directly inside theQuerymonad, rendering theEvaluatemonad unnecessary. (#56)
- 
The Labelabletype class has been removed as it’s no longer necessary. This class was mostly internal, though was visible in some public API functions. (#76)
- 
EitherTable,ListTable,MaybeTable,NonEmptyTableandTheseTablenow all have an extra parameter which tracks the context of the table. If you manually specified any types before, you need to provide this parameter - usuallyExprwill be the correct choice. (#101)
- 
We’ve done a lot of work to simplify the internals of Rel8, but some of these are breaking changes. However, most of these API changes shouldn’t affect users as they are mostly types used in type inference, or affect classes where users don’t need to define their own instances. - The kind of a Rel8ableis now(Type -> Type) -> Type. Previously it was(X -> Type) -> Type.
- Tablehas a new associated type -- FromExprs. This was previously an open type family.
- Tablehas a new associated type -- Transpose- and- Recontextualisehas been renamed to- Transposes. This- Transposesclass now operates in terms of- Transpose.
 
- The kind of a 
- 
select,insert,updateanddeletenow produce HasqlStatements, rather than actually running the statement as IO. This allows Rel8 to be used with transaction/connection-managing monads likehasql-transaction. (#94)
Bug fixes
- 
Fixes a bug where cartesian products of queries using catListTable,catNonEmptyTable,catListandcatNonEmptywould incorrectly be zipped instead. (#61)
- 
Require Opaleye 0.7.3.0. This version has better support for lateral queries, which can improve query plans, especially in optional/LEFT JOIN(#72)
- 
Rel8 now compiles with GHC 9. (#97) 
Other changes
- 
existsis now implemented in terms of the SQLEXISTSkeyword. (#69)
- 
alignByno longer requiresTables. (#67)
1.0.0.1 (2021-06-21)
This release contains various fixes for documentation.
1.0.0.0 (2021-06-18)
- Initial release.
 
