lint

package
v0.10.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 19, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

README

Linter Framework

The lint package provides a framework for static analysis of MySQL schema definitions and DDL statements. It enables validation and best-practice enforcement beyond the runtime checks provided by the check package.

Architecture

All built-in linters are automatically registered and enabled when the lint package is imported. The framework uses a flat package structure:

  • Core framework files: lint.go, linter.go, registry.go, violation.go
  • Linter implementations: lint_*.go (e.g., lint_invisible_index.go)

Quick Start

Using the Linter Framework
import (
    "github.com/block/spirit/pkg/lint"
)

// All built-in linters are automatically registered!
violations, err := lint.RunLinters(tables, stmts, lint.Config{})
if err != nil {
    // Handle configuration errors
}

// Check for errors
if lint.HasErrors(violations) {
    // Handle errors
}

// Filter violations
errors := lint.FilterBySeverity(violations, lint.SeverityError)
warnings := lint.FilterBySeverity(violations, lint.SeverityWarning)
Creating a Custom Linter

Custom linters can be

  1. added directly to the lint package (in new files with the lint_ prefix, for consistency)
  2. added to your own package and registered by blank import that relies on the init() function
  3. added to your own code and registered explicitly using lint.Register()
// lint_my_custom.go
package lint

import (
    "github.com/block/spirit/pkg/statement"
)

// Register your linter in init()
func init() {
    Register(&MyCustomLinter{})
}

// MyCustomLinter checks custom rules
type MyCustomLinter struct{}

func (l *MyCustomLinter) Name() string        { return "my_custom" }
func (l *MyCustomLinter) Category() string    { return "naming" }
func (l *MyCustomLinter) Description() string { return "Checks naming conventions" }
func (l *MyCustomLinter) String() string      { return l.Name() }

func (l *MyCustomLinter) Lint(existingTables []*statement.CreateTable, changes []*statement.AbstractStatement) []Violation {
    var violations []Violation
    
    for _, ct := range existingTables {
        // Check table properties
        if /* condition */ {
            violations = append(violations, Violation{
                Linter:   l,
                Severity: SeverityWarning,
                Message:  "Table name issue",
                Location: &Location{
                    Table: ct.GetTableName(),
                },
            })
        }
    }
    
    return violations
}
Configuring Linters
Enabling/Disabling Linters
// Disable specific linters
violations, err := lint.RunLinters(tables, stmts, lint.Config{
    Enabled: map[string]bool{
        "invisible_index_before_drop": false,
        "primary_key_type":            true,
    },
})
if err != nil {
    // Handle configuration errors
}
Configurable Linters

Some linters support additional configuration options via the Settings field. Linters that implement the ConfigurableLinter interface accept settings as map[string]string:

violations, err := lint.RunLinters(tables, stmts, lint.Config{
    Settings: map[string]map[string]string{
        "invisible_index_before_drop": {
            "raiseError": "true",  // Make violations errors instead of warnings
        },
    },
})
if err != nil {
    // Handle configuration errors (e.g., invalid settings)
}

Each configurable linter defines its own settings keys and values. See the individual linter documentation below for available options.

Core Types

Severity Levels
  • ERROR: Will cause actual problems (data loss, inconsistency, MySQL limitations)
  • WARNING: Best practice violations, potential issues
  • INFO: Suggestions, style preferences
Violation
type Violation struct {
    Linter     Linter              // The linter that produced this violation
    Severity   Severity            // ERROR, WARNING, or INFO
    Message    string              // Human-readable message
    Location   *Location           // Where the violation occurred
    Suggestion *string             // Optional fix suggestion
    Context    map[string]any      // Additional context
}
Location
type Location struct {
    Table      string   // Table name
    Column     *string  // Column name (if applicable)
    Index      *string  // Index name (if applicable)
    Constraint *string  // Constraint name (if applicable)
}

API Functions

Registration
  • Register(l Linter) - Register a linter (call from init())
  • Enable(name string) - Enable a linter by name
  • Disable(name string) - Disable a linter by name
  • List() - Get all registered linter names
  • ListByCategory(category string) - Get linters in a category
  • Get(name string) - Get a linter by name
