package store import ( "context" "errors" "fmt" "os" "path/filepath" ) // SQLiteBackend legt die Kontroll-Datenbank und jeden Workspace als eigene // SQLite-Datei ab. Das ist der Standard und der bisherige Aufbau // (AUSBAUSTUFEN.md 4.4). type SQLiteBackend struct { controlPath string // z.B. "/control.db" workspaceDir string // z.B. "/workspaces", muss existieren } // NewSQLite legt einen SQLite-Speicher an. Der Workspace-Ordner muss bereits // existieren — das übernimmt main.go beim Start. // // Wer nur Workspaces braucht (etwa ein Test des workspace.Manager), kann // controlPath leer lassen; OpenControl meldet dann einen Fehler. func NewSQLite(controlPath, workspaceDir string) *SQLiteBackend { return &SQLiteBackend{controlPath: controlPath, workspaceDir: workspaceDir} } func (s *SQLiteBackend) OpenControl() (*ControlStore, error) { if s.controlPath == "" { return nil, errors.New("SQLite-Speicher ohne Pfad zur Kontroll-Datenbank") } return OpenControl(s.controlPath) } func (s *SQLiteBackend) OpenWorkspace(workspaceID string) (*Store, error) { return Open(s.workspaceFile(workspaceID)) } // DeleteWorkspace entfernt die Workspace-Datei unwiederbringlich von der // Platte — samt WAL/SHM-Nebendateien, die SQLite im laufenden Betrieb // danebenlegt. func (s *SQLiteBackend) DeleteWorkspace(workspaceID string) error { filePath := s.workspaceFile(workspaceID) for _, suffix := range []string{"", "-wal", "-shm"} { if err := os.Remove(filePath + suffix); err != nil && !os.IsNotExist(err) { return fmt.Errorf("Workspace-Datei konnte nicht gelöscht werden: %w", err) } } return nil } // CheckCapacity: Jede SQLite-Datei hat ihre eigenen Verbindungen, eine // gemeinsame Obergrenze gibt es nicht. func (s *SQLiteBackend) CheckCapacity(context.Context, int) error { return nil } // Capacity: SQLite kennt keine gemeinsame Grenze. func (s *SQLiteBackend) Capacity(context.Context) (Capacity, error) { return Capacity{}, nil } // Location nennt die Datei relativ zum Datenverzeichnis, z.B. // "workspaces/mue9bh3x-1c0e1120.db" — der volle Pfad stünde schon im // Startprotokoll und wäre in der Tabelle nur lang. func (s *SQLiteBackend) Location(workspaceID string) string { return filepath.Join(filepath.Base(s.workspaceDir), workspaceID+".db") } func (s *SQLiteBackend) Description() string { return fmt.Sprintf("SQLite — Workspaces: %s, Kontrolle: %s", s.workspaceDir, s.controlPath) } // Hints: Beim SQLite-Speicher gibt es beim Start nichts zu prüfen. Eine // zu große WAL-Datei zeigt die Systemseite je Workspace (Store.DiskUsage). func (s *SQLiteBackend) Hints() []string { return nil } // Close: Der SQLite-Speicher hält selbst nichts offen. func (s *SQLiteBackend) Close() error { return nil } func (s *SQLiteBackend) workspaceFile(workspaceID string) string { return filepath.Join(s.workspaceDir, workspaceID+".db") }