Schema & Columns topic

Schema

A table is a final class extending TableRef<Self>, with a private const constructor and a static const table singleton. Columns are static const objects holding a typed reference to that singleton — using const means the very same TableColumn object is shared by the query builder (Users.age.gt(18)) and by derive annotations (@Column(Users.name)), since annotation arguments must be compile-time constants.

final class Users extends TableRef<Users> {
  const Users._() : super('users');

  static const table = Users._();

  static const id = PrimaryKey<int, Users>(table, 'id', IntSqlType());
  static const name = ValueColumn<String, Users>(table, 'name', StringSqlType());
  static const managerId = Ref<int?, Users, Users>(
      table, 'manager_id', NullableSqlType(IntSqlType()),
      references: Users.id);

  @override
  List<TableColumn<Object?, Object?>> get columns => const [id, name, managerId];
}

The shared type parameter ties each column to its own table: passing Customers.table to a TableColumn<T, Users> is a compile error.

Why columns is a getter

table and the columns reference each other — id holds table, and the default projection lists id. As two const fields that would be a const-initializer cycle, which Dart rejects. Overriding columns as a getter breaks the cycle: getters are evaluated lazily at runtime, so table's initializer depends on nothing and every column constant only depends on table. Foreign keys stay cycle-free the same way — a Ref points at the target's PrimaryKey constant, whose table singleton lists its columns only through the getter.

The column hierarchy

TableColumn is sealed: every column is exactly one of

  • ValueColumn — a plain column,
  • PrimaryKey — the table's primary key,
  • Ref — a foreign key, carrying its target table as a type parameter.

Being sealed lets the join API and codegen pattern-match on the column kind (e.g. to auto-derive FK-aware joins) and keeps the switch exhaustive as new column kinds are added.

Every TableColumn is also a Selection, so it can be read straight out of a row — see RowReader in Query Builder.

Table markers and sources

  • TableRef — the un-aliased source for a table (from(Users.table)).
  • TableAlias — an aliased source, used when the same table appears twice in one query (self-joins).
  • Aggregate / RawSelection / ColumnValue — supporting selectable/assignable types used by aggregation, raw SQL escape hatches, and INSERT/UPDATE values.

Classes

Aggregate<T> Schema & Columns
An aggregate over a column (or COUNT(*)), usable in select(...) and readable from a row via its readKey. Build with TableColumn.count, countAll, or the numeric aggregates (IntColumnAggregates).
ColumnValue<Tbl> Schema & Columns
A column-scoped assignment (column = value) for INSERT/UPDATE. The value is already encoded; Tbl keeps it bound to its table.
PrimaryKey<T, Tbl> Schema & Columns
A primary-key column.
RawSelection<T> Schema & Columns
A raw, typed SQL selection (escape hatch): emitted verbatim in the projection and read back by its readKey (the raw as alias). Uses ? placeholders.
Ref<T, Tbl, Target> Schema & Columns
A foreign-key column on Tbl that references the PrimaryKey of Target. The const graph stays acyclic even for mutual/self foreign keys: a Ref points at the target's PrimaryKey constant, whose own owner is a table singleton that lists its columns only through a lazily-evaluated getter. The shared T enforces matching key types.
TableAlias<Tbl> Schema & Columns
An aliased table for self-joins (the same table joined more than once). Columns are rebound to the alias, so sender.col(Users.id) serializes as "sender"."id" and is distinct from recipient.col(Users.id).
TableColumn<T, Tbl> Schema & Columns
A typed column belonging to table Tbl.
TableRef<Tbl> Schema & Columns
Table descriptor: a table marker extends TableRef<Self> and exposes a static const table singleton that its columns hold as their owner.
ValueColumn<T, Tbl> Schema & Columns
An ordinary value column.