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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/ext/clickhouse_native/client.cpp +115 -6
  3. data/ext/clickhouse_native/extconf.rb +30 -3
  4. data/ext/clickhouse_native/patches/0002-carry-settings-into-insert.patch +82 -0
  5. data/ext/clickhouse_native/vendor/clickhouse-cpp/.github/workflows/bazel.yml +120 -0
  6. data/ext/clickhouse_native/vendor/clickhouse-cpp/.github/workflows/cross-repo-bug-relay.yml +17 -0
  7. data/ext/clickhouse_native/vendor/clickhouse-cpp/.github/workflows/linux.yml +22 -23
  8. data/ext/clickhouse_native/vendor/clickhouse-cpp/.github/workflows/macos.yml +22 -21
  9. data/ext/clickhouse_native/vendor/clickhouse-cpp/.github/workflows/windows_mingw.yml +29 -36
  10. data/ext/clickhouse_native/vendor/clickhouse-cpp/.github/workflows/windows_msvc.yml +29 -36
  11. data/ext/clickhouse_native/vendor/clickhouse-cpp/.gitignore +6 -0
  12. data/ext/clickhouse_native/vendor/clickhouse-cpp/AI_POLICY.md +13 -0
  13. data/ext/clickhouse_native/vendor/clickhouse-cpp/BUILD.bazel +167 -0
  14. data/ext/clickhouse_native/vendor/clickhouse-cpp/CMakeLists.txt +2 -1
  15. data/ext/clickhouse_native/vendor/clickhouse-cpp/MODULE.bazel +17 -0
  16. data/ext/clickhouse_native/vendor/clickhouse-cpp/MODULE.bazel.lock +503 -0
  17. data/ext/clickhouse_native/vendor/clickhouse-cpp/README.md +32 -6
  18. data/ext/clickhouse_native/vendor/clickhouse-cpp/ci/docker-compose/config.xml +53 -0
  19. data/ext/clickhouse_native/vendor/clickhouse-cpp/ci/docker-compose/users.xml +35 -0
  20. data/ext/clickhouse_native/vendor/clickhouse-cpp/ci/docker-compose.yml +22 -0
  21. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/CMakeLists.txt +11 -0
  22. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/base/sslsocket.cpp +24 -0
  23. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/block.cpp +1 -1
  24. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/block.h +2 -1
  25. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/client.cpp +293 -136
  26. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/client.h +31 -2
  27. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/array.cpp +12 -0
  28. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/array.h +17 -7
  29. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/bool.cpp +79 -0
  30. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/bool.h +62 -0
  31. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/factory.cpp +16 -0
  32. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/itemview.cpp +2 -0
  33. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/itemview.h +6 -2
  34. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/json.cpp +102 -0
  35. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/json.h +82 -0
  36. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/lowcardinality.cpp +2 -1
  37. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/string.cpp +7 -2
  38. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/tuple.cpp +48 -5
  39. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/tuple.h +14 -1
  40. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/query.h +2 -2
  41. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/server_exception.h +0 -3
  42. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/types/type_parser.cpp +43 -0
  43. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/types/type_parser.h +9 -0
  44. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/types/types.cpp +61 -11
  45. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/types/types.h +18 -2
  46. data/ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/version.h +1 -1
  47. data/lib/clickhouse_native/client.rb +1 -1
  48. data/lib/clickhouse_native/pool.rb +22 -31
  49. data/lib/clickhouse_native/version.rb +1 -1
  50. data/lib/clickhouse_native.rb +1 -0
  51. metadata +16 -2
@@ -13,6 +13,8 @@ namespace clickhouse {
13
13
  class ColumnTuple : public Column {
14
14
  public:
15
15
  ColumnTuple(const std::vector<ColumnRef>& columns);
16
+ ColumnTuple(const std::vector<ColumnRef>& columns,
17
+ std::vector<std::string> names);
16
18
 
17
19
  /// Returns count of columns in the tuple.
18
20
  size_t TupleSize() const;
@@ -69,12 +71,22 @@ public:
69
71
  ColumnTupleT(std::tuple<std::shared_ptr<Columns>...> columns)
70
72
  : ColumnTuple(TupleToVector(columns)), typed_columns_(std::move(columns)) {}
71
73
 
74
+ ColumnTupleT(std::tuple<std::shared_ptr<Columns>...> columns, std::vector<std::string> names)
75
+ : ColumnTuple(TupleToVector(columns), std::move(names)), typed_columns_(std::move(columns)) {}
76
+
72
77
  ColumnTupleT(std::vector<ColumnRef> columns)
73
78
  : ColumnTuple(columns), typed_columns_(VectorToTuple(std::move(columns))) {}
74
79
 
80
+ ColumnTupleT(std::vector<ColumnRef> columns, std::vector<std::string> names)
81
+ : ColumnTuple(columns, std::move(names)), typed_columns_(VectorToTuple(std::move(columns))) {}
82
+
75
83
  ColumnTupleT(const std::initializer_list<ColumnRef> columns)
76
84
  : ColumnTuple(columns), typed_columns_(VectorToTuple(std::move(columns))) {}
77
85
 
86
+ ColumnTupleT(std::initializer_list<ColumnRef> columns, std::vector<std::string> names)
87
+ : ColumnTuple(std::vector<ColumnRef>(columns), std::move(names))
88
+ , typed_columns_(VectorToTuple(std::vector<ColumnRef>(columns))) {}
89
+
78
90
  inline ValueType At(size_t index) const { return GetTupleOfValues(index); }
79
91
 
80
92
  inline ValueType operator[](size_t index) const { return GetTupleOfValues(index); }
@@ -99,7 +111,8 @@ public:
99
111
  if (col.TupleSize() != std::tuple_size_v<TupleOfColumns>) {
100
112
  throw ValidationError("Can't wrap from " + col.GetType().GetName());
101
113
  }
102
- return std::make_shared<ColumnTupleT<Columns...>>(VectorToTuple(std::move(col)));
114
+ auto names = col.Type()->As<TupleType>()->GetItemNames();
115
+ return std::make_shared<ColumnTupleT<Columns...>>(VectorToTuple(std::move(col)), std::move(names));
103
116
  }
104
117
 
105
118
  static auto Wrap(Column&& col) { return Wrap(std::move(dynamic_cast<ColumnTuple&&>(col))); }
@@ -229,8 +229,8 @@ private:
229
229
  }
230
230
 
231
231
  private:
232
- const std::string query_;
233
- const std::string query_id_;
232
+ std::string query_;
233
+ std::string query_id_;
234
234
  std::optional<open_telemetry::TracingContext> tracing_context_;
235
235
  QuerySettings query_settings_;
236
236
  QueryParams query_params_;
@@ -1,7 +1,6 @@
1
1
  #pragma once
2
2
 
3
3
  #include <string>
4
- #include <memory>
5
4
 
6
5
  namespace clickhouse {
7
6
  struct Exception {
@@ -9,8 +8,6 @@ struct Exception {
9
8
  std::string name;
10
9
  std::string display_text;
11
10
  std::string stack_trace;
12
- /// Pointer to nested exception.
13
- std::unique_ptr<Exception> nested;
14
11
  };
15
12
 
16
13
  }
@@ -22,7 +22,9 @@ bool TypeAst::operator==(const TypeAst & other) const {
22
22
  return meta == other.meta
23
23
  && code == other.code
24
24
  && name == other.name
25
+ && element_name == other.element_name
25
26
  && value == other.value
27
+ && value_string == other.value_string
26
28
  && std::equal(elements.begin(), elements.end(), other.elements.begin(), other.elements.end());
27
29
  }
28
30
 
@@ -32,7 +34,11 @@ static const std::unordered_map<std::string, Type::Code> kTypeCode = {
32
34
  { "Int16", Type::Int16 },
33
35
  { "Int32", Type::Int32 },
34
36
  { "Int64", Type::Int64 },
37
+ #if CH_MAP_BOOL_TO_UINT8
35
38
  { "Bool", Type::UInt8 },
39
+ #else
40
+ { "Bool", Type::Bool },
41
+ #endif
36
42
  { "UInt8", Type::UInt8 },
37
43
  { "UInt16", Type::UInt16 },
38
44
  { "UInt32", Type::UInt32 },
@@ -67,6 +73,7 @@ static const std::unordered_map<std::string, Type::Code> kTypeCode = {
67
73
  { "MultiPolygon", Type::MultiPolygon },
68
74
  { "Time", Type::Time },
69
75
  { "Time64", Type::Time64 },
76
+ { "JSON", Type::JSON },
70
77
  };
71
78
 
72
79
  template <typename L, typename R>
@@ -166,7 +173,14 @@ bool TypeParser::Parse(TypeAst* type) {
166
173
  type_->code = Type::String;
167
174
  break;
168
175
  }
176
+ case Token::QuotedIdentifier:
169
177
  case Token::Name:
178
+ if (!type_->name.empty()) {
179
+ // A second Name token on the same element means the
180
+ // previous one was a field name in a named-tuple element
181
+ // (e.g. "a" in "Tuple(a Int32, …)").
182
+ type_->element_name = std::move(type_->name);
183
+ }
170
184
  type_->meta = GetTypeMeta(token.value);
171
185
  type_->name = token.value.to_string();
172
186
  type_->code = GetTypeCode(type_->name);
@@ -247,6 +261,35 @@ TypeParser::Token TypeParser::NextToken() {
247
261
  }
248
262
  return Token{Token::QuotedString, StringView(cur_++, 1)};
249
263
  }
264
+ case '"':
265
+ case '`':
266
+ {
267
+ const auto quote = *cur_;
268
+ ++cur_;
269
+ // Two escape forms are recognised, both quote-specific (e.g.
270
+ // inside a backtick-quoted identifier only backtick escapes
271
+ // apply; a doubled double-quote is treated as two literals):
272
+ // \q – backslash followed by the opening quote character
273
+ // qq – two consecutive opening quote characters
274
+ scratch_.clear();
275
+ for (; cur_ < end_; ++cur_) {
276
+ if (*cur_ == '\\' && cur_ + 1 < end_ && *(cur_ + 1) == quote) {
277
+ scratch_ += quote;
278
+ ++cur_;
279
+ } else if (*cur_ == quote) {
280
+ if (cur_ + 1 < end_ && *(cur_ + 1) == quote) {
281
+ scratch_ += quote;
282
+ ++cur_;
283
+ } else {
284
+ ++cur_;
285
+ return Token{Token::QuotedIdentifier, StringView{scratch_}};
286
+ }
287
+ } else {
288
+ scratch_ += *cur_;
289
+ }
290
+ }
291
+ return Token{Token::Invalid, StringView()};
292
+ }
250
293
 
251
294
  default: {
252
295
  const char* st = cur_;
@@ -31,6 +31,9 @@ struct TypeAst {
31
31
  /// Type's name.
32
32
  /// Need to cache TypeAst, so can't use StringView for name.
33
33
  std::string name;
34
+ /// Name of this element inside its parent (e.g. field name inside a named
35
+ /// Tuple). Empty for unnamed elements.
36
+ std::string element_name;
34
37
  /// Value associated with the node,
35
38
  /// used for fixed-width types and enum values.
36
39
  int64_t value = 0;
@@ -59,6 +62,7 @@ class TypeParser {
59
62
  RPar,
60
63
  Comma,
61
64
  QuotedString, // string with quotation marks included
65
+ QuotedIdentifier,
62
66
  EOS,
63
67
  };
64
68
 
@@ -81,6 +85,11 @@ private:
81
85
 
82
86
  TypeAst* type_;
83
87
  std::stack<TypeAst*> open_elements_;
88
+ // Backing storage for unescaped QuotedIdentifier token values. When a
89
+ // quoted identifier contains escape sequences the unescaped content is
90
+ // written here and the returned StringView points into this string.
91
+ // Valid only until the next NextToken() call.
92
+ std::string scratch_;
84
93
  };
85
94
 
86
95
 
@@ -54,6 +54,8 @@ const char* Type::TypeName(Type::Code code) {
54
54
  case Type::Code::MultiPolygon: return "MultiPolygon";
55
55
  case Type::Code::Time: return "Time";
56
56
  case Type::Code::Time64: return "Time64";
57
+ case Type::Code::JSON: return "JSON";
58
+ case Type::Code::Bool: return "Bool";
57
59
  }
58
60
 
59
61
  return "Unknown type";
@@ -85,6 +87,8 @@ std::string Type::GetName() const {
85
87
  case Ring:
86
88
  case Polygon:
87
89
  case MultiPolygon:
90
+ case JSON:
91
+ case Bool:
88
92
  return TypeName(code_);
89
93
  case Time64:
90
94
  return As<Time64Type>()->GetName();
@@ -138,6 +142,7 @@ uint64_t Type::GetTypeUniqueId() const {
138
142
  case Float32:
139
143
  case Float64:
140
144
  case String:
145
+ case JSON:
141
146
  case IPv4:
142
147
  case IPv6:
143
148
  case Date:
@@ -146,6 +151,7 @@ uint64_t Type::GetTypeUniqueId() const {
146
151
  case Ring:
147
152
  case Polygon:
148
153
  case MultiPolygon:
154
+ case Bool:
149
155
  // For simple types, unique ID is the same as Type::Code
150
156
  return code_;
151
157
 
@@ -239,8 +245,9 @@ TypeRef Type::CreateString(size_t n) {
239
245
  return TypeRef(new FixedStringType(n));
240
246
  }
241
247
 
242
- TypeRef Type::CreateTuple(const std::vector<TypeRef>& item_types) {
243
- return TypeRef(new TupleType(item_types));
248
+ TypeRef Type::CreateTuple(const std::vector<TypeRef>& item_types,
249
+ std::vector<std::string> item_names) {
250
+ return TypeRef(new TupleType(item_types, std::move(item_names)));
244
251
  }
245
252
 
246
253
  TypeRef Type::CreateEnum8(const std::vector<EnumItem>& enum_items) {
@@ -279,6 +286,10 @@ TypeRef Type::CreateMultiPolygon() {
279
286
  return TypeRef(new Type(Type::MultiPolygon));
280
287
  }
281
288
 
289
+ TypeRef Type::CreateJSON() {
290
+ return TypeRef(new Type(Type::JSON));
291
+ }
292
+
282
293
  /// class ArrayType
283
294
 
284
295
  ArrayType::ArrayType(TypeRef item_type) : Type(Array), item_type_(item_type) {
@@ -442,9 +453,17 @@ FixedStringType::FixedStringType(size_t n) : Type(FixedString), size_(n) {
442
453
  NullableType::NullableType(TypeRef nested_type) : Type(Nullable), nested_type_(nested_type) {
443
454
  }
444
455
 
445
- /// class TupleType
446
-
447
- TupleType::TupleType(const std::vector<TypeRef>& item_types) : Type(Tuple), item_types_(item_types) {
456
+ TupleType::TupleType(const std::vector<TypeRef>& item_types,
457
+ std::vector<std::string> item_names)
458
+ : Type(Tuple), item_types_(item_types), item_names_(std::move(item_names)) {
459
+ if (!item_names_.empty() && item_names_.size() != item_types_.size()) {
460
+ throw ValidationError("Tuple field names count doesn't match tuple element count");
461
+ }
462
+ for (const auto& item_name : item_names_) {
463
+ if (item_name.empty()) {
464
+ throw ValidationError("Tuple field names can't be empty");
465
+ }
466
+ }
448
467
  }
449
468
 
450
469
  /// class LowCardinalityType
@@ -454,15 +473,46 @@ LowCardinalityType::LowCardinalityType(TypeRef nested_type) : Type(LowCardinalit
454
473
  LowCardinalityType::~LowCardinalityType() {
455
474
  }
456
475
 
457
- std::string TupleType::GetName() const {
458
- std::string result("Tuple(");
476
+ // Checks if `name` is a valid plain identifier (must not be quoted).
477
+ // The condition for this is a match against `^[a-zA-Z_][0-9a-zA-Z_]*$`
478
+ static bool IsPlainIdentifier(const std::string& name) {
479
+ if (name.empty()) return false;
480
+ auto is_alpha_or_under = [](char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; };
481
+ auto is_alnum_or_under = [&is_alpha_or_under](char c) { return is_alpha_or_under(c) || (c >= '0' && c <= '9'); };
482
+ if (!is_alpha_or_under(name[0])) return false;
483
+ for (size_t i = 1; i < name.size(); ++i)
484
+ if (!is_alnum_or_under(name[i])) return false;
485
+ return true;
486
+ }
459
487
 
460
- if (!item_types_.empty()) {
461
- result += item_types_[0]->GetName();
488
+ // Appends a fieldname, potentially quoting it and escaping backticks.
489
+ static void AppendFieldname(const std::string& name, std::string& out) {
490
+ if (IsPlainIdentifier(name)) {
491
+ out += name;
492
+ return;
493
+ }
494
+ out += '`';
495
+ for (char c : name) {
496
+ if (c == '`')
497
+ out += "``";
498
+ else
499
+ out += c;
462
500
  }
501
+ out += '`';
502
+ }
503
+
504
+ std::string TupleType::GetName() const {
505
+ std::string result("Tuple(");
506
+ bool has_complete_names = !item_names_.empty();
463
507
 
464
- for (size_t i = 1; i < item_types_.size(); ++i) {
465
- result += ", " + item_types_[i]->GetName();
508
+ for (size_t i = 0; i < item_types_.size(); ++i) {
509
+ if (i > 0)
510
+ result += ", ";
511
+ if (has_complete_names) {
512
+ AppendFieldname(item_names_[i], result);
513
+ result += ' ';
514
+ }
515
+ result += item_types_[i]->GetName();
466
516
  }
467
517
 
468
518
  result += ")";
@@ -59,6 +59,8 @@ public:
59
59
  MultiPolygon,
60
60
  Time,
61
61
  Time64,
62
+ JSON,
63
+ Bool,
62
64
  };
63
65
 
64
66
  using EnumItem = std::pair<std::string /* name */, int16_t /* value */>;
@@ -124,7 +126,8 @@ public:
124
126
 
125
127
  static TypeRef CreateString(size_t n);
126
128
 
127
- static TypeRef CreateTuple(const std::vector<TypeRef>& item_types);
129
+ static TypeRef CreateTuple(const std::vector<TypeRef>& item_types,
130
+ std::vector<std::string> item_names = {});
128
131
 
129
132
  static TypeRef CreateEnum8(const std::vector<EnumItem>& enum_items);
130
133
 
@@ -148,6 +151,8 @@ public:
148
151
 
149
152
  static TypeRef CreateTime64(size_t precision);
150
153
 
154
+ static TypeRef CreateJSON();
155
+
151
156
  private:
152
157
  uint64_t GetTypeUniqueId() const;
153
158
 
@@ -292,15 +297,21 @@ private:
292
297
 
293
298
  class TupleType : public Type {
294
299
  public:
295
- explicit TupleType(const std::vector<TypeRef>& item_types);
300
+ explicit TupleType(const std::vector<TypeRef>& item_types,
301
+ std::vector<std::string> item_names = {});
296
302
 
297
303
  std::string GetName() const;
298
304
 
299
305
  /// Type of nested Tuple element type.
300
306
  std::vector<TypeRef> GetTupleType() const { return item_types_; }
301
307
 
308
+ /// Field names for named tuples. Same length as GetTupleType() when
309
+ /// populated, or empty when the tuple has no field names.
310
+ const std::vector<std::string>& GetItemNames() const { return item_names_; }
311
+
302
312
  private:
303
313
  std::vector<TypeRef> item_types_;
314
+ std::vector<std::string> item_names_;
304
315
  };
305
316
 
306
317
  class LowCardinalityType : public Type {
@@ -384,6 +395,11 @@ inline TypeRef Type::CreateSimple<uint64_t>() {
384
395
  return TypeRef(new Type(UInt64));
385
396
  }
386
397
 
398
+ template <>
399
+ inline TypeRef Type::CreateSimple<bool>() {
400
+ return TypeRef(new Type(Bool));
401
+ }
402
+
387
403
  template <>
388
404
  inline TypeRef Type::CreateSimple<float>() {
389
405
  return TypeRef(new Type(Float32));
@@ -2,7 +2,7 @@
2
2
 
3
3
  #define CLICKHOUSE_CPP_VERSION_MAJOR 2
4
4
  #define CLICKHOUSE_CPP_VERSION_MINOR 6
5
- #define CLICKHOUSE_CPP_VERSION_PATCH 1
5
+ #define CLICKHOUSE_CPP_VERSION_PATCH 2
6
6
 
7
7
  #define CLICKHOUSE_CPP_VERSION_BUILD 0
8
8
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ClickhouseNative
4
4
  class Client
5
- attr_reader :host, :port, :database
5
+ attr_reader :host, :port, :database, :ping_before_query, :tcp_keepalive, :retry_timeout
6
6
 
7
7
  def describe_table(table, db_name: nil)
8
8
  fq = db_name ? "#{db_name}.#{table}" : table
@@ -8,16 +8,17 @@ module ClickhouseNative
8
8
 
9
9
  def initialize(host:, port:, database: "default", user: "default", password: "",
10
10
  compression: :none, logger: nil, settings: {},
11
- pool_size: 5, pool_timeout: 5)
11
+ pool_size: 5, pool_timeout: 5,
12
+ ping_before_query: true, tcp_keepalive: true, retry_timeout: 1)
12
13
  @host = host
13
14
  @port = port
14
15
  @database = database
15
- client_kwargs = { host:, port:, database:, user:, password:, compression:, logger: }
16
- @set_sql = settings_sql(settings)
16
+ client_kwargs = {
17
+ host:, port:, database:, user:, password:, compression:, logger:, settings:,
18
+ ping_before_query:, tcp_keepalive:, retry_timeout:
19
+ }
17
20
  @pool = ConnectionPool.new(size: pool_size, timeout: pool_timeout) do
18
- client = Client.new(**client_kwargs)
19
- client.execute(@set_sql) if @set_sql
20
- client
21
+ Client.new(**client_kwargs)
21
22
  end
22
23
  end
23
24
 
@@ -25,18 +26,24 @@ module ClickhouseNative
25
26
  # path leaves the socket in an unknown state. The C++ binding issues
26
27
  # ResetConnection, but a subsequent send can still surface buffered
27
28
  # protocol errors from the prior aborted operation — those get
28
- # attributed to whatever SQL we tried next (e.g. the SET reapplying
29
- # session settings), producing misleading log lines and re-raises in
30
- # unrelated code. A fresh socket + handshake is cheap relative to
31
- # debugging that.
29
+ # attributed to whatever SQL we tried next, producing misleading log
30
+ # lines and re-raises in unrelated code. A fresh socket + handshake is
31
+ # cheap relative to debugging that.
32
32
  #
33
33
  # ConnectionError gets one automatic retry: pooled connections that
34
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.
35
+ # surface as "closed" on the very next recv (errno is whatever stale
36
+ # value was left in the thread — "closed: Success", "closed: Operation
37
+ # now in progress", etc. all mean the same recv()==0). Discarding and
38
+ # re-checking out lands a fresh socket and the operation succeeds. The
39
+ # retry only triggers when the dead-connection error fired before any
40
+ # data was sent, so write operations don't risk double-execution.
41
+ #
42
+ # This is the backstop: with ping_before_query on (the default) the
43
+ # driver already pings and transparently reconnects a dead socket
44
+ # before running the query, so most stale connections never surface as
45
+ # a ConnectionError here at all. This retry still covers the residual
46
+ # race (socket dies between the ping and the query).
40
47
  def with
41
48
  attempts = 0
42
49
  begin
@@ -84,21 +91,5 @@ module ClickhouseNative
84
91
  def describe_table(table, db_name: nil)
85
92
  with { |c| c.describe_table(table, db_name:) }
86
93
  end
87
-
88
- private
89
-
90
- # Render a `SET key1 = val1, key2 = val2` statement once at pool setup
91
- # so every checked-out connection starts with the same session
92
- # settings. Matches how the HTTP driver injected global_params per
93
- # request. Values: Integer / Float render bare; anything else is
94
- # quoted as a SQL string literal.
95
- def settings_sql(settings)
96
- return nil if settings.nil? || settings.empty?
97
- parts = settings.map do |k, v|
98
- literal = v.is_a?(Numeric) ? v.to_s : "'#{v.to_s.gsub("'", "''")}'"
99
- "#{k} = #{literal}"
100
- end
101
- "SET #{parts.join(', ')}"
102
- end
103
94
  end
104
95
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClickhouseNative
4
- VERSION = "0.9.0"
4
+ VERSION = "0.11.0"
5
5
  end
@@ -3,6 +3,7 @@
3
3
  require "time"
4
4
  require "date"
5
5
  require "bigdecimal"
6
+ require "json"
6
7
  require "clickhouse_native/version"
7
8
  require "clickhouse_native/errors"
8
9
 
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.9.0
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuri Smirnov
@@ -35,19 +35,29 @@ files:
35
35
  - ext/clickhouse_native/client.cpp
36
36
  - ext/clickhouse_native/extconf.rb
37
37
  - ext/clickhouse_native/patches/0001-preserve-declared-column-type.patch
38
+ - ext/clickhouse_native/patches/0002-carry-settings-into-insert.patch
38
39
  - ext/clickhouse_native/vendor/clickhouse-cpp/.clang-format
39
40
  - ext/clickhouse_native/vendor/clickhouse-cpp/.git
40
41
  - ext/clickhouse_native/vendor/clickhouse-cpp/.gitattributes
41
42
  - ext/clickhouse_native/vendor/clickhouse-cpp/.github/CODEOWNERS
43
+ - ext/clickhouse_native/vendor/clickhouse-cpp/.github/workflows/bazel.yml
44
+ - ext/clickhouse_native/vendor/clickhouse-cpp/.github/workflows/cross-repo-bug-relay.yml
42
45
  - ext/clickhouse_native/vendor/clickhouse-cpp/.github/workflows/linux.yml
43
46
  - ext/clickhouse_native/vendor/clickhouse-cpp/.github/workflows/macos.yml
44
47
  - ext/clickhouse_native/vendor/clickhouse-cpp/.github/workflows/windows_mingw.yml
45
48
  - ext/clickhouse_native/vendor/clickhouse-cpp/.github/workflows/windows_msvc.yml
46
49
  - ext/clickhouse_native/vendor/clickhouse-cpp/.gitignore
47
50
  - ext/clickhouse_native/vendor/clickhouse-cpp/.travis.yml
51
+ - ext/clickhouse_native/vendor/clickhouse-cpp/AI_POLICY.md
52
+ - ext/clickhouse_native/vendor/clickhouse-cpp/BUILD.bazel
48
53
  - ext/clickhouse_native/vendor/clickhouse-cpp/CMakeLists.txt
49
54
  - ext/clickhouse_native/vendor/clickhouse-cpp/LICENSE
55
+ - ext/clickhouse_native/vendor/clickhouse-cpp/MODULE.bazel
56
+ - ext/clickhouse_native/vendor/clickhouse-cpp/MODULE.bazel.lock
50
57
  - ext/clickhouse_native/vendor/clickhouse-cpp/README.md
58
+ - ext/clickhouse_native/vendor/clickhouse-cpp/ci/docker-compose.yml
59
+ - ext/clickhouse_native/vendor/clickhouse-cpp/ci/docker-compose/config.xml
60
+ - ext/clickhouse_native/vendor/clickhouse-cpp/ci/docker-compose/users.xml
51
61
  - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/CMakeLists.txt
52
62
  - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/base/buffer.h
53
63
  - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/base/compressed.cpp
@@ -78,6 +88,8 @@ files:
78
88
  - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/client.h
79
89
  - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/array.cpp
80
90
  - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/array.h
91
+ - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/bool.cpp
92
+ - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/bool.h
81
93
  - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/column.cpp
82
94
  - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/column.h
83
95
  - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/date.cpp
@@ -96,6 +108,8 @@ files:
96
108
  - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/ip6.h
97
109
  - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/itemview.cpp
98
110
  - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/itemview.h
111
+ - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/json.cpp
112
+ - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/json.h
99
113
  - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/lowcardinality.cpp
100
114
  - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/lowcardinality.h
101
115
  - ext/clickhouse_native/vendor/clickhouse-cpp/clickhouse/columns/lowcardinalityadaptor.h
@@ -310,7 +324,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
310
324
  - !ruby/object:Gem::Version
311
325
  version: '0'
312
326
  requirements: []
313
- rubygems_version: 4.0.10
327
+ rubygems_version: 4.0.16
314
328
  specification_version: 4
315
329
  summary: ClickHouse Ruby driver over the native TCP protocol
316
330
  test_files: []