mirror of
https://github.com/tursodatabase/libsql.git
synced 2025-06-11 05:06:55 +00:00
Refactor Go API (#1111)
* Go: refactor API Signed-off-by: Piotr Jastrzebski <piotr@chiselstrike.com> * Go: add read your writes test Signed-off-by: Piotr Jastrzebski <piotr@chiselstrike.com> --------- Signed-off-by: Piotr Jastrzebski <piotr@chiselstrike.com>
This commit is contained in:
committed by
GitHub
parent
4b2732c5e2
commit
db894cb68a
bindings/go
@ -19,6 +19,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
sqldriver "database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/antlr/antlr4/runtime/Go/antlr/v4"
|
||||
"github.com/libsql/sqlite-antlr4-parser/sqliteparser"
|
||||
@ -35,11 +36,96 @@ func init() {
|
||||
sql.Register("libsql", driver{})
|
||||
}
|
||||
|
||||
func NewEmbeddedReplicaConnector(dbPath, primaryUrl, authToken string, readYourWrites bool, encryptionKey string) (*Connector, error) {
|
||||
return openEmbeddedReplicaConnector(dbPath, primaryUrl, authToken, readYourWrites, encryptionKey, 0)
|
||||
type config struct {
|
||||
authToken *string
|
||||
readYourWrites *bool
|
||||
encryptionKey *string
|
||||
syncInterval *time.Duration
|
||||
}
|
||||
|
||||
func NewEmbeddedReplicaConnectorWithAutoSync(dbPath, primaryUrl, authToken string, readYourWrites bool, encryptionKey string, syncInterval time.Duration) (*Connector, error) {
|
||||
type Option interface {
|
||||
apply(*config) error
|
||||
}
|
||||
|
||||
type option func(*config) error
|
||||
|
||||
func (o option) apply(c *config) error {
|
||||
return o(c)
|
||||
}
|
||||
|
||||
func WithAuthToken(authToken string) Option {
|
||||
return option(func(o *config) error {
|
||||
if o.authToken != nil {
|
||||
return fmt.Errorf("authToken already set")
|
||||
}
|
||||
if authToken == "" {
|
||||
return fmt.Errorf("authToken must not be empty")
|
||||
}
|
||||
o.authToken = &authToken
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func WithReadYourWrites(readYourWrites bool) Option {
|
||||
return option(func(o *config) error {
|
||||
if o.readYourWrites != nil {
|
||||
return fmt.Errorf("read your writes already set")
|
||||
}
|
||||
o.readYourWrites = &readYourWrites
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func WithEncryption(key string) Option {
|
||||
return option(func(o *config) error {
|
||||
if o.encryptionKey != nil {
|
||||
return fmt.Errorf("encryption key already set")
|
||||
}
|
||||
if key == "" {
|
||||
return fmt.Errorf("encryption key must not be empty")
|
||||
}
|
||||
o.encryptionKey = &key
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func WithAutoSync(interval time.Duration) Option {
|
||||
return option(func(o *config) error {
|
||||
if o.syncInterval != nil {
|
||||
return fmt.Errorf("auto sync already set")
|
||||
}
|
||||
o.syncInterval = &interval
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func NewEmbeddedReplicaConnector(dbPath string, primaryUrl string, opts ...Option) (*Connector, error) {
|
||||
var config config
|
||||
errs := make([]error, 0, len(opts))
|
||||
for _, opt := range opts {
|
||||
if err := opt.apply(&config); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
authToken := ""
|
||||
if config.authToken != nil {
|
||||
authToken = *config.authToken
|
||||
}
|
||||
readYourWrites := true
|
||||
if config.readYourWrites != nil {
|
||||
readYourWrites = *config.readYourWrites
|
||||
}
|
||||
encryptionKey := ""
|
||||
if config.encryptionKey != nil {
|
||||
encryptionKey = *config.encryptionKey
|
||||
}
|
||||
syncInterval := time.Duration(0)
|
||||
if config.syncInterval != nil {
|
||||
syncInterval = *config.syncInterval
|
||||
}
|
||||
return openEmbeddedReplicaConnector(dbPath, primaryUrl, authToken, readYourWrites, encryptionKey, syncInterval)
|
||||
}
|
||||
|
||||
|
@ -65,8 +65,11 @@ func getEmbeddedDb(t T) *Database {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dbPath := dir + "/test.db"
|
||||
|
||||
connector, err := NewEmbeddedReplicaConnector(dbPath, primaryUrl, authToken, false, "")
|
||||
options := []Option{WithReadYourWrites(false)}
|
||||
if authToken != "" {
|
||||
options = append(options, WithAuthToken(authToken))
|
||||
}
|
||||
connector, err := NewEmbeddedReplicaConnector(dbPath, primaryUrl, options...)
|
||||
t.FatalOnError(err)
|
||||
db := sql.OpenDB(connector)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
@ -456,7 +459,11 @@ func testSync(t *testing.T, connect func(dbPath, primaryUrl, authToken string) *
|
||||
func TestAutoSync(t *testing.T) {
|
||||
syncInterval := 1 * time.Second
|
||||
testSync(t, func(dbPath, primaryUrl, authToken string) *Connector {
|
||||
connector, err := NewEmbeddedReplicaConnectorWithAutoSync(dbPath, primaryUrl, authToken, false, "", syncInterval)
|
||||
options := []Option{WithReadYourWrites(false), WithAutoSync(syncInterval)}
|
||||
if authToken != "" {
|
||||
options = append(options, WithAuthToken(authToken))
|
||||
}
|
||||
connector, err := NewEmbeddedReplicaConnector(dbPath, primaryUrl, options...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -468,7 +475,11 @@ func TestAutoSync(t *testing.T) {
|
||||
|
||||
func TestSync(t *testing.T) {
|
||||
testSync(t, func(dbPath, primaryUrl, authToken string) *Connector {
|
||||
connector, err := NewEmbeddedReplicaConnector(dbPath, primaryUrl, authToken, false, "")
|
||||
options := []Option{WithReadYourWrites(false)}
|
||||
if authToken != "" {
|
||||
options = append(options, WithAuthToken(authToken))
|
||||
}
|
||||
connector, err := NewEmbeddedReplicaConnector(dbPath, primaryUrl, options...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -500,7 +511,11 @@ func TestEncryption(tt *testing.T) {
|
||||
encryptionKey := "SuperSecretKey"
|
||||
table := "test_" + fmt.Sprint(rand.Int()) + "_" + time.Now().Format("20060102150405")
|
||||
|
||||
connector, err := NewEmbeddedReplicaConnector(dbPath, primaryUrl, authToken, false, encryptionKey)
|
||||
options := []Option{WithReadYourWrites(false)}
|
||||
if authToken != "" {
|
||||
options = append(options, WithAuthToken(authToken))
|
||||
}
|
||||
connector, err := NewEmbeddedReplicaConnector(dbPath, primaryUrl, append(options, WithEncryption(encryptionKey))...)
|
||||
t.FatalOnError(err)
|
||||
db := sql.OpenDB(connector)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
@ -522,7 +537,7 @@ func TestEncryption(tt *testing.T) {
|
||||
t.FatalOnError(err)
|
||||
err = connector.Close()
|
||||
t.FatalOnError(err)
|
||||
connector, err = NewEmbeddedReplicaConnector(dbPath, primaryUrl, authToken, false, encryptionKey)
|
||||
connector, err = NewEmbeddedReplicaConnector(dbPath, primaryUrl, append(options, WithEncryption(encryptionKey))...)
|
||||
t.FatalOnError(err)
|
||||
db = sql.OpenDB(connector)
|
||||
rows, err := db.QueryContext(ctx, "SELECT * FROM "+table)
|
||||
@ -566,7 +581,7 @@ func TestEncryption(tt *testing.T) {
|
||||
t.FatalOnError(err)
|
||||
err = connector.Close()
|
||||
t.FatalOnError(err)
|
||||
connector, err = NewEmbeddedReplicaConnector(dbPath, primaryUrl, authToken, false, "WrongKey")
|
||||
connector, err = NewEmbeddedReplicaConnector(dbPath, primaryUrl, append(options, WithEncryption("WrongKey"))...)
|
||||
if err == nil {
|
||||
t.Fatal("using wrong encryption key should have failed")
|
||||
}
|
||||
@ -599,6 +614,43 @@ func testExecAndQuery(db *Database) {
|
||||
table.assertRowExists(19)
|
||||
}
|
||||
|
||||
func TestReadYourWrites(tt *testing.T) {
|
||||
t := T{tt}
|
||||
primaryUrl := os.Getenv("LIBSQL_PRIMARY_URL")
|
||||
if primaryUrl == "" {
|
||||
t.Skip("LIBSQL_PRIMARY_URL is not set")
|
||||
return
|
||||
}
|
||||
authToken := os.Getenv("LIBSQL_AUTH_TOKEN")
|
||||
dir, err := os.MkdirTemp("", "libsql-*")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dbPath := dir + "/test.db"
|
||||
options := []Option{}
|
||||
if authToken != "" {
|
||||
options = append(options, WithAuthToken(authToken))
|
||||
}
|
||||
connector, err := NewEmbeddedReplicaConnector(dbPath, primaryUrl, options...)
|
||||
t.FatalOnError(err)
|
||||
database := sql.OpenDB(connector)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
||||
t.Cleanup(func() {
|
||||
database.Close()
|
||||
connector.Close()
|
||||
cancel()
|
||||
defer os.RemoveAll(dir)
|
||||
})
|
||||
db := &Database{database, connector, t, ctx}
|
||||
table := db.createTable()
|
||||
table.insertRows(0, 10)
|
||||
table.insertRowsWithArgs(10, 10)
|
||||
table.assertRowsCount(20)
|
||||
table.assertRowDoesNotExist(20)
|
||||
table.assertRowExists(0)
|
||||
table.assertRowExists(19)
|
||||
}
|
||||
|
||||
func TestPreparedStatements(t *testing.T) {
|
||||
db := getRemoteDb(T{t})
|
||||
testPreparedStatements(db)
|
||||
|
Reference in New Issue
Block a user