Skip to content

Commit 50cd057

Browse files
authored
add option to run in docker (#20)
add option to run in docker
1 parent 85f6be7 commit 50cd057

File tree

3 files changed

+103
-29
lines changed

3 files changed

+103
-29
lines changed

.github/workflows/go.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ jobs:
2727
unzip /tmp/duckdb.zip -d /usr/local/bin/
2828
duckdb --version
2929
30+
- name: add duckdb image
31+
run: docker pull datacatering/duckdb:v1.0.0
32+
3033
- name: Build
3134
run: go build -v ./...
3235

duck/duckdb.go

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ type DuckDB struct {
2828
chunk int
2929
cacheDuration int
3030
cache cache
31+
docker bool
32+
image string
3133
}
3234

3335
type Opts struct {
@@ -36,9 +38,14 @@ type Opts struct {
3638
Chunk int
3739
Exe string
3840
CacheDuration int
41+
Docker bool
42+
Image string
3943
}
4044

4145
const newline = "\n"
46+
const duckdbImage = "datacatering/duckdb:v1.0.0"
47+
48+
var tempDir = getTempDir()
4249

4350
// NewInMemoryDB creates a new in-memory DuckDB
4451
func NewInMemoryDB(opts ...Opts) *DuckDB {
@@ -68,10 +75,15 @@ func NewDuckDB(name string, opts ...Opts) *DuckDB {
6875
if opt.CacheDuration > 0 {
6976
db.cacheDuration = opt.CacheDuration
7077
}
78+
db.image = duckdbImage
79+
if opt.Image != "" {
80+
db.image = opt.Image
81+
}
82+
db.docker = opt.Docker
7183
}
7284

7385
// Find the executable if it is not configured
74-
if db.exe == "" {
86+
if db.exe == "" && !db.docker {
7587
db.exe = which.Which("duckdb")
7688
if db.exe == "" {
7789
db.exe = "/usr/local/bin/duckdb"
@@ -83,33 +95,7 @@ func NewDuckDB(name string, opts ...Opts) *DuckDB {
8395

8496
// RunCommands runs a series of of sql commands against duckdb
8597
func (d *DuckDB) RunCommands(commands []string) (string, error) {
86-
var stdout bytes.Buffer
87-
var stderr bytes.Buffer
88-
89-
var b bytes.Buffer
90-
b.Write([]byte(fmt.Sprintf(".mode %s %s", d.mode, newline)))
91-
for _, c := range commands {
92-
cmd := fmt.Sprintf("%s %s", c, newline)
93-
b.Write([]byte(cmd))
94-
}
95-
96-
cmd := exec.Command(d.exe, d.Name)
97-
cmd.Stdin = &b
98-
cmd.Stdout = &stdout
99-
cmd.Stderr = &stderr
100-
101-
err := cmd.Run()
102-
if err != nil {
103-
message := err.Error() + stderr.String()
104-
logger.Error("error running command", "cmd", b.String(), "message", message, "error", err)
105-
return "", errors.New(message)
106-
}
107-
if stderr.String() != "" {
108-
logger.Error("error running command", "cmd", b.String(), "error", stderr.String())
109-
return "", errors.New(stderr.String())
110-
}
111-
112-
return stdout.String(), nil
98+
return d.runCommands(commands)
11399
}
114100

115101
// Query runs a query against the database. For Databases that are NOT in-memory.
@@ -242,6 +228,42 @@ func resultsToFrame(name string, res string, f *sdk.Frame, frames []*sdk.Frame)
242228
return nil
243229
}
244230

231+
func (d *DuckDB) runCommands(commands []string) (string, error) {
232+
var stdout bytes.Buffer
233+
var stderr bytes.Buffer
234+
235+
var b bytes.Buffer
236+
b.Write([]byte(fmt.Sprintf(".mode %s %s", d.mode, newline)))
237+
for _, c := range commands {
238+
cmd := fmt.Sprintf("%s %s", c, newline)
239+
b.Write([]byte(cmd))
240+
}
241+
242+
var cmd *exec.Cmd
243+
if d.docker {
244+
volume := fmt.Sprintf("%s:%s", tempDir, tempDir)
245+
logger.Debug("running command in docker", "volume", volume, "image", duckdbImage)
246+
cmd = exec.Command("docker", "run", "-i", "-v", volume, duckdbImage)
247+
} else {
248+
cmd = exec.Command(d.exe, d.Name)
249+
}
250+
cmd.Stdin = &b
251+
cmd.Stdout = &stdout
252+
cmd.Stderr = &stderr
253+
254+
err := cmd.Run()
255+
if err != nil {
256+
message := err.Error() + stderr.String()
257+
logger.Error("error running command", "cmd", b.String(), "message", message, "error", err)
258+
return "", errors.New(message)
259+
}
260+
if stderr.String() != "" {
261+
logger.Error("error running command", "cmd", b.String(), "error", stderr.String())
262+
return "", errors.New(stderr.String())
263+
}
264+
return stdout.String(), nil
265+
}
266+
245267
// TODO
246268

247269
// func applyLabels(resultsFrame sdk.Frame, sourceFrames []*sdk.Frame) {
@@ -263,4 +285,12 @@ func resultsToFrame(name string, res string, f *sdk.Frame, frames []*sdk.Frame)
263285
// }
264286
// }
265287
// return nil
266-
//
288+
// }
289+
290+
func getTempDir() string {
291+
temp := os.Getenv("TMPDIR")
292+
if temp == "" {
293+
temp = "/tmp"
294+
}
295+
return temp
296+
}

duck/duckdb_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,22 @@ func TestCommands(t *testing.T) {
2626
assert.Contains(t, res, `[{"i":1,"j":5}]`)
2727
}
2828

29+
func TestCommandsDocker(t *testing.T) {
30+
db := NewInMemoryDB(Opts{Docker: true})
31+
32+
commands := []string{
33+
"CREATE TABLE t1 (i INTEGER, j INTEGER);",
34+
"INSERT INTO t1 VALUES (1, 5);",
35+
"SELECT * from t1;",
36+
}
37+
res, err := db.RunCommands(commands)
38+
if err != nil {
39+
t.Fail()
40+
return
41+
}
42+
assert.Contains(t, res, `[{"i":1,"j":5}]`)
43+
}
44+
2945
func TestQuery(t *testing.T) {
3046
db := NewDuckDB("foo")
3147

@@ -149,6 +165,31 @@ func TestQueryFrameIntoFrame(t *testing.T) {
149165
fmt.Printf("GOT: %s", txt)
150166
}
151167

168+
func TestQueryFrameIntoFrameDocker(t *testing.T) {
169+
db := NewInMemoryDB(Opts{Docker: true})
170+
171+
var values = []string{"2024-02-23 09:01:54"}
172+
frame := data.NewFrame("foo", data.NewField("value", nil, values))
173+
frame.RefID = "foo"
174+
175+
var values2 = []string{"2024-02-23 09:02:54"}
176+
frame2 := data.NewFrame("foo", data.NewField("value", nil, values2))
177+
frame2.RefID = "foo"
178+
179+
frames := []*data.Frame{frame, frame2}
180+
181+
model := &data.Frame{}
182+
err := db.QueryFramesInto("foo", "select * from foo order by value desc", frames, model)
183+
assert.Nil(t, err)
184+
185+
assert.Equal(t, 2, model.Rows())
186+
187+
txt, err := model.StringTable(-1, -1)
188+
assert.Nil(t, err)
189+
190+
fmt.Printf("GOT: %s", txt)
191+
}
192+
152193
func TestQueryFrameIntoFrameMultipleColumns(t *testing.T) {
153194
db := NewInMemoryDB()
154195

0 commit comments

Comments
 (0)