// Файл: internal/commands/crud.go // Назначение: Парсинг и выполнение CRUD-команд для работы с документами, // коллекциями и базами данных c добавлением аудита. Поддерживает MongoDB-подобный синтаксис. package commands import ( "fmt" "strings" "futriis/internal/storage" "futriis/internal/cluster" "futriis/pkg/utils" ) // Execute выполняет команду CRUD func Execute(store *storage.Storage, coord *cluster.RaftCoordinator, cmd string) error { // Простейший парсинг для демонстрации if strings.HasPrefix(cmd, "use ") { dbName := strings.TrimPrefix(cmd, "use ") if err := store.CreateDatabase(dbName); err != nil && err.Error() != "database already exists" { return err } storage.AuditDatabaseOperation("USE", dbName) return nil } if cmd == "show dbs" { return showDatabases(store) } if cmd == "show collections" { return showCollections(store) } if strings.HasPrefix(cmd, "db.") { return executeDatabaseCommand(store, coord, cmd) } return fmt.Errorf("%s", utils.ColorizeText("unknown command: "+cmd, "\033[31m")) } // ExecuteTransaction выполняет команды транзакций MongoDB-подобного синтаксиса func ExecuteTransaction(store *storage.Storage, coord *cluster.RaftCoordinator, cmd string) error { if strings.Contains(cmd, "startSession()") { if err := storage.InitTransactionManager("futriis.wal"); err != nil { return err } utils.Println("Session started") storage.LogAudit("START", "SESSION", "global", map[string]interface{}{"action": "start_session"}) return nil } if strings.Contains(cmd, "startTransaction()") { _ = storage.BeginTransaction() utils.Println("Transaction started") storage.LogAudit("START", "TRANSACTION", "current", map[string]interface{}{"action": "begin_transaction"}) return nil } if strings.Contains(cmd, "commitTransaction()") { if err := storage.CommitCurrentTransaction(); err != nil { return err } utils.Println("Transaction committed successfully") storage.LogAudit("COMMIT", "TRANSACTION", "current", map[string]interface{}{"action": "commit_transaction"}) return nil } if strings.Contains(cmd, "abortTransaction()") { if err := storage.AbortCurrentTransaction(); err != nil { return err } utils.Println("Transaction aborted") storage.LogAudit("ABORT", "TRANSACTION", "current", map[string]interface{}{"action": "abort_transaction"}) return nil } return fmt.Errorf("%s", utils.ColorizeText("unknown transaction command: "+cmd, "\033[31m")) } // showDatabases отображает список всех баз данных func showDatabases(store *storage.Storage) error { databases := store.ListDatabases() if len(databases) == 0 { utils.Println("No databases found") return nil } utils.Println("\nDatabases:") for _, db := range databases { utils.Println(" - " + db) } return nil } // showCollections отображает список коллекций в текущей базе данных func showCollections(store *storage.Storage) error { databases := store.ListDatabases() if len(databases) == 0 { utils.Println("No databases found") return nil } db, err := store.GetDatabase(databases[0]) if err != nil { return err } collections := db.ListCollections() if len(collections) == 0 { utils.Println("No collections found") return nil } utils.Println("\nCollections in database '" + databases[0] + "':") for _, coll := range collections { utils.Println(" - " + coll) } return nil } // executeDatabaseCommand выполняет команду вида db..() func executeDatabaseCommand(store *storage.Storage, coord *cluster.RaftCoordinator, cmd string) error { parts := strings.SplitN(cmd, ".", 3) if len(parts) < 3 { return fmt.Errorf("%s", utils.ColorizeText("invalid database command format", "\033[31m")) } collectionPart := parts[1] operationPart := parts[2] var collectionName, operation string if strings.Contains(collectionPart, ".") { collParts := strings.SplitN(collectionPart, ".", 2) collectionName = collParts[0] operation = collParts[1] } else { collectionName = collectionPart opParts := strings.SplitN(operationPart, "(", 2) if len(opParts) < 1 { return fmt.Errorf("%s", utils.ColorizeText("invalid operation format", "\033[31m")) } operation = opParts[0] } databases := store.ListDatabases() if len(databases) == 0 { if err := store.CreateDatabase("test"); err != nil { return err } storage.AuditDatabaseOperation("CREATE", "test") databases = store.ListDatabases() } db, err := store.GetDatabase(databases[0]) if err != nil { return err } coll, err := db.GetCollection(collectionName) if err != nil { if err := db.CreateCollection(collectionName); err != nil { return err } storage.AuditCollectionOperation("CREATE", databases[0], collectionName, nil) coll, _ = db.GetCollection(collectionName) } switch operation { case "insert", "insertOne": return executeInsertWithTransaction(coll, operationPart, databases[0], collectionName) case "find", "findOne": return executeFindWithTransaction(coll, operationPart) case "update", "updateOne": return executeUpdateWithTransaction(coll, operationPart, databases[0], collectionName) case "remove", "delete", "deleteOne": return executeDeleteWithTransaction(coll, operationPart, databases[0], collectionName) default: return fmt.Errorf("%s", utils.ColorizeText("unknown operation: "+operation, "\033[31m")) } } func executeInsertWithTransaction(coll *storage.Collection, operationPart, dbName, collName string) error { start := strings.Index(operationPart, "(") end := strings.LastIndex(operationPart, ")") if start == -1 || end == -1 { return fmt.Errorf("%s", utils.ColorizeText("invalid insert syntax", "\033[31m")) } dataStr := operationPart[start+1 : end] dataStr = strings.TrimSpace(dataStr) if dataStr == "" || dataStr == "{}" { return fmt.Errorf("%s", utils.ColorizeText("empty document", "\033[31m")) } doc := storage.NewDocument() dataStr = strings.Trim(dataStr, "{}") if dataStr != "" { fields := strings.Split(dataStr, ",") for _, field := range fields { field = strings.TrimSpace(field) if field == "" { continue } parts := strings.SplitN(field, ":", 2) if len(parts) == 2 { key := strings.TrimSpace(parts[0]) value := strings.TrimSpace(parts[1]) value = strings.Trim(value, "\"'") doc.SetField(key, value) } } } if storage.HasActiveTransaction() { if err := storage.AddToTransaction(coll, "insert", doc); err != nil { return err } utils.Println("Document staged for transaction") return nil } if err := coll.Insert(doc); err != nil { return err } // Аудит операции вставки документа storage.AuditDocumentOperation("INSERT", dbName, collName, doc.ID, doc.GetFields()) utils.Println("Inserted document with _id: " + doc.ID) return nil } func executeFindWithTransaction(coll *storage.Collection, operationPart string) error { start := strings.Index(operationPart, "{_id:") if start == -1 { docs := coll.GetAllDocuments() if len(docs) == 0 { utils.Println("No documents found") return nil } utils.Println("\nFound " + utils.ColorizeTextInt(len(docs)) + " documents:") for _, doc := range docs { utils.Println(" _id: " + doc.ID + ", fields: " + utils.ColorizeTextAny(doc.GetFields())) } return nil } end := strings.Index(operationPart[start:], "}") if end == -1 { return fmt.Errorf("%s", utils.ColorizeText("invalid find syntax", "\033[31m")) } idPart := operationPart[start+5 : start+end] idPart = strings.TrimSpace(idPart) idPart = strings.Trim(idPart, "\"'") if storage.HasActiveTransaction() { doc, err := storage.FindInTransaction(coll, idPart) if err != nil { return err } utils.Println("Found document (in transaction): _id: " + doc.ID + ", fields: " + utils.ColorizeTextAny(doc.GetFields())) return nil } doc, err := coll.Find(idPart) if err != nil { return err } utils.Println("Found document: _id: " + doc.ID + ", fields: " + utils.ColorizeTextAny(doc.GetFields())) return nil } func executeUpdateWithTransaction(coll *storage.Collection, operationPart, dbName, collName string) error { if storage.HasActiveTransaction() { utils.Println("Update operation staged for transaction") storage.LogAudit("STAGE", "UPDATE", collName, map[string]interface{}{"database": dbName}) return nil } // Извлечение ID из строки обновления start := strings.Index(operationPart, "{_id:") if start == -1 { return fmt.Errorf("%s", utils.ColorizeText("update requires _id filter", "\033[31m")) } end := strings.Index(operationPart[start:], "}") if end == -1 { return fmt.Errorf("%s", utils.ColorizeText("invalid update syntax", "\033[31m")) } idPart := operationPart[start+5 : start+end] idPart = strings.TrimSpace(idPart) idPart = strings.Trim(idPart, "\"'") storage.AuditDocumentOperation("UPDATE", dbName, collName, idPart, nil) utils.Println("Update operation - to be implemented") return nil } func executeDeleteWithTransaction(coll *storage.Collection, operationPart, dbName, collName string) error { if storage.HasActiveTransaction() { utils.Println("Delete operation staged for transaction") storage.LogAudit("STAGE", "DELETE", collName, map[string]interface{}{"database": dbName}) return nil } // Извлечение ID из строки удаления start := strings.Index(operationPart, "{_id:") if start == -1 { return fmt.Errorf("%s", utils.ColorizeText("delete requires _id filter", "\033[31m")) } end := strings.Index(operationPart[start:], "}") if end == -1 { return fmt.Errorf("%s", utils.ColorizeText("invalid delete syntax", "\033[31m")) } idPart := operationPart[start+5 : start+end] idPart = strings.TrimSpace(idPart) idPart = strings.Trim(idPart, "\"'") if err := coll.Delete(idPart); err != nil { return err } storage.AuditDocumentOperation("DELETE", dbName, collName, idPart, nil) utils.Println("Delete operation - to be implemented") return nil }