chdb 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +178 -0
- data/Rakefile +12 -0
- data/chdb.gemspec +39 -0
- data/ext/chdb/chdb.c +123 -0
- data/ext/chdb/extconf.rb +14 -0
- data/lib/chdb/local_result.rb +74 -0
- data/lib/chdb/session.rb +66 -0
- data/lib/chdb/version.rb +5 -0
- data/lib/chdb.rb +48 -0
- metadata +59 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0bfc7331198cabffa295674625f913fb6092c9ede2a263aa9a95794c055e0179
|
4
|
+
data.tar.gz: 10ec8acedb049d14a45077ad2fee6b877ce6575719019b49ce0e06ea75e83aa6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d09ffbf109f543df962ea69135e7d75749abb480f9b2c331b80ab7eb0ae6371b2eb239c11d2ab64a807f30cf0b8c0f117880197b2ec8e0a08bec3adce0c1b00e
|
7
|
+
data.tar.gz: 3c6f26c064a6a512afbd11d5755c5c4d569d0909434f67474e18e7256caf1dc6547d0ccfce4d142f02afde60916911704b9293310b54de37f16336bf9de25190
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2024 Gerardo Ortega
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
# Chdb Ruby Gem
|
2
|
+
|
3
|
+
Chdb is a Ruby gem that provides a direct interface to the [chDB](https://clickhouse.com/docs/en/chdb), an in-process SQL OLAP Engine powered by ClickHouse. It allows you to execute SQL queries and manage database sessions directly from your Ruby applications.
|
4
|
+
|
5
|
+
## Status
|
6
|
+
|
7
|
+
This gem is under development and the API is not stable yet.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
First of all, you need to install the chDB engine.
|
12
|
+
|
13
|
+
**Install libchdb**
|
14
|
+
|
15
|
+
```bash
|
16
|
+
curl -sL https://lib.chdb.io | bash
|
17
|
+
```
|
18
|
+
|
19
|
+
**Install the gem**
|
20
|
+
|
21
|
+
To install the gem, add the following line to your application's Gemfile:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
gem 'chdb'
|
25
|
+
```
|
26
|
+
|
27
|
+
Then, execute:
|
28
|
+
|
29
|
+
```bash
|
30
|
+
bundle install
|
31
|
+
```
|
32
|
+
|
33
|
+
If you are not using bundler, you can install the gem by running:
|
34
|
+
|
35
|
+
```bash
|
36
|
+
gem install chdb
|
37
|
+
```
|
38
|
+
|
39
|
+
Make sure you have the `chdb` C library installed on your system.
|
40
|
+
|
41
|
+
## Usage
|
42
|
+
|
43
|
+
The `Chdb` gem provides two main ways to interact with the chDB engine:
|
44
|
+
|
45
|
+
1. **Direct Query Execution:** Execute queries directly against the engine.
|
46
|
+
2. **Session-based Query Execution:** Execute queries within a session, providing temporary storage and resource cleanup.
|
47
|
+
|
48
|
+
### Direct Query Execution
|
49
|
+
|
50
|
+
The `Chdb.query` method allows you to execute SQL queries and get results.
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
require 'chdb'
|
54
|
+
|
55
|
+
# Execute a simple query
|
56
|
+
result = Chdb.query("SELECT 1")
|
57
|
+
puts result.buf # => The result as a string
|
58
|
+
|
59
|
+
# Execute a query with CSV output
|
60
|
+
result = Chdb.query("SELECT 1 AS a, 'test' AS b", "CSV")
|
61
|
+
puts result.rows # => An array of hashes with the data
|
62
|
+
puts result.columns # => The columns of the result
|
63
|
+
|
64
|
+
# Execute a query with JSON output
|
65
|
+
result = Chdb.query("SELECT 1 AS a, 'test' AS b", "JSON")
|
66
|
+
puts result.rows # => An array of hashes with the data
|
67
|
+
puts result.columns # => The columns of the result
|
68
|
+
|
69
|
+
# Handle errors
|
70
|
+
begin
|
71
|
+
Chdb.query("SELECT invalid syntax")
|
72
|
+
rescue Chdb::Error => e
|
73
|
+
puts "Error executing query: #{e.message}"
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
**Parameters:**
|
78
|
+
|
79
|
+
* `query_str` (String): The SQL query to be executed.
|
80
|
+
* `output_format` (String, optional): The output format for the query results, can be `"CSV"`, `"JSON"`, or `"debug"`. The default value is `"CSV"`.
|
81
|
+
|
82
|
+
**Returns:**
|
83
|
+
|
84
|
+
* A `Chdb::LocalResult` object, containing the query results, buffer, elapsed time, rows and columns.
|
85
|
+
|
86
|
+
**Output Formats:**
|
87
|
+
|
88
|
+
* **CSV:** Results are returned as CSV strings, parsed into an array of hashes.
|
89
|
+
* **JSON:** Results are returned as JSON strings, parsed into an array of hashes.
|
90
|
+
* **debug:** Results are returned as CSV format, with debug information.
|
91
|
+
|
92
|
+
### Session-based Query Execution
|
93
|
+
|
94
|
+
The `Chdb::Session` class allows you to execute queries within a session, enabling creation of temporary tables and other resources. Temporary sessions are automatically cleaned up when the session object is closed.
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
require 'chdb'
|
98
|
+
|
99
|
+
# Create a new session
|
100
|
+
session = Chdb::Session.new
|
101
|
+
|
102
|
+
# Create a table
|
103
|
+
session.query("CREATE TABLE IF NOT EXISTS my_table (a Int, b String) ENGINE = Memory")
|
104
|
+
|
105
|
+
# Insert some values
|
106
|
+
session.query("INSERT INTO my_table VALUES (1, 'one'), (2, 'two')")
|
107
|
+
|
108
|
+
# Query the created table
|
109
|
+
result = session.query("SELECT * FROM my_table")
|
110
|
+
puts result.rows # => An array of hashes with the data
|
111
|
+
|
112
|
+
# Close the session. The temporary directory is cleaned
|
113
|
+
session.close
|
114
|
+
|
115
|
+
# Using a presistent path
|
116
|
+
session = Chdb::Session.new("my_persistent_db")
|
117
|
+
# ...
|
118
|
+
session.close
|
119
|
+
```
|
120
|
+
|
121
|
+
**Parameters:**
|
122
|
+
|
123
|
+
* `path` (String, optional): The path to the database. If `nil` or empty, a temporary directory will be used, which is cleaned up when the session is closed.
|
124
|
+
|
125
|
+
**Methods:**
|
126
|
+
|
127
|
+
* `query(query_str, output_format = "CSV")`: Executes an SQL query within the session.
|
128
|
+
* `query_str` (String): The SQL query to be executed.
|
129
|
+
* `output_format` (String, optional): The output format for the query results, can be `"CSV"`, `"JSON"`, or `"debug"`. The default value is `"CSV"`.
|
130
|
+
* `close`: Closes the session. Cleans up the temporary directory if it was created by the session.
|
131
|
+
* `cleanup`: Removes the session directory, it's called automatically if the session is temporal
|
132
|
+
|
133
|
+
## LocalResult Class
|
134
|
+
|
135
|
+
The `Chdb::LocalResult` class represents the result of a chDB query.
|
136
|
+
|
137
|
+
**Attributes:**
|
138
|
+
|
139
|
+
* `buf`: The raw result buffer as a string.
|
140
|
+
* `elapsed`: The time elapsed for the query execution.
|
141
|
+
* `rows`: An array of hashes, representing the result rows.
|
142
|
+
* `columns`: An array of strings representing the column names in the result.
|
143
|
+
* `output_format`: The output format used for the query.
|
144
|
+
|
145
|
+
**Methods:**
|
146
|
+
|
147
|
+
* `each(&block)`: Allows iteration over the rows.
|
148
|
+
* `to_s`: Returns the buffer as a string
|
149
|
+
|
150
|
+
## Error Handling
|
151
|
+
|
152
|
+
The gem defines a custom error class `Chdb::Error`, that is raised when the chDB engine returns an error.
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
begin
|
156
|
+
Chdb.query("SELECT invalid syntax")
|
157
|
+
rescue Chdb::Error => e
|
158
|
+
puts "Error: #{e.message}"
|
159
|
+
end
|
160
|
+
```
|
161
|
+
|
162
|
+
## Development
|
163
|
+
|
164
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
165
|
+
|
166
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
167
|
+
|
168
|
+
## Contributing
|
169
|
+
|
170
|
+
Bug reports and pull requests are welcome on GitHub at [https://github.com/g3ortega/chdb](https://github.com/g3ortega/chdb). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/g3ortega/chdb/blob/main/CODE_OF_CONDUCT.md).
|
171
|
+
|
172
|
+
## License
|
173
|
+
|
174
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
175
|
+
|
176
|
+
## Code of Conduct
|
177
|
+
|
178
|
+
Everyone interacting in the Chdb project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/g3ortega/chdb/blob/main/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
data/chdb.gemspec
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/chdb/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "chdb"
|
7
|
+
spec.version = Chdb::VERSION
|
8
|
+
spec.authors = ["Gerardo Ortega"]
|
9
|
+
spec.email = ["g3ortega@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "Chdb implementation in Ruby"
|
12
|
+
spec.description = "Ruby interface for the chDB in-process SQL OLAP Engine, " \
|
13
|
+
"providing direct query execution and session management capabilities."
|
14
|
+
spec.homepage = "https://github.com/g3ortega/chdb"
|
15
|
+
spec.license = "MIT"
|
16
|
+
spec.required_ruby_version = ">= 3.0.0"
|
17
|
+
|
18
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
19
|
+
|
20
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
21
|
+
spec.metadata["source_code_uri"] = "https://github.com/g3ortega/chdb"
|
22
|
+
spec.metadata["changelog_uri"] = "https://github.com/g3ortega/chdb/blob/main/CHANGELOG.md"
|
23
|
+
|
24
|
+
# Specify which files should be added to the gem when it is released.
|
25
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
26
|
+
spec.files = Dir["lib/**/*.rb", "ext/**/*.{c,h}", "ext/**/Makefile", "README.md", "LICENSE.txt", "Rakefile",
|
27
|
+
"chdb.gemspec"]
|
28
|
+
|
29
|
+
spec.bindir = "exe"
|
30
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
31
|
+
spec.require_paths = ["lib"]
|
32
|
+
|
33
|
+
spec.extensions = ["ext/chdb/extconf.rb"]
|
34
|
+
# Uncomment to register a new dependency of your gem
|
35
|
+
# spec.add_dependency "example-gem", "~> 1.0"
|
36
|
+
|
37
|
+
# For more information and examples about making a new gem, check out our
|
38
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
39
|
+
end
|
data/ext/chdb/chdb.c
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include "chdb.h"
|
3
|
+
|
4
|
+
// Debug configuration
|
5
|
+
#define CHDB_DEBUG 0
|
6
|
+
#if CHDB_DEBUG
|
7
|
+
#define DEBUG_PRINT(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__)
|
8
|
+
#else
|
9
|
+
#define DEBUG_PRINT(fmt, ...) ((void)0)
|
10
|
+
#endif
|
11
|
+
|
12
|
+
// Constants
|
13
|
+
#define CHDB_MAX_ARGS 256
|
14
|
+
|
15
|
+
VALUE cChdbError;
|
16
|
+
|
17
|
+
// Define a Ruby wrapper for `local_result_v2`
|
18
|
+
typedef struct {
|
19
|
+
struct local_result_v2 *c_result;
|
20
|
+
} LocalResult;
|
21
|
+
|
22
|
+
static void local_result_free(void *ptr) {
|
23
|
+
LocalResult *result = (LocalResult *)ptr;
|
24
|
+
DEBUG_PRINT("Freeing LocalResult: %p", (void*)result);
|
25
|
+
if (result->c_result) {
|
26
|
+
free_result_v2(result->c_result);
|
27
|
+
}
|
28
|
+
free(result);
|
29
|
+
}
|
30
|
+
|
31
|
+
static VALUE local_result_alloc(VALUE klass) {
|
32
|
+
LocalResult *result = ALLOC(LocalResult);
|
33
|
+
DEBUG_PRINT("Allocating LocalResult: %p", (void*)result);
|
34
|
+
result->c_result = NULL;
|
35
|
+
return Data_Wrap_Struct(klass, NULL, local_result_free, result);
|
36
|
+
}
|
37
|
+
|
38
|
+
static VALUE local_result_initialize(VALUE self, VALUE argc, VALUE argv) {
|
39
|
+
DEBUG_PRINT("Initializing LocalResult with %d arguments", NUM2INT(argc));
|
40
|
+
|
41
|
+
// Type checking
|
42
|
+
Check_Type(argc, T_FIXNUM);
|
43
|
+
Check_Type(argv, T_ARRAY);
|
44
|
+
|
45
|
+
int c_argc = NUM2INT(argc);
|
46
|
+
if (c_argc < 0) {
|
47
|
+
rb_raise(rb_eArgError, "Argument count cannot be negative");
|
48
|
+
}
|
49
|
+
if (c_argc > CHDB_MAX_ARGS) {
|
50
|
+
rb_raise(rb_eArgError, "Too many arguments (max: %d)", CHDB_MAX_ARGS);
|
51
|
+
}
|
52
|
+
|
53
|
+
char **c_argv = ALLOC_N(char *, c_argc);
|
54
|
+
|
55
|
+
for (int i = 0; i < c_argc; i++) {
|
56
|
+
VALUE arg = rb_ary_entry(argv, i);
|
57
|
+
c_argv[i] = StringValueCStr(arg);
|
58
|
+
}
|
59
|
+
|
60
|
+
LocalResult *result;
|
61
|
+
Data_Get_Struct(self, LocalResult, result);
|
62
|
+
|
63
|
+
// Execute query
|
64
|
+
result->c_result = query_stable_v2(c_argc, c_argv);
|
65
|
+
|
66
|
+
if (!result->c_result) {
|
67
|
+
xfree(c_argv);
|
68
|
+
rb_gc_unregister_address(&argv);
|
69
|
+
rb_raise(cChdbError, "chDB query returned nil");
|
70
|
+
}
|
71
|
+
|
72
|
+
if (result->c_result->error_message) {
|
73
|
+
VALUE error_message = rb_str_new_cstr(result->c_result->error_message);
|
74
|
+
// Create error context
|
75
|
+
VALUE context = rb_hash_new();
|
76
|
+
rb_hash_aset(context, ID2SYM(rb_intern("args")), argv);
|
77
|
+
rb_hash_aset(context, ID2SYM(rb_intern("error")), error_message);
|
78
|
+
|
79
|
+
xfree(c_argv);
|
80
|
+
rb_gc_unregister_address(&argv);
|
81
|
+
rb_raise(cChdbError, "chDB error: %s", StringValueCStr(error_message));
|
82
|
+
}
|
83
|
+
|
84
|
+
xfree(c_argv);
|
85
|
+
rb_gc_unregister_address(&argv);
|
86
|
+
DEBUG_PRINT("LocalResult initialization complete");
|
87
|
+
return self;
|
88
|
+
}
|
89
|
+
|
90
|
+
static VALUE local_result_buf(VALUE self) {
|
91
|
+
LocalResult *result;
|
92
|
+
Data_Get_Struct(self, LocalResult, result);
|
93
|
+
|
94
|
+
if (!result->c_result || !result->c_result->buf) {
|
95
|
+
DEBUG_PRINT("Buffer access attempted on empty result");
|
96
|
+
return Qnil;
|
97
|
+
}
|
98
|
+
|
99
|
+
DEBUG_PRINT("Returning buffer of length %zu", result->c_result->len);
|
100
|
+
return rb_str_new(result->c_result->buf, result->c_result->len);
|
101
|
+
}
|
102
|
+
|
103
|
+
static VALUE local_result_elapsed(VALUE self) {
|
104
|
+
LocalResult *result;
|
105
|
+
Data_Get_Struct(self, LocalResult, result);
|
106
|
+
DEBUG_PRINT("Query elapsed time: %f", result->c_result->elapsed);
|
107
|
+
return DBL2NUM(result->c_result->elapsed);
|
108
|
+
}
|
109
|
+
|
110
|
+
void Init_chdb() {
|
111
|
+
DEBUG_PRINT("Initializing chdb extension");
|
112
|
+
|
113
|
+
VALUE mChdb = rb_define_module("Chdb");
|
114
|
+
cChdbError = rb_define_class_under(mChdb, "Error", rb_eStandardError);
|
115
|
+
|
116
|
+
VALUE cLocalResult = rb_define_class_under(mChdb, "LocalResult", rb_cObject);
|
117
|
+
rb_define_alloc_func(cLocalResult, local_result_alloc);
|
118
|
+
rb_define_method(cLocalResult, "initialize", local_result_initialize, 2);
|
119
|
+
rb_define_method(cLocalResult, "buf", local_result_buf, 0);
|
120
|
+
rb_define_method(cLocalResult, "elapsed", local_result_elapsed, 0);
|
121
|
+
|
122
|
+
DEBUG_PRINT("chdb extension initialized successfully");
|
123
|
+
}
|
data/ext/chdb/extconf.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mkmf"
|
4
|
+
|
5
|
+
# First try to find system-wide installations
|
6
|
+
system_found = have_library("chdb") && have_header("chdb.h")
|
7
|
+
|
8
|
+
unless system_found
|
9
|
+
# Abort if not found
|
10
|
+
abort "chdb.h or chdb library not found! Please install chdb development files"
|
11
|
+
end
|
12
|
+
|
13
|
+
# Create Makefile
|
14
|
+
create_makefile("chdb/chdb")
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "csv"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
module Chdb
|
7
|
+
# Handles ClickHouse query results with support for CSV and JSON formats,
|
8
|
+
# providing enumerable access to result rows and column information
|
9
|
+
class LocalResult
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
attr_accessor :output_format
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
buf
|
16
|
+
end
|
17
|
+
|
18
|
+
def rows
|
19
|
+
@rows ||= parse_buffer
|
20
|
+
end
|
21
|
+
|
22
|
+
def columns
|
23
|
+
@columns ||= extract_columns
|
24
|
+
end
|
25
|
+
|
26
|
+
def each(&block)
|
27
|
+
rows.each(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def parse_buffer
|
33
|
+
return [] if buf.nil? || buf.empty?
|
34
|
+
|
35
|
+
case output_format&.downcase
|
36
|
+
when "csv" then parse_csv
|
37
|
+
when "json" then parse_json
|
38
|
+
else
|
39
|
+
raise Chdb::Error, "Unsupported output format: #{output_format}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse_csv
|
44
|
+
csv_options = { headers: true }
|
45
|
+
CSV.parse(buf, **csv_options).map(&:to_h)
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_json
|
49
|
+
parsed = JSON.parse(buf)
|
50
|
+
parsed["data"] || []
|
51
|
+
end
|
52
|
+
|
53
|
+
def extract_columns
|
54
|
+
return [] if buf.nil? || buf.empty?
|
55
|
+
|
56
|
+
case output_format&.downcase
|
57
|
+
when "csv" then extract_csv_columns
|
58
|
+
when "json" then extract_json_columns
|
59
|
+
else
|
60
|
+
raise Chdb::Error, "Unsupported output format: #{output_format}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def extract_csv_columns
|
65
|
+
csv_options = { headers: true }
|
66
|
+
CSV.parse(buf, **csv_options).headers
|
67
|
+
end
|
68
|
+
|
69
|
+
def extract_json_columns
|
70
|
+
first_row = parse_buffer.first
|
71
|
+
first_row ? first_row.keys : []
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/chdb/session.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tempfile"
|
4
|
+
require "fileutils"
|
5
|
+
|
6
|
+
module Chdb
|
7
|
+
# Manages ClickHouse database sessions, handling temporary and permanent paths
|
8
|
+
# for query execution and resource cleanup
|
9
|
+
class Session
|
10
|
+
attr_reader :path, :is_temp
|
11
|
+
|
12
|
+
def initialize(path = nil)
|
13
|
+
if path.nil? || path.empty?
|
14
|
+
@path = Dir.mktmpdir("chdb_")
|
15
|
+
@is_temp = true
|
16
|
+
else
|
17
|
+
@path = path
|
18
|
+
@is_temp = false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def query(query_str, output_format = "CSV")
|
23
|
+
output_format ||= "CSV" # Default value
|
24
|
+
query_to_buffer(query_str, output_format, @path, "")
|
25
|
+
end
|
26
|
+
|
27
|
+
def close
|
28
|
+
cleanup if @is_temp && File.basename(@path).start_with?("chdb_")
|
29
|
+
end
|
30
|
+
|
31
|
+
def cleanup
|
32
|
+
FileUtils.remove_entry(@path) if Dir.exist?(@path)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def query_to_buffer(query_str, output_format, path, udf_path)
|
38
|
+
argv = ["clickhouse", "--multiquery"]
|
39
|
+
argv += format_options(output_format)
|
40
|
+
argv << "--path=#{path}" unless path.empty?
|
41
|
+
argv << "--query=#{query_str}"
|
42
|
+
argv += udf_options(udf_path) unless udf_path.empty?
|
43
|
+
|
44
|
+
create_result(argv, output_format)
|
45
|
+
end
|
46
|
+
|
47
|
+
def format_options(output_format)
|
48
|
+
if output_format.casecmp("debug").zero?
|
49
|
+
["--verbose", "--log-level=trace", "--output-format=CSV"]
|
50
|
+
else
|
51
|
+
["--output-format=#{output_format}"]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def udf_options(udf_path)
|
56
|
+
["--", "--user_scripts_path=#{udf_path}",
|
57
|
+
"--user_defined_executable_functions_config=#{udf_path}/*.xml"]
|
58
|
+
end
|
59
|
+
|
60
|
+
def create_result(argv, output_format)
|
61
|
+
result = Chdb::LocalResult.new(argv.length, argv)
|
62
|
+
result.output_format = output_format
|
63
|
+
result
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/chdb/version.rb
ADDED
data/lib/chdb.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "chdb/version"
|
4
|
+
require "chdb/chdb"
|
5
|
+
require "chdb/local_result"
|
6
|
+
require "chdb/session"
|
7
|
+
|
8
|
+
# Ruby interface for ClickHouse database, providing direct query execution
|
9
|
+
# and session management capabilities
|
10
|
+
module Chdb
|
11
|
+
class << self
|
12
|
+
def query(query_str, output_format = "CSV")
|
13
|
+
output_format ||= "CSV" # Default value
|
14
|
+
query_to_buffer(query_str, output_format, "", "")
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def query_to_buffer(query_str, output_format, path, udf_path)
|
20
|
+
argv = ["clickhouse", "--multiquery"]
|
21
|
+
argv << "--path=#{path}" unless path.empty?
|
22
|
+
argv << "--query=#{build_query_string(query_str, output_format)}"
|
23
|
+
argv += udf_options(udf_path) unless udf_path.empty?
|
24
|
+
|
25
|
+
create_result(argv, output_format)
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_query_string(query_str, output_format)
|
29
|
+
format_suffix = case output_format.downcase
|
30
|
+
when "csv" then " FORMAT CSVWithNames"
|
31
|
+
when "json" then " FORMAT JSON"
|
32
|
+
else ""
|
33
|
+
end
|
34
|
+
query_str + format_suffix
|
35
|
+
end
|
36
|
+
|
37
|
+
def udf_options(udf_path)
|
38
|
+
["--", "--user_scripts_path=#{udf_path}",
|
39
|
+
"--user_defined_executable_functions_config=#{udf_path}/*.xml"]
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_result(argv, output_format)
|
43
|
+
result = LocalResult.new(argv.length, argv)
|
44
|
+
result.output_format = output_format
|
45
|
+
result
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: chdb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gerardo Ortega
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-12-20 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Ruby interface for the chDB in-process SQL OLAP Engine, providing direct
|
14
|
+
query execution and session management capabilities.
|
15
|
+
email:
|
16
|
+
- g3ortega@gmail.com
|
17
|
+
executables: []
|
18
|
+
extensions:
|
19
|
+
- ext/chdb/extconf.rb
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- LICENSE.txt
|
23
|
+
- README.md
|
24
|
+
- Rakefile
|
25
|
+
- chdb.gemspec
|
26
|
+
- ext/chdb/chdb.c
|
27
|
+
- ext/chdb/extconf.rb
|
28
|
+
- lib/chdb.rb
|
29
|
+
- lib/chdb/local_result.rb
|
30
|
+
- lib/chdb/session.rb
|
31
|
+
- lib/chdb/version.rb
|
32
|
+
homepage: https://github.com/g3ortega/chdb
|
33
|
+
licenses:
|
34
|
+
- MIT
|
35
|
+
metadata:
|
36
|
+
allowed_push_host: https://rubygems.org
|
37
|
+
homepage_uri: https://github.com/g3ortega/chdb
|
38
|
+
source_code_uri: https://github.com/g3ortega/chdb
|
39
|
+
changelog_uri: https://github.com/g3ortega/chdb/blob/main/CHANGELOG.md
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 3.0.0
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubygems_version: 3.5.9
|
56
|
+
signing_key:
|
57
|
+
specification_version: 4
|
58
|
+
summary: Chdb implementation in Ruby
|
59
|
+
test_files: []
|