all repos — postern

@ 9799a3215ac0185fb9b4e7d6ec0645c2cb2ce55b

Modern mail management

clone
git clone https:///postern.git

internal/db/kv.go (view raw)

 1package db
 2
 3import (
 4	"context"
 5	"database/sql"
 6	"errors"
 7)
 8
 9func (db *DB) KVSet(ctx context.Context, key string, value any) error {
10	// Upsert into SQLite
11	query := `
12        INSERT INTO kv_store (key, value, updated_at) 
13        VALUES (?, ?, CURRENT_TIMESTAMP)
14        ON CONFLICT(key) DO UPDATE SET 
15            value = excluded.value, 
16            updated_at = CURRENT_TIMESTAMP;`
17
18	_, err := db.write.ExecContext(ctx, query, key, value)
19	return err
20}
21
22func (db *DB) KVGet(ctx context.Context, key string) (any, error) {
23	var rawVal any
24	err := db.read.QueryRowContext(ctx, "SELECT value FROM kv_store WHERE key = ?", key).Scan(&rawVal)
25	return rawVal, err
26}
27
28func (db *DB) KVInc(ctx context.Context, key string, delta int64) (int64, error) {
29	tx, err := db.GetWriteTx(ctx)
30	if err != nil {
31		return 0, err
32	}
33
34	var val any
35	err = tx.QueryRowContext(ctx, "SELECT value FROM kv_store WHERE key = ?", key).Scan(&val)
36	if err != nil && !errors.Is(err, sql.ErrNoRows) {
37		_ = tx.Rollback()
38		return 0, err
39	}
40
41	if errors.Is(err, sql.ErrNoRows) {
42		_, err = tx.ExecContext(ctx, "INSERT INTO kv_store (key, value, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP)", key, delta)
43		if err != nil {
44			_ = tx.Rollback()
45			return 0, err
46		}
47		return delta, tx.Commit()
48	}
49
50	if _, ok := val.(int64); !ok {
51		_ = tx.Rollback()
52		return 0, errors.New("KVInc: value is not an integer")
53	}
54
55	query := `
56INSERT INTO kv_store (key, value, updated_at)
57VALUES (?, ?, CURRENT_TIMESTAMP)
58ON CONFLICT(key) DO UPDATE SET
59	value = CAST(kv_store.value AS INTEGER) + excluded.value,
60	updated_at = CURRENT_TIMESTAMP
61RETURNING value;`
62
63	var newCount int64
64	err = tx.QueryRowContext(ctx, query, key, delta).Scan(&newCount)
65	if err != nil {
66		_ = tx.Rollback()
67		return 0, err
68	}
69
70	return newCount, tx.Commit()
71}
72
73func (db *DB) KVPop(ctx context.Context, key string) (any, error) {
74	var rawVal any
75	query := `DELETE FROM kv_store WHERE key = ? RETURNING kv_store.value;`
76	err := db.write.QueryRowContext(ctx, query, key).Scan(&rawVal)
77	return rawVal, err
78}