Compare commits
4 Commits
912157fbf0
...
1f8b4ab24f
Author | SHA1 | Date | |
---|---|---|---|
1f8b4ab24f | |||
d4e021386b | |||
eb63312542 | |||
0a4986d476 |
1
go.mod
1
go.mod
|
@ -8,6 +8,7 @@ require (
|
||||||
github.com/gorilla/websocket v1.5.1
|
github.com/gorilla/websocket v1.5.1
|
||||||
github.com/matryer/is v1.4.1
|
github.com/matryer/is v1.4.1
|
||||||
github.com/ravilushqa/otelgqlgen v0.15.0
|
github.com/ravilushqa/otelgqlgen v0.15.0
|
||||||
|
github.com/tursodatabase/go-libsql v0.0.0-20240322134723-08771dcdd2f1
|
||||||
github.com/vektah/gqlparser/v2 v2.5.11
|
github.com/vektah/gqlparser/v2 v2.5.11
|
||||||
go.opentelemetry.io/otel v1.23.1
|
go.opentelemetry.io/otel v1.23.1
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.23.1
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.23.1
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -99,6 +99,8 @@ github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
|
||||||
github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
|
github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
|
||||||
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
|
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
|
||||||
github.com/pelletier/go-toml v1.0.1-0.20170904195809-1d6b12b7cb29/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.0.1-0.20170904195809-1d6b12b7cb29/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
|
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
|
||||||
|
@ -221,6 +223,8 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||||
|
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
|
||||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||||
modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk=
|
modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk=
|
||||||
|
|
120
libsql_embed/open.go
Normal file
120
libsql_embed/open.go
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
package libsqlembed
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tursodatabase/go-libsql"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
sql.Register("libsql+embed", &db{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type db struct {
|
||||||
|
conns map[string]connector
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type connector struct {
|
||||||
|
*libsql.Connector
|
||||||
|
dsn string
|
||||||
|
dir string
|
||||||
|
driver *db
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *connector) Close() error {
|
||||||
|
c.driver.mu.Lock()
|
||||||
|
delete(c.driver.conns, c.dsn)
|
||||||
|
c.driver.mu.Unlock()
|
||||||
|
|
||||||
|
defer os.RemoveAll(c.dir)
|
||||||
|
|
||||||
|
if err := c.Connector.Sync(); err != nil {
|
||||||
|
return fmt.Errorf("syncing database: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Connector.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *db) OpenConnector(dsn string) (driver.Connector, error) {
|
||||||
|
if c, ok := func() (connector, bool) {
|
||||||
|
db.mu.RLock()
|
||||||
|
defer db.mu.RUnlock()
|
||||||
|
c, ok := db.conns[dsn]
|
||||||
|
return c, ok
|
||||||
|
}(); ok {
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
db.mu.Lock()
|
||||||
|
defer db.mu.Unlock()
|
||||||
|
|
||||||
|
u, err := url.Parse(dsn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var primary url.URL
|
||||||
|
primary.Scheme = strings.TrimSuffix(u.Scheme, "+embed")
|
||||||
|
primary.Host = u.Host
|
||||||
|
|
||||||
|
dbname, _, _ := strings.Cut(u.Host, ".")
|
||||||
|
|
||||||
|
authToken := u.Query().Get("authToken")
|
||||||
|
if authToken == "" {
|
||||||
|
return nil, fmt.Errorf("missing authToken")
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := []libsql.Option{
|
||||||
|
libsql.WithAuthToken(authToken),
|
||||||
|
}
|
||||||
|
|
||||||
|
if refresh, err := strconv.ParseInt(u.Query().Get("refresh"),10,64); err == nil {
|
||||||
|
opts = append(opts, libsql.WithSyncInterval(time.Duration(refresh)*time.Minute))
|
||||||
|
}
|
||||||
|
|
||||||
|
if readWrite, err := strconv.ParseBool(u.Query().Get("readYourWrites")); err == nil {
|
||||||
|
opts = append(opts, libsql.WithReadYourWrites(readWrite))
|
||||||
|
}
|
||||||
|
if key := u.Query().Get("key"); key != "" {
|
||||||
|
opts = append(opts, libsql.WithEncryption(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
dir, err := os.MkdirTemp("", "libsql-*")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("creating temporary directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dbPath := filepath.Join(dir, dbname)
|
||||||
|
|
||||||
|
c, err := libsql.NewEmbeddedReplicaConnector(
|
||||||
|
dbPath,
|
||||||
|
primary.String(),
|
||||||
|
opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("creating connector: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
connector := connector{c, dsn, dir, db}
|
||||||
|
db.conns[dsn] = connector
|
||||||
|
|
||||||
|
return connector, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *db) Open(dsn string) (driver.Conn, error) {
|
||||||
|
c, err := db.OpenConnector(dsn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c.Connect(context.Background())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user