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

schema.mddocs/

Schema Metadata and Utilities

GORM provides comprehensive schema parsing and metadata capabilities through the gorm.io/gorm/schema package. This allows runtime inspection of model structures, field types, relationships, and naming conventions.

Schema Package

import "gorm.io/gorm/schema"

Parsing Schemas

Parse Function

Parse a model into a schema structure.

func Parse(dest interface{}, cacheStore *sync.Map, namer Namer) (*Schema, error)

func ParseWithSpecialTableName(dest interface{}, cacheStore *sync.Map, namer Namer, specialTableName string) (*Schema, error)

Usage:

import (
    "sync"
    "gorm.io/gorm/schema"
)

type User struct {
    ID        uint
    Name      string
    Email     string
    CompanyID uint
    Company   Company
}

// Parse schema
s, err := schema.Parse(&User{}, &sync.Map{}, schema.NamingStrategy{})
if err != nil {
    // Handle error
}

fmt.Println("Table name:", s.Table)
fmt.Println("Primary fields:", len(s.PrimaryFields))

// Access through DB
stmt := &gorm.Statement{DB: db}
if err := stmt.Parse(&User{}); err != nil {
    // Handle error
}
schema := stmt.Schema

Schema Type

The Schema struct contains all metadata about a model.

type Schema struct {
    Name                      string
    ModelType                 reflect.Type
    Table                     string
    PrioritizedPrimaryField   *Field
    DBNames                   []string
    PrimaryFields             []*Field
    PrimaryFieldDBNames       []string
    Fields                    []*Field
    FieldsByName              map[string]*Field
    FieldsByBindName          map[string]*Field
    FieldsByDBName            map[string]*Field
    FieldsWithDefaultDBValue  []*Field
    Relationships             Relationships
    CreateClauses             []clause.Interface
    QueryClauses              []clause.Interface
    UpdateClauses             []clause.Interface
    DeleteClauses             []clause.Interface
    BeforeCreate              bool
    AfterCreate               bool
    BeforeUpdate              bool
    AfterUpdate               bool
    BeforeDelete              bool
    AfterDelete               bool
    BeforeSave                bool
    AfterSave                 bool
    AfterFind                 bool
}

Schema Methods

// Find field by name (searches by name, db name, and bind name)
func (schema *Schema) LookUpField(name string) *Field

// Find field by bind names
func (schema *Schema) LookUpFieldByBindName(bindNames []string, name string) *Field

Usage:

// Look up field
field := schema.LookUpField("Name")
if field != nil {
    fmt.Println("Field DB name:", field.DBName)
    fmt.Println("Field type:", field.DataType)
}

// Look up by database name
field = schema.LookUpField("user_name")

Field Type

The Field struct contains metadata about a model field.

type Field struct {
    Name                   string
    DBName                 string
    BindNames              []string
    EmbeddedBindNames      []string
    DataType               DataType
    GORMDataType           DataType
    PrimaryKey             bool
    AutoIncrement          bool
    AutoIncrementIncrement int64
    Creatable              bool
    Updatable              bool
    Readable               bool
    AutoCreateTime         TimeType
    AutoUpdateTime         TimeType
    HasDefaultValue        bool
    DefaultValue           string
    DefaultValueInterface  interface{}
    NotNull                bool
    Unique                 bool
    Comment                string
    Size                   int
    Precision              int
    Scale                  int
    IgnoreMigration        bool
    FieldType              reflect.Type
    IndirectFieldType      reflect.Type
    StructField            reflect.StructField
    Tag                    reflect.StructTag
    TagSettings            map[string]string
    Schema                 *Schema
    EmbeddedSchema         *Schema
    OwnerSchema            *Schema
    ReflectValueOf         func(context.Context, reflect.Value) reflect.Value
    ValueOf                func(context.Context, reflect.Value) (interface{}, error)
    Set                    func(context.Context, reflect.Value, interface{}) error
    Serializer             SerializerInterface
    NewValuePool           FieldNewValuePool
    UniqueIndex            string
}

