Skip to content

Commit 868277f

Browse files
authored
Merge pull request #3 from chdb-io/dev
Implement dynamic loading of libchdb.so via dlopen and auto-download during gem install
2 parents 78209d0 + c5d62d2 commit 868277f

13 files changed

+191
-104
lines changed

.rubocop.yml

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ AllCops:
44
- 'spec/**/*'
55
- 'tmp/**/*'
66
- 'ext/**/extconf.rb'
7+
- 'pkg/**/*'
78

89
NewCops: enable
910

INSTALLATION.md

+6-38
Original file line numberDiff line numberDiff line change
@@ -5,55 +5,23 @@ This document will help you install the `chdb-ruby` ruby gem.
55

66
## Installation
77

8-
### Native Gems
8+
### Compilation During Installation
99

10-
Native (precompiled) gems are available for recent Ruby versions on these platforms:
10+
The gem will automatically compile native extensions during installation (`gem install`). Supported platforms are:
1111

1212
- `aarch64-linux`
1313
- `arm64-darwin`
1414
- `x86_64-linux`
1515
- `x86_64-darwin`
1616

17-
If you are using one of these Ruby versions on one of these platforms, the native gem is the recommended way to install chdb-ruby.
17+
The `chdb-ruby` gem does not provide a pure Ruby implementation. Installation will fail if your platform is unsupported.
1818

19-
`chdb-ruby` gem does not provide a pure Ruby implementation. Installation will fail if your platform is unsupported.
19+
## Runtime Dependencies
2020

21-
## Post-Installation: Setting Up libchdb C++ Library
21+
The `chdb-ruby` gem requires the `libchdb` C++ library as its core engine. The library will be automatically downloaded during installation. The extension uses `dlopen` to dynamically load the library. No manual configuration is required.
2222

23-
After installing the `chdb-ruby` gem, you must also install the `libchdb` C++ library locally. If the library path is not in your system's default search paths, you'll need to configure the runtime library loading path.
24-
25-
### 1. Download the C++ Library
26-
27-
You can either:
28-
- Use the automated installation script:
29-
```bash
30-
curl -sSL https://github.com/chdb-io/chdb-io.github.io/blob/main/install_libchdb.sh | bash
31-
```
32-
33-
- Or manually download from chdb releases(example for arm64-darwin (v3.12)):
34-
```bash
35-
wget https://github.com/chdb-io/chdb/releases/download/v3.12/macos-arm64-libchdb.tar.gz
36-
tar -xzf macos-arm64-libchdb.tar.gz
37-
```
38-
39-
### 2. Configure Library Path
40-
- MacOS:
41-
```bash
42-
export DYLD_LIBRARY_PATH="/path/to/libchdb:$DYLD_LIBRARY_PATH"
43-
```
44-
(Add to your shell config file like ~/.zshrc for persistence)
45-
46-
- Linux:
47-
```bash
48-
export LD_LIBRARY_PATH="/path/to/libchdb:$LD_LIBRARY_PATH"
49-
```
50-
51-
### 3. Verify Installation
23+
## Verify Installation
5224
- Ruby:
5325
```bash
5426
require 'chdb'
5527
```
56-
57-
- Troubleshooting(If you get "Library not loaded" errors):
58-
- Verify the path in DYLD_LIBRARY_PATH/LD_LIBRARY_PATH is correct
59-
- Ensure you downloaded the right version for your platform

chdb.gemspec

+13-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,18 @@ Gem::Specification.new do |s|
3838
'INSTALLATION.md',
3939
'LICENSE',
4040
'README.md',
41+
'dependencies.yml',
42+
'ext/chdb/chdb_handle.c',
43+
'ext/chdb/chdb_handle.h',
44+
'ext/chdb/chdb.c',
45+
'ext/chdb/connection.c',
46+
'ext/chdb/connection.h',
47+
'ext/chdb/constants.h',
48+
'ext/chdb/exception.c',
49+
'ext/chdb/exception.h',
50+
'ext/chdb/extconf.rb',
51+
'ext/chdb/local_result.c',
52+
'ext/chdb/local_result.h',
4153
'lib/chdb.rb',
4254
'lib/chdb/constants.rb',
4355
'lib/chdb/data_path.rb',
@@ -57,5 +69,5 @@ Gem::Specification.new do |s|
5769

5870
s.add_dependency 'csv', '~> 3.1'
5971

60-
# s.extensions << 'ext/chdb/extconf.rb'
72+
s.extensions << 'ext/chdb/extconf.rb'
6173
end

ext/chdb/chdb.c

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <ruby.h>
22

33
#include "chdb.h"
4+
#include "chdb_handle.h"
45
#include "constants.h"
56
#include "connection.h"
67
#include "exception.h"
@@ -22,6 +23,7 @@ void Init_chdb_native()
2223
DEBUG_PRINT("Initializing chdb extension");
2324

2425
init_exception();
26+
init_chdb_handle();
2527
init_chdb_constants();
2628
init_local_result();
2729
init_connection();

