Skip to content

Commit 4a073a6

Browse files
committed
Result lifecycle handled by golang gc
1 parent a358f4d commit 4a073a6

File tree

3 files changed

+164
-0
lines changed

3 files changed

+164
-0
lines changed

chdbstable/chdb.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package chdbstable
2+
3+
/*
4+
#cgo LDFLAGS: -L. -lchdb
5+
#include <stdlib.h> // Include the C standard library for C.free
6+
#include "chdb.h"
7+
*/
8+
import "C"
9+
import (
10+
"runtime"
11+
"unsafe"
12+
)
13+
14+
// LocalResult mirrors the C struct local_result in Go.
15+
type LocalResult struct {
16+
cResult *C.struct_local_result
17+
}
18+
19+
// newLocalResult creates a new LocalResult and sets a finalizer to free C memory.
20+
func newLocalResult(cResult *C.struct_local_result) *LocalResult {
21+
result := &LocalResult{cResult: cResult}
22+
runtime.SetFinalizer(result, freeLocalResult)
23+
return result
24+
}
25+
26+
// freeLocalResult is called by the garbage collector.
27+
func freeLocalResult(result *LocalResult) {
28+
C.free_result(result.cResult)
29+
}
30+
31+
// QueryStable calls the C function query_stable.
32+
func QueryStable(argc int, argv []string) *LocalResult {
33+
cArgv := make([]*C.char, len(argv))
34+
for i, s := range argv {
35+
cArgv[i] = C.CString(s)
36+
defer C.free(unsafe.Pointer(cArgv[i]))
37+
}
38+
39+
cResult := C.query_stable(C.int(argc), &cArgv[0])
40+
return newLocalResult(cResult)
41+
}
42+
43+
// Accessor methods to access fields of the local_result struct.
44+
func (r *LocalResult) Buf() []byte {
45+
if r.cResult == nil {
46+
return nil
47+
}
48+
if r.cResult.buf == nil {
49+
return nil
50+
}
51+
return C.GoBytes(unsafe.Pointer(r.cResult.buf), C.int(r.cResult.len))
52+
}
53+
54+
// Stringer interface for LocalResult
55+
func (r LocalResult) String() string {
56+
ret := r.Buf()
57+
if ret == nil {
58+
return ""
59+
}
60+
return string(ret)
61+
}
62+
63+
func (r *LocalResult) Len() int {
64+
if r.cResult == nil {
65+
return 0
66+
}
67+
return int(r.cResult.len)
68+
}
69+
70+
func (r *LocalResult) Elapsed() float64 {
71+
if r.cResult == nil {
72+
return 0
73+
}
74+
return float64(r.cResult.elapsed)
75+
}
76+
77+
func (r *LocalResult) RowsRead() uint64 {
78+
if r.cResult == nil {
79+
return 0
80+
}
81+
return uint64(r.cResult.rows_read)
82+
}
83+
84+
func (r *LocalResult) BytesRead() uint64 {
85+
if r.cResult == nil {
86+
return 0
87+
}
88+
return uint64(r.cResult.bytes_read)
89+
}

chdbstable/chdb.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#pragma once
2+
3+
#ifdef __cplusplus
4+
# include <cstddef>
5+
# include <cstdint>
6+
extern "C" {
7+
#else
8+
# include <stddef.h>
9+
# include <stdint.h>
10+
#endif
11+
12+
#define CHDB_EXPORT __attribute__((visibility("default")))
13+
struct CHDB_EXPORT local_result
14+
{
15+
char * buf;
16+
size_t len;
17+
void * _vec; // std::vector<char> *, for freeing
18+
double elapsed;
19+
uint64_t rows_read;
20+
uint64_t bytes_read;
21+
};
22+
23+
CHDB_EXPORT struct local_result * query_stable(int argc, char ** argv);
24+
CHDB_EXPORT void free_result(struct local_result * result);
25+
26+
#ifdef __cplusplus
27+
}
28+
#endif

chdbstable/chdb_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package chdbstable
2+
3+
import (
4+
"testing"
5+
)
6+
7+
// TestCase defines the structure of a test case
8+
type TestCase struct {
9+
name string // Name of the test case
10+
argv []string // Arguments to pass to QueryStable
11+
expectError bool // Whether an error is expected
12+
expectOutput string // Expected output
13+
}
14+
15+
func TestQueryStableMultipleCases(t *testing.T) {
16+
// Define a series of test cases
17+
testCases := []TestCase{
18+
{
19+
name: "Single Query",
20+
argv: []string{"clickhouse", "--multiquery", "--output-format=CSV", "--query=SELECT 123;"},
21+
expectError: false,
22+
expectOutput: "123\n",
23+
},
24+
{
25+
name: "Multiple Queries",
26+
argv: []string{"clickhouse", "--multiquery", "--output-format=CSV", "--query=SELECT 'abc';"},
27+
expectError: false,
28+
expectOutput: "abc",
29+
},
30+
}
31+
32+
// Iterate over the test cases
33+
for _, tc := range testCases {
34+
t.Run(tc.name, func(t *testing.T) {
35+
result := QueryStable(len(tc.argv), tc.argv)
36+
37+
// Assert based on the expected outcome of the test case
38+
if (result == nil) != tc.expectError {
39+
t.Errorf("QueryStable() with args %v, expect error: %v, got result: %v", tc.argv, tc.expectError, result)
40+
}
41+
42+
if (result != nil) && (string(result) != tc.expectOutput) {
43+
t.Errorf("QueryStable() with args %v, expect output: %v, got output: %v", tc.argv, tc.expectOutput, string(result.Buf()))
44+
}
45+
})
46+
}
47+
}

0 commit comments

Comments
 (0)