clickhouse-native 0.7.0 → 0.9.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 +4 -4
- data/ext/clickhouse_native/client.cpp +73 -28
- data/lib/clickhouse_native/logging.rb +4 -4
- data/lib/clickhouse_native/pool.rb +27 -12
- data/lib/clickhouse_native/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7e0bcd3868daa1a69af028ff62e4c0104c82ceac165acfa12f40418259d74929
|
|
4
|
+
data.tar.gz: e40d67544c4b9fb99fbf0ecebf655dd572055fc4d8301521cf0958af1a4531c1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ace9d3e809fa558064f42d2ee66bce667f8514fb06f88d2eb2e24c09f244994e32365acae4f3a3c110025f6a3de947a242d766a72fa72dfaa560fbaf8825c9a0
|
|
7
|
+
data.tar.gz: a935ba30d5f897254492157e82bf98be08e202cd8b619de923c43b0b25ae07f3112d98b20768152dcd44c605eb57db7220e70c240e318415c9992bfb7bd8bf1b
|
|
@@ -694,6 +694,38 @@ static CompressionMethod kwarg_compression(VALUE kwargs) {
|
|
|
694
694
|
rb_id2name(sym));
|
|
695
695
|
}
|
|
696
696
|
|
|
697
|
+
// Stringify a Ruby setting value for the binary protocol's QuerySettings
|
|
698
|
+
// payload (which is wire-string-typed). Bool maps to "1"/"0" the way the
|
|
699
|
+
// HTTP gem rendered it; everything else goes through #to_s.
|
|
700
|
+
static std::string stringify_setting_value(VALUE v) {
|
|
701
|
+
if (v == Qtrue) return "1";
|
|
702
|
+
if (v == Qfalse) return "0";
|
|
703
|
+
VALUE s = rb_funcall(v, rb_intern("to_s"), 0);
|
|
704
|
+
StringValue(s);
|
|
705
|
+
return std::string(RSTRING_PTR(s), RSTRING_LEN(s));
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
static int apply_settings_cb(VALUE key, VALUE val, VALUE arg) {
|
|
709
|
+
auto* q = reinterpret_cast<Query*>(arg);
|
|
710
|
+
VALUE k = SYMBOL_P(key) ? rb_sym2str(key) : key;
|
|
711
|
+
StringValue(k);
|
|
712
|
+
q->SetSetting(
|
|
713
|
+
std::string(RSTRING_PTR(k), RSTRING_LEN(k)),
|
|
714
|
+
QuerySettingsField{stringify_setting_value(val), 0});
|
|
715
|
+
return ST_CONTINUE;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// Read a `settings:` Hash out of the parsed kwargs and stamp each entry
|
|
719
|
+
// onto `q` as a per-query setting. No-op if kwargs is nil or settings is
|
|
720
|
+
// missing/empty. Raises TypeError if settings is not a Hash.
|
|
721
|
+
static void apply_settings(Query& q, VALUE kwargs) {
|
|
722
|
+
if (NIL_P(kwargs)) return;
|
|
723
|
+
VALUE settings = rb_hash_lookup2(kwargs, ID2SYM(rb_intern("settings")), Qnil);
|
|
724
|
+
if (NIL_P(settings)) return;
|
|
725
|
+
Check_Type(settings, T_HASH);
|
|
726
|
+
rb_hash_foreach(settings, apply_settings_cb, reinterpret_cast<VALUE>(&q));
|
|
727
|
+
}
|
|
728
|
+
|
|
697
729
|
// Client.new(host:, port:, database:, user:, password:)
|
|
698
730
|
static VALUE ch_client_initialize(int argc, VALUE* argv, VALUE self) {
|
|
699
731
|
VALUE kwargs = Qnil;
|
|
@@ -734,7 +766,7 @@ static VALUE ch_client_initialize(int argc, VALUE* argv, VALUE self) {
|
|
|
734
766
|
namespace {
|
|
735
767
|
struct ExecuteNoGVL {
|
|
736
768
|
Client* client;
|
|
737
|
-
|
|
769
|
+
const Query* query;
|
|
738
770
|
std::exception_ptr err;
|
|
739
771
|
};
|
|
740
772
|
} // namespace
|
|
@@ -742,7 +774,7 @@ struct ExecuteNoGVL {
|
|
|
742
774
|
static void* execute_no_gvl(void* data) {
|
|
743
775
|
auto* a = static_cast<ExecuteNoGVL*>(data);
|
|
744
776
|
try {
|
|
745
|
-
a->client->Execute(
|
|
777
|
+
a->client->Execute(*a->query);
|
|
746
778
|
} catch (...) {
|
|
747
779
|
a->err = std::current_exception();
|
|
748
780
|
}
|
|
@@ -756,12 +788,17 @@ static void execute_unblock(void* data) {
|
|
|
756
788
|
try { a->client->ResetConnection(); } catch (...) {}
|
|
757
789
|
}
|
|
758
790
|
|
|
759
|
-
static VALUE ch_client_execute(VALUE
|
|
791
|
+
static VALUE ch_client_execute(int argc, VALUE* argv, VALUE self) {
|
|
792
|
+
VALUE rb_sql, kwargs = Qnil;
|
|
793
|
+
rb_scan_args(argc, argv, "1:", &rb_sql, &kwargs);
|
|
760
794
|
Check_Type(rb_sql, T_STRING);
|
|
761
795
|
CHClient* c = as_client(self);
|
|
762
796
|
if (!c->client) rb_raise(err_connection, "clickhouse-native: client is closed");
|
|
763
797
|
|
|
764
|
-
|
|
798
|
+
Query q(std::string(RSTRING_PTR(rb_sql), RSTRING_LEN(rb_sql)));
|
|
799
|
+
apply_settings(q, kwargs);
|
|
800
|
+
|
|
801
|
+
ExecuteNoGVL args{c->client.get(), &q, nullptr};
|
|
765
802
|
rb_thread_call_without_gvl(execute_no_gvl, &args, execute_unblock, &args);
|
|
766
803
|
if (args.err) {
|
|
767
804
|
// clickhouse-cpp may leave the read stream partially consumed when the
|
|
@@ -779,16 +816,19 @@ static VALUE ch_client_execute(VALUE self, VALUE rb_sql) {
|
|
|
779
816
|
// See query_each below for a streaming, GVL-releasing variant.
|
|
780
817
|
// ------------------------------------------------------------------
|
|
781
818
|
|
|
782
|
-
static VALUE ch_client_query(VALUE
|
|
819
|
+
static VALUE ch_client_query(int argc, VALUE* argv, VALUE self) {
|
|
820
|
+
VALUE rb_sql, kwargs = Qnil;
|
|
821
|
+
rb_scan_args(argc, argv, "1:", &rb_sql, &kwargs);
|
|
783
822
|
Check_Type(rb_sql, T_STRING);
|
|
784
823
|
CHClient* c = as_client(self);
|
|
785
824
|
if (!c->client) rb_raise(err_connection, "clickhouse-native: client is closed");
|
|
786
825
|
|
|
787
|
-
std::string sql(StringValueCStr(rb_sql));
|
|
788
826
|
VALUE rows = rb_ary_new();
|
|
789
827
|
try {
|
|
790
828
|
std::vector<ID> col_ids;
|
|
791
|
-
|
|
829
|
+
Query q(std::string(RSTRING_PTR(rb_sql), RSTRING_LEN(rb_sql)));
|
|
830
|
+
apply_settings(q, kwargs);
|
|
831
|
+
q.OnData([&](const Block& block) {
|
|
792
832
|
size_t ncols = block.GetColumnCount();
|
|
793
833
|
size_t nrows = block.GetRowCount();
|
|
794
834
|
if (nrows == 0) return;
|
|
@@ -808,6 +848,7 @@ static VALUE ch_client_query(VALUE self, VALUE rb_sql) {
|
|
|
808
848
|
rb_ary_push(rows, h);
|
|
809
849
|
}
|
|
810
850
|
});
|
|
851
|
+
c->client->Execute(q);
|
|
811
852
|
return rows;
|
|
812
853
|
} catch (const std::exception& e) {
|
|
813
854
|
try { c->client->ResetConnection(); } catch (...) {}
|
|
@@ -820,21 +861,25 @@ static VALUE ch_client_query(VALUE self, VALUE rb_sql) {
|
|
|
820
861
|
// query_value — returns the first cell of the first row, or nil
|
|
821
862
|
// ------------------------------------------------------------------
|
|
822
863
|
|
|
823
|
-
static VALUE ch_client_query_value(VALUE
|
|
864
|
+
static VALUE ch_client_query_value(int argc, VALUE* argv, VALUE self) {
|
|
865
|
+
VALUE rb_sql, kwargs = Qnil;
|
|
866
|
+
rb_scan_args(argc, argv, "1:", &rb_sql, &kwargs);
|
|
824
867
|
Check_Type(rb_sql, T_STRING);
|
|
825
868
|
CHClient* c = as_client(self);
|
|
826
869
|
if (!c->client) rb_raise(err_connection, "clickhouse-native: client is closed");
|
|
827
870
|
|
|
828
|
-
std::string sql(StringValueCStr(rb_sql));
|
|
829
871
|
try {
|
|
830
872
|
VALUE out = Qnil;
|
|
831
873
|
bool seen = false;
|
|
832
|
-
|
|
874
|
+
Query q(std::string(RSTRING_PTR(rb_sql), RSTRING_LEN(rb_sql)));
|
|
875
|
+
apply_settings(q, kwargs);
|
|
876
|
+
q.OnData([&](const Block& block) {
|
|
833
877
|
if (seen) return;
|
|
834
878
|
if (block.GetRowCount() == 0 || block.GetColumnCount() == 0) return;
|
|
835
879
|
out = value_at(block[0], 0, block.GetColumnType(0));
|
|
836
880
|
seen = true;
|
|
837
881
|
});
|
|
882
|
+
c->client->Execute(q);
|
|
838
883
|
return out;
|
|
839
884
|
} catch (const std::exception& e) {
|
|
840
885
|
try { c->client->ResetConnection(); } catch (...) {}
|
|
@@ -958,7 +1003,7 @@ struct YieldBlockArgs {
|
|
|
958
1003
|
|
|
959
1004
|
struct QueryEachNoGVL {
|
|
960
1005
|
Client* client;
|
|
961
|
-
|
|
1006
|
+
const Query* query;
|
|
962
1007
|
QueryEachState* state;
|
|
963
1008
|
std::exception_ptr err;
|
|
964
1009
|
};
|
|
@@ -1003,12 +1048,7 @@ static void* with_gvl_yield(void* data) {
|
|
|
1003
1048
|
static void* query_each_no_gvl(void* data) {
|
|
1004
1049
|
auto* a = static_cast<QueryEachNoGVL*>(data);
|
|
1005
1050
|
try {
|
|
1006
|
-
a->client->
|
|
1007
|
-
if (a->state->aborted) return false;
|
|
1008
|
-
YieldBlockArgs ya{&block, a->state};
|
|
1009
|
-
rb_thread_call_with_gvl(with_gvl_yield, &ya);
|
|
1010
|
-
return !a->state->aborted;
|
|
1011
|
-
});
|
|
1051
|
+
a->client->Execute(*a->query);
|
|
1012
1052
|
} catch (...) {
|
|
1013
1053
|
a->err = std::current_exception();
|
|
1014
1054
|
}
|
|
@@ -1021,19 +1061,24 @@ static void query_each_unblock(void* data) {
|
|
|
1021
1061
|
try { a->client->ResetConnection(); } catch (...) {}
|
|
1022
1062
|
}
|
|
1023
1063
|
|
|
1024
|
-
static VALUE ch_client_query_each(VALUE
|
|
1064
|
+
static VALUE ch_client_query_each(int argc, VALUE* argv, VALUE self) {
|
|
1025
1065
|
rb_need_block();
|
|
1066
|
+
VALUE rb_sql, kwargs = Qnil;
|
|
1067
|
+
rb_scan_args(argc, argv, "1:", &rb_sql, &kwargs);
|
|
1026
1068
|
Check_Type(rb_sql, T_STRING);
|
|
1027
1069
|
CHClient* c = as_client(self);
|
|
1028
1070
|
if (!c->client) rb_raise(err_connection, "clickhouse-native: client is closed");
|
|
1029
1071
|
|
|
1030
1072
|
QueryEachState state{rb_block_proc(), {}, 0, false};
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1073
|
+
Query q(std::string(RSTRING_PTR(rb_sql), RSTRING_LEN(rb_sql)));
|
|
1074
|
+
apply_settings(q, kwargs);
|
|
1075
|
+
q.OnDataCancelable([&state](const Block& block) -> bool {
|
|
1076
|
+
if (state.aborted) return false;
|
|
1077
|
+
YieldBlockArgs ya{&block, &state};
|
|
1078
|
+
rb_thread_call_with_gvl(with_gvl_yield, &ya);
|
|
1079
|
+
return !state.aborted;
|
|
1080
|
+
});
|
|
1081
|
+
QueryEachNoGVL args{c->client.get(), &q, &state, nullptr};
|
|
1037
1082
|
|
|
1038
1083
|
rb_thread_call_without_gvl(query_each_no_gvl, &args, query_each_unblock, &args);
|
|
1039
1084
|
|
|
@@ -1139,13 +1184,13 @@ extern "C" void Init_clickhouse_native(void) {
|
|
|
1139
1184
|
rb_define_method(rb_cClient, "initialize",
|
|
1140
1185
|
reinterpret_cast<VALUE (*)(ANYARGS)>(ch_client_initialize), -1);
|
|
1141
1186
|
rb_define_method(rb_cClient, "execute",
|
|
1142
|
-
reinterpret_cast<VALUE (*)(ANYARGS)>(ch_client_execute), 1);
|
|
1187
|
+
reinterpret_cast<VALUE (*)(ANYARGS)>(ch_client_execute), -1);
|
|
1143
1188
|
rb_define_method(rb_cClient, "query",
|
|
1144
|
-
reinterpret_cast<VALUE (*)(ANYARGS)>(ch_client_query), 1);
|
|
1189
|
+
reinterpret_cast<VALUE (*)(ANYARGS)>(ch_client_query), -1);
|
|
1145
1190
|
rb_define_method(rb_cClient, "query_value",
|
|
1146
|
-
reinterpret_cast<VALUE (*)(ANYARGS)>(ch_client_query_value), 1);
|
|
1191
|
+
reinterpret_cast<VALUE (*)(ANYARGS)>(ch_client_query_value), -1);
|
|
1147
1192
|
rb_define_method(rb_cClient, "query_each",
|
|
1148
|
-
reinterpret_cast<VALUE (*)(ANYARGS)>(ch_client_query_each), 1);
|
|
1193
|
+
reinterpret_cast<VALUE (*)(ANYARGS)>(ch_client_query_each), -1);
|
|
1149
1194
|
rb_define_method(rb_cClient, "insert_block",
|
|
1150
1195
|
reinterpret_cast<VALUE (*)(ANYARGS)>(ch_client_insert_block), 3);
|
|
1151
1196
|
rb_define_method(rb_cClient, "ping",
|
|
@@ -12,19 +12,19 @@ module ClickhouseNative
|
|
|
12
12
|
module Logging
|
|
13
13
|
LEVEL = :debug
|
|
14
14
|
|
|
15
|
-
def execute(sql)
|
|
15
|
+
def execute(sql, **opts)
|
|
16
16
|
log_sql(sql) { super }
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
def query(sql)
|
|
19
|
+
def query(sql, **opts)
|
|
20
20
|
log_sql(sql) { super }
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def query_value(sql)
|
|
23
|
+
def query_value(sql, **opts)
|
|
24
24
|
log_sql(sql) { super }
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
def query_each(sql, &)
|
|
27
|
+
def query_each(sql, **opts, &)
|
|
28
28
|
log_sql(sql) { super }
|
|
29
29
|
end
|
|
30
30
|
|
|
@@ -29,29 +29,44 @@ module ClickhouseNative
|
|
|
29
29
|
# session settings), producing misleading log lines and re-raises in
|
|
30
30
|
# unrelated code. A fresh socket + handshake is cheap relative to
|
|
31
31
|
# debugging that.
|
|
32
|
+
#
|
|
33
|
+
# ConnectionError gets one automatic retry: pooled connections that
|
|
34
|
+
# have been idle long enough for the server / an LB to FIN them
|
|
35
|
+
# surface as "closed" on the very next recv (errno 0, message
|
|
36
|
+
# "closed: Success"). Discarding and re-checking out lands a fresh
|
|
37
|
+
# socket and the operation succeeds. The retry only triggers when
|
|
38
|
+
# the dead-connection error fired before any data was sent, so
|
|
39
|
+
# write operations don't risk double-execution from this path.
|
|
32
40
|
def with
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
attempts = 0
|
|
42
|
+
begin
|
|
43
|
+
@pool.with do |client|
|
|
44
|
+
yield client
|
|
45
|
+
rescue
|
|
46
|
+
@pool.discard_current_connection(&:close)
|
|
47
|
+
raise
|
|
48
|
+
end
|
|
49
|
+
rescue ConnectionError
|
|
50
|
+
attempts += 1
|
|
51
|
+
retry if attempts == 1
|
|
37
52
|
raise
|
|
38
53
|
end
|
|
39
54
|
end
|
|
40
55
|
|
|
41
|
-
def execute(sql)
|
|
42
|
-
with { |c| c.execute(sql) }
|
|
56
|
+
def execute(sql, **opts)
|
|
57
|
+
with { |c| c.execute(sql, **opts) }
|
|
43
58
|
end
|
|
44
59
|
|
|
45
|
-
def query(sql)
|
|
46
|
-
with { |c| c.query(sql) }
|
|
60
|
+
def query(sql, **opts)
|
|
61
|
+
with { |c| c.query(sql, **opts) }
|
|
47
62
|
end
|
|
48
63
|
|
|
49
|
-
def query_each(sql, &block)
|
|
50
|
-
with { |c| c.query_each(sql, &block) }
|
|
64
|
+
def query_each(sql, **opts, &block)
|
|
65
|
+
with { |c| c.query_each(sql, **opts, &block) }
|
|
51
66
|
end
|
|
52
67
|
|
|
53
|
-
def query_value(sql)
|
|
54
|
-
with { |c| c.query_value(sql) }
|
|
68
|
+
def query_value(sql, **opts)
|
|
69
|
+
with { |c| c.query_value(sql, **opts) }
|
|
55
70
|
end
|
|
56
71
|
|
|
57
72
|
def insert(table, rows, **opts)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: clickhouse-native
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yuri Smirnov
|
|
@@ -310,7 +310,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
310
310
|
- !ruby/object:Gem::Version
|
|
311
311
|
version: '0'
|
|
312
312
|
requirements: []
|
|
313
|
-
rubygems_version: 4.0.
|
|
313
|
+
rubygems_version: 4.0.10
|
|
314
314
|
specification_version: 4
|
|
315
315
|
summary: ClickHouse Ruby driver over the native TCP protocol
|
|
316
316
|
test_files: []
|