Annotations are comments at interface’s methods, Gen will parse them and generate the query API for the applied structs.
Gen provides some conventions for dynamic conditionally SQL support, let us introduce them from three aspects:
- Returning Results
- Template Placeholder
- Template Expression
Returning Results
Gen allows to configure returning result type. Basic types:
| Option | Description |
|---|---|
gen.T |
returns the applied model struct (value) |
gen.M |
returns map[string]interface{} |
gen.RowsAffected |
returns rows affected from the database (type: int64); makes the method execute with Exec |
error |
returns error if any |
gen.SQLResult |
returns sql.Result from a direct ExecContext call |
gen.SQLRow |
returns *sql.Row (single row from a raw query) |
gen.SQLRows |
returns *sql.Rows (row cursor from a raw query) |
gen.SQLResult/gen.SQLRow/gen.SQLRows are generation markers, not Go
type aliases you can assign through: in the interface you may write either the
marker or the standard-library spelling (sql.Result, *sql.Row, *sql.Rows
with database/sql imported) — the generator recognizes both and emits the
standard-library type in the generated method signature.
Rules enforced at generation time:
- at most one data value and at most one
errorin the result list
(query method cannot return more than 1 ... value); - a result of
interface{}(orany) is rejected; - a struct from the
mainpackage cannot be returned; - omitting
erroris allowed — the generated method then discards it.
e.g:
type Querier interface { |
The data type can be combined with other symbols like *, []. Each of the
following is an independent alternative signature (a single interface cannot
declare the same method name twice — Go has no overloading):
// alternative signatures for the same raw SQL: |
Template Placeholder
Gen provides some placeholders to generate dynamic & safe SQL
| Name | Description |
|---|---|
@@table |
the applied model’s table name, inserted at generation time as a quoted Go string |
@@<name> |
a table/column name taken from a string parameter, escaped & quoted at runtime via the DO’s Quote |
@<name> |
a SQL query parameter bound as ? from the parameter’s value |
\@ |
a literal @ character |
@@<name> requires the corresponding method parameter to be a plain
(non-array) string; anything else fails generation withvariable name must be string :<name> type is <type>. @<name> accepts any
type; slices are bound as one argument and expanded by GORM (WHERE id IN @ids
-> IN (?, ?, ...)).
Generated code for @@<name> (from gen’s test fixtures):
generateSQL.WriteString("select * from users where " + u.Quote(name) + " in ? ") |
e.g:
type Filter interface { |
After generate the code, you can use it like this in your application.
SQL form markers
The whole comment body can optionally be wrapped to tell Gen how the SQL is
used (both markers are case-insensitive and allow the SQL to be additionally
wrapped in double quotes):
where(...)— the method is a condition snippet: the generated code callsUnderlyingDB().Where(sql, params...)instead ofRaw/Exec, so the snippet
can be combined with the typed query API (see
Dynamic SQL — Code Snippets);sql(...)— the method is an explicit raw-SQL query (same execution path as no
marker; useful to make intent visible or to start the SQL with awhere(
keyword).
type Querier interface { |
Without a marker, a method whose comment contains a blank comment line treats
the text after the blank line as the SQL and everything above as the method
description (this is why the blank line in the examples is required when you
describe the method above the SQL).
Application usage skeleton for FilterWithColumn: generate withgen.Config{OutPath: "./query", Mode: gen.WithDefaultQuery} and apply Filter
to both models before g.Execute(). Substitute your project’s imports and DSN.
The application opens and checks its own runtime DB, independently of any
schema connection used by the generator, then initializes the default queries:
import ( |
All subsequent application snippets on this page share this setup: the generated
package includes WithDefaultQuery, query.SetDefault(db) has run, and ctx is
defined. They are independent query/SQL illustrations, not standalone programs;
import your generated model package and time where used, consume returned
values, and check each returned error before issuing another query.
Template Expression
Gen provides powerful expressions support for dynamic conditional SQL, currently support following expressions:
if/elsewheresetfortrim
if/else
The if/else expression allows to use golang syntax as condition, it can be written like:
For example:
type Querier interface { |
A more complicated case:
type Querier interface { |
How it can be used:
query.User.WithContext(ctx).QueryWith(&model.User{Name: "zhangqiang"}) |
where
The where expression make you write the WHERE clause for the SQL query easier, let take a simple case as example:
type Querier interface { |
With the generated code, you can use it like:
query.User.WithContext(ctx).Query(10) |
Here is another complicated case, in this case, you will learn the WHERE clause only be inserted if there are any children expressions matched and it can smartly trim unnecessary and, or, xor, , inside the where clause (case-insensitive, both ends; the WHERE keyword is emitted only when something remains).
type Querier interface { |
The generated code can be used like:
var ( |
set
The set expression used to generate the SET clause for the SQL query, it will trim unnecessary , automatically, for example:
// UPDATE @@table |
(The method is named UpdateUserFields rather than Update — Update is part
of the generated CRUD API and reserved; see Errors and naming rules below.)
The generated code can be used like:
query.User.WithContext(ctx).UpdateUserFields(model.User{Name: "jinzhu", Age: 18}, 10) |
for
The for expression iterates over a slice to generate the SQL, let’s explain by example
// SELECT * FROM @@table |
Usage:
query.User.WithContext(ctx).Filter([]model.User{ |
trim
The trim expression strips one leading and one trailing and, or, xor
or , (case-insensitive) from whatever its children produced, and emits no
SQL keyword. It is useful inside for loops that build or-separated lists:
type TrimTest interface { |
Without {{trim}} the generated fragment would end with a dangling or.{{where}} and {{set}} apply the same trimming to their own content
automatically.
Errors and naming rules
Template and SQL problems are reported by the generator with the source
location of the method comment (file, line, column) and the offending snippet:
unknown syntax: <word>— a{{...}}block that is notif/else/for/where/set/trim/end;incomplete SQL— unbalanced quotes or an unterminated `