Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit988bbf6

Browse files
authored
Merge pull request#4 from chdb-io/dev
Implement global singleton pattern to ensure only one active database instance
2 parents868277f +228c0d8 commit988bbf6

20 files changed

+670
-98
lines changed

‎.gitignore‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ ports/
1818
tmp/
1919
vendor/
2020
test/
21-
testdb/
21+
testdb*/
2222
ext/chdb/include/
2323
ext/chdb/lib/

‎README.md‎

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,69 +20,111 @@ Note that this module is only compatible with ChDB 3.0.0 or newer.
2020

2121
##Quick start
2222

23-
```ruby
23+
Before using chdb-ruby, install the gem first. This will download the libchdb C++ library dependencies, so please be patient:
24+
```bash
25+
gem install chdb-ruby
26+
```
27+
28+
Below are examples of common interfaces usage:
29+
30+
```ruby
2431
require'chdb'
2532

2633
# Open a database
27-
db=ChDB::Database.new('test_db',results_as_hash:true)
34+
# Parameter explanation:
35+
# 1. path supports two formats:
36+
# - ":memory:" in-memory temporary database (data destroyed on close)
37+
# - "file:/path/to/db" file-based persistent database
38+
# Configuration parameters can be appended via URL-style query (e.g. 'file:test.db?results_as_hash=true')
39+
# 2. options hash supports:
40+
# - results_as_hash: controls whether result sets return as hashes (default: arrays)
41+
db=ChDB::Database.new('file:test.db',results_as_hash:true)
42+
43+
# Create a database
44+
db.execute('CREATE DATABASE IF NOT EXISTS test')
2845

2946
# Create a table
47+
db.execute('DROP TABLE IF EXISTS test.test_table')
3048
rows= db.execute<<-SQL
31-
CREATETABLEtest_table(
49+
CREATETABLEtest.test_table(
3250
id Int32,
3351
name String)
3452
ENGINE= MergeTree()
35-
ORDER BY id);
53+
ORDER BY id
3654
SQL
3755

3856
# Execute a few inserts
3957
{
40-
1 =>'Alice',
41-
2 =>'Bob'
58+
1 =>'Alice',
59+
2 =>'Bob'
4260
}.eachdo |pair|
43-
db.execute'INSERT INTO test_table VALUES ( ?, ? )', pair
61+
db.execute'INSERT INTOtest.test_table VALUES ( ?, ? )', pair
4462
end
4563

4664
# Find a few rows
47-
db.execute('SELECT * FROM test_table ORDER BY id')do |row|
65+
db.execute('SELECT * FROMtest.test_table ORDER BY id')do |row|
4866
p row
4967
end
5068
# [{ 'id' => '1', 'name' => 'Alice' },
5169
# { 'id' => '2', 'name' => 'Bob' }]
5270

71+
# When you need to open another database, you must first close the previous database
72+
db.close()
73+
5374
# Open another database
54-
db=ChDB::Database.new'test2.db'
75+
db=ChDB::Database.new'file:test.db'
5576

