Files
relspecgo/pkg/models/flatview.go
2025-12-28 11:42:05 +02:00

203 lines
10 KiB
Go

package models
import "fmt"
// Flat/Denormalized Views
//
// This file provides flattened data structures with fully qualified names
// for easier querying and analysis of database schemas without navigating
// nested hierarchies.
// FlatColumn represents a column with full database context in a single structure.
// It includes fully qualified names for easy identification and querying.
type FlatColumn struct {
DatabaseName string `json:"database_name" yaml:"database_name" xml:"database_name"`
SchemaName string `json:"schema_name" yaml:"schema_name" xml:"schema_name"`
TableName string `json:"table_name" yaml:"table_name" xml:"table_name"`
ColumnName string `json:"column_name" yaml:"column_name" xml:"column_name"`
FullyQualifiedName string `json:"fully_qualified_name" yaml:"fully_qualified_name" xml:"fully_qualified_name"` // database.schema.table.column
Type string `json:"type" yaml:"type" xml:"type"`
Length int `json:"length,omitempty" yaml:"length,omitempty" xml:"length,omitempty"`
Precision int `json:"precision,omitempty" yaml:"precision,omitempty" xml:"precision,omitempty"`
Scale int `json:"scale,omitempty" yaml:"scale,omitempty" xml:"scale,omitempty"`
NotNull bool `json:"not_null" yaml:"not_null" xml:"not_null"`
Default any `json:"default,omitempty" yaml:"default,omitempty" xml:"default,omitempty"`
AutoIncrement bool `json:"auto_increment" yaml:"auto_increment" xml:"auto_increment"`
IsPrimaryKey bool `json:"is_primary_key" yaml:"is_primary_key" xml:"is_primary_key"`
Description string `json:"description,omitempty" yaml:"description,omitempty" xml:"description,omitempty"`
Comment string `json:"comment,omitempty" yaml:"comment,omitempty" xml:"comment,omitempty"`
}
// ToFlatColumns converts a Database to a slice of FlatColumns for denormalized access to all columns.
func (d *Database) ToFlatColumns() []*FlatColumn {
flatColumns := make([]*FlatColumn, 0)
for _, schema := range d.Schemas {
for _, table := range schema.Tables {
for _, column := range table.Columns {
flatColumns = append(flatColumns, &FlatColumn{
DatabaseName: d.Name,
SchemaName: schema.Name,
TableName: table.Name,
ColumnName: column.Name,
FullyQualifiedName: fmt.Sprintf("%s.%s.%s.%s", d.Name, schema.Name, table.Name, column.Name),
Type: column.Type,
Length: column.Length,
Precision: column.Precision,
Scale: column.Scale,
NotNull: column.NotNull,
Default: column.Default,
AutoIncrement: column.AutoIncrement,
IsPrimaryKey: column.IsPrimaryKey,
Description: column.Description,
Comment: column.Comment,
})
}
}
}
return flatColumns
}
// FlatTable represents a table with full database context and aggregated counts.
type FlatTable struct {
DatabaseName string `json:"database_name" yaml:"database_name" xml:"database_name"`
SchemaName string `json:"schema_name" yaml:"schema_name" xml:"schema_name"`
TableName string `json:"table_name" yaml:"table_name" xml:"table_name"`
FullyQualifiedName string `json:"fully_qualified_name" yaml:"fully_qualified_name" xml:"fully_qualified_name"` // database.schema.table
Description string `json:"description,omitempty" yaml:"description,omitempty" xml:"description,omitempty"`
Comment string `json:"comment,omitempty" yaml:"comment,omitempty" xml:"comment,omitempty"`
Tablespace string `json:"tablespace,omitempty" yaml:"tablespace,omitempty" xml:"tablespace,omitempty"`
ColumnCount int `json:"column_count" yaml:"column_count" xml:"column_count"`
ConstraintCount int `json:"constraint_count" yaml:"constraint_count" xml:"constraint_count"`
IndexCount int `json:"index_count" yaml:"index_count" xml:"index_count"`
}
// ToFlatTables converts a Database to a slice of FlatTables for denormalized access to all tables.
func (d *Database) ToFlatTables() []*FlatTable {
flatTables := make([]*FlatTable, 0)
for _, schema := range d.Schemas {
for _, table := range schema.Tables {
flatTables = append(flatTables, &FlatTable{
DatabaseName: d.Name,
SchemaName: schema.Name,
TableName: table.Name,
FullyQualifiedName: fmt.Sprintf("%s.%s.%s", d.Name, schema.Name, table.Name),
Description: table.Description,
Comment: table.Comment,
Tablespace: table.Tablespace,
ColumnCount: len(table.Columns),
ConstraintCount: len(table.Constraints),
IndexCount: len(table.Indexes),
})
}
}
return flatTables
}
// FlatConstraint represents a constraint with full database context and resolved references.
type FlatConstraint struct {
DatabaseName string `json:"database_name" yaml:"database_name" xml:"database_name"`
SchemaName string `json:"schema_name" yaml:"schema_name" xml:"schema_name"`
TableName string `json:"table_name" yaml:"table_name" xml:"table_name"`
ConstraintName string `json:"constraint_name" yaml:"constraint_name" xml:"constraint_name"`
FullyQualifiedName string `json:"fully_qualified_name" yaml:"fully_qualified_name" xml:"fully_qualified_name"` // database.schema.table.constraint
Type ConstraintType `json:"type" yaml:"type" xml:"type"`
Columns []string `json:"columns" yaml:"columns" xml:"columns"`
Expression string `json:"expression,omitempty" yaml:"expression,omitempty" xml:"expression,omitempty"`
ReferencedTable string `json:"referenced_table,omitempty" yaml:"referenced_table,omitempty" xml:"referenced_table,omitempty"`
ReferencedSchema string `json:"referenced_schema,omitempty" yaml:"referenced_schema,omitempty" xml:"referenced_schema,omitempty"`
ReferencedColumns []string `json:"referenced_columns,omitempty" yaml:"referenced_columns,omitempty" xml:"referenced_columns,omitempty"`
ReferencedFQN string `json:"referenced_fqn,omitempty" yaml:"referenced_fqn,omitempty" xml:"referenced_fqn,omitempty"` // Fully qualified reference
OnDelete string `json:"on_delete,omitempty" yaml:"on_delete,omitempty" xml:"on_delete,omitempty"`
OnUpdate string `json:"on_update,omitempty" yaml:"on_update,omitempty" xml:"on_update,omitempty"`
}
// ToFlatConstraints converts a Database to a slice of FlatConstraints for denormalized access to all constraints.
func (d *Database) ToFlatConstraints() []*FlatConstraint {
flatConstraints := make([]*FlatConstraint, 0)
for _, schema := range d.Schemas {
for _, table := range schema.Tables {
for _, constraint := range table.Constraints {
fc := &FlatConstraint{
DatabaseName: d.Name,
SchemaName: schema.Name,
TableName: table.Name,
ConstraintName: constraint.Name,
FullyQualifiedName: fmt.Sprintf("%s.%s.%s.%s", d.Name, schema.Name, table.Name, constraint.Name),
Type: constraint.Type,
Columns: constraint.Columns,
Expression: constraint.Expression,
ReferencedTable: constraint.ReferencedTable,
ReferencedSchema: constraint.ReferencedSchema,
ReferencedColumns: constraint.ReferencedColumns,
OnDelete: constraint.OnDelete,
OnUpdate: constraint.OnUpdate,
}
// Build fully qualified reference name for foreign keys
if constraint.Type == ForeignKeyConstraint && constraint.ReferencedTable != "" {
fc.ReferencedFQN = fmt.Sprintf("%s.%s.%s", d.Name, constraint.ReferencedSchema, constraint.ReferencedTable)
}
flatConstraints = append(flatConstraints, fc)
}
}
}
return flatConstraints
}
// FlatRelationship represents a relationship with full database context and fully qualified table names.
type FlatRelationship struct {
DatabaseName string `json:"database_name" yaml:"database_name" xml:"database_name"`
RelationshipName string `json:"relationship_name" yaml:"relationship_name" xml:"relationship_name"`
Type RelationType `json:"type" yaml:"type" xml:"type"`
FromFQN string `json:"from_fqn" yaml:"from_fqn" xml:"from_fqn"` // database.schema.table
ToFQN string `json:"to_fqn" yaml:"to_fqn" xml:"to_fqn"` // database.schema.table
FromTable string `json:"from_table" yaml:"from_table" xml:"from_table"`
FromSchema string `json:"from_schema" yaml:"from_schema" xml:"from_schema"`
ToTable string `json:"to_table" yaml:"to_table" xml:"to_table"`
ToSchema string `json:"to_schema" yaml:"to_schema" xml:"to_schema"`
ForeignKey string `json:"foreign_key" yaml:"foreign_key" xml:"foreign_key"`
ThroughTableFQN string `json:"through_table_fqn,omitempty" yaml:"through_table_fqn,omitempty" xml:"through_table_fqn,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty" xml:"description,omitempty"`
}
// ToFlatRelationships converts a Database to a slice of FlatRelationships for denormalized access to all relationships.
func (d *Database) ToFlatRelationships() []*FlatRelationship {
flatRelationships := make([]*FlatRelationship, 0)
for _, schema := range d.Schemas {
for _, table := range schema.Tables {
for _, relationship := range table.Relationships {
fr := &FlatRelationship{
DatabaseName: d.Name,
RelationshipName: relationship.Name,
Type: relationship.Type,
FromFQN: fmt.Sprintf("%s.%s.%s", d.Name, relationship.FromSchema, relationship.FromTable),
ToFQN: fmt.Sprintf("%s.%s.%s", d.Name, relationship.ToSchema, relationship.ToTable),
FromTable: relationship.FromTable,
FromSchema: relationship.FromSchema,
ToTable: relationship.ToTable,
ToSchema: relationship.ToSchema,
ForeignKey: relationship.ForeignKey,
Description: relationship.Description,
}
// Add through table FQN for many-to-many relationships
if relationship.ThroughTable != "" {
fr.ThroughTableFQN = fmt.Sprintf("%s.%s.%s", d.Name, relationship.ThroughSchema, relationship.ThroughTable)
}
flatRelationships = append(flatRelationships, fr)
}
}
}
return flatRelationships
}