stead from pg
This commit is contained in:
24
ctx.go
Normal file
24
ctx.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package with
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Context(foo func(context.Context) error) error {
|
||||
ctx, can := signal.NotifyContext(context.Background(), syscall.SIGINT)
|
||||
defer can()
|
||||
|
||||
if err := foo(ctx); err != nil && ctx.Err() == nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Timeout(ctx context.Context, d time.Duration, foo func(context.Context) error) error {
|
||||
ctx, can := context.WithTimeout(ctx, d)
|
||||
defer can()
|
||||
return foo(ctx)
|
||||
}
|
||||
34
every.go
Normal file
34
every.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package with
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GoEvery(ctx context.Context, d time.Duration, foo func()) {
|
||||
every(ctx, d, foo, true)
|
||||
}
|
||||
|
||||
func Every(ctx context.Context, d time.Duration, foo func()) {
|
||||
every(ctx, d, foo, false)
|
||||
}
|
||||
|
||||
func every(ctx context.Context, d time.Duration, foo func(), async bool) {
|
||||
ticker := time.NewTicker(d)
|
||||
defer ticker.Stop()
|
||||
for ctx.Err() == nil {
|
||||
everyTry(ctx, foo, async)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-ticker.C:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func everyTry(ctx context.Context, foo func(), async bool) {
|
||||
if async {
|
||||
go foo()
|
||||
} else {
|
||||
foo()
|
||||
}
|
||||
}
|
||||
18
go.mod
18
go.mod
@@ -1,3 +1,21 @@
|
||||
module gitea.bel.blue/bel/with
|
||||
|
||||
go 1.24.2
|
||||
|
||||
require (
|
||||
github.com/lib/pq v1.11.2
|
||||
modernc.org/sqlite v1.46.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
modernc.org/libc v1.67.6 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
|
||||
55
go.sum
Normal file
55
go.sum
Normal file
@@ -0,0 +1,55 @@
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs=
|
||||
github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
|
||||
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc=
|
||||
modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM=
|
||||
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
|
||||
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE=
|
||||
modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.67.6 h1:eVOQvpModVLKOdT+LvBPjdQqfrZq+pC39BygcT+E7OI=
|
||||
modernc.org/libc v1.67.6/go.mod h1:JAhxUVlolfYDErnwiqaLvUqc8nfb2r6S6slAgZOnaiE=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU=
|
||||
modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
46
kv.go
Normal file
46
kv.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package with
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type SQLKV struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func KV(ctx context.Context, db *sql.DB, foo func(SQLKV) error) error {
|
||||
if _, err := db.ExecContext(ctx, `
|
||||
CREATE TABLE IF NOT EXISTS with_kv(
|
||||
k TEXT PRIMARY KEY
|
||||
, v TEXT
|
||||
)
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return foo(SQLKV{db: db})
|
||||
}
|
||||
|
||||
func (kv SQLKV) Get(ctx context.Context, k string) ([]byte, error) {
|
||||
row := kv.db.QueryRowContext(ctx, `
|
||||
SELECT v FROM with_kv WHERE k=$1
|
||||
`, k)
|
||||
|
||||
var v []byte
|
||||
if err := row.Scan(&v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return v, row.Err()
|
||||
}
|
||||
|
||||
func (kv SQLKV) Set(ctx context.Context, k string, v []byte) error {
|
||||
_, err := kv.db.ExecContext(ctx, `
|
||||
INSERT INTO with_kv
|
||||
(k, v) VALUES ($1, $2)
|
||||
ON CONFLICT DO UPDATE
|
||||
SET v=$2 WHERE k=$1
|
||||
`, k, v)
|
||||
return err
|
||||
}
|
||||
44
kv_test.go
Normal file
44
kv_test.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package with_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
"gitea.bel.blue/bel/with"
|
||||
)
|
||||
|
||||
func TestKV(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
if err := with.Sqlite(ctx, ":memory:", func(db *sql.DB) error {
|
||||
return with.KV(ctx, db, func(kv with.SQLKV) error {
|
||||
if _, err := kv.Get(ctx, "k"); err == nil {
|
||||
t.Error("404 no err")
|
||||
}
|
||||
|
||||
if err := kv.Set(ctx, "k", []byte("v")); err != nil {
|
||||
t.Error("err on insert", err)
|
||||
}
|
||||
|
||||
if v, err := kv.Get(ctx, "k"); err != nil {
|
||||
t.Error("err on get", err)
|
||||
} else if string(v) != "v" {
|
||||
t.Errorf("expected 'v' but got %q", v)
|
||||
}
|
||||
|
||||
if err := kv.Set(ctx, "k", []byte("v2")); err != nil {
|
||||
t.Error("err on update", err)
|
||||
}
|
||||
|
||||
if v, err := kv.Get(ctx, "k"); err != nil {
|
||||
t.Error("err on get updated", err)
|
||||
} else if string(v) != "v2" {
|
||||
t.Errorf("expected 'v2' but got %q", v)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
12
pg.go
Normal file
12
pg.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package with
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
func PSQL(ctx context.Context, conn string, foo func(db *sql.DB) error) error {
|
||||
return _sql(ctx, "postgres", conn, foo)
|
||||
}
|
||||
71
sql.go
Normal file
71
sql.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package with
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func SQL(ctx context.Context, conn string, foo func(*sql.DB) error) error {
|
||||
u, err := url.Parse(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch u.Scheme {
|
||||
case "sqlite":
|
||||
return Sqlite(ctx, conn, foo)
|
||||
case "postgres", "postgresql":
|
||||
return PSQL(ctx, conn, foo)
|
||||
}
|
||||
return fmt.Errorf("unknown sql scheme %q", u.Scheme)
|
||||
}
|
||||
|
||||
func _sql(ctx context.Context, engine, conn string, foo func(db *sql.DB) error) error {
|
||||
log.Printf("opening %s %s...", engine, conn)
|
||||
db, err := sql.Open(engine, conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
log.Println("closed:", db.Close())
|
||||
}()
|
||||
|
||||
func() {
|
||||
pinged := make(chan bool)
|
||||
defer close(pinged)
|
||||
for {
|
||||
log.Println("pinging...")
|
||||
go func() {
|
||||
err := db.PingContext(ctx)
|
||||
if err != nil {
|
||||
log.Println("!", err)
|
||||
}
|
||||
select {
|
||||
case pinged <- err == nil:
|
||||
case <-ctx.Done():
|
||||
case <-time.After(time.Second * 5):
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case ok := <-pinged:
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-time.After(time.Second):
|
||||
}
|
||||
}
|
||||
}()
|
||||
log.Println("connected")
|
||||
|
||||
return foo(db)
|
||||
}
|
||||
14
sqlite.go
Normal file
14
sqlite.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package with
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strings"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
func Sqlite(ctx context.Context, conn string, foo func(db *sql.DB) error) error {
|
||||
conn = strings.TrimPrefix(conn, "sqlite://")
|
||||
return _sql(ctx, "sqlite", conn, foo)
|
||||
}
|
||||
Reference in New Issue
Block a user