Execution
  • RunLinters(createTables, alterStatements, config) ([]Violation, error) - Run all enabled linters, returns violations and any configuration errors
  • HasErrors(violations) - Check if any violations are errors
  • HasWarnings(violations) - Check if any violations are warnings
  • FilterBySeverity(violations, severity) - Filter by severity level
  • FilterByLinter(violations, name) - Filter by linter name

Built-in Linters

The lint package includes 11 built-in linters covering schema design, data types, and safety best practices.

allow_charset

Severity: Warning
Configurable: Yes
Checks: CREATE TABLE, ALTER TABLE (ADD/MODIFY/CHANGE COLUMN)

Restricts which character sets are allowed for tables and columns. Helps enforce consistent encoding across your database.

Configuration Options:

  • charsets (string): Comma-separated list of allowed character sets. Default: "utf8mb4".

Examples:

-- ❌ Violation (latin1 not allowed by default)
CREATE TABLE users (
  id INT PRIMARY KEY,
  name VARCHAR(100) CHARACTER SET latin1
) CHARACTER SET latin1;

-- ✅ Correct
CREATE TABLE users (
  id INT PRIMARY KEY,
  name VARCHAR(100) CHARACTER SET utf8mb4
) CHARACTER SET utf8mb4;

-- ❌ Violation in ALTER TABLE
ALTER TABLE users ADD COLUMN legacy VARCHAR(100) CHARACTER SET latin1;

Configuration Example:

violations, err := lint.RunLinters(tables, stmts, lint.Config{
    Settings: map[string]map[string]string{
        "allow_charset": {
            "charsets": "utf8mb4,utf8mb3",  // Allow multiple charsets
        },
    },
})

allow_engine

Severity: Warning
Configurable: Yes
Checks: CREATE TABLE, ALTER TABLE ENGINE

Restricts which storage engines are allowed. Helps ensure consistent engine usage and avoid problematic engines like MyISAM.

Configuration Options:

  • allowed_engines (string): Comma-separated list of allowed engines. Default: "innodb".

Examples:

-- ❌ Violation (MyISAM not allowed by default)
CREATE TABLE users (
  id INT PRIMARY KEY
) ENGINE=MyISAM;

-- ✅ Correct
CREATE TABLE users (
  id INT PRIMARY KEY
) ENGINE=InnoDB;

-- ❌ Violation in ALTER TABLE
ALTER TABLE users ENGINE=MyISAM;

Configuration Example:

violations, err := lint.RunLinters(tables, stmts, lint.Config{
    Settings: map[string]map[string]string{
        "allow_engine": {
            "allowed_engines": "innodb,rocksdb",  // Allow multiple engines
        },
    },
})

auto_inc_capacity

Severity: Warning
Configurable: Yes
Checks: CREATE TABLE

Ensures that AUTO_INCREMENT values are not within a dangerous percentage of the column type's maximum capacity.

Configuration Options:

  • threshold (string): Percentage threshold (1-100). Default: "85".

Examples:

-- ❌ Violation (90% of INT UNSIGNED capacity)
CREATE TABLE users (
  id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT
) AUTO_INCREMENT=4000000000;

-- ✅ Correct (low value)
CREATE TABLE users (
  id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT
) AUTO_INCREMENT=100;

Configuration Example:

violations, err := lint.RunLinters(tables, stmts, lint.Config{
    Settings: map[string]map[string]string{
        "auto_inc_capacity": {
            "threshold": "90",  // Warn at 90% capacity
        },
    },
})

has_fk

Severity: Warning
Configurable: No
Checks: CREATE TABLE, ALTER TABLE (ADD CONSTRAINT)

Detects foreign key constraints, which can cause performance issues and operational complexity in large-scale systems.

Examples:

-- ❌ Violation (foreign key detected)
CREATE TABLE orders (
  id INT PRIMARY KEY,
  user_id INT,
  FOREIGN KEY (user_id) REFERENCES users(id)
);

-- ✅ Correct (no foreign key)
CREATE TABLE orders (
  id INT PRIMARY KEY,
  user_id INT,
  INDEX idx_user_id (user_id)
);

-- ❌ Violation in ALTER TABLE
ALTER TABLE orders ADD CONSTRAINT fk_user 
  FOREIGN KEY (user_id) REFERENCES users(id);

has_float

Severity: Warning
Configurable: No
Checks: CREATE TABLE, ALTER TABLE (ADD/MODIFY/CHANGE COLUMN)

