schema

package
v1.10.2 Latest Latest
Warning

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

Go to latest
Published: Mar 13, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package schema provides schema-aware SQL validation for GoSQLX.

The package defines a hierarchy of Schema, Table, Column, and Catalog types that represent a relational database schema. A Validator created from a Schema (or Catalog) walks the AST produced by the GoSQLX parser and reports semantic errors such as references to non-existent tables or columns, ambiguous cross-schema column references, and INSERT column-count mismatches. Schemas can be built programmatically or loaded from DDL (CREATE TABLE) statements using GoSQLX's own parser via LoadFromDDL. All table and column lookups are case-insensitive to match SQL standard behaviour. For multi-schema environments the Catalog type resolves table references across schemas and returns a clear error when the same table name exists in more than one schema.

Example - Programmatic schema building:

s := schema.NewSchema("mydb")
t := schema.NewTable("users")
t.AddColumn(&schema.Column{Name: "id", DataType: "INT", Nullable: false})
t.AddColumn(&schema.Column{Name: "name", DataType: "VARCHAR(100)", Nullable: false})
s.AddTable(t)

v := schema.NewValidator(s)
errors, err := v.Validate("SELECT id, name FROM users")

Example - Loading schema from DDL:

ddl := `CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(100) NOT NULL);`
s, err := schema.LoadFromDDL(ddl)
if err != nil {
    log.Fatal(err)
}
v := schema.NewValidator(s)
errors, _ := v.Validate("SELECT email FROM users")
// errors[0].Message: column "email" does not exist in table "users"

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Catalog

type Catalog struct {
	Name          string
	DefaultSchema string
	Schemas       map[string]*Schema
}

Catalog represents a database catalog containing multiple schemas (databases). It allows cross-schema query validation and catalog-level operations.

Example:

cat := schema.NewCatalog()
s1 := schema.NewSchema("app_db")
s2 := schema.NewSchema("audit_db")
cat.AddSchema(s1)
cat.AddSchema(s2)
cat.DefaultSchema = "app_db"

func LoadCatalogFromDDL

func LoadCatalogFromDDL(sql string) (*Catalog, error)

LoadCatalogFromDDL parses DDL statements from sql and builds a Catalog. It handles CREATE TABLE, ALTER TABLE (ADD/DROP/RENAME COLUMN), and CREATE SCHEMA statements. Multiple schemas can be separated by "USE schema_name;" or by prefixing table names with "schema.table". Non-DDL statements are silently ignored.

Example:

ddl := `
  CREATE TABLE app.users (id INT PRIMARY KEY, name VARCHAR(100) NOT NULL);
  CREATE TABLE audit.events (id INT, user_id INT, action VARCHAR(50));
  ALTER TABLE app.users ADD COLUMN email VARCHAR(255);
`
cat, err := schema.LoadCatalogFromDDL(ddl)

func LoadCatalogFromDDLFile

func LoadCatalogFromDDLFile(path string) (*Catalog, error)

LoadCatalogFromDDLFile reads a file and calls LoadCatalogFromDDL.

func NewCatalog

func NewCatalog() *Catalog

NewCatalog creates a new empty Catalog.

func (*Catalog) AddSchema

func (c *Catalog) AddSchema(s *Schema)

AddSchema adds (or replaces) a schema in the catalog. Lookups are case-insensitive.

func (*Catalog) GetDefaultSchema

func (c *Catalog) GetDefaultSchema() (*Schema, bool)

GetDefaultSchema returns the default schema of the catalog. If DefaultSchema is set, it is used. Otherwise the single schema is returned when there is exactly one, and nil/false is returned for empty or ambiguous catalogs.

func (*Catalog) GetSchema

func (c *Catalog) GetSchema(name string) (*Schema, bool)

GetSchema returns a schema by name (case-insensitive).

func (*Catalog) ResolveTable

func (c *Catalog) ResolveTable(tableName string) (*Schema, *Table, error)

ResolveTable looks up a table by name across the catalog. It first searches the default schema, then every other schema.

When no default schema is set and the same table name exists in more than one schema, ResolveTable returns an ambiguity error so callers are never silently handed the wrong table:

ambiguous table reference: 'users' exists in multiple schemas [app, audit]; qualify with schema name

Returns the owning schema, the table, and nil error when found unambiguously. Returns nil, nil, nil when the table does not exist in any schema. Returns nil, nil, error when the reference is ambiguous.

func (*Catalog) SchemaNames

func (c *Catalog) SchemaNames() []string

SchemaNames returns a sorted list of all schema names in the catalog.

type CatalogValidator

type CatalogValidator struct {
	Catalog *Catalog
}

CatalogValidator validates SQL queries against a full Catalog (multi-schema). It supports all the same checks as Validator plus cross-schema resolution.

Example:

cat, _ := schema.LoadCatalogFromDDL(ddl)
cv := schema.NewCatalogValidator(cat)
errors, err := cv.Validate("SELECT id, name FROM users WHERE bogus = 1")

func NewCatalogValidator

func NewCatalogValidator(cat *Catalog) *CatalogValidator

NewCatalogValidator creates a CatalogValidator for the given catalog.

func (*CatalogValidator) Validate

func (cv *CatalogValidator) Validate(sql string) ([]ValidationError, error)

Validate parses the SQL string and validates it against the catalog.

func (*CatalogValidator) ValidateAST

func (cv *CatalogValidator) ValidateAST(tree *ast.AST) []ValidationError

