ubgo is in early alpha: libraries and APIs may still change.

Skip to content
ubgo Alpha

Typed columns: mistakes fail the build

In sqlb every column has a Go type, so comparing, scanning or joining it the wrong way stops go build instead of failing in production.

Based on the sqlb README, section “Checked three times, not claimed once” About sqlb →

Step 1 of 8

Declare the table once

A library declares its columns as package-level values. Col[T] is where the type comes from: Email is an sqlb.Expr[string] and CreatedAt an sqlb.Expr[time.Time].

All steps as text
  1. Declare the table once. A library declares its columns as package-level values. Col[T] is where the type comes from: Email is an sqlb.Expr[string] and CreatedAt an sqlb.Expr[time.Time].
  2. A correct query builds. Comparing the string column ID with a string, and scanning Email into a string, type checks. go build succeeds and Build renders the SQL.
  3. Wrong type in a comparison. Comparing the string column Email with the number 42 does not compile. The compiler stops the build before any SQL exists.
  4. Wrong scan target. Binding the string column Email to an *int does not compile either, so a scan can never write into the wrong kind of variable.
  5. Joining mismatched columns. EqExpr compares two columns at their Go types. Joining the string ID to the time.Time CreatedAt does not compile.
  6. A Postgres clause on SQLite. Engine clauses are typed to their dialect. pg.ForUpdate() on a SQLite statement does not compile.
  7. Engine expressions: caught at Build. This is the honest limit of the type system. A pgvector expression under the SQLite dialect compiles, but Build returns ErrUnsupportedDialect naming the construct, in your process, before anything reaches a database.
  8. A real database checks execution. Rendering SQL is not the same as running it. Every driver runs a shared conformance suite, and the pgx driver executes every pg construct (593 cases) against Postgres 17.

The compiler messages in the diagram are the real output of go build for those statements, built against the sqlb source, with only the file:line:col prefix removed.

The README’s own list of what does not compile includes a string column bound to an *int, a time column compared with a string, a vector distance over a text column, and a sum over a non-numeric column. examples/typecheck in the repository holds deliberately wrong statements like these behind a build tag, and the project’s gate fails if that file ever compiles.

var Table = sqlb.Table{Name: "app_user", Alias: "u"}

var (
	ID        = sqlb.Col[string](Table, "id")
	Email     = sqlb.Col[string](Table, "email")
	CreatedAt = sqlb.Col[time.Time](Table, "created_at")
)