Kraken.Core
Types
Array
opaque type Array aJsonb
opaque type Jsonb aTypeTag
type TypeTag a =
| TypeTagA phantom carrier for a type, so a cast can name its target type without having a value of it.
Nullable
opaque type Nullable colsA column scope on the nullable side of a left join.
Wraps a table's column record. Because the joined row may be absent, selecting
a Nullable scope yields Maybe row rather than row.
Table
opaque type Table colsColRef
opaque type ColRefAn erased column reference (name + source) that drops the value type, so a primary key can name columns of differing types.
PrimaryKey
type PrimaryKey =
| NoKey
| Key ColRef
| Composite (List ColRef)A table's primary key, as declared by ColumnSet.primary_key.
Col
opaque type Col aGenerated
opaque type Generated aA column the database fills in (SERIAL / identity / DEFAULT). Marking a
schema column Generated a instead of Col a keeps it readable and usable in
predicates exactly like a normal column, but the type prevents it from being
supplied in an insert input — id-omission becomes a type guarantee, not a
convention.
DefaultValue
type DefaultValue a =
| UseDefault
| Provide aThe insert value for a column that may use its table default. UseDefault
renders SQL DEFAULT; Provide v binds an explicit value. This is different
from Maybe: Nothing binds SQL NULL, while UseDefault asks the database to
compute the column default.
GeneratedValue
type alias GeneratedValue a = DefaultValue aSetExpr
opaque type SetExprOne column = <expr> assignment in an upsert DO UPDATE SET. Build with assign.
ConflictTarget
type ConflictTarget =
| OnColumns (List ColRef)
| OnConstraint StringWhere an ON CONFLICT detects a collision: a column list
(on_columns [Db.ref u.id] → ON CONFLICT (id)) or a named constraint
(on_constraint "users_email_key" → ON CONFLICT ON CONSTRAINT users_email_key).
SqlPart
type SqlPart =
| Text String
| Param ValueSqlFrag
record SqlFrag {
parts: List SqlPart
}Fragment
opaque type FragmentSql
opaque type Sql aSqlArg
opaque type SqlArgExpr
opaque type ExprSelectItem
record SelectItem {
expr: SqlFrag,
alias: String,
relabelable: Bool
}Order
opaque type OrderGroup
opaque type GroupField
opaque type Field aRelSlot
type RelSlot =
| RelSlot Int IntLocates a relation's parent-key column during the first decode pass: the relation's
index (its slot, matching RelData) and the column offset of its key in the row.
Both plain ints — the key value itself is read later, typed, from the raw rows.
RelData
opaque type RelDataLoaded children for each relation, keyed by relation index. The values are the one
irreducible erasure: each is a Dict parent_key (List child) held opaquely because
relations in one query have differing child types. Empty during the first pass.
Projection
opaque type Projection aPreloaded
opaque type Preloaded outA relation field in a select: it occupies one selected column (its parent
key) but decodes to out — the children loaded by a separate query, shaped by
how the relation is consumed (List child for a to-many preload, Maybe child
for a to-one preload_one). Built by the query layer; selecting it contributes
the parent-key column to the SELECT and a deferred decode that resolves children
from the executor-supplied RelData.
Selection
type Selection =
| SelectionWindow
opaque type WindowA window specification — the OVER (…) frame. Build with window, then add
partition_by / order_window. Reuses Db.group for PARTITION BY and
Db.asc/Db.desc for the window's ORDER BY.
Traits
PgType
trait PgType a {
fun encode_pg : a -> Value
fun decode_pg : Decoder a
}PgTypeName
trait PgTypeName a {
fun pg_type_name : TypeTag a -> String
}The Postgres type name a Saga type casts to (x::<name>). Only types you cast
to need an instance.
ColumnSet
trait ColumnSet cols {
fun columns : String -> cols
fun column_names : cols -> List (String, String)
fun primary_key : cols -> PrimaryKey
}AsColRef
trait AsColRef c {
fun as_col_ref : c -> ColRef
}Columns (Col / Generated) that can be erased to a ColRef.
ToSql
trait ToSql input a {
fun to_sql : input -> Sql a
}ToArraySql
trait ToArraySql input a {
fun to_array_sql : input -> Sql (Array a)
}ToJsonbSql
trait ToJsonbSql input a {
fun to_jsonb_sql : input -> Sql (Jsonb a)
}Functions
array
fun array : List a -> Array aarray_to_list
fun array_to_list : Array a -> List ajsonb
fun jsonb : a -> Jsonb ajsonb_to_value
fun jsonb_to_value : Jsonb a -> aencode_via
fun encode_via : a -> b -> a -> Value where {b: PgType}Encode a by mapping it to a representation b that already has PgType.
The forward direction is total: encode_via to_rep.
decode_via
fun decode_via : b -> Result a String -> Decoder a where {b: PgType}Decode a from a representation b that has PgType, via a fallible
back-mapping — a stored value may not correspond to any a (an unknown enum
label, an unparseable number), so from_rep returns Result a String and the
Err message surfaces as a decode failure.
enum_text_encode
fun enum_text_encode : List (a, String) -> a -> Value where {a: Eq}Encode an enum-like type as text via an explicit value→label table, e.g. for a
Postgres enum column (or a text column with fixed values). The table is the
single source of truth for the wire labels — which can differ freely from the
Saga constructor names (SCREAMING_SNAKE ↔ friendly). Pair with
enum_text_decode over the same table in a tiny impl PgType:
type Status = Active | Archived deriving (Eq) status_labels = [(Active, "ACTIVE"), (Archived, "ARCHIVED")] impl PgType for Status { encode_pg = enum_text_encode status_labels decode_pg = enum_text_decode status_labels }
Panics if value is missing from the table (an incomplete mapping is a
programmer error, not a data error).
enum_text_decode
fun enum_text_decode : List (a, String) -> Decoder aDecode an enum-like type from text via an explicit value→label table (the
inverse of enum_text_encode). An unrecognized label fails the decode rather
than guessing.
as_nullable
fun as_nullable : cols -> Nullable colsMark a column scope as nullable. Used by left_join! to wrap the joined
table's columns so that selecting the scope produces Maybe row.
unwrap_cols
fun unwrap_cols : Nullable cols -> colsUnwrap a nullable scope to its underlying columns.
Use this only to reference a left-joined table's columns in post-join
predicates and ordering (where_!, order_by!, having!) — most commonly an
anti-join such as is_null (unwrap_cols p).id. SQL ignores result nullability
in those positions, so the plain columns are appropriate.
Do not select the unwrapped column directly: that decodes it as non-null and
turns absent joined rows into runtime decode errors. For scalar selections,
use read_maybe; for whole rows, use read_nullable. Join ON clauses never
need this — inner_join!/left_join! already pass their callback the plain
columns.
key
fun key : c -> PrimaryKey where {c: AsColRef}Name a single-column primary key (the common case): primary_key u = key u.id.
ref
fun ref : c -> ColRef where {c: AsColRef}Erase a column to a ColRef, for assembling a composite key.
composite
fun composite : List ColRef -> PrimaryKeyName a composite (multi-column) primary key.
primary_key_columns
fun primary_key_columns : PrimaryKey -> List StringThe primary key's column names in order (empty for NoKey).
ref_name
fun ref_name : ColRef -> StringThe column name a ColRef points to (e.g. for an ON CONFLICT target).
column_name
fun column_name : String -> c -> (String, String) where {c: AsColRef}Pair a Saga field label with the SQL column name from a typed column handle.
Use this in ColumnSet.column_names when the Saga field and SQL column differ:
column_names a = [column_name "email" a.email].
default
fun default : DefaultValue aUse the table's default for this column.
auto
fun auto : DefaultValue aLet the database assign a generated column (the common case).
provide
fun provide : a -> DefaultValue aForce a specific value into a defaulted or normally-generated column.
table
fun table : String -> Table cols where {cols: ColumnSet}cte_table
fun cte_table : String -> String -> cols -> Table colsBuild a Table whose columns come from a supplied scope-builder rather than a
ColumnSet — for CTEs and other derived sources, where the columns are whatever
the subquery selected (named by make_scope at the reference alias). The result
is referencable by from! / inner_join! / left_join! like any table.
table_as
fun table_as : String -> Table cols -> Table colstable_name
fun table_name : Table cols -> Stringtable_alias
fun table_alias : Table cols -> Maybe Stringtable_cols
fun table_cols : String -> Table cols -> colscol
fun col : String -> String -> Col agenerated
fun generated : String -> String -> Generated aDeclare a DB-generated column in a ColumnSet impl, mirroring col.
encode_value
fun encode_value : a -> Value where {a: PgType}Encode a value into a Postgres bind parameter via its PgType instance.
col_name
fun col_name : Col a -> StringThe bare column name (no table qualifier), e.g. for an UPDATE ... SET target.
generated_name
fun generated_name : Generated a -> Stringexcluded
fun excluded : Col a -> Col aReference a column on the EXCLUDED pseudo-table — the row proposed by the
conflicting insert — inside an upsert DO UPDATE: Db.excluded u.name renders
EXCLUDED.name. The existing row is referenced by the plain column (u.name).
assign
fun assign : Col a -> rhs -> SetExpr where {rhs: ToSql a}Assign a column an arbitrary SQL expression in a DO UPDATE SET. The RHS is any
ToSql value of the column's type, so it can combine the existing row, the
proposed row, and literals: Db.assign u.count (Db.add_sql u.count (Db.excluded u.count)) → count = users.count + EXCLUDED.count. Listing only the columns you
want also gives a partial DO UPDATE (a subset, not the blanket overwrite).
set_expr_column
fun set_expr_column : SetExpr -> Stringset_expr_frag
fun set_expr_frag : SetExpr -> SqlFragon_columns
fun on_columns : List ColRef -> ConflictTargeton_constraint
fun on_constraint : String -> ConflictTargetexpr_from_frag
fun expr_from_frag : SqlFrag -> ExprBuild a boolean Expr from a raw fragment. Used by the query layer to wrap a
rendered subquery (e.g. EXISTS (…)) as a predicate; the fragment's Params are
numbered by the enclosing query at render time.
empty_rel_data
fun empty_rel_data : RelDataThe empty relation data used during the first decode pass (and by callers with no relations at all).
rel_data_of
fun rel_data_of : List (Int, Dynamic) -> RelDataBuild relation data from (relation index, grouped-children-as-Dynamic) pairs.
rel_data_get
fun rel_data_get : Int -> RelData -> Maybe DynamicLook up a relation's grouped children (still opaque) by its index.
make_preloaded
fun make_preloaded : SelectItem -> RelData -> Int -> Dynamic -> Result (out, List RelSlot) DecodeError -> Preloaded outBuild a Preloaded field from its injected parent-key column and a resolver. The
query layer supplies both; the resolver closes over the child types and the
to-many/to-one shaping, so Preloaded is parameterized only by the output out.
map
fun map : a -> b -> Projection a -> Projection bselection
fun selection : Selectionprojection_into
fun projection_into : a -> b -> Projection (a -> b)Start building a projection for a constructor. This is the explicit primitive
used by build Selection Record { ... }.
projection_with
fun projection_with : Projection a -> Projection (a -> b) -> Projection bFeed one projected value into a projected constructor. The constructor projection is the piped value:
build Selection User { id: Db.read u.id, name: Db.read u.name, }
projection_field
fun projection_field : String -> Projection a -> Projection (a -> b) -> Projection bsql_text
fun sql_text : String -> Fragmentsql_param
fun sql_param : Value -> Fragmentsql_value
fun sql_value : a -> Fragment where {a: PgType}sql_column
fun sql_column : Col a -> Fragmentsql_from_frag
fun sql_from_frag : SqlFrag -> Sql a where {a: PgType}Wrap a raw fragment as a typed Sql a, decoding the result with a's default
PgType decoder. Exposed so the query layer can present a rendered scalar
subquery (SELECT … ) as a first-class Sql a.
sql
fun sql : input -> SqlArg where {input: ToSql a}value
fun value : a -> SqlArg where {a: PgType}lit
fun lit : a -> Sql a where {a: PgType + PgTypeName}A typed literal as a first-class Sql a: a bound parameter with an explicit
::type cast ($1::text). Use it for literals in positions where Postgres can't
infer the parameter type from context — concat, case_when branches, a bare
select of a constant — which otherwise fail with "could not determine data type
of parameter". In a comparison the other operand supplies the type, so Db.value
/ the eq family don't need this.
raw
fun raw : String -> List SqlArg -> Sql a where {a: PgType}raw_array
fun raw_array : String -> List SqlArg -> Sql (Array a) where {a: PgType}raw_array_like
fun raw_array_like : Col (Array a) -> String -> List SqlArg -> Sql (Array a) where {a: PgType}expr_raw
fun expr_raw : String -> List SqlArg -> Exprexpr
fun expr : Sql Bool -> Exprexpr_frag
fun expr_frag : Expr -> SqlFragfrag_of
fun frag_of : input -> SqlFrag where {input: ToSql a}The SQL fragment of any ToSql value (a column, raw expression, aggregate, …).
Exposed so the query layer can build predicates like IN (subquery) from a
left-hand value and a rendered subquery.
cast
fun cast : input -> Sql b where {input: ToSql a, b: PgType + PgTypeName}A SQL cast: render (<input>)::<target> and decode as the target type. The
result is a first-class Sql b usable in select / predicates. This is a
typed assertion, not a checked conversion — you vouch that the cast yields a
b (Postgres validates at runtime, like raw); Kraken just renders ::b and
decodes accordingly. The target b must be annotated or fixed by an as_*
helper, since it isn't determined by the input.
as_int
fun as_int : input -> Sql Int where {input: ToSql a}<input>::integer.
as_float
fun as_float : input -> Sql Float where {input: ToSql a}<input>::double precision.
as_text
fun as_text : input -> Sql String where {input: ToSql a}<input>::text — e.g. a timestamp or number rendered as a string.
as_bool
fun as_bool : input -> Sql Bool where {input: ToSql a}<input>::boolean.
as_timestamp
fun as_timestamp : input -> Sql NaiveDateTime where {input: ToSql a}<input>::timestamp — e.g. parse a string column as a timestamp.
as_date
fun as_date : input -> Sql Date where {input: ToSql a}<input>::date.
as_time
fun as_time : input -> Sql Time where {input: ToSql a}<input>::time.
sql_fn
fun sql_fn : String -> List SqlArg -> Sql a where {a: PgType}A generic SQL function call: name(arg1, arg2, …). Arguments are SqlArgs, so
pass columns/expressions with Db.sql and literals with Db.value. The result
type isn't inferable from the arguments, so annotate it or let the selection fix
it: Db.sql_fn "lower" [Db.sql u.name] : Db.Sql String.
coalesce
fun coalesce : input -> a -> Sql a where {input: ToSql a, a: PgType}COALESCE(<input>, <fallback>) — the first non-NULL of the two. The principled
way to recover a non-null scalar from a left join: after Db.unwrap_cols, a
left-joined column reads as Col a, and coalesce pairs it with a default of
the same type, yielding a non-null Sql a you can put in select
(Db.coalesce (Db.unwrap_cols p).count 0 → COALESCE(t1.count, $1)).
coalesce_sql
fun coalesce_sql : left -> right -> Sql a where {left: ToSql a, right: ToSql a, a: PgType}COALESCE(<left>, <right>) over two SQL values of the same type (e.g. two
columns) rather than a literal fallback.
not_
fun not_ : Expr -> ExprBoolean negation: NOT (<expr>). Complements and_ / or_ / is_null.
lower
fun lower : input -> Sql String where {input: ToSql String}LOWER(<input>).
upper
fun upper : input -> Sql String where {input: ToSql String}UPPER(<input>).
trim
fun trim : input -> Sql String where {input: ToSql String}TRIM(<input>).
concat
fun concat : List SqlArg -> Sql StringCONCAT(arg1, arg2, …) — NULL arguments are treated as empty (unlike ||).
Arguments are SqlArgs (Db.sql for columns, Db.value for literals).
add
fun add : input -> a -> Sql a where {input: ToSql a, a: PgType}(<input> + <literal>) — e.g. the upsert idiom count = users.count + 1.
sub
fun sub : input -> a -> Sql a where {input: ToSql a, a: PgType}(<input> - <literal>).
mul
fun mul : input -> a -> Sql a where {input: ToSql a, a: PgType}(<input> * <literal>).
div
fun div : input -> a -> Sql a where {input: ToSql a, a: PgType}(<input> / <literal>).
add_sql
fun add_sql : left -> right -> Sql a where {left: ToSql a, right: ToSql a, a: PgType}(<left> + <right>) between two SQL values (e.g. price + tax).
sub_sql
fun sub_sql : left -> right -> Sql a where {left: ToSql a, right: ToSql a, a: PgType}(<left> - <right>).
mul_sql
fun mul_sql : left -> right -> Sql a where {left: ToSql a, right: ToSql a, a: PgType}(<left> * <right>) — e.g. price * quantity.
div_sql
fun div_sql : left -> right -> Sql a where {left: ToSql a, right: ToSql a, a: PgType}(<left> / <right>).
case_when
fun case_when : List (Expr, SqlArg) -> SqlArg -> Sql a where {a: PgType}CASE WHEN <cond> THEN <val> … ELSE <else> END. Each branch pairs a predicate
with a SqlArg result; the else is the fallback SqlArg. Branch and else
values use Db.sql (columns/expressions) or Db.value (literals). The result
type is fixed by annotation or the selection. Panics with no branches.
eq
fun eq : input -> a -> Expr where {input: ToSql a, a: PgType}not_eq
fun not_eq : input -> a -> Expr where {input: ToSql a, a: PgType}gt
fun gt : input -> a -> Expr where {input: ToSql a, a: PgType}gte
fun gte : input -> a -> Expr where {input: ToSql a, a: PgType}lt
fun lt : input -> a -> Expr where {input: ToSql a, a: PgType}lte
fun lte : input -> a -> Expr where {input: ToSql a, a: PgType}eq_sql
fun eq_sql : left -> right -> Expr where {left: ToSql a, right: ToSql a, a: PgType}eq_col
fun eq_col : left -> right -> Expr where {left: ToSql a, right: ToSql a, a: PgType}not_eq_sql
fun not_eq_sql : left -> right -> Expr where {left: ToSql a, right: ToSql a, a: PgType}Comparisons between two SQL values (columns, expressions, scalar subqueries)
rather than against a literal. The eq family compares against a literal RHS;
these compare two ToSql operands of the same element type — e.g.
Db.gt_sql u.age (Db.scalar_subquery …).
gt_sql
fun gt_sql : left -> right -> Expr where {left: ToSql a, right: ToSql a, a: PgType}gte_sql
fun gte_sql : left -> right -> Expr where {left: ToSql a, right: ToSql a, a: PgType}lt_sql
fun lt_sql : left -> right -> Expr where {left: ToSql a, right: ToSql a, a: PgType}lte_sql
fun lte_sql : left -> right -> Expr where {left: ToSql a, right: ToSql a, a: PgType}like
fun like : input -> String -> Expr where {input: ToSql String}ilike
fun ilike : input -> String -> Expr where {input: ToSql String}between
fun between : input -> a -> a -> Expr where {input: ToSql a, a: PgType}contains
fun contains : input -> Array a -> Expr where {input: ToArraySql a, a: PgType}contained_by
fun contained_by : input -> Array a -> Expr where {input: ToArraySql a, a: PgType}overlaps
fun overlaps : input -> Array a -> Expr where {input: ToArraySql a, a: PgType}json_contains
fun json_contains : input -> Jsonb a -> Expr where {input: ToJsonbSql a, a: ToJson + FromJson}json_has_key
fun json_has_key : input -> String -> Expr where {input: ToJsonbSql a}json_text
fun json_text : input -> String -> Sql String where {input: ToJsonbSql a}in_
fun in_ : input -> List a -> Expr where {input: ToSql a, a: PgType}not_in
fun not_in : input -> List a -> Expr where {input: ToSql a, a: PgType}eq_any
fun eq_any : input -> List a -> Expr where {input: ToSql a, a: PgType}not_eq_all
fun not_eq_all : input -> List a -> Expr where {input: ToSql a, a: PgType}like_any
fun like_any : input -> List String -> Expr where {input: ToSql String}ilike_any
fun ilike_any : input -> List String -> Expr where {input: ToSql String}is_null
fun is_null : input -> Expr where {input: ToSql a, a: PgType}is_not_null
fun is_not_null : input -> Expr where {input: ToSql a, a: PgType}and_
fun and_ : List Expr -> Expror_
fun or_ : List Expr -> Exprnullable_row
fun nullable_row : Projection a -> Projection (Maybe a)Adapt a whole-row projection into its nullable form for left joins.
If every column the projection spans is SQL NULL in a result row, it decodes
to Nothing; otherwise the row is decoded and wrapped in Just. The all-NULL
rule remains correct even when a domain field is itself nullable, since a
matched row still carries at least one non-null required column.
projection_relabel
fun projection_relabel : String -> Projection a -> Projection aRelabel a projection for a record field. This is the explicit primitive used by
build Selection Record { field: projection }: single-column fields take the field
label directly, while multi-column fields get prefixed labels like user_id.
projection_selections
fun projection_selections : Projection a -> List SelectItemmake_projection
fun make_projection : List SelectItem -> Int -> RelData -> Int -> Dynamic -> Result (a, List RelSlot) DecodeError -> Projection aBuild a Projection from explicit selections, width, and a relation-aware
decoder. Exposed so the query layer can construct a Preloaded field's
projection (its single parent-key column plus a deferred child-resolving decode).
as_sql
fun as_sql : input -> Sql a where {input: ToSql a}Coerce any column-like value (Col / Generated / Sql) to a Sql a. Exposed
so the query layer can accept a relation key as a column accessor regardless of
whether the schema field is a plain or generated column.
read
fun read : input -> Projection a where {input: ToSql a}Project any SQL-like value (Col, Generated, or Sql) as a single decoded
column. This is the explicit leaf used by projection_into / projection_with.
read_sql
fun read_sql : Sql a -> Projection aProject an already-built SQL expression. Alias is supplied by the surrounding projection construction or by the generated select item.
read_nullable
fun read_nullable : Nullable cols -> cols -> Projection a -> Projection (Maybe a)Project a nullable scope through a whole-row projection. This keeps the left
join all-NULL sentinel in one named operation instead of making callers unwrap
a scope directly at a select site.
read_maybe
fun read_maybe : Nullable cols -> cols -> input -> Projection (Maybe a) where {input: ToSql a}Project one SQL-like value from a nullable scope as Maybe a. This is the
scalar sibling of read_nullable:
Db.read_maybe p (fun p -> p.title) decodes Maybe String.
read_preloaded
fun read_preloaded : Preloaded out -> Projection outProject a preloaded relation field. It contributes the hidden parent-key column and decodes through the relation data stitched in by the query executor.
col_select_item
fun col_select_item : String -> Sql a -> SelectItemA SelectItem selecting a single Sql value under the given alias. Exposed so
the query layer can inject a relation's parent-key column into the SELECT.
col_decoder
fun col_decoder : Sql a -> Int -> Dynamic -> Result a DecodeErrorA positional decoder for a single Sql value, using its own decoder. Exposed so
the query layer can read a relation's parent-key value at decode time.
col_projection
fun col_projection : Sql a -> Projection aA single-column Projection for a Sql value. Exposed so the query layer can
pair a relation's foreign-key column alongside the child row.
project_pair
fun project_pair : Projection a -> Projection b -> Projection (a, b)Pair two projections into one yielding a tuple (selections concatenated, widths
summed, decodes threaded). Exposed for relation loading's (fk, child) rows.
decode_projection
fun decode_projection : Projection a -> RelData -> Dynamic -> Result (a, List RelSlot) DecodeErrorDecode a result row through a projection, threading relation data and collecting
any relation keys the row's Preloaded slots emit. Callers with no relations
pass empty_rel_data and ignore the (empty) key list.
asc
fun asc : input -> Order where {input: ToSql a, a: PgType}desc
fun desc : input -> Order where {input: ToSql a, a: PgType}group
fun group : input -> Group where {input: ToSql a, a: PgType}order_expr
fun order_expr : Order -> SqlFragorder_direction
fun order_direction : Order -> Stringgroup_frag
fun group_frag : Group -> SqlFragcount_star
fun count_star : Sql Intcount
fun count : input -> Sql Int where {input: ToSql a, a: PgType}count_distinct
fun count_distinct : input -> Sql Int where {input: ToSql a, a: PgType}sum
fun sum : input -> Sql (Maybe a) where {input: ToSql a, a: PgType}avg
fun avg : input -> Sql (Maybe Float) where {input: ToSql a, a: PgType}min
fun min : input -> Sql (Maybe a) where {input: ToSql a, a: PgType}max
fun max : input -> Sql (Maybe a) where {input: ToSql a, a: PgType}window
fun window : WindowAn empty window (OVER ()). Refine it with partition_by / order_window.
partition_by
fun partition_by : List Group -> Window -> WindowSet the window's PARTITION BY keys.
order_window
fun order_window : List Order -> Window -> WindowSet the window's ORDER BY terms (needed for ranking / running aggregates).
over
fun over : Sql a -> Window -> Sql aTurn any Sql a into its windowed form: <expr> OVER (<window>). Most useful on
an aggregate for a running / partitioned total —
Db.over (Db.sum u.amount) (Db.partition_by [Db.group u.dept] Db.window) →
SUM(u.amount) OVER (PARTITION BY u.dept). The decoder is preserved, so a
windowed sum is still Sql (Maybe a).
row_number
fun row_number : Window -> Sql IntROW_NUMBER() OVER (<window>) — sequential row number within each partition.
rank
fun rank : Window -> Sql IntRANK() OVER (<window>) — rank with gaps after ties.
dense_rank
fun dense_rank : Window -> Sql IntDENSE_RANK() OVER (<window>) — rank without gaps after ties.
lag
fun lag : input -> Window -> Sql (Maybe a) where {input: ToSql a, a: PgType}LAG(<input>) OVER (<window>) — the value from the previous row in the window
(NULL on the first row, hence Maybe a).
lead
fun lead : input -> Window -> Sql (Maybe a) where {input: ToSql a, a: PgType}LEAD(<input>) OVER (<window>) — the value from the next row in the window
(NULL on the last row, hence Maybe a).