go-migrationGo Migration
Schema Builder
Documentation

Utility Operations

Drop, rename, and inspect tables using Schema Builder utility methods.

Utility Operations

Beyond creating and altering tables, the Schema Builder provides utility methods for dropping, renaming, and inspecting tables and columns.

Drop Table

Remove a table from the database:

go
s.Drop("posts")

This will fail with an error if the table doesn't exist.

Drop If Exists

Safely remove a table only if it exists:

go
s.DropIfExists("posts")

This is the recommended approach in Down methods since it won't error if the table has already been removed:

go
func (m *CreatePostsTable) Down(s *schema.Builder) error {
    return s.DropIfExists("posts")
}

Rename Table

Rename an existing table:

go
s.Rename("posts", "articles")

The first argument is the current table name, and the second is the new name.

migrations/20240601_rename_posts_to_articles.go
package migrations

import (
    "github.com/gopackx/go-migration/pkg/schema"
)

type RenamePostsToArticles struct{}

func (m *RenamePostsToArticles) Up(s *schema.Builder) error {
    return s.Rename("posts", "articles")
}

func (m *RenamePostsToArticles) Down(s *schema.Builder) error {
    return s.Rename("articles", "posts")
}

Has Table

Check if a table exists in the database. It returns (bool, error):

go
exists, err := s.HasTable("users")
if err != nil {
    return err
}
if exists {
    // Table exists
}

This is useful for conditional logic in migrations:

go
func (m *ConditionalMigration) Up(s *schema.Builder) error {
    exists, err := s.HasTable("settings")
    if err != nil {
        return err
    }
    if !exists {
        return s.Create("settings", func(bp *schema.Blueprint) {
            bp.ID()
            bp.String("key", 255).Unique()
            bp.Text("value").Nullable()
        })
    }
    return nil
}

Has Column

Check if a column exists in a table. Like HasTable, it returns (bool, error):

go
exists, err := s.HasColumn("users", "phone")
if err != nil {
    return err
}
if exists {
    // Column exists
}

Useful for safely adding columns that might already exist:

go
func (m *AddPhoneToUsers) Up(s *schema.Builder) error {
    exists, err := s.HasColumn("users", "phone")
    if err != nil {
        return err
    }
    if !exists {
        return s.Alter("users", func(bp *schema.Blueprint) {
            bp.String("phone", 20).Nullable()
        })
    }
    return nil
}

HasTable and HasColumn query the database's information schema at runtime. They're useful for writing defensive migrations that can run safely even if the schema is in an unexpected state.

Raw SQL Execution (v1.0.0)

Execute arbitrary SQL statements directly, bypassing the fluent API. This is useful for materialized views, custom DDL, database-specific features, or anything the Schema Builder doesn't cover.

go
func (s *Builder) RawExec(statements ...string) error
ParameterTypeDescription
statements...stringOne or more raw SQL statements to execute

Each statement is executed sequentially, and RawExec returns an error if any statement fails. Pass multiple statements as separate arguments:

go
func (m *CreateReportViews) Up(s *schema.Builder) error {
    return s.RawExec(
        `CREATE MATERIALIZED VIEW monthly_sales AS
         SELECT date_trunc('month', created_at) AS month, SUM(amount) AS total
         FROM orders GROUP BY month`,
        `CREATE UNIQUE INDEX idx_monthly_sales_month ON monthly_sales (month)`,
    )
}

func (m *CreateReportViews) Down(s *schema.Builder) error {
    return s.RawExec(
        "DROP MATERIALIZED VIEW IF EXISTS monthly_sales",
    )
}

Other common use cases:

go
// Enable a PostgreSQL extension
s.RawExec("CREATE EXTENSION IF NOT EXISTS pgcrypto")

// Create a partial index
s.RawExec("CREATE INDEX idx_active_users ON users (email) WHERE active = true")

// Set up a trigger
s.RawExec(
    `CREATE OR REPLACE FUNCTION update_timestamp()
     RETURNS TRIGGER AS $$
     BEGIN NEW.updated_at = NOW(); RETURN NEW; END;
     $$ LANGUAGE plpgsql`,
    `CREATE TRIGGER set_updated_at BEFORE UPDATE ON users
     FOR EACH ROW EXECUTE FUNCTION update_timestamp()`,
)

RawExec sends SQL directly to the database without grammar validation. Make sure the SQL you write is compatible with the database driver you are using. For operations supported by the fluent API (create table, alter, drop, etc.), prefer the available Builder methods.

Quick Reference

MethodDescription
s.Drop(table)Drop a table (errors if not found); returns error
s.DropIfExists(table)Drop a table if it exists; returns error
s.Rename(from, to)Rename a table; returns error
s.HasTable(table)Check if a table exists (returns (bool, error))
s.HasColumn(table, column)Check if a column exists (returns (bool, error))
s.RawExec(statements...)Execute raw SQL statements directly; returns error

What's Next?