0
0
mirror of https://github.com/tursodatabase/libsql.git synced 2025-06-11 05:06:55 +00:00

Refactor Go API ()

* 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:
Piotr Jastrzębski
2024-02-29 13:14:42 +01:00
committed by GitHub
parent 4b2732c5e2
commit db894cb68a
2 changed files with 148 additions and 10 deletions

@ -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)