package database import ( "fmt" "github.com/asdine/storm" "github.com/asdine/storm/q" ) // ConfigureDB sets up bolt and Storm according to the path of the database // this is done here so that different databases can be configured in different scenarios func (db *DB) ConfigureDB(dbPath string) error { var err error if db.DB, err = storm.Open(dbPath); err != nil { return err } var file File if err := db.One("Name", "-- All Files --", &file); err != nil { if err.Error() != "not found" { db.Err(err).Msg("Error finding file by path") return err } db.Warn().Msg("No existing databse found. initialising new database") file.Name = "-- All Files --" //file.Ignore = true //this is currently not used however could result in this file being ignored when file watching (etc) starts if err := db.Save(&file); err != nil { db.Err(err).Msg("Error storing the diff") return err } } return nil } // CheckIfFileCurrentlyMonitored checks if the file is already being monitored. This is a read-only check // to see whether the file was correctly initialised // (BUG) The hash causes the same file to be in database multiple times! func (db *DB) CheckIfFileCurrentlyMonitored(path string) bool { var file File //TODO: check this actually works still (don't need hash passed to this anymore) if err := db.One("Path", path, &file); err != nil { if err.Error() != "not found" { db.Err(err).Msg("Error finding file by path") fmt.Printf("Not found: %s error: %s\n", path, err) return false } db.Warn().Msg("no file found") fmt.Printf("Not found: %s error: %s\n", path, err) return false } fmt.Printf("Found!: %s searched: %s\n", file.Path, path) return true } // RetrieveTrackedFiles all files that are in the database as "watched files" // This can be used to trigger the same files to be watched again func (db *DB) RetrieveTrackedFiles() ([]File, error) { var files []File if err := db.All(&files); err != nil { db.Err(err).Msg("Error retrieving all watched files") return []File{}, err } return files, nil } // RetrieveAllDiffs retrieves all files that are in the database as "watched files" // This can be used to trigger the same files to be watched again // func (db *DB) RetrieveAllDiffs() ([]DiffObject, error) { // var diffs []DiffObject // if err := db.All(&diffs); err != nil { // db.Err(err).Msg("Error retrieving all diffs ") // return []DiffObject{}, err // } else { // return diffs, nil // } // } // InitialiseFileInDatabase should be called before any file is copied/renamed/diff'd/patched, // and this should be checked before any operation occurs on a file. Any loss of data is completely as a result // of losing references func (db *DB) InitializeFileInDatabase(file File) (int, error) { if err := db.Save(&file); err != nil { db.Err(err).Msg("Error initialising file in database") return file.ID, err } return file.ID, nil } // FindFileByPath is a search function looking for file details func (db *DB) FindFileByPath(filePath string) (File, error) { var file File if err := db.One("Path", filePath, &file); err != nil { db.Err(err).Msg("Error finding file by path") return File{}, err } return file, nil } // FindFileByID is a search function looking for file details func (db *DB) FindFileByID(ID int) (File, error) { var file File if err := db.One("ID", ID, &file); err != nil { db.Err(err).Msg("Error finding file by path") return File{}, err } return file, nil } // UpdateFileData updates the current base file that diffs will compare to func (db *DB) UpdateFileData(filePath, basePath string, hash []byte) error { if file, err := db.FindFileByPath(filePath); err != nil { db.Err(err).Msg("Error updating the file base") return err } else { err := db.Update(&File{ID: file.ID, CurrentBase: basePath, Hash: hash}) return err } } // RetrieveDiffsForFileByHash returns the diffs for a file. Diffs can be applied to a specific file (by its hash), // so when looking for the diffs, the hash is a good place to start in terms of finding the diffs func (db *DB) RetrieveDiffsForFileByHash(fileHash [16]byte, direction bool) ([]DiffObject, error) { var diffs []DiffObject var field string if direction { field = "ObjectHash" } else { field = "SubjectHash" } if err := db.Find(field, fileHash, &diffs); err != nil { return []DiffObject{}, err } return diffs, nil } // RetrieveDiffsForFileByPath returns the diffs for a file base on the file path. Diffs are very specific to a file, // so this may not be as robust as searching by diff, however we may not have the diff available func (db *DB) RetrieveDiffsForFileByPath(filePath string) ([]DiffObject, error) { var objDiffs []DiffObject var subDiffs []DiffObject if err := db.Find("Object", filePath, &objDiffs); err != nil && err.Error() != "not found" { db.Err(err).Msg("Error finding diff by object") return []DiffObject{}, err } if err := db.Find("Subject", filePath, &subDiffs); err != nil && err.Error() != "not found" { db.Err(err).Msg("Error finding diff by subject") return []DiffObject{}, err } return append(objDiffs, subDiffs...), nil } // StoreDiff just places the information about a diff in the database. Currently there is no protection // to stop the entire diff entering the database (if fs is false), which may be very slow/bulky... // TODO: decide what to do with diffs in memory // func (db *DB) StoreDiff(diff DiffObject) error { // if err := db.Save(&diff); err != nil { // db.Err(err).Msg("Error storing the diff") // return err // } // return nil // } // FindDiffByPath is a search function looking for a diff func (db *DB) FindDiffByPath(patchPath string) (DiffObject, error) { var diff DiffObject if err := db.One("DiffPath", patchPath, &diff); err != nil { db.Err(err).Msg("Error finding diff by path") return DiffObject{}, err } return diff, nil } //RetrieveDiffsByID returns a diff based on the id it has in the database func (db *DB) RetrieveDiffsByID(ID int) (DiffObject, error) { var diff DiffObject if err := db.One("ID", ID, &diff); err != nil { db.Err(err).Msg("Error finding diff by ID") return DiffObject{}, err } return diff, nil } // UpdateDescription is a simple function to set the label on a patch func (db *DB) UpdateDescription(patchID int, description string) error { fmt.Println("attempting to path with id ", patchID, " description ", description) if err := db.Update(&DiffObject{ID: patchID}); err != nil { db.Err(err).Msg("Error changing diff label") return err } return nil } // FetchCommitByNumber fetches the commit number of the passed branch func (db *DB) FetchCommitByNumber(branch string, commitNumber string) (commitResult Commit, err error) { var commit Commit db.Info().Msgf("fetching commit by number: %s in branch: %s", commitNumber, branch) query := db.Select(q.And(q.Eq("Branch", branch), q.Eq("Number", commitNumber))) err = query.Find(&commit) if err != nil { db.Err(err).Msgf("Failed to find commit by number: %s on branch: %s", commitNumber, branch) return commit, err } return commit, nil } // FetchCommitByHash fetches a single commit on any branch by hash //TODO: Hash collision? func (db *DB) FetchCommitByHash(hash string) (commitResult Commit, err error) { var commit Commit db.Info().Msgf("Searching for commit by hash: %s", hash) if err := db.One("CommitHash", hash, &commit); err != nil { db.Err(err).Msgf("Failed to find commit by hash: %s", hash) return commit, err } return commit, nil } // FetchLastCommitOnBranch gets the latest commit to the provided branch func (db *DB) FetchLastCommitOnBranch(branch string) (commitResult Commit, err error) { var commit Commit db.Info().Msgf("Fetching last commit on branch: %s", branch) query := db.Select(q.Eq("Branch", branch)) //Selecting everything that applies to that branch err = query.OrderBy("Number").Reverse().First(&commit) // Getting the last entry by number if err != nil { db.Err(err).Msgf("Failed to find last commit on branch: %s", branch) } return commit, nil }