ch_connect 0.3.1 → 0.4.1
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/CHANGELOG.md +14 -0
- data/README.md +8 -8
- data/ext/ch_connect_native/ch_connect_native.c +196 -49
- data/lib/ch_connect/connection.rb +19 -1
- data/lib/ch_connect/response.rb +12 -1
- data/lib/ch_connect/version.rb +1 -1
- data/vendor/clickhouse-c/VENDOR.md +5 -2
- data/vendor/clickhouse-c/clickhouse-async.h +15 -22
- data/vendor/clickhouse-c/clickhouse-client.h +187 -212
- data/vendor/clickhouse-c/clickhouse-compression.h +8 -5
- data/vendor/clickhouse-c/clickhouse.h +1033 -982
- metadata +1 -1
|
@@ -68,6 +68,21 @@
|
|
|
68
68
|
# define CHC_REPRODUCIBLE
|
|
69
69
|
#endif
|
|
70
70
|
|
|
71
|
+
#if CHC__HAS_ATTR(fallthrough)
|
|
72
|
+
# define CHC_FALLTHROUGH [[fallthrough]]
|
|
73
|
+
#else
|
|
74
|
+
# define CHC_FALLTHROUGH
|
|
75
|
+
#endif
|
|
76
|
+
|
|
77
|
+
/* unreachable() is C23 <stddef.h>, already included above. */
|
|
78
|
+
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
|
|
79
|
+
# define CHC_UNREACHABLE() unreachable()
|
|
80
|
+
#elif defined(__GNUC__) || defined(__clang__)
|
|
81
|
+
# define CHC_UNREACHABLE() __builtin_unreachable()
|
|
82
|
+
#else
|
|
83
|
+
# define CHC_UNREACHABLE() ((void) 0)
|
|
84
|
+
#endif
|
|
85
|
+
|
|
71
86
|
/* ckd_mul (C23 <stdckdint.h>) backs chc__mul_size; see CHC__HAVE_CKD_MUL. */
|
|
72
87
|
#if defined(__has_include)
|
|
73
88
|
# if __has_include(<stdckdint.h>)
|
|
@@ -102,16 +117,11 @@ enum {
|
|
|
102
117
|
#endif
|
|
103
118
|
|
|
104
119
|
typedef struct chc_err {
|
|
105
|
-
int server_code;
|
|
106
120
|
char msg[CHC_ERR_MSG_LEN];
|
|
107
|
-
char server_name[64];
|
|
108
121
|
} chc_err;
|
|
109
122
|
|
|
110
123
|
static inline void chc_err_reset(chc_err *e) {
|
|
111
|
-
if (
|
|
112
|
-
e->server_code = 0;
|
|
113
|
-
e->msg[0] = '\0';
|
|
114
|
-
e->server_name[0] = '\0';
|
|
124
|
+
if (e) e->msg[0] = '\0';
|
|
115
125
|
}
|
|
116
126
|
|
|
117
127
|
/* -------------------------------------------------------------------------- */
|
|
@@ -180,7 +190,8 @@ typedef enum chc_kind {
|
|
|
180
190
|
CHC_NULLABLE, CHC_ARRAY, CHC_TUPLE, CHC_MAP, CHC_NESTED,
|
|
181
191
|
CHC_LOW_CARDINALITY,
|
|
182
192
|
CHC_INTERVAL,
|
|
183
|
-
CHC_POINT, CHC_RING,
|
|
193
|
+
CHC_POINT, CHC_RING, CHC_LINE_STRING,
|
|
194
|
+
CHC_POLYGON, CHC_MULTI_POLYGON, CHC_MULTI_LINE_STRING,
|
|
184
195
|
CHC_VARIANT, CHC_DYNAMIC, CHC_JSON, CHC_OBJECT,
|
|
185
196
|
CHC_AGGREGATE_FUNCTION, CHC_SIMPLE_AGGREGATE_FUNCTION,
|
|
186
197
|
CHC_QBIT,
|
|
@@ -188,6 +199,22 @@ typedef enum chc_kind {
|
|
|
188
199
|
CHC_KIND_COUNT
|
|
189
200
|
} chc_kind;
|
|
190
201
|
|
|
202
|
+
/* Interval unit, ordered as ClickHouse IntervalKind */
|
|
203
|
+
typedef enum chc_interval_unit {
|
|
204
|
+
CHC_INTERVAL_NONE = 0,
|
|
205
|
+
CHC_INTERVAL_NANOSECOND,
|
|
206
|
+
CHC_INTERVAL_MICROSECOND,
|
|
207
|
+
CHC_INTERVAL_MILLISECOND,
|
|
208
|
+
CHC_INTERVAL_SECOND,
|
|
209
|
+
CHC_INTERVAL_MINUTE,
|
|
210
|
+
CHC_INTERVAL_HOUR,
|
|
211
|
+
CHC_INTERVAL_DAY,
|
|
212
|
+
CHC_INTERVAL_WEEK,
|
|
213
|
+
CHC_INTERVAL_MONTH,
|
|
214
|
+
CHC_INTERVAL_QUARTER,
|
|
215
|
+
CHC_INTERVAL_YEAR,
|
|
216
|
+
} chc_interval_unit;
|
|
217
|
+
|
|
191
218
|
typedef struct chc_type chc_type;
|
|
192
219
|
|
|
193
220
|
CHC_NODISCARD int chc_type_parse(const char *name, size_t name_len,
|
|
@@ -204,6 +231,9 @@ int chc_type_decimal_precision(const chc_type *t);
|
|
|
204
231
|
int chc_type_decimal_scale(const chc_type *t);
|
|
205
232
|
int chc_type_datetime64_scale(const chc_type *t);
|
|
206
233
|
|
|
234
|
+
/* Values arrive as Int64 ticks of unit. */
|
|
235
|
+
chc_interval_unit chc_type_interval_unit(const chc_type *t);
|
|
236
|
+
|
|
207
237
|
/* QBit(T, N): N (vector dimension). 0 on non-QBit types. The element type
|
|
208
238
|
* (BFloat16/Float32/Float64) is children[0], reached via chc_type_child(t, 0). */
|
|
209
239
|
size_t chc_type_qbit_dimension(const chc_type *t);
|
|
@@ -218,11 +248,14 @@ void chc_type_enum_at(const chc_type *t, size_t i,
|
|
|
218
248
|
const char **name, size_t *name_len,
|
|
219
249
|
int64_t *value);
|
|
220
250
|
|
|
221
|
-
/*
|
|
222
|
-
*
|
|
251
|
+
/* Return a Tuple or Nested field name, or NULL for unnamed fields,
|
|
252
|
+
* invalid indexes, or other types */
|
|
223
253
|
const char *chc_type_tuple_field_name(const chc_type *t, size_t i,
|
|
224
254
|
size_t *out_len);
|
|
225
255
|
|
|
256
|
+
/* Return an aggregate function name without parameters, or NULL when absent */
|
|
257
|
+
const char *chc_type_agg_function(const chc_type *t, size_t *out_len);
|
|
258
|
+
|
|
226
259
|
/* Reproduce the printable type name into buf. Returns the number of bytes
|
|
227
260
|
* that would have been written (snprintf-style); use to size buf on a
|
|
228
261
|
* second pass when the return value >= buf_len. buf may be NULL when
|
|
@@ -245,6 +278,23 @@ typedef enum chc_col_kind {
|
|
|
245
278
|
|
|
246
279
|
typedef struct chc_column chc_column;
|
|
247
280
|
|
|
281
|
+
/* Column tree. Reader allocates & owns instances, freed by chc_block_destroy.
|
|
282
|
+
* chc_build_* initializes caller-owned instances, usually on stack, over
|
|
283
|
+
* caller slabs. Pointers use host byte order; writer converts to LE on BE
|
|
284
|
+
* hosts. Access through helpers below or fields directly */
|
|
285
|
+
struct chc_column {
|
|
286
|
+
chc_col_kind layout;
|
|
287
|
+
size_t n_rows;
|
|
288
|
+
union {
|
|
289
|
+
struct { void *data; size_t elem_size; } fixed;
|
|
290
|
+
struct { uint8_t *data; uint64_t *offsets; size_t bytes; } str;
|
|
291
|
+
struct { uint8_t *null_map; chc_column *inner; } nullable;
|
|
292
|
+
struct { uint64_t *offsets; chc_column *values; } array;
|
|
293
|
+
struct { chc_column **children; size_t arity; } tuple;
|
|
294
|
+
struct { int key_size; void *keys; chc_column *dict; size_t dict_n; } lc;
|
|
295
|
+
};
|
|
296
|
+
};
|
|
297
|
+
|
|
248
298
|
chc_col_kind chc_column_layout(const chc_column *c);
|
|
249
299
|
size_t chc_column_n_rows(const chc_column *c);
|
|
250
300
|
|
|
@@ -283,10 +333,10 @@ const chc_column *chc_column_lc_dict(const chc_column *c);
|
|
|
283
333
|
|
|
284
334
|
/* Walk a column tree & enforce cross-field invariants the server itself
|
|
285
335
|
* enforces on its native deserialization path:
|
|
286
|
-
* - Array offsets non-decreasing
|
|
287
|
-
* "Arrays offsets are not monotonically increasing")
|
|
288
|
-
* - LowCardinality keys < dict size
|
|
289
|
-
* "Index for LowCardinality is out of range")
|
|
336
|
+
* - Array offsets non-decreasing
|
|
337
|
+
* ("Arrays offsets are not monotonically increasing")
|
|
338
|
+
* - LowCardinality keys < dict size
|
|
339
|
+
* ("Index for LowCardinality is out of range")
|
|
290
340
|
* chc_block_read does NOT call this automatically — a peer that forges
|
|
291
341
|
* offsets or LC keys can cause callers to read past inner-column bounds.
|
|
292
342
|
* Consumers ingesting from untrusted senders should call this on each
|
|
@@ -302,13 +352,14 @@ CHC_NODISCARD int chc_column_validate(const chc_column *c, chc_err *err);
|
|
|
302
352
|
typedef struct chc_block chc_block;
|
|
303
353
|
|
|
304
354
|
typedef struct chc_block_opts {
|
|
305
|
-
/* TCP path
|
|
306
|
-
*
|
|
355
|
+
/* TCP path: an 8-byte BlockInfo prefix is on the wire before
|
|
356
|
+
* num_columns. clickhouse-local does not emit it. */
|
|
307
357
|
bool has_block_info;
|
|
308
358
|
|
|
309
|
-
/* TCP path
|
|
310
|
-
*
|
|
311
|
-
*
|
|
359
|
+
/* TCP path: a has_custom_serialization byte follows each column's type
|
|
360
|
+
* name, with serialization kinds when set. Reader decodes sparse
|
|
361
|
+
* columns into dense layouts, writer always emits 0. clickhouse-local
|
|
362
|
+
* does not emit it. */
|
|
312
363
|
bool has_custom_serialization;
|
|
313
364
|
|
|
314
365
|
/* Internal read-buffer size. 0 = default (8 KiB). */
|
|
@@ -339,124 +390,67 @@ int32_t chc_block_bucket_num(const chc_block *b);
|
|
|
339
390
|
/* Block writer */
|
|
340
391
|
/* -------------------------------------------------------------------------- */
|
|
341
392
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
/*
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
* must match the helper variant (e.g. _nullable_fixed expects
|
|
368
|
-
* Nullable(<fixed>), _array_string expects Array(String), and
|
|
369
|
-
* _low_cardinality_string expects LowCardinality(String) or
|
|
370
|
-
* LowCardinality(Nullable(String))).
|
|
393
|
+
/* Block column: name, full CH type & column tree. Build tree with chc_build_*
|
|
394
|
+
* or reuse reader output for round-trip. Tree stays caller-owned & must
|
|
395
|
+
* outlive write */
|
|
396
|
+
typedef struct {
|
|
397
|
+
const char *name;
|
|
398
|
+
size_t name_len;
|
|
399
|
+
const chc_type *type;
|
|
400
|
+
const chc_column *col;
|
|
401
|
+
} chc_block_col;
|
|
402
|
+
|
|
403
|
+
/* Stack builder over caller-provided chc_block_col storage. Initialize with the
|
|
404
|
+
* array, append columns, then call chc_block_write. Caller sizes the array;
|
|
405
|
+
* appending more columns than it holds is undefined. Skip builder by passing an
|
|
406
|
+
* array directly to chc_block_write_cols */
|
|
407
|
+
typedef struct chc_block_builder {
|
|
408
|
+
chc_block_col *cols; /* caller-owned storage */
|
|
409
|
+
size_t n_cols;
|
|
410
|
+
size_t n_rows; /* shared across columns */
|
|
411
|
+
} chc_block_builder;
|
|
412
|
+
|
|
413
|
+
void chc_block_builder_init(chc_block_builder *bb, chc_block_col *cols);
|
|
414
|
+
|
|
415
|
+
/* Build column trees over caller-owned slabs without copying. Children must
|
|
416
|
+
* outlive write. Nest constructors to match any composite reader emits, e.g.
|
|
417
|
+
* Array(LowCardinality(Nullable(String))):
|
|
371
418
|
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
/* Nested Array(Array(...(<fixed/string>))) variants. `t` is top-level
|
|
409
|
-
* Array type, `ndim` is nesting depth (must match `t`). level_offsets
|
|
410
|
-
* is ndim cumulative-end arrays ordered outer-to-inner, level_offsets_len
|
|
411
|
-
* gives count at each level. n_rows is top-level row count, must equal
|
|
412
|
-
* level_offsets_len[0] */
|
|
413
|
-
CHC_NODISCARD int chc_block_builder_append_array_nested_fixed(
|
|
414
|
-
chc_block_builder *bb,
|
|
415
|
-
const char *name, size_t name_len,
|
|
416
|
-
const chc_type *t,
|
|
417
|
-
int ndim,
|
|
418
|
-
const uint64_t * const *level_offsets,
|
|
419
|
-
const size_t *level_offsets_len,
|
|
420
|
-
const void *values,
|
|
421
|
-
size_t n_rows, chc_err *err);
|
|
422
|
-
|
|
423
|
-
CHC_NODISCARD int chc_block_builder_append_array_nested_string(
|
|
424
|
-
chc_block_builder *bb,
|
|
425
|
-
const char *name, size_t name_len,
|
|
426
|
-
const chc_type *t,
|
|
427
|
-
int ndim,
|
|
428
|
-
const uint64_t * const *level_offsets,
|
|
429
|
-
const size_t *level_offsets_len,
|
|
430
|
-
const uint64_t *values_offsets,
|
|
431
|
-
const uint8_t *values_data,
|
|
432
|
-
size_t n_rows, chc_err *err);
|
|
433
|
-
|
|
434
|
-
/* LowCardinality(String) or LowCardinality(Nullable(String)). For the
|
|
435
|
-
* Nullable variant the caller must place a null-sentinel entry at dict
|
|
436
|
-
* index 0 (CH convention) and use key 0 for null rows. */
|
|
437
|
-
/* JSON column, STRING serialization. `t` must be CHC_JSON. Rows are JSON
|
|
438
|
-
* document text, one per offset; builder emits an 8-byte LE serialization-
|
|
439
|
-
* version prefix (value 1) once before the same wire format as
|
|
440
|
-
* chc_block_builder_append_string. Caller is responsible for the input
|
|
441
|
-
* being valid JSON; server rejects malformed documents at INSERT time. */
|
|
442
|
-
CHC_NODISCARD int chc_block_builder_append_json_string(
|
|
443
|
-
chc_block_builder *bb,
|
|
444
|
-
const char *name, size_t name_len,
|
|
445
|
-
const chc_type *t, /* CHC_JSON */
|
|
446
|
-
const uint64_t *offsets,
|
|
447
|
-
const uint8_t *data,
|
|
448
|
-
size_t n_rows, chc_err *err);
|
|
449
|
-
|
|
450
|
-
CHC_NODISCARD int chc_block_builder_append_low_cardinality_string(
|
|
451
|
-
chc_block_builder *bb,
|
|
452
|
-
const char *name, size_t name_len,
|
|
453
|
-
const chc_type *t,
|
|
454
|
-
int key_size,
|
|
455
|
-
const void *keys,
|
|
456
|
-
const uint64_t *dict_offsets,
|
|
457
|
-
const uint8_t *dict_data,
|
|
458
|
-
size_t dict_n,
|
|
459
|
-
size_t n_rows, chc_err *err);
|
|
419
|
+
* chc_column d = chc_build_string(dict_offs, dict_data, dict_n);
|
|
420
|
+
* chc_column n = chc_build_nullable(dict_null_map, &d);
|
|
421
|
+
* chc_column l = chc_build_lc(key_size, keys, leaf_rows, &n);
|
|
422
|
+
* chc_column a = chc_build_array(offsets, n_rows, &l);
|
|
423
|
+
* chc_block_builder_append(bb, "c", 1, t, &a);
|
|
424
|
+
*
|
|
425
|
+
* Each node stores row count for its level. Fixed, string, array & LC builders
|
|
426
|
+
* receive it explicitly; nullable & tuple builders derive it from children.
|
|
427
|
+
* Array leaf count equals offsets[n_rows - 1]; LC leaf count equals key count.
|
|
428
|
+
* Fixed data contains n_rows * elem_size LE bytes; string offsets contain
|
|
429
|
+
* cumulative exclusive ends. For LC(Nullable(T)), supply inner-typed dict
|
|
430
|
+
* with null sentinel at slot 0 or Nullable wrapper. Plain String, FixedString,
|
|
431
|
+
* JSON & Object need one matching chc_build_fixed or chc_build_string */
|
|
432
|
+
chc_column chc_build_fixed(const void *data, size_t elem_size, size_t n_rows);
|
|
433
|
+
chc_column chc_build_string(const uint64_t *offsets, const uint8_t *data,
|
|
434
|
+
size_t n_rows);
|
|
435
|
+
chc_column chc_build_nullable(const uint8_t *null_map, chc_column *inner);
|
|
436
|
+
chc_column chc_build_array(const uint64_t *offsets, size_t n_rows,
|
|
437
|
+
chc_column *values);
|
|
438
|
+
chc_column chc_build_tuple(chc_column **children, size_t arity);
|
|
439
|
+
chc_column chc_build_lc(int key_size, const void *keys, size_t n_rows,
|
|
440
|
+
chc_column *dict);
|
|
441
|
+
|
|
442
|
+
/* Append column. `t` gives full CH type; `col` must match structurally & share
|
|
443
|
+
* the block row count, both checked during write. Caller must not exceed the
|
|
444
|
+
* storage passed to chc_block_builder_init */
|
|
445
|
+
void chc_block_builder_append(chc_block_builder *bb,
|
|
446
|
+
const char *name, size_t name_len,
|
|
447
|
+
const chc_type *t, const chc_column *col);
|
|
448
|
+
|
|
449
|
+
/* Write directly from caller-built columns. Require cols[i].col->n_rows ==
|
|
450
|
+
* n_rows for every column */
|
|
451
|
+
CHC_NODISCARD int chc_block_write_cols(chc_io *io, const chc_block_col *cols,
|
|
452
|
+
size_t n_cols, size_t n_rows,
|
|
453
|
+
const chc_block_opts *opts, chc_err *err);
|
|
460
454
|
|
|
461
455
|
CHC_NODISCARD int chc_block_write(chc_io *io, const chc_block_builder *bb,
|
|
462
456
|
const chc_block_opts *opts, chc_err *err);
|
|
@@ -504,10 +498,10 @@ static inline uint64_t chc__bswap64(uint64_t v) {
|
|
|
504
498
|
/* -------- CityHash short-string helpers ---------- */
|
|
505
499
|
|
|
506
500
|
/* Frozen v1.0.3 variant of CityHash, ported from city.cc.
|
|
507
|
-
* Original: Copyright (c) 2011 Google, Inc. (MIT
|
|
508
|
-
* Short-string path lives here so
|
|
509
|
-
*
|
|
510
|
-
*
|
|
501
|
+
* Original: Copyright (c) 2011 Google, Inc. (MIT license).
|
|
502
|
+
* Short-string path lives here so chc__name_lookup can reuse it; 128-bit
|
|
503
|
+
* compressed-frame checksum driver in clickhouse-compression.h builds on
|
|
504
|
+
* these helpers */
|
|
511
505
|
|
|
512
506
|
static uint64_t chc__city_fetch64(const char *p) CHC_REPRODUCIBLE
|
|
513
507
|
{
|
|
@@ -596,14 +590,10 @@ static int CHC__PRINTF_FMT(3, 4)
|
|
|
596
590
|
chc__err_set(chc_err *e, int code, const char *fmt, ...)
|
|
597
591
|
{
|
|
598
592
|
if (!e) return code;
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
__builtin_va_end(ap);
|
|
604
|
-
} else {
|
|
605
|
-
e->msg[0] = '\0';
|
|
606
|
-
}
|
|
593
|
+
va_list ap;
|
|
594
|
+
va_start(ap, fmt);
|
|
595
|
+
vsnprintf(e->msg, sizeof e->msg, fmt, ap);
|
|
596
|
+
va_end(ap);
|
|
607
597
|
return code;
|
|
608
598
|
}
|
|
609
599
|
|
|
@@ -731,11 +721,7 @@ chc_alloc chc_alloc_stdlib(void) {
|
|
|
731
721
|
#define CHC_READ_BUFFER 8192
|
|
732
722
|
#endif
|
|
733
723
|
|
|
734
|
-
/*
|
|
735
|
-
* https://github.com/ClickHouse/ClickHouse/blob/ef11941cf5a/src/IO/ReadHelpers.h#L38
|
|
736
|
-
* https://github.com/ClickHouse/ClickHouse/blob/ef11941cf5a/src/Core/Defines.h#L156-L158
|
|
737
|
-
* https://github.com/ClickHouse/ClickHouse/blob/ef11941cf5a/src/DataTypes/DataTypeFixedString.h#L5
|
|
738
|
-
* https://github.com/ClickHouse/ClickHouse/blob/ef11941cf5a/src/DataTypes/DataTypeFactory.cpp#L122-L127 */
|
|
724
|
+
/* Mirrors the limits ClickHouse enforces server-side. */
|
|
739
725
|
#ifndef CHC_MAX_STRING_SIZE
|
|
740
726
|
#define CHC_MAX_STRING_SIZE (1ULL << 30)
|
|
741
727
|
#endif
|
|
@@ -795,9 +781,9 @@ chc_in_init_ioless(chc_in *in, const chc_alloc *al)
|
|
|
795
781
|
return CHC_OK;
|
|
796
782
|
}
|
|
797
783
|
|
|
798
|
-
/* Drop prefix [0, keep)
|
|
799
|
-
* consumed
|
|
800
|
-
* mark
|
|
784
|
+
/* Drop prefix [0, keep), using mark while checkpoint is live, otherwise pos.
|
|
785
|
+
* consumed tracks returned bytes rather than offsets, so compaction leaves it
|
|
786
|
+
* unchanged; shifting mark & pos preserves pos - mark for rewind */
|
|
801
787
|
static void
|
|
802
788
|
chc__in_compact(chc_in *in)
|
|
803
789
|
{
|
|
@@ -853,8 +839,8 @@ chc_in_free(chc_in *in)
|
|
|
853
839
|
in->buf = NULL;
|
|
854
840
|
}
|
|
855
841
|
|
|
856
|
-
/* Mark read cursor
|
|
857
|
-
*
|
|
842
|
+
/* Mark read cursor for rewind. Ioless mode checkpoints at packet boundaries,
|
|
843
|
+
* allowing mid-parse CHC_WOULD_BLOCK to rewind & retry after more bytes arrive */
|
|
858
844
|
CHC_MAYBE_UNUSED static void
|
|
859
845
|
chc__in_checkpoint(chc_in *in)
|
|
860
846
|
{
|
|
@@ -887,7 +873,7 @@ chc__in_refill(chc_in *in, chc_err *err)
|
|
|
887
873
|
if (in->eof) return chc__err_set(err, CHC_ERR_EOF, "unexpected eof");
|
|
888
874
|
|
|
889
875
|
if (in->io->check_cancel && in->io->check_cancel(in->io->ud))
|
|
890
|
-
return chc__err_set(err, CHC_ERR_CANCELLED, "
|
|
876
|
+
return chc__err_set(err, CHC_ERR_CANCELLED, "canceled");
|
|
891
877
|
|
|
892
878
|
in->pos = 0;
|
|
893
879
|
in->fill = 0;
|
|
@@ -933,13 +919,13 @@ chc__read_bytes(chc_in *in, void *dst, size_t n, chc_err *err)
|
|
|
933
919
|
* straight into caller's dst to skip the staging memcpy. Only fires
|
|
934
920
|
* after the staging buf is drained, so buffered-reader invariants
|
|
935
921
|
* (pos, fill, consumed) stay consistent. Disabled in ioless: bypassed
|
|
936
|
-
* bytes
|
|
937
|
-
*
|
|
922
|
+
* bytes bypass in->buf & cannot be rewound; ioless routes all reads
|
|
923
|
+
* through growable staging buf. */
|
|
938
924
|
while (!CHC__IOLESS(in) && n > in->cap) {
|
|
939
925
|
if (in->eof)
|
|
940
926
|
return chc__err_set(err, CHC_ERR_EOF, "short read");
|
|
941
927
|
if (in->io->check_cancel && in->io->check_cancel(in->io->ud))
|
|
942
|
-
return chc__err_set(err, CHC_ERR_CANCELLED, "
|
|
928
|
+
return chc__err_set(err, CHC_ERR_CANCELLED, "canceled");
|
|
943
929
|
size_t got = 0;
|
|
944
930
|
int rc = in->io->read(in->io->ud, p, n, &got, err);
|
|
945
931
|
if (rc != CHC_OK) {
|
|
@@ -973,6 +959,19 @@ chc__read_bytes(chc_in *in, void *dst, size_t n, chc_err *err)
|
|
|
973
959
|
return CHC_OK;
|
|
974
960
|
}
|
|
975
961
|
|
|
962
|
+
static int
|
|
963
|
+
chc__skip_bytes(chc_in *in, size_t n, chc_err *err)
|
|
964
|
+
{
|
|
965
|
+
uint8_t sink[256];
|
|
966
|
+
while (n) {
|
|
967
|
+
size_t take = n < sizeof sink ? n : sizeof sink;
|
|
968
|
+
int rc = chc__read_bytes(in, sink, take, err);
|
|
969
|
+
if (rc != CHC_OK) return rc;
|
|
970
|
+
n -= take;
|
|
971
|
+
}
|
|
972
|
+
return CHC_OK;
|
|
973
|
+
}
|
|
974
|
+
|
|
976
975
|
static int
|
|
977
976
|
chc__read_varuint(chc_in *in, uint64_t *out, chc_err *err)
|
|
978
977
|
{
|
|
@@ -1046,6 +1045,8 @@ struct chc_type {
|
|
|
1046
1045
|
struct { int precision, scale; } decimal; /* Decimal(P, S) */
|
|
1047
1046
|
struct { int scale; char *tz; size_t tz_len; } temporal; /* DateTime / DateTime64 / Time64 */
|
|
1048
1047
|
struct { size_t dimension; } qbit; /* QBit(T, N): N; element type in children[0] */
|
|
1048
|
+
struct { char *func; size_t func_len; } agg;
|
|
1049
|
+
chc_interval_unit interval; /* IntervalNanosecond .. IntervalYear */
|
|
1049
1050
|
struct {
|
|
1050
1051
|
size_t n;
|
|
1051
1052
|
struct { char *name; uint32_t name_len; int16_t value; } *items;
|
|
@@ -1059,6 +1060,8 @@ static bool chc__kind_is_enum(chc_kind k)
|
|
|
1059
1060
|
{ return k == CHC_ENUM8 || k == CHC_ENUM16; }
|
|
1060
1061
|
static bool chc__kind_has_tz(chc_kind k)
|
|
1061
1062
|
{ return k == CHC_DATETIME || k == CHC_DATETIME64 || k == CHC_TIME64; }
|
|
1063
|
+
static bool chc__kind_is_agg(chc_kind k)
|
|
1064
|
+
{ return k == CHC_AGGREGATE_FUNCTION || k == CHC_SIMPLE_AGGREGATE_FUNCTION; }
|
|
1062
1065
|
|
|
1063
1066
|
void
|
|
1064
1067
|
chc_type_destroy(chc_type *t, const chc_alloc *al)
|
|
@@ -1081,6 +1084,8 @@ chc_type_destroy(chc_type *t, const chc_alloc *al)
|
|
|
1081
1084
|
t->enum_.n * sizeof *t->enum_.items);
|
|
1082
1085
|
} else if (chc__kind_has_tz(t->kind))
|
|
1083
1086
|
al->free(al->ud, t->temporal.tz, t->temporal.tz_len + 1);
|
|
1087
|
+
else if (chc__kind_is_agg(t->kind))
|
|
1088
|
+
al->free(al->ud, t->agg.func, t->agg.func_len + 1);
|
|
1084
1089
|
al->free(al->ud, t->name, t->name_len + 1);
|
|
1085
1090
|
al->free(al->ud, t, sizeof *t);
|
|
1086
1091
|
}
|
|
@@ -1091,6 +1096,7 @@ const chc_type *chc_type_child(const chc_type *t, size_t i) { return (t && i
|
|
|
1091
1096
|
int chc_type_fixed_size(const chc_type *t) { return t && t->kind == CHC_FIXED_STRING ? t->fixed_string.n : 0; }
|
|
1092
1097
|
int chc_type_decimal_scale(const chc_type *t) { return (t && chc__kind_is_decimal(t->kind)) ? t->decimal.scale : 0; }
|
|
1093
1098
|
int chc_type_datetime64_scale(const chc_type *t) { return (t && (t->kind == CHC_DATETIME64 || t->kind == CHC_TIME64)) ? t->temporal.scale : 0; }
|
|
1099
|
+
chc_interval_unit chc_type_interval_unit(const chc_type *t) { return (t && t->kind == CHC_INTERVAL) ? t->interval : CHC_INTERVAL_NONE; }
|
|
1094
1100
|
size_t chc_type_qbit_dimension(const chc_type *t) { return (t && t->kind == CHC_QBIT) ? t->qbit.dimension : 0; }
|
|
1095
1101
|
size_t chc_type_qbit_element_size(const chc_type *t) { return (t && t->kind == CHC_QBIT && t->n_children == 1) ? chc_type_elem_size(t->children[0]) * 8 : 0; }
|
|
1096
1102
|
const char *chc_type_name(const chc_type *t, size_t *out_len) {
|
|
@@ -1120,7 +1126,8 @@ void chc_type_enum_at(const chc_type *t, size_t i,
|
|
|
1120
1126
|
const char *
|
|
1121
1127
|
chc_type_tuple_field_name(const chc_type *t, size_t i, size_t *out_len)
|
|
1122
1128
|
{
|
|
1123
|
-
|
|
1129
|
+
bool named = t && (t->kind == CHC_TUPLE || t->kind == CHC_NESTED);
|
|
1130
|
+
if (!named || !t->field_names || i >= t->n_children) {
|
|
1124
1131
|
if (out_len) *out_len = 0;
|
|
1125
1132
|
return NULL;
|
|
1126
1133
|
}
|
|
@@ -1128,6 +1135,14 @@ chc_type_tuple_field_name(const chc_type *t, size_t i, size_t *out_len)
|
|
|
1128
1135
|
return t->field_names[i];
|
|
1129
1136
|
}
|
|
1130
1137
|
|
|
1138
|
+
const char *
|
|
1139
|
+
chc_type_agg_function(const chc_type *t, size_t *out_len)
|
|
1140
|
+
{
|
|
1141
|
+
bool has = t && chc__kind_is_agg(t->kind);
|
|
1142
|
+
if (out_len) *out_len = has ? t->agg.func_len : 0;
|
|
1143
|
+
return has ? t->agg.func : NULL;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1131
1146
|
int
|
|
1132
1147
|
chc_type_decimal_precision(const chc_type *t)
|
|
1133
1148
|
{
|
|
@@ -1138,14 +1153,15 @@ chc_type_decimal_precision(const chc_type *t)
|
|
|
1138
1153
|
case CHC_DECIMAL64: return 18;
|
|
1139
1154
|
case CHC_DECIMAL128: return 38;
|
|
1140
1155
|
case CHC_DECIMAL256: return 76;
|
|
1141
|
-
default:
|
|
1156
|
+
default: CHC_UNREACHABLE();
|
|
1142
1157
|
}
|
|
1143
1158
|
}
|
|
1144
1159
|
|
|
1145
1160
|
/* -------- type parser ---------- */
|
|
1146
1161
|
|
|
1147
|
-
/* Tokens & lexer
|
|
1148
|
-
*
|
|
1162
|
+
/* Tokens & lexer for printable ClickHouse type names. The parser is
|
|
1163
|
+
* recursive on '(' / ')' / ','; single-quoted parameter strings carry no
|
|
1164
|
+
* escapes. */
|
|
1149
1165
|
typedef enum {
|
|
1150
1166
|
CHC__TOK_EOS = 0, CHC__TOK_NAME, CHC__TOK_NUMBER, CHC__TOK_STRING,
|
|
1151
1167
|
CHC__TOK_LPAREN, CHC__TOK_RPAREN, CHC__TOK_COMMA, CHC__TOK_EQ,
|
|
@@ -1180,8 +1196,8 @@ chc__next_tok(chc__lex *lx)
|
|
|
1180
1196
|
if (c == ',') { lx->cur++; return (chc__tok){CHC__TOK_COMMA, st, 1, 0}; }
|
|
1181
1197
|
if (c == '=') { lx->cur++; return (chc__tok){CHC__TOK_EQ, st, 1, 0}; }
|
|
1182
1198
|
if (c == '\'') {
|
|
1183
|
-
/* single-quoted string;
|
|
1184
|
-
*
|
|
1199
|
+
/* single-quoted string; type names carry no escapes, so accept
|
|
1200
|
+
* anything up to the next quote. */
|
|
1185
1201
|
lx->cur++;
|
|
1186
1202
|
const char *body = lx->cur;
|
|
1187
1203
|
while (lx->cur < lx->end && *lx->cur != '\'') lx->cur++;
|
|
@@ -1191,9 +1207,9 @@ chc__next_tok(chc__lex *lx)
|
|
|
1191
1207
|
return (chc__tok){CHC__TOK_STRING, body, blen, 0};
|
|
1192
1208
|
}
|
|
1193
1209
|
if (c == '`' || c == '"') {
|
|
1194
|
-
/* Quoted identifier
|
|
1195
|
-
*
|
|
1196
|
-
*
|
|
1210
|
+
/* Quoted identifier: doubled quote (`` `` `` or `""`) &
|
|
1211
|
+
* backslash-escapes are skipped during scanning, resolved at
|
|
1212
|
+
* copy time -- same rule as the ClickHouse SQL lexer. */
|
|
1197
1213
|
char q = c;
|
|
1198
1214
|
lx->cur++;
|
|
1199
1215
|
const char *body = lx->cur;
|
|
@@ -1272,91 +1288,133 @@ chc__atoi64(const char *s, size_t n, int64_t *out)
|
|
|
1272
1288
|
|
|
1273
1289
|
/* AUTO-GENERATED-NAME-TABLE-BEGIN -- regenerate via tools/regen_name_table.sh */
|
|
1274
1290
|
#define CHC__NAME_TABLE_M 256u
|
|
1275
|
-
#define CHC__NAME_TABLE_SEED
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1291
|
+
#define CHC__NAME_TABLE_SEED 5935ull
|
|
1292
|
+
#define CHC__NAME_TABLE_MAXLEN 23u
|
|
1293
|
+
struct chc__name_row { uint16_t off; uint8_t len; uint8_t kind; uint8_t unit; };
|
|
1294
|
+
static const char chc__name_blob[] =
|
|
1295
|
+
"SimpleAggregateFunctionIntervalMicrosecondIntervalMillisecondInt"
|
|
1296
|
+
"ervalNanosecondMultiLineStringIntervalQuarterLowCardinalityInter"
|
|
1297
|
+
"valSecondIntervalMinuteIntervalMonthMultiPolygonIntervalHourInte"
|
|
1298
|
+
"rvalWeekIntervalYearFixedStringIntervalDayDateTime64Decimal128De"
|
|
1299
|
+
"cimal256Decimal32Decimal64BFloat16NullableUInt128UInt256Float32F"
|
|
1300
|
+
"loat64NothingVariantDynamicUInt16UInt32UInt64Date32Enum16NestedO"
|
|
1301
|
+
"bjectUInt8Enum8ArrayTuplePointBoolUUIDIPv4IPv6VoidRingJSONQBitMa"
|
|
1302
|
+
"p";
|
|
1303
|
+
static const struct chc__name_row chc__name_rows[] = {
|
|
1304
|
+
{390, 4, CHC_INT8, CHC_INTERVAL_NONE},
|
|
1305
|
+
{348, 5, CHC_INT16, CHC_INTERVAL_NONE},
|
|
1306
|
+
{354, 5, CHC_INT32, CHC_INTERVAL_NONE},
|
|
1307
|
+
{360, 5, CHC_INT64, CHC_INTERVAL_NONE},
|
|
1308
|
+
{299, 6, CHC_INT128, CHC_INTERVAL_NONE},
|
|
1309
|
+
{306, 6, CHC_INT256, CHC_INTERVAL_NONE},
|
|
1310
|
+
{389, 5, CHC_UINT8, CHC_INTERVAL_NONE},
|
|
1311
|
+
{347, 6, CHC_UINT16, CHC_INTERVAL_NONE},
|
|
1312
|
+
{353, 6, CHC_UINT32, CHC_INTERVAL_NONE},
|
|
1313
|
+
{359, 6, CHC_UINT64, CHC_INTERVAL_NONE},
|
|
1314
|
+
{298, 7, CHC_UINT128, CHC_INTERVAL_NONE},
|
|
1315
|
+
{305, 7, CHC_UINT256, CHC_INTERVAL_NONE},
|
|
1316
|
+
{312, 7, CHC_FLOAT32, CHC_INTERVAL_NONE},
|
|
1317
|
+
{319, 7, CHC_FLOAT64, CHC_INTERVAL_NONE},
|
|
1318
|
+
{282, 8, CHC_BFLOAT16, CHC_INTERVAL_NONE},
|
|
1319
|
+
{414, 4, CHC_BOOL, CHC_INTERVAL_NONE},
|
|
1320
|
+
{ 88, 6, CHC_STRING, CHC_INTERVAL_NONE},
|
|
1321
|
+
{212, 11, CHC_FIXED_STRING, CHC_INTERVAL_NONE},
|
|
1322
|
+
{234, 4, CHC_DATE, CHC_INTERVAL_NONE},
|
|
1323
|
+
{365, 6, CHC_DATE32, CHC_INTERVAL_NONE},
|
|
1324
|
+
{234, 8, CHC_DATETIME, CHC_INTERVAL_NONE},
|
|
1325
|
+
{234, 10, CHC_DATETIME64, CHC_INTERVAL_NONE},
|
|
1326
|
+
{238, 4, CHC_TIME, CHC_INTERVAL_NONE},
|
|
1327
|
+
{238, 6, CHC_TIME64, CHC_INTERVAL_NONE},
|
|
1328
|
+
{418, 4, CHC_UUID, CHC_INTERVAL_NONE},
|
|
1329
|
+
{422, 4, CHC_IPV4, CHC_INTERVAL_NONE},
|
|
1330
|
+
{426, 4, CHC_IPV6, CHC_INTERVAL_NONE},
|
|
1331
|
+
{394, 5, CHC_ENUM8, CHC_INTERVAL_NONE},
|
|
1332
|
+
{371, 6, CHC_ENUM16, CHC_INTERVAL_NONE},
|
|
1333
|
+
{264, 9, CHC_DECIMAL32, CHC_INTERVAL_NONE},
|
|
1334
|
+
{273, 9, CHC_DECIMAL64, CHC_INTERVAL_NONE},
|
|
1335
|
+
{244, 10, CHC_DECIMAL128, CHC_INTERVAL_NONE},
|
|
1336
|
+
{254, 10, CHC_DECIMAL256, CHC_INTERVAL_NONE},
|
|
1337
|
+
{290, 8, CHC_NULLABLE, CHC_INTERVAL_NONE},
|
|
1338
|
+
{399, 5, CHC_ARRAY, CHC_INTERVAL_NONE},
|
|
1339
|
+
{404, 5, CHC_TUPLE, CHC_INTERVAL_NONE},
|
|
1340
|
+
{446, 3, CHC_MAP, CHC_INTERVAL_NONE},
|
|
1341
|
+
{377, 6, CHC_NESTED, CHC_INTERVAL_NONE},
|
|
1342
|
+
{109, 14, CHC_LOW_CARDINALITY, CHC_INTERVAL_NONE},
|
|
1343
|
+
{326, 7, CHC_NOTHING, CHC_INTERVAL_NONE},
|
|
1344
|
+
{430, 4, CHC_VOID, CHC_INTERVAL_NONE},
|
|
1345
|
+
{409, 5, CHC_POINT, CHC_INTERVAL_NONE},
|
|
1346
|
+
{434, 4, CHC_RING, CHC_INTERVAL_NONE},
|
|
1347
|
+
{169, 7, CHC_POLYGON, CHC_INTERVAL_NONE},
|
|
1348
|
+
{164, 12, CHC_MULTI_POLYGON, CHC_INTERVAL_NONE},
|
|
1349
|
+
{ 84, 10, CHC_LINE_STRING, CHC_INTERVAL_NONE},
|
|
1350
|
+
{ 79, 15, CHC_MULTI_LINE_STRING, CHC_INTERVAL_NONE},
|
|
1351
|
+
{ 0, 23, CHC_SIMPLE_AGGREGATE_FUNCTION, CHC_INTERVAL_NONE},
|
|
1352
|
+
{ 6, 17, CHC_AGGREGATE_FUNCTION, CHC_INTERVAL_NONE},
|
|
1353
|
+
{333, 7, CHC_VARIANT, CHC_INTERVAL_NONE},
|
|
1354
|
+
{340, 7, CHC_DYNAMIC, CHC_INTERVAL_NONE},
|
|
1355
|
+
{438, 4, CHC_JSON, CHC_INTERVAL_NONE},
|
|
1356
|
+
{383, 6, CHC_OBJECT, CHC_INTERVAL_NONE},
|
|
1357
|
+
{442, 4, CHC_QBIT, CHC_INTERVAL_NONE},
|
|
1358
|
+
{ 61, 18, CHC_INTERVAL, CHC_INTERVAL_NANOSECOND},
|
|
1359
|
+
{ 23, 19, CHC_INTERVAL, CHC_INTERVAL_MICROSECOND},
|
|
1360
|
+
{ 42, 19, CHC_INTERVAL, CHC_INTERVAL_MILLISECOND},
|
|
1361
|
+
{123, 14, CHC_INTERVAL, CHC_INTERVAL_SECOND},
|
|
1362
|
+
{137, 14, CHC_INTERVAL, CHC_INTERVAL_MINUTE},
|
|
1363
|
+
{176, 12, CHC_INTERVAL, CHC_INTERVAL_HOUR},
|
|
1364
|
+
{223, 11, CHC_INTERVAL, CHC_INTERVAL_DAY},
|
|
1365
|
+
{188, 12, CHC_INTERVAL, CHC_INTERVAL_WEEK},
|
|
1366
|
+
{151, 13, CHC_INTERVAL, CHC_INTERVAL_MONTH},
|
|
1367
|
+
{ 94, 15, CHC_INTERVAL, CHC_INTERVAL_QUARTER},
|
|
1368
|
+
{200, 12, CHC_INTERVAL, CHC_INTERVAL_YEAR},
|
|
1369
|
+
};
|
|
1370
|
+
static const uint8_t chc__name_slot[CHC__NAME_TABLE_M] = {
|
|
1371
|
+
[ 2] = 43, [ 6] = 36, [ 9] = 60, [ 27] = 7, [ 29] = 40, [ 36] = 53,
|
|
1372
|
+
[ 39] = 41, [ 42] = 65, [ 45] = 34, [ 51] = 28, [ 52] = 46, [ 59] = 16,
|
|
1373
|
+
[ 67] = 18, [ 68] = 29, [ 71] = 2, [ 72] = 61, [ 73] = 25, [ 80] = 49,
|
|
1374
|
+
[ 81] = 22, [ 85] = 45, [ 86] = 64, [ 97] = 31, [ 98] = 35, [107] = 24,
|
|
1375
|
+
[109] = 39, [111] = 54, [112] = 55, [113] = 4, [118] = 9, [123] = 57,
|
|
1376
|
+
[126] = 47, [127] = 11, [129] = 21, [133] = 59, [134] = 19, [137] = 58,
|
|
1377
|
+
[138] = 17, [139] = 20, [141] = 27, [142] = 42, [151] = 63, [153] = 23,
|
|
1378
|
+
[154] = 13, [159] = 1, [160] = 15, [168] = 50, [170] = 33, [173] = 48,
|
|
1379
|
+
[174] = 37, [175] = 30, [176] = 62, [180] = 52, [192] = 14, [202] = 5,
|
|
1380
|
+
[204] = 6, [205] = 38, [206] = 3, [215] = 44, [223] = 12, [226] = 8,
|
|
1381
|
+
[238] = 10, [240] = 56, [247] = 26, [251] = 51, [252] = 32,
|
|
1341
1382
|
};
|
|
1342
1383
|
/* AUTO-GENERATED-NAME-TABLE-END */
|
|
1343
1384
|
|
|
1344
1385
|
/* Plain "Decimal" is intentionally absent from the table; the parser's
|
|
1345
|
-
* decimal_alias branch resolves it from precision.
|
|
1346
|
-
|
|
1347
|
-
*
|
|
1348
|
-
static chc_kind
|
|
1349
|
-
chc__name_to_kind(const char *s, size_t n) CHC_REPRODUCIBLE
|
|
1386
|
+
* decimal_alias branch resolves it from precision. */
|
|
1387
|
+
static const struct chc__name_row *
|
|
1388
|
+
chc__name_lookup(const char *s, size_t n) CHC_REPRODUCIBLE
|
|
1350
1389
|
{
|
|
1351
|
-
if (n == 0 || n >
|
|
1390
|
+
if (n == 0 || n > CHC__NAME_TABLE_MAXLEN) return NULL;
|
|
1352
1391
|
size_t h_len = n < 16 ? n : 16;
|
|
1353
1392
|
uint64_t h = chc__city_hash_len_16(
|
|
1354
1393
|
chc__city_hash_len_0_to_16(s, h_len) + (uint64_t) n,
|
|
1355
1394
|
CHC__NAME_TABLE_SEED);
|
|
1356
|
-
|
|
1357
|
-
if (
|
|
1358
|
-
|
|
1359
|
-
return
|
|
1395
|
+
uint8_t i = chc__name_slot[h & (CHC__NAME_TABLE_M - 1)];
|
|
1396
|
+
if (!i) return NULL;
|
|
1397
|
+
const struct chc__name_row *r = &chc__name_rows[i - 1];
|
|
1398
|
+
return (r->len == n && memcmp(chc__name_blob + r->off, s, n) == 0) ? r : NULL;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
/* Skip aggregate and JSON parameters, which can contain more than type names */
|
|
1402
|
+
static bool
|
|
1403
|
+
chc__skip_params(chc__lex *lx)
|
|
1404
|
+
{
|
|
1405
|
+
size_t depth = 1;
|
|
1406
|
+
while (lx->cur < lx->end) {
|
|
1407
|
+
char c = *lx->cur++;
|
|
1408
|
+
if (c == '\'' || c == '`' || c == '"') {
|
|
1409
|
+
while (lx->cur < lx->end && *lx->cur != c) lx->cur++;
|
|
1410
|
+
if (lx->cur == lx->end) return false;
|
|
1411
|
+
lx->cur++;
|
|
1412
|
+
} else if (c == '(')
|
|
1413
|
+
depth++;
|
|
1414
|
+
else if (c == ')' && !--depth)
|
|
1415
|
+
return true;
|
|
1416
|
+
}
|
|
1417
|
+
return false;
|
|
1360
1418
|
}
|
|
1361
1419
|
|
|
1362
1420
|
static int chc__parse_type(chc__lex *lx, const chc_alloc *al,
|
|
@@ -1383,20 +1441,58 @@ chc__type_push_enum(const chc_alloc *al, chc_type *parent,
|
|
|
1383
1441
|
const char *name, size_t name_len, int64_t value,
|
|
1384
1442
|
chc_err *err)
|
|
1385
1443
|
{
|
|
1444
|
+
char *nm = chc__strdup(al, name, name_len, err);
|
|
1445
|
+
if (!nm) return CHC_ERR_OOM;
|
|
1386
1446
|
size_t n = parent->enum_.n;
|
|
1387
1447
|
void *arr = chc__realloc(al, parent->enum_.items,
|
|
1388
1448
|
n * sizeof *parent->enum_.items,
|
|
1389
1449
|
(n + 1) * sizeof *parent->enum_.items, err);
|
|
1390
|
-
if (!arr) return CHC_ERR_OOM;
|
|
1450
|
+
if (!arr) { al->free(al->ud, nm, name_len + 1); return CHC_ERR_OOM; }
|
|
1391
1451
|
parent->enum_.items = arr;
|
|
1392
|
-
parent->enum_.items[n].name =
|
|
1393
|
-
if (!parent->enum_.items[n].name) return CHC_ERR_OOM;
|
|
1452
|
+
parent->enum_.items[n].name = nm;
|
|
1394
1453
|
parent->enum_.items[n].name_len = name_len;
|
|
1395
1454
|
parent->enum_.items[n].value = value;
|
|
1396
1455
|
parent->enum_.n = n + 1;
|
|
1397
1456
|
return CHC_OK;
|
|
1398
1457
|
}
|
|
1399
1458
|
|
|
1459
|
+
/* Tuple field-name scratch: parallel arrays handed to the type on success,
|
|
1460
|
+
* freed exactly as far as each grew when a mid-list allocation fails. */
|
|
1461
|
+
typedef struct {
|
|
1462
|
+
char **names;
|
|
1463
|
+
size_t *lens;
|
|
1464
|
+
size_t n_names;
|
|
1465
|
+
size_t n_lens;
|
|
1466
|
+
} chc__fields;
|
|
1467
|
+
|
|
1468
|
+
static void
|
|
1469
|
+
chc__fields_free(const chc_alloc *al, chc__fields *f)
|
|
1470
|
+
{
|
|
1471
|
+
for (size_t i = 0; i < f->n_names && i < f->n_lens; i++)
|
|
1472
|
+
al->free(al->ud, f->names[i], f->lens[i] + 1);
|
|
1473
|
+
al->free(al->ud, f->names, f->n_names * sizeof *f->names);
|
|
1474
|
+
al->free(al->ud, f->lens, f->n_lens * sizeof *f->lens);
|
|
1475
|
+
*f = (chc__fields) {};
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
static int
|
|
1479
|
+
chc__fields_grow(const chc_alloc *al, chc__fields *f, size_t n, chc_err *err)
|
|
1480
|
+
{
|
|
1481
|
+
char **nn = chc__realloc(al, f->names, f->n_names * sizeof *f->names,
|
|
1482
|
+
n * sizeof *f->names, err);
|
|
1483
|
+
if (!nn) return CHC_ERR_OOM;
|
|
1484
|
+
f->names = nn;
|
|
1485
|
+
f->names[n - 1] = NULL;
|
|
1486
|
+
f->n_names = n;
|
|
1487
|
+
size_t *nl = chc__realloc(al, f->lens, f->n_lens * sizeof *f->lens,
|
|
1488
|
+
n * sizeof *f->lens, err);
|
|
1489
|
+
if (!nl) return CHC_ERR_OOM;
|
|
1490
|
+
f->lens = nl;
|
|
1491
|
+
f->lens[n - 1] = 0;
|
|
1492
|
+
f->n_lens = n;
|
|
1493
|
+
return CHC_OK;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1400
1496
|
static int
|
|
1401
1497
|
chc__parse_type(chc__lex *lx, const chc_alloc *al,
|
|
1402
1498
|
const char *whole_start, const char *whole_end,
|
|
@@ -1417,17 +1513,23 @@ chc__parse_type(chc__lex *lx, const chc_alloc *al,
|
|
|
1417
1513
|
if (decimal_alias) {
|
|
1418
1514
|
t->kind = CHC_DECIMAL128; /* placeholder; refined from precision */
|
|
1419
1515
|
} else {
|
|
1420
|
-
|
|
1421
|
-
if (
|
|
1516
|
+
const struct chc__name_row *row = chc__name_lookup(head.start, head.len);
|
|
1517
|
+
if (!row) {
|
|
1422
1518
|
chc_type_destroy(t, al);
|
|
1423
1519
|
return chc__err_set(err, CHC_ERR_TYPE, "unknown type: %.*s",
|
|
1424
1520
|
(int) head.len, head.start);
|
|
1425
1521
|
}
|
|
1522
|
+
t->kind = (chc_kind) row->kind;
|
|
1523
|
+
if (t->kind == CHC_INTERVAL) t->interval = (chc_interval_unit) row->unit;
|
|
1426
1524
|
}
|
|
1427
1525
|
|
|
1428
1526
|
const char *name_start = head.start;
|
|
1429
1527
|
const char *name_end = head.start + head.len;
|
|
1430
1528
|
|
|
1529
|
+
/* ClickHouse defaults to milliseconds. */
|
|
1530
|
+
if (t->kind == CHC_DATETIME64 || t->kind == CHC_TIME64)
|
|
1531
|
+
t->temporal.scale = 3;
|
|
1532
|
+
|
|
1431
1533
|
/* Optional parameter list. */
|
|
1432
1534
|
if (chc__peek_tok(lx).kind == CHC__TOK_LPAREN) {
|
|
1433
1535
|
chc__eat_tok(lx);
|
|
@@ -1526,20 +1628,22 @@ chc__parse_type(chc__lex *lx, const chc_alloc *al,
|
|
|
1526
1628
|
}
|
|
1527
1629
|
t->decimal.scale = (int) scale;
|
|
1528
1630
|
} else if (t->kind == CHC_DATETIME64 || t->kind == CHC_TIME64) {
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1631
|
+
if (chc__peek_tok(lx).kind != CHC__TOK_RPAREN) {
|
|
1632
|
+
chc__tok num = chc__eat_tok(lx);
|
|
1633
|
+
if (num.kind != CHC__TOK_NUMBER) {
|
|
1634
|
+
chc_type_destroy(t, al);
|
|
1635
|
+
return chc__err_set(err, CHC_ERR_TYPE, "DateTime64: expected precision");
|
|
1636
|
+
}
|
|
1637
|
+
int64_t scale;
|
|
1638
|
+
if (!chc__atoi64(num.start, num.len, &scale)
|
|
1639
|
+
|| scale < 0 || scale > 9) {
|
|
1640
|
+
chc_type_destroy(t, al);
|
|
1641
|
+
return chc__err_set(err, CHC_ERR_TYPE,
|
|
1642
|
+
"DateTime64: precision out of range: %.*s",
|
|
1643
|
+
(int) num.len, num.start);
|
|
1644
|
+
}
|
|
1645
|
+
t->temporal.scale = (int) scale;
|
|
1541
1646
|
}
|
|
1542
|
-
t->temporal.scale = (int) scale;
|
|
1543
1647
|
if (chc__peek_tok(lx).kind == CHC__TOK_COMMA) {
|
|
1544
1648
|
chc__eat_tok(lx);
|
|
1545
1649
|
chc__tok s = chc__eat_tok(lx);
|
|
@@ -1560,12 +1664,19 @@ chc__parse_type(chc__lex *lx, const chc_alloc *al,
|
|
|
1560
1664
|
t->temporal.tz = chc__strdup(al, s.start, s.len, err);
|
|
1561
1665
|
if (!t->temporal.tz) { chc_type_destroy(t, al); return CHC_ERR_OOM; }
|
|
1562
1666
|
t->temporal.tz_len = s.len;
|
|
1667
|
+
} else if (t->kind == CHC_JSON) {
|
|
1668
|
+
/* JSON options affect server storage but not JSON sent as strings */
|
|
1669
|
+
if (!chc__skip_params(lx)) {
|
|
1670
|
+
chc_type_destroy(t, al);
|
|
1671
|
+
return chc__err_set(err, CHC_ERR_TYPE,
|
|
1672
|
+
"JSON: unterminated parameters");
|
|
1673
|
+
}
|
|
1674
|
+
lx->cur--;
|
|
1563
1675
|
} else if (t->kind == CHC_OBJECT) {
|
|
1564
|
-
/* Object('name')
|
|
1565
|
-
*
|
|
1566
|
-
*
|
|
1567
|
-
*
|
|
1568
|
-
* round-trip & error messages. */
|
|
1676
|
+
/* Object('name'), legacy JSON object syntax. Argument is schema
|
|
1677
|
+
* identifier (eg 'json'); any quoted string is accepted.
|
|
1678
|
+
* Discard argument & retain full source text in t->name
|
|
1679
|
+
* for round-trip & errors */
|
|
1569
1680
|
chc__tok s = chc__eat_tok(lx);
|
|
1570
1681
|
if (s.kind != CHC__TOK_STRING) {
|
|
1571
1682
|
chc_type_destroy(t, al);
|
|
@@ -1605,23 +1716,66 @@ chc__parse_type(chc__lex *lx, const chc_alloc *al,
|
|
|
1605
1716
|
(int) num.len, num.start);
|
|
1606
1717
|
}
|
|
1607
1718
|
t->qbit.dimension = (size_t) n;
|
|
1719
|
+
} else if (chc__kind_is_agg(t->kind)) {
|
|
1720
|
+
/* ClickHouse may include a version before an aggregate function name */
|
|
1721
|
+
if (chc__peek_tok(lx).kind == CHC__TOK_NUMBER) {
|
|
1722
|
+
chc__eat_tok(lx);
|
|
1723
|
+
if (chc__eat_tok(lx).kind != CHC__TOK_COMMA) {
|
|
1724
|
+
chc_type_destroy(t, al);
|
|
1725
|
+
return chc__err_set(err, CHC_ERR_TYPE,
|
|
1726
|
+
"%.*s: expected ',' after version",
|
|
1727
|
+
(int) head.len, head.start);
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
chc__tok fn = chc__eat_tok(lx);
|
|
1731
|
+
if (fn.kind != CHC__TOK_NAME || fn.quote) {
|
|
1732
|
+
chc_type_destroy(t, al);
|
|
1733
|
+
return chc__err_set(err, CHC_ERR_TYPE,
|
|
1734
|
+
"%.*s: expected function name", (int) head.len, head.start);
|
|
1735
|
+
}
|
|
1736
|
+
t->agg.func = chc__strdup(al, fn.start, fn.len, err);
|
|
1737
|
+
if (!t->agg.func) { chc_type_destroy(t, al); return CHC_ERR_OOM; }
|
|
1738
|
+
t->agg.func_len = fn.len;
|
|
1739
|
+
if (chc__peek_tok(lx).kind == CHC__TOK_LPAREN) {
|
|
1740
|
+
chc__eat_tok(lx);
|
|
1741
|
+
if (!chc__skip_params(lx)) {
|
|
1742
|
+
chc_type_destroy(t, al);
|
|
1743
|
+
return chc__err_set(err, CHC_ERR_TYPE,
|
|
1744
|
+
"%.*s: %.*s parameters unterminated",
|
|
1745
|
+
(int) head.len, head.start, (int) fn.len, fn.start);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
while (chc__peek_tok(lx).kind == CHC__TOK_COMMA) {
|
|
1749
|
+
chc__eat_tok(lx);
|
|
1750
|
+
chc_type *arg = NULL;
|
|
1751
|
+
int rc = chc__parse_type(lx, al, whole_start, whole_end,
|
|
1752
|
+
depth + 1, &arg, err);
|
|
1753
|
+
if (rc == CHC_OK) rc = chc__type_push_child(al, t, arg, err);
|
|
1754
|
+
if (rc != CHC_OK) {
|
|
1755
|
+
chc_type_destroy(arg, al);
|
|
1756
|
+
chc_type_destroy(t, al);
|
|
1757
|
+
return rc;
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
if (!t->n_children) {
|
|
1761
|
+
chc_type_destroy(t, al);
|
|
1762
|
+
return chc__err_set(err, CHC_ERR_TYPE,
|
|
1763
|
+
"%.*s: %.*s has no argument type",
|
|
1764
|
+
(int) head.len, head.start, (int) fn.len, fn.start);
|
|
1765
|
+
}
|
|
1608
1766
|
} else {
|
|
1609
|
-
/*
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
char **fn_buf = NULL;
|
|
1615
|
-
size_t *fn_lens = NULL;
|
|
1616
|
-
size_t fn_cap = 0;
|
|
1617
|
-
bool any_named = false;
|
|
1767
|
+
/* Tuple and Nested fields may have names before their types */
|
|
1768
|
+
bool named = (t->kind == CHC_TUPLE
|
|
1769
|
+
|| t->kind == CHC_NESTED);
|
|
1770
|
+
bool any_named = false;
|
|
1771
|
+
chc__fields fields = {};
|
|
1618
1772
|
for (;;) {
|
|
1619
1773
|
chc__tok la = chc__peek_tok(lx);
|
|
1620
1774
|
if (la.kind == CHC__TOK_RPAREN) break;
|
|
1621
1775
|
|
|
1622
1776
|
chc__tok field = {};
|
|
1623
1777
|
bool has_field = false;
|
|
1624
|
-
if (
|
|
1778
|
+
if (named && la.kind == CHC__TOK_NAME) {
|
|
1625
1779
|
chc__eat_tok(lx);
|
|
1626
1780
|
if (la.quote) {
|
|
1627
1781
|
/* `\`x\`` or `"x"` is never a type head, so it must be
|
|
@@ -1648,80 +1802,45 @@ chc__parse_type(chc__lex *lx, const chc_alloc *al,
|
|
|
1648
1802
|
|
|
1649
1803
|
chc_type *child = NULL;
|
|
1650
1804
|
int rc = chc__parse_type(lx, al, whole_start, whole_end, depth + 1, &child, err);
|
|
1651
|
-
if (rc == CHC_OK)
|
|
1805
|
+
if (rc == CHC_OK) {
|
|
1652
1806
|
rc = chc__type_push_child(al, t, child, err);
|
|
1653
|
-
|
|
1807
|
+
if (rc == CHC_OK) child = NULL; /* t owns it now */
|
|
1808
|
+
} else
|
|
1654
1809
|
child = NULL;
|
|
1810
|
+
if (rc == CHC_OK && named)
|
|
1811
|
+
rc = chc__fields_grow(al, &fields, t->n_children, err);
|
|
1812
|
+
if (rc == CHC_OK && named && has_field) {
|
|
1813
|
+
size_t flen = field.len;
|
|
1814
|
+
char *nm = field.quote
|
|
1815
|
+
? chc__strdup_unquote(al, field.start, field.len,
|
|
1816
|
+
field.quote, &flen, err)
|
|
1817
|
+
: chc__strdup(al, field.start, field.len, err);
|
|
1818
|
+
if (nm) {
|
|
1819
|
+
fields.names[fields.n_names - 1] = nm;
|
|
1820
|
+
fields.lens[fields.n_names - 1] = flen;
|
|
1821
|
+
any_named = true;
|
|
1822
|
+
} else
|
|
1823
|
+
rc = CHC_ERR_OOM;
|
|
1824
|
+
}
|
|
1655
1825
|
if (rc != CHC_OK) {
|
|
1656
1826
|
if (child) chc_type_destroy(child, al);
|
|
1657
|
-
|
|
1658
|
-
for (size_t i = 0; i < fn_cap; i++)
|
|
1659
|
-
al->free(al->ud, fn_buf[i], fn_lens[i] + 1);
|
|
1660
|
-
al->free(al->ud, fn_buf, fn_cap * sizeof *fn_buf);
|
|
1661
|
-
al->free(al->ud, fn_lens, fn_cap * sizeof *fn_lens);
|
|
1662
|
-
}
|
|
1827
|
+
chc__fields_free(al, &fields);
|
|
1663
1828
|
chc_type_destroy(t, al);
|
|
1664
1829
|
return rc;
|
|
1665
1830
|
}
|
|
1666
1831
|
|
|
1667
|
-
if (is_tuple) {
|
|
1668
|
-
size_t new_cap = t->n_children;
|
|
1669
|
-
char **nfn = chc__realloc(al, fn_buf,
|
|
1670
|
-
fn_cap * sizeof *fn_buf,
|
|
1671
|
-
new_cap * sizeof *fn_buf, err);
|
|
1672
|
-
if (!nfn) { chc_type_destroy(t, al); return CHC_ERR_OOM; }
|
|
1673
|
-
size_t *nfl = chc__realloc(al, fn_lens,
|
|
1674
|
-
fn_cap * sizeof *fn_lens,
|
|
1675
|
-
new_cap * sizeof *fn_lens, err);
|
|
1676
|
-
if (!nfl) {
|
|
1677
|
-
al->free(al->ud, nfn, new_cap * sizeof *nfn);
|
|
1678
|
-
chc_type_destroy(t, al); return CHC_ERR_OOM;
|
|
1679
|
-
}
|
|
1680
|
-
fn_buf = nfn;
|
|
1681
|
-
fn_lens = nfl;
|
|
1682
|
-
fn_buf[fn_cap] = NULL;
|
|
1683
|
-
fn_lens[fn_cap] = 0;
|
|
1684
|
-
fn_cap = new_cap;
|
|
1685
|
-
if (has_field) {
|
|
1686
|
-
size_t flen = field.len;
|
|
1687
|
-
if (field.quote)
|
|
1688
|
-
fn_buf[fn_cap - 1] = chc__strdup_unquote(al, field.start,
|
|
1689
|
-
field.len, field.quote,
|
|
1690
|
-
&flen, err);
|
|
1691
|
-
else
|
|
1692
|
-
fn_buf[fn_cap - 1] = chc__strdup(al, field.start,
|
|
1693
|
-
field.len, err);
|
|
1694
|
-
if (!fn_buf[fn_cap - 1]) {
|
|
1695
|
-
for (size_t i = 0; i < fn_cap - 1; i++)
|
|
1696
|
-
al->free(al->ud, fn_buf[i], fn_lens[i] + 1);
|
|
1697
|
-
al->free(al->ud, fn_buf, fn_cap * sizeof *fn_buf);
|
|
1698
|
-
al->free(al->ud, fn_lens, fn_cap * sizeof *fn_lens);
|
|
1699
|
-
chc_type_destroy(t, al); return CHC_ERR_OOM;
|
|
1700
|
-
}
|
|
1701
|
-
fn_lens[fn_cap - 1] = flen;
|
|
1702
|
-
any_named = true;
|
|
1703
|
-
}
|
|
1704
|
-
}
|
|
1705
|
-
|
|
1706
1832
|
chc__tok c = chc__peek_tok(lx);
|
|
1707
1833
|
if (c.kind == CHC__TOK_COMMA) { chc__eat_tok(lx); continue; }
|
|
1708
1834
|
if (c.kind == CHC__TOK_RPAREN) break;
|
|
1709
|
-
|
|
1710
|
-
for (size_t i = 0; i < fn_cap; i++)
|
|
1711
|
-
al->free(al->ud, fn_buf[i], fn_lens[i] + 1);
|
|
1712
|
-
al->free(al->ud, fn_buf, fn_cap * sizeof *fn_buf);
|
|
1713
|
-
al->free(al->ud, fn_lens, fn_cap * sizeof *fn_lens);
|
|
1714
|
-
}
|
|
1835
|
+
chc__fields_free(al, &fields);
|
|
1715
1836
|
chc_type_destroy(t, al);
|
|
1716
1837
|
return chc__err_set(err, CHC_ERR_TYPE, "expected ',' or ')'");
|
|
1717
1838
|
}
|
|
1718
1839
|
if (any_named) {
|
|
1719
|
-
t->field_names =
|
|
1720
|
-
t->field_name_lens =
|
|
1721
|
-
} else
|
|
1722
|
-
|
|
1723
|
-
al->free(al->ud, fn_lens, fn_cap * sizeof *fn_lens);
|
|
1724
|
-
}
|
|
1840
|
+
t->field_names = fields.names;
|
|
1841
|
+
t->field_name_lens = fields.lens;
|
|
1842
|
+
} else
|
|
1843
|
+
chc__fields_free(al, &fields);
|
|
1725
1844
|
}
|
|
1726
1845
|
|
|
1727
1846
|
chc__tok rp = chc__eat_tok(lx);
|
|
@@ -1735,7 +1854,7 @@ chc__parse_type(chc__lex *lx, const chc_alloc *al,
|
|
|
1735
1854
|
/* Decimal(P, S) compatibility: width selected by precision. */
|
|
1736
1855
|
if (t->kind == CHC_DECIMAL128 && head.len == 7
|
|
1737
1856
|
&& memcmp(head.start, "Decimal", 7) == 0 && t->n_children == 0) {
|
|
1738
|
-
/*
|
|
1857
|
+
/* Treat Decimal without parameters as Decimal128 */
|
|
1739
1858
|
}
|
|
1740
1859
|
|
|
1741
1860
|
t->name = chc__strdup(al, name_start, (size_t) (name_end - name_start), err);
|
|
@@ -1778,19 +1897,7 @@ chc_type_format(const chc_type *t, char *buf, size_t buf_len)
|
|
|
1778
1897
|
}
|
|
1779
1898
|
|
|
1780
1899
|
/* -------- column internals ---------- */
|
|
1781
|
-
|
|
1782
|
-
struct chc_column {
|
|
1783
|
-
chc_col_kind layout;
|
|
1784
|
-
size_t n_rows;
|
|
1785
|
-
union {
|
|
1786
|
-
struct { void *data; size_t elem_size; } fixed;
|
|
1787
|
-
struct { uint8_t *data; uint64_t *offsets; size_t bytes; } str;
|
|
1788
|
-
struct { uint8_t *null_map; chc_column *inner; } nullable;
|
|
1789
|
-
struct { uint64_t *offsets; chc_column *values; } array;
|
|
1790
|
-
struct { chc_column **children; size_t arity; } tuple;
|
|
1791
|
-
struct { int key_size; void *keys; chc_column *dict; size_t dict_n; } lc;
|
|
1792
|
-
};
|
|
1793
|
-
};
|
|
1900
|
+
/* Public API defines struct chc_column */
|
|
1794
1901
|
|
|
1795
1902
|
chc_col_kind chc_column_layout(const chc_column *c) { return c ? c->layout : (chc_col_kind) 0; }
|
|
1796
1903
|
size_t chc_column_n_rows(const chc_column *c) { return c ? c->n_rows : 0; }
|
|
@@ -2019,8 +2126,10 @@ chc__col_read_string(chc_in *in, size_t n_rows,
|
|
|
2019
2126
|
return CHC_OK;
|
|
2020
2127
|
}
|
|
2021
2128
|
|
|
2022
|
-
/* Composite columns might have a prefix sub-stream
|
|
2023
|
-
*
|
|
2129
|
+
/* Composite columns might have a prefix sub-stream, emitted for whole
|
|
2130
|
+
* column tree before any body bytes. In formats handled here:
|
|
2131
|
+
* LowCardinality has a uint64 key version, JSON a uint64 serialization
|
|
2132
|
+
* version, legacy Object a uint8 serialization kind */
|
|
2024
2133
|
static int
|
|
2025
2134
|
chc__col_read_prefix(chc_in *in, const chc_type *t, chc_err *err)
|
|
2026
2135
|
{
|
|
@@ -2033,8 +2142,34 @@ chc__col_read_prefix(chc_in *in, const chc_type *t, chc_err *err)
|
|
|
2033
2142
|
"LowCardinality: unexpected key version %llu", (unsigned long long) v);
|
|
2034
2143
|
return CHC_OK;
|
|
2035
2144
|
}
|
|
2145
|
+
if (t->kind == CHC_JSON) {
|
|
2146
|
+
/* Only STRING (=1) in scope; other versions need the consumer to
|
|
2147
|
+
* set output_format_native_write_json_as_string=1 on the SELECT */
|
|
2148
|
+
uint64_t v;
|
|
2149
|
+
int rc = chc__read_u64_le(in, &v, err);
|
|
2150
|
+
if (rc != CHC_OK) return rc;
|
|
2151
|
+
if (v != 1)
|
|
2152
|
+
return chc__err_set(err, CHC_ERR_TYPE,
|
|
2153
|
+
"unsupported JSON serialization version %llu "
|
|
2154
|
+
"(set output_format_native_write_json_as_string=1)",
|
|
2155
|
+
(unsigned long long) v);
|
|
2156
|
+
return CHC_OK;
|
|
2157
|
+
}
|
|
2158
|
+
if (t->kind == CHC_OBJECT) {
|
|
2159
|
+
/* Legacy Object Native format uses a one-byte kind: TUPLE=0,
|
|
2160
|
+
* STRING=1. Tuple decoding needs its dynamic type descriptor */
|
|
2161
|
+
uint8_t kind;
|
|
2162
|
+
int rc = chc__read_byte(in, &kind, err);
|
|
2163
|
+
if (rc != CHC_OK) return rc;
|
|
2164
|
+
if (kind != 1)
|
|
2165
|
+
return chc__err_set(err, CHC_ERR_TYPE,
|
|
2166
|
+
"unsupported Object serialization kind %u (STRING=1 required)",
|
|
2167
|
+
(unsigned) kind);
|
|
2168
|
+
return CHC_OK;
|
|
2169
|
+
}
|
|
2036
2170
|
if (t->kind == CHC_NULLABLE || t->kind == CHC_ARRAY
|
|
2037
2171
|
|| t->kind == CHC_TUPLE || t->kind == CHC_MAP
|
|
2172
|
+
|| t->kind == CHC_NESTED
|
|
2038
2173
|
|| t->kind == CHC_SIMPLE_AGGREGATE_FUNCTION) {
|
|
2039
2174
|
for (size_t i = 0; i < t->n_children; i++) {
|
|
2040
2175
|
int rc = chc__col_read_prefix(in, t->children[i], err);
|
|
@@ -2044,12 +2179,65 @@ chc__col_read_prefix(chc_in *in, const chc_type *t, chc_err *err)
|
|
|
2044
2179
|
return CHC_OK;
|
|
2045
2180
|
}
|
|
2046
2181
|
|
|
2047
|
-
/* Geo types
|
|
2048
|
-
*
|
|
2049
|
-
|
|
2050
|
-
*
|
|
2051
|
-
static
|
|
2052
|
-
|
|
2182
|
+
/* Geo types alias nested Arrays over Point = Tuple(Float64, Float64),
|
|
2183
|
+
* as in the server's type factory. Never mutated */
|
|
2184
|
+
static chc_type chc__geo_f64 = { .kind = CHC_FLOAT64, .name = (char *) "Float64", .name_len = 7 };
|
|
2185
|
+
static chc_type *chc__geo_point_elems[] = { &chc__geo_f64, &chc__geo_f64 };
|
|
2186
|
+
static chc_type chc__geo_point = {
|
|
2187
|
+
.kind = CHC_TUPLE, .name = (char *) "Point", .name_len = 5,
|
|
2188
|
+
.n_children = 2, .children = chc__geo_point_elems,
|
|
2189
|
+
};
|
|
2190
|
+
static chc_type *chc__geo_ring_elems[] = { &chc__geo_point };
|
|
2191
|
+
static chc_type chc__geo_ring = {
|
|
2192
|
+
.kind = CHC_ARRAY, .name = (char *) "Array(Point)", .name_len = 12,
|
|
2193
|
+
.n_children = 1, .children = chc__geo_ring_elems,
|
|
2194
|
+
};
|
|
2195
|
+
static chc_type *chc__geo_polygon_elems[] = { &chc__geo_ring };
|
|
2196
|
+
static chc_type chc__geo_polygon = {
|
|
2197
|
+
.kind = CHC_ARRAY, .name = (char *) "Array(Array(Point))", .name_len = 19,
|
|
2198
|
+
.n_children = 1, .children = chc__geo_polygon_elems,
|
|
2199
|
+
};
|
|
2200
|
+
static chc_type *chc__geo_multi_polygon_elems[] = { &chc__geo_polygon };
|
|
2201
|
+
static chc_type chc__geo_multi_polygon = {
|
|
2202
|
+
.kind = CHC_ARRAY, .name = (char *) "Array(Array(Array(Point)))", .name_len = 26,
|
|
2203
|
+
.n_children = 1, .children = chc__geo_multi_polygon_elems,
|
|
2204
|
+
};
|
|
2205
|
+
|
|
2206
|
+
static const chc_type *
|
|
2207
|
+
chc__geo_alias(const chc_type *t)
|
|
2208
|
+
{
|
|
2209
|
+
switch (t->kind) {
|
|
2210
|
+
case CHC_POINT: return &chc__geo_point;
|
|
2211
|
+
case CHC_RING:
|
|
2212
|
+
case CHC_LINE_STRING: return &chc__geo_ring;
|
|
2213
|
+
case CHC_POLYGON:
|
|
2214
|
+
case CHC_MULTI_LINE_STRING: return &chc__geo_polygon;
|
|
2215
|
+
case CHC_MULTI_POLYGON: return &chc__geo_multi_polygon;
|
|
2216
|
+
default: return t;
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
/* Resolve types serialized as another type, SimpleAggregateFunction stores
|
|
2221
|
+
* values using first argument type */
|
|
2222
|
+
static const chc_type *
|
|
2223
|
+
chc__serial_type(const chc_type *t)
|
|
2224
|
+
{
|
|
2225
|
+
while (t->kind == CHC_SIMPLE_AGGREGATE_FUNCTION && t->n_children)
|
|
2226
|
+
t = t->children[0];
|
|
2227
|
+
return chc__geo_alias(t);
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
/* Serialization kind, DEFAULT is 0. 23.3 ISerialization::Kind & current
|
|
2231
|
+
* KindStackBinarySerializationType agree on both */
|
|
2232
|
+
#define CHC__KIND_SPARSE 1u
|
|
2233
|
+
|
|
2234
|
+
/* Final sparse offset group carries this flag (Native Format spec,
|
|
2235
|
+
* kind_stack & sparse encoding). */
|
|
2236
|
+
#define CHC__SPARSE_END (UINT64_C(1) << 62)
|
|
2237
|
+
|
|
2238
|
+
static int chc__col_read_kinds(chc_in *in, const chc_type *t, size_t n_rows,
|
|
2239
|
+
const uint8_t **kinds, chc_column **out,
|
|
2240
|
+
chc_err *err);
|
|
2053
2241
|
|
|
2054
2242
|
/* Byte-swap a host-typed uint64/keys array in place on BE hosts. No-op on LE. */
|
|
2055
2243
|
static void
|
|
@@ -2074,6 +2262,143 @@ chc__swap_keys(CHC_MAYBE_UNUSED void *p, CHC_MAYBE_UNUSED size_t n,
|
|
|
2074
2262
|
#endif
|
|
2075
2263
|
}
|
|
2076
2264
|
|
|
2265
|
+
/* Read t's children as Tuple column, serving Tuple, Map & Nested values.
|
|
2266
|
+
* kinds NULL when dense */
|
|
2267
|
+
static int
|
|
2268
|
+
chc__col_read_tuple(chc_in *in, const chc_type *t, size_t n_rows,
|
|
2269
|
+
const uint8_t **kinds, chc_column **out, chc_err *err)
|
|
2270
|
+
{
|
|
2271
|
+
const chc_alloc *al = in->al;
|
|
2272
|
+
chc_column *c = chc__calloc(al, sizeof *c, err);
|
|
2273
|
+
if (!c) return CHC_ERR_OOM;
|
|
2274
|
+
c->layout = CHC_COL_TUPLE;
|
|
2275
|
+
c->n_rows = n_rows;
|
|
2276
|
+
/* LOCAL PATCH (ch_connect): ClickHouse serializes the empty Tuple()
|
|
2277
|
+
* as one UInt8 (zero) per row; skipping nothing desyncs the stream. */
|
|
2278
|
+
if (t->n_children == 0) {
|
|
2279
|
+
uint8_t scratch[256];
|
|
2280
|
+
size_t left = n_rows;
|
|
2281
|
+
while (left) {
|
|
2282
|
+
size_t take = left > sizeof scratch ? sizeof scratch : left;
|
|
2283
|
+
int rc = chc__read_bytes(in, scratch, take, err);
|
|
2284
|
+
if (rc != CHC_OK) { chc__column_destroy(c, al); return rc; }
|
|
2285
|
+
left -= take;
|
|
2286
|
+
}
|
|
2287
|
+
*out = c;
|
|
2288
|
+
return CHC_OK;
|
|
2289
|
+
}
|
|
2290
|
+
if (t->n_children) {
|
|
2291
|
+
c->tuple.children = chc__calloc(al, t->n_children * sizeof *c->tuple.children, err);
|
|
2292
|
+
if (!c->tuple.children) { chc__column_destroy(c, al); return CHC_ERR_OOM; }
|
|
2293
|
+
c->tuple.arity = t->n_children;
|
|
2294
|
+
}
|
|
2295
|
+
for (size_t i = 0; i < t->n_children; i++) {
|
|
2296
|
+
chc_column **slot = &c->tuple.children[i];
|
|
2297
|
+
int rc = kinds ? chc__col_read_kinds(in, t->children[i], n_rows, kinds, slot, err)
|
|
2298
|
+
: chc__col_read(in, t->children[i], n_rows, slot, err);
|
|
2299
|
+
if (rc != CHC_OK) { chc__column_destroy(c, al); return rc; }
|
|
2300
|
+
}
|
|
2301
|
+
*out = c;
|
|
2302
|
+
return CHC_OK;
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
/* Sparse body: varuint gap groups locating non-default rows, then those rows'
|
|
2306
|
+
* values densely. Gaps become zero bytes or empty strings, matching
|
|
2307
|
+
* ColumnSparse storage default even where type default differs (Enum) */
|
|
2308
|
+
static int
|
|
2309
|
+
chc__col_read_sparse(chc_in *in, const chc_type *t, size_t n_rows,
|
|
2310
|
+
chc_column **out, chc_err *err)
|
|
2311
|
+
{
|
|
2312
|
+
const chc_alloc *al = in->al;
|
|
2313
|
+
size_t es = chc_type_elem_size(t);
|
|
2314
|
+
/* Bounds positions, dense values & string offsets alike */
|
|
2315
|
+
size_t unit = es > sizeof(uint64_t) ? es : sizeof(uint64_t), bytes;
|
|
2316
|
+
if (chc__mul_size(n_rows, unit, &bytes))
|
|
2317
|
+
return chc__err_set(err, CHC_ERR_PROTOCOL, "column size overflow");
|
|
2318
|
+
|
|
2319
|
+
size_t *pos = NULL, n = 0, cap = 0, row = 0;
|
|
2320
|
+
chc_column *c = NULL;
|
|
2321
|
+
int rc;
|
|
2322
|
+
for (;;) {
|
|
2323
|
+
uint64_t g;
|
|
2324
|
+
if ((rc = chc__read_varuint(in, &g, err))) goto fail;
|
|
2325
|
+
bool end = g & CHC__SPARSE_END;
|
|
2326
|
+
g &= ~CHC__SPARSE_END;
|
|
2327
|
+
if (g >= CHC__SPARSE_END || g > n_rows - row) {
|
|
2328
|
+
rc = chc__err_set(err, CHC_ERR_PROTOCOL,
|
|
2329
|
+
"sparse offsets overrun %zu rows", n_rows);
|
|
2330
|
+
goto fail;
|
|
2331
|
+
}
|
|
2332
|
+
row += (size_t) g;
|
|
2333
|
+
if (end) break;
|
|
2334
|
+
if (row == n_rows) {
|
|
2335
|
+
rc = chc__err_set(err, CHC_ERR_PROTOCOL,
|
|
2336
|
+
"sparse offsets overrun %zu rows", n_rows);
|
|
2337
|
+
goto fail;
|
|
2338
|
+
}
|
|
2339
|
+
if (n == cap) {
|
|
2340
|
+
size_t ncap = cap ? cap * 2 : 64;
|
|
2341
|
+
if (ncap > n_rows) ncap = n_rows;
|
|
2342
|
+
size_t *np = chc__realloc(al, pos, cap * sizeof *pos, ncap * sizeof *pos, err);
|
|
2343
|
+
if (!np) { rc = CHC_ERR_OOM; goto fail; }
|
|
2344
|
+
pos = np;
|
|
2345
|
+
cap = ncap;
|
|
2346
|
+
}
|
|
2347
|
+
pos[n++] = row++;
|
|
2348
|
+
}
|
|
2349
|
+
if (row != n_rows) {
|
|
2350
|
+
rc = chc__err_set(err, CHC_ERR_PROTOCOL,
|
|
2351
|
+
"sparse offsets end at row %zu of %zu", row, n_rows);
|
|
2352
|
+
goto fail;
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
rc = chc__col_read(in, t, n, &c, err);
|
|
2356
|
+
if (rc != CHC_OK) goto fail;
|
|
2357
|
+
|
|
2358
|
+
if (n < n_rows && c->layout == CHC_COL_FIXED) {
|
|
2359
|
+
uint8_t *dense = chc__calloc(al, n_rows * es, err);
|
|
2360
|
+
if (!dense) { rc = CHC_ERR_OOM; goto fail; }
|
|
2361
|
+
const uint8_t *src = c->fixed.data;
|
|
2362
|
+
for (size_t k = 0; k < n; k++)
|
|
2363
|
+
memcpy(dense + pos[k] * es, src + k * es, es);
|
|
2364
|
+
al->free(al->ud, c->fixed.data, n * es);
|
|
2365
|
+
c->fixed.data = dense;
|
|
2366
|
+
} else if (n < n_rows && c->layout == CHC_COL_STRING) {
|
|
2367
|
+
uint64_t *offs = chc__alloc(al, n_rows * sizeof *offs, err);
|
|
2368
|
+
if (!offs) { rc = CHC_ERR_OOM; goto fail; }
|
|
2369
|
+
uint64_t end = 0;
|
|
2370
|
+
for (size_t r = 0, k = 0; r < n_rows; r++) {
|
|
2371
|
+
if (k < n && pos[k] == r) end = c->str.offsets[k++];
|
|
2372
|
+
offs[r] = end;
|
|
2373
|
+
}
|
|
2374
|
+
al->free(al->ud, c->str.offsets, n * sizeof *c->str.offsets);
|
|
2375
|
+
c->str.offsets = offs;
|
|
2376
|
+
}
|
|
2377
|
+
c->n_rows = n_rows;
|
|
2378
|
+
al->free(al->ud, pos, cap * sizeof *pos);
|
|
2379
|
+
*out = c;
|
|
2380
|
+
return CHC_OK;
|
|
2381
|
+
|
|
2382
|
+
fail:
|
|
2383
|
+
chc__column_destroy(c, al);
|
|
2384
|
+
al->free(al->ud, pos, cap * sizeof *pos);
|
|
2385
|
+
return rc;
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
/* Dispatch on serialization kinds read by chc__read_kinds, advancing cursor
|
|
2389
|
+
* in same order: Tuple own kind, ignored as ClickHouse ignores it, then each
|
|
2390
|
+
* element's */
|
|
2391
|
+
static int
|
|
2392
|
+
chc__col_read_kinds(chc_in *in, const chc_type *t, size_t n_rows,
|
|
2393
|
+
const uint8_t **kinds, chc_column **out, chc_err *err)
|
|
2394
|
+
{
|
|
2395
|
+
t = chc__serial_type(t);
|
|
2396
|
+
uint8_t kind = *(*kinds)++;
|
|
2397
|
+
if (t->kind == CHC_TUPLE) return chc__col_read_tuple(in, t, n_rows, kinds, out, err);
|
|
2398
|
+
if (kind == CHC__KIND_SPARSE) return chc__col_read_sparse(in, t, n_rows, out, err);
|
|
2399
|
+
return chc__col_read(in, t, n_rows, out, err);
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2077
2402
|
static int
|
|
2078
2403
|
chc__col_read(chc_in *in, const chc_type *t,
|
|
2079
2404
|
size_t n_rows, chc_column **out, chc_err *err)
|
|
@@ -2085,27 +2410,11 @@ chc__col_read(chc_in *in, const chc_type *t,
|
|
|
2085
2410
|
|
|
2086
2411
|
switch (t->kind) {
|
|
2087
2412
|
case CHC_STRING:
|
|
2088
|
-
return chc__col_read_string(in, n_rows, out, err);
|
|
2089
|
-
|
|
2090
2413
|
case CHC_JSON:
|
|
2091
|
-
case CHC_OBJECT:
|
|
2092
|
-
/* JSON
|
|
2093
|
-
*
|
|
2094
|
-
* scope; other versions need the consumer to set
|
|
2095
|
-
* output_format_native_write_json_as_string=1 on the SELECT.
|
|
2096
|
-
* Body bytes per row are writeStringBinary, identical to a String
|
|
2097
|
-
* column — reuse chc__col_read_string and keep CHC_COL_STRING
|
|
2098
|
-
* layout so callers reuse string accessors. */
|
|
2099
|
-
uint64_t version;
|
|
2100
|
-
int rc = chc__read_u64_le(in, &version, err);
|
|
2101
|
-
if (rc != CHC_OK) return rc;
|
|
2102
|
-
if (version != 1)
|
|
2103
|
-
return chc__err_set(err, CHC_ERR_TYPE,
|
|
2104
|
-
"unsupported JSON serialization version %llu "
|
|
2105
|
-
"(set output_format_native_write_json_as_string=1)",
|
|
2106
|
-
(unsigned long long) version);
|
|
2414
|
+
case CHC_OBJECT:
|
|
2415
|
+
/* JSON/Object STRING-mode body rows use writeStringBinary, identical
|
|
2416
|
+
* to String. Prefix lives in chc__col_read_prefix */
|
|
2107
2417
|
return chc__col_read_string(in, n_rows, out, err);
|
|
2108
|
-
}
|
|
2109
2418
|
|
|
2110
2419
|
case CHC_NULLABLE: {
|
|
2111
2420
|
if (t->n_children != 1)
|
|
@@ -2127,11 +2436,14 @@ chc__col_read(chc_in *in, const chc_type *t,
|
|
|
2127
2436
|
}
|
|
2128
2437
|
|
|
2129
2438
|
case CHC_ARRAY:
|
|
2439
|
+
case CHC_NESTED:
|
|
2130
2440
|
case CHC_MAP: {
|
|
2131
2441
|
if (t->kind == CHC_ARRAY && t->n_children != 1)
|
|
2132
2442
|
return chc__err_set(err, CHC_ERR_TYPE, "Array expects 1 child");
|
|
2133
2443
|
if (t->kind == CHC_MAP && t->n_children != 2)
|
|
2134
2444
|
return chc__err_set(err, CHC_ERR_TYPE, "Map expects 2 children");
|
|
2445
|
+
if (t->kind == CHC_NESTED && !t->n_children)
|
|
2446
|
+
return chc__err_set(err, CHC_ERR_TYPE, "Nested has no fields");
|
|
2135
2447
|
chc_column *c = chc__calloc(al, sizeof *c, err);
|
|
2136
2448
|
if (!c) return CHC_ERR_OOM;
|
|
2137
2449
|
c->layout = CHC_COL_ARRAY;
|
|
@@ -2156,61 +2468,17 @@ chc__col_read(chc_in *in, const chc_type *t,
|
|
|
2156
2468
|
(unsigned long long) total);
|
|
2157
2469
|
}
|
|
2158
2470
|
}
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
/* Map: synthesise an implicit Tuple(K, V) column. */
|
|
2165
|
-
chc_column *tup = chc__calloc(al, sizeof *tup, err);
|
|
2166
|
-
if (!tup) { chc__column_destroy(c, al); return CHC_ERR_OOM; }
|
|
2167
|
-
tup->layout = CHC_COL_TUPLE;
|
|
2168
|
-
tup->n_rows = (size_t) total;
|
|
2169
|
-
tup->tuple.arity = 2;
|
|
2170
|
-
tup->tuple.children = chc__calloc(al, 2 * sizeof *tup->tuple.children, err);
|
|
2171
|
-
if (!tup->tuple.children) { chc__column_destroy(tup, al); chc__column_destroy(c, al); return CHC_ERR_OOM; }
|
|
2172
|
-
int rc = chc__col_read(in, t->children[0], (size_t) total,
|
|
2173
|
-
&tup->tuple.children[0], err);
|
|
2174
|
-
if (rc != CHC_OK) { chc__column_destroy(tup, al); chc__column_destroy(c, al); return rc; }
|
|
2175
|
-
rc = chc__col_read(in, t->children[1], (size_t) total,
|
|
2176
|
-
&tup->tuple.children[1], err);
|
|
2177
|
-
if (rc != CHC_OK) { chc__column_destroy(tup, al); chc__column_destroy(c, al); return rc; }
|
|
2178
|
-
c->array.values = tup;
|
|
2179
|
-
}
|
|
2471
|
+
/* ClickHouse sends Map and Nested values as arrays of tuples */
|
|
2472
|
+
int rc = t->kind == CHC_ARRAY
|
|
2473
|
+
? chc__col_read(in, t->children[0], (size_t) total, &c->array.values, err)
|
|
2474
|
+
: chc__col_read_tuple(in, t, (size_t) total, NULL, &c->array.values, err);
|
|
2475
|
+
if (rc != CHC_OK) { chc__column_destroy(c, al); return rc; }
|
|
2180
2476
|
*out = c;
|
|
2181
2477
|
return CHC_OK;
|
|
2182
2478
|
}
|
|
2183
2479
|
|
|
2184
|
-
case CHC_TUPLE:
|
|
2185
|
-
|
|
2186
|
-
if (!c) return CHC_ERR_OOM;
|
|
2187
|
-
c->layout = CHC_COL_TUPLE;
|
|
2188
|
-
c->n_rows = n_rows;
|
|
2189
|
-
c->tuple.arity = t->n_children;
|
|
2190
|
-
/* LOCAL PATCH (ch_connect): ClickHouse serializes the empty Tuple()
|
|
2191
|
-
* as one UInt8 (zero) per row; skipping nothing desyncs the stream. */
|
|
2192
|
-
if (t->n_children == 0) {
|
|
2193
|
-
uint8_t scratch[256];
|
|
2194
|
-
size_t left = n_rows;
|
|
2195
|
-
while (left) {
|
|
2196
|
-
size_t take = left > sizeof scratch ? sizeof scratch : left;
|
|
2197
|
-
int rc = chc__read_bytes(in, scratch, take, err);
|
|
2198
|
-
if (rc != CHC_OK) { chc__column_destroy(c, al); return rc; }
|
|
2199
|
-
left -= take;
|
|
2200
|
-
}
|
|
2201
|
-
*out = c;
|
|
2202
|
-
return CHC_OK;
|
|
2203
|
-
}
|
|
2204
|
-
c->tuple.children = chc__calloc(al, t->n_children * sizeof *c->tuple.children, err);
|
|
2205
|
-
if (!c->tuple.children) { chc__column_destroy(c, al); return CHC_ERR_OOM; }
|
|
2206
|
-
for (size_t i = 0; i < t->n_children; i++) {
|
|
2207
|
-
int rc = chc__col_read(in, t->children[i], n_rows,
|
|
2208
|
-
&c->tuple.children[i], err);
|
|
2209
|
-
if (rc != CHC_OK) { chc__column_destroy(c, al); return rc; }
|
|
2210
|
-
}
|
|
2211
|
-
*out = c;
|
|
2212
|
-
return CHC_OK;
|
|
2213
|
-
}
|
|
2480
|
+
case CHC_TUPLE:
|
|
2481
|
+
return chc__col_read_tuple(in, t, n_rows, NULL, out, err);
|
|
2214
2482
|
|
|
2215
2483
|
case CHC_QBIT: {
|
|
2216
2484
|
/* Wire form is Tuple(FixedString(ceil(N/8)) x element_size): one
|
|
@@ -2223,9 +2491,9 @@ chc__col_read(chc_in *in, const chc_type *t,
|
|
|
2223
2491
|
if (!c) return CHC_ERR_OOM;
|
|
2224
2492
|
c->layout = CHC_COL_TUPLE;
|
|
2225
2493
|
c->n_rows = n_rows;
|
|
2226
|
-
c->tuple.arity = bits;
|
|
2227
2494
|
c->tuple.children = chc__calloc(al, bits * sizeof *c->tuple.children, err);
|
|
2228
2495
|
if (!c->tuple.children) { chc__column_destroy(c, al); return CHC_ERR_OOM; }
|
|
2496
|
+
c->tuple.arity = bits;
|
|
2229
2497
|
for (size_t i = 0; i < bits; i++) {
|
|
2230
2498
|
int rc = chc__col_read_fixed(in, bytes_per_plane, n_rows,
|
|
2231
2499
|
&c->tuple.children[i], err);
|
|
@@ -2305,9 +2573,9 @@ chc__col_read(chc_in *in, const chc_type *t,
|
|
|
2305
2573
|
|
|
2306
2574
|
if (nullable_wrap) {
|
|
2307
2575
|
/* Wire convention: slot 0 of the inner-typed dict is the NULL
|
|
2308
|
-
* sentinel (
|
|
2309
|
-
* Wrap the dict in a Nullable column so the caller's
|
|
2310
|
-
* null-map dispatch covers the LC(Nullable) case. */
|
|
2576
|
+
* sentinel (see the LowCardinality section of the Native Format
|
|
2577
|
+
* spec). Wrap the dict in a Nullable column so the caller's
|
|
2578
|
+
* standard null-map dispatch covers the LC(Nullable) case. */
|
|
2311
2579
|
chc_column *wrapped = chc__calloc(al, sizeof *wrapped, err);
|
|
2312
2580
|
if (!wrapped) { chc__column_destroy(inner_dict, al); chc__column_destroy(c, al); return CHC_ERR_OOM; }
|
|
2313
2581
|
wrapped->layout = CHC_COL_NULLABLE;
|
|
@@ -2351,17 +2619,18 @@ chc__col_read(chc_in *in, const chc_type *t,
|
|
|
2351
2619
|
}
|
|
2352
2620
|
|
|
2353
2621
|
case CHC_SIMPLE_AGGREGATE_FUNCTION:
|
|
2354
|
-
/*
|
|
2622
|
+
/* ClickHouse stores values using this function's first argument type */
|
|
2355
2623
|
if (t->n_children < 1)
|
|
2356
2624
|
return chc__err_set(err, CHC_ERR_TYPE, "SimpleAggregateFunction has no inner type");
|
|
2357
|
-
return chc__col_read(in, t->children[
|
|
2625
|
+
return chc__col_read(in, t->children[0], n_rows, out, err);
|
|
2358
2626
|
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
case
|
|
2362
|
-
case
|
|
2363
|
-
case
|
|
2364
|
-
case CHC_MULTI_POLYGON:
|
|
2627
|
+
case CHC_POINT:
|
|
2628
|
+
case CHC_RING:
|
|
2629
|
+
case CHC_LINE_STRING:
|
|
2630
|
+
case CHC_POLYGON:
|
|
2631
|
+
case CHC_MULTI_LINE_STRING:
|
|
2632
|
+
case CHC_MULTI_POLYGON:
|
|
2633
|
+
return chc__col_read(in, chc__geo_alias(t), n_rows, out, err);
|
|
2365
2634
|
|
|
2366
2635
|
case CHC_NOTHING:
|
|
2367
2636
|
case CHC_VOID: {
|
|
@@ -2370,16 +2639,8 @@ chc__col_read(chc_in *in, const chc_type *t,
|
|
|
2370
2639
|
c->layout = CHC_COL_NOTHING;
|
|
2371
2640
|
c->n_rows = n_rows;
|
|
2372
2641
|
/* Wire shape for Nothing is a sequence of UInt8 bytes per row. */
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
size_t left = n_rows;
|
|
2376
|
-
while (left) {
|
|
2377
|
-
size_t take = left < sizeof throwaway ? left : sizeof throwaway;
|
|
2378
|
-
int rc = chc__read_bytes(in, throwaway, take, err);
|
|
2379
|
-
if (rc != CHC_OK) { chc__column_destroy(c, al); return rc; }
|
|
2380
|
-
left -= take;
|
|
2381
|
-
}
|
|
2382
|
-
}
|
|
2642
|
+
int rc = chc__skip_bytes(in, n_rows, err);
|
|
2643
|
+
if (rc != CHC_OK) { chc__column_destroy(c, al); return rc; }
|
|
2383
2644
|
*out = c;
|
|
2384
2645
|
return CHC_OK;
|
|
2385
2646
|
}
|
|
@@ -2393,60 +2654,74 @@ chc__col_read(chc_in *in, const chc_type *t,
|
|
|
2393
2654
|
}
|
|
2394
2655
|
}
|
|
2395
2656
|
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2657
|
+
/* -------- block reader ---------- */
|
|
2658
|
+
|
|
2659
|
+
/* Serialization kind count, Tuple emits own kind then each element's */
|
|
2660
|
+
static size_t
|
|
2661
|
+
chc__kinds_len(const chc_type *t)
|
|
2399
2662
|
{
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2663
|
+
t = chc__serial_type(t);
|
|
2664
|
+
size_t n = 1;
|
|
2665
|
+
if (t->kind == CHC_TUPLE)
|
|
2666
|
+
for (size_t i = 0; i < t->n_children; i++)
|
|
2667
|
+
n += chc__kinds_len(t->children[i]);
|
|
2668
|
+
return n;
|
|
2669
|
+
}
|
|
2670
|
+
|
|
2671
|
+
/* Validate kinds in chc__kinds_len order. Sparse applies to leaves stored
|
|
2672
|
+
* as fixed width values, String or Nothing. Other kinds carry payloads not
|
|
2673
|
+
* handled here */
|
|
2674
|
+
static int
|
|
2675
|
+
chc__kinds_check(const chc_type *t, const uint8_t **kinds, chc_err *err)
|
|
2676
|
+
{
|
|
2677
|
+
t = chc__serial_type(t);
|
|
2678
|
+
uint8_t kind = *(*kinds)++;
|
|
2679
|
+
if (kind > CHC__KIND_SPARSE)
|
|
2680
|
+
return chc__err_set(err, CHC_ERR_PROTOCOL,
|
|
2681
|
+
"unsupported serialization kind %u", (unsigned) kind);
|
|
2682
|
+
if (t->kind == CHC_TUPLE) {
|
|
2683
|
+
for (size_t i = 0; i < t->n_children; i++) {
|
|
2684
|
+
int rc = chc__kinds_check(t->children[i], kinds, err);
|
|
2685
|
+
if (rc != CHC_OK) return rc;
|
|
2413
2686
|
}
|
|
2414
|
-
*out = c;
|
|
2415
2687
|
return CHC_OK;
|
|
2416
2688
|
}
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
if (n_rows) {
|
|
2424
|
-
size_t offs_bytes;
|
|
2425
|
-
if (chc__mul_size(n_rows, sizeof(uint64_t), &offs_bytes)) {
|
|
2426
|
-
chc__column_destroy(c, al);
|
|
2427
|
-
return chc__err_set(err, CHC_ERR_PROTOCOL, "array offsets size overflow");
|
|
2428
|
-
}
|
|
2429
|
-
c->array.offsets = chc__alloc(al, offs_bytes, err);
|
|
2430
|
-
if (!c->array.offsets) { chc__column_destroy(c, al); return CHC_ERR_OOM; }
|
|
2431
|
-
int rc = chc__read_bytes(in, c->array.offsets, offs_bytes, err);
|
|
2432
|
-
if (rc != CHC_OK) { chc__column_destroy(c, al); return rc; }
|
|
2433
|
-
chc__swap_offsets(c->array.offsets, n_rows);
|
|
2434
|
-
total = c->array.offsets[n_rows - 1];
|
|
2435
|
-
if (total > CHC_MAX_NUM_ROWS) {
|
|
2436
|
-
chc__column_destroy(c, al);
|
|
2437
|
-
return chc__err_set(err, CHC_ERR_PROTOCOL,
|
|
2438
|
-
"array nested length too large: %llu",
|
|
2439
|
-
(unsigned long long) total);
|
|
2440
|
-
}
|
|
2689
|
+
if (kind == CHC__KIND_SPARSE && !chc_type_elem_size(t) && t->kind != CHC_STRING
|
|
2690
|
+
&& t->kind != CHC_NOTHING && t->kind != CHC_VOID) {
|
|
2691
|
+
size_t nl;
|
|
2692
|
+
const char *nm = chc_type_name(t, &nl);
|
|
2693
|
+
return chc__err_set(err, CHC_ERR_PROTOCOL,
|
|
2694
|
+
"sparse serialization unsupported for %.*s", (int) nl, nm ? nm : "");
|
|
2441
2695
|
}
|
|
2442
|
-
int rc = chc__col_read_geo(in, depth - 1, (size_t) total,
|
|
2443
|
-
&c->array.values, err);
|
|
2444
|
-
if (rc != CHC_OK) { chc__column_destroy(c, al); return rc; }
|
|
2445
|
-
*out = c;
|
|
2446
2696
|
return CHC_OK;
|
|
2447
2697
|
}
|
|
2448
2698
|
|
|
2449
|
-
/*
|
|
2699
|
+
/* Read has_custom flag & serialization kinds following column type. *out
|
|
2700
|
+
* stays NULL when every kind is DEFAULT */
|
|
2701
|
+
static int
|
|
2702
|
+
chc__read_kinds(chc_in *in, const chc_type *t, uint8_t **out, size_t *out_len,
|
|
2703
|
+
chc_err *err)
|
|
2704
|
+
{
|
|
2705
|
+
uint8_t has_custom;
|
|
2706
|
+
int rc = chc__read_byte(in, &has_custom, err);
|
|
2707
|
+
if (rc != CHC_OK || !has_custom) return rc;
|
|
2708
|
+
if (has_custom != 1)
|
|
2709
|
+
return chc__err_set(err, CHC_ERR_PROTOCOL,
|
|
2710
|
+
"invalid custom serialization flag %u", (unsigned) has_custom);
|
|
2711
|
+
size_t n = chc__kinds_len(t);
|
|
2712
|
+
uint8_t *k = chc__alloc(in->al, n, err);
|
|
2713
|
+
if (!k) return CHC_ERR_OOM;
|
|
2714
|
+
const uint8_t *cursor = k;
|
|
2715
|
+
rc = chc__read_bytes(in, k, n, err);
|
|
2716
|
+
if (rc == CHC_OK) rc = chc__kinds_check(t, &cursor, err);
|
|
2717
|
+
if (rc != CHC_OK || !memchr(k, CHC__KIND_SPARSE, n)) {
|
|
2718
|
+
in->al->free(in->al->ud, k, n);
|
|
2719
|
+
return rc;
|
|
2720
|
+
}
|
|
2721
|
+
*out = k;
|
|
2722
|
+
*out_len = n;
|
|
2723
|
+
return CHC_OK;
|
|
2724
|
+
}
|
|
2450
2725
|
|
|
2451
2726
|
struct chc_block {
|
|
2452
2727
|
size_t n_columns;
|
|
@@ -2600,7 +2875,8 @@ chc__block_resume_in(chc_in *in, const chc_alloc *al,
|
|
|
2600
2875
|
|
|
2601
2876
|
size_t nrows = b->n_rows;
|
|
2602
2877
|
for (size_t i = *next_col; i < b->n_columns; i++) {
|
|
2603
|
-
char *type_name
|
|
2878
|
+
char *type_name; size_t type_len;
|
|
2879
|
+
uint8_t *kinds = NULL; size_t kinds_len = 0;
|
|
2604
2880
|
|
|
2605
2881
|
/* Checkpoint column start so a mid-column would-block rewinds here,
|
|
2606
2882
|
* dropping completed columns' wire bytes on the caller's next submit. */
|
|
@@ -2611,37 +2887,33 @@ chc__block_resume_in(chc_in *in, const chc_alloc *al,
|
|
|
2611
2887
|
|
|
2612
2888
|
rc = chc__read_string(in, &type_name, &type_len, err);
|
|
2613
2889
|
if (rc != CHC_OK) goto col_fail;
|
|
2890
|
+
rc = chc_type_parse(type_name, type_len, al, &b->types[i], err);
|
|
2891
|
+
al->free(al->ud, type_name, type_len + 1);
|
|
2892
|
+
if (rc != CHC_OK) goto col_fail;
|
|
2614
2893
|
|
|
2615
2894
|
if (opts->has_custom_serialization) {
|
|
2616
|
-
|
|
2617
|
-
rc = chc__read_byte(in, &hcs, err);
|
|
2895
|
+
rc = chc__read_kinds(in, b->types[i], &kinds, &kinds_len, err);
|
|
2618
2896
|
if (rc != CHC_OK) goto col_fail;
|
|
2619
|
-
if (hcs) {
|
|
2620
|
-
rc = chc__err_set(err, CHC_ERR_PROTOCOL,
|
|
2621
|
-
"custom serialization not supported on column '%s'", b->names[i]);
|
|
2622
|
-
goto col_fail;
|
|
2623
|
-
}
|
|
2624
2897
|
}
|
|
2625
2898
|
|
|
2626
|
-
|
|
2627
|
-
al->free(al->ud, type_name, type_len + 1);
|
|
2628
|
-
type_name = NULL;
|
|
2629
|
-
if (rc != CHC_OK) goto col_fail;
|
|
2630
|
-
|
|
2899
|
+
/* Zero-row column carries kinds but no body, not even sparse offsets */
|
|
2631
2900
|
if (nrows) {
|
|
2632
2901
|
rc = chc__col_read_prefix(in, b->types[i], err);
|
|
2633
2902
|
if (rc != CHC_OK) goto col_fail;
|
|
2634
2903
|
|
|
2635
|
-
|
|
2904
|
+
const uint8_t *cursor = kinds;
|
|
2905
|
+
rc = kinds ? chc__col_read_kinds(in, b->types[i], nrows, &cursor, &b->columns[i], err)
|
|
2906
|
+
: chc__col_read(in, b->types[i], nrows, &b->columns[i], err);
|
|
2636
2907
|
if (rc != CHC_OK) goto col_fail;
|
|
2637
2908
|
}
|
|
2909
|
+
al->free(al->ud, kinds, kinds_len);
|
|
2638
2910
|
continue;
|
|
2639
2911
|
|
|
2640
2912
|
col_fail:
|
|
2913
|
+
al->free(al->ud, kinds, kinds_len);
|
|
2641
2914
|
if (ioless && rc == CHC_WOULD_BLOCK) {
|
|
2642
2915
|
/* Retain columns [0,i); reset slot i to a destroy-safe NULL state
|
|
2643
2916
|
* and rewind so column i re-parses from its checkpoint. */
|
|
2644
|
-
if (type_name) al->free(al->ud, type_name, type_len + 1);
|
|
2645
2917
|
if (b->names[i]) {
|
|
2646
2918
|
al->free(al->ud, b->names[i], b->name_lens[i] + 1);
|
|
2647
2919
|
b->names[i] = NULL;
|
|
@@ -2655,7 +2927,6 @@ chc__block_resume_in(chc_in *in, const chc_alloc *al,
|
|
|
2655
2927
|
*next_col = i;
|
|
2656
2928
|
return CHC_WOULD_BLOCK;
|
|
2657
2929
|
}
|
|
2658
|
-
if (type_name) al->free(al->ud, type_name, type_len + 1);
|
|
2659
2930
|
chc_block_destroy(b, al);
|
|
2660
2931
|
*blk = NULL;
|
|
2661
2932
|
return rc;
|
|
@@ -2671,7 +2942,7 @@ header_fail:
|
|
|
2671
2942
|
return rc;
|
|
2672
2943
|
}
|
|
2673
2944
|
|
|
2674
|
-
/* Block read from an already-
|
|
2945
|
+
/* Block read from an already-initialized chc_in. Also used by
|
|
2675
2946
|
* clickhouse-client.h's recv_packet (persistent buffer). Thin non-looping
|
|
2676
2947
|
* wrapper over chc__block_resume_in. Returns 0 with *out == NULL on clean EOF
|
|
2677
2948
|
* at block boundary (only when opts->has_block_info is false; TCP path has no
|
|
@@ -2711,410 +2982,93 @@ chc_block_read(chc_in *in, const chc_alloc *al,
|
|
|
2711
2982
|
|
|
2712
2983
|
/* -------- block writer ---------- */
|
|
2713
2984
|
|
|
2714
|
-
typedef enum {
|
|
2715
|
-
CHC__BLD_FIXED = 1,
|
|
2716
|
-
CHC__BLD_STRING = 2,
|
|
2717
|
-
CHC__BLD_NULL_FIXED = 3,
|
|
2718
|
-
CHC__BLD_NULL_STRING = 4,
|
|
2719
|
-
CHC__BLD_ARRAY_FIXED = 5,
|
|
2720
|
-
CHC__BLD_ARRAY_STRING = 6,
|
|
2721
|
-
CHC__BLD_LC_STRING = 7,
|
|
2722
|
-
CHC__BLD_JSON_STRING = 8,
|
|
2723
|
-
CHC__BLD_ARRAY_NESTED_FIXED = 9,
|
|
2724
|
-
CHC__BLD_ARRAY_NESTED_STRING = 10,
|
|
2725
|
-
} chc__bld_kind;
|
|
2726
|
-
|
|
2727
|
-
typedef struct {
|
|
2728
|
-
const char *name;
|
|
2729
|
-
size_t name_len;
|
|
2730
|
-
const chc_type *type; /* NULL only for legacy STRING entries */
|
|
2731
|
-
chc__bld_kind kind;
|
|
2732
|
-
size_t n_rows;
|
|
2733
|
-
size_t inner_n; /* element count of the inner array/string/dict body */
|
|
2734
|
-
/* Pointers into caller-owned memory; library never copies. */
|
|
2735
|
-
/* Base representation: fixed-width xor variable-length. */
|
|
2736
|
-
union {
|
|
2737
|
-
struct { const void *data; size_t elem_size; } fixed; /* *_FIXED */
|
|
2738
|
-
struct { const uint64_t *offsets; const uint8_t *data; } str; /* *_STRING / LC dict */
|
|
2739
|
-
};
|
|
2740
|
-
/* Structural modifier over base; absent for plain FIXED / STRING / JSON. */
|
|
2741
|
-
union {
|
|
2742
|
-
struct { const uint8_t *null_map; } nullable; /* NULL_* */
|
|
2743
|
-
struct { const uint64_t *offsets; } array; /* ARRAY_FIXED / ARRAY_STRING (cumulative ends) */
|
|
2744
|
-
struct { /* ARRAY_NESTED_*, ndim >= 2 */
|
|
2745
|
-
int ndim;
|
|
2746
|
-
const uint64_t * const *level_offsets; /* ndim cumulative-end arrays */
|
|
2747
|
-
const size_t *level_offsets_len; /* count per level */
|
|
2748
|
-
} nested;
|
|
2749
|
-
struct { int key_size; const void *keys; } lc; /* LC_STRING */
|
|
2750
|
-
};
|
|
2751
|
-
} chc__col_entry;
|
|
2752
|
-
|
|
2753
|
-
struct chc_block_builder {
|
|
2754
|
-
const chc_alloc *al; /* captured at init */
|
|
2755
|
-
chc__col_entry *cols;
|
|
2756
|
-
size_t n_cols;
|
|
2757
|
-
size_t cap;
|
|
2758
|
-
size_t n_rows; /* common across all columns */
|
|
2759
|
-
bool n_rows_set;
|
|
2760
|
-
};
|
|
2761
|
-
|
|
2762
|
-
int
|
|
2763
|
-
chc_block_builder_init(chc_block_builder **out, const chc_alloc *al,
|
|
2764
|
-
chc_err *err)
|
|
2765
|
-
{
|
|
2766
|
-
chc_block_builder *bb = chc__calloc(al, sizeof *bb, err);
|
|
2767
|
-
if (!bb) return CHC_ERR_OOM;
|
|
2768
|
-
bb->al = al;
|
|
2769
|
-
*out = bb;
|
|
2770
|
-
return CHC_OK;
|
|
2771
|
-
}
|
|
2772
|
-
|
|
2773
2985
|
void
|
|
2774
|
-
|
|
2986
|
+
chc_block_builder_init(chc_block_builder *bb, chc_block_col *cols)
|
|
2775
2987
|
{
|
|
2776
|
-
|
|
2777
|
-
const chc_alloc *al = bb->al;
|
|
2778
|
-
al->free(al->ud, bb->cols, bb->cap * sizeof *bb->cols);
|
|
2779
|
-
al->free(al->ud, bb, sizeof *bb);
|
|
2988
|
+
*bb = (chc_block_builder) { .cols = cols };
|
|
2780
2989
|
}
|
|
2781
2990
|
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
if (bb->n_cols < bb->cap) return CHC_OK;
|
|
2786
|
-
size_t new_cap = bb->cap ? bb->cap * 2 : 4;
|
|
2787
|
-
chc__col_entry *p = chc__realloc(bb->al, bb->cols,
|
|
2788
|
-
bb->cap * sizeof *bb->cols,
|
|
2789
|
-
new_cap * sizeof *bb->cols, err);
|
|
2790
|
-
if (!p) return CHC_ERR_OOM;
|
|
2791
|
-
bb->cols = p;
|
|
2792
|
-
bb->cap = new_cap;
|
|
2793
|
-
return CHC_OK;
|
|
2794
|
-
}
|
|
2991
|
+
/* -------- compositional column builders ---------- */
|
|
2992
|
+
/* Cast away const for reader-compatible tree shape; writer never mutates
|
|
2993
|
+
* caller trees */
|
|
2795
2994
|
|
|
2796
|
-
|
|
2797
|
-
|
|
2995
|
+
chc_column
|
|
2996
|
+
chc_build_fixed(const void *data, size_t elem_size, size_t n_rows)
|
|
2798
2997
|
{
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2998
|
+
return (chc_column) {
|
|
2999
|
+
.layout = CHC_COL_FIXED,
|
|
3000
|
+
.n_rows = n_rows,
|
|
3001
|
+
.fixed.data = (void *) data,
|
|
3002
|
+
.fixed.elem_size = elem_size,
|
|
3003
|
+
};
|
|
2804
3004
|
}
|
|
2805
3005
|
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
const chc_type *type, chc__bld_kind kind, size_t n_rows,
|
|
2809
|
-
chc__col_entry **out, chc_err *err)
|
|
3006
|
+
chc_column
|
|
3007
|
+
chc_build_string(const uint64_t *offsets, const uint8_t *data, size_t n_rows)
|
|
2810
3008
|
{
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
*e = (chc__col_entry) {
|
|
2817
|
-
.name = name, .name_len = name_len, .type = type,
|
|
2818
|
-
.kind = kind, .n_rows = n_rows,
|
|
3009
|
+
return (chc_column) {
|
|
3010
|
+
.layout = CHC_COL_STRING,
|
|
3011
|
+
.n_rows = n_rows,
|
|
3012
|
+
.str.offsets = (uint64_t *) offsets,
|
|
3013
|
+
.str.data = (uint8_t *) data,
|
|
2819
3014
|
};
|
|
2820
|
-
*out = e;
|
|
2821
|
-
return CHC_OK;
|
|
2822
3015
|
}
|
|
2823
3016
|
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
const char *name, size_t name_len,
|
|
2827
|
-
const chc_type *t,
|
|
2828
|
-
const void *data, size_t n_rows,
|
|
2829
|
-
chc_err *err)
|
|
3017
|
+
chc_column
|
|
3018
|
+
chc_build_nullable(const uint8_t *null_map, chc_column *inner)
|
|
2830
3019
|
{
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
if (rc != CHC_OK) return rc;
|
|
2838
|
-
e->fixed.data = data;
|
|
2839
|
-
e->fixed.elem_size = es;
|
|
2840
|
-
return CHC_OK;
|
|
2841
|
-
}
|
|
2842
|
-
|
|
2843
|
-
int
|
|
2844
|
-
chc_block_builder_append_string(chc_block_builder *bb,
|
|
2845
|
-
const char *name, size_t name_len,
|
|
2846
|
-
const uint64_t *offsets,
|
|
2847
|
-
const uint8_t *data, size_t n_rows,
|
|
2848
|
-
chc_err *err)
|
|
2849
|
-
{
|
|
2850
|
-
chc__col_entry *e;
|
|
2851
|
-
int rc = chc__bld_add(bb, name, name_len, NULL, CHC__BLD_STRING,
|
|
2852
|
-
n_rows, &e, err);
|
|
2853
|
-
if (rc != CHC_OK) return rc;
|
|
2854
|
-
e->str.offsets = offsets;
|
|
2855
|
-
e->str.data = data;
|
|
2856
|
-
e->inner_n = n_rows;
|
|
2857
|
-
return CHC_OK;
|
|
3020
|
+
return (chc_column) {
|
|
3021
|
+
.layout = CHC_COL_NULLABLE,
|
|
3022
|
+
.n_rows = inner ? inner->n_rows : 0,
|
|
3023
|
+
.nullable.null_map = (uint8_t *) null_map,
|
|
3024
|
+
.nullable.inner = inner,
|
|
3025
|
+
};
|
|
2858
3026
|
}
|
|
2859
3027
|
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
static size_t
|
|
2863
|
-
chc__bld_inner_fixed_size(const chc_type *t, chc_kind outer)
|
|
3028
|
+
chc_column
|
|
3029
|
+
chc_build_array(const uint64_t *offsets, size_t n_rows, chc_column *values)
|
|
2864
3030
|
{
|
|
2865
|
-
|
|
2866
|
-
|
|
3031
|
+
return (chc_column) {
|
|
3032
|
+
.layout = CHC_COL_ARRAY,
|
|
3033
|
+
.n_rows = n_rows,
|
|
3034
|
+
.array.offsets = (uint64_t *) offsets,
|
|
3035
|
+
.array.values = values,
|
|
3036
|
+
};
|
|
2867
3037
|
}
|
|
2868
3038
|
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
chc__bld_inner_is_string(const chc_type *t, chc_kind outer)
|
|
3039
|
+
chc_column
|
|
3040
|
+
chc_build_tuple(chc_column **children, size_t arity)
|
|
2872
3041
|
{
|
|
2873
|
-
return
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
chc__bld_lc_inner_is_string(const chc_type *t)
|
|
2880
|
-
{
|
|
2881
|
-
if (!t || t->kind != CHC_LOW_CARDINALITY || t->n_children != 1) return false;
|
|
2882
|
-
const chc_type *inner = t->children[0];
|
|
2883
|
-
if (inner->kind == CHC_STRING) return true;
|
|
2884
|
-
if (inner->kind == CHC_NULLABLE && inner->n_children == 1
|
|
2885
|
-
&& inner->children[0]->kind == CHC_STRING)
|
|
2886
|
-
return true;
|
|
2887
|
-
return false;
|
|
2888
|
-
}
|
|
2889
|
-
|
|
2890
|
-
int
|
|
2891
|
-
chc_block_builder_append_nullable_fixed(chc_block_builder *bb,
|
|
2892
|
-
const char *name, size_t name_len,
|
|
2893
|
-
const chc_type *t,
|
|
2894
|
-
const uint8_t *null_map,
|
|
2895
|
-
const void *inner_data,
|
|
2896
|
-
size_t n_rows, chc_err *err)
|
|
2897
|
-
{
|
|
2898
|
-
size_t es = chc__bld_inner_fixed_size(t, CHC_NULLABLE);
|
|
2899
|
-
if (!es) return chc__err_set(err, CHC_ERR_TYPE,
|
|
2900
|
-
"append_nullable_fixed: type is not Nullable(<fixed>)");
|
|
2901
|
-
chc__col_entry *e;
|
|
2902
|
-
int rc = chc__bld_add(bb, name, name_len, t, CHC__BLD_NULL_FIXED,
|
|
2903
|
-
n_rows, &e, err);
|
|
2904
|
-
if (rc != CHC_OK) return rc;
|
|
2905
|
-
e->nullable.null_map = null_map;
|
|
2906
|
-
e->fixed.data = inner_data;
|
|
2907
|
-
e->fixed.elem_size = es;
|
|
2908
|
-
return CHC_OK;
|
|
2909
|
-
}
|
|
2910
|
-
|
|
2911
|
-
int
|
|
2912
|
-
chc_block_builder_append_nullable_string(chc_block_builder *bb,
|
|
2913
|
-
const char *name, size_t name_len,
|
|
2914
|
-
const chc_type *t,
|
|
2915
|
-
const uint8_t *null_map,
|
|
2916
|
-
const uint64_t *inner_offsets,
|
|
2917
|
-
const uint8_t *inner_data,
|
|
2918
|
-
size_t n_rows, chc_err *err)
|
|
2919
|
-
{
|
|
2920
|
-
if (!chc__bld_inner_is_string(t, CHC_NULLABLE))
|
|
2921
|
-
return chc__err_set(err, CHC_ERR_TYPE,
|
|
2922
|
-
"append_nullable_string: type is not Nullable(String)");
|
|
2923
|
-
chc__col_entry *e;
|
|
2924
|
-
int rc = chc__bld_add(bb, name, name_len, t, CHC__BLD_NULL_STRING,
|
|
2925
|
-
n_rows, &e, err);
|
|
2926
|
-
if (rc != CHC_OK) return rc;
|
|
2927
|
-
e->nullable.null_map = null_map;
|
|
2928
|
-
e->str.offsets = inner_offsets;
|
|
2929
|
-
e->str.data = inner_data;
|
|
2930
|
-
e->inner_n = n_rows;
|
|
2931
|
-
return CHC_OK;
|
|
2932
|
-
}
|
|
2933
|
-
|
|
2934
|
-
int
|
|
2935
|
-
chc_block_builder_append_array_fixed(chc_block_builder *bb,
|
|
2936
|
-
const char *name, size_t name_len,
|
|
2937
|
-
const chc_type *t,
|
|
2938
|
-
const uint64_t *offsets,
|
|
2939
|
-
const void *values,
|
|
2940
|
-
size_t n_rows, chc_err *err)
|
|
2941
|
-
{
|
|
2942
|
-
size_t es = chc__bld_inner_fixed_size(t, CHC_ARRAY);
|
|
2943
|
-
if (!es) return chc__err_set(err, CHC_ERR_TYPE,
|
|
2944
|
-
"append_array_fixed: type is not Array(<fixed>)");
|
|
2945
|
-
chc__col_entry *e;
|
|
2946
|
-
int rc = chc__bld_add(bb, name, name_len, t, CHC__BLD_ARRAY_FIXED,
|
|
2947
|
-
n_rows, &e, err);
|
|
2948
|
-
if (rc != CHC_OK) return rc;
|
|
2949
|
-
e->array.offsets = offsets;
|
|
2950
|
-
e->fixed.data = values;
|
|
2951
|
-
e->fixed.elem_size = es;
|
|
2952
|
-
e->inner_n = n_rows ? (size_t) offsets[n_rows - 1] : 0;
|
|
2953
|
-
return CHC_OK;
|
|
2954
|
-
}
|
|
2955
|
-
|
|
2956
|
-
int
|
|
2957
|
-
chc_block_builder_append_array_string(chc_block_builder *bb,
|
|
2958
|
-
const char *name, size_t name_len,
|
|
2959
|
-
const chc_type *t,
|
|
2960
|
-
const uint64_t *offsets,
|
|
2961
|
-
const uint64_t *values_offsets,
|
|
2962
|
-
const uint8_t *values_data,
|
|
2963
|
-
size_t n_rows, chc_err *err)
|
|
2964
|
-
{
|
|
2965
|
-
if (!chc__bld_inner_is_string(t, CHC_ARRAY))
|
|
2966
|
-
return chc__err_set(err, CHC_ERR_TYPE,
|
|
2967
|
-
"append_array_string: type is not Array(String)");
|
|
2968
|
-
chc__col_entry *e;
|
|
2969
|
-
int rc = chc__bld_add(bb, name, name_len, t, CHC__BLD_ARRAY_STRING,
|
|
2970
|
-
n_rows, &e, err);
|
|
2971
|
-
if (rc != CHC_OK) return rc;
|
|
2972
|
-
e->array.offsets = offsets;
|
|
2973
|
-
e->str.offsets = values_offsets;
|
|
2974
|
-
e->str.data = values_data;
|
|
2975
|
-
e->inner_n = n_rows ? (size_t) offsets[n_rows - 1] : 0;
|
|
2976
|
-
return CHC_OK;
|
|
3042
|
+
return (chc_column) {
|
|
3043
|
+
.layout = CHC_COL_TUPLE,
|
|
3044
|
+
.n_rows = arity ? children[0]->n_rows : 0,
|
|
3045
|
+
.tuple.children = children,
|
|
3046
|
+
.tuple.arity = arity,
|
|
3047
|
+
};
|
|
2977
3048
|
}
|
|
2978
3049
|
|
|
2979
|
-
|
|
2980
|
-
*
|
|
2981
|
-
static const chc_type *
|
|
2982
|
-
chc__bld_array_leaf(const chc_type *t, int ndim)
|
|
3050
|
+
chc_column
|
|
3051
|
+
chc_build_lc(int key_size, const void *keys, size_t n_rows, chc_column *dict)
|
|
2983
3052
|
{
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
chc_block_builder_append_array_nested_fixed(chc_block_builder *bb,
|
|
2993
|
-
const char *name, size_t name_len,
|
|
2994
|
-
const chc_type *t,
|
|
2995
|
-
int ndim,
|
|
2996
|
-
const uint64_t * const *level_offsets,
|
|
2997
|
-
const size_t *level_offsets_len,
|
|
2998
|
-
const void *values,
|
|
2999
|
-
size_t n_rows, chc_err *err)
|
|
3000
|
-
{
|
|
3001
|
-
if (ndim < 2)
|
|
3002
|
-
return chc__err_set(err, CHC_ERR_USAGE,
|
|
3003
|
-
"append_array_nested_fixed: ndim must be >= 2");
|
|
3004
|
-
const chc_type *leaf = chc__bld_array_leaf(t, ndim);
|
|
3005
|
-
if (!leaf)
|
|
3006
|
-
return chc__err_set(err, CHC_ERR_TYPE,
|
|
3007
|
-
"append_array_nested_fixed: type does not match ndim");
|
|
3008
|
-
size_t es = chc_type_elem_size(leaf);
|
|
3009
|
-
if (!es)
|
|
3010
|
-
return chc__err_set(err, CHC_ERR_TYPE,
|
|
3011
|
-
"append_array_nested_fixed: leaf is not fixed-size");
|
|
3012
|
-
if (n_rows != level_offsets_len[0])
|
|
3013
|
-
return chc__err_set(err, CHC_ERR_USAGE,
|
|
3014
|
-
"append_array_nested_fixed: n_rows != level_offsets_len[0]");
|
|
3015
|
-
chc__col_entry *e;
|
|
3016
|
-
int rc = chc__bld_add(bb, name, name_len, t,
|
|
3017
|
-
CHC__BLD_ARRAY_NESTED_FIXED, n_rows, &e, err);
|
|
3018
|
-
if (rc != CHC_OK) return rc;
|
|
3019
|
-
e->nested.ndim = ndim;
|
|
3020
|
-
e->nested.level_offsets = level_offsets;
|
|
3021
|
-
e->nested.level_offsets_len = level_offsets_len;
|
|
3022
|
-
e->fixed.data = values;
|
|
3023
|
-
e->fixed.elem_size = es;
|
|
3024
|
-
/* inner_n holds leaf element count: last cumulative end of innermost level */
|
|
3025
|
-
{
|
|
3026
|
-
size_t ilen = level_offsets_len[ndim - 1];
|
|
3027
|
-
e->inner_n = ilen ? (size_t) level_offsets[ndim - 1][ilen - 1] : 0;
|
|
3028
|
-
}
|
|
3029
|
-
return CHC_OK;
|
|
3030
|
-
}
|
|
3031
|
-
|
|
3032
|
-
int
|
|
3033
|
-
chc_block_builder_append_array_nested_string(chc_block_builder *bb,
|
|
3034
|
-
const char *name, size_t name_len,
|
|
3035
|
-
const chc_type *t,
|
|
3036
|
-
int ndim,
|
|
3037
|
-
const uint64_t * const *level_offsets,
|
|
3038
|
-
const size_t *level_offsets_len,
|
|
3039
|
-
const uint64_t *values_offsets,
|
|
3040
|
-
const uint8_t *values_data,
|
|
3041
|
-
size_t n_rows, chc_err *err)
|
|
3042
|
-
{
|
|
3043
|
-
if (ndim < 2)
|
|
3044
|
-
return chc__err_set(err, CHC_ERR_USAGE,
|
|
3045
|
-
"append_array_nested_string: ndim must be >= 2");
|
|
3046
|
-
const chc_type *leaf = chc__bld_array_leaf(t, ndim);
|
|
3047
|
-
if (!leaf || leaf->kind != CHC_STRING)
|
|
3048
|
-
return chc__err_set(err, CHC_ERR_TYPE,
|
|
3049
|
-
"append_array_nested_string: leaf is not String");
|
|
3050
|
-
if (n_rows != level_offsets_len[0])
|
|
3051
|
-
return chc__err_set(err, CHC_ERR_USAGE,
|
|
3052
|
-
"append_array_nested_string: n_rows != level_offsets_len[0]");
|
|
3053
|
-
chc__col_entry *e;
|
|
3054
|
-
int rc = chc__bld_add(bb, name, name_len, t,
|
|
3055
|
-
CHC__BLD_ARRAY_NESTED_STRING, n_rows, &e, err);
|
|
3056
|
-
if (rc != CHC_OK) return rc;
|
|
3057
|
-
e->nested.ndim = ndim;
|
|
3058
|
-
e->nested.level_offsets = level_offsets;
|
|
3059
|
-
e->nested.level_offsets_len = level_offsets_len;
|
|
3060
|
-
e->str.offsets = values_offsets;
|
|
3061
|
-
e->str.data = values_data;
|
|
3062
|
-
{
|
|
3063
|
-
size_t ilen = level_offsets_len[ndim - 1];
|
|
3064
|
-
e->inner_n = ilen ? (size_t) level_offsets[ndim - 1][ilen - 1] : 0;
|
|
3065
|
-
}
|
|
3066
|
-
return CHC_OK;
|
|
3067
|
-
}
|
|
3068
|
-
|
|
3069
|
-
int
|
|
3070
|
-
chc_block_builder_append_json_string(chc_block_builder *bb,
|
|
3071
|
-
const char *name, size_t name_len,
|
|
3072
|
-
const chc_type *t,
|
|
3073
|
-
const uint64_t *offsets,
|
|
3074
|
-
const uint8_t *data,
|
|
3075
|
-
size_t n_rows, chc_err *err)
|
|
3076
|
-
{
|
|
3077
|
-
if (!t || t->kind != CHC_JSON)
|
|
3078
|
-
return chc__err_set(err, CHC_ERR_TYPE,
|
|
3079
|
-
"append_json_string requires CHC_JSON type, got %d",
|
|
3080
|
-
(int) (t ? t->kind : 0));
|
|
3081
|
-
chc__col_entry *e;
|
|
3082
|
-
int rc = chc__bld_add(bb, name, name_len, t, CHC__BLD_JSON_STRING,
|
|
3083
|
-
n_rows, &e, err);
|
|
3084
|
-
if (rc != CHC_OK) return rc;
|
|
3085
|
-
e->str.offsets = offsets;
|
|
3086
|
-
e->str.data = data;
|
|
3087
|
-
e->inner_n = n_rows;
|
|
3088
|
-
return CHC_OK;
|
|
3053
|
+
return (chc_column) {
|
|
3054
|
+
.layout = CHC_COL_LOW_CARDINALITY,
|
|
3055
|
+
.n_rows = n_rows,
|
|
3056
|
+
.lc.key_size = key_size,
|
|
3057
|
+
.lc.keys = (void *) keys,
|
|
3058
|
+
.lc.dict = dict,
|
|
3059
|
+
.lc.dict_n = dict ? dict->n_rows : 0,
|
|
3060
|
+
};
|
|
3089
3061
|
}
|
|
3090
3062
|
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
size_t n_rows, chc_err *err)
|
|
3101
|
-
{
|
|
3102
|
-
if (!chc__bld_lc_inner_is_string(t))
|
|
3103
|
-
return chc__err_set(err, CHC_ERR_TYPE,
|
|
3104
|
-
"append_low_cardinality_string: type is not LowCardinality(String) or LowCardinality(Nullable(String))");
|
|
3105
|
-
if (key_size != 1 && key_size != 2 && key_size != 4 && key_size != 8)
|
|
3106
|
-
return chc__err_set(err, CHC_ERR_USAGE,
|
|
3107
|
-
"append_low_cardinality_string: key_size must be 1/2/4/8 (got %d)", key_size);
|
|
3108
|
-
chc__col_entry *e;
|
|
3109
|
-
int rc = chc__bld_add(bb, name, name_len, t, CHC__BLD_LC_STRING,
|
|
3110
|
-
n_rows, &e, err);
|
|
3111
|
-
if (rc != CHC_OK) return rc;
|
|
3112
|
-
e->lc.key_size = key_size;
|
|
3113
|
-
e->lc.keys = keys;
|
|
3114
|
-
e->str.offsets = dict_offsets;
|
|
3115
|
-
e->str.data = dict_data;
|
|
3116
|
-
e->inner_n = dict_n;
|
|
3117
|
-
return CHC_OK;
|
|
3063
|
+
void
|
|
3064
|
+
chc_block_builder_append(chc_block_builder *bb,
|
|
3065
|
+
const char *name, size_t name_len,
|
|
3066
|
+
const chc_type *t, const chc_column *col)
|
|
3067
|
+
{
|
|
3068
|
+
bb->n_rows = col->n_rows;
|
|
3069
|
+
bb->cols[bb->n_cols++] = (chc_block_col) {
|
|
3070
|
+
.name = name, .name_len = name_len, .type = t, .col = col,
|
|
3071
|
+
};
|
|
3118
3072
|
}
|
|
3119
3073
|
|
|
3120
3074
|
/* -------- write helpers ---------- */
|
|
@@ -3244,94 +3198,185 @@ chc__write_string_body(chc_io *io, const uint64_t *offsets,
|
|
|
3244
3198
|
return CHC_OK;
|
|
3245
3199
|
}
|
|
3246
3200
|
|
|
3247
|
-
/*
|
|
3201
|
+
/* -------- recursive column writer ---------- */
|
|
3202
|
+
|
|
3248
3203
|
static int
|
|
3249
|
-
|
|
3204
|
+
chc__col_write_mismatch(chc_err *err, const chc_type *t)
|
|
3250
3205
|
{
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
e->n_rows * e->fixed.elem_size, err);
|
|
3257
|
-
return CHC_OK;
|
|
3206
|
+
size_t nl;
|
|
3207
|
+
const char *nm = chc_type_name(t, &nl);
|
|
3208
|
+
return chc__err_set(err, CHC_ERR_TYPE,
|
|
3209
|
+
"column layout does not match type %.*s", (int) nl, nm ? nm : "");
|
|
3210
|
+
}
|
|
3258
3211
|
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3212
|
+
/* Emit stream prefix before column bodies, mirroring chc__col_read_prefix:
|
|
3213
|
+
* LowCardinality key version 1, JSON serialization version 1, legacy Object
|
|
3214
|
+
* serialization kind 1. Recurse through composites */
|
|
3215
|
+
static int
|
|
3216
|
+
chc__col_write_prefix(chc_io *io, const chc_type *t, chc_err *err)
|
|
3217
|
+
{
|
|
3218
|
+
if (t->kind == CHC_LOW_CARDINALITY || t->kind == CHC_JSON)
|
|
3219
|
+
return chc__write_u64_le(io, 1, err);
|
|
3220
|
+
if (t->kind == CHC_OBJECT) {
|
|
3221
|
+
uint8_t kind = 1;
|
|
3222
|
+
return chc__write_bytes(io, &kind, 1, err);
|
|
3223
|
+
}
|
|
3224
|
+
if (t->kind == CHC_NULLABLE || t->kind == CHC_ARRAY
|
|
3225
|
+
|| t->kind == CHC_TUPLE || t->kind == CHC_MAP
|
|
3226
|
+
|| t->kind == CHC_NESTED
|
|
3227
|
+
|| t->kind == CHC_SIMPLE_AGGREGATE_FUNCTION) {
|
|
3228
|
+
for (size_t i = 0; i < t->n_children; i++) {
|
|
3229
|
+
int rc = chc__col_write_prefix(io, t->children[i], err);
|
|
3230
|
+
if (rc != CHC_OK) return rc;
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
return CHC_OK;
|
|
3234
|
+
}
|
|
3263
3235
|
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3236
|
+
/* Emit body from chc_column tree. Each node carries row count for its level.
|
|
3237
|
+
* Assume top-level block contains rows */
|
|
3238
|
+
static int
|
|
3239
|
+
chc__col_write(chc_io *io, const chc_column *c, const chc_type *t, chc_err *err)
|
|
3240
|
+
{
|
|
3241
|
+
int rc;
|
|
3242
|
+
size_t es = chc_type_elem_size(t);
|
|
3243
|
+
if (es) {
|
|
3244
|
+
if (c->layout != CHC_COL_FIXED) return chc__col_write_mismatch(err, t);
|
|
3245
|
+
if (c->n_rows)
|
|
3246
|
+
return chc__write_bytes(io, c->fixed.data, c->n_rows * es, err);
|
|
3269
3247
|
return CHC_OK;
|
|
3248
|
+
}
|
|
3270
3249
|
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3250
|
+
switch (t->kind) {
|
|
3251
|
+
case CHC_STRING:
|
|
3252
|
+
case CHC_JSON:
|
|
3253
|
+
case CHC_OBJECT:
|
|
3254
|
+
if (c->layout != CHC_COL_STRING) return chc__col_write_mismatch(err, t);
|
|
3255
|
+
return chc__write_string_body(io, c->str.offsets, c->str.data,
|
|
3256
|
+
c->n_rows, err);
|
|
3257
|
+
|
|
3258
|
+
case CHC_NULLABLE:
|
|
3259
|
+
if (c->layout != CHC_COL_NULLABLE || t->n_children != 1)
|
|
3260
|
+
return chc__col_write_mismatch(err, t);
|
|
3261
|
+
if (c->n_rows
|
|
3262
|
+
&& (rc = chc__write_bytes(io, c->nullable.null_map, c->n_rows, err)))
|
|
3278
3263
|
return rc;
|
|
3279
|
-
|
|
3280
|
-
return chc__write_bytes(io, e->fixed.data,
|
|
3281
|
-
e->inner_n * e->fixed.elem_size, err);
|
|
3282
|
-
return CHC_OK;
|
|
3264
|
+
return chc__col_write(io, c->nullable.inner, t->children[0], err);
|
|
3283
3265
|
|
|
3284
|
-
case
|
|
3285
|
-
if (
|
|
3266
|
+
case CHC_ARRAY:
|
|
3267
|
+
if (c->layout != CHC_COL_ARRAY || t->n_children != 1)
|
|
3268
|
+
return chc__col_write_mismatch(err, t);
|
|
3269
|
+
if ((rc = chc__write_u64_le_array(io, c->array.offsets, c->n_rows, err)))
|
|
3270
|
+
return rc;
|
|
3271
|
+
return chc__col_write(io, c->array.values, t->children[0], err);
|
|
3272
|
+
|
|
3273
|
+
case CHC_MAP:
|
|
3274
|
+
case CHC_NESTED: {
|
|
3275
|
+
size_t arity = t->kind == CHC_MAP ? 2 : t->n_children;
|
|
3276
|
+
if (c->layout != CHC_COL_ARRAY || !arity || t->n_children != arity)
|
|
3277
|
+
return chc__col_write_mismatch(err, t);
|
|
3278
|
+
if ((rc = chc__write_u64_le_array(io, c->array.offsets, c->n_rows, err)))
|
|
3286
3279
|
return rc;
|
|
3287
|
-
|
|
3288
|
-
|
|
3280
|
+
const chc_column *tup = c->array.values;
|
|
3281
|
+
if (!tup || tup->layout != CHC_COL_TUPLE || tup->tuple.arity != arity)
|
|
3282
|
+
return chc__col_write_mismatch(err, t);
|
|
3283
|
+
for (size_t i = 0; i < arity; i++)
|
|
3284
|
+
if ((rc = chc__col_write(io, tup->tuple.children[i], t->children[i], err)))
|
|
3285
|
+
return rc;
|
|
3286
|
+
return CHC_OK;
|
|
3287
|
+
}
|
|
3289
3288
|
|
|
3290
|
-
case
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3289
|
+
case CHC_TUPLE:
|
|
3290
|
+
if (c->layout != CHC_COL_TUPLE || c->tuple.arity != t->n_children)
|
|
3291
|
+
return chc__col_write_mismatch(err, t);
|
|
3292
|
+
for (size_t i = 0; i < t->n_children; i++)
|
|
3293
|
+
if ((rc = chc__col_write(io, c->tuple.children[i], t->children[i], err)))
|
|
3294
3294
|
return rc;
|
|
3295
|
-
}
|
|
3296
|
-
if (e->inner_n && e->fixed.elem_size)
|
|
3297
|
-
return chc__write_bytes(io, e->fixed.data,
|
|
3298
|
-
e->inner_n * e->fixed.elem_size, err);
|
|
3299
3295
|
return CHC_OK;
|
|
3300
3296
|
|
|
3301
|
-
case
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3297
|
+
case CHC_QBIT: {
|
|
3298
|
+
size_t bits = chc_type_qbit_element_size(t);
|
|
3299
|
+
if (!bits || t->n_children != 1
|
|
3300
|
+
|| c->layout != CHC_COL_TUPLE || c->tuple.arity != bits)
|
|
3301
|
+
return chc__col_write_mismatch(err, t);
|
|
3302
|
+
size_t bytes_per_plane = (chc_type_qbit_dimension(t) + 7) / 8;
|
|
3303
|
+
for (size_t i = 0; i < bits; i++) {
|
|
3304
|
+
const chc_column *pl = c->tuple.children[i];
|
|
3305
|
+
if (pl->layout != CHC_COL_FIXED) return chc__col_write_mismatch(err, t);
|
|
3306
|
+
if (c->n_rows && (rc = chc__write_bytes(io, pl->fixed.data,
|
|
3307
|
+
c->n_rows * bytes_per_plane, err)))
|
|
3305
3308
|
return rc;
|
|
3306
3309
|
}
|
|
3307
|
-
return
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
case
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3310
|
+
return CHC_OK;
|
|
3311
|
+
}
|
|
3312
|
+
|
|
3313
|
+
case CHC_LOW_CARDINALITY: {
|
|
3314
|
+
if (c->layout != CHC_COL_LOW_CARDINALITY || t->n_children != 1)
|
|
3315
|
+
return chc__col_write_mismatch(err, t);
|
|
3316
|
+
/* Empty LC, including one under empty Array, has no body */
|
|
3317
|
+
if (c->n_rows == 0) return CHC_OK;
|
|
3318
|
+
const chc_type *inner = t->children[0];
|
|
3319
|
+
const chc_type *dict_type = (inner->kind == CHC_NULLABLE
|
|
3320
|
+
&& inner->n_children == 1)
|
|
3321
|
+
? inner->children[0] : inner;
|
|
3322
|
+
/* Reader wraps LC(Nullable) dict in Nullable for caller dispatch. Wire
|
|
3323
|
+
* body contains inner-typed strings with null sentinel at slot 0.
|
|
3324
|
+
* Unwrap before writing */
|
|
3325
|
+
const chc_column *dictc = c->lc.dict;
|
|
3326
|
+
if (dictc && dictc->layout == CHC_COL_NULLABLE)
|
|
3327
|
+
dictc = dictc->nullable.inner;
|
|
3328
|
+
uint64_t flags;
|
|
3329
|
+
switch (c->lc.key_size) {
|
|
3330
|
+
case 1: flags = 0; break;
|
|
3331
|
+
case 2: flags = 1; break;
|
|
3332
|
+
case 4: flags = 2; break;
|
|
3333
|
+
case 8: flags = 3; break;
|
|
3334
|
+
default: return chc__err_set(err, CHC_ERR_USAGE,
|
|
3335
|
+
"LowCardinality: bad key_size %d", c->lc.key_size);
|
|
3317
3336
|
}
|
|
3318
3337
|
flags |= CHC__LC_HAS_ADDITIONAL_KEYS;
|
|
3319
3338
|
flags |= CHC__LC_NEED_UPDATE_DICT;
|
|
3320
3339
|
if ((rc = chc__write_u64_le(io, flags, err))) return rc;
|
|
3321
|
-
if ((rc = chc__write_u64_le(io, (uint64_t)
|
|
3322
|
-
if ((rc =
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3340
|
+
if ((rc = chc__write_u64_le(io, (uint64_t) c->lc.dict_n, err))) return rc;
|
|
3341
|
+
if ((rc = chc__col_write(io, dictc, dict_type, err))) return rc;
|
|
3342
|
+
if ((rc = chc__write_u64_le(io, (uint64_t) c->n_rows, err))) return rc;
|
|
3343
|
+
return chc__write_keys_array(io, c->lc.keys, c->n_rows,
|
|
3344
|
+
c->lc.key_size, err);
|
|
3345
|
+
}
|
|
3346
|
+
|
|
3347
|
+
case CHC_SIMPLE_AGGREGATE_FUNCTION:
|
|
3348
|
+
if (t->n_children < 1) return chc__col_write_mismatch(err, t);
|
|
3349
|
+
return chc__col_write(io, c, t->children[0], err);
|
|
3350
|
+
|
|
3351
|
+
case CHC_POINT:
|
|
3352
|
+
case CHC_RING:
|
|
3353
|
+
case CHC_LINE_STRING:
|
|
3354
|
+
case CHC_POLYGON:
|
|
3355
|
+
case CHC_MULTI_LINE_STRING:
|
|
3356
|
+
case CHC_MULTI_POLYGON:
|
|
3357
|
+
return chc__col_write(io, c, chc__geo_alias(t), err);
|
|
3358
|
+
|
|
3359
|
+
case CHC_NOTHING:
|
|
3360
|
+
case CHC_VOID: {
|
|
3361
|
+
uint8_t zeros[256] = {};
|
|
3362
|
+
size_t left = c->n_rows;
|
|
3363
|
+
while (left) {
|
|
3364
|
+
size_t take = left < sizeof zeros ? left : sizeof zeros;
|
|
3365
|
+
if ((rc = chc__write_bytes(io, zeros, take, err))) return rc;
|
|
3366
|
+
left -= take;
|
|
3367
|
+
}
|
|
3368
|
+
return CHC_OK;
|
|
3327
3369
|
}
|
|
3370
|
+
|
|
3371
|
+
default:
|
|
3372
|
+
return chc__col_write_mismatch(err, t);
|
|
3328
3373
|
}
|
|
3329
|
-
return chc__err_set(err, CHC_ERR_USAGE, "unknown builder kind %d", e->kind);
|
|
3330
3374
|
}
|
|
3331
3375
|
|
|
3332
3376
|
int
|
|
3333
|
-
|
|
3334
|
-
|
|
3377
|
+
chc_block_write_cols(chc_io *io, const chc_block_col *cols,
|
|
3378
|
+
size_t n_cols, size_t n_rows,
|
|
3379
|
+
const chc_block_opts *opts, chc_err *err)
|
|
3335
3380
|
{
|
|
3336
3381
|
chc_block_opts def = {};
|
|
3337
3382
|
if (!opts) opts = &def;
|
|
@@ -3341,28 +3386,30 @@ chc_block_write(chc_io *io, const chc_block_builder *bb,
|
|
|
3341
3386
|
if (rc != CHC_OK) return rc;
|
|
3342
3387
|
}
|
|
3343
3388
|
|
|
3344
|
-
|
|
3345
|
-
int rc = chc__write_varuint(io, (uint64_t) bb->n_cols, err);
|
|
3389
|
+
int rc = chc__write_varuint(io, (uint64_t) n_cols, err);
|
|
3346
3390
|
if (rc != CHC_OK) return rc;
|
|
3347
3391
|
rc = chc__write_varuint(io, (uint64_t) n_rows, err);
|
|
3348
3392
|
if (rc != CHC_OK) return rc;
|
|
3349
3393
|
|
|
3350
|
-
for (size_t i = 0; i <
|
|
3351
|
-
const
|
|
3394
|
+
for (size_t i = 0; i < n_cols; i++) {
|
|
3395
|
+
const chc_block_col *e = &cols[i];
|
|
3396
|
+
if (!e->type || !e->col)
|
|
3397
|
+
return chc__err_set(err, CHC_ERR_USAGE,
|
|
3398
|
+
"block_write: column %zu has NULL type or tree", i);
|
|
3399
|
+
if (e->col->n_rows != n_rows)
|
|
3400
|
+
return chc__err_set(err, CHC_ERR_USAGE,
|
|
3401
|
+
"block_write: column %zu row count %zu != block %zu",
|
|
3402
|
+
i, e->col->n_rows, n_rows);
|
|
3403
|
+
|
|
3352
3404
|
rc = chc__write_string(io, e->name, e->name_len, err);
|
|
3353
3405
|
if (rc != CHC_OK) return rc;
|
|
3354
3406
|
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
if (need >= sizeof tbuf)
|
|
3362
|
-
return chc__err_set(err, CHC_ERR_USAGE,
|
|
3363
|
-
"type name too long for inline buffer");
|
|
3364
|
-
rc = chc__write_string(io, tbuf, need, err);
|
|
3365
|
-
}
|
|
3407
|
+
char tbuf[256];
|
|
3408
|
+
size_t need = chc_type_format(e->type, tbuf, sizeof tbuf);
|
|
3409
|
+
if (need >= sizeof tbuf)
|
|
3410
|
+
return chc__err_set(err, CHC_ERR_USAGE,
|
|
3411
|
+
"type name too long for inline buffer");
|
|
3412
|
+
rc = chc__write_string(io, tbuf, need, err);
|
|
3366
3413
|
if (rc != CHC_OK) return rc;
|
|
3367
3414
|
|
|
3368
3415
|
if (opts->has_custom_serialization) {
|
|
@@ -3371,20 +3418,24 @@ chc_block_write(chc_io *io, const chc_block_builder *bb,
|
|
|
3371
3418
|
if (rc != CHC_OK) return rc;
|
|
3372
3419
|
}
|
|
3373
3420
|
|
|
3374
|
-
if (
|
|
3375
|
-
|
|
3376
|
-
/* LC and JSON prefixes use version 1. */
|
|
3377
|
-
if (e->kind == CHC__BLD_LC_STRING || e->kind == CHC__BLD_JSON_STRING) {
|
|
3378
|
-
rc = chc__write_u64_le(io, 1, err);
|
|
3379
|
-
if (rc != CHC_OK) return rc;
|
|
3380
|
-
}
|
|
3421
|
+
if (n_rows == 0) continue;
|
|
3381
3422
|
|
|
3382
|
-
rc =
|
|
3423
|
+
rc = chc__col_write_prefix(io, e->type, err);
|
|
3424
|
+
if (rc != CHC_OK) return rc;
|
|
3425
|
+
rc = chc__col_write(io, e->col, e->type, err);
|
|
3383
3426
|
if (rc != CHC_OK) return rc;
|
|
3384
3427
|
}
|
|
3385
3428
|
return CHC_OK;
|
|
3386
3429
|
}
|
|
3387
3430
|
|
|
3431
|
+
int
|
|
3432
|
+
chc_block_write(chc_io *io, const chc_block_builder *bb,
|
|
3433
|
+
const chc_block_opts *opts, chc_err *err)
|
|
3434
|
+
{
|
|
3435
|
+
return chc_block_write_cols(io, bb->cols, bb->n_cols,
|
|
3436
|
+
bb->n_rows, opts, err);
|
|
3437
|
+
}
|
|
3438
|
+
|
|
3388
3439
|
#endif /* CHC_IMPLEMENTATION */
|
|
3389
3440
|
|
|
3390
3441
|
#ifdef __cplusplus
|