Detects FLOAT and DOUBLE columns, which can have precision issues. Recommends DECIMAL for exact numeric values.

Examples:

-- ❌ Violation (FLOAT has precision issues)
CREATE TABLE products (
  id INT PRIMARY KEY,
  price FLOAT
);

-- ✅ Correct (DECIMAL for exact values)
CREATE TABLE products (
  id INT PRIMARY KEY,
  price DECIMAL(10,2)
);

-- ❌ Violation in ALTER TABLE
ALTER TABLE products ADD COLUMN discount DOUBLE;

invisible_index_before_drop

Severity: Warning (default), Error (configurable)
Configurable: Yes
Checks: ALTER TABLE (DROP INDEX)

Requires indexes to be made invisible before dropping them as a safety measure. This ensures the index isn't needed before permanently removing it.

Configuration Options:

  • raiseError (string): Set to "true" to make violations errors instead of warnings. Default: "false".

Examples:

-- ❌ Violation
ALTER TABLE users DROP INDEX idx_email;

-- ✅ Correct
ALTER TABLE users ALTER INDEX idx_email INVISIBLE;
-- Wait and monitor performance
ALTER TABLE users DROP INDEX idx_email;

Configuration Example:

violations, err := lint.RunLinters(tables, stmts, lint.Config{
    Settings: map[string]map[string]string{
        "invisible_index_before_drop": {
            "raiseError": "true",  // Violations will be errors
        },
    },
})

multiple_alter_table

Severity: Warning
Configurable: No
Checks: ALTER TABLE

Detects multiple ALTER TABLE statements on the same table that could be combined into one for better performance and fewer table rebuilds.

Examples:

-- ❌ Violation (multiple ALTERs on same table)
ALTER TABLE users ADD COLUMN age INT;
ALTER TABLE users ADD INDEX idx_age (age);

-- ✅ Better (combined into one)
ALTER TABLE users 
  ADD COLUMN age INT,
  ADD INDEX idx_age (age);

name_case

Severity: Warning
Configurable: No
Checks: CREATE TABLE, ALTER TABLE (RENAME)

Ensures that table names are all lowercase to avoid case-sensitivity issues across different operating systems.

Examples:

-- ❌ Violation (mixed case)
CREATE TABLE UserAccounts (
  id INT PRIMARY KEY
);

-- ✅ Correct (lowercase)
CREATE TABLE user_accounts (
  id INT PRIMARY KEY
);

-- ❌ Violation in ALTER TABLE
ALTER TABLE users RENAME TO UserAccounts;

primary_key

Severity: Warning (missing/invalid types), Warning (signed integers)
Configurable: Yes
Checks: CREATE TABLE

Ensures primary keys are defined and use appropriate data types (BIGINT UNSIGNED, BINARY, or VARBINARY by default).

Configuration Options:

  • allowedTypes (string): Comma-separated list of allowed types. Default: "BIGINT,BINARY,VARBINARY".
  • Supported types: BINARY, VARBINARY, BIGINT, CHAR, VARCHAR, BIT, DECIMAL, ENUM, SET, TINYINT, SMALLINT, MEDIUMINT, INT, TIME, TIMESTAMP, YEAR, DATE, DATETIME

Examples:

-- ❌ Error (no primary key)
CREATE TABLE users (
  id INT,
  name VARCHAR(100)
);

-- ❌ Error (INT not allowed by default)
CREATE TABLE users (
  id INT PRIMARY KEY
);

-- ⚠️ Warning (signed BIGINT, should be UNSIGNED)
CREATE TABLE users (
  id BIGINT PRIMARY KEY
);

-- ✅ Correct
CREATE TABLE users (
  id BIGINT UNSIGNED PRIMARY KEY
);

Configuration Example:

violations, err := lint.RunLinters(tables, stmts, lint.Config{
    Settings: map[string]map[string]string{
        "primary_key": {
            "allowedTypes": "BIGINT,INT,VARCHAR",  // Allow additional types
        },
    },
})

reserved_words

Severity: Warning
Configurable: No
Checks: CREATE TABLE, ALTER TABLE (ADD/MODIFY/CHANGE COLUMN, RENAME)