ValidateAST validates a parsed AST against the catalog.

type Column

type Column struct {
	Name       string
	DataType   string
	Nullable   bool
	Default    string
	References *ForeignKeyRef // if this column references another table
}

Column represents a table column.

type ForeignKey

type ForeignKey struct {
	Name       string
	Columns    []string
	RefTable   string
	RefColumns []string
}

ForeignKey represents a foreign key constraint.

type ForeignKeyRef

type ForeignKeyRef struct {
	Table  string
	Column string
}

ForeignKeyRef is a column-level FK reference.

type Index

type Index struct {
	Name    string
	Columns []string
	Unique  bool
}

Index represents a table index.

type Schema

type Schema struct {
	Name   string
	Tables map[string]*Table
}

Schema represents a database schema with tables, columns, and constraints.

func LoadFromDDL

func LoadFromDDL(sql string) (*Schema, error)

LoadFromDDL parses CREATE TABLE statements from the given SQL string and builds a Schema. Multiple CREATE TABLE statements can be included in the input, separated by semicolons or newlines. Non-CREATE TABLE statements are silently ignored.

Example:

ddl := `
  CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(255) UNIQUE
  );
  CREATE TABLE orders (
    id INT PRIMARY KEY,
    user_id INT REFERENCES users(id),
    total DECIMAL(10,2)
  );
`
schema, err := LoadFromDDL(ddl)

func LoadFromDDLFile

func LoadFromDDLFile(path string) (*Schema, error)

LoadFromDDLFile reads a file at the given path and loads schema from the DDL statements contained within it.

func NewSchema

func NewSchema(name string) *Schema

NewSchema creates a new Schema with the given name.

func (*Schema) AddTable

func (s *Schema) AddTable(table *Table)

AddTable adds a table to the schema. If a table with the same name already exists, it is replaced. Tables are stored with their original name but lookups are case-insensitive.

func (*Schema) GetTable

func (s *Schema) GetTable(name string) (*Table, bool)

GetTable looks up a table by name. Returns the table and true if found, or nil and false if not found. The lookup is case-insensitive.

func (*Schema) TableNames

func (s *Schema) TableNames() []string

TableNames returns a sorted list of all table names in the schema, using the original names from the Table structs.

type Table

type Table struct {
	Name        string
	Columns     map[string]*Column
	PrimaryKey  []string
	ForeignKeys []ForeignKey
	Indexes     []Index
}

Table represents a database table.

func NewTable

func NewTable(name string) *Table

NewTable creates a new Table with the given name.

func (*Table) AddColumn

func (t *Table) AddColumn(col *Column)

AddColumn adds a column to the table. If a column with the same name already exists, it is replaced. Columns are stored with their original name but lookups are case-insensitive.

func (*Table) ColumnNames

func (t *Table) ColumnNames() []string

ColumnNames returns a sorted list of all column names in the table, using the original names from the Column structs.

func (*Table) GetColumn

func (t *Table) GetColumn(name string) (*Column, bool)

GetColumn looks up a column by name. Returns the column and true if found, or nil and false if not found. The lookup is case-insensitive.

type TypeCategory

type TypeCategory int

TypeCategory represents a broad category of SQL data types for compatibility checking.

const (
	TypeCategoryUnknown TypeCategory = iota
	TypeCategoryNumeric
	TypeCategoryString
	TypeCategoryDateTime
	TypeCategoryBoolean
)

type ValidationError

type ValidationError struct {
	Message    string // Human-readable description of the issue
	Line       int    // Line number (1-based, 0 if unknown)
	Column     int    // Column number (1-based, 0 if unknown)
	Severity   string // "error" or "warning"
	Suggestion string // Optional suggestion for how to fix the issue
}

ValidationError represents a single validation issue found in a SQL query. Note: Line and Column are populated when position information is available from AST nodes. Currently, most AST nodes do not carry position data, so these fields will be 0 for most errors. Future AST enhancements will enable precise source location tracking.

func ValidateQuery

func ValidateQuery(tree *ast.AST, cat *Catalog) []ValidationError

ValidateQuery validates a pre-parsed AST against an explicit catalog. This is a convenience function for when the caller already has both.

func (ValidationError) Error

func (e ValidationError) Error() string

Error returns a human-readable string for the validation error.

type Validator

type Validator struct {
	Schema *Schema
}

Validator validates SQL queries against a schema.

func NewValidator

func NewValidator(schema *Schema) *Validator

NewValidator creates a new Validator for the given schema.

func (*Validator) Validate

func (v *Validator) Validate(sql string) ([]ValidationError, error)

Validate parses a SQL query and validates it against the schema. Returns a slice of validation errors (which may be empty if valid) and an error if parsing fails.

func (*Validator) ValidateAST

func (v *Validator) ValidateAST(tree *ast.AST) []ValidationError

ValidateAST checks a parsed AST against the schema and returns any validation errors found.

func (*Validator) ValidateForeignKeys

func (v *Validator) ValidateForeignKeys() []ValidationError

ValidateForeignKeys checks that all foreign key references in the schema point to valid tables and columns. This is a schema-level check that can be called independently of query validation.

func (*Validator) ValidateSelectFull

func (v *Validator) ValidateSelectFull(sql string) ([]ValidationError, error)

ValidateSelectFull performs full SELECT validation including ORDER BY and GROUP BY aggregate semantics via the existing single-schema Validator. This makes the enhanced validation available without requiring a Catalog.

Jump to

Keyboard shortcuts

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