ext/chdb/chdb_handle.c

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#include "chdb_handle.h"
2+
3+
#include <dlfcn.h>
4+
#include <ruby.h>
5+
6+
#include "exception.h"
7+
8+
void *chdb_handle = NULL;
9+
connect_chdb_func connect_chdb_ptr = NULL;
10+
close_conn_func close_conn_ptr = NULL;
11+
query_conn_func query_conn_ptr = NULL;
12+
13+
VALUE get_chdb_rb_path(void)
14+
{
15+
VALUE chdb_module = rb_const_get(rb_cObject, rb_intern("ChDB"));
16+
return rb_funcall(chdb_module, rb_intern("lib_file_path"), 0);
17+
}
18+
19+
void init_chdb_handle()
20+
{
21+
VALUE rb_path = get_chdb_rb_path();
22+
VALUE lib_dir = rb_file_dirname(rb_file_dirname(rb_path));
23+
VALUE lib_path = rb_str_cat2(lib_dir, "/lib/chdb/lib/libchdb.so");
24+
// printf("chdb.rb path from Ruby: %s\n", StringValueCStr(lib_path));
25+
26+
connect_chdb_ptr = NULL;
27+
close_conn_ptr = NULL;
28+
query_conn_ptr = NULL;
29+
30+
chdb_handle = dlopen(RSTRING_PTR(lib_path), RTLD_LAZY | RTLD_GLOBAL);
31+
if (!chdb_handle)
32+
{
33+
rb_raise(cChDBError, "Failed to load chdb library: %s\nCheck if libchdb.so exists at: %s",
34+
dlerror(), RSTRING_PTR(lib_path));
35+
}
36+
37+
connect_chdb_ptr = (connect_chdb_func)dlsym(chdb_handle, "connect_chdb");
38+
close_conn_ptr = (close_conn_func)dlsym(chdb_handle, "close_conn");
39+
query_conn_ptr = (query_conn_func)dlsym(chdb_handle, "query_conn");
40+
41+
if (!connect_chdb_ptr || !close_conn_ptr || !query_conn_ptr)
42+
{
43+
rb_raise(cChDBError, "Symbol loading failed: %s\nMissing functions: connect_chdb(%p) close_conn(%p) query_conn(%p)",
44+
dlerror(),
45+
(void*)connect_chdb_ptr,
46+
(void*)close_conn_ptr,
47+
(void*)query_conn_ptr);
48+
}
49+
}

ext/chdb/chdb_handle.h

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef CHDB_HANDLE_H
2+
#define CHDB_HANDLE_H
3+
4+
typedef struct chdb_conn **(*connect_chdb_func)(int, char**);
5+
typedef void (*close_conn_func)(struct chdb_conn**);
6+
typedef struct local_result_v2 *(*query_conn_func)(struct chdb_conn*, const char*, const char*);
7+
8+
extern connect_chdb_func connect_chdb_ptr;
9+
extern close_conn_func close_conn_ptr;
10+
extern query_conn_func query_conn_ptr;
11+
12+
extern void *chdb_handle;
13+
14+
void init_chdb_handle();
15+
16+
#endif

ext/chdb/connection.c

+22-22
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,29 @@
11
#include "connection.h"
22

3+
#include "chdb_handle.h"
34
#include "constants.h"
45
#include "exception.h"
56
#include "include/chdb.h"
67
#include "local_result.h"
78

9+
static void connection_free(void *ptr)
10+
{
11+
Connection *conn = (Connection *)ptr;
12+
DEBUG_PRINT("Closing connection: %p", (void*)conn->c_conn);
13+
if (conn->c_conn)
14+
{
15+
close_conn_ptr(conn->c_conn);
16+
conn->c_conn = NULL;
17+
}
18+
free(conn);
19+
}
20+
21+
const rb_data_type_t ConnectionType =
22+
{
23+
"Connection",
24+
{NULL, connection_free, NULL},
25+
};
26+
827
void init_connection()
928
{
1029
VALUE mChDB = rb_define_module("ChDB");
@@ -39,7 +58,7 @@ VALUE connection_initialize(VALUE self, VALUE argc, VALUE argv)
3958

4059
Connection *conn;
4160
TypedData_Get_Struct(self, Connection, &ConnectionType, conn);
42-
conn->c_conn = connect_chdb(c_argc, c_argv);
61+
conn->c_conn = connect_chdb_ptr(c_argc, c_argv);
4362

4463
if (!conn->c_conn)
4564
{
@@ -60,7 +79,7 @@ VALUE connection_query(VALUE self, VALUE query, VALUE format)
6079
Check_Type(query, T_STRING);
6180
Check_Type(format, T_STRING);
6281

63-
struct local_result_v2 *c_result = query_conn(
82+
struct local_result_v2 *c_result = query_conn_ptr(
6483
*conn->c_conn,
6584
StringValueCStr(query),
6685
StringValueCStr(format)
@@ -92,27 +111,8 @@ VALUE connection_close(VALUE self)
92111

93112
if (conn->c_conn)
94113
{
95-
close_conn(conn->c_conn);
114+
close_conn_ptr(conn->c_conn);
96115
conn->c_conn = NULL;
97116
}
98117
return Qnil;
99118
}
100-
101-
static void connection_free(void *ptr)
102-
{
103-
Connection *conn = (Connection *)ptr;
104-
DEBUG_PRINT("Closing connection: %p", (void*)conn->c_conn);
105-
if (conn->c_conn)
106-
{
107-
close_conn(conn->c_conn);
108-
}
109-
free(conn);
110-
}
111-
112-
const rb_data_type_t ConnectionType =
113-
{
114-
"Connection",
115-
{NULL, connection_free, NULL},
116-
};
117-
118-
// 其他 Connection 方法保持不变...

0 commit comments

Comments
 (0)