5677
# Create another table
78+
db.execute('DROP TABLE IF EXISTS test.test2_table')
5779
rows= db.execute<<-SQL
58-
CREATETABLEtest2_table(
80+
CREATETABLEtest.test2_table(
5981
id Int32,
6082
name String)
6183
ENGINE= MergeTree()
62-
ORDER BY id");
84+
ORDER BY id
6385
SQL
6486

6587
# Execute inserts with parameter markers
66-
db.execute('INSERT INTO test2_table (id, name)
88+
db.execute('INSERT INTOtest.test2_table (id, name)
6789
VALUES (?, ?)', [3,'Charlie'])
6890

69-
db.execute2('SELECT * FROM test2_table') do |row|
91+
# Find rows with the first row displaying column names
92+
db.execute2('SELECT * FROM test.test2_table')do |row|
7093
p row
7194
end
72-
# [['id', 'name'], [3, 'Charlie']],
95+
# ["id", "name"]
96+
# ["3", "Charlie"]
97+
98+
# Close the database
99+
db.close()
100+
101+
# Use ChDB::Database.open to automatically close the database connection:
102+
ChDB::Database.open('file:test.db')do |db|
103+
result= db.execute('SELECT 1')
104+
p result.to_a# => [["1"]]
105+
end
106+
107+
# Query with specific output formats (CSV, JSON, etc.):
108+
# See more details at https://clickhouse.com/docs/interfaces/formats.
109+
ChDB::Database.open(':memory:')do |db|
110+
csv_data= db.query_with_format('SELECT 1 as a, 2 as b','CSV')
111+
p csv_data
112+
# "1,2\n"
113+
114+
json_data= db.query_with_format('SELECT 1 as a, 2 as b','JSON')
115+
p json_data
116+
end
73117
```
74118

75119
##Thread Safety
76120

77-
When using `ChDB::Database.new` to open a session, all read/write operations within that session are thread-safe. However, currently only one active session is allowed per process. Therefore, when you need to open another session, you must first close the previous session.
78-
79-
For example, the following code is fine because only the database
80-
instance is shared among threads:
121+
When using`ChDB::Database.new` or`ChDB::Database.open` to open a database connection, all read/write operations within that session are thread-safe. However, currently only one active database connection is allowed per process. Therefore, when you need to open another database connection, you must first close the previous connection.
122+
**Please note that`ChDB::Database.new`,`ChDB::Database.open`, and`ChDB::Database.close` methods themselves are not thread-safe.** If used in multi-threaded environments, external synchronization must be implemented to prevent concurrent calls to these methods, which could lead to undefined behavior.
81123

82124
```ruby
83125
require'chdb'
84126

85-
db = ChDB::Database.new":memory:'
127+
db=ChDB::Database.new':memory:'
86128

87129
latch=Queue.new
88130

@@ -95,6 +137,9 @@ ts = 10.times.map {
95137
10.times { latch<<nil }
96138

97139
p ts.map(&:value)
140+
# [[["1"]], [["1"]], [["1"]], [["1"]], [["1"]], [["1"]], [["1"]], [["1"]], [["1"]], [["1"]]]
141+
142+
db.close()
98143
```
99144

100145
Other instances can be shared among threads, but they require that you provide

‎chdb.gemspec‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Gem::Specification.new do |s|
4545
'ext/chdb/connection.c',
4646
'ext/chdb/connection.h',
4747
'ext/chdb/constants.h',
48+
'ext/chdb/constants.c',
4849
'ext/chdb/exception.c',
4950
'ext/chdb/exception.h',
5051
'ext/chdb/extconf.rb',

‎ext/chdb/chdb.c‎

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,6 @@
77
#include"exception.h"
88
#include"local_result.h"
99

10-
voidinit_chdb_constants()
11-
{
12-
VALUEmChDB=rb_define_module("ChDB");
13-
VALUEmChDBConstants=rb_define_module_under(mChDB,"Constants");
14-
VALUEmmChDBOpen=rb_define_module_under(mChDBConstants,"Open");
15-
16-
rb_define_const(mmChDBOpen,"READONLY",INT2FIX(CHDB_OPEN_READONLY));
17-
rb_define_const(mmChDBOpen,"READWRITE",INT2FIX(CHDB_OPEN_READWRITE));
18-
rb_define_const(mmChDBOpen,"CREATE",INT2FIX(CHDB_OPEN_CREATE));
19-
}
20-
2110
voidInit_chdb_native()
2211
{
2312
DEBUG_PRINT("Initializing chdb extension");

‎ext/chdb/chdb_handle.c‎

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,43 @@
22

33
#include<dlfcn.h>
44
#include<ruby.h>
5-
5+
#include"constants.h"
66
#include"exception.h"
77

88
void*chdb_handle=NULL;
99
connect_chdb_funcconnect_chdb_ptr=NULL;
1010
close_conn_funcclose_conn_ptr=NULL;
1111
query_conn_funcquery_conn_ptr=NULL;
12+
free_result_v2_funcfree_result_v2_ptr=NULL;
1213

13-
VALUEget_chdb_rb_path(void)
14+
VALUEget_chdb_rb_path()
1415
{
1516
VALUEchdb_module=rb_const_get(rb_cObject,rb_intern("ChDB"));
1617
returnrb_funcall(chdb_module,rb_intern("lib_file_path"),0);
1718
}
1819

20+
voidclose_chdb_handle()
21+
{
22+
if (chdb_handle)
23+
{
24+
dlclose(chdb_handle);
25+
chdb_handle=NULL;
26+
DEBUG_PRINT("Close chdb handle");
27+
}
28+
}
29+
1930
voidinit_chdb_handle()
2031
{
2132
VALUErb_path=get_chdb_rb_path();
2233
VALUElib_dir=rb_file_dirname(rb_file_dirname(rb_path));
2334
VALUElib_path=rb_str_cat2(lib_dir,"/lib/chdb/lib/libchdb.so");
24-
// printf("chdb.rb path from Ruby: %s\n", StringValueCStr(lib_path));
35+
36+
DEBUG_PRINT("chdb.rb path from Ruby: %s\n",StringValueCStr(lib_path));
2537

2638
connect_chdb_ptr=NULL;
2739
close_conn_ptr=NULL;
2840
query_conn_ptr=NULL;
41+
free_result_v2_ptr=NULL;
2942

3043
chdb_handle=dlopen(RSTRING_PTR(lib_path),RTLD_LAZY |RTLD_GLOBAL);
3144
if (!chdb_handle)
@@ -37,13 +50,20 @@ void init_chdb_handle()
3750
connect_chdb_ptr= (connect_chdb_func)dlsym(chdb_handle,"connect_chdb");
3851
close_conn_ptr= (close_conn_func)dlsym(chdb_handle,"close_conn");
3952
query_conn_ptr= (query_conn_func)dlsym(chdb_handle,"query_conn");
53+
free_result_v2_ptr= (free_result_v2_func)dlsym(chdb_handle,"free_result_v2");
4054

41-
if (!connect_chdb_ptr|| !close_conn_ptr|| !query_conn_ptr)
55+
if (!connect_chdb_ptr|| !close_conn_ptr|| !query_conn_ptr|| !free_result_v2_ptr)
4256
{
43-
rb_raise(cChDBError,"Symbol loading failed: %s\nMissing functions: connect_chdb(%p) close_conn(%p) query_conn(%p)",
57+
close_chdb_handle();
58+
59+
rb_raise(cChDBError,
60+
"Symbol loading failed: %s\nMissing functions: connect_chdb(%p) close_conn(%p) query_conn(%p), free_result_v2(%p)",
4461
dlerror(),
4562
(void*)connect_chdb_ptr,
4663
(void*)close_conn_ptr,
47-
(void*)query_conn_ptr);
64+
(void*)query_conn_ptr,
65+
(void*)free_result_v2_ptr);
4866
}
49-
}
67+
68+
rb_set_end_proc(close_chdb_handle,0);
69+
}

‎ext/chdb/chdb_handle.h‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
typedefstructchdb_conn**(*connect_chdb_func)(int,char**);
55
typedefvoid (*close_conn_func)(structchdb_conn**);
66
typedefstructlocal_result_v2*(*query_conn_func)(structchdb_conn*,constchar*,constchar*);
7+
typedefvoid (*free_result_v2_func)(structlocal_result_v2*);
78

89
externconnect_chdb_funcconnect_chdb_ptr;
910
externclose_conn_funcclose_conn_ptr;
1011
externquery_conn_funcquery_conn_ptr;
12+
externfree_result_v2_funcfree_result_v2_ptr;
1113

1214
externvoid*chdb_handle;
1315

1416
voidinit_chdb_handle();
1517

16-
#endif
18+
#endif

‎ext/chdb/connection.c‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
#include"include/chdb.h"
77
#include"local_result.h"
88

9-
staticvoidconnection_free(void*ptr)
9+
voidconnection_free(void*ptr)
1010
{
1111
Connection*conn= (Connection*)ptr;
12-
DEBUG_PRINT("Closing connection: %p", (void*)conn->c_conn);
12+
DEBUG_PRINT("Closing connection in connection_free: %p", (void*)conn->c_conn);
13+
1314
if (conn->c_conn)
1415
{
1516
close_conn_ptr(conn->c_conn);
@@ -67,7 +68,6 @@ VALUE connection_initialize(VALUE self, VALUE argc, VALUE argv)
6768
}
6869

6970
xfree(c_argv);
70-
rb_gc_unregister_address(&argv);
7171
returnself;
7272
}
7373

@@ -93,6 +93,7 @@ VALUE connection_query(VALUE self, VALUE query, VALUE format)
9393
if (c_result->error_message)
9494
{
9595
VALUEerror_message=rb_str_new_cstr(c_result->error_message);
96+
free_result_v2_ptr(c_result);
9697
rb_raise(cChDBError,"CHDB error: %s",StringValueCStr(error_message));
9798
}
9899

@@ -108,8 +109,9 @@ VALUE connection_close(VALUE self)
108109
{
109110
Connection*conn;
110111
TypedData_Get_Struct(self,Connection,&ConnectionType,conn);
112+
DEBUG_PRINT("Closing connection in connection_close: %p", (void*)conn->c_conn);
111113

112-
if (conn->c_conn)
114+
if (conn&&conn->c_conn)
113115
{
114116
close_conn_ptr(conn->c_conn);
115117
conn->c_conn=NULL;

‎ext/chdb/constants.c‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include"constants.h"
2+
3+
#include<ruby.h>
4+
5+
voidinit_chdb_constants()
6+
{
7+
VALUEmChDB=rb_define_module("ChDB");
8+
VALUEmChDBConstants=rb_define_module_under(mChDB,"Constants");
9+
VALUEmmChDBOpen=rb_define_module_under(mChDBConstants,"Open");
10+
11+
rb_define_const(mmChDBOpen,"READONLY",INT2FIX(CHDB_OPEN_READONLY));
12+
rb_define_const(mmChDBOpen,"READWRITE",INT2FIX(CHDB_OPEN_READWRITE));
13+
rb_define_const(mmChDBOpen,"CREATE",INT2FIX(CHDB_OPEN_CREATE));
14+
}

‎ext/chdb/constants.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,6 @@
1212
#defineDEBUG_PRINT(fmt, ...) ((void)0)
1313
#endif
1414

15+
voidinit_chdb_constants();
16+
1517
#endif

‎ext/chdb/exception.c‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ void init_exception()
1212
else
1313
{
1414
cChDBError=rb_define_class_under(mChDB,"Exception",rb_eStandardError);
15+
rb_global_variable(&cChDBError);
1516
}
1617
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp