sqkv/bucket.go

105 lines
2.6 KiB
Go

package sqkv
import (
"database/sql"
"errors"
"fmt"
"strings"
)
const DefaultBucket = "SQKV"
var (
ErrBucketNameEmpty = errors.New("bucket name cannot be empty")
ErrBucketDoesNotExist = errors.New("bucket does not exist")
ErrBucketExists = errors.New("bucket already exists")
ErrKeyDoesNotExist = errors.New("key does not exist in bucket")
)
// Bucket is a KV bucket
type Bucket struct {
db *sql.DB
table string
}
// Bucket gets a bucket by name, or returns nil if it doesn't exist
func (d *DB) Bucket(name string) (*Bucket, error) {
name = escapeIdent(name)
if !d.bucketExists(name) {
return nil, ErrBucketDoesNotExist
}
return &Bucket{
db: d.db,
table: name,
}, nil
}
// CreateBucket creates a new bucket and returns it. Returns an error if the bucket already exists or name is empty.
func (d *DB) CreateBucket(name string) (*Bucket, error) {
name = escapeIdent(name)
if strings.TrimSpace(name) == "" {
return nil, ErrBucketNameEmpty
}
if ok := d.bucketExists(name); ok {
return nil, ErrBucketExists
}
if _, err := d.db.Exec(fmt.Sprintf(`CREATE TABLE %s (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
KEY TEXT UNIQUE,
VALUE TEXT
)`, name)); err != nil {
return nil, err
}
return &Bucket{
db: d.db,
table: name,
}, nil
}
// CreateBucketIfNotExist creates a new bucket (if it does not exist) and returns it. Returns an error if name is empty.
func (d *DB) CreateBucketIfNotExist(name string) (*Bucket, error) {
name = escapeIdent(name)
b, err := d.CreateBucket(name)
if err != nil {
if errors.Is(err, ErrBucketExists) {
return d.Bucket(name)
}
return nil, err
}
return b, nil
}
func (d *DB) bucketExists(name string) bool {
res, err := d.db.Query("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?;", name)
if err != nil {
return false
}
defer res.Close()
return res.Next()
}
// Get returns a value from the bucket
func (b *Bucket) Get(key string) (string, error) {
row := b.db.QueryRow(fmt.Sprintf("SELECT value FROM %s WHERE key = ?", b.table), key)
var value string
if err := row.Scan(&value); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return "", ErrKeyDoesNotExist
}
return "", err
}
return value, row.Err()
}
// Put sets a value in the bucket
func (b *Bucket) Put(key, value string) error {
_, err := b.db.Exec(fmt.Sprintf("INSERT INTO %s ( key, value ) VALUES( ?, ? ) ON CONFLICT(key) DO UPDATE SET value=excluded.value", b.table), key, value)
return err
}
// Delete removes a value from the bucket
func (b *Bucket) Delete(key string) error {
_, err := b.db.Exec(fmt.Sprintf("DELETE FROM %s WHERE key = ?", b.table), key)
return err
}