pg-schema: Type-level definition of database schema and safe DML for PostgreSQL.

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

Schema definition

Use updateSchemaFile function from PgSchema.Generation module to generate type-level definition of database schema. You can make several schemas with any parts of your database.

Usually you make executable which imports this module and run it to generate schema definition. You can run it on CI to check if schema definition is up to date.

Safe DML for PostgreSQL

Use PgSchema.DML module to describe and generate (in runtime) safe DML for PostgreSQL. All operations are statically checked and you can't write invalid SQL.

With pg-schema you can select or insert/upsert data into tree-like ADT in one request.

Example

Let's say

Then we can write a function to insert an order with order positions. And select orders with order positions and articles in one request using some conditions for orders and order positions that return in order.

data Order = Order { num :: Text, createdAt :: Day, items :: [OrdPos] } deriving Generic
data OrdPos = OrdPos { id :: Int32, num :: Int32, article :: Article, price :: Double } deriving Generic
data Article = Article { id :: Int32, name :: Text } deriving Generic
type MyAnn tabName = 'Ann 5 CamelToSnake MySch ("dbSchema" ->> tabName)
    ...
do

  void $ insertJSON_ (MyAnn "orders") conn
    [ "num" =: "num1" :. "ord__ord_pos" =:
      [ "num" =: 1 :. "articleId" =: 42 :. "price" =: 10
      , "num" =: 2 :. "articleId" =: 41 :. "price" =: 15 ] ]


  (xs :: [Order]) <- selectSch (MyAnn "orders") conn $ qRoot do
    qWhere $ "created_at" >? someDay
      &&& pchild "ord__ord_pos" defTabParam
        (pparent "ord_pos__article" $ "name" ~~? "%pencil%")
    qOrderBy [descf "created_at", descf "num"]
    qPath "ord__ord_pos" do
      qWhere $ pparent "ord_pos__article" $ "name" ~~? "%pencil%"
      qOrderBy [ascf "num"]
    qLimit 20

Module structure


[Skip to Readme]

Properties

Versions 0.5.0.0
Change log ChangeLog.md
Dependencies aeson (>=2.0 && <2.3), base (>=4.21.0 && <4.22), bytestring (>=0.12.2 && <0.13), case-insensitive (>=1.0 && <1.3), containers (>=0.7 && <0.9), directory (>=1.3.9 && <1.4), exceptions (>=0.9 && <0.11), mtl (>=2.0 && <2.4), pg-schema, postgresql-simple (>=0.6 && <0.8), scientific (>=0.2 && <0.4), singletons (>=3.0.3 && <3.1), singletons-base (>=3.4 && <3.6), singletons-th (>=3.4 && <3.6), text (>=2.0 && <2.2), time (>=1.12 && <2), uuid-types (>=1.0 && <1.1) [details]
License BSD-3-Clause
Copyright Dmitry Olshansky
Author Dmitry Olshansky
Maintainer olshanskydr@gmail.com
Category Database
Home page https://github.com/odr/pg-schema/tree/master/pg-schema#readme
Bug tracker https://github.com/odr/pg-schema/issues
Source repo head: git clone https://github.com/odr/pg-schema
Uploaded by odr at 2026-03-22T06:00:01Z

Modules

[Index] [Quick Jump]

Flags

Manual Flags

NameDescriptionDefault
arbitrary

Make Arbitrary instances for all types (Enums, PgTag, SchList)

Disabled
debug

Trace queries

Disabled
flat

Make Flat instances for all types (Enums, PgTag, SchList)

Disabled
hashable

Make Hashable instances for all types (Enums, PgTag, SchList)

Disabled

Use -f <flag> to enable a flag, or -f -<flag> to disable that flag. More info

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


Readme for pg-schema-0.5.0.0

[back to package description]

pg-schema

pg-schema is a Haskell library that lifts a PostgreSQL schema description to the type level and gives you type-safe, compile-time-checked access to data.

The core idea is a type provider: from a live database (tables, primary and unique keys, foreign keys, column types—including arrays and enums) a schema representation is built that you then use with ordinary GHC types. You can have any number of such schemas; each may include any subset of tables.

To generate schemas you create a separate application that uses PgSchema.Generation.updateSchemaFile to produce .hs files describing each schema (sets of tables, relationships between them, and the types in use).

For reading and writing data, use the PgSchema.DML module. Queries are built from a typed EDSL, without hand-written SQL. You can describe data trees (nested records along relationships), including inserting an entire tree in one database round-trip (JSON is used internally), and fetching a tree with a single SELECT, with predicates, ordering, and limits at each level of the tree (WHERE, ORDER BY, LIMIT/OFFSET, and so on).

Inserts and reads use either ordinary Haskell records with a Generic instance or types of the form "fld1" := Int32 :. "fld2" := Maybe Text. Field names in Haskell records must match those in the database (with renaming supported) but you can work with any subset of the table's fields. Navigation along relationships uses foreign-key constraint names. Types and nullability are checked against the data layout.

SELECT and INSERT/UPSERT are implemented for the nested structures. UPDATE and DELETE apply to a single table.

When reading data, an EDSL sets conditions, ordering, and grouping. All of these operations are type-safe.

For inserts and updates, additional compile-time checks derive from database constraints—for example, inserts verify that mandatory fields are present at every level.

Requirements

GHC ≥ 9.10. Theoretically, one could target older GHC version at the cost of a slightly worse API without RequiredTypeArguments.