Skip to content

Commit 866a2b9

Browse files
committed
adapters/sqlite: Add adapter
1 parent 97e3cbb commit 866a2b9

File tree

12 files changed

+1446
-0
lines changed

12 files changed

+1446
-0
lines changed

adapters/sqlite/README.md

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# SQLite Adapter
2+
3+
A pure Go SQLite implementation for the workflow library's `RecordStore`, `TimeoutStore`, and `EventStreamer` interfaces.
4+
5+
## Features
6+
7+
- **Pure Go**: Uses `modernc.org/sqlite` (no CGO dependencies)
8+
- **Complete Implementation**: All three core interfaces supported
9+
- **Minimal Dependencies**: Only SQLite driver and workflow library
10+
- **Optimized Configuration**: WAL mode, proper pragmas, and retry logic
11+
- **Thread-Safe**: Safe for concurrent use within SQLite's limitations
12+
13+
## Interfaces Implemented
14+
15+
-**RecordStore**: Store and retrieve workflow records with transactional outbox pattern
16+
-**TimeoutStore**: Manage workflow timeouts with expiration tracking
17+
-**EventStreamer**: Event streaming with SQLite-based persistence and cursor tracking
18+
19+
## Installation
20+
21+
```go
22+
go get github.com/luno/workflow/adapters/sqlite
23+
```
24+
25+
## Quick Start
26+
27+
```go
28+
package main
29+
30+
import (
31+
"context"
32+
"log"
33+
34+
"github.com/luno/workflow/adapters/sqlite"
35+
)
36+
37+
func main() {
38+
// Open database with optimized settings
39+
db, err := sqlite.Open("workflow.db")
40+
if err != nil {
41+
log.Fatal(err)
42+
}
43+
defer db.Close()
44+
45+
// Initialize schema
46+
if err := sqlite.InitSchema(db); err != nil {
47+
log.Fatal(err)
48+
}
49+
50+
// Create adapters
51+
recordStore := sqlite.NewRecordStore(db)
52+
timeoutStore := sqlite.NewTimeoutStore(db)
53+
eventStreamer := sqlite.NewEventStreamer(db)
54+
55+
// Use with workflow builder
56+
// builder := workflow.NewBuilder[MyType](...)
57+
// .AddRecordStore(recordStore)
58+
// .AddTimeoutStore(timeoutStore)
59+
// .AddEventStreamer(eventStreamer)
60+
}
61+
```
62+
63+
## Configuration
64+
65+
The `sqlite.Open()` function automatically configures SQLite for optimal workflow usage:
66+
67+
- **WAL Mode**: Write-Ahead Logging for better concurrency
68+
- **Busy Timeout**: 5-second wait for database locks
69+
- **Cache Size**: Increased for better performance
70+
- **Single Connection**: Optimized for SQLite's architecture
71+
72+
## Schema
73+
74+
The adapter creates these tables automatically:
75+
76+
- `workflow_records`: Store workflow execution records
77+
- `workflow_outbox`: Transactional outbox for events
78+
- `workflow_timeouts`: Timeout tracking with expiration
79+
- `workflow_events`: Event streaming log
80+
- `workflow_cursors`: Consumer position tracking
81+
82+
## Usage Notes
83+
84+
### Concurrency Limitations
85+
86+
SQLite is designed for single-writer scenarios. While this adapter includes retry logic and optimizations:
87+
88+
-**Good for**: Single-node applications, moderate load, development/testing
89+
- ⚠️ **Limited**: High-concurrency scenarios with many concurrent writers
90+
-**Not for**: Multi-node deployments, high-throughput production systems
91+
92+
For high-concurrency scenarios, consider:
93+
- `sqlstore` + `sqltimeout` (MySQL/PostgreSQL)
94+
- `reflexstreamer` (dedicated event streaming)
95+
- `kafkastreamer` (Kafka-based streaming)
96+
97+
### Event Streaming
98+
99+
The EventStreamer implementation:
100+
- Stores events in SQLite tables (not in-memory)
101+
- Supports `StreamFromLatest()` option
102+
- Uses polling-based consumption (10ms intervals by default)
103+
- Includes automatic retry logic for database locks
104+
105+
### Error Handling
106+
107+
The adapter includes retry logic for common SQLite contention issues:
108+
- Automatic retry on `SQLITE_BUSY` errors
109+
- Exponential backoff for lock conflicts
110+
- Graceful handling of concurrent access patterns
111+
112+
## Testing
113+
114+
All adapters are tested against the standard adapter test suite:
115+
116+
```bash
117+
go test -v
118+
```
119+
120+
**Note**: The full `RunEventStreamerTest` may timeout in high-concurrency scenarios due to SQLite's single-writer nature. Individual interface tests pass completely.
121+
122+
## Performance Characteristics
123+
124+
- **Read Performance**: Excellent with proper indexing
125+
- **Write Performance**: Good for moderate loads
126+
- **Concurrent Reads**: Supported via WAL mode
127+
- **Concurrent Writes**: Limited by SQLite's single-writer design
128+
- **Storage**: Efficient, single-file database
129+
130+
## Best Practices
131+
132+
1. **Use for appropriate workloads**: Single-node, moderate concurrency
133+
2. **Monitor database size**: SQLite can handle large databases efficiently
134+
3. **Regular maintenance**: Use `PRAGMA optimize` periodically
135+
4. **Backup strategy**: Simple file-based backups work well
136+
5. **Connection management**: Use single connection per process
137+
138+
## Comparison with Other Adapters
139+
140+
| Feature | SQLite | SQL + Timeout | Reflex + Kafka | In-Memory |
141+
|---------|---------|---------------|----------------|-----------|
142+
| Setup Complexity | Low | Medium | High | None |
143+
| Production Ready | Limited | Yes | Yes | No |
144+
| Multi-Node | No | Yes | Yes | No |
145+
| Persistence | Yes | Yes | Yes | No |
146+
| High Concurrency | No | Yes | Yes | Yes |
147+
148+
## Contributing
149+
150+
This adapter follows the same patterns as other workflow adapters. When contributing:
151+
152+
1. Run the full test suite
153+
2. Follow existing error handling patterns
154+
3. Maintain compatibility with the workflow interfaces
155+
4. Update documentation for any new features
156+
157+
## License
158+
159+
Same as the workflow library.