Checks for usage of MySQL reserved words in table and column names. Using reserved words as identifiers can cause syntax errors and requires backtick quoting. The linter uses a static list of 259 reserved words from MySQL 9.5.0.

Examples:

-- ❌ Violation (SELECT is reserved)
CREATE TABLE `select` (
  id INT PRIMARY KEY
);

-- ❌ Violation (ORDER is reserved)
CREATE TABLE `order` (
  id INT PRIMARY KEY,
  `where` VARCHAR(100)  -- WHERE is also reserved
);

-- ✅ Correct (non-reserved words)
CREATE TABLE orders (
  id INT PRIMARY KEY,
  location VARCHAR(100)
);

-- ❌ Violation in ALTER TABLE
ALTER TABLE users ADD COLUMN `select` VARCHAR(100);
ALTER TABLE users RENAME TO `table`;

Note: While MySQL allows reserved words as identifiers when quoted with backticks, it's better practice to avoid them entirely to prevent confusion and potential issues. The reserved words list is sourced directly from MySQL 9.5.0 and includes all keywords that cannot be used as unquoted identifiers.


unsafe

Severity: Warning
Configurable: Yes
Checks: ALTER TABLE, DROP TABLE, TRUNCATE TABLE, DROP DATABASE

Detects unsafe operations that can cause data loss or service disruption, such as DROP COLUMN, DROP TABLE, TRUNCATE, and DROP DATABASE.

Configuration Options:

  • allowUnsafe (string): Set to "true" to disable this linter. Default: "false".

Examples:

-- ❌ Violation (drops data)
ALTER TABLE users DROP COLUMN email;

-- ❌ Violation (drops table)
DROP TABLE users;

-- ❌ Violation (truncates data)
TRUNCATE TABLE users;

-- ✅ Safe operations
ALTER TABLE users ADD COLUMN email VARCHAR(255);
ALTER TABLE users ADD INDEX idx_email (email);

Configuration Example:

violations, err := lint.RunLinters(tables, stmts, lint.Config{
    Settings: map[string]map[string]string{
        "unsafe": {
            "allowUnsafe": "true",  // Disable unsafe checks (not recommended)
        },
    },
})

zero_date

Severity: Warning
Configurable: No
Checks: CREATE TABLE, ALTER TABLE (ADD/MODIFY/CHANGE COLUMN)

Checks for columns with zero-date default values (0000-00-00 or 0000-00-00 00:00:00) and NOT NULL date columns without defaults, which can cause issues with strict SQL modes.

Examples:

-- ❌ Violation (zero-date default)
CREATE TABLE users (
  id INT PRIMARY KEY,
  created_at DATETIME DEFAULT '0000-00-00 00:00:00'
);

-- ❌ Violation (NOT NULL without default)
CREATE TABLE users (
  id INT PRIMARY KEY,
  created_at DATETIME NOT NULL
);

