-
-
Notifications
You must be signed in to change notification settings - Fork 7
Implement chdb v3.0.0 with pureGo instead of cGo #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 16 commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
a54ca89
bump chdb v3, implemented first part of connection based query
agoncear-mwb 0b87dd8
add tests for `connect` method
agoncear-mwb 5fa7af5
fix comments on connection.go
agoncear-mwb de314c6
fix flaky test
agoncear-mwb 8980a7a
add parquet with connection test
agoncear-mwb b2ba138
change session mode to sqlite like api (v3.0.0)
agoncear-mwb 9c2376b
add purego bindings
agoncear-mwb ef98543
fix function signatures
agoncear-mwb d8a963f
remove arrow for the moment since it has a problem in the library itself
agoncear-mwb 01896db
removed arrow dependency, fix tests
agoncear-mwb f54b767
fix macos runner (github actions)
agoncear-mwb 7a5fd4e
fix wrapper_test
agoncear-mwb 342e4b5
fix tests
agoncear-mwb 8a983de
update documentation
agoncear-mwb 43c572e
remove useless error in `Free()` method
agoncear-mwb 67d0538
remove test hardcoded path on main.go
agoncear-mwb f1aaefc
fix session tests, remove RawQuery, fix docs
agoncear-mwb 51ede24
fix tests
agoncear-mwb 54e2bdd
Update session_test.go
auxten 7e1c4ab
remove flaky test
agoncear-mwb 81e31db
fix flaky test with global session initializer
agoncear-mwb 6a4de22
fix wrong docs
agoncear-mwb File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package chdbpurego | ||
|
||
import ( | ||
"os" | ||
"os/exec" | ||
|
||
"github.com/ebitengine/purego" | ||
) | ||
|
||
func findLibrary() string { | ||
// Env var | ||
if envPath := os.Getenv("CHDB_LIB_PATH"); envPath != "" { | ||
return envPath | ||
} | ||
|
||
// ldconfig with Linux | ||
if path, err := exec.LookPath("libchdb.so"); err == nil { | ||
return path | ||
} | ||
|
||
// default path | ||
commonPaths := []string{ | ||
"/usr/local/lib/libchdb.so", | ||
"/opt/homebrew/lib/libchdb.dylib", | ||
} | ||
|
||
for _, p := range commonPaths { | ||
if _, err := os.Stat(p); err == nil { | ||
return p | ||
} | ||
} | ||
|
||
//should be an error ? | ||
return "libchdb.so" | ||
} | ||
|
||
var ( | ||
queryStable func(argc int, argv []string) *local_result | ||
freeResult func(result *local_result) | ||
queryStableV2 func(argc int, argv []string) *local_result_v2 | ||
freeResultV2 func(result *local_result_v2) | ||
connectChdb func(argc int, argv []string) **chdb_conn | ||
closeConn func(conn **chdb_conn) | ||
queryConn func(conn *chdb_conn, query string, format string) *local_result_v2 | ||
) | ||
|
||
func init() { | ||
path := findLibrary() | ||
libchdb, err := purego.Dlopen(path, purego.RTLD_NOW|purego.RTLD_GLOBAL) | ||
if err != nil { | ||
panic(err) | ||
} | ||
purego.RegisterLibFunc(&queryStable, libchdb, "query_stable") | ||
purego.RegisterLibFunc(&freeResult, libchdb, "free_result") | ||
purego.RegisterLibFunc(&queryStableV2, libchdb, "query_stable_v2") | ||
|
||
purego.RegisterLibFunc(&freeResultV2, libchdb, "free_result_v2") | ||
purego.RegisterLibFunc(&connectChdb, libchdb, "connect_chdb") | ||
purego.RegisterLibFunc(&closeConn, libchdb, "close_conn") | ||
purego.RegisterLibFunc(&queryConn, libchdb, "query_conn") | ||
|
||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
package chdbpurego | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"unsafe" | ||
) | ||
|
||
type result struct { | ||
localResv2 *local_result_v2 | ||
} | ||
|
||
func newChdbResult(cRes *local_result_v2) ChdbResult { | ||
res := &result{ | ||
localResv2: cRes, | ||
} | ||
// runtime.SetFinalizer(res, res.Free) | ||
return res | ||
|
||
} | ||
|
||
// Buf implements ChdbResult. | ||
func (c *result) Buf() []byte { | ||
if c.localResv2 != nil { | ||
if c.localResv2.buf != nil && c.localResv2.len > 0 { | ||
return unsafe.Slice(c.localResv2.buf, c.localResv2.len) | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// BytesRead implements ChdbResult. | ||
func (c *result) BytesRead() uint64 { | ||
if c.localResv2 != nil { | ||
return c.localResv2.bytes_read | ||
} | ||
return 0 | ||
} | ||
|
||
// Elapsed implements ChdbResult. | ||
func (c *result) Elapsed() float64 { | ||
if c.localResv2 != nil { | ||
return c.localResv2.elapsed | ||
} | ||
return 0 | ||
} | ||
|
||
// Error implements ChdbResult. | ||
func (c *result) Error() error { | ||
if c.localResv2 != nil { | ||
if c.localResv2.error_message != nil { | ||
return errors.New(ptrToGoString(c.localResv2.error_message)) | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// Free implements ChdbResult. | ||
func (c *result) Free() { | ||
if c.localResv2 != nil { | ||
freeResultV2(c.localResv2) | ||
c.localResv2 = nil | ||
} | ||
|
||
} | ||
|
||
// Len implements ChdbResult. | ||
func (c *result) Len() int { | ||
if c.localResv2 != nil { | ||
return int(c.localResv2.len) | ||
} | ||
return 0 | ||
} | ||
|
||
// RowsRead implements ChdbResult. | ||
func (c *result) RowsRead() uint64 { | ||
if c.localResv2 != nil { | ||
return c.localResv2.rows_read | ||
} | ||
return 0 | ||
} | ||
|
||
// String implements ChdbResult. | ||
func (c *result) String() string { | ||
ret := c.Buf() | ||
if ret == nil { | ||
return "" | ||
} | ||
return string(ret) | ||
} | ||
|
||
type connection struct { | ||
conn **chdb_conn | ||
} | ||
|
||
func newChdbConn(conn **chdb_conn) ChdbConn { | ||
c := &connection{ | ||
conn: conn, | ||
} | ||
// runtime.SetFinalizer(c, c.Close) | ||
return c | ||
} | ||
|
||
// Close implements ChdbConn. | ||
func (c *connection) Close() { | ||
if c.conn != nil { | ||
closeConn(c.conn) | ||
} | ||
} | ||
|
||
// Query implements ChdbConn. | ||
func (c *connection) Query(queryStr string, formatStr string) (result ChdbResult, err error) { | ||
|
||
if c.conn == nil { | ||
return nil, fmt.Errorf("invalid connection") | ||
} | ||
|
||
rawConn := *c.conn | ||
|
||
res := queryConn(rawConn, queryStr, formatStr) | ||
if res == nil { | ||
// According to the C ABI of chDB v1.2.0, the C function query_stable_v2 | ||
// returns nil if the query returns no data. This is not an error. We | ||
// will change this behavior in the future. | ||
return newChdbResult(res), nil | ||
} | ||
if res.error_message != nil { | ||
return nil, errors.New(ptrToGoString(res.error_message)) | ||
} | ||
|
||
return newChdbResult(res), nil | ||
} | ||
|
||
func (c *connection) Ready() bool { | ||
if c.conn != nil { | ||
deref := *c.conn | ||
if deref != nil { | ||
return deref.connected | ||
} | ||
} | ||
return false | ||
} | ||
|
||
// RawQuery will execute the given clickouse query without using any session. | ||
func RawQuery(argc int, argv []string) (result ChdbResult, err error) { | ||
res := queryStableV2(argc, argv) | ||
if res == nil { | ||
// According to the C ABI of chDB v1.2.0, the C function query_stable_v2 | ||
// returns nil if the query returns no data. This is not an error. We | ||
// will change this behavior in the future. | ||
return newChdbResult(res), nil | ||
} | ||
if res.error_message != nil { | ||
return nil, errors.New(ptrToGoString(res.error_message)) | ||
} | ||
|
||
return newChdbResult(res), nil | ||
} | ||
|
||
// Session will keep the state of query. | ||
// If path is None, it will create a temporary directory and use it as the database path | ||
// and the temporary directory will be removed when the session is closed. | ||
// You can also pass in a path to create a database at that path where will keep your data. | ||
// | ||
// You can also use a connection string to pass in the path and other parameters. | ||
// Examples: | ||
// - ":memory:" (for in-memory database) | ||
// - "test.db" (for relative path) | ||
// - "file:test.db" (same as above) | ||
// - "/path/to/test.db" (for absolute path) | ||
// - "file:/path/to/test.db" (same as above) | ||
// - "file:test.db?param1=value1¶m2=value2" (for relative path with query params) | ||
// - "file::memory:?verbose&log-level=test" (for in-memory database with query params) | ||
// - "///path/to/test.db?param1=value1¶m2=value2" (for absolute path) | ||
// | ||
// Connection string args handling: | ||
// | ||
// Connection string can contain query params like "file:test.db?param1=value1¶m2=value2" | ||
// "param1=value1" will be passed to ClickHouse engine as start up args. | ||
// | ||
// For more details, see `clickhouse local --help --verbose` | ||
// Some special args handling: | ||
// - "mode=ro" would be "--readonly=1" for clickhouse (read-only mode) | ||
// | ||
// Important: | ||
// - There can be only one session at a time. If you want to create a new session, you need to close the existing one. | ||
// - Creating a new session will close the existing one. | ||
func NewConnection(argc int, argv []string) (ChdbConn, error) { | ||
conn := connectChdb(argc, argv) | ||
if conn == nil { | ||
return nil, fmt.Errorf("could not create a chdb connection") | ||
} | ||
return newChdbConn(conn), nil | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package chdbpurego | ||
|
||
import ( | ||
"unsafe" | ||
) | ||
|
||
func ptrToGoString(ptr *byte) string { | ||
if ptr == nil { | ||
return "" | ||
} | ||
|
||
var length int | ||
for { | ||
if *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr)) + uintptr(length))) == 0 { | ||
break | ||
} | ||
length++ | ||
} | ||
|
||
return string(unsafe.Slice(ptr, length)) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package chdbpurego | ||
|
||
import "unsafe" | ||
|
||
// old local result struct. for reference: | ||
// https://github.com/chdb-io/chdb/blob/main/programs/local/chdb.h#L29 | ||
type local_result struct { | ||
buf *byte | ||
len uintptr | ||
_vec unsafe.Pointer | ||
elapsed float64 | ||
rows_read uint64 | ||
bytes_read uint64 | ||
} | ||
|
||
// new local result struct. for reference: https://github.com/chdb-io/chdb/blob/main/programs/local/chdb.h#L40 | ||
type local_result_v2 struct { | ||
buf *byte | ||
len uintptr | ||
_vec unsafe.Pointer | ||
elapsed float64 | ||
rows_read uint64 | ||
bytes_read uint64 | ||
error_message *byte | ||
} | ||
|
||
// clickhouse background server connection.for reference: https://github.com/chdb-io/chdb/blob/main/programs/local/chdb.h#L82 | ||
type chdb_conn struct { | ||
server unsafe.Pointer | ||
connected bool | ||
queue unsafe.Pointer | ||
} | ||
|
||
type ChdbResult interface { | ||
// Raw bytes result buffer, used for reading the result of clickhouse query | ||
Buf() []byte | ||
// String rapresentation of the the buffer | ||
String() string | ||
// Lenght in bytes of the buffer | ||
Len() int | ||
// Number of seconds elapsed for the query execution | ||
Elapsed() float64 | ||
// Amount of rows returned by the query | ||
RowsRead() uint64 | ||
// Amount of bytes returned by the query | ||
BytesRead() uint64 | ||
// If the query had any error during execution, here you can retrieve the cause. | ||
Error() error | ||
// Free the query result and all the allocated memory | ||
Free() | ||
} | ||
|
||
type ChdbConn interface { | ||
//Query executes the given queryStr in the underlying clickhouse connection, and output the result in the given formatStr | ||
Query(queryStr string, formatStr string) (result ChdbResult, err error) | ||
//Ready returns a boolean indicating if the connections is successfully established. | ||
Ready() bool | ||
//Close the connection and free the underlying allocated memory | ||
Close() | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.