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 }