Initial commit
This commit is contained in:
160
sqlitestore.go
Normal file
160
sqlitestore.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/gopatchy/metadata"
|
||||
// Register sqlite3 db handler.
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
type SQLiteStore struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewSQLiteStore(conn string) (*SQLiteStore, error) {
|
||||
db, err := sql.Open("sqlite3", conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: Keep a set of prepared statements with PrepareContext()
|
||||
// TODO: Consider tuning per https://phiresky.github.io/blog/2020/sqlite-performance-tuning/
|
||||
|
||||
return &SQLiteStore{
|
||||
db: db,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (sls *SQLiteStore) Close() {
|
||||
sls.db.Close()
|
||||
}
|
||||
|
||||
func (sls *SQLiteStore) Write(ctx context.Context, t string, obj any) error {
|
||||
id := metadata.GetMetadata(obj).ID
|
||||
|
||||
js, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = sls.exec(ctx, "INSERT INTO `%s` (id, obj) VALUES (?,?) ON CONFLICT(id) DO UPDATE SET obj=?;", t, id, js, js)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sls *SQLiteStore) Delete(ctx context.Context, t, id string) error {
|
||||
err := sls.exec(ctx, "DELETE FROM `%s` WHERE id=?", t, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sls *SQLiteStore) Read(ctx context.Context, t, id string, factory func() any) (any, error) {
|
||||
rows, err := sls.query(ctx, "SELECT obj FROM `%s` WHERE id=?;", t, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
if !rows.Next() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var js []byte
|
||||
|
||||
err = rows.Scan(&js)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
obj := factory()
|
||||
|
||||
err = json.Unmarshal(js, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
func (sls *SQLiteStore) List(ctx context.Context, t string, factory func() any) ([]any, error) {
|
||||
rows, err := sls.query(ctx, "SELECT obj FROM `%s`;", t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
ret := []any{}
|
||||
|
||||
for rows.Next() {
|
||||
var js []byte
|
||||
|
||||
err = rows.Scan(&js)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
obj := factory()
|
||||
|
||||
err = json.Unmarshal(js, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret = append(ret, obj)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (sls *SQLiteStore) exec(ctx context.Context, query, t string, args ...any) error {
|
||||
query = fmt.Sprintf(query, t)
|
||||
|
||||
_, err := sls.db.ExecContext(ctx, query, args...)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = sls.db.ExecContext(ctx, sls.tableSQL(t))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = sls.db.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sls *SQLiteStore) query(ctx context.Context, query, t string, args ...any) (*sql.Rows, error) {
|
||||
query = fmt.Sprintf(query, t)
|
||||
|
||||
rows, err := sls.db.QueryContext(ctx, query, args...)
|
||||
if err == nil {
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
_, err = sls.db.ExecContext(ctx, sls.tableSQL(t))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sls.db.QueryContext(ctx, query, args...)
|
||||
}
|
||||
|
||||
func (sls *SQLiteStore) tableSQL(t string) string {
|
||||
return fmt.Sprintf("CREATE TABLE IF NOT EXISTS `%s` (id TEXT NOT NULL PRIMARY KEY, obj TEXT NOT NULL);", t)
|
||||
}
|
||||
Reference in New Issue
Block a user