Data Types

type DataType string

const (
    Bool   DataType = "bool"
    Int    DataType = "int"
    Uint   DataType = "uint"
    Float  DataType = "float"
    String DataType = "string"
    Time   DataType = "time"
    Bytes  DataType = "bytes"
)

Time Types

type TimeType int64

const (
    UnixTime        TimeType = 1  // Unix timestamp
    UnixSecond      TimeType = 2  // Unix seconds
    UnixMillisecond TimeType = 3  // Unix milliseconds
    UnixNanosecond  TimeType = 4  // Unix nanoseconds
)

Relationships

Relationships Type

type Relationships struct {
    HasOne              []*Relationship
    BelongsTo           []*Relationship
    HasMany             []*Relationship
    Many2Many           []*Relationship
    Relations           map[string]*Relationship
    EmbeddedRelations   map[string]*Relationships
}

Relationship Type

type RelationshipType string

const (
    HasOne    RelationshipType = "has_one"
    HasMany   RelationshipType = "has_many"
    BelongsTo RelationshipType = "belongs_to"
    Many2Many RelationshipType = "many_to_many"
)

type Relationship struct {
    Name         string
    Type         RelationshipType
    Field        *Field
    Polymorphic  *Polymorphic
    References   []*Reference
    Schema       *Schema
    FieldSchema  *Schema
    JoinTable    *Schema
}

Polymorphic Relationships

type Polymorphic struct {
    PolymorphicID   *Field
    PolymorphicType *Field
    Value           string
}

Foreign Key References

type Reference struct {
    PrimaryKey    *Field
    PrimaryValue  string
    ForeignKey    *Field
    OwnPrimaryKey bool
}

Naming Strategies

Namer Interface

type Namer interface {
    TableName(table string) string
    SchemaName(table string) string
    ColumnName(table, column string) string
    JoinTableName(joinTable string) string
    RelationshipFKName(Relationship) string
    CheckerName(table, column string) string
    IndexName(table, column string) string
    UniqueName(table, column string) string
}

NamingStrategy

The default naming strategy implementation.

type NamingStrategy struct {
    TablePrefix          string
    SingularTable        bool
    NameReplacer         Replacer
    NoLowerCase          bool
    IdentifierMaxLength  int
}

Usage:

import "gorm.io/gorm/schema"

// Custom naming strategy
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
    NamingStrategy: schema.NamingStrategy{
        TablePrefix:   "t_",           // Table prefix
        SingularTable: true,            // Use singular table names
        NoLowerCase:   true,            // Don't lowercase names
    },
})

// Usage examples:
// User struct -> t_User table (with config above)
// User struct -> t_users table (default)

Replacer Interface

type Replacer interface {
    Replace(name string) string
}

Table Name Interface

Tabler Interface

Implement this interface to customize table name.

type Tabler interface {
    TableName() string
}

Usage:

type User struct {
    ID   uint
    Name string
}

func (User) TableName() string {
    return "custom_users"
}

// User model will use "custom_users" table

TablerWithNamer Interface

type TablerWithNamer interface {
    TableName(Namer) string
}

Usage:

type User struct {
    ID   uint
    Name string
}

func (User) TableName(namer schema.Namer) string {
    // Use namer to transform name
    return namer.TableName("users")
}

Indexes

Index Type

type Index struct {
    Name      string
    Class     string
    Type      string
    Where     string
    Comment   string
    Option    string
    Fields    []IndexOption
    Composite bool
}

IndexOption Type

type IndexOption struct {
    DBName     string
    Expression string
    Sort       string
    Collate    string
    Length     int
}

Usage:

type User struct {
    ID   uint
    Name string `gorm:"index:idx_name,unique,sort:desc,length:100"`
    Age  int    `gorm:"index:idx_age_active,composite:age_active"`
    Active bool `gorm:"index:idx_age_active,composite:age_active"`
}

Constraints

Constraint Type

