RelSpec Inspector
Database Schema Validation and Linting Tool
The RelSpec Inspector validates database schemas against configurable rules, helping you maintain consistency, enforce naming conventions, and catch common schema design issues across your database models.
Overview
The Inspector reads database schemas from any supported RelSpec format and validates them against a set of configurable rules. It generates detailed reports highlighting violations, warnings, and passed checks.
Features
- Flexible Rule Configuration: YAML-based rules with three severity levels (enforce, warn, off)
- Generic Validators: Reusable regex-based validators for custom naming conventions
- Multiple Input Formats: Works with all RelSpec readers (PostgreSQL, DBML, JSON, GORM, Bun, etc.)
- Multiple Output Formats: Markdown with ANSI colors for terminals, JSON for tooling integration
- Smart Defaults: Works out-of-the-box with sensible default rules
- Terminal-Aware: Automatic color support detection for improved readability
- Exit Codes: Proper exit codes for CI/CD integration
Quick Start
Basic Usage
# Inspect a PostgreSQL database with default rules
relspec inspect --from pgsql --from-conn "postgres://user:pass@localhost/mydb"
# Inspect a DBML file
relspec inspect --from dbml --from-path schema.dbml
# Inspect with custom rules
relspec inspect --from json --from-path db.json --rules my-rules.yaml
# Output JSON report to file
relspec inspect --from pgsql --from-conn "..." \
--output-format json --output report.json
# Inspect specific schema only
relspec inspect --from pgsql --from-conn "..." --schema public
Configuration
Create a .relspec-rules.yaml file to customize validation rules. If the file doesn't exist, the inspector uses sensible defaults.
version: "1.0"
rules:
# Primary key columns must start with "id_"
primary_key_naming:
enabled: enforce # enforce|warn|off
function: primary_key_naming
pattern: "^id_"
message: "Primary key columns must start with 'id_'"
# Foreign key columns must start with "rid_"
foreign_key_column_naming:
enabled: warn
function: foreign_key_column_naming
pattern: "^rid_"
message: "Foreign key columns should start with 'rid_'"
# Table names must be lowercase snake_case
table_naming_case:
enabled: warn
function: table_regexpr # Generic regex validator
pattern: "^[a-z][a-z0-9_]*$"
message: "Table names should be lowercase with underscores"
# Ensure all tables have primary keys
missing_primary_key:
enabled: warn
function: have_primary_key
message: "Table is missing a primary key"
Built-in Validation Rules
Primary Key Rules
| Rule | Function | Description |
|---|---|---|
primary_key_naming |
primary_key_naming |
Validate PK column names against regex pattern |
primary_key_datatype |
primary_key_datatype |
Enforce approved PK data types (bigint, serial, etc.) |
primary_key_auto_increment |
primary_key_auto_increment |
Check if PKs have auto-increment enabled |
Foreign Key Rules
| Rule | Function | Description |
|---|---|---|
foreign_key_column_naming |
foreign_key_column_naming |
Validate FK column names against regex pattern |
foreign_key_constraint_naming |
foreign_key_constraint_naming |
Validate FK constraint names against regex pattern |
foreign_key_index |
foreign_key_index |
Ensure FK columns have indexes for performance |
Naming Convention Rules
| Rule | Function | Description |
|---|---|---|
table_naming_case |
table_regexpr |
Generic regex validator for table names |
column_naming_case |
column_regexpr |
Generic regex validator for column names |
Length Rules
| Rule | Function | Description |
|---|---|---|
table_name_length |
table_name_length |
Limit table name length (default: 64 chars) |
column_name_length |
column_name_length |
Limit column name length (default: 64 chars) |
Reserved Keywords
| Rule | Function | Description |
|---|---|---|
reserved_keywords |
reserved_words |
Detect use of SQL reserved keywords as identifiers |
Schema Integrity Rules
| Rule | Function | Description |
|---|---|---|
missing_primary_key |
have_primary_key |
Ensure tables have primary keys |
orphaned_foreign_key |
orphaned_foreign_key |
Detect FKs referencing non-existent tables |
circular_dependency |
circular_dependency |
Detect circular FK dependencies |
Rule Configuration
Severity Levels
Rules support three severity levels:
enforce: Violations are errors (exit code 1)warn: Violations are warnings (exit code 0)off: Rule is disabled
Rule Structure
rule_name:
enabled: enforce|warn|off
function: validator_function_name
message: "Custom message shown on violation"
# Rule-specific parameters
pattern: "^regex_pattern$" # For pattern-based validators
allowed_types: [type1, type2] # For type validators
max_length: 64 # For length validators
check_tables: true # For keyword validator
check_columns: true # For keyword validator
require_index: true # For FK index validator
Generic Validators
The inspector provides generic validator functions that can be reused for custom rules:
table_regexpr
Generic regex validator for table names. Create custom table naming rules:
# Example: Ensure table names don't contain numbers
table_no_numbers:
enabled: warn
function: table_regexpr
pattern: "^[a-z_]+$"
message: "Table names should not contain numbers"
# Example: Tables must start with "tbl_"
table_prefix:
enabled: enforce
function: table_regexpr
pattern: "^tbl_[a-z][a-z0-9_]*$"
message: "Table names must start with 'tbl_'"
column_regexpr
Generic regex validator for column names. Create custom column naming rules:
# Example: Audit columns must end with "_audit"
audit_column_suffix:
enabled: enforce
function: column_regexpr
pattern: ".*_audit$"
message: "Audit columns must end with '_audit'"
# Example: Timestamp columns must end with "_at"
timestamp_suffix:
enabled: warn
function: column_regexpr
pattern: ".*(created|updated|deleted)_at$"
message: "Timestamp columns should end with '_at'"
Output Formats
Markdown (Default)
Human-readable markdown report with ANSI colors when outputting to a terminal:
# RelSpec Inspector Report
**Database:** my_database
**Source Format:** pgsql
**Generated:** 2025-12-31T10:30:45Z
## Summary
- Rules Checked: 13
- Errors: 2
- Warnings: 5
- Passed: 120
## Violations
### Errors (2)
#### primary_key_naming
**Location:** public.users.user_id
**Message:** Primary key columns must start with 'id_'
**Details:** expected_pattern=^id_
### Warnings (5)
#### foreign_key_index
**Location:** public.orders.customer_id
**Message:** Foreign key columns should have indexes
**Details:** has_index=false
JSON
Structured JSON output for tooling integration:
{
"summary": {
"total_rules": 13,
"rules_checked": 13,
"error_count": 2,
"warning_count": 5,
"passed_count": 120
},
"violations": [
{
"rule_name": "primary_key_naming",
"level": "error",
"message": "Primary key columns must start with 'id_'",
"location": "public.users.user_id",
"context": {
"schema": "public",
"table": "users",
"column": "user_id",
"expected_pattern": "^id_"
},
"passed": false
}
],
"generated_at": "2025-12-31T10:30:45Z",
"database": "my_database",
"source_format": "pgsql"
}
CLI Reference
Flags
| Flag | Type | Description |
|---|---|---|
--from |
string | Required. Source format (dbml, pgsql, json, yaml, gorm, etc.) |
--from-path |
string | Source file path (for file-based formats) |
--from-conn |
string | Connection string (for database formats) |
--rules |
string | Path to rules YAML file (default: .relspec-rules.yaml) |
--output-format |
string | Output format: markdown or json (default: markdown) |
--output |
string | Output file path (default: stdout) |
--schema |
string | Filter to specific schema by name |
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success (no errors, only warnings or all passed) |
| 1 | Validation errors found (rules with enabled: enforce failed) |
| 2 | Runtime error (invalid config, reader error, etc.) |
CI/CD Integration
GitHub Actions Example
name: Schema Validation
on: [pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install RelSpec
run: go install git.warky.dev/wdevs/relspecgo/cmd/relspec@latest
- name: Validate Schema
run: |
relspec inspect \
--from dbml \
--from-path schema.dbml \
--rules .relspec-rules.yaml \
--output-format json \
--output validation-report.json
- name: Upload Report
if: always()
uses: actions/upload-artifact@v3
with:
name: validation-report
path: validation-report.json
Pre-commit Hook Example
#!/bin/bash
# .git/hooks/pre-commit
echo "Running schema validation..."
relspec inspect \
--from dbml \
--from-path schema.dbml \
--rules .relspec-rules.yaml
exit $?
Example Configuration File
See .relspec-rules.yaml.example for a fully documented example configuration with all available rules and customization options.
Common Use Cases
Enforce Naming Standards
# Ensure consistent naming across your schema
table_naming_case:
enabled: enforce
function: table_regexpr
pattern: "^[a-z][a-z0-9_]*$"
message: "Tables must use snake_case"
column_naming_case:
enabled: enforce
function: column_regexpr
pattern: "^[a-z][a-z0-9_]*$"
message: "Columns must use snake_case"
primary_key_naming:
enabled: enforce
function: primary_key_naming
pattern: "^id$"
message: "Primary key must be named 'id'"
foreign_key_column_naming:
enabled: enforce
function: foreign_key_column_naming
pattern: "^[a-z]+_id$"
message: "Foreign keys must end with '_id'"
Performance Best Practices
# Ensure optimal database performance
foreign_key_index:
enabled: enforce
function: foreign_key_index
require_index: true
message: "Foreign keys must have indexes"
primary_key_datatype:
enabled: enforce
function: primary_key_datatype
allowed_types: [bigserial, bigint]
message: "Use bigserial or bigint for primary keys"
Schema Integrity
# Prevent common schema issues
missing_primary_key:
enabled: enforce
function: have_primary_key
message: "All tables must have a primary key"
orphaned_foreign_key:
enabled: enforce
function: orphaned_foreign_key
message: "Foreign keys must reference existing tables"
circular_dependency:
enabled: warn
function: circular_dependency
message: "Circular dependencies detected"
Avoid Reserved Keywords
reserved_keywords:
enabled: warn
function: reserved_words
check_tables: true
check_columns: true
message: "Avoid using SQL reserved keywords"
Programmatic Usage
You can use the inspector programmatically in your Go code:
package main
import (
"fmt"
"git.warky.dev/wdevs/relspecgo/pkg/inspector"
"git.warky.dev/wdevs/relspecgo/pkg/models"
)
func main() {
// Load your database model
db := &models.Database{
Name: "my_database",
Schemas: []*models.Schema{
// ... your schema
},
}
// Load rules configuration
config, err := inspector.LoadConfig(".relspec-rules.yaml")
if err != nil {
panic(err)
}
// Create and run inspector
insp := inspector.NewInspector(db, config)
report, err := insp.Inspect()
if err != nil {
panic(err)
}
// Generate report
formatter := inspector.NewMarkdownFormatter(os.Stdout)
output, err := formatter.Format(report)
if err != nil {
panic(err)
}
fmt.Println(output)
// Check for errors
if report.HasErrors() {
os.Exit(1)
}
}
Contributing
Contributions are welcome! To add a new validator:
- Add the validator function to
validators.go - Register it in
inspector.gogetValidator()function - Add default configuration to
rules.goGetDefaultConfig() - Update this README with the new rule documentation
License
Apache License 2.0 - See LICENSE for details.