|
@@ -0,0 +1,208 @@
|
|
|
+//go:build mipsle
|
|
|
+
|
|
|
+package database
|
|
|
+
|
|
|
+import (
|
|
|
+ "encoding/json"
|
|
|
+ "errors"
|
|
|
+ "io/ioutil"
|
|
|
+ "log"
|
|
|
+ "os"
|
|
|
+ "path/filepath"
|
|
|
+ "strings"
|
|
|
+ "sync"
|
|
|
+)
|
|
|
+
|
|
|
+func newDatabase(dbfile string, readOnlyMode bool) (*Database, error) {
|
|
|
+ dbRootPath := filepath.ToSlash(filepath.Clean(dbfile))
|
|
|
+ dbRootPath = "fsdb/" + dbRootPath
|
|
|
+ err := os.MkdirAll(dbRootPath, 0755)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ tableMap := sync.Map{}
|
|
|
+ //build the table list from file system
|
|
|
+ files, err := filepath.Glob(filepath.Join(dbRootPath, "/*"))
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, file := range files {
|
|
|
+ if isDirectory(file) {
|
|
|
+ tableMap.Store(filepath.Base(file), "")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ log.Println("Filesystem Emulated Key-value Database Service Started: " + dbRootPath)
|
|
|
+ return &Database{
|
|
|
+ Db: dbRootPath,
|
|
|
+ Tables: tableMap,
|
|
|
+ ReadOnly: readOnlyMode,
|
|
|
+ }, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (d *Database) dump(filename string) ([]string, error) {
|
|
|
+ //Get all file objects from root
|
|
|
+ rootfiles, err := filepath.Glob(filepath.Join(d.Db.(string), "/*"))
|
|
|
+ if err != nil {
|
|
|
+ return []string{}, err
|
|
|
+ }
|
|
|
+
|
|
|
+ //Filter out the folders
|
|
|
+ rootFolders := []string{}
|
|
|
+ for _, file := range rootfiles {
|
|
|
+ if !isDirectory(file) {
|
|
|
+ rootFolders = append(rootFolders, filepath.Base(file))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return rootFolders, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (d *Database) newTable(tableName string) error {
|
|
|
+ if d.ReadOnly {
|
|
|
+ return errors.New("Operation rejected in ReadOnly mode")
|
|
|
+ }
|
|
|
+ tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
|
|
|
+ if !fileExists(tablePath) {
|
|
|
+ return os.MkdirAll(tablePath, 0755)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (d *Database) tableExists(tableName string) bool {
|
|
|
+ tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
|
|
|
+ if _, err := os.Stat(tablePath); errors.Is(err, os.ErrNotExist) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ if !isDirectory(tablePath) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+func (d *Database) dropTable(tableName string) error {
|
|
|
+ if d.ReadOnly {
|
|
|
+ return errors.New("Operation rejected in ReadOnly mode")
|
|
|
+ }
|
|
|
+ tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
|
|
|
+ if d.tableExists(tableName) {
|
|
|
+ return os.RemoveAll(tablePath)
|
|
|
+ } else {
|
|
|
+ return errors.New("table not exists")
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+func (d *Database) write(tableName string, key string, value interface{}) error {
|
|
|
+ if d.ReadOnly {
|
|
|
+ return errors.New("Operation rejected in ReadOnly mode")
|
|
|
+ }
|
|
|
+ tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
|
|
|
+ js, err := json.Marshal(value)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")
|
|
|
+
|
|
|
+ return ioutil.WriteFile(filepath.Join(tablePath, key+".entry"), js, 0755)
|
|
|
+}
|
|
|
+
|
|
|
+func (d *Database) read(tableName string, key string, assignee interface{}) error {
|
|
|
+ if !d.keyExists(tableName, key) {
|
|
|
+ return errors.New("key not exists")
|
|
|
+ }
|
|
|
+
|
|
|
+ key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")
|
|
|
+
|
|
|
+ tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
|
|
|
+ entryPath := filepath.Join(tablePath, key+".entry")
|
|
|
+ content, err := ioutil.ReadFile(entryPath)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ err = json.Unmarshal(content, &assignee)
|
|
|
+ return err
|
|
|
+}
|
|
|
+
|
|
|
+func (d *Database) keyExists(tableName string, key string) bool {
|
|
|
+ key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")
|
|
|
+ tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
|
|
|
+ entryPath := filepath.Join(tablePath, key+".entry")
|
|
|
+ return fileExists(entryPath)
|
|
|
+}
|
|
|
+
|
|
|
+func (d *Database) delete(tableName string, key string) error {
|
|
|
+ if d.ReadOnly {
|
|
|
+ return errors.New("Operation rejected in ReadOnly mode")
|
|
|
+ }
|
|
|
+ if !d.keyExists(tableName, key) {
|
|
|
+ return errors.New("key not exists")
|
|
|
+ }
|
|
|
+ key = strings.ReplaceAll(key, "/", "-SLASH_SIGN-")
|
|
|
+ tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
|
|
|
+ entryPath := filepath.Join(tablePath, key+".entry")
|
|
|
+
|
|
|
+ return os.Remove(entryPath)
|
|
|
+}
|
|
|
+
|
|
|
+func (d *Database) listTable(tableName string) ([][][]byte, error) {
|
|
|
+ if !d.tableExists(tableName) {
|
|
|
+ return [][][]byte{}, errors.New("table not exists")
|
|
|
+ }
|
|
|
+ tablePath := filepath.Join(d.Db.(string), filepath.Base(tableName))
|
|
|
+ entries, err := filepath.Glob(filepath.Join(tablePath, "/*.entry"))
|
|
|
+ if err != nil {
|
|
|
+ return [][][]byte{}, err
|
|
|
+ }
|
|
|
+
|
|
|
+ var results [][][]byte = [][][]byte{}
|
|
|
+ for _, entry := range entries {
|
|
|
+ if !isDirectory(entry) {
|
|
|
+ //Read it
|
|
|
+ key := filepath.Base(entry)
|
|
|
+ key = strings.TrimSuffix(key, filepath.Ext(key))
|
|
|
+ key = strings.ReplaceAll(key, "-SLASH_SIGN-", "/")
|
|
|
+
|
|
|
+ bkey := []byte(key)
|
|
|
+ bval := []byte("")
|
|
|
+ c, err := ioutil.ReadFile(entry)
|
|
|
+ if err != nil {
|
|
|
+ break
|
|
|
+ }
|
|
|
+
|
|
|
+ bval = c
|
|
|
+ results = append(results, [][]byte{bkey, bval})
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return results, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (d *Database) close() {
|
|
|
+ //Nothing to close as it is file system
|
|
|
+}
|
|
|
+
|
|
|
+func isDirectory(path string) bool {
|
|
|
+ fileInfo, err := os.Stat(path)
|
|
|
+ if err != nil {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ return fileInfo.IsDir()
|
|
|
+}
|
|
|
+
|
|
|
+func fileExists(name string) bool {
|
|
|
+ _, err := os.Stat(name)
|
|
|
+ if err == nil {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ if errors.Is(err, os.ErrNotExist) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|