Expression DSL

This page covers the building blocks behind the row-filter shortcuts in Row Filter Syntax: boolean combinators, terms, and the lower-level predicate constructors.

The DSL lives entirely in the root iceberg package (see exprs.go and predicates.go).

Boolean combinators

iceberg.NewAnd(a, b)              // a AND b
iceberg.NewAnd(a, b, c, d)        // a AND b AND c AND d (variadic)
iceberg.NewOr(a, b)               // a OR b
iceberg.NewOr(a, b, c, d)         // a OR b OR c OR d
iceberg.NewNot(a)                 // NOT a

NewAnd and NewOr accept two required arguments plus a variadic tail (exprs.go:226, exprs.go:287). They simplify automatically:

  • NewAnd(x, AlwaysTrue{}) reduces to x.
  • NewAnd(x, AlwaysFalse{}) reduces to AlwaysFalse{}.
  • NewOr(x, AlwaysFalse{}) reduces to x.
  • NewOr(x, AlwaysTrue{}) reduces to AlwaysTrue{}.
  • NewNot(NewNot(x)) reduces to x.

Constants

iceberg.AlwaysTrue{}
iceberg.AlwaysFalse{}

Both satisfy BooleanExpression. Use them as the identity element when composing filters dynamically.

Terms

A term is the left-hand side of a predicate. Iceberg-go has two flavors:

  • Reference("column_name") - an unbound term that names a column (exprs.go:373). Typing happens at bind time, when the expression is matched against a schema. This is what you almost always want.
  • BoundReference - the resolved form, produced by Reference.Bind(schema, caseSensitive) (exprs.go:389). You only encounter these when writing custom expression visitors.

The interfaces are:

type Term interface { ... }                        // shared marker
type UnboundTerm interface { Term; ... }           // pre-bind
type BoundTerm interface { Term; Ref() BoundReference; ... }

(exprs.go:317-348)

Predicates

A predicate applies an Operation to one or more terms. BooleanExpression is the shared interface (exprs.go:123).

For all the common shapes, the constructors in predicates.go are the right tool. See Row Filter Syntax for the full list (EqualTo, LessThan, IsIn, IsNull, StartsWith, etc.).

Lower-level escape hatches

When the convenience builders are not enough (custom operators, dynamic operation selection, working with already-typed Literal values), use the predicate constructors directly:

// Unary predicates: IS NULL / NOT NULL / IS NaN / NOT NaN
pred := iceberg.UnaryPredicate(iceberg.OpIsNull, iceberg.Reference("col"))

// Literal predicates: <, <=, >, >=, ==, !=, STARTS WITH, NOT STARTS WITH
lit := iceberg.NewLiteral(int64(42))
pred := iceberg.LiteralPredicate(iceberg.OpEQ, iceberg.Reference("col"), lit)

// Set predicates: IN / NOT IN
lits := []iceberg.Literal{iceberg.NewLiteral("a"), iceberg.NewLiteral("b")}
pred := iceberg.SetPredicate(iceberg.OpIn, iceberg.Reference("col"), lits)

UnaryPredicate lives at exprs.go:534. LiteralPredicate and SetPredicate live in the same file.

Negation

Every BooleanExpression and every Operation knows how to negate itself.

op := iceberg.OpEQ
op.Negate()  // -> OpNEQ

(exprs.go:65-98. OpNot, OpAnd, and OpOr panic on direct negation - negate the wrapping expression instead.)

expr := iceberg.EqualTo(iceberg.Reference("status"), "active")
inverted := expr.Negate()  // equivalent to NotEqualTo(...)

Binding and evaluation

Most user code stops at constructing the unbound expression - the scan pipeline handles binding, projection, and evaluation internally. If you are writing a custom visitor:

  • (Reference).Bind(schema, caseSensitive) returns a BoundTerm (exprs.go:389).
  • BoundExpressions expose Ref(), Type(), and (for terms) the underlying accessor for evaluating against a StructLike row.

For projection, evaluation, and visitor patterns, see visitors.go and the scan internals in table/scanner.go.

When to reach for what

GoalUse
Filter a scan or row-level deleteConvenience builders + NewAnd/NewOr/NewNot
Combine many clauses dynamicallyStart from AlwaysTrue{} (for AND) or AlwaysFalse{} (for OR), fold with NewAnd/NewOr
Construct a predicate whose operator is chosen at runtimeUnaryPredicate(op, term), LiteralPredicate(op, term, lit), SetPredicate(op, term, lits)
Walk an expression treeA custom BooleanExprVisitor from visitors.go

For the per-operator cookbook, return to Row Filter Syntax.