type Constraint struct {
    Name            string
    Field           *Field
    Schema          *Schema
    ForeignKeys     []*Field
    ReferenceSchema *Schema
    References      []*Field
    OnDelete        string
    OnUpdate        string
}

Check Constraint

type CheckConstraint struct {
    Name       string
    Constraint string
    Field      *Field
}

Unique Constraint

type UniqueConstraint struct {
    Name  string
    Field *Field
}

ConstraintInterface

type ConstraintInterface interface {
    GetName() string
    Build() (sql string, vars []interface{})
}

Serializers

Serializers control how field values are stored in and retrieved from the database.

SerializerInterface

type SerializerInterface interface {
    SerializerValuerInterface
    Scan(ctx context.Context, field *Field, dst reflect.Value, dbValue interface{}) error
}

type SerializerValuerInterface interface {
    Value(ctx context.Context, field *Field, dst reflect.Value, fieldValue interface{}) (interface{}, error)
}

Built-in Serializers

GORM provides several built-in serializers:

  • JSONSerializer - Serializes data as JSON
  • UnixSecondSerializer - Stores time as Unix timestamp
  • GobSerializer - Uses Go's gob encoding

Registering Custom Serializers

func RegisterSerializer(name string, serializer SerializerInterface)
func GetSerializer(name string) (SerializerInterface, bool)

Usage:

import "gorm.io/gorm/schema"

// Register custom serializer
schema.RegisterSerializer("custom", &CustomSerializer{})

// Use in model
type User struct {
    ID       uint
    Metadata map[string]interface{} `gorm:"serializer:json"`
    Config   Settings                `gorm:"serializer:custom"`
}

// Implement custom serializer
type CustomSerializer struct{}

func (CustomSerializer) Scan(ctx context.Context, field *schema.Field, dst reflect.Value, dbValue interface{}) error {
    // Deserialize from database value
    return nil
}

func (CustomSerializer) Value(ctx context.Context, field *schema.Field, dst reflect.Value, fieldValue interface{}) (interface{}, error) {
    // Serialize to database value
    return nil, nil
}

Custom Data Types

GormDataTypeInterface

Implement this interface to provide custom database data type.

type GormDataTypeInterface interface {
    GormDataType() string
}

Usage:

import (
    "database/sql/driver"
    "encoding/json"
)

type JSON json.RawMessage

func (JSON) GormDataType() string {
    return "json"
}

func (j JSON) Value() (driver.Value, error) {
    if len(j) == 0 {
        return nil, nil
    }
    return string(j), nil
}

func (j *JSON) Scan(value interface{}) error {
    if value == nil {
        *j = JSON("null")
        return nil
    }
    s, ok := value.([]byte)
    if !ok {
        return errors.New("invalid Scan source")
    }
    *j = JSON(s)
    return nil
}

// Use in model
type User struct {
    ID       uint
    Metadata JSON
}

Clause Interfaces

Clause Interfaces for Models

Models can implement these interfaces to provide custom clauses.

type CreateClausesInterface interface {
    CreateClauses(*Field) []clause.Interface
}

type QueryClausesInterface interface {
    QueryClauses(*Field) []clause.Interface
}

type UpdateClausesInterface interface {
    UpdateClauses(*Field) []clause.Interface
}

type DeleteClausesInterface interface {
    DeleteClauses(*Field) []clause.Interface
}

Field Value Pool

FieldNewValuePool Interface

type FieldNewValuePool interface {
    Get() interface{}
    Put(interface{})
}

This interface allows implementing custom value pooling for fields to reduce allocations.

Utility Functions

Default Values

const DefaultAutoIncrementIncrement int64 = 1

Working with Schema at Runtime

Inspecting Model Structure

import "gorm.io/gorm/schema"

type User struct {
    ID        uint   `gorm:"primaryKey"`
    Name      string `gorm:"size:256;not null"`
    Email     string `gorm:"uniqueIndex"`
    Age       int
    CompanyID uint
    Company   Company
}

