quickshare/src/db/rdb/sqlite/files.go
2022-09-25 12:14:55 +08:00

270 lines
5.2 KiB
Go

package sqlite
import (
"context"
"database/sql"
"encoding/json"
"errors"
"fmt"
"path"
"strings"
"github.com/ihexxa/quickshare/src/db"
)
const (
InitNs = "Init"
InitTimeKey = "initTime"
SchemaVerKey = "SchemaVersion"
SchemaV1 = "v1"
)
var (
maxHashingTime = 10
)
func (st *SQLiteStore) getFileInfo(ctx context.Context, itemPath string) (*db.FileInfo, error) {
var infoStr string
fInfo := &db.FileInfo{}
var isDir bool
var size int64
var shareId string
err := st.db.QueryRowContext(
ctx,
`select is_dir, size, share_id, info
from t_file_info
where path=?`,
itemPath,
).Scan(
&isDir,
&size,
&shareId,
&infoStr,
)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, db.ErrFileInfoNotFound
}
return nil, err
}
err = json.Unmarshal([]byte(infoStr), &fInfo)
if err != nil {
return nil, err
}
fInfo.IsDir = isDir
fInfo.Size = size
fInfo.ShareID = shareId
fInfo.Shared = shareId != ""
return fInfo, nil
}
func (st *SQLiteStore) GetFileInfo(ctx context.Context, itemPath string) (*db.FileInfo, error) {
st.RLock()
defer st.RUnlock()
return st.getFileInfo(ctx, itemPath)
}
func (st *SQLiteStore) ListFileInfos(ctx context.Context, itemPaths []string) (map[string]*db.FileInfo, error) {
st.RLock()
defer st.RUnlock()
// TODO: add pagination
placeholders := []string{}
values := []any{}
for i := 0; i < len(itemPaths); i++ {
placeholders = append(placeholders, "?")
values = append(values, itemPaths[i])
}
rows, err := st.db.QueryContext(
ctx,
fmt.Sprintf(
`select path, is_dir, size, share_id, info
from t_file_info
where path in (%s)
`,
strings.Join(placeholders, ","),
),
values...,
)
if err != nil {
return nil, err
}
defer rows.Close()
var fInfoStr, itemPath, shareId string
var isDir bool
var size int64
fInfos := map[string]*db.FileInfo{}
for rows.Next() {
fInfo := &db.FileInfo{}
err = rows.Scan(&itemPath, &isDir, &size, &shareId, &fInfoStr)
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(fInfoStr), fInfo)
if err != nil {
return nil, err
}
fInfo.IsDir = isDir
fInfo.Size = size
fInfo.ShareID = shareId
fInfo.Shared = shareId != ""
fInfos[itemPath] = fInfo
}
if rows.Err() != nil {
return nil, rows.Err()
}
return fInfos, nil
}
func (st *SQLiteStore) addFileInfo(ctx context.Context, userId uint64, itemPath string, info *db.FileInfo) error {
infoStr, err := json.Marshal(info)
if err != nil {
return err
}
dirPath, itemName := path.Split(itemPath)
_, err = st.db.ExecContext(
ctx,
`insert into t_file_info
(path, user, parent, name, is_dir, size, share_id, info) values (?, ?, ?, ?, ?, ?, ?, ?)`,
itemPath,
userId,
dirPath,
itemName,
info.IsDir,
info.Size,
info.ShareID,
infoStr,
)
return err
}
func (st *SQLiteStore) AddFileInfo(ctx context.Context, userId uint64, itemPath string, info *db.FileInfo) error {
st.Lock()
defer st.Unlock()
err := st.addFileInfo(ctx, userId, itemPath, info)
if err != nil {
return err
}
// increase used space
return st.setUsed(ctx, userId, true, info.Size)
}
func (st *SQLiteStore) delFileInfo(ctx context.Context, itemPath string) error {
_, err := st.db.ExecContext(
ctx,
`delete from t_file_info
where path=?
`,
itemPath,
)
return err
}
func (st *SQLiteStore) SetSha1(ctx context.Context, itemPath, sign string) error {
st.Lock()
defer st.Unlock()
info, err := st.getFileInfo(ctx, itemPath)
if err != nil {
return err
}
info.Sha1 = sign
infoStr, err := json.Marshal(info)
if err != nil {
return err
}
_, err = st.db.ExecContext(
ctx,
`update t_file_info
set info=?
where path=?`,
infoStr,
itemPath,
)
return err
}
func (st *SQLiteStore) DelFileInfo(ctx context.Context, userID uint64, itemPath string) error {
st.Lock()
defer st.Unlock()
// get all children and size
rows, err := st.db.QueryContext(
ctx,
`select path, size
from t_file_info
where path = ? or path like ?
`,
itemPath,
fmt.Sprintf("%s/%%", itemPath),
)
if err != nil {
return err
}
defer rows.Close()
var childrenPath string
var itemSize int64
placeholders := []string{}
values := []any{}
decrSize := int64(0)
for rows.Next() {
err = rows.Scan(&childrenPath, &itemSize)
if err != nil {
return err
}
placeholders = append(placeholders, "?")
values = append(values, childrenPath)
decrSize += itemSize
}
// decrease used space
err = st.setUsed(ctx, userID, false, decrSize)
if err != nil {
return err
}
// delete file info entries
_, err = st.db.ExecContext(
ctx,
fmt.Sprintf(
`delete from t_file_info
where path in (%s)`,
strings.Join(placeholders, ","),
),
values...,
)
return err
}
func (st *SQLiteStore) MoveFileInfos(ctx context.Context, userId uint64, oldPath, newPath string, isDir bool) error {
st.Lock()
defer st.Unlock()
info, err := st.getFileInfo(ctx, oldPath)
if err != nil {
if errors.Is(err, db.ErrFileInfoNotFound) {
// info for file does not exist so no need to move it
// e.g. folder info is not created before
// TODO: but sometimes it could be a bug
return nil
}
return err
}
err = st.delFileInfo(ctx, oldPath)
if err != nil {
return err
}
return st.addFileInfo(ctx, userId, newPath, info)
}