Predict: Detecting Columns from SQL
Setting predict: true populates columns automatically at preprocessing time by parsing query (or the file named
by queryName) as a plain SQL SELECT and detecting its projected columns — the exact column-detection mechanism
Query Predict has always used, ported into a standalone lib/sqlpredict
package (no dependency on the rest of Emi’s core) so vsql can use it without Query Predict needing to stick around.
It is a no-op whenever columns is already non-empty — a hand-written list always wins, never silently overwritten.
vsqls: - name: listActiveUsers predict: true query: | SELECT id, email, field(count(o.order_id), 'int64', 'totalOrders', true) FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE u.id > {{ .MinId }} GROUP BY u.id;id/email come out as plain string columns (the default when no type hint is given); the
field(expr, 'type', 'goName', optional) marker — identical syntax to Query Predict’s own — names totalOrders
explicitly, types it int64, and its true fourth argument makes it optional, which comes out as
emigo.Nullable[int64] in the generated row.
What it can and can’t do
Section titled “What it can and can’t do”| Case | Behavior |
|---|---|
| Only a plain SELECT | The underlying parser implements MySQL grammar and has no notion of |
| Template markup | Every |
| A wildcard select |
|
| Joins with same-named columns |
|
| Genuine collisions | If two columns still resolve to the same Go name after that, preprocessing fails with a clear error rather than generating a struct with a duplicate field. |
A join example
Section titled “A join example”vsqls: - name: userOrderTotals predict: true query: | SELECT u.id, o.id AS order_id, u.email, o.amount_cents FROM users u JOIN orders o ON o.user_id = u.id;This generates a row with four distinct fields — UId, OrderId, UEmail, OAmountCents — instead of colliding
u.id and o.id into one duplicate Id field (the actual bug the original, unqualified column-detection logic had
before this fix). Cols() reprojects u.id, o.id AS order_id, u.email, o.amount_cents — every table qualifier and
alias preserved, so the regenerated SQL stays unambiguous against the same join.
examples/vsql-sqlite runs this exact query against a real, in-memory SQLite database with both tables populated.