-- ✅ Correct
CREATE TABLE users (
  id INT PRIMARY KEY,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- ❌ Violation in ALTER TABLE
ALTER TABLE users ADD COLUMN legacy_date DATETIME DEFAULT '0000-00-00 00:00:00';

Linter Summary Table

Linter Configurable CREATE TABLE ALTER TABLE Severity
allow_charset Warning
allow_engine Warning
auto_inc_capacity Warning
has_fk Warning
has_float Warning
invisible_index_before_drop Warning
multiple_alter_table Warning
name_case Warning
primary_key Warning
reserved_words Warning
unsafe Warning
zero_date Warning

Example Linters

The example package provides demonstration linters for learning purposes:

TableNameLengthLinter

Checks that table names don't exceed MySQL's 64 character limit.

DuplicateColumnLinter

Detects duplicate column definitions in CREATE TABLE statements.

See pkg/lint/example/ for reference implementations.

Contributing

When adding new linters to the lint package:

  1. Create a new file with the lint_ prefix (e.g., lint_my_rule.go)
  2. Implement the Linter interface with all required methods
  3. Register in init() function to enable automatic registration
  4. Add comprehensive tests in a corresponding lint_my_rule_test.go file
  5. Document the linter with clear comments and examples
  6. Choose appropriate severity levels:
    • SeverityError for violations that will cause actual problems
    • SeverityWarning for best practice violations
    • SeverityInfo for suggestions and style preferences
  7. Provide helpful messages with actionable suggestions when possible
  8. Update this README with documentation for the new linter
File Naming Convention
  • Linter implementation: lint_<name>.go
  • Linter tests: lint_<name>_test.go
Example Structure
pkg/lint/
├── lint.go                      # Core API
├── linter.go                    # Interface definition
├── registry.go                  # Registration system
├── violation.go                 # Violation types
├── lint_invisible_index.go      # Built-in linter
├── lint_multiple_alter.go       # Built-in linter
├── lint_primary_key_type.go     # Built-in linter
├── lint_my_new_rule.go          # Your new linter
└── lint_my_new_rule_test.go     # Your tests

Documentation

Overview

Package lint provides a framework for static analysis of MySQL schema definitions and DDL statements. It enables validation and best-practice enforcement beyond the runtime checks provided by the check package.

The linter framework operates on parsed CREATE TABLE statements rather than live database connections.

Basic Usage

Linters are registered via init() functions and executed via RunLinters():

package naming

func init() {
    lint.Register(TableNameLinter{})
}

// Later, run all linters:
violations := lint.RunLinters(tables, stmts, config)

Creating a Linter

To create a custom linter, implement the Linter interface:

type MyLinter struct{}

func (l *MyLinter) Name() string { return "my_linter" }
func (l *MyLinter) Category() string { return "custom" }
func (l *MyLinter) Description() string { return "My custom linter" }
func (l *MyLinter) Lint(createTables []*statement.CreateTable, statements []*statement.AbstractStatement) []lint.Violation {
    // Perform linting logic
    return violations
}

Configuration

Linters can be enabled/disabled via the Config.Enabled map:

config := lint.Config{
    Enabled: map[string]bool{
        "table_name": true,
        "column_name": false,
    },
}

Configurable linters can implement the ConfigurableLinter interface to accept custom settings via Config.Settings. Settings must be provided as map[string]string:

config := lint.Config{
    Settings: map[string]map[string]string{
        "my_linter": {
            "option1": "value1",
            "option2": "value2",
        },
    },
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AlterTableTypeToString

func AlterTableTypeToString(tp ast.AlterTableType) string

AlterTableTypeToString converts an AlterTableType constant to a human-readable string

func ConfigBool

func ConfigBool(value string, key string) (bool, error)

ConfigBool parses a boolean configuration value from a string. It accepts "true" or "false" (case-insensitive) and returns an error for invalid values. The key parameter is used in error messages to provide context.

func CreateTableStatements

func CreateTableStatements(statements ...any) iter.Seq[*statement.CreateTable]

CreateTableStatements returns an iterator over all CREATE TABLE statements, combining those from the existing schema state and those included in incoming changes.

func Disable

func Disable(names ...string) error

Disable disables specific linters by name. Returns an error if the linter is not found.

func Enable

func Enable(names ...string) error

Enable enables specific linters by name. Returns an error if the linter is not found.

func Enabled

func Enabled(name string) bool

Enabled returns whether a specific linter is enabled. Returns an error if the linter is not found.

func GetConfigBool

func GetConfigBool(c map[string]string, key string) bool

func HasErrors

func HasErrors(violations []Violation) bool

HasErrors returns true if any violations have ERROR severity.

func HasWarnings

func HasWarnings(violations []Violation) bool

HasWarnings returns true if any violations have WARNING severity.

func List

func List() []string

List returns the names of all registered linters in sorted order.

func Register

func Register(l Linter)

Register registers a linter with the global registry. This should be called from init() functions in linter implementations. Linters are enabled by default when registered.

func Reset

func Reset()

Reset clears all registered linters. This is primarily useful for testing.

func Stringer

func Stringer(l Linter) string

Stringer returns a string representation of the linter This is a helper function used by linters' String() methods.

Types

type AllowCharset

type AllowCharset struct {
	// contains filtered or unexported fields
}

func (*AllowCharset) Configure

func (l *AllowCharset) Configure(config map[string]string) error

func (*AllowCharset) DefaultConfig

func (l *AllowCharset) DefaultConfig() map[string]string

func (*AllowCharset) Description

func (l *AllowCharset) Description() string

func (*AllowCharset) Lint

func (l *AllowCharset) Lint(createTables []*statement.CreateTable, changes []*statement.AbstractStatement) (violations []Violation)

func (*AllowCharset) Name

func (l *AllowCharset) Name() string

func (*AllowCharset) String

func (l *AllowCharset) String() string

type AllowEngine

type AllowEngine struct {
	// contains filtered or unexported fields
}

func (*AllowEngine) Configure

func (l *AllowEngine) Configure(config map[string]string) error

func (*AllowEngine) DefaultConfig

func (l *AllowEngine) DefaultConfig() map[string]string

func (*AllowEngine) Description

func (l *AllowEngine) Description() string

func (*AllowEngine) Lint

func (l *AllowEngine) Lint(existingTables []*statement.CreateTable, changes []*statement.AbstractStatement) (violations []Violation)

func (*AllowEngine) Name

func (l *AllowEngine) Name() string

func (*AllowEngine) String

func (l *AllowEngine) String() string

type AutoIncCapacityLinter

type AutoIncCapacityLinter struct {
	// contains filtered or unexported fields
}

func (*AutoIncCapacityLinter) Configure

func (l *AutoIncCapacityLinter) Configure(config map[string]string) error

func (*AutoIncCapacityLinter) DefaultConfig

func (l *AutoIncCapacityLinter) DefaultConfig() map[string]string

func (*AutoIncCapacityLinter) Description

func (l *AutoIncCapacityLinter) Description() string

func (*AutoIncCapacityLinter) Lint

func (l *AutoIncCapacityLinter) Lint(existingTables []*statement.CreateTable, changes []*statement.AbstractStatement) (violations []Violation)

func (*AutoIncCapacityLinter) Name

func (l *AutoIncCapacityLinter) Name() string

func (*AutoIncCapacityLinter) String

func (l *AutoIncCapacityLinter) String() string

type Config

type Config struct {
	// Enabled maps linter names to whether they are enabled
	// If a linter is not in this map, it uses its default enabled state
	Enabled map[string]bool

	// Settings maps linter names to their configuration as map[string]string
	// Each linter's settings are provided as key-value string pairs
	Settings map[string]map[string]string

	// LintOnlyChanges indicates whether to lint only the changes
	// or all of the existing schema plus the changes.
	LintOnlyChanges bool

	// IgnoreTables can be used to discard violations for specific tables
	IgnoreTables map[string]bool
}

Config holds linter configuration

func (*Config) IsEnabled

func (c *Config) IsEnabled(linterName string) bool

IsEnabled checks the config as well as the registry to see if a given linter is enabled in either place. If the linter doesn't exist, false is returned because a non-existent linter can't be enabled.

type ConfigurableLinter

type ConfigurableLinter interface {
	Linter

	// Configure applies configuration to the linter
	// Configuration is provided as a map of string keys to string values
	Configure(config map[string]string) error

	// DefaultConfig returns the default configuration for this linter
	DefaultConfig() map[string]string
}

ConfigurableLinter is an optional interface for linters that support configuration

type HasFKLinter

type HasFKLinter struct{}

func (*HasFKLinter) Description

func (l *HasFKLinter) Description() string

func (*HasFKLinter) Lint

func (l *HasFKLinter) Lint(existingTables []*statement.CreateTable, changes []*statement.AbstractStatement) (violations []Violation)

func (*HasFKLinter) Name

func (l *HasFKLinter) Name() string

func (*HasFKLinter) String

func (l *HasFKLinter) String() string

type HasFloatLinter

type HasFloatLinter struct{}

func (*HasFloatLinter) Description

func (l *HasFloatLinter) Description() string

func (*HasFloatLinter) Lint

func (l *HasFloatLinter) Lint(existingTables []*statement.CreateTable, changes []*statement.AbstractStatement) (violations []Violation)

func (*HasFloatLinter) Name

func (l *HasFloatLinter) Name() string

func (*HasFloatLinter) String

func (l *HasFloatLinter) String() string

type InvisibleIndexBeforeDropLinter

type InvisibleIndexBeforeDropLinter struct {
	// contains filtered or unexported fields
}

InvisibleIndexBeforeDropLinter checks that indexes are made invisible before dropping. This is a safety practice to ensure the index is not needed before permanently removing it.

func (*InvisibleIndexBeforeDropLinter) Configure

func (l *InvisibleIndexBeforeDropLinter) Configure(config map[string]string) error

func (*InvisibleIndexBeforeDropLinter) DefaultConfig

func (l *InvisibleIndexBeforeDropLinter) DefaultConfig() map[string]string

func (*InvisibleIndexBeforeDropLinter) Description

func (l *InvisibleIndexBeforeDropLinter) Description() string

func (*InvisibleIndexBeforeDropLinter) Lint

func (*InvisibleIndexBeforeDropLinter) Name

func (*InvisibleIndexBeforeDropLinter) String

type Linter

type Linter interface {
	// Name returns the unique name of this linter
	Name() string

	// Description returns a human-readable description of what this linter checks
	Description() string

	// Lint performs the actual linting and returns any violations found.
	// Linters can use either or both of the parameters as needed.
	Lint(existingTables []*statement.CreateTable, changes []*statement.AbstractStatement) (violations []Violation)

	// String returns a string representation of the linter
	String() string
}

Linter is the interface that all linters must implement

func Get

func Get(name string) (Linter, error)

Get returns a linter by name. Returns an error if the linter is not found.

type Location

type Location struct {
	// Table is the name of the table where the violation occurred
	Table string

	// Column is the name of the column (if applicable)
	Column *string

	// Index is the name of the index (if applicable)
	Index *string

	// Constraint is the name of the constraint (if applicable)
	Constraint *string
}

Location provides information about where a violation occurred

func (*Location) String

func (l *Location) String() string

type MultipleAlterTableLinter

type MultipleAlterTableLinter struct{}

MultipleAlterTableLinter checks for multiple ALTER TABLE statements affecting the same table. Multiple ALTER TABLE statements on the same table should be combined into a single statement for better performance, fewer table rebuilds, and decreased danger of bad intermediate state.

func (*MultipleAlterTableLinter) Description

func (l *MultipleAlterTableLinter) Description() string

func (*MultipleAlterTableLinter) Lint

func (*MultipleAlterTableLinter) Name

func (l *MultipleAlterTableLinter) Name() string

func (*MultipleAlterTableLinter) String

func (l *MultipleAlterTableLinter) String() string

type NameCaseLinter

type NameCaseLinter struct{}

func (*NameCaseLinter) Description

func (l *NameCaseLinter) Description() string

func (*NameCaseLinter) Lint

func (l *NameCaseLinter) Lint(createTables []*statement.CreateTable, changes []*statement.AbstractStatement) (violations []Violation)

func (*NameCaseLinter) Name

func (*NameCaseLinter) Name() string

func (*NameCaseLinter) String

func (l *NameCaseLinter) String() string

type PrimaryKeyLinter

type PrimaryKeyLinter struct {
	// contains filtered or unexported fields
}

PrimaryKeyLinter checks that primary keys are defined and use appropriate data types. Primary keys should be BIGINT (preferably UNSIGNED) or BINARY/VARBINARY, but the linter can be configured to allow other types. Missing PK and other types are flagged as errors, and signed int types are flagged as a warning.

func (*PrimaryKeyLinter) Configure

func (l *PrimaryKeyLinter) Configure(config map[string]string) error

func (*PrimaryKeyLinter) DefaultConfig

func (l *PrimaryKeyLinter) DefaultConfig() map[string]string

func (*PrimaryKeyLinter) Description

func (l *PrimaryKeyLinter) Description() string

func (*PrimaryKeyLinter) Lint

func (l *PrimaryKeyLinter) Lint(existingTables []*statement.CreateTable, changes []*statement.AbstractStatement) (violations []Violation)

func (*PrimaryKeyLinter) Name

func (l *PrimaryKeyLinter) Name() string

func (*PrimaryKeyLinter) String

func (l *PrimaryKeyLinter) String() string

type RedundantIndexLinter

type RedundantIndexLinter struct{}

RedundantIndexLinter checks for redundant indexes in tables. An index is considered redundant if: 1. Its columns are a prefix of the PRIMARY KEY columns 2. Another index's column list starts with this index's columns (prefix match) 3. Another index has exactly the same columns in the same order (duplicate) 4. The index ends with PRIMARY KEY columns (suffix redundancy)

In InnoDB, secondary indexes automatically include PRIMARY KEY columns as a suffix, making certain index patterns redundant and wasteful.

func (*RedundantIndexLinter) Description

func (l *RedundantIndexLinter) Description() string

func (*RedundantIndexLinter) Lint

func (l *RedundantIndexLinter) Lint(existingTables []*statement.CreateTable, changes []*statement.AbstractStatement) []Violation

func (*RedundantIndexLinter) Name

func (l *RedundantIndexLinter) Name() string

func (*RedundantIndexLinter) String

func (l *RedundantIndexLinter) String() string

type ReservedWordsLinter

type ReservedWordsLinter struct{}

ReservedWordsLinter checks for usage of MySQL reserved words in table and column names. Using reserved words as identifiers can cause syntax errors and requires quoting.

func (*ReservedWordsLinter) Description

func (l *ReservedWordsLinter) Description() string

func (*ReservedWordsLinter) Lint

func (l *ReservedWordsLinter) Lint(existingTables []*statement.CreateTable, changes []*statement.AbstractStatement) (violations []Violation)

func (*ReservedWordsLinter) Name

func (l *ReservedWordsLinter) Name() string

func (*ReservedWordsLinter) String

func (l *ReservedWordsLinter) String() string

type Severity

type Severity int

Severity represents the severity level of a linting violation

const (
	// SeverityInfo indicates a suggestion or style preference
	// This is the default value if no explicit Severity is given
	SeverityInfo Severity = iota

	// SeverityWarning indicates a best practice violation or potential issue
	SeverityWarning

	// SeverityError indicates a violation that will cause actual problems
	// (syntax errors, MySQL limitations, etc.)
	SeverityError
)

func (Severity) String added in v0.10.1

func (s Severity) String() string

type UnsafeLinter

type UnsafeLinter struct {
	// contains filtered or unexported fields
}

func (*UnsafeLinter) Configure

func (l *UnsafeLinter) Configure(config map[string]string) error

func (*UnsafeLinter) DefaultConfig

func (l *UnsafeLinter) DefaultConfig() map[string]string

func (*UnsafeLinter) Description

func (l *UnsafeLinter) Description() string

func (*UnsafeLinter) Lint

func (l *UnsafeLinter) Lint(_ []*statement.CreateTable, changes []*statement.AbstractStatement) (violations []Violation)

func (*UnsafeLinter) Name

func (l *UnsafeLinter) Name() string

func (*UnsafeLinter) String

func (l *UnsafeLinter) String() string

type Violation

type Violation struct {
	// Linter is the linter that produced this violation
	Linter Linter

	// Severity is the severity level of the violation
	Severity Severity

	// Message is a human-readable description of the violation
	Message string

	// Location provides information about where the violation occurred
	Location *Location

	// Suggestion is an optional suggestion for fixing the violation
	Suggestion *string

	// Context provides additional context-specific information
	Context map[string]any
}

Violation represents a linting violation found during analysis

func FilterByLinter

func FilterByLinter(violations []Violation, linterName string) []Violation

FilterByLinter returns only violations from the specified linter.

func FilterBySeverity

func FilterBySeverity(violations []Violation, severity Severity) []Violation

FilterBySeverity returns only violations with the specified severity.

func RunLinters

func RunLinters(existingSchema []*statement.CreateTable, changes []*statement.AbstractStatement, config Config) ([]Violation, error)

RunLinters runs all enabled linters and returns any violations found. Linters are executed in an undefined order.

A linter is executed if:

  • It is enabled by default (set during Register), AND
  • It is not explicitly disabled in config.Enabled

OR:

  • It is explicitly enabled in config.Enabled

If a linter implements ConfigurableLinter and has settings in config.Settings, those settings are applied before running the linter.

func (Violation) String

func (v Violation) String() string

type ZeroDateLinter

type ZeroDateLinter struct{}

func (*ZeroDateLinter) Description

func (l *ZeroDateLinter) Description() string

func (*ZeroDateLinter) Lint

func (l *ZeroDateLinter) Lint(existingTables []*statement.CreateTable, changes []*statement.AbstractStatement) (violations []Violation)

func (*ZeroDateLinter) Name

func (l *ZeroDateLinter) Name() string

func (*ZeroDateLinter) String

func (l *ZeroDateLinter) String() string

Directories

Path Synopsis
Package example provides example linters to demonstrate the linter framework.
Package example provides example linters to demonstrate the linter framework.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL