clickhouse-native 0.9.0 → 0.11.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 +115 -6
- data/ext/clickhouse_native/extconf.rb +30 -3
- data/ext/clickhouse_native/patches/0002-carry-settings-into-insert.patch +82 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/.github/workflows/bazel.yml +120 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/.github/workflows/cross-repo-bug-relay.yml +17 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/.github/workflows/linux.yml +22 -23
- data/ext/clickhouse_native/vendor/clickhouse-cpp/.github/workflows/macos.yml +22 -21
- data/ext/clickhouse_native/vendor/clickhouse-cpp/.github/workflows/windows_mingw.yml +29 -36
- data/ext/clickhouse_native/vendor/clickhouse-cpp/.github/workflows/windows_msvc.yml +29 -36
- data/ext/clickhouse_native/vendor/clickhouse-cpp/.gitignore +6 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/AI_POLICY.md +13 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/BUILD.bazel +167 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/CMakeLists.txt +2 -1
- data/ext/clickhouse_native/vendor/clickhouse-cpp/MODULE.bazel +17 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/MODULE.bazel.lock +503 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/README.md +32 -6
- data/ext/clickhouse_native/vendor/clickhouse-cpp/ci/docker-compose/config.xml +53 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/ci/docker-compose/users.xml +35 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/ci/docker-compose.yml +22 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/CMakeLists.txt +11 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/base/sslsocket.cpp +24 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/block.cpp +1 -1
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/block.h +2 -1
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/client.cpp +293 -136
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/client.h +31 -2
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/array.cpp +12 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/array.h +17 -7
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/bool.cpp +79 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/bool.h +62 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/factory.cpp +16 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/itemview.cpp +2 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/itemview.h +6 -2
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/json.cpp +102 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/json.h +82 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/lowcardinality.cpp +2 -1
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/string.cpp +7 -2
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/tuple.cpp +48 -5
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/tuple.h +14 -1
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/query.h +2 -2
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/server_exception.h +0 -3
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/types/type_parser.cpp +43 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/types/type_parser.h +9 -0
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/types/types.cpp +61 -11
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/types/types.h +18 -2
- data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/version.h +1 -1
- data/lib/clickhouse_native/client.rb +1 -1
- data/lib/clickhouse_native/pool.rb +22 -31
- data/lib/clickhouse_native/version.rb +1 -1
- data/lib/clickhouse_native.rb +1 -0
- metadata +16 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 984a63d34de19388f777cfe71ecd0a844d22f0b67b89e9fddfe07c5c0ac1cc05
|
|
4
|
+
data.tar.gz: 78f829e40f715aef0264e1419cb9b06b97faab1ac38f33329c5da6ada1dd251b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c579da78d2185108712c92e902217c2e508655533c2e78efc7e36ae358a826f809f41d884684b8fcb0e1b36749bc80be5f23b76c85ebf081423adb615d73928b
|
|
7
|
+
data.tar.gz: 93259646a6f150ed271202e3eaca075e0d1cf8d707aed5ac2ac9b530061de47cf1a65082289718857b367dd5b8d764c73cf270c34d080018ce8caac540d8bab6
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#include <clickhouse/columns/decimal.h>
|
|
8
8
|
#include <clickhouse/columns/enum.h>
|
|
9
9
|
#include <clickhouse/columns/factory.h>
|
|
10
|
+
#include <clickhouse/columns/json.h>
|
|
10
11
|
#include <clickhouse/columns/lowcardinality.h>
|
|
11
12
|
#include <clickhouse/columns/map.h>
|
|
12
13
|
#include <clickhouse/columns/numeric.h>
|
|
@@ -16,11 +17,13 @@
|
|
|
16
17
|
#include <clickhouse/exceptions.h>
|
|
17
18
|
#include <clickhouse/types/types.h>
|
|
18
19
|
|
|
20
|
+
#include <chrono>
|
|
19
21
|
#include <cstdint>
|
|
20
22
|
#include <exception>
|
|
21
23
|
#include <memory>
|
|
22
24
|
#include <string>
|
|
23
25
|
#include <system_error>
|
|
26
|
+
#include <utility>
|
|
24
27
|
#include <vector>
|
|
25
28
|
|
|
26
29
|
using namespace clickhouse;
|
|
@@ -188,6 +191,17 @@ static VALUE value_at(const ColumnRef& col, size_t idx, const std::string& decla
|
|
|
188
191
|
return rb_utf8_str_new(sv.data(), sv.size());
|
|
189
192
|
}
|
|
190
193
|
|
|
194
|
+
case Type::JSON: {
|
|
195
|
+
// clickhouse-cpp's ColumnJSON is string-backed (it relies on the
|
|
196
|
+
// server's output_format_native_write_json_as_string). Decode the
|
|
197
|
+
// raw JSON text into a Ruby object so a JSON column reads back like
|
|
198
|
+
// Map does — a Hash — rather than a string the caller must re-parse.
|
|
199
|
+
auto sv = col->As<ColumnJSON>()->At(idx);
|
|
200
|
+
VALUE str = rb_utf8_str_new(sv.data(), sv.size());
|
|
201
|
+
VALUE rb_mJSON = rb_const_get(rb_cObject, rb_intern("JSON"));
|
|
202
|
+
return rb_funcall(rb_mJSON, rb_intern("parse"), 1, str);
|
|
203
|
+
}
|
|
204
|
+
|
|
191
205
|
case Type::Date: {
|
|
192
206
|
// Return a Ruby Date so as_json/to_s gives "YYYY-MM-DD" instead of
|
|
193
207
|
// a full ISO8601 timestamp. CH Date is days since 1970-01-01 UTC.
|
|
@@ -327,6 +341,8 @@ static void append_default(const ColumnRef& col) {
|
|
|
327
341
|
case Type::Float64: col->As<ColumnFloat64>()->Append(0); return;
|
|
328
342
|
case Type::String: col->As<ColumnString>()->Append(std::string_view()); return;
|
|
329
343
|
case Type::FixedString: col->As<ColumnFixedString>()->Append(std::string_view()); return;
|
|
344
|
+
// CH rejects an empty string as JSON; the empty value is "{}".
|
|
345
|
+
case Type::JSON: col->As<ColumnJSON>()->Append(std::string_view("{}")); return;
|
|
330
346
|
case Type::Date: col->As<ColumnDate>()->Append(0); return;
|
|
331
347
|
case Type::Date32: col->As<ColumnDate32>()->Append(0); return;
|
|
332
348
|
case Type::DateTime: col->As<ColumnDateTime>()->Append(0); return;
|
|
@@ -490,6 +506,19 @@ static void append_value(const ColumnRef& col, VALUE value) {
|
|
|
490
506
|
return;
|
|
491
507
|
}
|
|
492
508
|
|
|
509
|
+
case Type::JSON: {
|
|
510
|
+
// A String is taken as already-serialized JSON text and passed
|
|
511
|
+
// through verbatim; anything else (Hash / Array / ...) is rendered
|
|
512
|
+
// via #to_json. nil is handled earlier via append_default ("{}").
|
|
513
|
+
VALUE str = RB_TYPE_P(value, T_STRING)
|
|
514
|
+
? value
|
|
515
|
+
: rb_funcall(value, rb_intern("to_json"), 0);
|
|
516
|
+
StringValue(str);
|
|
517
|
+
col->As<ColumnJSON>()->Append(
|
|
518
|
+
std::string_view(RSTRING_PTR(str), RSTRING_LEN(str)));
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
|
|
493
522
|
case Type::Date: col->As<ColumnDate>()->Append(static_cast<std::time_t>(coerce_to_date_epoch(value))); return;
|
|
494
523
|
case Type::Date32: col->As<ColumnDate32>()->Append(static_cast<std::time_t>(coerce_to_date_epoch(value))); return;
|
|
495
524
|
case Type::DateTime: {
|
|
@@ -639,6 +668,11 @@ static void append_value(const ColumnRef& col, VALUE value) {
|
|
|
639
668
|
|
|
640
669
|
struct CHClient {
|
|
641
670
|
std::unique_ptr<Client> client;
|
|
671
|
+
// Session settings applied to *every* query as per-query settings, rather
|
|
672
|
+
// than a one-time session `SET`. Per-query settings ride each query packet,
|
|
673
|
+
// so they survive a transparent ping_before_query reconnect (a fresh socket
|
|
674
|
+
// would otherwise lose a session-level SET and fall back to server defaults).
|
|
675
|
+
std::vector<std::pair<std::string, std::string>> default_settings;
|
|
642
676
|
};
|
|
643
677
|
|
|
644
678
|
static void ch_client_free(void* p) {
|
|
@@ -680,6 +714,18 @@ static uint16_t kwarg_uint16(VALUE kwargs, const char* key, uint16_t fallback) {
|
|
|
680
714
|
return static_cast<uint16_t>(NUM2UINT(v));
|
|
681
715
|
}
|
|
682
716
|
|
|
717
|
+
static bool kwarg_bool(VALUE kwargs, const char* key, bool fallback) {
|
|
718
|
+
VALUE v = rb_hash_lookup2(kwargs, ID2SYM(rb_intern(key)), Qundef);
|
|
719
|
+
if (v == Qundef) return fallback;
|
|
720
|
+
return RTEST(v);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
static unsigned int kwarg_uint(VALUE kwargs, const char* key, unsigned int fallback) {
|
|
724
|
+
VALUE v = rb_hash_lookup2(kwargs, ID2SYM(rb_intern(key)), Qundef);
|
|
725
|
+
if (v == Qundef || NIL_P(v)) return fallback;
|
|
726
|
+
return NUM2UINT(v);
|
|
727
|
+
}
|
|
728
|
+
|
|
683
729
|
static CompressionMethod kwarg_compression(VALUE kwargs) {
|
|
684
730
|
VALUE v = rb_hash_lookup2(kwargs, ID2SYM(rb_intern("compression")), Qundef);
|
|
685
731
|
if (v == Qundef || NIL_P(v)) return CompressionMethod::None;
|
|
@@ -715,6 +761,16 @@ static int apply_settings_cb(VALUE key, VALUE val, VALUE arg) {
|
|
|
715
761
|
return ST_CONTINUE;
|
|
716
762
|
}
|
|
717
763
|
|
|
764
|
+
// Collect a `settings:` Hash into a client's default_settings vector at
|
|
765
|
+
// construction. Values are stringified the same way per-query settings are.
|
|
766
|
+
static int collect_setting_cb(VALUE key, VALUE val, VALUE arg) {
|
|
767
|
+
auto* vec = reinterpret_cast<std::vector<std::pair<std::string, std::string>>*>(arg);
|
|
768
|
+
VALUE k = SYMBOL_P(key) ? rb_sym2str(key) : key;
|
|
769
|
+
StringValue(k);
|
|
770
|
+
vec->emplace_back(std::string(RSTRING_PTR(k), RSTRING_LEN(k)), stringify_setting_value(val));
|
|
771
|
+
return ST_CONTINUE;
|
|
772
|
+
}
|
|
773
|
+
|
|
718
774
|
// Read a `settings:` Hash out of the parsed kwargs and stamp each entry
|
|
719
775
|
// onto `q` as a per-query setting. No-op if kwargs is nil or settings is
|
|
720
776
|
// missing/empty. Raises TypeError if settings is not a Hash.
|
|
@@ -726,6 +782,28 @@ static void apply_settings(Query& q, VALUE kwargs) {
|
|
|
726
782
|
rb_hash_foreach(settings, apply_settings_cb, reinterpret_cast<VALUE>(&q));
|
|
727
783
|
}
|
|
728
784
|
|
|
785
|
+
// Same as apply_settings, but first defaults
|
|
786
|
+
// output_format_native_write_json_as_string=1 so JSON columns come back as
|
|
787
|
+
// strings (clickhouse-cpp's ColumnJSON can only read them that way). The flag
|
|
788
|
+
// is 0 (not "important"), so servers that don't know the setting ignore it
|
|
789
|
+
// instead of erroring. User-supplied settings are applied afterwards and win,
|
|
790
|
+
// so callers can still override it.
|
|
791
|
+
static void apply_read_settings(Query& q, VALUE kwargs) {
|
|
792
|
+
q.SetSetting("output_format_native_write_json_as_string",
|
|
793
|
+
QuerySettingsField{"1", 0});
|
|
794
|
+
apply_settings(q, kwargs);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// Stamp the client's construction-time `settings:` onto `q`. Marked important
|
|
798
|
+
// (flag 1) so an unknown setting name errors — matching the old session `SET`,
|
|
799
|
+
// which failed loudly instead of silently ignoring a typo'd setting. Applied
|
|
800
|
+
// before per-call settings so an explicit `settings:` on the call still wins.
|
|
801
|
+
static void apply_default_settings(Query& q, CHClient* c) {
|
|
802
|
+
for (const auto& kv : c->default_settings) {
|
|
803
|
+
q.SetSetting(kv.first, QuerySettingsField{kv.second, 1});
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
729
807
|
// Client.new(host:, port:, database:, user:, password:)
|
|
730
808
|
static VALUE ch_client_initialize(int argc, VALUE* argv, VALUE self) {
|
|
731
809
|
VALUE kwargs = Qnil;
|
|
@@ -739,12 +817,35 @@ static VALUE ch_client_initialize(int argc, VALUE* argv, VALUE self) {
|
|
|
739
817
|
std::string password = kwarg_str(kwargs, "password", "");
|
|
740
818
|
CompressionMethod compression = kwarg_compression(kwargs);
|
|
741
819
|
|
|
820
|
+
// Connection-resilience defaults. Long-lived pooled connections get
|
|
821
|
+
// silently closed by the server (idle_connection_timeout) or an LB, so
|
|
822
|
+
// the next use of a checked-out client would otherwise hit recv()==0 and
|
|
823
|
+
// raise ConnectionError("closed: ..."). ping_before_query makes the driver
|
|
824
|
+
// ping first and transparently reconnect on a dead socket; tcp_keepalive
|
|
825
|
+
// keeps idle sockets healthy at the OS level. retry_timeout is the backoff
|
|
826
|
+
// before each reconnect attempt — kept low (1s) since a pooled reconnect
|
|
827
|
+
// to a live server should be quick and we don't want to stall queries.
|
|
828
|
+
bool ping_before_query = kwarg_bool(kwargs, "ping_before_query", true);
|
|
829
|
+
bool tcp_keepalive = kwarg_bool(kwargs, "tcp_keepalive", true);
|
|
830
|
+
unsigned int retry_timeout = kwarg_uint(kwargs, "retry_timeout", 1);
|
|
831
|
+
|
|
742
832
|
CHClient* c = as_client(self);
|
|
833
|
+
|
|
834
|
+
VALUE settings = rb_hash_lookup2(kwargs, ID2SYM(rb_intern("settings")), Qnil);
|
|
835
|
+
if (!NIL_P(settings)) {
|
|
836
|
+
Check_Type(settings, T_HASH);
|
|
837
|
+
rb_hash_foreach(settings, collect_setting_cb,
|
|
838
|
+
reinterpret_cast<VALUE>(&c->default_settings));
|
|
839
|
+
}
|
|
840
|
+
|
|
743
841
|
try {
|
|
744
842
|
ClientOptions opts;
|
|
745
843
|
opts.SetHost(host).SetPort(port)
|
|
746
844
|
.SetDefaultDatabase(database).SetUser(user).SetPassword(password)
|
|
747
|
-
.SetCompressionMethod(compression)
|
|
845
|
+
.SetCompressionMethod(compression)
|
|
846
|
+
.SetPingBeforeQuery(ping_before_query)
|
|
847
|
+
.TcpKeepAlive(tcp_keepalive)
|
|
848
|
+
.SetRetryTimeout(std::chrono::seconds(retry_timeout));
|
|
748
849
|
c->client = std::make_unique<Client>(opts);
|
|
749
850
|
} catch (const std::exception& e) {
|
|
750
851
|
raise_mapped_ex(e);
|
|
@@ -753,6 +854,9 @@ static VALUE ch_client_initialize(int argc, VALUE* argv, VALUE self) {
|
|
|
753
854
|
rb_ivar_set(self, rb_intern("@host"), rb_utf8_str_new(host.data(), host.size()));
|
|
754
855
|
rb_ivar_set(self, rb_intern("@port"), UINT2NUM(port));
|
|
755
856
|
rb_ivar_set(self, rb_intern("@database"), rb_utf8_str_new(database.data(), database.size()));
|
|
857
|
+
rb_ivar_set(self, rb_intern("@ping_before_query"), ping_before_query ? Qtrue : Qfalse);
|
|
858
|
+
rb_ivar_set(self, rb_intern("@tcp_keepalive"), tcp_keepalive ? Qtrue : Qfalse);
|
|
859
|
+
rb_ivar_set(self, rb_intern("@retry_timeout"), UINT2NUM(retry_timeout));
|
|
756
860
|
|
|
757
861
|
VALUE logger = rb_hash_lookup2(kwargs, ID2SYM(rb_intern("logger")), Qnil);
|
|
758
862
|
rb_ivar_set(self, rb_intern("@logger"), logger);
|
|
@@ -796,6 +900,7 @@ static VALUE ch_client_execute(int argc, VALUE* argv, VALUE self) {
|
|
|
796
900
|
if (!c->client) rb_raise(err_connection, "clickhouse-native: client is closed");
|
|
797
901
|
|
|
798
902
|
Query q(std::string(RSTRING_PTR(rb_sql), RSTRING_LEN(rb_sql)));
|
|
903
|
+
apply_default_settings(q, c);
|
|
799
904
|
apply_settings(q, kwargs);
|
|
800
905
|
|
|
801
906
|
ExecuteNoGVL args{c->client.get(), &q, nullptr};
|
|
@@ -827,7 +932,8 @@ static VALUE ch_client_query(int argc, VALUE* argv, VALUE self) {
|
|
|
827
932
|
try {
|
|
828
933
|
std::vector<ID> col_ids;
|
|
829
934
|
Query q(std::string(RSTRING_PTR(rb_sql), RSTRING_LEN(rb_sql)));
|
|
830
|
-
|
|
935
|
+
apply_default_settings(q, c);
|
|
936
|
+
apply_read_settings(q, kwargs);
|
|
831
937
|
q.OnData([&](const Block& block) {
|
|
832
938
|
size_t ncols = block.GetColumnCount();
|
|
833
939
|
size_t nrows = block.GetRowCount();
|
|
@@ -872,7 +978,8 @@ static VALUE ch_client_query_value(int argc, VALUE* argv, VALUE self) {
|
|
|
872
978
|
VALUE out = Qnil;
|
|
873
979
|
bool seen = false;
|
|
874
980
|
Query q(std::string(RSTRING_PTR(rb_sql), RSTRING_LEN(rb_sql)));
|
|
875
|
-
|
|
981
|
+
apply_default_settings(q, c);
|
|
982
|
+
apply_read_settings(q, kwargs);
|
|
876
983
|
q.OnData([&](const Block& block) {
|
|
877
984
|
if (seen) return;
|
|
878
985
|
if (block.GetRowCount() == 0 || block.GetColumnCount() == 0) return;
|
|
@@ -897,6 +1004,7 @@ struct InsertNoGVL {
|
|
|
897
1004
|
Client* client;
|
|
898
1005
|
const std::string* table;
|
|
899
1006
|
const Block* block;
|
|
1007
|
+
const std::vector<std::pair<std::string, std::string>>* settings;
|
|
900
1008
|
std::exception_ptr err;
|
|
901
1009
|
};
|
|
902
1010
|
} // namespace
|
|
@@ -904,7 +1012,7 @@ struct InsertNoGVL {
|
|
|
904
1012
|
static void* insert_no_gvl(void* data) {
|
|
905
1013
|
auto* a = static_cast<InsertNoGVL*>(data);
|
|
906
1014
|
try {
|
|
907
|
-
a->client->Insert(*a->table, *a->block);
|
|
1015
|
+
a->client->Insert(*a->table, *a->block, *a->settings);
|
|
908
1016
|
} catch (...) {
|
|
909
1017
|
a->err = std::current_exception();
|
|
910
1018
|
}
|
|
@@ -970,7 +1078,7 @@ static VALUE ch_client_insert_block(VALUE self, VALUE rb_table, VALUE rb_columns
|
|
|
970
1078
|
block.AppendColumn(names[i], cols[i]);
|
|
971
1079
|
}
|
|
972
1080
|
|
|
973
|
-
InsertNoGVL args{c->client.get(), &table, &block, nullptr};
|
|
1081
|
+
InsertNoGVL args{c->client.get(), &table, &block, &c->default_settings, nullptr};
|
|
974
1082
|
rb_thread_call_without_gvl(insert_no_gvl, &args, insert_unblock, &args);
|
|
975
1083
|
if (args.err) {
|
|
976
1084
|
try { c->client->ResetConnection(); } catch (...) {}
|
|
@@ -1071,7 +1179,8 @@ static VALUE ch_client_query_each(int argc, VALUE* argv, VALUE self) {
|
|
|
1071
1179
|
|
|
1072
1180
|
QueryEachState state{rb_block_proc(), {}, 0, false};
|
|
1073
1181
|
Query q(std::string(RSTRING_PTR(rb_sql), RSTRING_LEN(rb_sql)));
|
|
1074
|
-
|
|
1182
|
+
apply_default_settings(q, c);
|
|
1183
|
+
apply_read_settings(q, kwargs);
|
|
1075
1184
|
q.OnDataCancelable([&state](const Block& block) -> bool {
|
|
1076
1185
|
if (state.aborted) return false;
|
|
1077
1186
|
YieldBlockArgs ya{&block, &state};
|
|
@@ -22,16 +22,39 @@ def fatal(msg)
|
|
|
22
22
|
abort "clickhouse-native: cannot build extension"
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
# Bundler `git:` installs don't fetch submodules unless the Gemfile sets
|
|
26
|
+
# `submodules: true`, which leaves vendor/clickhouse-cpp empty. When we're
|
|
27
|
+
# sitting in a git checkout, fetch it ourselves so the build can proceed
|
|
28
|
+
# without every consumer having to remember that flag. Released gems and
|
|
29
|
+
# `submodules: recursive` checkouts already have the tree, so this is a no-op
|
|
30
|
+
# there.
|
|
31
|
+
unless File.exist?(File.join(VENDOR, "CMakeLists.txt"))
|
|
32
|
+
in_git_checkout = system(
|
|
33
|
+
"git", "-C", EXT_DIR, "rev-parse", "--is-inside-work-tree",
|
|
34
|
+
out: File::NULL, err: File::NULL
|
|
35
|
+
)
|
|
36
|
+
if in_git_checkout
|
|
37
|
+
warn "clickhouse-native: vendored clickhouse-cpp is missing; " \
|
|
38
|
+
"running `git submodule update --init --recursive`…"
|
|
39
|
+
system("git", "-C", EXT_DIR, "submodule", "update", "--init", "--recursive")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
25
43
|
unless File.exist?(File.join(VENDOR, "CMakeLists.txt"))
|
|
26
44
|
fatal <<~MSG
|
|
27
45
|
clickhouse-cpp submodule not found at:
|
|
28
46
|
#{VENDOR}
|
|
29
47
|
|
|
30
|
-
|
|
48
|
+
Installing from a git source? Bundler skips submodules unless you add
|
|
49
|
+
`submodules: true` to the gem line:
|
|
50
|
+
|
|
51
|
+
gem "clickhouse-native", git: "...", submodules: true
|
|
52
|
+
|
|
53
|
+
From a manual clone, run:
|
|
31
54
|
git submodule update --init --recursive
|
|
32
55
|
|
|
33
|
-
If you see this
|
|
34
|
-
|
|
56
|
+
If you see this while installing a released gem from RubyGems, please
|
|
57
|
+
report a bug — the published .gem should bundle the submodule tree.
|
|
35
58
|
MSG
|
|
36
59
|
end
|
|
37
60
|
|
|
@@ -97,6 +120,10 @@ configure_args = [
|
|
|
97
120
|
"-DBUILD_BENCHMARK=OFF",
|
|
98
121
|
"-DBUILD_TESTS=OFF",
|
|
99
122
|
"-DWITH_OPENSSL=OFF",
|
|
123
|
+
# clickhouse-cpp v2.6.2 added a distinct Bool column; this gem relies on
|
|
124
|
+
# Bool normalising to UInt8 (see the declared-type handling in client.cpp).
|
|
125
|
+
# Pin the upstream default ON so a future default flip can't break us.
|
|
126
|
+
"-DCH_MAP_BOOL_TO_UINT8=ON",
|
|
100
127
|
"-DCMAKE_POSITION_INDEPENDENT_CODE=ON",
|
|
101
128
|
"-DCMAKE_BUILD_TYPE=Release"
|
|
102
129
|
]
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
diff --git a/clickhouse/client.cpp b/clickhouse/client.cpp
|
|
2
|
+
index 34a733d..c9732f4 100644
|
|
3
|
+
--- a/clickhouse/client.cpp
|
|
4
|
+
+++ b/clickhouse/client.cpp
|
|
5
|
+
@@ -211,7 +211,8 @@ public:
|
|
6
|
+
|
|
7
|
+
bool IsSelecting() const { return state_ == State::Selecting; }
|
|
8
|
+
|
|
9
|
+
- void Insert(const std::string& table_name, const std::string& query_id, const Block& block);
|
|
10
|
+
+ void Insert(const std::string& table_name, const std::string& query_id, const Block& block,
|
|
11
|
+
+ const std::vector<std::pair<std::string, std::string>>& settings = {});
|
|
12
|
+
|
|
13
|
+
Block BeginInsert(Query query);
|
|
14
|
+
|
|
15
|
+
@@ -485,7 +486,8 @@ std::string NameToQueryString(const std::string &input)
|
|
16
|
+
return output;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
-void Client::Impl::Insert(const std::string& table_name, const std::string& query_id, const Block& block) {
|
|
20
|
+
+void Client::Impl::Insert(const std::string& table_name, const std::string& query_id, const Block& block,
|
|
21
|
+
+ const std::vector<std::pair<std::string, std::string>>& settings) {
|
|
22
|
+
if (state_ == State::Inserting) {
|
|
23
|
+
throw ValidationError("cannot execute query while inserting, use SendInsertData instead");
|
|
24
|
+
}
|
|
25
|
+
@@ -511,6 +513,9 @@ void Client::Impl::Insert(const std::string& table_name, const std::string& quer
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
Query query("INSERT INTO " + table_name + " ( " + fields_section.str() + " ) VALUES", query_id);
|
|
29
|
+
+ for (const auto& kv : settings) {
|
|
30
|
+
+ query.SetSetting(kv.first, QuerySettingsField{kv.second, 1});
|
|
31
|
+
+ }
|
|
32
|
+
SendQuery(query);
|
|
33
|
+
|
|
34
|
+
// Wait for a data packet and return
|
|
35
|
+
@@ -1365,12 +1370,14 @@ bool Client::IsSelecting() const
|
|
36
|
+
return impl_->IsSelecting();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
-void Client::Insert(const std::string& table_name, const Block& block) {
|
|
40
|
+
- impl_->Insert(table_name, Query::default_query_id, block);
|
|
41
|
+
+void Client::Insert(const std::string& table_name, const Block& block,
|
|
42
|
+
+ const std::vector<std::pair<std::string, std::string>>& settings) {
|
|
43
|
+
+ impl_->Insert(table_name, Query::default_query_id, block, settings);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
-void Client::Insert(const std::string& table_name, const std::string& query_id, const Block& block) {
|
|
47
|
+
- impl_->Insert(table_name, query_id, block);
|
|
48
|
+
+void Client::Insert(const std::string& table_name, const std::string& query_id, const Block& block,
|
|
49
|
+
+ const std::vector<std::pair<std::string, std::string>>& settings) {
|
|
50
|
+
+ impl_->Insert(table_name, query_id, block, settings);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Block Client::BeginInsert(const std::string& query) {
|
|
54
|
+
diff --git a/clickhouse/client.h b/clickhouse/client.h
|
|
55
|
+
index a55fd74..6c6ff53 100644
|
|
56
|
+
--- a/clickhouse/client.h
|
|
57
|
+
+++ b/clickhouse/client.h
|
|
58
|
+
@@ -23,6 +23,8 @@
|
|
59
|
+
#include "columns/bool.h"
|
|
60
|
+
|
|
61
|
+
#include <chrono>
|
|
62
|
+
+#include <utility>
|
|
63
|
+
+#include <vector>
|
|
64
|
+
#include <cstdint>
|
|
65
|
+
#include <memory>
|
|
66
|
+
#include <ostream>
|
|
67
|
+
@@ -298,8 +300,13 @@ public:
|
|
68
|
+
bool IsSelecting() const;
|
|
69
|
+
|
|
70
|
+
/// Intends for insert block of data into a table \p table_name.
|
|
71
|
+
- void Insert(const std::string& table_name, const Block& block);
|
|
72
|
+
- void Insert(const std::string& table_name, const std::string& query_id, const Block& block);
|
|
73
|
+
+ /// \p settings are applied to the generated INSERT query as per-query
|
|
74
|
+
+ /// settings (patched-in for clickhouse-native: lets a pooled client carry
|
|
75
|
+
+ /// its session settings into inserts without a session-level SET).
|
|
76
|
+
+ void Insert(const std::string& table_name, const Block& block,
|
|
77
|
+
+ const std::vector<std::pair<std::string, std::string>>& settings = {});
|
|
78
|
+
+ void Insert(const std::string& table_name, const std::string& query_id, const Block& block,
|
|
79
|
+
+ const std::vector<std::pair<std::string, std::string>>& settings = {});
|
|
80
|
+
|
|
81
|
+
/// Start an \p INSERT statement, insert batches of data, then finish the insert.
|
|
82
|
+
Block BeginInsert(const std::string& query);
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
name: Bazel
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
- cron: '0 0 * * 1'
|
|
6
|
+
push:
|
|
7
|
+
branches: [ '*' ]
|
|
8
|
+
pull_request:
|
|
9
|
+
branches: [ master ]
|
|
10
|
+
|
|
11
|
+
release:
|
|
12
|
+
types:
|
|
13
|
+
- published
|
|
14
|
+
- prereleased
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
build:
|
|
21
|
+
strategy:
|
|
22
|
+
fail-fast: false
|
|
23
|
+
matrix:
|
|
24
|
+
os: [ubuntu-24.04, macos-latest, windows-latest]
|
|
25
|
+
tls: [boringssl, openssl, 'no']
|
|
26
|
+
|
|
27
|
+
runs-on: ${{ matrix.os }}
|
|
28
|
+
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v6
|
|
31
|
+
with:
|
|
32
|
+
fetch-depth: 100
|
|
33
|
+
fetch-tags: true
|
|
34
|
+
|
|
35
|
+
- name: Setup Bazel
|
|
36
|
+
uses: bazel-contrib/setup-bazel@0.15.0
|
|
37
|
+
with:
|
|
38
|
+
bazelisk-cache: true
|
|
39
|
+
disk-cache: true
|
|
40
|
+
repository-cache: true
|
|
41
|
+
|
|
42
|
+
- name: Build project
|
|
43
|
+
run: bazel build //... --@clickhouse-cpp-client//:tls=${{ matrix.tls }}
|
|
44
|
+
|
|
45
|
+
- name: Run unit tests
|
|
46
|
+
run: bazel test //ut:unit_tests --@clickhouse-cpp-client//:tls=${{ matrix.tls }} --test_output=errors
|
|
47
|
+
|
|
48
|
+
# //ut:e2e_tests is tagged `manual`, so `bazel build //...` above
|
|
49
|
+
# skips it; build it explicitly so the run step below doesn't pay
|
|
50
|
+
# for compilation under the running-server timeout.
|
|
51
|
+
- name: Build e2e tests
|
|
52
|
+
run: bazel build //ut:e2e_tests --@clickhouse-cpp-client//:tls=${{ matrix.tls }}
|
|
53
|
+
|
|
54
|
+
# The e2e suite needs a live server on localhost:9000. Each OS starts
|
|
55
|
+
# one the same way the CMake build's per-OS workflows do, since hosted
|
|
56
|
+
# runners can't all run a Linux container the same way:
|
|
57
|
+
# * Linux — docker-compose (linux.yml)
|
|
58
|
+
# * macOS — the native ClickHouse build run as a process (macos.yml)
|
|
59
|
+
# * Windows — the Linux container under WSL2 + podman (windows_msvc.yml)
|
|
60
|
+
- name: Start ClickHouse (Linux, docker-compose)
|
|
61
|
+
if: runner.os == 'Linux'
|
|
62
|
+
uses: hoverkraft-tech/compose-action@v2.0.1
|
|
63
|
+
with:
|
|
64
|
+
compose-file: ci/docker-compose.yml
|
|
65
|
+
down-flags: --volumes
|
|
66
|
+
|
|
67
|
+
- name: Start ClickHouse (macOS, native binary)
|
|
68
|
+
if: runner.os == 'macOS'
|
|
69
|
+
working-directory: ${{ runner.temp }}
|
|
70
|
+
run: |
|
|
71
|
+
curl https://builds.clickhouse.com/25.12/macos-aarch64/clickhouse -o clickhouse
|
|
72
|
+
chmod +x ./clickhouse
|
|
73
|
+
sudo mkdir -p /var/lib/clickhouse /var/log/clickhouse-server
|
|
74
|
+
sudo chown -R "$USER" /var/lib/clickhouse /var/log/clickhouse-server
|
|
75
|
+
nohup ./clickhouse server --config-file="$GITHUB_WORKSPACE/ci/docker-compose/config.xml" > clickhouse.log 2>&1 &
|
|
76
|
+
for i in {1..60}; do
|
|
77
|
+
if curl -fsS http://localhost:8123/ > /dev/null; then
|
|
78
|
+
echo "ClickHouse is ready"
|
|
79
|
+
exit 0
|
|
80
|
+
fi
|
|
81
|
+
sleep 1
|
|
82
|
+
done
|
|
83
|
+
echo "ClickHouse failed to start"
|
|
84
|
+
tail -200 clickhouse.log || true
|
|
85
|
+
exit 1
|
|
86
|
+
|
|
87
|
+
- name: Enable WSL (Windows)
|
|
88
|
+
if: runner.os == 'Windows'
|
|
89
|
+
uses: Vampire/setup-wsl@v5
|
|
90
|
+
with:
|
|
91
|
+
distribution: Ubuntu-24.04
|
|
92
|
+
additional-packages:
|
|
93
|
+
podman
|
|
94
|
+
podman-compose
|
|
95
|
+
|
|
96
|
+
- name: Start ClickHouse (Windows, WSL + podman)
|
|
97
|
+
if: runner.os == 'Windows'
|
|
98
|
+
shell: wsl-bash {0}
|
|
99
|
+
run: |
|
|
100
|
+
cd $(wslpath -u "${{ github.workspace }}/ci/")
|
|
101
|
+
podman-compose up -d
|
|
102
|
+
timeout 60s bash -c \
|
|
103
|
+
'until curl -s -o /dev/null -w "%{http_code}" http://localhost:8123 | grep -q "200"; do sleep 2; done'
|
|
104
|
+
curl -s http://localhost:8123/?query=SELECT%20VERSION%28%29
|
|
105
|
+
|
|
106
|
+
- name: Wait for ClickHouse (Linux)
|
|
107
|
+
if: runner.os == 'Linux'
|
|
108
|
+
run: |
|
|
109
|
+
for i in {1..60}; do
|
|
110
|
+
if curl -fsS http://localhost:8123/ > /dev/null; then
|
|
111
|
+
echo "ClickHouse is ready"
|
|
112
|
+
exit 0
|
|
113
|
+
fi
|
|
114
|
+
sleep 1
|
|
115
|
+
done
|
|
116
|
+
echo "ClickHouse failed to start"
|
|
117
|
+
exit 1
|
|
118
|
+
|
|
119
|
+
- name: Run e2e tests
|
|
120
|
+
run: bazel test //ut:e2e_tests --@clickhouse-cpp-client//:tls=${{ matrix.tls }} --test_output=errors
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: Relay bugs for cross-repo investigation
|
|
2
|
+
|
|
3
|
+
# Relays newly-opened issues to ClickHouse/integrations-ai-playground for
|
|
4
|
+
# cross-repo investigation.
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
issues:
|
|
8
|
+
types: [opened]
|
|
9
|
+
|
|
10
|
+
permissions: {}
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
relay:
|
|
14
|
+
uses: ClickHouse/integrations-shared-workflows/.github/workflows/cross-repo-bug-relay.yml@main
|
|
15
|
+
secrets:
|
|
16
|
+
WORKFLOW_AUTH_PUBLIC_APP_ID: ${{ secrets.WORKFLOW_AUTH_PUBLIC_APP_ID }}
|
|
17
|
+
WORKFLOW_AUTH_PUBLIC_PRIVATE_KEY: ${{ secrets.WORKFLOW_AUTH_PUBLIC_PRIVATE_KEY }}
|
|
@@ -15,10 +15,9 @@ on:
|
|
|
15
15
|
|
|
16
16
|
env:
|
|
17
17
|
BUILD_TYPE: Release
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
CLICKHOUSE_SECURE_PASSWORD: ${{ secrets.INTEGRATIONS_TEAM_TESTS_CLOUD_PASSWORD_SMT_PROD }}
|
|
18
|
+
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
22
21
|
|
|
23
22
|
jobs:
|
|
24
23
|
build:
|
|
@@ -83,7 +82,7 @@ jobs:
|
|
|
83
82
|
runs-on: ${{matrix.os}}
|
|
84
83
|
|
|
85
84
|
steps:
|
|
86
|
-
- uses: actions/checkout@
|
|
85
|
+
- uses: actions/checkout@v6
|
|
87
86
|
with:
|
|
88
87
|
fetch-depth: 100
|
|
89
88
|
fetch-tags: true
|
|
@@ -96,22 +95,13 @@ jobs:
|
|
|
96
95
|
${{matrix.COMPILER_INSTALL}} \
|
|
97
96
|
${{matrix.DEPENDENCIES_INSTALL}}
|
|
98
97
|
|
|
99
|
-
- name: Install dependencies - Docker
|
|
100
|
-
run: |
|
|
101
|
-
sudo apt remove -y docker docker-engine docker.io containerd runc
|
|
102
|
-
sudo apt install -y apt-transport-https ca-certificates curl gnupg lsb-release
|
|
103
|
-
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
|
104
|
-
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
|
105
|
-
sudo apt update -q
|
|
106
|
-
sudo apt install docker-ce docker-ce-cli containerd.io
|
|
107
|
-
sudo docker run hello-world
|
|
108
|
-
|
|
109
98
|
- name: Configure project
|
|
110
99
|
run: |
|
|
111
100
|
cmake \
|
|
112
101
|
-D CMAKE_C_COMPILER=${{matrix.C_COMPILER}} \
|
|
113
102
|
-D CMAKE_CXX_COMPILER=${{matrix.CXX_COMPILER}} \
|
|
114
103
|
-D CMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \
|
|
104
|
+
-D CH_MAP_BOOL_TO_UINT8=OFF \
|
|
115
105
|
-D BUILD_TESTS=ON \
|
|
116
106
|
${{matrix.SSL_CMAKE_OPTION}} \
|
|
117
107
|
${{matrix.DEPENDENCIES_CMAKE_OPTIONS}} \
|
|
@@ -125,15 +115,24 @@ jobs:
|
|
|
125
115
|
--config ${{env.BUILD_TYPE}} \
|
|
126
116
|
--target all
|
|
127
117
|
|
|
128
|
-
- name:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
docker stats -a --no-stream
|
|
134
|
-
## Check and wait until CH is ready to accept connections
|
|
135
|
-
docker exec clickhouse bash -c 'for i in {1..10}; do echo checking if clickhouse server is started attempt \#$i; if ( grep -q "<Information> Application: Ready for connections." /var/log/clickhouse-server/clickhouse-server.log ); then echo seems like clickhouse server is started; exit 0; fi; sleep 1; done; exit -1'
|
|
118
|
+
- name: Start ClickHouse in Docker
|
|
119
|
+
uses: hoverkraft-tech/compose-action@v2.0.1
|
|
120
|
+
with:
|
|
121
|
+
compose-file: ci/docker-compose.yml
|
|
122
|
+
down-flags: --volumes
|
|
136
123
|
|
|
124
|
+
- name: Check if ClickHouse is available
|
|
125
|
+
run: |
|
|
126
|
+
for i in {1..60}; do
|
|
127
|
+
if curl -fsS http://localhost:8123/ > /dev/null; then
|
|
128
|
+
echo "ClickHouse is ready"
|
|
129
|
+
exit 0
|
|
130
|
+
fi
|
|
131
|
+
sleep 1
|
|
132
|
+
done
|
|
133
|
+
echo "ClickHouse failed to start"
|
|
134
|
+
exit 1
|
|
135
|
+
|
|
137
136
|
- name: Test
|
|
138
137
|
working-directory: ${{github.workspace}}/build/ut
|
|
139
138
|
run: ./clickhouse-cpp-ut
|