// Parse schema
s, _ := schema.Parse(&User{}, &sync.Map{}, schema.NamingStrategy{})

// Inspect fields
for _, field := range s.Fields {
    fmt.Printf("Field: %s, DB: %s, Type: %s\n",
        field.Name, field.DBName, field.DataType)

    if field.PrimaryKey {
        fmt.Println("  - Primary Key")
    }
    if field.Unique {
        fmt.Println("  - Unique")
    }
    if field.NotNull {
        fmt.Println("  - Not Null")
    }
    if field.Size > 0 {
        fmt.Printf("  - Size: %d\n", field.Size)
    }
}

// Inspect relationships
for name, rel := range s.Relationships.Relations {
    fmt.Printf("Relationship: %s, Type: %s\n", name, rel.Type)
}

Dynamic Table Names

// Get table name at runtime
stmt := &gorm.Statement{DB: db}
stmt.Parse(&User{})
tableName := stmt.Schema.Table

// With custom naming strategy
namer := schema.NamingStrategy{TablePrefix: "app_"}
customTable := namer.TableName("users")  // "app_users"

Field Tag Settings

// Access field tags
field := schema.LookUpField("Email")
if field != nil {
    // TagSettings is a parsed map of tag settings
    if size, ok := field.TagSettings["SIZE"]; ok {
        fmt.Println("Field size:", size)
    }

    // Check for specific tags
    if _, ok := field.TagSettings["UNIQUE"]; ok {
        fmt.Println("Field is unique")
    }
}

Advanced Schema Usage

Caching Schemas

// Use a cache for parsed schemas
cache := &sync.Map{}

// First parse - parses and caches
s1, _ := schema.Parse(&User{}, cache, schema.NamingStrategy{})

// Second parse - retrieves from cache
s2, _ := schema.Parse(&User{}, cache, schema.NamingStrategy{})

// s1 and s2 point to the same schema object

Custom Field Value Access

// Fields provide value access functions
field := schema.LookUpField("Name")

// Get field value from model
userValue := reflect.ValueOf(&user)
fieldValue := field.ReflectValueOf(context.Background(), userValue)

// Get field value as interface
value, err := field.ValueOf(context.Background(), userValue)

// Set field value
err = field.Set(context.Background(), userValue, "Alice")

Field Serializers

GORM provides built-in serializers for custom field serialization. Serializers allow you to store complex data types in database columns.

Serializer Interfaces

// SerializerInterface defines field serializer
type SerializerInterface interface {
    Scan(ctx context.Context, field *Field, dst reflect.Value, dbValue interface{}) error
    SerializerValuerInterface
}

// SerializerValuerInterface defines value serialization
type SerializerValuerInterface interface {
    Value(ctx context.Context, field *Field, dst reflect.Value, fieldValue interface{}) (interface{}, error)
}

Built-in Serializers

GORM includes three built-in serializers:

// JSONSerializer serializes values as JSON
type JSONSerializer struct{}

// UnixSecondSerializer serializes time.Time as Unix seconds
type UnixSecondSerializer struct{}

// GobSerializer serializes values using Go's gob encoding
type GobSerializer struct{}

Serializer Registration

// Register a custom serializer
func RegisterSerializer(name string, serializer SerializerInterface)

// Get a registered serializer
func GetSerializer(name string) (serializer SerializerInterface, ok bool)

Usage:

import "gorm.io/gorm/schema"

// Use JSON serializer on a field
type User struct {
    ID       uint
    Name     string
    Metadata map[string]interface{} `gorm:"serializer:json"`
}

// Use UnixTime serializer
type Event struct {
    ID        uint
    Timestamp int64 `gorm:"serializer:unixtime"`
}

// Use Gob serializer
type Config struct {
    ID       uint
    Settings interface{} `gorm:"serializer:gob"`
}

// Register custom serializer
schema.RegisterSerializer("custom", MyCustomSerializer{})

// Use custom serializer
type Record struct {
    ID   uint
    Data string `gorm:"serializer:custom"`
}