The gorm.io/gorm/clause package provides types and builders for constructing SQL clauses programmatically. This is useful for building complex queries, implementing custom dialects, and creating advanced query patterns.
import "gorm.io/gorm/clause"Special identifiers and options for clause building.
const (
// Special identifiers for dynamic clause building
PrimaryKey string = "~~~py~~~" // Primary key placeholder
CurrentTable string = "~~~ct~~~" // Current table placeholder
Associations string = "~~~as~~~" // Associations placeholder
// WHERE clause operators
AndWithSpace = " AND "
OrWithSpace = " OR "
// Locking strength options
LockingStrengthUpdate = "UPDATE"
LockingStrengthShare = "SHARE"
// Locking behavior options
LockingOptionsSkipLocked = "SKIP LOCKED"
LockingOptionsNoWait = "NOWAIT"
)
var PrimaryColumn = Column{Table: CurrentTable, Name: PrimaryKey}Base interface for all SQL expressions.
type Expression interface {
Build(builder Builder)
}Interface for building SQL strings.
type Writer interface {
WriteByte(byte) error
WriteString(string) (int, error)
}
type Builder interface {
Writer
WriteQuoted(field interface{})
AddVar(Writer, ...interface{})
AddError(error) error
}Interface for SQL clauses.
type Interface interface {
Name() string
Build(Builder)
MergeClause(*Clause)
}Interface for expressions that can be negated.
type NegationExpressionBuilder interface {
NegationBuild(builder Builder)
}type Clause struct {
Name string
BeforeExpression Expression
AfterNameExpression Expression
AfterExpression Expression
Expression Expression
Builder ClauseBuilder
}type Column struct {
Table string
Name string
Alias string
Raw bool
}type Table struct {
Name string
Alias string
Raw bool
}Raw SQL expression.
type Expr struct {
SQL string
Vars []interface{}
WithoutParentheses bool
}Usage:
import "gorm.io/gorm/clause"
// Use in query
db.Where(clause.Expr{SQL: "age > ?", Vars: []interface{}{18}}).Find(&users)
// Without parentheses
db.Where(clause.Expr{
SQL: "age > ? AND status = ?",
Vars: []interface{}{18, "active"},
WithoutParentheses: true,
}).Find(&users)
// In SELECT
db.Select(clause.Expr{SQL: "COALESCE(name, ?)", Vars: []interface{}{"unknown"}}).Find(&users)Named parameter expression.
type NamedExpr struct {
SQL string
Vars []interface{}
}Usage:
db.Where(clause.NamedExpr{
SQL: "name = @name AND age > @age",
Vars: []interface{}{sql.Named("name", "Alice"), sql.Named("age", 18)},
}).Find(&users)type IN struct {
Column interface{}
Values []interface{}
}Usage:
db.Where(clause.IN{
Column: clause.Column{Name: "id"},
Values: []interface{}{1, 2, 3},
}).Find(&users)Equality condition.
type Eq struct {
Column interface{}
Value interface{}
}Usage:
db.Where(clause.Eq{
Column: clause.Column{Name: "name"},
Value: "Alice",
}).Find(&users)Not equal condition.
type Neq EqUsage:
db.Where(clause.Neq{
Column: clause.Column{Name: "status"},
Value: "deleted",
}).Find(&users)Greater than condition.
type Gt EqUsage:
db.Where(clause.Gt{
Column: clause.Column{Name: "age"},
Value: 18,
}).Find(&users)Greater than or equal to condition.
type Gte EqUsage:
db.Where(clause.Gte{
Column: clause.Column{Name: "age"},
Value: 18,
}).Find(&users)Less than condition.
type Lt EqUsage:
db.Where(clause.Lt{
Column: clause.Column{Name: "price"},
Value: 100,
}).Find(&users)Less than or equal to condition.
type Lte EqUsage:
db.Where(clause.Lte{
Column: clause.Column{Name: "price"},
Value: 100,
}).Find(&users)LIKE pattern matching condition.
type Like EqUsage:
db.Where(clause.Like{
Column: clause.Column{Name: "email"},
Value: "%@example.com",
}).Find(&users)Comma-separated expressions.
type CommaExpression struct {
Exprs []Expression
}type Select struct {
Distinct bool
Columns []Column
Expression Expression
}Usage:
// Add SELECT clause
db.Clauses(clause.Select{
Columns: []clause.Column{{Name: "name"}, {Name: "age"}},
}).Find(&users)
// SELECT DISTINCT
db.Clauses(clause.Select{
Distinct: true,
Columns: []clause.Column{{Name: "name"}},
}).Find(&users)
// SELECT with expression
db.Clauses(clause.Select{
Expression: clause.Expr{SQL: "COUNT(*)"},
}).Find(&result)type Where struct {
Exprs []Expression
}
type AndConditions struct {
Exprs []Expression
}
type OrConditions struct {
Exprs []Expression
}
type NotConditions struct {
Exprs []Expression
}Usage:
// WHERE clause
db.Clauses(clause.Where{
Exprs: []clause.Expression{
clause.Eq{Column: "name", Value: "Alice"},
clause.Eq{Column: "age", Value: 25},
},
}).Find(&users)
// OR conditions
db.Clauses(clause.Where{
Exprs: []clause.Expression{
clause.OrConditions{
Exprs: []clause.Expression{
clause.Eq{Column: "name", Value: "Alice"},
clause.Eq{Column: "name", Value: "Bob"},
},
},
},
}).Find(&users)
// NOT conditions
db.Clauses(clause.Where{
Exprs: []clause.Expression{
clause.NotConditions{
Exprs: []clause.Expression{
clause.Eq{Column: "status", Value: "deleted"},
},
},
},
}).Find(&users)type From struct {
Tables []Table
Joins []Join
}type JoinType string
const (
CrossJoin JoinType = "CROSS"
InnerJoin JoinType = "INNER"
LeftJoin JoinType = "LEFT"
RightJoin JoinType = "RIGHT"
)
type Join struct {
Type JoinType
Table Table
ON Where
Using []string
Expression Expression
}
type JoinTarget struct {
Type JoinType
Association string
Subquery Expression
Table string
}Usage:
// INNER JOIN
db.Clauses(clause.Join{
Type: clause.InnerJoin,
Table: clause.Table{Name: "orders"},
ON: clause.Where{
Exprs: []clause.Expression{
clause.Eq{
Column: clause.Column{Table: "orders", Name: "user_id"},
Value: clause.Column{Table: "users", Name: "id"},
},
},
},
}).Find(&users)
// LEFT JOIN with expression
db.Clauses(clause.Join{
Type: clause.LeftJoin,
Table: clause.Table{Name: "profiles"},
Expression: clause.Expr{
SQL: "profiles.user_id = users.id AND profiles.active = ?",
Vars: []interface{}{true},
},
}).Find(&users)type OrderByColumn struct {
Column Column
Desc bool
Reorder bool
}
type OrderBy struct {
Columns []OrderByColumn
Expression Expression
}Usage:
// ORDER BY
db.Clauses(clause.OrderBy{
Columns: []clause.OrderByColumn{
{Column: clause.Column{Name: "age"}, Desc: true},
{Column: clause.Column{Name: "name"}},
},
}).Find(&users)type Limit struct {
Limit *int
Offset int
}Usage:
limit := 10
db.Clauses(clause.Limit{
Limit: &limit,
Offset: 20,
}).Find(&users)type GroupBy struct {
Columns []Column
Having []Expression
}Usage:
// GROUP BY with HAVING
db.Clauses(clause.GroupBy{
Columns: []clause.Column{{Name: "age"}},
Having: []clause.Expression{
clause.Expr{SQL: "COUNT(*) > ?", Vars: []interface{}{5}},
},
}).Find(&results)type Insert struct {
Table Table
Modifier string
}type Values struct {
Columns []Column
Values [][]interface{}
}Usage:
// INSERT with VALUES
db.Clauses(
clause.Insert{Table: clause.Table{Name: "users"}},
clause.Values{
Columns: []clause.Column{{Name: "name"}, {Name: "age"}},
Values: [][]interface{}{
{"Alice", 25},
{"Bob", 30},
},
},
).Create(&struct{}{})type Update struct {
Modifier string
Table Table
}type Set []Assignment
type Assignment struct {
Column Column
Value interface{}
}Functions:
func Assignments(values map[string]interface{}) Set
func AssignmentColumns(values []string) SetUsage:
// UPDATE SET
db.Clauses(
clause.Update{Table: clause.Table{Name: "users"}},
clause.Set([]clause.Assignment{
{Column: clause.Column{Name: "name"}, Value: "Alice"},
{Column: clause.Column{Name: "age"}, Value: 26},
}),
clause.Where{
Exprs: []clause.Expression{
clause.Eq{Column: clause.Column{Name: "id"}, Value: 1},
},
},
).Update(&User{})
// From map
assignments := clause.Assignments(map[string]interface{}{
"name": "Alice",
"age": 26,
})
db.Clauses(clause.Set(assignments)).Update(&User{})type Delete struct {
Modifier string
}type OnConflict struct {
Columns []Column
Where Where
TargetWhere Where
OnConstraint string
DoNothing bool
DoUpdates Set
UpdateAll bool
}Usage:
// INSERT ... ON CONFLICT DO NOTHING
db.Clauses(clause.OnConflict{
DoNothing: true,
}).Create(&users)
// INSERT ... ON CONFLICT DO UPDATE
db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns([]string{"name", "age"}),
}).Create(&users)
// ON CONFLICT with WHERE
db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "email"}},
Where: clause.Where{
Exprs: []clause.Expression{
clause.Eq{Column: clause.Column{Name: "deleted_at"}, Value: nil},
},
},
DoUpdates: clause.Assignments(map[string]interface{}{
"name": gorm.Expr("EXCLUDED.name"),
"age": gorm.Expr("EXCLUDED.age"),
}),
}).Create(&users)
// Update all columns on conflict
db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
UpdateAll: true,
}).Create(&users)type Returning struct {
Columns []Column
}Usage:
// INSERT ... RETURNING
var user User
db.Clauses(clause.Returning{
Columns: []clause.Column{{Name: "id"}, {Name: "created_at"}},
}).Create(&user)Row locking for SELECT queries.
type Locking struct {
Strength string
Table string
Options string
}Usage:
// SELECT ... FOR UPDATE
db.Clauses(clause.Locking{
Strength: "UPDATE",
}).Find(&users)
// SELECT ... FOR SHARE
db.Clauses(clause.Locking{
Strength: "SHARE",
}).Find(&users)
// With table
db.Clauses(clause.Locking{
Strength: "UPDATE",
Table: "users",
}).Find(&users)
// With options
db.Clauses(clause.Locking{
Strength: "UPDATE",
Options: "NOWAIT",
}).Find(&users)
db.Clauses(clause.Locking{
Strength: "UPDATE",
Options: "SKIP LOCKED",
}).Find(&users)For managing associations.
type AssociationOpType int
const (
OpUnlink AssociationOpType = iota
OpDelete
OpUpdate
OpCreate
)
type Association struct {
Association string
Type AssociationOpType
Conditions []Expression
Set []Assignment
Values []interface{}
}type Assigner interface {
Assignments() []Assignment
}
type AssociationAssigner interface {
AssociationAssignments() []Association
}import "gorm.io/gorm/clause"
// Add single clause
db.Clauses(clause.Locking{Strength: "UPDATE"}).Find(&users)
// Add multiple clauses
db.Clauses(
clause.Select{Columns: []clause.Column{{Name: "name"}}},
clause.Where{Exprs: []clause.Expression{clause.Eq{Column: "active", Value: true}}},
clause.OrderBy{Columns: []clause.OrderByColumn{{Column: clause.Column{Name: "name"}}}},
).Find(&users)
// Chainable
db.Clauses(clause.Locking{Strength: "UPDATE"}).
Clauses(clause.Returning{Columns: []clause.Column{{Name: "id"}}}).
Create(&user)// Complex query with multiple clauses
type Result struct {
Name string
Total int
}
var results []Result
db.Clauses(
clause.Select{
Columns: []clause.Column{
{Name: "name"},
{Table: "orders", Name: "SUM(amount)", Alias: "total"},
},
},
clause.Join{
Type: clause.LeftJoin,
Table: clause.Table{Name: "orders"},
ON: clause.Where{
Exprs: []clause.Expression{
clause.Eq{
Column: clause.Column{Table: "orders", Name: "user_id"},
Value: clause.Column{Table: "users", Name: "id"},
},
},
},
},
clause.GroupBy{
Columns: []clause.Column{{Table: "users", Name: "name"}},
Having: []clause.Expression{
clause.Expr{SQL: "SUM(amount) > ?", Vars: []interface{}{100}},
},
},
clause.OrderBy{
Columns: []clause.OrderByColumn{
{Column: clause.Column{Name: "total"}, Desc: true},
},
},
clause.Limit{Limit: &[]int{10}[0]},
).Table("users").Scan(&results)You can implement custom clause builders for advanced use cases.
// Define custom clause builder
type CustomClauseBuilder struct{}
func (CustomClauseBuilder) Build(clause clause.Clause, builder clause.Builder) {
// Custom SQL generation logic
builder.WriteString("CUSTOM SQL")
}
// Register in config
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
ClauseBuilders: map[string]clause.ClauseBuilder{
"CUSTOM": CustomClauseBuilder{},
},
})// Simple column
col := clause.Column{Name: "name"}
// Column with table
col := clause.Column{Table: "users", Name: "name"}
// Column with alias
col := clause.Column{Name: "name", Alias: "user_name"}
// Raw column (won't be quoted)
col := clause.Column{Name: "COUNT(*)", Raw: true}// Simple table
table := clause.Table{Name: "users"}
// Table with alias
table := clause.Table{Name: "users", Alias: "u"}
// Raw table (won't be quoted)
table := clause.Table{Name: "(SELECT * FROM users)", Raw: true}Use clauses for:
// Type-safe clause building
whereClause := clause.Where{
Exprs: []clause.Expression{
clause.Eq{Column: clause.Column{Name: "active"}, Value: true},
clause.IN{
Column: clause.Column{Name: "role"},
Values: []interface{}{"admin", "moderator"},
},
},
}
db.Clauses(whereClause).Find(&users)// Create reusable clause constructors
func ActiveUsersClause() clause.Expression {
return clause.Where{
Exprs: []clause.Expression{
clause.Eq{Column: clause.Column{Name: "active"}, Value: true},
},
}
}
func PaginationClause(page, size int) []clause.Expression {
limit := size
return []clause.Expression{
clause.Limit{Limit: &limit, Offset: (page - 1) * size},
}
}
// Use them
db.Clauses(ActiveUsersClause()).
Clauses(PaginationClause(2, 20)...).
Find(&users)