adapters/sqlite/example_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package sqlite_test
2+
3+
import (
4+
"context"
5+
"path/filepath"
6+
"testing"
7+
"time"
8+
9+
"github.com/luno/workflow"
10+
"github.com/stretchr/testify/require"
11+
12+
"github.com/luno/workflow/adapters/sqlite"
13+
)
14+
15+
func TestSQLiteAdapterExample(t *testing.T) {
16+
// Create a temporary database
17+
tempDir := t.TempDir()
18+
dbPath := filepath.Join(tempDir, "example.db")
19+
20+
// Open SQLite database with optimized settings
21+
db, err := sqlite.Open(dbPath)
22+
require.NoError(t, err)
23+
defer db.Close()
24+
25+
// Initialize schema
26+
err = sqlite.InitSchema(db)
27+
require.NoError(t, err)
28+
29+
// Create adapters
30+
recordStore := sqlite.NewRecordStore(db)
31+
timeoutStore := sqlite.NewTimeoutStore(db)
32+
eventStreamer := sqlite.NewEventStreamer(db)
33+
34+
ctx := context.Background()
35+
36+
// Example: RecordStore usage
37+
record := &workflow.Record{
38+
WorkflowName: "example_workflow",
39+
ForeignID: "user-123",
40+
RunID: "run-456",
41+
RunState: workflow.RunStateInitiated,
42+
Status: 1,
43+
Object: []byte(`{"data":"example"}`),
44+
Meta: workflow.Meta{StatusDescription: "example status"},
45+
}
46+
47+
err = recordStore.Store(ctx, record)
48+
require.NoError(t, err)
49+
50+
// Lookup record
51+
retrieved, err := recordStore.Lookup(ctx, "run-456")
52+
require.NoError(t, err)
53+
require.Equal(t, "user-123", retrieved.ForeignID)
54+
55+
// Example: TimeoutStore usage
56+
expireAt := time.Now().Add(1 * time.Hour)
57+
err = timeoutStore.Create(ctx, "example_workflow", "user-123", "run-456", 1, expireAt)
58+
require.NoError(t, err)
59+
60+
timeouts, err := timeoutStore.List(ctx, "example_workflow")
61+
require.NoError(t, err)
62+
require.Len(t, timeouts, 1)
63+
64+
// Example: EventStreamer usage (basic)
65+
sender, err := eventStreamer.NewSender(ctx, "example_topic")
66+
require.NoError(t, err)
67+
defer sender.Close()
68+
69+
headers := map[workflow.Header]string{
70+
workflow.HeaderTopic: "example_topic",
71+
workflow.HeaderWorkflowName: "example_workflow",
72+
workflow.HeaderForeignID: "user-123",
73+
}
74+
75+
err = sender.Send(ctx, "user-123", 1, headers)
76+
require.NoError(t, err)
77+
78+
t.Log("SQLite adapters created successfully!")
79+
t.Log("Database file:", dbPath)
80+
}

