264 lines
7.3 KiB
Go
264 lines
7.3 KiB
Go
package ui
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
|
)
|
|
|
|
// Data operations for indexes, views, sequences, scripts and domain/table assignment.
|
|
|
|
func (se *SchemaEditor) schemaAt(schemaIndex int) (*models.Schema, error) {
|
|
if schemaIndex < 0 || schemaIndex >= len(se.db.Schemas) {
|
|
return nil, errors.New("schema not found")
|
|
}
|
|
return se.db.Schemas[schemaIndex], nil
|
|
}
|
|
|
|
func (se *SchemaEditor) tableAt(schemaIndex, tableIndex int) (*models.Schema, *models.Table, error) {
|
|
schema, err := se.schemaAt(schemaIndex)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
if tableIndex < 0 || tableIndex >= len(schema.Tables) {
|
|
return nil, nil, errors.New("table not found")
|
|
}
|
|
return schema, schema.Tables[tableIndex], nil
|
|
}
|
|
|
|
// splitList splits a comma separated list, trimming blanks and dropping empty entries.
|
|
func splitList(s string) []string {
|
|
parts := make([]string, 0)
|
|
for _, p := range strings.Split(s, ",") {
|
|
if p = strings.TrimSpace(p); p != "" {
|
|
parts = append(parts, p)
|
|
}
|
|
}
|
|
return parts
|
|
}
|
|
|
|
// SaveIndex adds an index to a table. When oldName is non-empty the index of that
|
|
// name is replaced (and renamed if needed).
|
|
func (se *SchemaEditor) SaveIndex(schemaIndex, tableIndex int, oldName string, idx *models.Index) error {
|
|
schema, table, err := se.tableAt(schemaIndex, tableIndex)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
idx.Name = strings.TrimSpace(idx.Name)
|
|
if idx.Name == "" {
|
|
return errors.New("index name is required")
|
|
}
|
|
if len(idx.Columns) == 0 {
|
|
return errors.New("index needs at least one column")
|
|
}
|
|
for _, c := range idx.Columns {
|
|
if _, ok := table.Columns[c]; !ok {
|
|
return fmt.Errorf("column %q not found in table %s", c, table.Name)
|
|
}
|
|
}
|
|
if _, exists := table.Indexes[idx.Name]; exists && idx.Name != oldName {
|
|
return fmt.Errorf("index %q already exists", idx.Name)
|
|
}
|
|
if table.Indexes == nil {
|
|
table.Indexes = make(map[string]*models.Index)
|
|
}
|
|
if oldName != "" {
|
|
delete(table.Indexes, oldName)
|
|
}
|
|
idx.Table = table.Name
|
|
idx.Schema = schema.Name
|
|
table.Indexes[idx.Name] = idx
|
|
table.UpdateDate()
|
|
se.db.UpdateDate()
|
|
return nil
|
|
}
|
|
|
|
// DeleteIndex removes an index from a table.
|
|
func (se *SchemaEditor) DeleteIndex(schemaIndex, tableIndex int, name string) bool {
|
|
_, table, err := se.tableAt(schemaIndex, tableIndex)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
if _, ok := table.Indexes[name]; !ok {
|
|
return false
|
|
}
|
|
delete(table.Indexes, name)
|
|
table.UpdateDate()
|
|
se.db.UpdateDate()
|
|
return true
|
|
}
|
|
|
|
// SaveView adds a view to a schema, or replaces the one at position at (use -1 to add).
|
|
func (se *SchemaEditor) SaveView(schemaIndex, at int, v *models.View) error {
|
|
schema, err := se.schemaAt(schemaIndex)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
v.Name = strings.TrimSpace(v.Name)
|
|
if v.Name == "" {
|
|
return errors.New("view name is required")
|
|
}
|
|
if strings.TrimSpace(v.Definition) == "" {
|
|
return errors.New("view definition is required")
|
|
}
|
|
for i, o := range schema.Views {
|
|
if i != at && o.Name == v.Name {
|
|
return fmt.Errorf("view %q already exists", v.Name)
|
|
}
|
|
}
|
|
v.Schema = schema.Name
|
|
if at >= 0 && at < len(schema.Views) {
|
|
schema.Views[at] = v
|
|
} else {
|
|
schema.Views = append(schema.Views, v)
|
|
}
|
|
schema.UpdateDate()
|
|
se.db.UpdateDate()
|
|
return nil
|
|
}
|
|
|
|
// DeleteView removes the view at position at.
|
|
func (se *SchemaEditor) DeleteView(schemaIndex, at int) bool {
|
|
schema, err := se.schemaAt(schemaIndex)
|
|
if err != nil || at < 0 || at >= len(schema.Views) {
|
|
return false
|
|
}
|
|
schema.Views = append(schema.Views[:at], schema.Views[at+1:]...)
|
|
schema.UpdateDate()
|
|
se.db.UpdateDate()
|
|
return true
|
|
}
|
|
|
|
// SaveSequence adds a sequence to a schema, or replaces the one at position at (use -1 to add).
|
|
func (se *SchemaEditor) SaveSequence(schemaIndex, at int, s *models.Sequence) error {
|
|
schema, err := se.schemaAt(schemaIndex)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.Name = strings.TrimSpace(s.Name)
|
|
if s.Name == "" {
|
|
return errors.New("sequence name is required")
|
|
}
|
|
if s.IncrementBy == 0 {
|
|
return errors.New("increment must not be zero")
|
|
}
|
|
for i, o := range schema.Sequences {
|
|
if i != at && o.Name == s.Name {
|
|
return fmt.Errorf("sequence %q already exists", s.Name)
|
|
}
|
|
}
|
|
s.Schema = schema.Name
|
|
if at >= 0 && at < len(schema.Sequences) {
|
|
schema.Sequences[at] = s
|
|
} else {
|
|
schema.Sequences = append(schema.Sequences, s)
|
|
}
|
|
schema.UpdateDate()
|
|
se.db.UpdateDate()
|
|
return nil
|
|
}
|
|
|
|
// DeleteSequence removes the sequence at position at.
|
|
func (se *SchemaEditor) DeleteSequence(schemaIndex, at int) bool {
|
|
schema, err := se.schemaAt(schemaIndex)
|
|
if err != nil || at < 0 || at >= len(schema.Sequences) {
|
|
return false
|
|
}
|
|
schema.Sequences = append(schema.Sequences[:at], schema.Sequences[at+1:]...)
|
|
schema.UpdateDate()
|
|
se.db.UpdateDate()
|
|
return true
|
|
}
|
|
|
|
// SaveScript adds a script to a schema, or replaces the one at position at (use -1 to add).
|
|
func (se *SchemaEditor) SaveScript(schemaIndex, at int, s *models.Script) error {
|
|
schema, err := se.schemaAt(schemaIndex)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.Name = strings.TrimSpace(s.Name)
|
|
if s.Name == "" {
|
|
return errors.New("script name is required")
|
|
}
|
|
if strings.TrimSpace(s.SQL) == "" {
|
|
return errors.New("script SQL is required")
|
|
}
|
|
for i, o := range schema.Scripts {
|
|
if i != at && o.Name == s.Name {
|
|
return fmt.Errorf("script %q already exists", s.Name)
|
|
}
|
|
}
|
|
s.Schema = schema.Name
|
|
if at >= 0 && at < len(schema.Scripts) {
|
|
schema.Scripts[at] = s
|
|
} else {
|
|
schema.Scripts = append(schema.Scripts, s)
|
|
}
|
|
schema.UpdateDate()
|
|
se.db.UpdateDate()
|
|
return nil
|
|
}
|
|
|
|
// DeleteScript removes the script at position at.
|
|
func (se *SchemaEditor) DeleteScript(schemaIndex, at int) bool {
|
|
schema, err := se.schemaAt(schemaIndex)
|
|
if err != nil || at < 0 || at >= len(schema.Scripts) {
|
|
return false
|
|
}
|
|
schema.Scripts = append(schema.Scripts[:at], schema.Scripts[at+1:]...)
|
|
schema.UpdateDate()
|
|
se.db.UpdateDate()
|
|
return true
|
|
}
|
|
|
|
// AssignTableToDomain adds a reference to schemaName.tableName to the domain at domainIndex.
|
|
func (se *SchemaEditor) AssignTableToDomain(domainIndex int, schemaName, tableName string) error {
|
|
if domainIndex < 0 || domainIndex >= len(se.db.Domains) {
|
|
return errors.New("domain not found")
|
|
}
|
|
domain := se.db.Domains[domainIndex]
|
|
var table *models.Table
|
|
for _, s := range se.db.Schemas {
|
|
if s.Name != schemaName {
|
|
continue
|
|
}
|
|
for _, t := range s.Tables {
|
|
if t.Name == tableName {
|
|
table = t
|
|
}
|
|
}
|
|
}
|
|
if table == nil {
|
|
return fmt.Errorf("table %s.%s not found", schemaName, tableName)
|
|
}
|
|
for _, dt := range domain.Tables {
|
|
if dt.SchemaName == schemaName && dt.TableName == tableName {
|
|
return fmt.Errorf("table %s.%s is already in domain %s", schemaName, tableName, domain.Name)
|
|
}
|
|
}
|
|
dt := models.InitDomainTable(tableName, schemaName)
|
|
dt.RefTable = table
|
|
dt.Sequence = uint(len(domain.Tables))
|
|
domain.Tables = append(domain.Tables, dt)
|
|
se.db.UpdateDate()
|
|
return nil
|
|
}
|
|
|
|
// UnassignTableFromDomain removes the reference to schemaName.tableName from the domain.
|
|
func (se *SchemaEditor) UnassignTableFromDomain(domainIndex int, schemaName, tableName string) bool {
|
|
if domainIndex < 0 || domainIndex >= len(se.db.Domains) {
|
|
return false
|
|
}
|
|
domain := se.db.Domains[domainIndex]
|
|
for i, dt := range domain.Tables {
|
|
if dt.SchemaName == schemaName && dt.TableName == tableName {
|
|
domain.Tables = append(domain.Tables[:i], domain.Tables[i+1:]...)
|
|
se.db.UpdateDate()
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|