Raw SQL
The query builder covers a wide surface, but when you need SQL it doesn’t model — an engine-specific construct, a query hint, a hand-tuned statement — drop to query.Raw[T]. It runs your SQL with bound args and scans the rows into any result type T. This is the deliberate escape hatch, not a fallback for missing features: everything on the advanced SQL page is typed and validated, so reach for Raw only past that edge.
For exhaustive API detail, see the reference at pkg.go.dev/liteorm.org/query.
Raw[T]
Section titled “Raw[T]”query.Raw[T] runs a statement and maps each row into a struct T — usually a small struct shaped to the projection, with db:"..." tags where the column names differ from the fields:
type catStat struct { Category string `db:"category"` Items int64 `db:"items"` Total int64 `db:"total"`}
stats, err := query.Raw[catStat](ctx, db, `SELECT category, count(*) AS items, sum(stock) AS total FROM products GROUP BY category ORDER BY total DESC`)Args bind positionally with ? markers and are renumbered for the dialect, so your SQL stays parameterized on every backend — pass values as arguments, never interpolate them into the string.
Raw[T] expects T to be a struct. For a single-column scalar, use query.Pluck (a typed column) or query.PluckExpr / PluckExprFirst (a raw expression) instead — they scan into []V / V. See streaming & aggregates.
Raw fragments inside a typed query
Section titled “Raw fragments inside a typed query”You rarely need a fully raw statement. Most one-off SQL fits inside a typed builder as a fragment, so you keep the typed predicates, ordering, and scanning around it:
Where("frag", args...)adds a raw,AND-joined predicate — for a condition that spans joined tables or uses SQL the typed predicates don’t cover.OrderBy("...")/GroupBy("...")take raw terms (a collation,NULLS FIRST, a function).Project("...")overrides the SELECT list;Expr("...")is the raw item inside anIntoprojection.Having("frag", args...)and a rawJoin("clause", args...)cover the remaining clauses.
recent, err := query.Select[Product](db). Filter(query.Col[bool]("active").Eq(true)). // typed Where("created_at > ?", cutoff). // raw fragment OrderBy("created_at DESC NULLS LAST"). // raw ordering All(ctx)A raw fragment is not column-validated — a typo in a Where string fails at the database, not at build time, unlike a typed Filter predicate. Keep as much of the query typed as you can, and reserve raw fragments for the piece that genuinely needs them.
No query hints — use Raw
Section titled “No query hints — use Raw”The builder deliberately has no API for engine-specific query hints (index hints, FORCE INDEX, optimizer directives, and the like). They’re non-portable by nature, so rather than model them per dialect, LiteORM leaves them to Raw — write the full statement with the hint inline and scan it into your struct. The same goes for any other backend-specific syntax the typed API doesn’t expose.
See also
Section titled “See also”- Advanced SQL — CTEs, window functions, set ops, and locking, all typed (try these before
Raw). - Streaming & aggregates —
Pluck/PluckExprfor single-column raw reads. - Reading data — the typed predicates a raw
Wherecomposes with. - Errors — how a failing raw statement surfaces.