adapters/sqlite/go.mod

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
module github.com/luno/workflow/adapters/sqlite
2+
3+
go 1.24.2
4+
5+
replace github.com/luno/workflow => ../..
6+
7+
require (
8+
github.com/luno/workflow v0.3.0
9+
github.com/stretchr/testify v1.10.0
10+
modernc.org/sqlite v1.34.4
11+
)
12+
13+
require (
14+
github.com/beorn7/perks v1.0.1 // indirect
15+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
16+
github.com/davecgh/go-spew v1.1.1 // indirect
17+
github.com/dustin/go-humanize v1.0.1 // indirect
18+
github.com/google/uuid v1.6.0 // indirect
19+
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
20+
github.com/mattn/go-isatty v0.0.20 // indirect
21+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
22+
github.com/ncruces/go-strftime v0.1.9 // indirect
23+
github.com/pmezard/go-difflib v1.0.0 // indirect
24+
github.com/prometheus/client_golang v1.22.0 // indirect
25+
github.com/prometheus/client_model v0.6.1 // indirect
26+
github.com/prometheus/common v0.62.0 // indirect
27+
github.com/prometheus/procfs v0.15.1 // indirect
28+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
29+
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 // indirect
30+
golang.org/x/sys v0.31.0 // indirect
31+
google.golang.org/protobuf v1.36.6 // indirect
32+
gopkg.in/yaml.v3 v3.0.1 // indirect
33+
k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 // indirect
34+
modernc.org/gc/v3 v3.0.0-20241004144649-1aea3fae8852 // indirect
35+
modernc.org/libc v1.61.4 // indirect
36+
modernc.org/mathutil v1.6.0 // indirect
37+
modernc.org/memory v1.8.0 // indirect
38+
modernc.org/strutil v1.2.0 // indirect
39+
modernc.org/token v1.1.0 // indirect
40+
)

adapters/sqlite/go.sum

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
2+
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
3+
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
4+
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
5+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
6+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7+
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
8+
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
9+
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
10+
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
11+
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
12+
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
13+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
14+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
15+
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
16+
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
17+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
18+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
19+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
20+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
21+
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
22+
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
23+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
24+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
25+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
26+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
27+
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
28+
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
29+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
30+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
31+
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
32+
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
33+
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
34+
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
35+
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
36+
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
37+
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
38+
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
39+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
40+
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
41+
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
42+
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
43+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
44+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
45+
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w=
46+
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
47+
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
48+
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
49+
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
50+
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
51+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
52+
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
53+
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
54+
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
55+
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
56+
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
57+
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
58+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
59+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
60+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
61+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
62+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
63+
k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 h1:MDF6h2H/h4tbzmtIKTuctcwZmY0tY9mD9fNT47QO6HI=
64+
k8s.io/utils v0.0.0-20240921022957-49e7df575cb6/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
65+
modernc.org/cc/v4 v4.23.1 h1:WqJoPL3x4cUufQVHkXpXX7ThFJ1C4ik80i2eXEXbhD8=
66+
modernc.org/cc/v4 v4.23.1/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
67+
modernc.org/ccgo/v4 v4.23.1 h1:N49a7JiWGWV7lkPE4yYcvjkBGZQi93/JabRYjdWmJXc=
68+
modernc.org/ccgo/v4 v4.23.1/go.mod h1:JoIUegEIfutvoWV/BBfDFpPpfR2nc3U0jKucGcbmwDU=
69+
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
70+
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
71+
modernc.org/gc/v2 v2.5.0 h1:bJ9ChznK1L1mUtAQtxi0wi5AtAs5jQuw4PrPHO5pb6M=
72+
modernc.org/gc/v2 v2.5.0/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
73+
modernc.org/gc/v3 v3.0.0-20241004144649-1aea3fae8852 h1:IYXPPTTjjoSHvUClZIYexDiO7g+4x+XveKT4gCIAwiY=
74+
modernc.org/gc/v3 v3.0.0-20241004144649-1aea3fae8852/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
75+
modernc.org/libc v1.61.4 h1:wVyqEx6tlltte9lPTjq0kDAdtdM9c4JH8rU6M1ZVawA=
76+
modernc.org/libc v1.61.4/go.mod h1:VfXVuM/Shh5XsMNrh3C6OkfL78G3loa4ZC/Ljv9k7xc=
77+
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
78+
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
79+
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
80+
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
81+
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
82+
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
83+
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
84+
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
85+
modernc.org/sqlite v1.34.4 h1:sjdARozcL5KJBvYQvLlZEmctRgW9xqIZc2ncN7PU0P8=
86+
modernc.org/sqlite v1.34.4/go.mod h1:3QQFCG2SEMtc2nv+Wq4cQCH7Hjcg+p/RMlS1XK+zwbk=
87+
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
88+
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
89+
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
90+
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=

0 commit comments

Comments
 (0)