105 lines
2.6 KiB
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
|
|
}
|