or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

associations.mdclause.mddatabase-operations.mdhooks.mdindex.mdlogger.mdmigrations.mdquery-building.mdschema.mdtransactions.md
tile.json

clause.mddocs/

Clause Builder

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.

Core Clause Interfaces

import "gorm.io/gorm/clause"

Constants

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}

Expression Interface

Base interface for all SQL expressions.

type Expression interface {
    Build(builder Builder)
}

Builder Interface

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
}

Clause Interface

Interface for SQL clauses.

type Interface interface {
    Name() string
    Build(Builder)
    MergeClause(*Clause)
}

NegationExpressionBuilder

Interface for expressions that can be negated.

type NegationExpressionBuilder interface {
    NegationBuild(builder Builder)
}

Core Types

Clause

type Clause struct {
    Name                string
    BeforeExpression    Expression
    AfterNameExpression Expression
    AfterExpression     Expression
    Expression          Expression
    Builder             ClauseBuilder
}

Column

type Column struct {
    Table string
    Name  string
    Alias string
    Raw   bool
}

Table

type Table struct {
    Name  string
    Alias string
    Raw   bool
}

Expression Types

Expr

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)

NamedExpr

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)

IN Expression

type IN struct {
    Column interface{}
    Values []interface{}
}

Usage:

db.Where(clause.IN{
    Column: clause.Column{Name: "id"},
    Values: []interface{}{1, 2, 3},
}).Find(&users)

Eq Expression

Equality condition.

type Eq struct {
    Column interface{}
    Value  interface{}
}

Usage:

db.Where(clause.Eq{
    Column: clause.Column{Name: "name"},
    Value:  "Alice",
}).Find(&users)

Neq Expression

Not equal condition.

type Neq Eq

Usage:

db.Where(clause.Neq{
    Column: clause.Column{Name: "status"},
    Value:  "deleted",
}).Find(&users)

Gt Expression

Greater than condition.

type Gt Eq

Usage:

db.Where(clause.Gt{
    Column: clause.Column{Name: "age"},
    Value:  18,
}).Find(&users)

Gte Expression

Greater than or equal to condition.

type Gte Eq

Usage:

db.Where(clause.Gte{
    Column: clause.Column{Name: "age"},
    Value:  18,
}).Find(&users)

Lt Expression

Less than condition.

type Lt Eq

Usage:

db.Where(clause.Lt{
    Column: clause.Column{Name: "price"},
    Value:  100,
}).Find(&users)

Lte Expression

Less than or equal to condition.

type Lte Eq

Usage:

db.Where(clause.Lte{
    Column: clause.Column{Name: "price"},
    Value:  100,
}).Find(&users)

Like Expression

LIKE pattern matching condition.

type Like Eq

Usage:

db.Where(clause.Like{
    Column: clause.Column{Name: "email"},
    Value:  "%@example.com",
}).Find(&users)

CommaExpression

Comma-separated expressions.

type CommaExpression struct {
    Exprs []Expression
}

SELECT Clause

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)

WHERE Clause

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)

FROM Clause

type From struct {
    Tables []Table
    Joins  []Join
}

JOIN Clause

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)

ORDER BY Clause

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)

LIMIT Clause

type Limit struct {
    Limit  *int
    Offset int
}

Usage:

limit := 10
db.Clauses(clause.Limit{
    Limit:  &limit,
    Offset: 20,
}).Find(&users)

GROUP BY Clause

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)

INSERT Clause

type Insert struct {
    Table    Table
    Modifier string
}

VALUES Clause

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{}{})

UPDATE Clause

type Update struct {
    Modifier string
    Table    Table
}

SET Clause

type Set []Assignment

type Assignment struct {
    Column Column
    Value  interface{}
}

Functions:

func Assignments(values map[string]interface{}) Set
func AssignmentColumns(values []string) Set

Usage:

// 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{})

DELETE Clause

type Delete struct {
    Modifier string
}

ON CONFLICT Clause (Upsert)

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)

RETURNING Clause

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)

LOCKING Clause

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)

Association Clause

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{}
}

Assigner Interfaces

type Assigner interface {
    Assignments() []Assignment
}

type AssociationAssigner interface {
    AssociationAssignments() []Association
}

Using Clauses

Adding Clauses to Queries

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 Example

// 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)

Custom Clause Builders

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{},
    },
})

Utilities

Creating Columns

// 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}

Creating Tables

// 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}

Best Practices

When to Use Clauses

Use clauses for:

  • Complex queries that are hard to express with chainable methods
  • Database-specific features
  • Building dynamic queries programmatically
  • Implementing custom dialects or plugins

Type Safety

// 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)

Reusable Clause Functions

// 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)