On this page
How inference works
A query's Go types come from its SQL expressions, not just the tables it reads. Seagull first resolves names within their scopes, computes SQL types and nullability, then maps those results into Go types. This separation lets the same analysed query target more than one driver API.
A result is not a table row
Consider a book's optional author:
SELECT b.title, a.name
FROM books b
LEFT JOIN authors a ON a.id = b.author_id;
Even if authors.name is NOT NULL, the join can find no author. The query's name field
therefore needs to represent NULL, usually as *string, while the table model keeps a
plain string. CTEs and derived tables carry the same distinction: their projections
are resolved in the scope where they are used.
Expressions can introduce NULL too. A scalar subquery may find no row; most aggregates
over no rows and a CASE without ELSE are nullable. Ordinary comparisons follow SQL's
three-valued logic, so comparing a nullable value can produce NULL. IS NULL always
produces a non-null test, and COALESCE can give a non-null alternative. These rules
explain why a result struct may differ from a table model even without an outer join.
Set operations combine the types of their branches. A cast changes the SQL type before Go mapping, while a dialect supplies engine-specific rules for operators and functions. For the precise nullability and collection rules, see type mapping.
Overrides are assertions
An SQL type override changes what the analyser believes the expression returns. A Go type override changes how that SQL value is represented. Neither converts stored data nor implements scanning for a custom type. Declaring a value non-null is an assertion that must hold when the query executes.
A direct column projection retains its catalogue identity and its overrides. Computing
lower(status) produces a new expression instead, so its type is inferred from the
function rather than the status column's Go override. CTEs and derived tables can lose
that identity too. This is why override scope
matters alongside precedence.
Schema inspection exposes the inferred types before you choose an override. Analysis guarantees explains the limits of inference without a database.