tranfi 0.1.2 → 0.2.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.
- package/LICENSE +177 -0
- package/NOTICE +8 -0
- package/README.md +443 -51
- package/app/assets/{index-pDFMluyz.js → index-BIAIKnrp.js} +1 -1
- package/app/index.html +1 -1
- package/binding.gyp +55 -3
- package/csrc/arena.c +7 -5
- package/csrc/batch.c +818 -71
- package/csrc/buffer.c +84 -8
- package/csrc/cJSON.c +262 -19
- package/csrc/cJSON.h +17 -1
- package/csrc/codec_csv.c +1074 -181
- package/csrc/codec_jsonl.c +830 -118
- package/csrc/codec_table.c +108 -78
- package/csrc/codec_text.c +286 -68
- package/csrc/compiler.c +31 -3
- package/csrc/config.h +21 -0
- package/csrc/dsl.c +4722 -485
- package/csrc/expr.c +363 -55
- package/csrc/expr.h +2 -0
- package/csrc/internal.h +316 -27
- package/csrc/ir.c +65 -18
- package/csrc/ir.h +41 -0
- package/csrc/ir_schema.c +20 -5
- package/csrc/ir_serialize.c +68 -6
- package/csrc/ir_sql.c +796 -185
- package/csrc/ir_validate.c +462 -6
- package/csrc/json_path.c +210 -0
- package/csrc/main.c +879 -30
- package/csrc/memory_estimate.c +477 -0
- package/csrc/op_acf.c +171 -21
- package/csrc/op_across.c +477 -0
- package/csrc/op_anomaly.c +167 -32
- package/csrc/op_assert.c +761 -0
- package/csrc/op_bin.c +168 -29
- package/csrc/op_cast.c +383 -55
- package/csrc/op_clip.c +30 -19
- package/csrc/op_date_trunc.c +208 -34
- package/csrc/op_datetime.c +259 -77
- package/csrc/op_derive.c +65 -97
- package/csrc/op_diff.c +146 -30
- package/csrc/op_ewma.c +149 -30
- package/csrc/op_explode.c +124 -26
- package/csrc/op_fill_down.c +125 -53
- package/csrc/op_fill_null.c +176 -31
- package/csrc/op_filter.c +89 -40
- package/csrc/op_frequency.c +571 -43
- package/csrc/op_grep.c +36 -18
- package/csrc/op_group_agg.c +1790 -119
- package/csrc/op_hash.c +48 -15
- package/csrc/op_head.c +21 -86
- package/csrc/op_interpolate.c +268 -62
- package/csrc/op_join.c +2700 -182
- package/csrc/op_json_extract.c +227 -0
- package/csrc/op_json_filter.c +384 -0
- package/csrc/op_json_flatten.c +293 -0
- package/csrc/op_json_schema.c +503 -0
- package/csrc/op_label_encode.c +328 -53
- package/csrc/op_lag.c +181 -0
- package/csrc/op_lead.c +141 -89
- package/csrc/op_normalize.c +363 -79
- package/csrc/op_onehot.c +345 -73
- package/csrc/op_pivot.c +1546 -162
- package/csrc/op_quarantine.c +189 -0
- package/csrc/op_registry.c +2062 -166
- package/csrc/op_rename.c +41 -50
- package/csrc/op_replace.c +270 -118
- package/csrc/op_rleid.c +297 -0
- package/csrc/op_rowid.c +559 -0
- package/csrc/op_sample.c +80 -23
- package/csrc/op_schema.c +1341 -0
- package/csrc/op_schema_infer.c +252 -0
- package/csrc/op_select.c +265 -65
- package/csrc/op_set.c +3449 -0
- package/csrc/op_skip.c +30 -87
- package/csrc/op_sort.c +670 -124
- package/csrc/op_source_name.c +120 -0
- package/csrc/op_split.c +65 -28
- package/csrc/op_split_data.c +41 -9
- package/csrc/op_stack.c +178 -222
- package/csrc/op_stats.c +206 -110
- package/csrc/op_step.c +217 -55
- package/csrc/op_tail.c +21 -12
- package/csrc/op_tee.c +338 -0
- package/csrc/op_top.c +260 -53
- package/csrc/op_trim.c +48 -19
- package/csrc/op_unique.c +1193 -150
- package/csrc/op_unpivot.c +100 -66
- package/csrc/op_validate.c +601 -24
- package/csrc/op_window.c +492 -51
- package/csrc/path_policy.c +85 -0
- package/csrc/pipeline.c +872 -99
- package/csrc/recipes.c +3 -1
- package/csrc/report.c +73 -30
- package/csrc/selector.c +1097 -0
- package/csrc/size_utils.c +352 -0
- package/csrc/spill.c +317 -0
- package/csrc/spill.h +21 -0
- package/csrc/tranfi.h +169 -1
- package/csrc/transform.h +209 -0
- package/csrc/transform_api.c +2237 -0
- package/csrc/transform_categorical.c +923 -0
- package/csrc/transform_internal.h +472 -0
- package/csrc/transform_json.c +3812 -0
- package/csrc/transform_numeric.c +1966 -0
- package/csrc/transform_sha256.c +154 -0
- package/csrc/transform_wasm.h +162 -0
- package/csrc/transform_wasm_api.c +1373 -0
- package/csrc/wasm_api.c +70 -9
- package/napi_api.c +219 -11
- package/napi_transform.c +1648 -0
- package/napi_transform.h +8 -0
- package/package.json +27 -11
- package/scripts/install-native.js +76 -0
- package/scripts/prepack.js +64 -0
- package/scripts/sync-csrc.js +23 -0
- package/src/cli.js +81 -41
- package/src/engines/duckdb.js +45 -12
- package/src/index.js +661 -42
- package/src/memory_policy.js +411 -0
- package/src/native.js +1 -5
- package/src/pipeline.js +454 -31
- package/src/recipe_json.js +80 -0
- package/src/server.js +10 -8
- package/src/transform.js +403 -0
- package/src/transform_error.js +10 -0
- package/src/wasm.js +6 -4
- package/wasm/index.js +498 -10
- package/wasm/tranfi_core.js +0 -0
- package/wasm/transform.js +1156 -0
- package/wasm/worker.js +786 -0
- package/csrc/plan.c +0 -206
package/csrc/op_set.c
ADDED
|
@@ -0,0 +1,3449 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* op_set.c -- Left-streaming row-set membership operations.
|
|
3
|
+
*
|
|
4
|
+
* intersect file.csv [columns=...] keeps distinct left rows whose key exists
|
|
5
|
+
* in the lookup file. setdiff file.csv [columns=...] keeps distinct left rows
|
|
6
|
+
* whose key does not exist in the lookup file. union file.csv keeps distinct
|
|
7
|
+
* rows across input and file under an emitted-key cap. union-all file.csv
|
|
8
|
+
* appends all rows and streams the file at finish.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#include "internal.h"
|
|
12
|
+
#include "spill.h"
|
|
13
|
+
#include "cJSON.h"
|
|
14
|
+
#include <stdlib.h>
|
|
15
|
+
#include <string.h>
|
|
16
|
+
#include <stdio.h>
|
|
17
|
+
#include <limits.h>
|
|
18
|
+
#include <stdarg.h>
|
|
19
|
+
#include <errno.h>
|
|
20
|
+
#include <stdint.h>
|
|
21
|
+
#include <unistd.h>
|
|
22
|
+
|
|
23
|
+
#define SET_DEFAULT_RUN_ROWS 8192
|
|
24
|
+
#define SET_DEFAULT_OUTPUT_ROWS 1024
|
|
25
|
+
#define SET_MIN_RUN_ROWS 16
|
|
26
|
+
|
|
27
|
+
typedef struct {
|
|
28
|
+
char *key;
|
|
29
|
+
size_t count;
|
|
30
|
+
} set_bucket;
|
|
31
|
+
|
|
32
|
+
typedef union {
|
|
33
|
+
uint8_t b;
|
|
34
|
+
int64_t i64;
|
|
35
|
+
double f64;
|
|
36
|
+
int32_t date;
|
|
37
|
+
char *str;
|
|
38
|
+
} set_key_data;
|
|
39
|
+
|
|
40
|
+
typedef struct {
|
|
41
|
+
int valid;
|
|
42
|
+
int is_null;
|
|
43
|
+
tf_type type;
|
|
44
|
+
set_key_data v;
|
|
45
|
+
} set_key_value;
|
|
46
|
+
|
|
47
|
+
typedef struct {
|
|
48
|
+
set_key_value *values;
|
|
49
|
+
size_t n;
|
|
50
|
+
int valid;
|
|
51
|
+
} set_key_tuple;
|
|
52
|
+
|
|
53
|
+
typedef struct {
|
|
54
|
+
uint64_t ordinal;
|
|
55
|
+
uint8_t *nulls;
|
|
56
|
+
tf_owned_cell_value *cells;
|
|
57
|
+
} set_spill_row;
|
|
58
|
+
|
|
59
|
+
typedef struct {
|
|
60
|
+
FILE *file;
|
|
61
|
+
set_spill_row row;
|
|
62
|
+
int has_row;
|
|
63
|
+
int done;
|
|
64
|
+
int kind; /* 0=left, 1=lookup, 2=output */
|
|
65
|
+
} set_spill_reader;
|
|
66
|
+
|
|
67
|
+
typedef struct {
|
|
68
|
+
set_bucket *buckets;
|
|
69
|
+
size_t n_buckets;
|
|
70
|
+
size_t count;
|
|
71
|
+
size_t key_bytes;
|
|
72
|
+
} set_hash_map;
|
|
73
|
+
|
|
74
|
+
typedef struct {
|
|
75
|
+
char *file;
|
|
76
|
+
char *validated_file;
|
|
77
|
+
char **columns;
|
|
78
|
+
size_t n_columns;
|
|
79
|
+
size_t max_lookup_rows;
|
|
80
|
+
size_t max_lookup_keys;
|
|
81
|
+
size_t max_lookup_bytes;
|
|
82
|
+
size_t max_output_keys;
|
|
83
|
+
size_t max_state_bytes;
|
|
84
|
+
int mode; /* 0=intersect, 1=setdiff, 2=union, 3=union-all, 4=intersect-all, 5=setdiff-all */
|
|
85
|
+
int sorted;
|
|
86
|
+
int loaded;
|
|
87
|
+
int use_spill;
|
|
88
|
+
|
|
89
|
+
char *spill_dir;
|
|
90
|
+
tf_spill_session *spill;
|
|
91
|
+
size_t spill_memory_bytes;
|
|
92
|
+
size_t configured_run_rows;
|
|
93
|
+
size_t run_rows;
|
|
94
|
+
size_t output_batch_rows;
|
|
95
|
+
|
|
96
|
+
int spill_has_schema;
|
|
97
|
+
int spill_lookup_cols_ready;
|
|
98
|
+
char **spill_schema_names;
|
|
99
|
+
tf_type *spill_schema_types;
|
|
100
|
+
tf_type *spill_key_types;
|
|
101
|
+
size_t spill_n_cols;
|
|
102
|
+
|
|
103
|
+
tf_batch *spill_left_buf;
|
|
104
|
+
tf_batch *spill_lookup_buf;
|
|
105
|
+
tf_batch *spill_out_buf;
|
|
106
|
+
uint64_t *spill_left_ordinals;
|
|
107
|
+
uint64_t *spill_lookup_ordinals;
|
|
108
|
+
uint64_t *spill_out_ordinals;
|
|
109
|
+
size_t spill_left_ordinal_cap;
|
|
110
|
+
size_t spill_lookup_ordinal_cap;
|
|
111
|
+
size_t spill_out_ordinal_cap;
|
|
112
|
+
uint64_t spill_next_left_ordinal;
|
|
113
|
+
uint64_t spill_next_lookup_ordinal;
|
|
114
|
+
|
|
115
|
+
char **spill_left_run_paths;
|
|
116
|
+
char **spill_lookup_run_paths;
|
|
117
|
+
char **spill_out_run_paths;
|
|
118
|
+
size_t spill_n_left_runs;
|
|
119
|
+
size_t spill_n_lookup_runs;
|
|
120
|
+
size_t spill_n_out_runs;
|
|
121
|
+
size_t spill_cap_left_runs;
|
|
122
|
+
size_t spill_cap_lookup_runs;
|
|
123
|
+
size_t spill_cap_out_runs;
|
|
124
|
+
size_t spill_left_run_seq;
|
|
125
|
+
size_t spill_lookup_run_seq;
|
|
126
|
+
size_t spill_out_run_seq;
|
|
127
|
+
|
|
128
|
+
set_spill_reader *spill_left_readers;
|
|
129
|
+
set_spill_reader *spill_lookup_readers;
|
|
130
|
+
set_spill_reader *spill_out_readers;
|
|
131
|
+
size_t spill_n_left_readers;
|
|
132
|
+
size_t spill_n_lookup_readers;
|
|
133
|
+
size_t spill_n_out_readers;
|
|
134
|
+
|
|
135
|
+
int spill_lookup_loaded;
|
|
136
|
+
int spill_key_merge_done;
|
|
137
|
+
int spill_output_merge_started;
|
|
138
|
+
int spill_output_merge_done;
|
|
139
|
+
char *spill_last_lookup_key;
|
|
140
|
+
char *spill_last_left_key;
|
|
141
|
+
|
|
142
|
+
size_t spill_bytes;
|
|
143
|
+
size_t spill_runs;
|
|
144
|
+
size_t spill_output_batches;
|
|
145
|
+
size_t spill_output_rows;
|
|
146
|
+
size_t spill_distinct_rows;
|
|
147
|
+
size_t spill_kept_rows;
|
|
148
|
+
size_t spill_lookup_rows;
|
|
149
|
+
size_t spill_lookup_keys;
|
|
150
|
+
size_t spill_lookup_key_bytes;
|
|
151
|
+
|
|
152
|
+
int union_schema_ready;
|
|
153
|
+
char **union_col_names;
|
|
154
|
+
tf_type *union_col_types;
|
|
155
|
+
size_t union_n_cols;
|
|
156
|
+
int *union_right_cols;
|
|
157
|
+
size_t union_file_rows;
|
|
158
|
+
tf_batch *union_sorted_right_row;
|
|
159
|
+
|
|
160
|
+
int *left_cols;
|
|
161
|
+
int *right_cols;
|
|
162
|
+
size_t n_key_cols;
|
|
163
|
+
char **key_names;
|
|
164
|
+
|
|
165
|
+
set_hash_map lookup;
|
|
166
|
+
set_hash_map emitted;
|
|
167
|
+
|
|
168
|
+
FILE *sorted_file;
|
|
169
|
+
tf_decoder *sorted_decoder;
|
|
170
|
+
tf_batch **sorted_batches;
|
|
171
|
+
size_t sorted_n_batches;
|
|
172
|
+
size_t sorted_batch_index;
|
|
173
|
+
tf_batch *sorted_current;
|
|
174
|
+
size_t sorted_row;
|
|
175
|
+
int sorted_flushed;
|
|
176
|
+
int sorted_have_row;
|
|
177
|
+
int sorted_exhausted;
|
|
178
|
+
int sorted_right_cols_ready;
|
|
179
|
+
set_key_tuple prev_lookup_key;
|
|
180
|
+
set_key_tuple current_lookup_key;
|
|
181
|
+
set_key_tuple prev_left_key;
|
|
182
|
+
size_t sorted_current_lookup_count;
|
|
183
|
+
size_t sorted_left_run_count;
|
|
184
|
+
} set_state;
|
|
185
|
+
|
|
186
|
+
static size_t set_map_retained_bytes(const set_hash_map *m);
|
|
187
|
+
static size_t set_retained_state_bytes(const set_state *st);
|
|
188
|
+
static int set_check_state_bytes(const set_state *st, tf_side_channels *side);
|
|
189
|
+
static int set_copy_cell(tf_batch *dst, size_t dr, size_t dc,
|
|
190
|
+
const tf_batch *src, size_t sr, int sc,
|
|
191
|
+
tf_side_channels *side, const char *op);
|
|
192
|
+
static int set_spill_produce_union_output_runs(set_state *st, tf_side_channels *side);
|
|
193
|
+
static int union_next_decoded_batch(set_state *st, tf_batch **out,
|
|
194
|
+
tf_side_channels *side);
|
|
195
|
+
|
|
196
|
+
typedef struct {
|
|
197
|
+
char *data;
|
|
198
|
+
size_t len;
|
|
199
|
+
size_t cap;
|
|
200
|
+
} keybuf;
|
|
201
|
+
|
|
202
|
+
static void keybuf_free(keybuf *b) {
|
|
203
|
+
free(b->data);
|
|
204
|
+
b->data = NULL;
|
|
205
|
+
b->len = b->cap = 0;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
static int keybuf_reserve(keybuf *b, size_t extra) {
|
|
209
|
+
size_t need = 0;
|
|
210
|
+
if (tf_size_add(b->len, extra, &need) != TF_OK ||
|
|
211
|
+
tf_size_add(need, 1, &need) != TF_OK) {
|
|
212
|
+
return TF_ERROR;
|
|
213
|
+
}
|
|
214
|
+
if (need <= b->cap) return TF_OK;
|
|
215
|
+
size_t cap = 0;
|
|
216
|
+
if (tf_size_grow_pow2(b->cap, need, 128, &cap) != TF_OK) return TF_ERROR;
|
|
217
|
+
char *tmp = tf_reallocarray_checked(b->data, cap, sizeof(char));
|
|
218
|
+
if (!tmp) return TF_ERROR;
|
|
219
|
+
b->data = tmp;
|
|
220
|
+
b->cap = cap;
|
|
221
|
+
return TF_OK;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
static int keybuf_append(keybuf *b, const char *s, size_t n) {
|
|
225
|
+
if (keybuf_reserve(b, n) != TF_OK) return TF_ERROR;
|
|
226
|
+
memcpy(b->data + b->len, s, n);
|
|
227
|
+
b->len += n;
|
|
228
|
+
b->data[b->len] = '\0';
|
|
229
|
+
return TF_OK;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
static int keybuf_appendf(keybuf *b, const char *fmt, ...) {
|
|
233
|
+
char tmp[128];
|
|
234
|
+
va_list ap;
|
|
235
|
+
va_start(ap, fmt);
|
|
236
|
+
int n = vsnprintf(tmp, sizeof(tmp), fmt, ap);
|
|
237
|
+
va_end(ap);
|
|
238
|
+
if (n < 0) return TF_ERROR;
|
|
239
|
+
if ((size_t)n < sizeof(tmp)) return keybuf_append(b, tmp, (size_t)n);
|
|
240
|
+
|
|
241
|
+
char *big = malloc((size_t)n + 1);
|
|
242
|
+
if (!big) return TF_ERROR;
|
|
243
|
+
va_start(ap, fmt);
|
|
244
|
+
int n2 = vsnprintf(big, (size_t)n + 1, fmt, ap);
|
|
245
|
+
va_end(ap);
|
|
246
|
+
if (n2 < 0) { free(big); return TF_ERROR; }
|
|
247
|
+
int rc = keybuf_append(b, big, (size_t)n2);
|
|
248
|
+
free(big);
|
|
249
|
+
return rc;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
static int keybuf_append_field(keybuf *b, char tag, const char *value) {
|
|
253
|
+
size_t len = value ? strlen(value) : 0;
|
|
254
|
+
if (keybuf_appendf(b, "%c%zu:", tag, len) != TF_OK) return TF_ERROR;
|
|
255
|
+
return len ? keybuf_append(b, value, len) : TF_OK;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
static uint64_t set_hash(const char *s) {
|
|
259
|
+
uint64_t h = 14695981039346656037ULL;
|
|
260
|
+
while (*s) { h ^= (uint8_t)*s++; h *= 1099511628211ULL; }
|
|
261
|
+
return h;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
static void set_key_value_clear(set_key_value *v) {
|
|
265
|
+
if (!v) return;
|
|
266
|
+
if (v->valid && !v->is_null && v->type == TF_TYPE_STRING) free(v->v.str);
|
|
267
|
+
memset(v, 0, sizeof(*v));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
static int set_key_value_set(set_key_value *dst, const tf_batch *b, size_t row, int col) {
|
|
271
|
+
if (!dst || !b || col < 0) return TF_ERROR;
|
|
272
|
+
set_key_value_clear(dst);
|
|
273
|
+
dst->valid = 1;
|
|
274
|
+
dst->type = b->col_types[col];
|
|
275
|
+
dst->is_null = tf_batch_is_null(b, row, (size_t)col) ? 1 : 0;
|
|
276
|
+
if (dst->is_null) return TF_OK;
|
|
277
|
+
|
|
278
|
+
switch (dst->type) {
|
|
279
|
+
case TF_TYPE_BOOL:
|
|
280
|
+
dst->v.b = tf_batch_get_bool(b, row, (size_t)col) ? 1 : 0;
|
|
281
|
+
return TF_OK;
|
|
282
|
+
case TF_TYPE_INT64:
|
|
283
|
+
dst->v.i64 = tf_batch_get_int64(b, row, (size_t)col);
|
|
284
|
+
return TF_OK;
|
|
285
|
+
case TF_TYPE_FLOAT64:
|
|
286
|
+
dst->v.f64 = tf_batch_get_float64(b, row, (size_t)col);
|
|
287
|
+
return TF_OK;
|
|
288
|
+
case TF_TYPE_STRING: {
|
|
289
|
+
const char *s = tf_batch_get_string(b, row, (size_t)col);
|
|
290
|
+
dst->v.str = strdup(s ? s : "");
|
|
291
|
+
return dst->v.str ? TF_OK : TF_ERROR;
|
|
292
|
+
}
|
|
293
|
+
case TF_TYPE_DATE:
|
|
294
|
+
dst->v.date = tf_batch_get_date(b, row, (size_t)col);
|
|
295
|
+
return TF_OK;
|
|
296
|
+
case TF_TYPE_TIMESTAMP:
|
|
297
|
+
dst->v.i64 = tf_batch_get_timestamp(b, row, (size_t)col);
|
|
298
|
+
return TF_OK;
|
|
299
|
+
default:
|
|
300
|
+
dst->is_null = 1;
|
|
301
|
+
return TF_OK;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
static int set_key_value_copy(set_key_value *dst, const set_key_value *src) {
|
|
306
|
+
if (!dst || !src || !src->valid) return TF_ERROR;
|
|
307
|
+
set_key_value_clear(dst);
|
|
308
|
+
*dst = *src;
|
|
309
|
+
if (!src->is_null && src->type == TF_TYPE_STRING) {
|
|
310
|
+
dst->v.str = strdup(src->v.str ? src->v.str : "");
|
|
311
|
+
if (!dst->v.str) { memset(dst, 0, sizeof(*dst)); return TF_ERROR; }
|
|
312
|
+
}
|
|
313
|
+
return TF_OK;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
static int set_key_value_compare(const set_key_value *a, const set_key_value *b, int *cmp) {
|
|
317
|
+
if (!a || !b || !cmp || !a->valid || !b->valid) return TF_ERROR;
|
|
318
|
+
if (a->type != b->type) return TF_ERROR;
|
|
319
|
+
if (a->is_null && b->is_null) { *cmp = 0; return TF_OK; }
|
|
320
|
+
if (a->is_null) { *cmp = 1; return TF_OK; }
|
|
321
|
+
if (b->is_null) { *cmp = -1; return TF_OK; }
|
|
322
|
+
|
|
323
|
+
switch (a->type) {
|
|
324
|
+
case TF_TYPE_BOOL:
|
|
325
|
+
*cmp = (int)a->v.b - (int)b->v.b;
|
|
326
|
+
return TF_OK;
|
|
327
|
+
case TF_TYPE_INT64:
|
|
328
|
+
case TF_TYPE_TIMESTAMP:
|
|
329
|
+
*cmp = (a->v.i64 > b->v.i64) - (a->v.i64 < b->v.i64);
|
|
330
|
+
return TF_OK;
|
|
331
|
+
case TF_TYPE_FLOAT64:
|
|
332
|
+
*cmp = (a->v.f64 > b->v.f64) - (a->v.f64 < b->v.f64);
|
|
333
|
+
return TF_OK;
|
|
334
|
+
case TF_TYPE_STRING:
|
|
335
|
+
*cmp = strcmp(a->v.str ? a->v.str : "", b->v.str ? b->v.str : "");
|
|
336
|
+
return TF_OK;
|
|
337
|
+
case TF_TYPE_DATE:
|
|
338
|
+
*cmp = (a->v.date > b->v.date) - (a->v.date < b->v.date);
|
|
339
|
+
return TF_OK;
|
|
340
|
+
default:
|
|
341
|
+
*cmp = 0;
|
|
342
|
+
return TF_OK;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
static void set_key_tuple_clear(set_key_tuple *t) {
|
|
347
|
+
if (!t) return;
|
|
348
|
+
for (size_t i = 0; i < t->n; i++) set_key_value_clear(&t->values[i]);
|
|
349
|
+
free(t->values);
|
|
350
|
+
memset(t, 0, sizeof(*t));
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
static const char *set_mode_name(int mode) {
|
|
354
|
+
switch (mode) {
|
|
355
|
+
case 0: return "intersect";
|
|
356
|
+
case 1: return "setdiff";
|
|
357
|
+
case 2: return "union";
|
|
358
|
+
case 3: return "union-all";
|
|
359
|
+
case 4: return "intersect-all";
|
|
360
|
+
case 5: return "setdiff-all";
|
|
361
|
+
default: return "set op";
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
static int set_key_tuple_set_from_row(set_key_tuple *dst, const tf_batch *b,
|
|
366
|
+
size_t row, const int *cols, size_t n_cols) {
|
|
367
|
+
if (!dst || !b || (!cols && n_cols > 0)) return TF_ERROR;
|
|
368
|
+
set_key_tuple_clear(dst);
|
|
369
|
+
dst->values = tf_callocarray_checked(n_cols ? n_cols : 1, sizeof(set_key_value));
|
|
370
|
+
if (!dst->values) return TF_ERROR;
|
|
371
|
+
dst->n = n_cols;
|
|
372
|
+
dst->valid = 1;
|
|
373
|
+
for (size_t i = 0; i < n_cols; i++) {
|
|
374
|
+
if (set_key_value_set(&dst->values[i], b, row, cols[i]) != TF_OK) return TF_ERROR;
|
|
375
|
+
}
|
|
376
|
+
return TF_OK;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
static int set_key_tuple_copy(set_key_tuple *dst, const set_key_tuple *src) {
|
|
380
|
+
if (!dst || !src || !src->valid) return TF_ERROR;
|
|
381
|
+
set_key_tuple_clear(dst);
|
|
382
|
+
dst->values = tf_callocarray_checked(src->n ? src->n : 1, sizeof(set_key_value));
|
|
383
|
+
if (!dst->values) return TF_ERROR;
|
|
384
|
+
dst->n = src->n;
|
|
385
|
+
dst->valid = 1;
|
|
386
|
+
for (size_t i = 0; i < src->n; i++) {
|
|
387
|
+
if (set_key_value_copy(&dst->values[i], &src->values[i]) != TF_OK) return TF_ERROR;
|
|
388
|
+
}
|
|
389
|
+
return TF_OK;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
static int set_key_tuple_compare(const set_key_tuple *a, const set_key_tuple *b, int *cmp) {
|
|
393
|
+
if (!a || !b || !cmp || !a->valid || !b->valid || a->n != b->n) return TF_ERROR;
|
|
394
|
+
for (size_t i = 0; i < a->n; i++) {
|
|
395
|
+
int c = 0;
|
|
396
|
+
if (set_key_value_compare(&a->values[i], &b->values[i], &c) != TF_OK) return TF_ERROR;
|
|
397
|
+
if (c != 0) { *cmp = c; return TF_OK; }
|
|
398
|
+
}
|
|
399
|
+
*cmp = 0;
|
|
400
|
+
return TF_OK;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
static int set_map_init(set_hash_map *m, size_t hint) {
|
|
404
|
+
size_t n = 64;
|
|
405
|
+
size_t target = 0;
|
|
406
|
+
if (tf_size_mul(hint, 2, &target) != TF_OK) return TF_ERROR;
|
|
407
|
+
while (n < target) {
|
|
408
|
+
if (n > SIZE_MAX / 2) return TF_ERROR;
|
|
409
|
+
n *= 2;
|
|
410
|
+
}
|
|
411
|
+
m->buckets = tf_callocarray_checked(n, sizeof(set_bucket));
|
|
412
|
+
if (!m->buckets) return TF_ERROR;
|
|
413
|
+
m->n_buckets = n;
|
|
414
|
+
m->count = 0;
|
|
415
|
+
m->key_bytes = 0;
|
|
416
|
+
return TF_OK;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
static void set_map_free(set_hash_map *m) {
|
|
420
|
+
if (!m->buckets) return;
|
|
421
|
+
for (size_t i = 0; i < m->n_buckets; i++) free(m->buckets[i].key);
|
|
422
|
+
free(m->buckets);
|
|
423
|
+
m->buckets = NULL;
|
|
424
|
+
m->n_buckets = 0;
|
|
425
|
+
m->count = 0;
|
|
426
|
+
m->key_bytes = 0;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
static void set_free_string_array(char **items, size_t n) {
|
|
430
|
+
if (!items) return;
|
|
431
|
+
for (size_t i = 0; i < n; i++) free(items[i]);
|
|
432
|
+
free(items);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
static int set_map_rehash(set_hash_map *m) {
|
|
436
|
+
set_hash_map next = {0};
|
|
437
|
+
if (set_map_init(&next, m->n_buckets) != TF_OK) return TF_ERROR;
|
|
438
|
+
for (size_t i = 0; i < m->n_buckets; i++) {
|
|
439
|
+
char *key = m->buckets[i].key;
|
|
440
|
+
if (!key) continue;
|
|
441
|
+
uint64_t h = set_hash(key);
|
|
442
|
+
size_t idx = h & (next.n_buckets - 1);
|
|
443
|
+
while (next.buckets[idx].key)
|
|
444
|
+
idx = (idx + 1) & (next.n_buckets - 1);
|
|
445
|
+
next.buckets[idx] = m->buckets[i];
|
|
446
|
+
next.count++;
|
|
447
|
+
m->buckets[i].key = NULL;
|
|
448
|
+
}
|
|
449
|
+
next.key_bytes = m->key_bytes;
|
|
450
|
+
set_map_free(m);
|
|
451
|
+
*m = next;
|
|
452
|
+
return TF_OK;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
static set_bucket *set_map_find_bucket(const set_hash_map *m, const char *key) {
|
|
456
|
+
if (!m->buckets) return NULL;
|
|
457
|
+
uint64_t h = set_hash(key);
|
|
458
|
+
size_t idx = h & (m->n_buckets - 1);
|
|
459
|
+
while (m->buckets[idx].key) {
|
|
460
|
+
if (strcmp(m->buckets[idx].key, key) == 0) return (set_bucket *)&m->buckets[idx];
|
|
461
|
+
idx = (idx + 1) & (m->n_buckets - 1);
|
|
462
|
+
}
|
|
463
|
+
return NULL;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
static int set_map_contains(const set_hash_map *m, const char *key) {
|
|
467
|
+
return set_map_find_bucket(m, key) != NULL;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
TF_WARN_UNUSED static int set_write_error(tf_side_channels *side, const char *msg) {
|
|
471
|
+
return tf_side_write_error(side, msg);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
TF_WARN_UNUSED static int set_limit_error(tf_side_channels *side, const char *op,
|
|
475
|
+
const char *field, size_t limit, size_t actual) {
|
|
476
|
+
char msg[224];
|
|
477
|
+
snprintf(msg, sizeof(msg), "%s: %s=%zu exceeded (%zu)", op, field, limit, actual);
|
|
478
|
+
return set_write_error(side, msg);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
static int set_map_insert_owned(set_hash_map *m, char *key, size_t max_keys,
|
|
482
|
+
const char *op, const char *field,
|
|
483
|
+
tf_side_channels *side, int *inserted) {
|
|
484
|
+
*inserted = 0;
|
|
485
|
+
if (set_map_contains(m, key)) {
|
|
486
|
+
free(key);
|
|
487
|
+
return TF_OK;
|
|
488
|
+
}
|
|
489
|
+
if (max_keys > 0 && m->count >= max_keys) {
|
|
490
|
+
if (set_limit_error(side, op, field, max_keys, m->count + 1) != TF_OK) {
|
|
491
|
+
free(key);
|
|
492
|
+
return TF_ERROR;
|
|
493
|
+
}
|
|
494
|
+
free(key);
|
|
495
|
+
return TF_ERROR;
|
|
496
|
+
}
|
|
497
|
+
size_t load_count = 0;
|
|
498
|
+
if (tf_size_mul(m->count, 2, &load_count) != TF_OK) {
|
|
499
|
+
free(key);
|
|
500
|
+
return TF_ERROR;
|
|
501
|
+
}
|
|
502
|
+
if (load_count >= m->n_buckets && set_map_rehash(m) != TF_OK) {
|
|
503
|
+
free(key);
|
|
504
|
+
return TF_ERROR;
|
|
505
|
+
}
|
|
506
|
+
uint64_t h = set_hash(key);
|
|
507
|
+
size_t idx = h & (m->n_buckets - 1);
|
|
508
|
+
while (m->buckets[idx].key)
|
|
509
|
+
idx = (idx + 1) & (m->n_buckets - 1);
|
|
510
|
+
size_t key_len = 0;
|
|
511
|
+
size_t new_key_bytes = 0;
|
|
512
|
+
if (tf_size_add(strlen(key), 1, &key_len) != TF_OK ||
|
|
513
|
+
tf_size_add(m->key_bytes, key_len, &new_key_bytes) != TF_OK) {
|
|
514
|
+
free(key);
|
|
515
|
+
return TF_ERROR;
|
|
516
|
+
}
|
|
517
|
+
m->buckets[idx].key = key;
|
|
518
|
+
m->buckets[idx].count = 1;
|
|
519
|
+
m->count++;
|
|
520
|
+
m->key_bytes = new_key_bytes;
|
|
521
|
+
*inserted = 1;
|
|
522
|
+
return TF_OK;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
static int set_map_increment_owned(set_hash_map *m, char *key, size_t max_keys,
|
|
526
|
+
const char *op, const char *field,
|
|
527
|
+
tf_side_channels *side, int *inserted) {
|
|
528
|
+
*inserted = 0;
|
|
529
|
+
set_bucket *bucket = set_map_find_bucket(m, key);
|
|
530
|
+
if (bucket) {
|
|
531
|
+
if (bucket->count == SIZE_MAX) { free(key); return TF_ERROR; }
|
|
532
|
+
bucket->count++;
|
|
533
|
+
free(key);
|
|
534
|
+
return TF_OK;
|
|
535
|
+
}
|
|
536
|
+
return set_map_insert_owned(m, key, max_keys, op, field, side, inserted);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
static int set_map_decrement_if_present(set_hash_map *m, const char *key) {
|
|
540
|
+
set_bucket *bucket = set_map_find_bucket(m, key);
|
|
541
|
+
if (!bucket || bucket->count == 0) return 0;
|
|
542
|
+
bucket->count--;
|
|
543
|
+
return 1;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
static char *format_set_key(const tf_batch *b, size_t row, const int *cols, size_t n_cols) {
|
|
547
|
+
keybuf kb = {0};
|
|
548
|
+
for (size_t i = 0; i < n_cols; i++) {
|
|
549
|
+
int col = cols[i];
|
|
550
|
+
char tmp[80];
|
|
551
|
+
if (tf_batch_is_null(b, row, (size_t)col)) {
|
|
552
|
+
if (keybuf_append_field(&kb, 'N', "") != TF_OK) goto fail;
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
switch (b->col_types[col]) {
|
|
556
|
+
case TF_TYPE_BOOL:
|
|
557
|
+
if (keybuf_append_field(&kb, 'B', tf_batch_get_bool(b, row, (size_t)col) ? "true" : "false") != TF_OK) goto fail;
|
|
558
|
+
break;
|
|
559
|
+
case TF_TYPE_INT64:
|
|
560
|
+
snprintf(tmp, sizeof(tmp), "%lld", (long long)tf_batch_get_int64(b, row, (size_t)col));
|
|
561
|
+
if (keybuf_append_field(&kb, 'I', tmp) != TF_OK) goto fail;
|
|
562
|
+
break;
|
|
563
|
+
case TF_TYPE_FLOAT64:
|
|
564
|
+
snprintf(tmp, sizeof(tmp), "%.17g", tf_batch_get_float64(b, row, (size_t)col));
|
|
565
|
+
if (keybuf_append_field(&kb, 'F', tmp) != TF_OK) goto fail;
|
|
566
|
+
break;
|
|
567
|
+
case TF_TYPE_STRING:
|
|
568
|
+
if (keybuf_append_field(&kb, 'S', tf_batch_get_string(b, row, (size_t)col)) != TF_OK) goto fail;
|
|
569
|
+
break;
|
|
570
|
+
case TF_TYPE_DATE:
|
|
571
|
+
snprintf(tmp, sizeof(tmp), "%d", (int)tf_batch_get_date(b, row, (size_t)col));
|
|
572
|
+
if (keybuf_append_field(&kb, 'D', tmp) != TF_OK) goto fail;
|
|
573
|
+
break;
|
|
574
|
+
case TF_TYPE_TIMESTAMP:
|
|
575
|
+
snprintf(tmp, sizeof(tmp), "%lld", (long long)tf_batch_get_timestamp(b, row, (size_t)col));
|
|
576
|
+
if (keybuf_append_field(&kb, 'T', tmp) != TF_OK) goto fail;
|
|
577
|
+
break;
|
|
578
|
+
default:
|
|
579
|
+
if (keybuf_append_field(&kb, '?', "") != TF_OK) goto fail;
|
|
580
|
+
break;
|
|
581
|
+
}
|
|
582
|
+
if (keybuf_append(&kb, "|", 1) != TF_OK) goto fail;
|
|
583
|
+
}
|
|
584
|
+
if (!kb.data) return strdup("");
|
|
585
|
+
return kb.data;
|
|
586
|
+
fail:
|
|
587
|
+
keybuf_free(&kb);
|
|
588
|
+
return NULL;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
static int csv_header_has_column(const uint8_t *data, size_t len, const char *name) {
|
|
592
|
+
if (!data || !name) return 0;
|
|
593
|
+
size_t name_len = strlen(name);
|
|
594
|
+
size_t start = 0;
|
|
595
|
+
for (size_t i = 0; i <= len; i++) {
|
|
596
|
+
int end_field = (i == len || data[i] == ',' || data[i] == '\n' || data[i] == '\r');
|
|
597
|
+
if (!end_field) continue;
|
|
598
|
+
size_t a = start;
|
|
599
|
+
size_t b = i;
|
|
600
|
+
while (a < b && (data[a] == ' ' || data[a] == '\t')) a++;
|
|
601
|
+
while (b > a && (data[b - 1] == ' ' || data[b - 1] == '\t')) b--;
|
|
602
|
+
if (b > a + 1 && data[a] == '"' && data[b - 1] == '"') { a++; b--; }
|
|
603
|
+
if (b - a == name_len && memcmp(data + a, name, name_len) == 0) return 1;
|
|
604
|
+
if (i == len || data[i] == '\n' || data[i] == '\r') return 0;
|
|
605
|
+
start = i + 1;
|
|
606
|
+
}
|
|
607
|
+
return 0;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
static int csv_file_header_has_column(const char *path, const char *validated_path, const char *name) {
|
|
611
|
+
if (!path || !name) return 0;
|
|
612
|
+
FILE *f = tf_policy_fopen_read(path, validated_path);
|
|
613
|
+
if (!f) return 0;
|
|
614
|
+
size_t cap = 4096;
|
|
615
|
+
uint8_t *buf = malloc(cap);
|
|
616
|
+
if (!buf) { fclose(f); return 0; }
|
|
617
|
+
size_t len = 0;
|
|
618
|
+
int found_eol = 0;
|
|
619
|
+
while (len < cap) {
|
|
620
|
+
int ch = fgetc(f);
|
|
621
|
+
if (ch == EOF) break;
|
|
622
|
+
buf[len++] = (uint8_t)ch;
|
|
623
|
+
if (ch == '\n' || ch == '\r') { found_eol = 1; break; }
|
|
624
|
+
}
|
|
625
|
+
fclose(f);
|
|
626
|
+
int ok = (len > 0 && (found_eol || len < cap)) ? csv_header_has_column(buf, len, name) : 0;
|
|
627
|
+
free(buf);
|
|
628
|
+
return ok;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
static const char *set_key_column_name(const set_state *st, const tf_batch *left, size_t i) {
|
|
632
|
+
return st->n_columns > 0 ? st->columns[i] : left->col_names[i];
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
static int prepare_sorted_key_columns(set_state *st, const tf_batch *left,
|
|
636
|
+
tf_side_channels *side) {
|
|
637
|
+
const char *op = set_mode_name(st->mode);
|
|
638
|
+
size_t n = st->n_columns > 0 ? st->n_columns : left->n_cols;
|
|
639
|
+
int *left_cols = tf_callocarray_checked(n ? n : 1, sizeof(int));
|
|
640
|
+
int *right_cols = tf_callocarray_checked(n ? n : 1, sizeof(int));
|
|
641
|
+
char **key_names = tf_callocarray_checked(n ? n : 1, sizeof(char *));
|
|
642
|
+
if (!left_cols || !right_cols || !key_names) goto fail;
|
|
643
|
+
for (size_t i = 0; i < n; i++) {
|
|
644
|
+
const char *name = set_key_column_name(st, left, i);
|
|
645
|
+
int lc = tf_batch_col_index(left, name);
|
|
646
|
+
if (lc < 0) {
|
|
647
|
+
char msg[192];
|
|
648
|
+
snprintf(msg, sizeof(msg), "%s: input column '%s' not found", op, name);
|
|
649
|
+
if (set_write_error(side, msg) != TF_OK) goto fail;
|
|
650
|
+
goto fail;
|
|
651
|
+
}
|
|
652
|
+
if (!csv_file_header_has_column(st->file, st->validated_file, name)) {
|
|
653
|
+
char msg[192];
|
|
654
|
+
snprintf(msg, sizeof(msg), "%s: lookup column '%s' not found", op, name);
|
|
655
|
+
if (set_write_error(side, msg) != TF_OK) goto fail;
|
|
656
|
+
goto fail;
|
|
657
|
+
}
|
|
658
|
+
left_cols[i] = lc;
|
|
659
|
+
right_cols[i] = -1;
|
|
660
|
+
key_names[i] = strdup(name ? name : "");
|
|
661
|
+
if (!key_names[i]) goto fail;
|
|
662
|
+
}
|
|
663
|
+
st->left_cols = left_cols;
|
|
664
|
+
st->right_cols = right_cols;
|
|
665
|
+
st->key_names = key_names;
|
|
666
|
+
st->n_key_cols = n;
|
|
667
|
+
return TF_OK;
|
|
668
|
+
|
|
669
|
+
fail:
|
|
670
|
+
free(left_cols);
|
|
671
|
+
free(right_cols);
|
|
672
|
+
set_free_string_array(key_names, n);
|
|
673
|
+
return TF_ERROR;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
static int set_ensure_ordinals(uint64_t **ord, size_t *cap, size_t need) {
|
|
677
|
+
if (*cap >= need) return TF_OK;
|
|
678
|
+
size_t new_cap = 0;
|
|
679
|
+
if (tf_size_grow_pow2(*cap, need, 16, &new_cap) != TF_OK) return TF_ERROR;
|
|
680
|
+
uint64_t *tmp = tf_reallocarray_checked(*ord, new_cap, sizeof(uint64_t));
|
|
681
|
+
if (!tmp) return TF_ERROR;
|
|
682
|
+
*ord = tmp;
|
|
683
|
+
*cap = new_cap;
|
|
684
|
+
return TF_OK;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
static tf_batch *set_spill_create_batch_from_schema(size_t n_cols, char **names,
|
|
688
|
+
const tf_type *types, size_t rows) {
|
|
689
|
+
tf_batch *b = tf_batch_create(n_cols, rows ? rows : 1);
|
|
690
|
+
if (!b) return NULL;
|
|
691
|
+
for (size_t c = 0; c < n_cols; c++) {
|
|
692
|
+
if (tf_batch_set_schema(b, c, names[c], types[c]) != TF_OK) {
|
|
693
|
+
tf_batch_free(b);
|
|
694
|
+
return NULL;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
return b;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
static tf_batch *set_spill_create_left_batch(const set_state *st, size_t rows) {
|
|
701
|
+
return set_spill_create_batch_from_schema(st->spill_n_cols, st->spill_schema_names,
|
|
702
|
+
st->spill_schema_types, rows);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
static tf_batch *set_spill_create_lookup_batch(const set_state *st, size_t rows) {
|
|
706
|
+
tf_batch *b = tf_batch_create(st->n_key_cols, rows ? rows : 1);
|
|
707
|
+
if (!b) return NULL;
|
|
708
|
+
for (size_t k = 0; k < st->n_key_cols; k++) {
|
|
709
|
+
if (tf_batch_set_schema(b, k, st->key_names[k], st->spill_key_types[k]) != TF_OK) {
|
|
710
|
+
tf_batch_free(b);
|
|
711
|
+
return NULL;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
return b;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
static size_t set_spill_estimated_row_bytes_for_types(const tf_type *types, size_t n_cols) {
|
|
718
|
+
size_t bytes = 40;
|
|
719
|
+
for (size_t c = 0; c < n_cols; c++) {
|
|
720
|
+
bytes += 1;
|
|
721
|
+
switch (types[c]) {
|
|
722
|
+
case TF_TYPE_BOOL: bytes += 1; break;
|
|
723
|
+
case TF_TYPE_INT64: bytes += sizeof(int64_t); break;
|
|
724
|
+
case TF_TYPE_FLOAT64: bytes += sizeof(double); break;
|
|
725
|
+
case TF_TYPE_STRING: bytes += sizeof(char *) + 64; break;
|
|
726
|
+
case TF_TYPE_DATE: bytes += sizeof(int32_t); break;
|
|
727
|
+
case TF_TYPE_TIMESTAMP: bytes += sizeof(int64_t); break;
|
|
728
|
+
default: break;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return bytes < 64 ? 64 : bytes;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
static int set_spill_init_schema(set_state *st, const tf_batch *in, tf_side_channels *side) {
|
|
735
|
+
if (st->spill_has_schema) return TF_OK;
|
|
736
|
+
const char *op = set_mode_name(st->mode);
|
|
737
|
+
size_t n_cols = in->n_cols;
|
|
738
|
+
size_t n = st->n_columns > 0 ? st->n_columns : in->n_cols;
|
|
739
|
+
char **schema_names = tf_callocarray_checked(n_cols ? n_cols : 1, sizeof(char *));
|
|
740
|
+
tf_type *schema_types = tf_callocarray_checked(n_cols ? n_cols : 1, sizeof(tf_type));
|
|
741
|
+
int *left_cols = NULL;
|
|
742
|
+
int *right_cols = NULL;
|
|
743
|
+
char **key_names = NULL;
|
|
744
|
+
tf_type *key_types = NULL;
|
|
745
|
+
tf_batch *left_buf = NULL;
|
|
746
|
+
tf_batch *out_buf = NULL;
|
|
747
|
+
if (!schema_names || !schema_types) goto fail;
|
|
748
|
+
for (size_t c = 0; c < in->n_cols; c++) {
|
|
749
|
+
schema_names[c] = strdup(in->col_names[c] ? in->col_names[c] : "");
|
|
750
|
+
if (!schema_names[c]) goto fail;
|
|
751
|
+
schema_types[c] = in->col_types[c];
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
left_cols = tf_callocarray_checked(n ? n : 1, sizeof(int));
|
|
755
|
+
right_cols = tf_callocarray_checked(n ? n : 1, sizeof(int));
|
|
756
|
+
key_names = tf_callocarray_checked(n ? n : 1, sizeof(char *));
|
|
757
|
+
key_types = tf_callocarray_checked(n ? n : 1, sizeof(tf_type));
|
|
758
|
+
if (!left_cols || !right_cols || !key_names || !key_types) goto fail;
|
|
759
|
+
for (size_t i = 0; i < n; i++) {
|
|
760
|
+
const char *name = st->n_columns > 0 ? st->columns[i] : in->col_names[i];
|
|
761
|
+
int lc = tf_batch_col_index(in, name);
|
|
762
|
+
if (lc < 0) {
|
|
763
|
+
char msg[192];
|
|
764
|
+
snprintf(msg, sizeof(msg), "%s: input column '%s' not found", op, name ? name : "");
|
|
765
|
+
if (set_write_error(side, msg) != TF_OK) goto fail;
|
|
766
|
+
goto fail;
|
|
767
|
+
}
|
|
768
|
+
if (!csv_file_header_has_column(st->file, st->validated_file, name)) {
|
|
769
|
+
char msg[192];
|
|
770
|
+
snprintf(msg, sizeof(msg), "%s: lookup column '%s' not found", op, name ? name : "");
|
|
771
|
+
if (set_write_error(side, msg) != TF_OK) goto fail;
|
|
772
|
+
goto fail;
|
|
773
|
+
}
|
|
774
|
+
left_cols[i] = lc;
|
|
775
|
+
right_cols[i] = -1;
|
|
776
|
+
key_names[i] = strdup(name ? name : "");
|
|
777
|
+
if (!key_names[i]) goto fail;
|
|
778
|
+
key_types[i] = in->col_types[lc];
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
size_t run_rows = SET_DEFAULT_RUN_ROWS;
|
|
782
|
+
if (st->configured_run_rows > 0) {
|
|
783
|
+
run_rows = st->configured_run_rows;
|
|
784
|
+
} else if (st->spill_memory_bytes > 0) {
|
|
785
|
+
size_t row_bytes = set_spill_estimated_row_bytes_for_types(schema_types, n_cols);
|
|
786
|
+
run_rows = st->spill_memory_bytes / (row_bytes * 4);
|
|
787
|
+
if (run_rows < SET_MIN_RUN_ROWS) run_rows = SET_MIN_RUN_ROWS;
|
|
788
|
+
}
|
|
789
|
+
size_t output_batch_rows = st->output_batch_rows == 0 ? SET_DEFAULT_OUTPUT_ROWS : st->output_batch_rows;
|
|
790
|
+
left_buf = set_spill_create_batch_from_schema(n_cols, schema_names, schema_types, run_rows);
|
|
791
|
+
out_buf = set_spill_create_batch_from_schema(n_cols, schema_names, schema_types, run_rows);
|
|
792
|
+
if (!left_buf || !out_buf) goto fail;
|
|
793
|
+
|
|
794
|
+
st->spill_n_cols = n_cols;
|
|
795
|
+
st->spill_schema_names = schema_names;
|
|
796
|
+
st->spill_schema_types = schema_types;
|
|
797
|
+
st->left_cols = left_cols;
|
|
798
|
+
st->right_cols = right_cols;
|
|
799
|
+
st->key_names = key_names;
|
|
800
|
+
st->spill_key_types = key_types;
|
|
801
|
+
st->n_key_cols = n;
|
|
802
|
+
st->run_rows = run_rows;
|
|
803
|
+
st->output_batch_rows = output_batch_rows;
|
|
804
|
+
st->spill_left_buf = left_buf;
|
|
805
|
+
st->spill_out_buf = out_buf;
|
|
806
|
+
st->spill_has_schema = 1;
|
|
807
|
+
return TF_OK;
|
|
808
|
+
|
|
809
|
+
fail:
|
|
810
|
+
set_free_string_array(schema_names, n_cols);
|
|
811
|
+
free(schema_types);
|
|
812
|
+
free(left_cols);
|
|
813
|
+
free(right_cols);
|
|
814
|
+
set_free_string_array(key_names, n);
|
|
815
|
+
free(key_types);
|
|
816
|
+
if (left_buf) tf_batch_free(left_buf);
|
|
817
|
+
if (out_buf) tf_batch_free(out_buf);
|
|
818
|
+
return TF_ERROR;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
static int set_spill_compare_batch_cell(const tf_batch *a, size_t ra, size_t ca,
|
|
822
|
+
const tf_batch *b, size_t rb, size_t cb) {
|
|
823
|
+
tf_type ta = a->col_types[ca], tb = b->col_types[cb];
|
|
824
|
+
if (ta != tb) return (ta > tb) - (ta < tb);
|
|
825
|
+
int na = tf_batch_is_null(a, ra, ca) ? 1 : 0;
|
|
826
|
+
int nb = tf_batch_is_null(b, rb, cb) ? 1 : 0;
|
|
827
|
+
if (na && nb) return 0;
|
|
828
|
+
if (na) return 1;
|
|
829
|
+
if (nb) return -1;
|
|
830
|
+
switch (ta) {
|
|
831
|
+
case TF_TYPE_BOOL: return (int)tf_batch_get_bool(a, ra, ca) - (int)tf_batch_get_bool(b, rb, cb);
|
|
832
|
+
case TF_TYPE_INT64:
|
|
833
|
+
case TF_TYPE_TIMESTAMP: {
|
|
834
|
+
int64_t va = ta == TF_TYPE_TIMESTAMP ? tf_batch_get_timestamp(a, ra, ca) : tf_batch_get_int64(a, ra, ca);
|
|
835
|
+
int64_t vb = tb == TF_TYPE_TIMESTAMP ? tf_batch_get_timestamp(b, rb, cb) : tf_batch_get_int64(b, rb, cb);
|
|
836
|
+
return (va > vb) - (va < vb);
|
|
837
|
+
}
|
|
838
|
+
case TF_TYPE_FLOAT64: {
|
|
839
|
+
double va = tf_batch_get_float64(a, ra, ca), vb = tf_batch_get_float64(b, rb, cb);
|
|
840
|
+
return (va > vb) - (va < vb);
|
|
841
|
+
}
|
|
842
|
+
case TF_TYPE_STRING: return strcmp(tf_batch_get_string(a, ra, ca), tf_batch_get_string(b, rb, cb));
|
|
843
|
+
case TF_TYPE_DATE: {
|
|
844
|
+
int32_t va = tf_batch_get_date(a, ra, ca), vb = tf_batch_get_date(b, rb, cb);
|
|
845
|
+
return (va > vb) - (va < vb);
|
|
846
|
+
}
|
|
847
|
+
default: return 0;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
static int set_spill_compare_batch_key_rows(const set_state *st, const tf_batch *b, size_t ra, size_t rb, int lookup) {
|
|
852
|
+
for (size_t k = 0; k < st->n_key_cols; k++) {
|
|
853
|
+
size_t ca = lookup ? k : (size_t)st->left_cols[k];
|
|
854
|
+
int cmp = set_spill_compare_batch_cell(b, ra, ca, b, rb, ca);
|
|
855
|
+
if (cmp != 0) return cmp;
|
|
856
|
+
}
|
|
857
|
+
return 0;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
typedef struct {
|
|
861
|
+
const set_state *st;
|
|
862
|
+
const tf_batch *batch;
|
|
863
|
+
const uint64_t *ordinals;
|
|
864
|
+
int kind;
|
|
865
|
+
} set_spill_sort_ctx;
|
|
866
|
+
|
|
867
|
+
static int set_spill_compare_indices(const void *ctx, size_t a, size_t b) {
|
|
868
|
+
const set_spill_sort_ctx *sort = (const set_spill_sort_ctx *)ctx;
|
|
869
|
+
if (sort->kind == 2) {
|
|
870
|
+
uint64_t oa = sort->ordinals[a], ob = sort->ordinals[b];
|
|
871
|
+
return (oa > ob) - (oa < ob);
|
|
872
|
+
}
|
|
873
|
+
int cmp = set_spill_compare_batch_key_rows(sort->st, sort->batch, a, b, sort->kind == 1);
|
|
874
|
+
if (cmp != 0) return cmp;
|
|
875
|
+
uint64_t oa = sort->ordinals[a], ob = sort->ordinals[b];
|
|
876
|
+
return (oa > ob) - (oa < ob);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
static size_t *set_spill_sorted_indices(const set_state *st, const tf_batch *b,
|
|
880
|
+
const uint64_t *ordinals, int kind) {
|
|
881
|
+
size_t *idx = tf_mallocarray_checked(b->n_rows ? b->n_rows : 1, sizeof(size_t));
|
|
882
|
+
if (!idx) return NULL;
|
|
883
|
+
for (size_t i = 0; i < b->n_rows; i++) idx[i] = i;
|
|
884
|
+
set_spill_sort_ctx ctx = { .st = st, .batch = b, .ordinals = ordinals, .kind = kind };
|
|
885
|
+
tf_sort_indices(idx, b->n_rows, set_spill_compare_indices, &ctx);
|
|
886
|
+
return idx;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
static int set_spill_write_exact(FILE *f, const void *ptr, size_t len) {
|
|
890
|
+
return fwrite(ptr, 1, len, f) == len ? TF_OK : TF_ERROR;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
static int set_spill_read_exact(FILE *f, void *ptr, size_t len) {
|
|
894
|
+
return fread(ptr, 1, len, f) == len ? TF_OK : TF_ERROR;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
static int set_spill_write_cell(FILE *f, const tf_batch *b, size_t r, size_t c) {
|
|
898
|
+
uint8_t is_null = tf_batch_is_null(b, r, c) ? 1 : 0;
|
|
899
|
+
if (set_spill_write_exact(f, &is_null, sizeof(is_null)) != TF_OK) return TF_ERROR;
|
|
900
|
+
if (is_null) return TF_OK;
|
|
901
|
+
switch (b->col_types[c]) {
|
|
902
|
+
case TF_TYPE_BOOL: { uint8_t v = tf_batch_get_bool(b, r, c) ? 1 : 0; return set_spill_write_exact(f, &v, sizeof(v)); }
|
|
903
|
+
case TF_TYPE_INT64: { int64_t v = tf_batch_get_int64(b, r, c); return set_spill_write_exact(f, &v, sizeof(v)); }
|
|
904
|
+
case TF_TYPE_FLOAT64: { double v = tf_batch_get_float64(b, r, c); return set_spill_write_exact(f, &v, sizeof(v)); }
|
|
905
|
+
case TF_TYPE_STRING: {
|
|
906
|
+
const char *s = tf_batch_get_string(b, r, c);
|
|
907
|
+
uint64_t len = s ? (uint64_t)strlen(s) : 0;
|
|
908
|
+
if (set_spill_write_exact(f, &len, sizeof(len)) != TF_OK) return TF_ERROR;
|
|
909
|
+
return len ? set_spill_write_exact(f, s, (size_t)len) : TF_OK;
|
|
910
|
+
}
|
|
911
|
+
case TF_TYPE_DATE: { int32_t v = tf_batch_get_date(b, r, c); return set_spill_write_exact(f, &v, sizeof(v)); }
|
|
912
|
+
case TF_TYPE_TIMESTAMP: { int64_t v = tf_batch_get_timestamp(b, r, c); return set_spill_write_exact(f, &v, sizeof(v)); }
|
|
913
|
+
default: return TF_OK;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
static int set_spill_append_path(char ***paths, size_t *n, size_t *cap, char *path) {
|
|
918
|
+
if (*n == *cap) {
|
|
919
|
+
size_t need = 0;
|
|
920
|
+
size_t new_cap = 0;
|
|
921
|
+
if (tf_size_add(*n, 1, &need) != TF_OK ||
|
|
922
|
+
tf_size_grow_pow2(*cap, need, 8, &new_cap) != TF_OK) {
|
|
923
|
+
return TF_ERROR;
|
|
924
|
+
}
|
|
925
|
+
char **tmp = tf_reallocarray_checked(*paths, new_cap, sizeof(char *));
|
|
926
|
+
if (!tmp) return TF_ERROR;
|
|
927
|
+
*paths = tmp;
|
|
928
|
+
*cap = new_cap;
|
|
929
|
+
}
|
|
930
|
+
(*paths)[(*n)++] = path;
|
|
931
|
+
return TF_OK;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
static int set_spill_write_batch_run(set_state *st, tf_batch *batch, const uint64_t *ordinals,
|
|
935
|
+
size_t *indices, size_t n, int kind) {
|
|
936
|
+
const char *kind_label = kind == 0 ? "left" : (kind == 1 ? "lookup" : "out");
|
|
937
|
+
char label[48];
|
|
938
|
+
snprintf(label, sizeof(label), "set-%s", kind_label);
|
|
939
|
+
char *path = NULL;
|
|
940
|
+
FILE *f = tf_spill_open_run_file(st->spill, label, &path);
|
|
941
|
+
if (!f) return TF_ERROR;
|
|
942
|
+
for (size_t i = 0; i < n; i++) {
|
|
943
|
+
size_t r = indices[i];
|
|
944
|
+
uint64_t ordinal = ordinals[r];
|
|
945
|
+
if (set_spill_write_exact(f, &ordinal, sizeof(ordinal)) != TF_OK) goto write_fail;
|
|
946
|
+
for (size_t c = 0; c < batch->n_cols; c++) {
|
|
947
|
+
if (set_spill_write_cell(f, batch, r, c) != TF_OK) goto write_fail;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
long pos = ftell(f);
|
|
951
|
+
if (pos > 0) st->spill_bytes += (size_t)pos;
|
|
952
|
+
if (fclose(f) != 0) {
|
|
953
|
+
tf_set_last_error("set spill: failed closing run file");
|
|
954
|
+
remove(path);
|
|
955
|
+
free(path);
|
|
956
|
+
return TF_ERROR;
|
|
957
|
+
}
|
|
958
|
+
int rc;
|
|
959
|
+
if (kind == 0) rc = set_spill_append_path(&st->spill_left_run_paths, &st->spill_n_left_runs, &st->spill_cap_left_runs, path);
|
|
960
|
+
else if (kind == 1) rc = set_spill_append_path(&st->spill_lookup_run_paths, &st->spill_n_lookup_runs, &st->spill_cap_lookup_runs, path);
|
|
961
|
+
else rc = set_spill_append_path(&st->spill_out_run_paths, &st->spill_n_out_runs, &st->spill_cap_out_runs, path);
|
|
962
|
+
if (rc != TF_OK) { remove(path); free(path); return TF_ERROR; }
|
|
963
|
+
st->spill_runs++;
|
|
964
|
+
return TF_OK;
|
|
965
|
+
|
|
966
|
+
write_fail:
|
|
967
|
+
tf_set_last_error("set spill: failed writing run file");
|
|
968
|
+
fclose(f);
|
|
969
|
+
remove(path);
|
|
970
|
+
free(path);
|
|
971
|
+
return TF_ERROR;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
static int set_spill_write_left_run(set_state *st) {
|
|
975
|
+
if (!st->spill_left_buf || st->spill_left_buf->n_rows == 0) return TF_OK;
|
|
976
|
+
size_t *idx = set_spill_sorted_indices(st, st->spill_left_buf, st->spill_left_ordinals, 0);
|
|
977
|
+
if (!idx) return TF_ERROR;
|
|
978
|
+
int rc = set_spill_write_batch_run(st, st->spill_left_buf, st->spill_left_ordinals, idx, st->spill_left_buf->n_rows, 0);
|
|
979
|
+
free(idx);
|
|
980
|
+
if (rc != TF_OK) return TF_ERROR;
|
|
981
|
+
tf_batch_free(st->spill_left_buf);
|
|
982
|
+
st->spill_left_buf = set_spill_create_left_batch(st, st->run_rows);
|
|
983
|
+
free(st->spill_left_ordinals); st->spill_left_ordinals = NULL; st->spill_left_ordinal_cap = 0;
|
|
984
|
+
return st->spill_left_buf ? TF_OK : TF_ERROR;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
static int set_spill_write_lookup_run(set_state *st) {
|
|
988
|
+
if (!st->spill_lookup_buf || st->spill_lookup_buf->n_rows == 0) return TF_OK;
|
|
989
|
+
size_t *idx = set_spill_sorted_indices(st, st->spill_lookup_buf, st->spill_lookup_ordinals, 1);
|
|
990
|
+
if (!idx) return TF_ERROR;
|
|
991
|
+
int rc = set_spill_write_batch_run(st, st->spill_lookup_buf, st->spill_lookup_ordinals, idx, st->spill_lookup_buf->n_rows, 1);
|
|
992
|
+
free(idx);
|
|
993
|
+
if (rc != TF_OK) return TF_ERROR;
|
|
994
|
+
tf_batch_free(st->spill_lookup_buf);
|
|
995
|
+
st->spill_lookup_buf = set_spill_create_lookup_batch(st, st->run_rows);
|
|
996
|
+
free(st->spill_lookup_ordinals); st->spill_lookup_ordinals = NULL; st->spill_lookup_ordinal_cap = 0;
|
|
997
|
+
return st->spill_lookup_buf ? TF_OK : TF_ERROR;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
static int set_spill_write_output_run(set_state *st) {
|
|
1001
|
+
if (!st->spill_out_buf || st->spill_out_buf->n_rows == 0) return TF_OK;
|
|
1002
|
+
size_t *idx = set_spill_sorted_indices(st, st->spill_out_buf, st->spill_out_ordinals, 2);
|
|
1003
|
+
if (!idx) return TF_ERROR;
|
|
1004
|
+
int rc = set_spill_write_batch_run(st, st->spill_out_buf, st->spill_out_ordinals, idx, st->spill_out_buf->n_rows, 2);
|
|
1005
|
+
free(idx);
|
|
1006
|
+
if (rc != TF_OK) return TF_ERROR;
|
|
1007
|
+
tf_batch_free(st->spill_out_buf);
|
|
1008
|
+
st->spill_out_buf = set_spill_create_left_batch(st, st->run_rows);
|
|
1009
|
+
free(st->spill_out_ordinals); st->spill_out_ordinals = NULL; st->spill_out_ordinal_cap = 0;
|
|
1010
|
+
return st->spill_out_buf ? TF_OK : TF_ERROR;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
static void set_spill_row_clear(set_spill_row *row, const tf_type *types, size_t n_cols) {
|
|
1014
|
+
if (!row || !row->cells || !row->nulls) return;
|
|
1015
|
+
for (size_t c = 0; c < n_cols; c++) {
|
|
1016
|
+
if (!row->nulls[c] && types[c] == TF_TYPE_STRING) free(row->cells[c].str);
|
|
1017
|
+
row->cells[c].str = NULL;
|
|
1018
|
+
row->nulls[c] = 1;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
static int set_spill_row_init(set_spill_row *row, size_t n_cols) {
|
|
1023
|
+
row->ordinal = 0;
|
|
1024
|
+
row->nulls = tf_callocarray_checked(n_cols ? n_cols : 1, sizeof(uint8_t));
|
|
1025
|
+
row->cells = tf_callocarray_checked(n_cols ? n_cols : 1, sizeof(*row->cells));
|
|
1026
|
+
if (!row->nulls || !row->cells) {
|
|
1027
|
+
free(row->nulls);
|
|
1028
|
+
free(row->cells);
|
|
1029
|
+
row->nulls = NULL;
|
|
1030
|
+
row->cells = NULL;
|
|
1031
|
+
return TF_ERROR;
|
|
1032
|
+
}
|
|
1033
|
+
for (size_t c = 0; c < n_cols; c++) row->nulls[c] = 1;
|
|
1034
|
+
return TF_OK;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
static void set_spill_row_free(set_spill_row *row, const tf_type *types, size_t n_cols) {
|
|
1038
|
+
if (!row) return;
|
|
1039
|
+
set_spill_row_clear(row, types, n_cols);
|
|
1040
|
+
free(row->nulls);
|
|
1041
|
+
free(row->cells);
|
|
1042
|
+
row->nulls = NULL;
|
|
1043
|
+
row->cells = NULL;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
static int set_spill_read_cell_value(FILE *f, set_spill_row *row, const tf_type *types, size_t c) {
|
|
1047
|
+
switch (types[c]) {
|
|
1048
|
+
case TF_TYPE_BOOL: { uint8_t v = 0; if (set_spill_read_exact(f, &v, sizeof(v)) != TF_OK) return TF_ERROR; row->cells[c].b = v; return TF_OK; }
|
|
1049
|
+
case TF_TYPE_INT64: { int64_t v = 0; if (set_spill_read_exact(f, &v, sizeof(v)) != TF_OK) return TF_ERROR; row->cells[c].i64 = v; return TF_OK; }
|
|
1050
|
+
case TF_TYPE_FLOAT64: { double v = 0; if (set_spill_read_exact(f, &v, sizeof(v)) != TF_OK) return TF_ERROR; row->cells[c].f64 = v; return TF_OK; }
|
|
1051
|
+
case TF_TYPE_STRING: {
|
|
1052
|
+
uint64_t len = 0;
|
|
1053
|
+
if (set_spill_read_exact(f, &len, sizeof(len)) != TF_OK) return TF_ERROR;
|
|
1054
|
+
if (len > (uint64_t)SIZE_MAX - 1) return TF_ERROR;
|
|
1055
|
+
char *s = malloc((size_t)len + 1);
|
|
1056
|
+
if (!s) return TF_ERROR;
|
|
1057
|
+
if (len && set_spill_read_exact(f, s, (size_t)len) != TF_OK) { free(s); return TF_ERROR; }
|
|
1058
|
+
s[len] = '\0';
|
|
1059
|
+
row->cells[c].str = s;
|
|
1060
|
+
return TF_OK;
|
|
1061
|
+
}
|
|
1062
|
+
case TF_TYPE_DATE: { int32_t v = 0; if (set_spill_read_exact(f, &v, sizeof(v)) != TF_OK) return TF_ERROR; row->cells[c].date = v; return TF_OK; }
|
|
1063
|
+
case TF_TYPE_TIMESTAMP: { int64_t v = 0; if (set_spill_read_exact(f, &v, sizeof(v)) != TF_OK) return TF_ERROR; row->cells[c].i64 = v; return TF_OK; }
|
|
1064
|
+
default: return TF_OK;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
static const tf_type *set_spill_reader_types(const set_state *st, int kind, size_t *n_cols) {
|
|
1069
|
+
if (kind == 1) { *n_cols = st->n_key_cols; return st->spill_key_types; }
|
|
1070
|
+
*n_cols = st->spill_n_cols;
|
|
1071
|
+
return st->spill_schema_types;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
static int set_spill_reader_advance(set_state *st, set_spill_reader *reader) {
|
|
1075
|
+
if (!reader || !reader->file || reader->done) return 0;
|
|
1076
|
+
size_t n_cols = 0;
|
|
1077
|
+
const tf_type *types = set_spill_reader_types(st, reader->kind, &n_cols);
|
|
1078
|
+
set_spill_row_clear(&reader->row, types, n_cols);
|
|
1079
|
+
if (fread(&reader->row.ordinal, sizeof(reader->row.ordinal), 1, reader->file) != 1) {
|
|
1080
|
+
if (feof(reader->file)) {
|
|
1081
|
+
reader->done = 1;
|
|
1082
|
+
reader->has_row = 0;
|
|
1083
|
+
return 0;
|
|
1084
|
+
}
|
|
1085
|
+
tf_set_last_error("set spill: failed reading run file");
|
|
1086
|
+
return -1;
|
|
1087
|
+
}
|
|
1088
|
+
for (size_t c = 0; c < n_cols; c++) {
|
|
1089
|
+
uint8_t is_null = 1;
|
|
1090
|
+
if (set_spill_read_exact(reader->file, &is_null, sizeof(is_null)) != TF_OK) {
|
|
1091
|
+
tf_set_last_error("set spill: corrupt run file");
|
|
1092
|
+
return -1;
|
|
1093
|
+
}
|
|
1094
|
+
reader->row.nulls[c] = is_null ? 1 : 0;
|
|
1095
|
+
if (!reader->row.nulls[c] && set_spill_read_cell_value(reader->file, &reader->row, types, c) != TF_OK) {
|
|
1096
|
+
tf_set_last_error("set spill: corrupt run file");
|
|
1097
|
+
return -1;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
reader->has_row = 1;
|
|
1101
|
+
return 1;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
static void set_spill_close_readers(set_state *st, int kind) {
|
|
1105
|
+
set_spill_reader **readers;
|
|
1106
|
+
size_t *n_readers;
|
|
1107
|
+
if (kind == 0) { readers = &st->spill_left_readers; n_readers = &st->spill_n_left_readers; }
|
|
1108
|
+
else if (kind == 1) { readers = &st->spill_lookup_readers; n_readers = &st->spill_n_lookup_readers; }
|
|
1109
|
+
else { readers = &st->spill_out_readers; n_readers = &st->spill_n_out_readers; }
|
|
1110
|
+
if (!*readers) return;
|
|
1111
|
+
size_t n_cols = 0;
|
|
1112
|
+
const tf_type *types = set_spill_reader_types(st, kind, &n_cols);
|
|
1113
|
+
for (size_t i = 0; i < *n_readers; i++) {
|
|
1114
|
+
if ((*readers)[i].file) fclose((*readers)[i].file);
|
|
1115
|
+
set_spill_row_free(&(*readers)[i].row, types, n_cols);
|
|
1116
|
+
}
|
|
1117
|
+
free(*readers);
|
|
1118
|
+
*readers = NULL;
|
|
1119
|
+
*n_readers = 0;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
static void set_spill_remove_paths(char ***paths, size_t *n, size_t *cap) {
|
|
1123
|
+
for (size_t i = 0; i < *n; i++) {
|
|
1124
|
+
if ((*paths)[i]) {
|
|
1125
|
+
remove((*paths)[i]);
|
|
1126
|
+
free((*paths)[i]);
|
|
1127
|
+
(*paths)[i] = NULL;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
free(*paths);
|
|
1131
|
+
*paths = NULL;
|
|
1132
|
+
*n = 0;
|
|
1133
|
+
*cap = 0;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
static int set_spill_open_readers(set_state *st, int kind) {
|
|
1137
|
+
char **paths;
|
|
1138
|
+
size_t n_paths;
|
|
1139
|
+
set_spill_reader **readers;
|
|
1140
|
+
size_t *n_readers;
|
|
1141
|
+
if (kind == 0) { paths = st->spill_left_run_paths; n_paths = st->spill_n_left_runs; readers = &st->spill_left_readers; n_readers = &st->spill_n_left_readers; }
|
|
1142
|
+
else if (kind == 1) { paths = st->spill_lookup_run_paths; n_paths = st->spill_n_lookup_runs; readers = &st->spill_lookup_readers; n_readers = &st->spill_n_lookup_readers; }
|
|
1143
|
+
else { paths = st->spill_out_run_paths; n_paths = st->spill_n_out_runs; readers = &st->spill_out_readers; n_readers = &st->spill_n_out_readers; }
|
|
1144
|
+
if (n_paths == 0) return TF_OK;
|
|
1145
|
+
*readers = tf_callocarray_checked(n_paths, sizeof(set_spill_reader));
|
|
1146
|
+
if (!*readers) return TF_ERROR;
|
|
1147
|
+
*n_readers = n_paths;
|
|
1148
|
+
size_t n_cols = 0;
|
|
1149
|
+
set_spill_reader_types(st, kind, &n_cols);
|
|
1150
|
+
for (size_t i = 0; i < n_paths; i++) {
|
|
1151
|
+
(*readers)[i].kind = kind;
|
|
1152
|
+
(*readers)[i].file = fopen(paths[i], "rb");
|
|
1153
|
+
if (!(*readers)[i].file) { tf_set_last_error("set spill: cannot reopen run file"); return TF_ERROR; }
|
|
1154
|
+
if (set_spill_row_init(&(*readers)[i].row, n_cols) != TF_OK) return TF_ERROR;
|
|
1155
|
+
int rc = set_spill_reader_advance(st, &(*readers)[i]);
|
|
1156
|
+
if (rc < 0) return TF_ERROR;
|
|
1157
|
+
}
|
|
1158
|
+
return TF_OK;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
static int set_spill_key_cell(const set_state *st, const set_spill_row *row, int kind, size_t k,
|
|
1162
|
+
tf_type *type, int *is_null, const tf_owned_cell_value **cell) {
|
|
1163
|
+
size_t c = kind == 1 ? k : (size_t)st->left_cols[k];
|
|
1164
|
+
*type = kind == 1 ? st->spill_key_types[k] : st->spill_schema_types[c];
|
|
1165
|
+
*is_null = row->nulls[c] ? 1 : 0;
|
|
1166
|
+
*cell = &row->cells[c];
|
|
1167
|
+
return TF_OK;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
static int set_spill_compare_key_rows(const set_state *st, const set_spill_row *a, int kind_a,
|
|
1171
|
+
const set_spill_row *b, int kind_b) {
|
|
1172
|
+
for (size_t k = 0; k < st->n_key_cols; k++) {
|
|
1173
|
+
tf_type ta, tb;
|
|
1174
|
+
int na, nb;
|
|
1175
|
+
const tf_owned_cell_value *ca, *cb;
|
|
1176
|
+
set_spill_key_cell(st, a, kind_a, k, &ta, &na, &ca);
|
|
1177
|
+
set_spill_key_cell(st, b, kind_b, k, &tb, &nb, &cb);
|
|
1178
|
+
if (ta != tb) return (ta > tb) - (ta < tb);
|
|
1179
|
+
if (na && nb) continue;
|
|
1180
|
+
if (na) return 1;
|
|
1181
|
+
if (nb) return -1;
|
|
1182
|
+
int cmp = 0;
|
|
1183
|
+
switch (ta) {
|
|
1184
|
+
case TF_TYPE_BOOL: cmp = (int)ca->b - (int)cb->b; break;
|
|
1185
|
+
case TF_TYPE_INT64:
|
|
1186
|
+
case TF_TYPE_TIMESTAMP: cmp = (ca->i64 > cb->i64) - (ca->i64 < cb->i64); break;
|
|
1187
|
+
case TF_TYPE_FLOAT64: cmp = (ca->f64 > cb->f64) - (ca->f64 < cb->f64); break;
|
|
1188
|
+
case TF_TYPE_STRING: cmp = strcmp(ca->str ? ca->str : "", cb->str ? cb->str : ""); break;
|
|
1189
|
+
case TF_TYPE_DATE: cmp = (ca->date > cb->date) - (ca->date < cb->date); break;
|
|
1190
|
+
default: break;
|
|
1191
|
+
}
|
|
1192
|
+
if (cmp != 0) return cmp;
|
|
1193
|
+
}
|
|
1194
|
+
return 0;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
static char *set_spill_build_key(const set_state *st, const set_spill_row *row, int kind) {
|
|
1198
|
+
keybuf kb = {0};
|
|
1199
|
+
for (size_t k = 0; k < st->n_key_cols; k++) {
|
|
1200
|
+
tf_type type;
|
|
1201
|
+
int is_null;
|
|
1202
|
+
const tf_owned_cell_value *cell;
|
|
1203
|
+
set_spill_key_cell(st, row, kind, k, &type, &is_null, &cell);
|
|
1204
|
+
if (k > 0 && keybuf_append(&kb, "|", 1) != TF_OK) goto fail;
|
|
1205
|
+
char tmp[96];
|
|
1206
|
+
if (is_null) {
|
|
1207
|
+
snprintf(tmp, sizeof(tmp), "N:%d", (int)type);
|
|
1208
|
+
if (keybuf_append(&kb, tmp, strlen(tmp)) != TF_OK) goto fail;
|
|
1209
|
+
continue;
|
|
1210
|
+
}
|
|
1211
|
+
switch (type) {
|
|
1212
|
+
case TF_TYPE_BOOL:
|
|
1213
|
+
if (keybuf_append(&kb, cell->b ? "B:1" : "B:0", 3) != TF_OK) goto fail;
|
|
1214
|
+
break;
|
|
1215
|
+
case TF_TYPE_INT64:
|
|
1216
|
+
snprintf(tmp, sizeof(tmp), "I:%lld", (long long)cell->i64);
|
|
1217
|
+
if (keybuf_append(&kb, tmp, strlen(tmp)) != TF_OK) goto fail;
|
|
1218
|
+
break;
|
|
1219
|
+
case TF_TYPE_FLOAT64: {
|
|
1220
|
+
int n = snprintf(tmp, sizeof(tmp), "F:%.17g", cell->f64);
|
|
1221
|
+
if (n < 0 || keybuf_append(&kb, tmp, (size_t)n) != TF_OK) goto fail;
|
|
1222
|
+
break;
|
|
1223
|
+
}
|
|
1224
|
+
case TF_TYPE_STRING: {
|
|
1225
|
+
const char *s = cell->str ? cell->str : "";
|
|
1226
|
+
int n = snprintf(tmp, sizeof(tmp), "S:%zu:", strlen(s));
|
|
1227
|
+
if (n < 0 || keybuf_append(&kb, tmp, (size_t)n) != TF_OK) goto fail;
|
|
1228
|
+
if (keybuf_append(&kb, s, strlen(s)) != TF_OK) goto fail;
|
|
1229
|
+
break;
|
|
1230
|
+
}
|
|
1231
|
+
case TF_TYPE_DATE:
|
|
1232
|
+
snprintf(tmp, sizeof(tmp), "D:%lld", (long long)cell->date);
|
|
1233
|
+
if (keybuf_append(&kb, tmp, strlen(tmp)) != TF_OK) goto fail;
|
|
1234
|
+
break;
|
|
1235
|
+
case TF_TYPE_TIMESTAMP:
|
|
1236
|
+
snprintf(tmp, sizeof(tmp), "T:%lld", (long long)cell->i64);
|
|
1237
|
+
if (keybuf_append(&kb, tmp, strlen(tmp)) != TF_OK) goto fail;
|
|
1238
|
+
break;
|
|
1239
|
+
default:
|
|
1240
|
+
if (keybuf_append(&kb, "U", 1) != TF_OK) goto fail;
|
|
1241
|
+
break;
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
if (!kb.data) return strdup("");
|
|
1245
|
+
return kb.data;
|
|
1246
|
+
fail:
|
|
1247
|
+
keybuf_free(&kb);
|
|
1248
|
+
return NULL;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
static int set_spill_best_reader(const set_state *st, int kind) {
|
|
1252
|
+
set_spill_reader *readers;
|
|
1253
|
+
size_t n_readers;
|
|
1254
|
+
if (kind == 0) { readers = st->spill_left_readers; n_readers = st->spill_n_left_readers; }
|
|
1255
|
+
else if (kind == 1) { readers = st->spill_lookup_readers; n_readers = st->spill_n_lookup_readers; }
|
|
1256
|
+
else { readers = st->spill_out_readers; n_readers = st->spill_n_out_readers; }
|
|
1257
|
+
int best = -1;
|
|
1258
|
+
for (size_t i = 0; i < n_readers; i++) {
|
|
1259
|
+
set_spill_reader *r = &readers[i];
|
|
1260
|
+
if (!r->has_row || r->done) continue;
|
|
1261
|
+
if (best < 0) { best = (int)i; continue; }
|
|
1262
|
+
int cmp;
|
|
1263
|
+
if (kind == 2) {
|
|
1264
|
+
uint64_t a = r->row.ordinal, b = readers[best].row.ordinal;
|
|
1265
|
+
cmp = (a > b) - (a < b);
|
|
1266
|
+
} else {
|
|
1267
|
+
cmp = set_spill_compare_key_rows(st, &r->row, kind, &readers[best].row, kind);
|
|
1268
|
+
if (cmp == 0) cmp = (r->row.ordinal > readers[best].row.ordinal) - (r->row.ordinal < readers[best].row.ordinal);
|
|
1269
|
+
}
|
|
1270
|
+
if (cmp < 0 || (cmp == 0 && i < (size_t)best)) best = (int)i;
|
|
1271
|
+
}
|
|
1272
|
+
return best;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
static int set_spill_row_to_batch(const set_state *st, tf_batch *out, size_t dst_row,
|
|
1276
|
+
const set_spill_row *row) {
|
|
1277
|
+
if (tf_batch_ensure_capacity(out, dst_row + 1) != TF_OK) return TF_ERROR;
|
|
1278
|
+
for (size_t c = 0; c < st->spill_n_cols; c++) {
|
|
1279
|
+
if (tf_batch_set_owned_cell_value(out, dst_row, c, st->spill_schema_types[c],
|
|
1280
|
+
row->nulls[c], &row->cells[c]) != TF_OK)
|
|
1281
|
+
return TF_ERROR;
|
|
1282
|
+
}
|
|
1283
|
+
return TF_OK;
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
static int set_spill_count_lookup_key(set_state *st, const set_spill_row *row, tf_side_channels *side) {
|
|
1287
|
+
char *key = set_spill_build_key(st, row, 1);
|
|
1288
|
+
if (!key) return TF_ERROR;
|
|
1289
|
+
if (!st->spill_last_lookup_key || strcmp(st->spill_last_lookup_key, key) != 0) {
|
|
1290
|
+
if (st->max_lookup_keys > 0 && st->spill_lookup_keys >= st->max_lookup_keys) {
|
|
1291
|
+
int err_rc = set_limit_error(side, set_mode_name(st->mode), "max_lookup_keys",
|
|
1292
|
+
st->max_lookup_keys, st->spill_lookup_keys + 1);
|
|
1293
|
+
free(key);
|
|
1294
|
+
if (err_rc != TF_OK) return TF_ERROR;
|
|
1295
|
+
return TF_ERROR;
|
|
1296
|
+
}
|
|
1297
|
+
size_t key_bytes_delta = 0;
|
|
1298
|
+
size_t new_spill_lookup_key_bytes = 0;
|
|
1299
|
+
if (tf_size_add(strlen(key), 1, &key_bytes_delta) != TF_OK ||
|
|
1300
|
+
tf_size_add(st->spill_lookup_key_bytes, key_bytes_delta,
|
|
1301
|
+
&new_spill_lookup_key_bytes) != TF_OK) {
|
|
1302
|
+
free(key);
|
|
1303
|
+
return TF_ERROR;
|
|
1304
|
+
}
|
|
1305
|
+
free(st->spill_last_lookup_key);
|
|
1306
|
+
st->spill_last_lookup_key = key;
|
|
1307
|
+
key = NULL;
|
|
1308
|
+
st->spill_lookup_keys++;
|
|
1309
|
+
st->spill_lookup_key_bytes = new_spill_lookup_key_bytes;
|
|
1310
|
+
}
|
|
1311
|
+
free(key);
|
|
1312
|
+
return TF_OK;
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
static int set_spill_append_selected_row(set_state *st, const set_spill_row *row) {
|
|
1316
|
+
size_t dst = st->spill_out_buf->n_rows;
|
|
1317
|
+
if (set_spill_row_to_batch(st, st->spill_out_buf, dst, row) != TF_OK) return TF_ERROR;
|
|
1318
|
+
if (set_ensure_ordinals(&st->spill_out_ordinals, &st->spill_out_ordinal_cap, dst + 1) != TF_OK) return TF_ERROR;
|
|
1319
|
+
st->spill_out_ordinals[dst] = row->ordinal;
|
|
1320
|
+
if (tf_batch_expose_row(st->spill_out_buf, dst) != TF_OK) return TF_ERROR;
|
|
1321
|
+
st->spill_kept_rows++;
|
|
1322
|
+
if (st->mode != 4 && st->mode != 5) st->spill_distinct_rows++;
|
|
1323
|
+
if (st->spill_out_buf->n_rows >= st->run_rows) return set_spill_write_output_run(st);
|
|
1324
|
+
return TF_OK;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
static int set_spill_process_lookup_batch(set_state *st, const tf_batch *batch, tf_side_channels *side) {
|
|
1328
|
+
const char *op = set_mode_name(st->mode);
|
|
1329
|
+
if (!st->spill_lookup_cols_ready) {
|
|
1330
|
+
int *right_cols = tf_callocarray_checked(st->n_key_cols ? st->n_key_cols : 1, sizeof(int));
|
|
1331
|
+
tf_batch *lookup_buf = NULL;
|
|
1332
|
+
if (!right_cols) return TF_ERROR;
|
|
1333
|
+
for (size_t k = 0; k < st->n_key_cols; k++) {
|
|
1334
|
+
int rc = tf_batch_col_index(batch, st->key_names[k]);
|
|
1335
|
+
if (rc < 0) {
|
|
1336
|
+
char msg[192];
|
|
1337
|
+
snprintf(msg, sizeof(msg), "%s: lookup column '%s' not found", op, st->key_names[k]);
|
|
1338
|
+
int err_rc = set_write_error(side, msg);
|
|
1339
|
+
free(right_cols);
|
|
1340
|
+
if (err_rc != TF_OK) return TF_ERROR;
|
|
1341
|
+
return TF_ERROR;
|
|
1342
|
+
}
|
|
1343
|
+
right_cols[k] = rc;
|
|
1344
|
+
}
|
|
1345
|
+
lookup_buf = set_spill_create_lookup_batch(st, st->run_rows);
|
|
1346
|
+
if (!lookup_buf) {
|
|
1347
|
+
free(right_cols);
|
|
1348
|
+
return TF_ERROR;
|
|
1349
|
+
}
|
|
1350
|
+
for (size_t k = 0; k < st->n_key_cols; k++) st->right_cols[k] = right_cols[k];
|
|
1351
|
+
free(right_cols);
|
|
1352
|
+
st->spill_lookup_buf = lookup_buf;
|
|
1353
|
+
st->spill_lookup_cols_ready = 1;
|
|
1354
|
+
}
|
|
1355
|
+
for (size_t r = 0; r < batch->n_rows; r++) {
|
|
1356
|
+
if (st->max_lookup_rows > 0 && st->spill_lookup_rows >= st->max_lookup_rows) {
|
|
1357
|
+
if (set_limit_error(side, op, "max_lookup_rows", st->max_lookup_rows,
|
|
1358
|
+
st->spill_lookup_rows + 1) != TF_OK)
|
|
1359
|
+
return TF_ERROR;
|
|
1360
|
+
return TF_ERROR;
|
|
1361
|
+
}
|
|
1362
|
+
st->spill_lookup_rows++;
|
|
1363
|
+
int type_mismatch = 0;
|
|
1364
|
+
for (size_t k = 0; k < st->n_key_cols; k++) {
|
|
1365
|
+
int rc = st->right_cols[k];
|
|
1366
|
+
if (rc < 0) return TF_ERROR;
|
|
1367
|
+
if (batch->col_types[rc] != st->spill_key_types[k]) {
|
|
1368
|
+
type_mismatch = 1;
|
|
1369
|
+
break;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
if (type_mismatch) continue;
|
|
1373
|
+
|
|
1374
|
+
size_t dst = st->spill_lookup_buf->n_rows;
|
|
1375
|
+
if (tf_batch_ensure_capacity(st->spill_lookup_buf, dst + 1) != TF_OK) return TF_ERROR;
|
|
1376
|
+
for (size_t k = 0; k < st->n_key_cols; k++) {
|
|
1377
|
+
int rc = st->right_cols[k];
|
|
1378
|
+
if (tf_batch_copy_cell(st->spill_lookup_buf, dst, k, batch, r, (size_t)rc) != TF_OK) {
|
|
1379
|
+
return TF_ERROR;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
if (set_ensure_ordinals(&st->spill_lookup_ordinals, &st->spill_lookup_ordinal_cap, dst + 1) != TF_OK) return TF_ERROR;
|
|
1383
|
+
st->spill_lookup_ordinals[dst] = st->spill_next_lookup_ordinal++;
|
|
1384
|
+
if (tf_batch_expose_row(st->spill_lookup_buf, dst) != TF_OK) return TF_ERROR;
|
|
1385
|
+
if (st->spill_lookup_buf->n_rows >= st->run_rows && set_spill_write_lookup_run(st) != TF_OK)
|
|
1386
|
+
return TF_ERROR;
|
|
1387
|
+
}
|
|
1388
|
+
return TF_OK;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
static int set_spill_load_lookup_runs(set_state *st, tf_side_channels *side) {
|
|
1392
|
+
if (st->spill_lookup_loaded) return TF_OK;
|
|
1393
|
+
st->spill_lookup_loaded = 1;
|
|
1394
|
+
FILE *f = tf_policy_fopen_read(st->file, st->validated_file);
|
|
1395
|
+
if (!f) {
|
|
1396
|
+
char msg[128];
|
|
1397
|
+
snprintf(msg, sizeof(msg), "%s: cannot open lookup file", set_mode_name(st->mode));
|
|
1398
|
+
if (set_write_error(side, msg) != TF_OK) return TF_ERROR;
|
|
1399
|
+
return TF_ERROR;
|
|
1400
|
+
}
|
|
1401
|
+
tf_decoder *dec = tf_csv_decoder_create(NULL);
|
|
1402
|
+
if (!dec) { fclose(f); return TF_ERROR; }
|
|
1403
|
+
size_t bytes_read = 0;
|
|
1404
|
+
int flushed = 0;
|
|
1405
|
+
int rc = TF_OK;
|
|
1406
|
+
while (!flushed) {
|
|
1407
|
+
uint8_t buf[64 * 1024];
|
|
1408
|
+
size_t n = fread(buf, 1, sizeof(buf), f);
|
|
1409
|
+
tf_batch **batches = NULL;
|
|
1410
|
+
size_t n_batches = 0;
|
|
1411
|
+
if (n > 0) {
|
|
1412
|
+
bytes_read += n;
|
|
1413
|
+
if (st->max_lookup_bytes > 0 && bytes_read > st->max_lookup_bytes) {
|
|
1414
|
+
if (set_limit_error(side, set_mode_name(st->mode), "max_lookup_bytes",
|
|
1415
|
+
st->max_lookup_bytes, bytes_read) != TF_OK)
|
|
1416
|
+
rc = TF_ERROR;
|
|
1417
|
+
rc = TF_ERROR;
|
|
1418
|
+
break;
|
|
1419
|
+
}
|
|
1420
|
+
rc = dec->decode(dec, buf, n, &batches, &n_batches, side);
|
|
1421
|
+
} else {
|
|
1422
|
+
if (ferror(f)) {
|
|
1423
|
+
char msg[160];
|
|
1424
|
+
snprintf(msg, sizeof(msg), "%s: failed reading lookup file", set_mode_name(st->mode));
|
|
1425
|
+
if (set_write_error(side, msg) != TF_OK) rc = TF_ERROR;
|
|
1426
|
+
rc = TF_ERROR;
|
|
1427
|
+
break;
|
|
1428
|
+
}
|
|
1429
|
+
flushed = 1;
|
|
1430
|
+
rc = dec->flush(dec, &batches, &n_batches, side);
|
|
1431
|
+
}
|
|
1432
|
+
if (rc != TF_OK) { tf_batch_array_free(batches, n_batches); break; }
|
|
1433
|
+
for (size_t i = 0; i < n_batches; i++) {
|
|
1434
|
+
if (set_spill_process_lookup_batch(st, batches[i], side) != TF_OK) rc = TF_ERROR;
|
|
1435
|
+
tf_batch_free(batches[i]);
|
|
1436
|
+
if (rc != TF_OK) {
|
|
1437
|
+
tf_batch_array_free_items(batches + i + 1, n_batches - i - 1);
|
|
1438
|
+
break;
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
free(batches);
|
|
1442
|
+
if (rc != TF_OK) break;
|
|
1443
|
+
}
|
|
1444
|
+
dec->destroy(dec);
|
|
1445
|
+
fclose(f);
|
|
1446
|
+
if (rc != TF_OK) return TF_ERROR;
|
|
1447
|
+
if (st->spill_lookup_buf && st->spill_lookup_buf->n_rows > 0)
|
|
1448
|
+
return set_spill_write_lookup_run(st);
|
|
1449
|
+
if (st->spill_lookup_buf) { tf_batch_free(st->spill_lookup_buf); st->spill_lookup_buf = NULL; }
|
|
1450
|
+
return TF_OK;
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
|
|
1454
|
+
static int set_spill_lookup_count_for_left_key(set_state *st, const set_spill_row *left_row,
|
|
1455
|
+
size_t *count, tf_side_channels *side) {
|
|
1456
|
+
*count = 0;
|
|
1457
|
+
for (;;) {
|
|
1458
|
+
int lookup_idx = set_spill_best_reader(st, 1);
|
|
1459
|
+
if (lookup_idx < 0) return TF_OK;
|
|
1460
|
+
set_spill_reader *lookup = &st->spill_lookup_readers[lookup_idx];
|
|
1461
|
+
int cmp = set_spill_compare_key_rows(st, &lookup->row, 1, left_row, 0);
|
|
1462
|
+
if (cmp < 0) {
|
|
1463
|
+
if (set_spill_count_lookup_key(st, &lookup->row, side) != TF_OK) return TF_ERROR;
|
|
1464
|
+
int adv = set_spill_reader_advance(st, lookup);
|
|
1465
|
+
if (adv < 0) return TF_ERROR;
|
|
1466
|
+
continue;
|
|
1467
|
+
}
|
|
1468
|
+
if (cmp > 0) return TF_OK;
|
|
1469
|
+
|
|
1470
|
+
while (lookup_idx >= 0) {
|
|
1471
|
+
lookup = &st->spill_lookup_readers[lookup_idx];
|
|
1472
|
+
cmp = set_spill_compare_key_rows(st, &lookup->row, 1, left_row, 0);
|
|
1473
|
+
if (cmp != 0) break;
|
|
1474
|
+
if (set_spill_count_lookup_key(st, &lookup->row, side) != TF_OK) return TF_ERROR;
|
|
1475
|
+
if (*count == SIZE_MAX) return TF_ERROR;
|
|
1476
|
+
(*count)++;
|
|
1477
|
+
int adv = set_spill_reader_advance(st, lookup);
|
|
1478
|
+
if (adv < 0) return TF_ERROR;
|
|
1479
|
+
lookup_idx = set_spill_best_reader(st, 1);
|
|
1480
|
+
}
|
|
1481
|
+
return TF_OK;
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
static int set_spill_drain_lookup_stats(set_state *st, tf_side_channels *side) {
|
|
1486
|
+
for (;;) {
|
|
1487
|
+
int lookup_idx = set_spill_best_reader(st, 1);
|
|
1488
|
+
if (lookup_idx < 0) break;
|
|
1489
|
+
set_spill_reader *lookup = &st->spill_lookup_readers[lookup_idx];
|
|
1490
|
+
if (set_spill_count_lookup_key(st, &lookup->row, side) != TF_OK) return TF_ERROR;
|
|
1491
|
+
int adv = set_spill_reader_advance(st, lookup);
|
|
1492
|
+
if (adv < 0) return TF_ERROR;
|
|
1493
|
+
}
|
|
1494
|
+
return TF_OK;
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
static int set_spill_produce_bag_output_runs(set_state *st, tf_side_channels *side) {
|
|
1498
|
+
if (st->spill_key_merge_done) return TF_OK;
|
|
1499
|
+
if (!st->spill_has_schema) { st->spill_key_merge_done = 1; return TF_OK; }
|
|
1500
|
+
if (st->spill_left_buf && st->spill_left_buf->n_rows > 0 && set_spill_write_left_run(st) != TF_OK) return TF_ERROR;
|
|
1501
|
+
if (st->spill_left_buf) { tf_batch_free(st->spill_left_buf); st->spill_left_buf = NULL; }
|
|
1502
|
+
free(st->spill_left_ordinals); st->spill_left_ordinals = NULL; st->spill_left_ordinal_cap = 0;
|
|
1503
|
+
|
|
1504
|
+
if (set_spill_load_lookup_runs(st, side) != TF_OK) return TF_ERROR;
|
|
1505
|
+
if (set_spill_open_readers(st, 0) != TF_OK) return TF_ERROR;
|
|
1506
|
+
if (set_spill_open_readers(st, 1) != TF_OK) return TF_ERROR;
|
|
1507
|
+
|
|
1508
|
+
for (;;) {
|
|
1509
|
+
int left_idx = set_spill_best_reader(st, 0);
|
|
1510
|
+
if (left_idx < 0) break;
|
|
1511
|
+
set_spill_reader *left = &st->spill_left_readers[left_idx];
|
|
1512
|
+
char *run_key = set_spill_build_key(st, &left->row, 0);
|
|
1513
|
+
if (!run_key) return TF_ERROR;
|
|
1514
|
+
size_t lookup_count = 0;
|
|
1515
|
+
if (set_spill_lookup_count_for_left_key(st, &left->row, &lookup_count, side) != TF_OK) {
|
|
1516
|
+
free(run_key);
|
|
1517
|
+
return TF_ERROR;
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
size_t left_count = 0;
|
|
1521
|
+
for (;;) {
|
|
1522
|
+
left_idx = set_spill_best_reader(st, 0);
|
|
1523
|
+
if (left_idx < 0) break;
|
|
1524
|
+
left = &st->spill_left_readers[left_idx];
|
|
1525
|
+
char *left_key = set_spill_build_key(st, &left->row, 0);
|
|
1526
|
+
if (!left_key) { free(run_key); return TF_ERROR; }
|
|
1527
|
+
int same_key = strcmp(left_key, run_key) == 0;
|
|
1528
|
+
free(left_key);
|
|
1529
|
+
if (!same_key) break;
|
|
1530
|
+
|
|
1531
|
+
if (left_count == SIZE_MAX) { free(run_key); return TF_ERROR; }
|
|
1532
|
+
left_count++;
|
|
1533
|
+
int keep = st->mode == 4 ? (left_count <= lookup_count) : (left_count > lookup_count);
|
|
1534
|
+
if (keep && set_spill_append_selected_row(st, &left->row) != TF_OK) {
|
|
1535
|
+
free(run_key);
|
|
1536
|
+
return TF_ERROR;
|
|
1537
|
+
}
|
|
1538
|
+
int adv_left = set_spill_reader_advance(st, left);
|
|
1539
|
+
if (adv_left < 0) { free(run_key); return TF_ERROR; }
|
|
1540
|
+
}
|
|
1541
|
+
free(run_key);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
if (set_spill_drain_lookup_stats(st, side) != TF_OK) return TF_ERROR;
|
|
1545
|
+
if (st->spill_out_buf && st->spill_out_buf->n_rows > 0 && set_spill_write_output_run(st) != TF_OK) return TF_ERROR;
|
|
1546
|
+
set_spill_close_readers(st, 0);
|
|
1547
|
+
set_spill_close_readers(st, 1);
|
|
1548
|
+
set_spill_remove_paths(&st->spill_left_run_paths, &st->spill_n_left_runs, &st->spill_cap_left_runs);
|
|
1549
|
+
set_spill_remove_paths(&st->spill_lookup_run_paths, &st->spill_n_lookup_runs, &st->spill_cap_lookup_runs);
|
|
1550
|
+
st->spill_key_merge_done = 1;
|
|
1551
|
+
return TF_OK;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
static int set_spill_produce_output_runs(set_state *st, tf_side_channels *side) {
|
|
1555
|
+
if (st->mode == 2) return set_spill_produce_union_output_runs(st, side);
|
|
1556
|
+
if (st->mode == 4 || st->mode == 5) return set_spill_produce_bag_output_runs(st, side);
|
|
1557
|
+
if (st->spill_key_merge_done) return TF_OK;
|
|
1558
|
+
if (!st->spill_has_schema) { st->spill_key_merge_done = 1; return TF_OK; }
|
|
1559
|
+
if (st->spill_left_buf && st->spill_left_buf->n_rows > 0 && set_spill_write_left_run(st) != TF_OK) return TF_ERROR;
|
|
1560
|
+
if (st->spill_left_buf) { tf_batch_free(st->spill_left_buf); st->spill_left_buf = NULL; }
|
|
1561
|
+
free(st->spill_left_ordinals); st->spill_left_ordinals = NULL; st->spill_left_ordinal_cap = 0;
|
|
1562
|
+
|
|
1563
|
+
if (set_spill_load_lookup_runs(st, side) != TF_OK) return TF_ERROR;
|
|
1564
|
+
if (set_spill_open_readers(st, 0) != TF_OK) return TF_ERROR;
|
|
1565
|
+
if (set_spill_open_readers(st, 1) != TF_OK) return TF_ERROR;
|
|
1566
|
+
|
|
1567
|
+
for (;;) {
|
|
1568
|
+
int left_idx = set_spill_best_reader(st, 0);
|
|
1569
|
+
if (left_idx < 0) break;
|
|
1570
|
+
set_spill_reader *left = &st->spill_left_readers[left_idx];
|
|
1571
|
+
char *left_key = set_spill_build_key(st, &left->row, 0);
|
|
1572
|
+
if (!left_key) return TF_ERROR;
|
|
1573
|
+
int duplicate_left = st->spill_last_left_key && strcmp(st->spill_last_left_key, left_key) == 0;
|
|
1574
|
+
if (duplicate_left) {
|
|
1575
|
+
free(left_key);
|
|
1576
|
+
int adv_left = set_spill_reader_advance(st, left);
|
|
1577
|
+
if (adv_left < 0) return TF_ERROR;
|
|
1578
|
+
continue;
|
|
1579
|
+
}
|
|
1580
|
+
free(st->spill_last_left_key);
|
|
1581
|
+
st->spill_last_left_key = left_key;
|
|
1582
|
+
left_key = NULL;
|
|
1583
|
+
|
|
1584
|
+
int lookup_idx = set_spill_best_reader(st, 1);
|
|
1585
|
+
while (lookup_idx >= 0) {
|
|
1586
|
+
set_spill_reader *lookup = &st->spill_lookup_readers[lookup_idx];
|
|
1587
|
+
if (set_spill_count_lookup_key(st, &lookup->row, side) != TF_OK) return TF_ERROR;
|
|
1588
|
+
int cmp = set_spill_compare_key_rows(st, &lookup->row, 1, &left->row, 0);
|
|
1589
|
+
if (cmp >= 0) break;
|
|
1590
|
+
int adv = set_spill_reader_advance(st, lookup);
|
|
1591
|
+
if (adv < 0) return TF_ERROR;
|
|
1592
|
+
lookup_idx = set_spill_best_reader(st, 1);
|
|
1593
|
+
}
|
|
1594
|
+
int in_lookup = 0;
|
|
1595
|
+
if (lookup_idx >= 0) {
|
|
1596
|
+
set_spill_reader *lookup = &st->spill_lookup_readers[lookup_idx];
|
|
1597
|
+
if (set_spill_count_lookup_key(st, &lookup->row, side) != TF_OK) return TF_ERROR;
|
|
1598
|
+
in_lookup = set_spill_compare_key_rows(st, &lookup->row, 1, &left->row, 0) == 0;
|
|
1599
|
+
}
|
|
1600
|
+
int keep = st->mode == 0 ? in_lookup : !in_lookup;
|
|
1601
|
+
if (keep && set_spill_append_selected_row(st, &left->row) != TF_OK) return TF_ERROR;
|
|
1602
|
+
int adv_left = set_spill_reader_advance(st, left);
|
|
1603
|
+
if (adv_left < 0) return TF_ERROR;
|
|
1604
|
+
}
|
|
1605
|
+
for (;;) {
|
|
1606
|
+
int lookup_idx = set_spill_best_reader(st, 1);
|
|
1607
|
+
if (lookup_idx < 0) break;
|
|
1608
|
+
set_spill_reader *lookup = &st->spill_lookup_readers[lookup_idx];
|
|
1609
|
+
if (set_spill_count_lookup_key(st, &lookup->row, side) != TF_OK) return TF_ERROR;
|
|
1610
|
+
int adv = set_spill_reader_advance(st, lookup);
|
|
1611
|
+
if (adv < 0) return TF_ERROR;
|
|
1612
|
+
}
|
|
1613
|
+
if (st->spill_out_buf && st->spill_out_buf->n_rows > 0 && set_spill_write_output_run(st) != TF_OK) return TF_ERROR;
|
|
1614
|
+
set_spill_close_readers(st, 0);
|
|
1615
|
+
set_spill_close_readers(st, 1);
|
|
1616
|
+
set_spill_remove_paths(&st->spill_left_run_paths, &st->spill_n_left_runs, &st->spill_cap_left_runs);
|
|
1617
|
+
set_spill_remove_paths(&st->spill_lookup_run_paths, &st->spill_n_lookup_runs, &st->spill_cap_lookup_runs);
|
|
1618
|
+
st->spill_key_merge_done = 1;
|
|
1619
|
+
return TF_OK;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
static int set_spill_begin_output_merge(set_state *st) {
|
|
1623
|
+
if (st->spill_output_merge_started) return TF_OK;
|
|
1624
|
+
st->spill_output_merge_started = 1;
|
|
1625
|
+
if (st->spill_out_buf) { tf_batch_free(st->spill_out_buf); st->spill_out_buf = NULL; }
|
|
1626
|
+
free(st->spill_out_ordinals); st->spill_out_ordinals = NULL; st->spill_out_ordinal_cap = 0;
|
|
1627
|
+
if (st->spill_n_out_runs == 0) {
|
|
1628
|
+
tf_spill_cleanup(st->spill);
|
|
1629
|
+
st->spill = NULL;
|
|
1630
|
+
st->spill_output_merge_done = 1;
|
|
1631
|
+
return TF_OK;
|
|
1632
|
+
}
|
|
1633
|
+
return set_spill_open_readers(st, 2);
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
static int set_spill_output_next_batch(set_state *st, tf_batch **out, tf_side_channels *side) {
|
|
1637
|
+
*out = NULL;
|
|
1638
|
+
if (set_spill_produce_output_runs(st, side) != TF_OK) return TF_ERROR;
|
|
1639
|
+
if (set_spill_begin_output_merge(st) != TF_OK) return TF_ERROR;
|
|
1640
|
+
if (st->spill_output_merge_done) return TF_OK;
|
|
1641
|
+
tf_batch *ob = set_spill_create_left_batch(st, st->output_batch_rows);
|
|
1642
|
+
if (!ob) return TF_ERROR;
|
|
1643
|
+
while (ob->n_rows < st->output_batch_rows) {
|
|
1644
|
+
int best = set_spill_best_reader(st, 2);
|
|
1645
|
+
if (best < 0) break;
|
|
1646
|
+
set_spill_reader *reader = &st->spill_out_readers[best];
|
|
1647
|
+
size_t out_row = ob->n_rows;
|
|
1648
|
+
if (set_spill_row_to_batch(st, ob, out_row, &reader->row) != TF_OK) { tf_batch_free(ob); return TF_ERROR; }
|
|
1649
|
+
if (tf_batch_expose_row(ob, out_row) != TF_OK) { tf_batch_free(ob); return TF_ERROR; }
|
|
1650
|
+
int rc = set_spill_reader_advance(st, reader);
|
|
1651
|
+
if (rc < 0) { tf_batch_free(ob); return TF_ERROR; }
|
|
1652
|
+
}
|
|
1653
|
+
if (ob->n_rows == 0) {
|
|
1654
|
+
tf_batch_free(ob);
|
|
1655
|
+
set_spill_close_readers(st, 2);
|
|
1656
|
+
set_spill_remove_paths(&st->spill_out_run_paths, &st->spill_n_out_runs, &st->spill_cap_out_runs);
|
|
1657
|
+
tf_spill_cleanup(st->spill);
|
|
1658
|
+
st->spill = NULL;
|
|
1659
|
+
st->spill_output_merge_done = 1;
|
|
1660
|
+
return TF_OK;
|
|
1661
|
+
}
|
|
1662
|
+
st->spill_output_batches++;
|
|
1663
|
+
st->spill_output_rows += ob->n_rows;
|
|
1664
|
+
*out = ob;
|
|
1665
|
+
return TF_OK;
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
static int set_process_spill(tf_step *self, tf_batch *in, tf_batch **out, tf_side_channels *side) {
|
|
1669
|
+
set_state *st = self->state;
|
|
1670
|
+
*out = NULL;
|
|
1671
|
+
if (set_spill_init_schema(st, in, side) != TF_OK) return TF_ERROR;
|
|
1672
|
+
for (size_t r = 0; r < in->n_rows; r++) {
|
|
1673
|
+
size_t dst = st->spill_left_buf->n_rows;
|
|
1674
|
+
if (tf_batch_copy_row(st->spill_left_buf, dst, in, r) != TF_OK) return TF_ERROR;
|
|
1675
|
+
if (set_ensure_ordinals(&st->spill_left_ordinals, &st->spill_left_ordinal_cap, dst + 1) != TF_OK) return TF_ERROR;
|
|
1676
|
+
st->spill_left_ordinals[dst] = st->spill_next_left_ordinal++;
|
|
1677
|
+
if (tf_batch_expose_row(st->spill_left_buf, dst) != TF_OK) return TF_ERROR;
|
|
1678
|
+
if (st->spill_left_buf->n_rows >= st->run_rows && set_spill_write_left_run(st) != TF_OK) return TF_ERROR;
|
|
1679
|
+
}
|
|
1680
|
+
return TF_OK;
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
static void sorted_set_free_pending_batches(set_state *st) {
|
|
1684
|
+
if (!st || !st->sorted_batches) return;
|
|
1685
|
+
for (size_t i = st->sorted_batch_index; i < st->sorted_n_batches; i++) {
|
|
1686
|
+
if (st->sorted_batches[i]) tf_batch_free(st->sorted_batches[i]);
|
|
1687
|
+
}
|
|
1688
|
+
free(st->sorted_batches);
|
|
1689
|
+
st->sorted_batches = NULL;
|
|
1690
|
+
st->sorted_n_batches = 0;
|
|
1691
|
+
st->sorted_batch_index = 0;
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
static void sorted_set_close(set_state *st) {
|
|
1695
|
+
if (!st) return;
|
|
1696
|
+
if (st->sorted_current) {
|
|
1697
|
+
tf_batch_free(st->sorted_current);
|
|
1698
|
+
st->sorted_current = NULL;
|
|
1699
|
+
}
|
|
1700
|
+
sorted_set_free_pending_batches(st);
|
|
1701
|
+
if (st->sorted_decoder) {
|
|
1702
|
+
st->sorted_decoder->destroy(st->sorted_decoder);
|
|
1703
|
+
st->sorted_decoder = NULL;
|
|
1704
|
+
}
|
|
1705
|
+
if (st->sorted_file) {
|
|
1706
|
+
fclose(st->sorted_file);
|
|
1707
|
+
st->sorted_file = NULL;
|
|
1708
|
+
}
|
|
1709
|
+
st->sorted_have_row = 0;
|
|
1710
|
+
st->sorted_exhausted = 1;
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
static int sorted_set_capture_schema(set_state *st, const tf_batch *b,
|
|
1714
|
+
tf_side_channels *side) {
|
|
1715
|
+
if (st->sorted_right_cols_ready) return TF_OK;
|
|
1716
|
+
const char *op = set_mode_name(st->mode);
|
|
1717
|
+
int *right_cols = tf_callocarray_checked(st->n_key_cols ? st->n_key_cols : 1, sizeof(int));
|
|
1718
|
+
if (!right_cols) return TF_ERROR;
|
|
1719
|
+
for (size_t i = 0; i < st->n_key_cols; i++) {
|
|
1720
|
+
int rc = tf_batch_col_index(b, st->key_names[i]);
|
|
1721
|
+
if (rc < 0) {
|
|
1722
|
+
char msg[192];
|
|
1723
|
+
snprintf(msg, sizeof(msg), "%s: lookup column '%s' not found", op, st->key_names[i]);
|
|
1724
|
+
int err_rc = set_write_error(side, msg);
|
|
1725
|
+
free(right_cols);
|
|
1726
|
+
if (err_rc != TF_OK) return TF_ERROR;
|
|
1727
|
+
return TF_ERROR;
|
|
1728
|
+
}
|
|
1729
|
+
right_cols[i] = rc;
|
|
1730
|
+
}
|
|
1731
|
+
for (size_t i = 0; i < st->n_key_cols; i++) st->right_cols[i] = right_cols[i];
|
|
1732
|
+
free(right_cols);
|
|
1733
|
+
st->sorted_right_cols_ready = 1;
|
|
1734
|
+
return TF_OK;
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
static int sorted_set_open(set_state *st, tf_side_channels *side) {
|
|
1738
|
+
if (!st || st->sorted_file || st->sorted_decoder) return TF_OK;
|
|
1739
|
+
st->sorted_file = tf_policy_fopen_read(st->file, st->validated_file);
|
|
1740
|
+
if (!st->sorted_file) {
|
|
1741
|
+
if (set_write_error(side, st->mode == 0 ? "intersect: cannot open lookup file" : "setdiff: cannot open lookup file") != TF_OK)
|
|
1742
|
+
return TF_ERROR;
|
|
1743
|
+
return TF_ERROR;
|
|
1744
|
+
}
|
|
1745
|
+
st->sorted_decoder = tf_csv_decoder_create(NULL);
|
|
1746
|
+
if (!st->sorted_decoder) return TF_ERROR;
|
|
1747
|
+
return TF_OK;
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
static int sorted_set_next_batch(set_state *st, tf_side_channels *side) {
|
|
1751
|
+
if (st->sorted_current) {
|
|
1752
|
+
tf_batch_free(st->sorted_current);
|
|
1753
|
+
st->sorted_current = NULL;
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
for (;;) {
|
|
1757
|
+
if (st->sorted_batches && st->sorted_batch_index < st->sorted_n_batches) {
|
|
1758
|
+
st->sorted_current = st->sorted_batches[st->sorted_batch_index++];
|
|
1759
|
+
if (st->sorted_current && sorted_set_capture_schema(st, st->sorted_current, side) != TF_OK)
|
|
1760
|
+
return TF_ERROR;
|
|
1761
|
+
st->sorted_row = 0;
|
|
1762
|
+
if (st->sorted_current && st->sorted_current->n_rows > 0) return TF_OK;
|
|
1763
|
+
if (st->sorted_current) {
|
|
1764
|
+
tf_batch_free(st->sorted_current);
|
|
1765
|
+
st->sorted_current = NULL;
|
|
1766
|
+
}
|
|
1767
|
+
continue;
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
sorted_set_free_pending_batches(st);
|
|
1771
|
+
if (st->sorted_flushed) {
|
|
1772
|
+
st->sorted_exhausted = 1;
|
|
1773
|
+
return TF_OK;
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
if (sorted_set_open(st, side) != TF_OK) return TF_ERROR;
|
|
1777
|
+
uint8_t buf[64 * 1024];
|
|
1778
|
+
size_t n = fread(buf, 1, sizeof(buf), st->sorted_file);
|
|
1779
|
+
tf_batch **batches = NULL;
|
|
1780
|
+
size_t n_batches = 0;
|
|
1781
|
+
int rc;
|
|
1782
|
+
if (n > 0) {
|
|
1783
|
+
rc = st->sorted_decoder->decode(st->sorted_decoder, buf, n, &batches, &n_batches, side);
|
|
1784
|
+
} else {
|
|
1785
|
+
if (ferror(st->sorted_file)) {
|
|
1786
|
+
if (set_write_error(side, st->mode == 0 ? "intersect: failed reading lookup file" : "setdiff: failed reading lookup file") != TF_OK)
|
|
1787
|
+
return TF_ERROR;
|
|
1788
|
+
return TF_ERROR;
|
|
1789
|
+
}
|
|
1790
|
+
st->sorted_flushed = 1;
|
|
1791
|
+
rc = st->sorted_decoder->flush(st->sorted_decoder, &batches, &n_batches, side);
|
|
1792
|
+
}
|
|
1793
|
+
if (rc != TF_OK) {
|
|
1794
|
+
tf_batch_array_free(batches, n_batches);
|
|
1795
|
+
return TF_ERROR;
|
|
1796
|
+
}
|
|
1797
|
+
st->sorted_batches = batches;
|
|
1798
|
+
st->sorted_n_batches = n_batches;
|
|
1799
|
+
st->sorted_batch_index = 0;
|
|
1800
|
+
if (n_batches == 0) {
|
|
1801
|
+
free(batches);
|
|
1802
|
+
st->sorted_batches = NULL;
|
|
1803
|
+
if (st->sorted_flushed) {
|
|
1804
|
+
st->sorted_exhausted = 1;
|
|
1805
|
+
return TF_OK;
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
static int sorted_set_load_row_if_needed(set_state *st, tf_side_channels *side) {
|
|
1812
|
+
if (st->sorted_have_row) return TF_OK;
|
|
1813
|
+
for (;;) {
|
|
1814
|
+
if (!st->sorted_current || st->sorted_row >= st->sorted_current->n_rows) {
|
|
1815
|
+
if (sorted_set_next_batch(st, side) != TF_OK) return TF_ERROR;
|
|
1816
|
+
if (st->sorted_exhausted) return TF_OK;
|
|
1817
|
+
continue;
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
set_key_tuple cur = {0};
|
|
1821
|
+
if (set_key_tuple_set_from_row(&cur, st->sorted_current, st->sorted_row,
|
|
1822
|
+
st->right_cols, st->n_key_cols) != TF_OK) {
|
|
1823
|
+
set_key_tuple_clear(&cur);
|
|
1824
|
+
return TF_ERROR;
|
|
1825
|
+
}
|
|
1826
|
+
if (st->prev_lookup_key.valid) {
|
|
1827
|
+
int cmp = 0;
|
|
1828
|
+
if (set_key_tuple_compare(&st->prev_lookup_key, &cur, &cmp) != TF_OK) {
|
|
1829
|
+
set_key_tuple_clear(&cur);
|
|
1830
|
+
char msg[192];
|
|
1831
|
+
snprintf(msg, sizeof(msg), "%s: lookup key types changed", set_mode_name(st->mode));
|
|
1832
|
+
if (set_write_error(side, msg) != TF_OK) return TF_ERROR;
|
|
1833
|
+
return TF_ERROR;
|
|
1834
|
+
}
|
|
1835
|
+
if (cmp > 0) {
|
|
1836
|
+
set_key_tuple_clear(&cur);
|
|
1837
|
+
char msg[192];
|
|
1838
|
+
snprintf(msg, sizeof(msg), "%s: lookup side is not sorted by key", set_mode_name(st->mode));
|
|
1839
|
+
if (set_write_error(side, msg) != TF_OK) return TF_ERROR;
|
|
1840
|
+
return TF_ERROR;
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
if (set_key_tuple_copy(&st->prev_lookup_key, &cur) != TF_OK) {
|
|
1844
|
+
set_key_tuple_clear(&cur);
|
|
1845
|
+
return TF_ERROR;
|
|
1846
|
+
}
|
|
1847
|
+
set_key_tuple_clear(&cur);
|
|
1848
|
+
st->sorted_have_row = 1;
|
|
1849
|
+
return TF_OK;
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
static void sorted_set_consume_row(set_state *st) {
|
|
1854
|
+
if (!st || !st->sorted_have_row) return;
|
|
1855
|
+
st->sorted_row++;
|
|
1856
|
+
st->sorted_have_row = 0;
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
static int sorted_set_load_next_lookup_key(set_state *st, tf_side_channels *side) {
|
|
1860
|
+
const char *op = set_mode_name(st->mode);
|
|
1861
|
+
set_key_tuple_clear(&st->current_lookup_key);
|
|
1862
|
+
st->sorted_current_lookup_count = 0;
|
|
1863
|
+
if (sorted_set_load_row_if_needed(st, side) != TF_OK) return TF_ERROR;
|
|
1864
|
+
if (st->sorted_exhausted && !st->sorted_have_row) return TF_OK;
|
|
1865
|
+
|
|
1866
|
+
if (set_key_tuple_set_from_row(&st->current_lookup_key, st->sorted_current,
|
|
1867
|
+
st->sorted_row, st->right_cols, st->n_key_cols) != TF_OK)
|
|
1868
|
+
return TF_ERROR;
|
|
1869
|
+
|
|
1870
|
+
while (st->sorted_have_row) {
|
|
1871
|
+
set_key_tuple row_key = {0};
|
|
1872
|
+
if (set_key_tuple_set_from_row(&row_key, st->sorted_current, st->sorted_row,
|
|
1873
|
+
st->right_cols, st->n_key_cols) != TF_OK) {
|
|
1874
|
+
set_key_tuple_clear(&row_key);
|
|
1875
|
+
return TF_ERROR;
|
|
1876
|
+
}
|
|
1877
|
+
int cmp = 0;
|
|
1878
|
+
if (set_key_tuple_compare(&st->current_lookup_key, &row_key, &cmp) != TF_OK) {
|
|
1879
|
+
set_key_tuple_clear(&row_key);
|
|
1880
|
+
char msg[192];
|
|
1881
|
+
snprintf(msg, sizeof(msg), "%s: lookup key types changed", op);
|
|
1882
|
+
if (set_write_error(side, msg) != TF_OK) return TF_ERROR;
|
|
1883
|
+
return TF_ERROR;
|
|
1884
|
+
}
|
|
1885
|
+
set_key_tuple_clear(&row_key);
|
|
1886
|
+
if (cmp != 0) break;
|
|
1887
|
+
if (st->sorted_current_lookup_count == SIZE_MAX) return TF_ERROR;
|
|
1888
|
+
st->sorted_current_lookup_count++;
|
|
1889
|
+
sorted_set_consume_row(st);
|
|
1890
|
+
if (sorted_set_load_row_if_needed(st, side) != TF_OK) return TF_ERROR;
|
|
1891
|
+
if (st->sorted_exhausted && !st->sorted_have_row) break;
|
|
1892
|
+
}
|
|
1893
|
+
return TF_OK;
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
static int sorted_set_ensure_lookup_at_least(set_state *st, const set_key_tuple *left_key,
|
|
1897
|
+
tf_side_channels *side) {
|
|
1898
|
+
if (!st->current_lookup_key.valid && !st->sorted_exhausted) {
|
|
1899
|
+
if (sorted_set_load_next_lookup_key(st, side) != TF_OK) return TF_ERROR;
|
|
1900
|
+
}
|
|
1901
|
+
while (st->current_lookup_key.valid) {
|
|
1902
|
+
int cmp = 0;
|
|
1903
|
+
if (set_key_tuple_compare(&st->current_lookup_key, left_key, &cmp) != TF_OK) {
|
|
1904
|
+
char msg[192];
|
|
1905
|
+
snprintf(msg, sizeof(msg), "%s: input and lookup key types differ", set_mode_name(st->mode));
|
|
1906
|
+
if (set_write_error(side, msg) != TF_OK) return TF_ERROR;
|
|
1907
|
+
return TF_ERROR;
|
|
1908
|
+
}
|
|
1909
|
+
if (cmp >= 0) return TF_OK;
|
|
1910
|
+
if (sorted_set_load_next_lookup_key(st, side) != TF_OK) return TF_ERROR;
|
|
1911
|
+
}
|
|
1912
|
+
return TF_OK;
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
static int set_process_sorted(tf_step *self, tf_batch *in, tf_batch **out,
|
|
1916
|
+
tf_side_channels *side) {
|
|
1917
|
+
set_state *st = self->state;
|
|
1918
|
+
const char *op = set_mode_name(st->mode);
|
|
1919
|
+
*out = NULL;
|
|
1920
|
+
if (!st->loaded) {
|
|
1921
|
+
if (prepare_sorted_key_columns(st, in, side) != TF_OK) return TF_ERROR;
|
|
1922
|
+
st->loaded = 1;
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
tf_batch *ob = tf_batch_create(in->n_cols, in->n_rows > 0 ? in->n_rows : 16);
|
|
1926
|
+
if (!ob) return TF_ERROR;
|
|
1927
|
+
for (size_t c = 0; c < in->n_cols; c++) {
|
|
1928
|
+
if (tf_batch_set_schema(ob, c, in->col_names[c], in->col_types[c]) != TF_OK) {
|
|
1929
|
+
tf_batch_free(ob);
|
|
1930
|
+
return TF_ERROR;
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
size_t out_row = 0;
|
|
1935
|
+
for (size_t r = 0; r < in->n_rows; r++) {
|
|
1936
|
+
set_key_tuple left_key = {0};
|
|
1937
|
+
if (set_key_tuple_set_from_row(&left_key, in, r, st->left_cols, st->n_key_cols) != TF_OK) {
|
|
1938
|
+
set_key_tuple_clear(&left_key);
|
|
1939
|
+
tf_batch_free(ob);
|
|
1940
|
+
return TF_ERROR;
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
int duplicate_left = 0;
|
|
1944
|
+
if (st->prev_left_key.valid) {
|
|
1945
|
+
int cmp = 0;
|
|
1946
|
+
if (set_key_tuple_compare(&st->prev_left_key, &left_key, &cmp) != TF_OK) {
|
|
1947
|
+
set_key_tuple_clear(&left_key);
|
|
1948
|
+
tf_batch_free(ob);
|
|
1949
|
+
char msg[192];
|
|
1950
|
+
snprintf(msg, sizeof(msg), "%s: left key types changed", op);
|
|
1951
|
+
if (set_write_error(side, msg) != TF_OK) return TF_ERROR;
|
|
1952
|
+
return TF_ERROR;
|
|
1953
|
+
}
|
|
1954
|
+
if (cmp > 0) {
|
|
1955
|
+
set_key_tuple_clear(&left_key);
|
|
1956
|
+
tf_batch_free(ob);
|
|
1957
|
+
char msg[192];
|
|
1958
|
+
snprintf(msg, sizeof(msg), "%s: left side is not sorted by key", op);
|
|
1959
|
+
if (set_write_error(side, msg) != TF_OK) return TF_ERROR;
|
|
1960
|
+
return TF_ERROR;
|
|
1961
|
+
}
|
|
1962
|
+
duplicate_left = (cmp == 0);
|
|
1963
|
+
}
|
|
1964
|
+
if (set_key_tuple_copy(&st->prev_left_key, &left_key) != TF_OK) {
|
|
1965
|
+
set_key_tuple_clear(&left_key);
|
|
1966
|
+
tf_batch_free(ob);
|
|
1967
|
+
return TF_ERROR;
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
if (st->mode == 4 || st->mode == 5) {
|
|
1971
|
+
if (duplicate_left) {
|
|
1972
|
+
if (st->sorted_left_run_count == SIZE_MAX) {
|
|
1973
|
+
set_key_tuple_clear(&left_key);
|
|
1974
|
+
tf_batch_free(ob);
|
|
1975
|
+
return TF_ERROR;
|
|
1976
|
+
}
|
|
1977
|
+
st->sorted_left_run_count++;
|
|
1978
|
+
} else {
|
|
1979
|
+
st->sorted_left_run_count = 1;
|
|
1980
|
+
}
|
|
1981
|
+
} else if (duplicate_left) {
|
|
1982
|
+
set_key_tuple_clear(&left_key);
|
|
1983
|
+
continue;
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
if (sorted_set_ensure_lookup_at_least(st, &left_key, side) != TF_OK) {
|
|
1987
|
+
set_key_tuple_clear(&left_key);
|
|
1988
|
+
tf_batch_free(ob);
|
|
1989
|
+
return TF_ERROR;
|
|
1990
|
+
}
|
|
1991
|
+
int in_lookup = 0;
|
|
1992
|
+
size_t lookup_count = 0;
|
|
1993
|
+
if (st->current_lookup_key.valid) {
|
|
1994
|
+
int cmp = 0;
|
|
1995
|
+
if (set_key_tuple_compare(&st->current_lookup_key, &left_key, &cmp) != TF_OK) {
|
|
1996
|
+
set_key_tuple_clear(&left_key);
|
|
1997
|
+
tf_batch_free(ob);
|
|
1998
|
+
char msg[192];
|
|
1999
|
+
snprintf(msg, sizeof(msg), "%s: input and lookup key types differ", op);
|
|
2000
|
+
if (set_write_error(side, msg) != TF_OK) return TF_ERROR;
|
|
2001
|
+
return TF_ERROR;
|
|
2002
|
+
}
|
|
2003
|
+
in_lookup = (cmp == 0);
|
|
2004
|
+
if (in_lookup) lookup_count = st->sorted_current_lookup_count;
|
|
2005
|
+
}
|
|
2006
|
+
int keep = 0;
|
|
2007
|
+
if (st->mode == 4) keep = st->sorted_left_run_count <= lookup_count;
|
|
2008
|
+
else if (st->mode == 5) keep = st->sorted_left_run_count > lookup_count;
|
|
2009
|
+
else keep = st->mode == 0 ? in_lookup : !in_lookup;
|
|
2010
|
+
if (keep) {
|
|
2011
|
+
if (tf_batch_copy_row(ob, out_row, in, r) != TF_OK) {
|
|
2012
|
+
set_key_tuple_clear(&left_key);
|
|
2013
|
+
tf_batch_free(ob);
|
|
2014
|
+
return TF_ERROR;
|
|
2015
|
+
}
|
|
2016
|
+
if (tf_batch_expose_row(ob, out_row) != TF_OK) {
|
|
2017
|
+
set_key_tuple_clear(&left_key);
|
|
2018
|
+
tf_batch_free(ob);
|
|
2019
|
+
return TF_ERROR;
|
|
2020
|
+
}
|
|
2021
|
+
out_row++;
|
|
2022
|
+
}
|
|
2023
|
+
set_key_tuple_clear(&left_key);
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
if (out_row > 0) *out = ob;
|
|
2027
|
+
else tf_batch_free(ob);
|
|
2028
|
+
return TF_OK;
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
static int read_lookup_file(set_state *st, uint8_t **out, size_t *out_len,
|
|
2032
|
+
tf_side_channels *side) {
|
|
2033
|
+
FILE *f = tf_policy_fopen_read(st->file, st->validated_file);
|
|
2034
|
+
if (!f) return TF_ERROR;
|
|
2035
|
+
if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return TF_ERROR; }
|
|
2036
|
+
long fsize = ftell(f);
|
|
2037
|
+
if (fseek(f, 0, SEEK_SET) != 0) { fclose(f); return TF_ERROR; }
|
|
2038
|
+
if (fsize < 0) { fclose(f); return TF_ERROR; }
|
|
2039
|
+
if (st->max_lookup_bytes > 0 && (size_t)fsize > st->max_lookup_bytes) {
|
|
2040
|
+
fclose(f);
|
|
2041
|
+
if (set_limit_error(side, set_mode_name(st->mode),
|
|
2042
|
+
"max_lookup_bytes", st->max_lookup_bytes,
|
|
2043
|
+
(size_t)fsize) != TF_OK)
|
|
2044
|
+
return TF_ERROR;
|
|
2045
|
+
return TF_ERROR;
|
|
2046
|
+
}
|
|
2047
|
+
uint8_t *data = NULL;
|
|
2048
|
+
if (fsize > 0) {
|
|
2049
|
+
data = malloc((size_t)fsize);
|
|
2050
|
+
if (!data) { fclose(f); return TF_ERROR; }
|
|
2051
|
+
size_t nread = fread(data, 1, (size_t)fsize, f);
|
|
2052
|
+
if (nread != (size_t)fsize) { free(data); fclose(f); return TF_ERROR; }
|
|
2053
|
+
}
|
|
2054
|
+
fclose(f);
|
|
2055
|
+
*out = data;
|
|
2056
|
+
*out_len = (size_t)fsize;
|
|
2057
|
+
return TF_OK;
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
static int prepare_key_columns(set_state *st, const tf_batch *left,
|
|
2061
|
+
const tf_batch *right_schema,
|
|
2062
|
+
const uint8_t *raw, size_t raw_len,
|
|
2063
|
+
tf_side_channels *side) {
|
|
2064
|
+
const char *op = set_mode_name(st->mode);
|
|
2065
|
+
size_t n = st->n_columns > 0 ? st->n_columns : left->n_cols;
|
|
2066
|
+
int *left_cols = tf_callocarray_checked(n ? n : 1, sizeof(int));
|
|
2067
|
+
int *right_cols = tf_callocarray_checked(n ? n : 1, sizeof(int));
|
|
2068
|
+
if (!left_cols || !right_cols) goto fail;
|
|
2069
|
+
for (size_t i = 0; i < n; i++) {
|
|
2070
|
+
const char *name = st->n_columns > 0 ? st->columns[i] : left->col_names[i];
|
|
2071
|
+
int lc = tf_batch_col_index(left, name);
|
|
2072
|
+
if (lc < 0) {
|
|
2073
|
+
char msg[192];
|
|
2074
|
+
snprintf(msg, sizeof(msg), "%s: input column '%s' not found", op, name);
|
|
2075
|
+
if (set_write_error(side, msg) != TF_OK) goto fail;
|
|
2076
|
+
goto fail;
|
|
2077
|
+
}
|
|
2078
|
+
left_cols[i] = lc;
|
|
2079
|
+
if (right_schema) {
|
|
2080
|
+
int rc = tf_batch_col_index(right_schema, name);
|
|
2081
|
+
if (rc < 0) {
|
|
2082
|
+
char msg[192];
|
|
2083
|
+
snprintf(msg, sizeof(msg), "%s: lookup column '%s' not found", op, name);
|
|
2084
|
+
if (set_write_error(side, msg) != TF_OK) goto fail;
|
|
2085
|
+
goto fail;
|
|
2086
|
+
}
|
|
2087
|
+
right_cols[i] = rc;
|
|
2088
|
+
} else if (!csv_header_has_column(raw, raw_len, name)) {
|
|
2089
|
+
char msg[192];
|
|
2090
|
+
snprintf(msg, sizeof(msg), "%s: lookup column '%s' not found", op, name);
|
|
2091
|
+
if (set_write_error(side, msg) != TF_OK) goto fail;
|
|
2092
|
+
goto fail;
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
st->left_cols = left_cols;
|
|
2096
|
+
st->right_cols = right_cols;
|
|
2097
|
+
st->n_key_cols = n;
|
|
2098
|
+
return TF_OK;
|
|
2099
|
+
|
|
2100
|
+
fail:
|
|
2101
|
+
free(left_cols);
|
|
2102
|
+
free(right_cols);
|
|
2103
|
+
return TF_ERROR;
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
static int load_lookup(set_state *st, const tf_batch *left, tf_side_channels *side) {
|
|
2107
|
+
uint8_t *data = NULL;
|
|
2108
|
+
size_t data_len = 0;
|
|
2109
|
+
tf_decoder *dec = NULL;
|
|
2110
|
+
tf_batch **batches = NULL, **flush_batches = NULL, **all_batches = NULL;
|
|
2111
|
+
size_t n_batches = 0, n_flush = 0, total_batches = 0, total_rows = 0;
|
|
2112
|
+
const char *op = set_mode_name(st->mode);
|
|
2113
|
+
|
|
2114
|
+
if (read_lookup_file(st, &data, &data_len, side) != TF_OK) goto fail;
|
|
2115
|
+
dec = tf_csv_decoder_create(NULL);
|
|
2116
|
+
if (!dec) goto fail;
|
|
2117
|
+
if (data_len > 0 && dec->decode(dec, data, data_len, &batches, &n_batches, NULL) != TF_OK) goto fail;
|
|
2118
|
+
if (dec->flush(dec, &flush_batches, &n_flush, NULL) != TF_OK) goto fail;
|
|
2119
|
+
|
|
2120
|
+
total_batches = n_batches + n_flush;
|
|
2121
|
+
all_batches = tf_mallocarray_checked(total_batches ? total_batches : 1, sizeof(tf_batch *));
|
|
2122
|
+
if (!all_batches) goto fail;
|
|
2123
|
+
for (size_t i = 0; i < n_batches; i++) {
|
|
2124
|
+
all_batches[i] = batches[i];
|
|
2125
|
+
total_rows += batches[i]->n_rows;
|
|
2126
|
+
if (st->max_lookup_rows > 0 && total_rows > st->max_lookup_rows) {
|
|
2127
|
+
if (set_limit_error(side, op, "max_lookup_rows", st->max_lookup_rows,
|
|
2128
|
+
total_rows) != TF_OK)
|
|
2129
|
+
goto fail;
|
|
2130
|
+
goto fail;
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
for (size_t i = 0; i < n_flush; i++) {
|
|
2134
|
+
all_batches[n_batches + i] = flush_batches[i];
|
|
2135
|
+
total_rows += flush_batches[i]->n_rows;
|
|
2136
|
+
if (st->max_lookup_rows > 0 && total_rows > st->max_lookup_rows) {
|
|
2137
|
+
if (set_limit_error(side, op, "max_lookup_rows", st->max_lookup_rows,
|
|
2138
|
+
total_rows) != TF_OK)
|
|
2139
|
+
goto fail;
|
|
2140
|
+
goto fail;
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
tf_batch *schema = total_batches > 0 ? all_batches[0] : NULL;
|
|
2145
|
+
if (prepare_key_columns(st, left, schema, data, data_len, side) != TF_OK) goto fail;
|
|
2146
|
+
if (set_map_init(&st->lookup, total_rows ? total_rows : 1) != TF_OK) goto fail;
|
|
2147
|
+
if (st->mode == 0 || st->mode == 1) {
|
|
2148
|
+
if (set_map_init(&st->emitted, st->max_output_keys ? st->max_output_keys : 64) != TF_OK) goto fail;
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
for (size_t b = 0; b < total_batches; b++) {
|
|
2152
|
+
for (size_t r = 0; r < all_batches[b]->n_rows; r++) {
|
|
2153
|
+
char *key = format_set_key(all_batches[b], r, st->right_cols, st->n_key_cols);
|
|
2154
|
+
if (!key) goto fail;
|
|
2155
|
+
int inserted = 0;
|
|
2156
|
+
int rc = (st->mode == 4 || st->mode == 5)
|
|
2157
|
+
? set_map_increment_owned(&st->lookup, key, st->max_lookup_keys, op, "max_lookup_keys", side, &inserted)
|
|
2158
|
+
: set_map_insert_owned(&st->lookup, key, st->max_lookup_keys, op, "max_lookup_keys", side, &inserted);
|
|
2159
|
+
if (rc != TF_OK) goto fail;
|
|
2160
|
+
if (inserted && set_check_state_bytes(st, side) != TF_OK) goto fail;
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
for (size_t i = 0; i < total_batches; i++) all_batches[i] = NULL;
|
|
2165
|
+
tf_batch_array_free(batches, n_batches);
|
|
2166
|
+
tf_batch_array_free(flush_batches, n_flush);
|
|
2167
|
+
free(all_batches);
|
|
2168
|
+
dec->destroy(dec);
|
|
2169
|
+
free(data);
|
|
2170
|
+
return TF_OK;
|
|
2171
|
+
|
|
2172
|
+
fail:
|
|
2173
|
+
if (all_batches) {
|
|
2174
|
+
for (size_t i = 0; i < total_batches; i++) tf_batch_free(all_batches[i]);
|
|
2175
|
+
free(all_batches);
|
|
2176
|
+
if (batches) for (size_t i = 0; i < n_batches; i++) batches[i] = NULL;
|
|
2177
|
+
if (flush_batches) for (size_t i = 0; i < n_flush; i++) flush_batches[i] = NULL;
|
|
2178
|
+
}
|
|
2179
|
+
tf_batch_array_free(batches, n_batches);
|
|
2180
|
+
tf_batch_array_free(flush_batches, n_flush);
|
|
2181
|
+
if (dec) dec->destroy(dec);
|
|
2182
|
+
free(data);
|
|
2183
|
+
return TF_ERROR;
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
static int set_process(tf_step *self, tf_batch *in, tf_batch **out,
|
|
2187
|
+
tf_side_channels *side) {
|
|
2188
|
+
set_state *st = self->state;
|
|
2189
|
+
*out = NULL;
|
|
2190
|
+
const char *op = set_mode_name(st->mode);
|
|
2191
|
+
|
|
2192
|
+
if (st->use_spill) return set_process_spill(self, in, out, side);
|
|
2193
|
+
if (st->sorted) return set_process_sorted(self, in, out, side);
|
|
2194
|
+
|
|
2195
|
+
if (!st->loaded) {
|
|
2196
|
+
if (load_lookup(st, in, side) != TF_OK) return TF_ERROR;
|
|
2197
|
+
st->loaded = 1;
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
tf_batch *ob = tf_batch_create(in->n_cols, in->n_rows > 0 ? in->n_rows : 16);
|
|
2201
|
+
if (!ob) return TF_ERROR;
|
|
2202
|
+
for (size_t c = 0; c < in->n_cols; c++) {
|
|
2203
|
+
if (tf_batch_set_schema(ob, c, in->col_names[c], in->col_types[c]) != TF_OK) {
|
|
2204
|
+
tf_batch_free(ob);
|
|
2205
|
+
return TF_ERROR;
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
size_t out_row = 0;
|
|
2210
|
+
for (size_t r = 0; r < in->n_rows; r++) {
|
|
2211
|
+
char *key = format_set_key(in, r, st->left_cols, st->n_key_cols);
|
|
2212
|
+
if (!key) { tf_batch_free(ob); return TF_ERROR; }
|
|
2213
|
+
int keep = 0;
|
|
2214
|
+
if (st->mode == 4 || st->mode == 5) {
|
|
2215
|
+
int consumed = set_map_decrement_if_present(&st->lookup, key);
|
|
2216
|
+
keep = st->mode == 4 ? consumed : !consumed;
|
|
2217
|
+
free(key);
|
|
2218
|
+
} else {
|
|
2219
|
+
int in_lookup = set_map_contains(&st->lookup, key);
|
|
2220
|
+
keep = st->mode == 0 ? in_lookup : !in_lookup;
|
|
2221
|
+
if (!keep) { free(key); continue; }
|
|
2222
|
+
int inserted = 0;
|
|
2223
|
+
if (set_map_insert_owned(&st->emitted, key, st->max_output_keys,
|
|
2224
|
+
op, "max_output_keys", side, &inserted) != TF_OK) {
|
|
2225
|
+
tf_batch_free(ob);
|
|
2226
|
+
return TF_ERROR;
|
|
2227
|
+
}
|
|
2228
|
+
if (inserted && set_check_state_bytes(st, side) != TF_OK) {
|
|
2229
|
+
tf_batch_free(ob);
|
|
2230
|
+
return TF_ERROR;
|
|
2231
|
+
}
|
|
2232
|
+
if (!inserted) continue;
|
|
2233
|
+
}
|
|
2234
|
+
if (!keep) continue;
|
|
2235
|
+
if (tf_batch_copy_row(ob, out_row, in, r) != TF_OK) { tf_batch_free(ob); return TF_ERROR; }
|
|
2236
|
+
if (tf_batch_expose_row(ob, out_row) != TF_OK) { tf_batch_free(ob); return TF_ERROR; }
|
|
2237
|
+
out_row++;
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
if (out_row > 0) *out = ob;
|
|
2241
|
+
else tf_batch_free(ob);
|
|
2242
|
+
return TF_OK;
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
|
|
2246
|
+
static int set_copy_cell(tf_batch *dst, size_t dr, size_t dc,
|
|
2247
|
+
const tf_batch *src, size_t sr, int sc,
|
|
2248
|
+
tf_side_channels *side, const char *op) {
|
|
2249
|
+
if (sc < 0) return TF_ERROR;
|
|
2250
|
+
tf_type dt = dst->col_types[dc];
|
|
2251
|
+
tf_type stype = src->col_types[sc];
|
|
2252
|
+
if (tf_batch_is_null(src, sr, (size_t)sc)) {
|
|
2253
|
+
return tf_batch_set_null(dst, dr, dc);
|
|
2254
|
+
}
|
|
2255
|
+
if (dt != stype && !(dt == TF_TYPE_FLOAT64 && stype == TF_TYPE_INT64)) {
|
|
2256
|
+
char msg[224];
|
|
2257
|
+
snprintf(msg, sizeof(msg), "%s: file column '%s' type does not match input schema",
|
|
2258
|
+
op, src->col_names[sc] ? src->col_names[sc] : "");
|
|
2259
|
+
if (set_write_error(side, msg) != TF_OK) return TF_ERROR;
|
|
2260
|
+
return TF_ERROR;
|
|
2261
|
+
}
|
|
2262
|
+
switch (dt) {
|
|
2263
|
+
case TF_TYPE_BOOL:
|
|
2264
|
+
return tf_batch_set_bool(dst, dr, dc, tf_batch_get_bool(src, sr, (size_t)sc));
|
|
2265
|
+
case TF_TYPE_INT64:
|
|
2266
|
+
return tf_batch_set_int64(dst, dr, dc, tf_batch_get_int64(src, sr, (size_t)sc));
|
|
2267
|
+
case TF_TYPE_FLOAT64:
|
|
2268
|
+
if (stype == TF_TYPE_INT64)
|
|
2269
|
+
return tf_batch_set_float64(dst, dr, dc, (double)tf_batch_get_int64(src, sr, (size_t)sc));
|
|
2270
|
+
return tf_batch_set_float64(dst, dr, dc, tf_batch_get_float64(src, sr, (size_t)sc));
|
|
2271
|
+
case TF_TYPE_STRING:
|
|
2272
|
+
return tf_batch_set_string(dst, dr, dc, tf_batch_get_string(src, sr, (size_t)sc));
|
|
2273
|
+
case TF_TYPE_DATE:
|
|
2274
|
+
return tf_batch_set_date(dst, dr, dc, tf_batch_get_date(src, sr, (size_t)sc));
|
|
2275
|
+
case TF_TYPE_TIMESTAMP:
|
|
2276
|
+
return tf_batch_set_timestamp(dst, dr, dc, tf_batch_get_timestamp(src, sr, (size_t)sc));
|
|
2277
|
+
default:
|
|
2278
|
+
return tf_batch_set_null(dst, dr, dc);
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
static int set_spill_capture_union_right_schema(set_state *st, const tf_batch *b,
|
|
2283
|
+
tf_side_channels *side) {
|
|
2284
|
+
const char *op = set_mode_name(st->mode);
|
|
2285
|
+
if (st->union_right_cols) return TF_OK;
|
|
2286
|
+
int *union_right_cols = tf_callocarray_checked(st->spill_n_cols ? st->spill_n_cols : 1, sizeof(int));
|
|
2287
|
+
int *key_right_cols = tf_callocarray_checked(st->n_key_cols ? st->n_key_cols : 1, sizeof(int));
|
|
2288
|
+
if (!union_right_cols || !key_right_cols) {
|
|
2289
|
+
free(union_right_cols);
|
|
2290
|
+
free(key_right_cols);
|
|
2291
|
+
return TF_ERROR;
|
|
2292
|
+
}
|
|
2293
|
+
for (size_t c = 0; c < st->spill_n_cols; c++) {
|
|
2294
|
+
int rc = tf_batch_col_index(b, st->spill_schema_names[c]);
|
|
2295
|
+
if (rc < 0) {
|
|
2296
|
+
char msg[192];
|
|
2297
|
+
snprintf(msg, sizeof(msg), "%s: file column '%s' not found", op, st->spill_schema_names[c]);
|
|
2298
|
+
if (set_write_error(side, msg) != TF_OK) goto fail;
|
|
2299
|
+
goto fail;
|
|
2300
|
+
}
|
|
2301
|
+
if (b->col_types[rc] != st->spill_schema_types[c] &&
|
|
2302
|
+
!(st->spill_schema_types[c] == TF_TYPE_FLOAT64 && b->col_types[rc] == TF_TYPE_INT64)) {
|
|
2303
|
+
char msg[224];
|
|
2304
|
+
snprintf(msg, sizeof(msg), "%s: file column '%s' type does not match input schema",
|
|
2305
|
+
op, st->spill_schema_names[c]);
|
|
2306
|
+
if (set_write_error(side, msg) != TF_OK) goto fail;
|
|
2307
|
+
goto fail;
|
|
2308
|
+
}
|
|
2309
|
+
union_right_cols[c] = rc;
|
|
2310
|
+
}
|
|
2311
|
+
for (size_t k = 0; k < st->n_key_cols; k++) {
|
|
2312
|
+
int rc = tf_batch_col_index(b, st->key_names[k]);
|
|
2313
|
+
if (rc < 0) {
|
|
2314
|
+
char msg[192];
|
|
2315
|
+
snprintf(msg, sizeof(msg), "%s: file column '%s' not found", op, st->key_names[k]);
|
|
2316
|
+
if (set_write_error(side, msg) != TF_OK) goto fail;
|
|
2317
|
+
goto fail;
|
|
2318
|
+
}
|
|
2319
|
+
key_right_cols[k] = rc;
|
|
2320
|
+
}
|
|
2321
|
+
st->union_right_cols = union_right_cols;
|
|
2322
|
+
for (size_t k = 0; k < st->n_key_cols; k++) st->right_cols[k] = key_right_cols[k];
|
|
2323
|
+
free(key_right_cols);
|
|
2324
|
+
return TF_OK;
|
|
2325
|
+
|
|
2326
|
+
fail:
|
|
2327
|
+
free(union_right_cols);
|
|
2328
|
+
free(key_right_cols);
|
|
2329
|
+
return TF_ERROR;
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
static int set_spill_process_union_file_batch(set_state *st, const tf_batch *batch,
|
|
2333
|
+
tf_side_channels *side) {
|
|
2334
|
+
const char *op = set_mode_name(st->mode);
|
|
2335
|
+
if (set_spill_capture_union_right_schema(st, batch, side) != TF_OK) return TF_ERROR;
|
|
2336
|
+
for (size_t r = 0; r < batch->n_rows; r++) {
|
|
2337
|
+
if (st->max_lookup_rows > 0 && st->union_file_rows >= st->max_lookup_rows) {
|
|
2338
|
+
if (set_limit_error(side, op, "max_lookup_rows", st->max_lookup_rows,
|
|
2339
|
+
st->union_file_rows + 1) != TF_OK)
|
|
2340
|
+
return TF_ERROR;
|
|
2341
|
+
return TF_ERROR;
|
|
2342
|
+
}
|
|
2343
|
+
size_t dst = st->spill_left_buf->n_rows;
|
|
2344
|
+
if (tf_batch_ensure_capacity(st->spill_left_buf, dst + 1) != TF_OK) return TF_ERROR;
|
|
2345
|
+
for (size_t c = 0; c < st->spill_n_cols; c++) {
|
|
2346
|
+
if (set_copy_cell(st->spill_left_buf, dst, c, batch, r, st->union_right_cols[c], side, op) != TF_OK)
|
|
2347
|
+
return TF_ERROR;
|
|
2348
|
+
}
|
|
2349
|
+
if (set_ensure_ordinals(&st->spill_left_ordinals, &st->spill_left_ordinal_cap, dst + 1) != TF_OK) return TF_ERROR;
|
|
2350
|
+
st->spill_left_ordinals[dst] = st->spill_next_left_ordinal++;
|
|
2351
|
+
if (tf_batch_expose_row(st->spill_left_buf, dst) != TF_OK) return TF_ERROR;
|
|
2352
|
+
st->union_file_rows++;
|
|
2353
|
+
if (st->spill_left_buf->n_rows >= st->run_rows && set_spill_write_left_run(st) != TF_OK)
|
|
2354
|
+
return TF_ERROR;
|
|
2355
|
+
}
|
|
2356
|
+
return TF_OK;
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
static int set_spill_load_union_file_runs(set_state *st, tf_side_channels *side) {
|
|
2360
|
+
if (st->spill_lookup_loaded) return TF_OK;
|
|
2361
|
+
st->spill_lookup_loaded = 1;
|
|
2362
|
+
FILE *f = tf_policy_fopen_read(st->file, st->validated_file);
|
|
2363
|
+
if (!f) {
|
|
2364
|
+
if (set_write_error(side, "union: cannot open file") != TF_OK) return TF_ERROR;
|
|
2365
|
+
return TF_ERROR;
|
|
2366
|
+
}
|
|
2367
|
+
tf_decoder *dec = tf_csv_decoder_create(NULL);
|
|
2368
|
+
if (!dec) { fclose(f); return TF_ERROR; }
|
|
2369
|
+
size_t bytes_read = 0;
|
|
2370
|
+
int flushed = 0;
|
|
2371
|
+
int rc = TF_OK;
|
|
2372
|
+
while (!flushed) {
|
|
2373
|
+
uint8_t buf[64 * 1024];
|
|
2374
|
+
size_t n = fread(buf, 1, sizeof(buf), f);
|
|
2375
|
+
tf_batch **batches = NULL;
|
|
2376
|
+
size_t n_batches = 0;
|
|
2377
|
+
if (n > 0) {
|
|
2378
|
+
bytes_read += n;
|
|
2379
|
+
if (st->max_lookup_bytes > 0 && bytes_read > st->max_lookup_bytes) {
|
|
2380
|
+
if (set_limit_error(side, "union", "max_lookup_bytes",
|
|
2381
|
+
st->max_lookup_bytes, bytes_read) != TF_OK)
|
|
2382
|
+
rc = TF_ERROR;
|
|
2383
|
+
rc = TF_ERROR;
|
|
2384
|
+
break;
|
|
2385
|
+
}
|
|
2386
|
+
rc = dec->decode(dec, buf, n, &batches, &n_batches, side);
|
|
2387
|
+
} else {
|
|
2388
|
+
if (ferror(f)) {
|
|
2389
|
+
if (set_write_error(side, "union: failed reading file") != TF_OK)
|
|
2390
|
+
rc = TF_ERROR;
|
|
2391
|
+
rc = TF_ERROR;
|
|
2392
|
+
break;
|
|
2393
|
+
}
|
|
2394
|
+
flushed = 1;
|
|
2395
|
+
rc = dec->flush(dec, &batches, &n_batches, side);
|
|
2396
|
+
}
|
|
2397
|
+
if (rc != TF_OK) { tf_batch_array_free(batches, n_batches); break; }
|
|
2398
|
+
for (size_t i = 0; i < n_batches; i++) {
|
|
2399
|
+
if (set_spill_process_union_file_batch(st, batches[i], side) != TF_OK) rc = TF_ERROR;
|
|
2400
|
+
tf_batch_free(batches[i]);
|
|
2401
|
+
if (rc != TF_OK) {
|
|
2402
|
+
tf_batch_array_free_items(batches + i + 1, n_batches - i - 1);
|
|
2403
|
+
break;
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
free(batches);
|
|
2407
|
+
if (rc != TF_OK) break;
|
|
2408
|
+
}
|
|
2409
|
+
dec->destroy(dec);
|
|
2410
|
+
fclose(f);
|
|
2411
|
+
return rc == TF_OK ? TF_OK : TF_ERROR;
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2414
|
+
static int set_spill_produce_union_output_runs(set_state *st, tf_side_channels *side) {
|
|
2415
|
+
if (st->spill_key_merge_done) return TF_OK;
|
|
2416
|
+
if (!st->spill_has_schema) { st->spill_key_merge_done = 1; return TF_OK; }
|
|
2417
|
+
|
|
2418
|
+
if (set_spill_load_union_file_runs(st, side) != TF_OK) return TF_ERROR;
|
|
2419
|
+
if (st->spill_left_buf && st->spill_left_buf->n_rows > 0 && set_spill_write_left_run(st) != TF_OK) return TF_ERROR;
|
|
2420
|
+
if (st->spill_left_buf) { tf_batch_free(st->spill_left_buf); st->spill_left_buf = NULL; }
|
|
2421
|
+
free(st->spill_left_ordinals); st->spill_left_ordinals = NULL; st->spill_left_ordinal_cap = 0;
|
|
2422
|
+
|
|
2423
|
+
if (set_spill_open_readers(st, 0) != TF_OK) return TF_ERROR;
|
|
2424
|
+
for (;;) {
|
|
2425
|
+
int left_idx = set_spill_best_reader(st, 0);
|
|
2426
|
+
if (left_idx < 0) break;
|
|
2427
|
+
set_spill_reader *row = &st->spill_left_readers[left_idx];
|
|
2428
|
+
char *key = set_spill_build_key(st, &row->row, 0);
|
|
2429
|
+
if (!key) return TF_ERROR;
|
|
2430
|
+
int duplicate = st->spill_last_left_key && strcmp(st->spill_last_left_key, key) == 0;
|
|
2431
|
+
if (!duplicate) {
|
|
2432
|
+
free(st->spill_last_left_key);
|
|
2433
|
+
st->spill_last_left_key = key;
|
|
2434
|
+
key = NULL;
|
|
2435
|
+
if (st->max_output_keys > 0 && st->spill_distinct_rows >= st->max_output_keys) {
|
|
2436
|
+
if (set_limit_error(side, "union", "max_output_keys",
|
|
2437
|
+
st->max_output_keys,
|
|
2438
|
+
st->spill_distinct_rows + 1) != TF_OK)
|
|
2439
|
+
return TF_ERROR;
|
|
2440
|
+
return TF_ERROR;
|
|
2441
|
+
}
|
|
2442
|
+
if (set_spill_append_selected_row(st, &row->row) != TF_OK) return TF_ERROR;
|
|
2443
|
+
}
|
|
2444
|
+
free(key);
|
|
2445
|
+
int adv = set_spill_reader_advance(st, row);
|
|
2446
|
+
if (adv < 0) return TF_ERROR;
|
|
2447
|
+
}
|
|
2448
|
+
if (st->spill_out_buf && st->spill_out_buf->n_rows > 0 && set_spill_write_output_run(st) != TF_OK) return TF_ERROR;
|
|
2449
|
+
set_spill_close_readers(st, 0);
|
|
2450
|
+
set_spill_remove_paths(&st->spill_left_run_paths, &st->spill_n_left_runs, &st->spill_cap_left_runs);
|
|
2451
|
+
st->spill_key_merge_done = 1;
|
|
2452
|
+
return TF_OK;
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
static tf_batch *set_create_output_like_union_schema(const set_state *st, size_t rows) {
|
|
2456
|
+
tf_batch *ob = tf_batch_create(st->union_n_cols, rows > 0 ? rows : 1);
|
|
2457
|
+
if (!ob) return NULL;
|
|
2458
|
+
for (size_t c = 0; c < st->union_n_cols; c++) {
|
|
2459
|
+
if (tf_batch_set_schema(ob, c, st->union_col_names[c], st->union_col_types[c]) != TF_OK) {
|
|
2460
|
+
tf_batch_free(ob);
|
|
2461
|
+
return NULL;
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
return ob;
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
static int union_capture_left_schema(set_state *st, const tf_batch *in,
|
|
2468
|
+
tf_side_channels *side) {
|
|
2469
|
+
const char *op = set_mode_name(st->mode);
|
|
2470
|
+
if (st->union_schema_ready) return TF_OK;
|
|
2471
|
+
|
|
2472
|
+
size_t union_n_cols = in->n_cols;
|
|
2473
|
+
char **union_col_names = tf_callocarray_checked(union_n_cols ? union_n_cols : 1, sizeof(char *));
|
|
2474
|
+
tf_type *union_col_types = tf_callocarray_checked(union_n_cols ? union_n_cols : 1, sizeof(tf_type));
|
|
2475
|
+
int *left_cols = NULL;
|
|
2476
|
+
int *right_cols = NULL;
|
|
2477
|
+
char **key_names = NULL;
|
|
2478
|
+
size_t n_key_cols = 0;
|
|
2479
|
+
set_hash_map emitted = {0};
|
|
2480
|
+
|
|
2481
|
+
if (!union_col_names || !union_col_types) goto fail;
|
|
2482
|
+
for (size_t c = 0; c < union_n_cols; c++) {
|
|
2483
|
+
union_col_names[c] = strdup(in->col_names[c] ? in->col_names[c] : "");
|
|
2484
|
+
if (!union_col_names[c]) goto fail;
|
|
2485
|
+
union_col_types[c] = in->col_types[c];
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
if (st->mode == 2) {
|
|
2489
|
+
size_t n = st->n_columns > 0 ? st->n_columns : in->n_cols;
|
|
2490
|
+
left_cols = tf_callocarray_checked(n ? n : 1, sizeof(int));
|
|
2491
|
+
right_cols = tf_callocarray_checked(n ? n : 1, sizeof(int));
|
|
2492
|
+
key_names = tf_callocarray_checked(n ? n : 1, sizeof(char *));
|
|
2493
|
+
if (!left_cols || !right_cols || !key_names) goto fail;
|
|
2494
|
+
n_key_cols = n;
|
|
2495
|
+
for (size_t i = 0; i < n; i++) {
|
|
2496
|
+
const char *name = st->n_columns > 0 ? st->columns[i] : in->col_names[i];
|
|
2497
|
+
int lc = tf_batch_col_index(in, name);
|
|
2498
|
+
if (lc < 0) {
|
|
2499
|
+
char msg[192];
|
|
2500
|
+
snprintf(msg, sizeof(msg), "%s: input column '%s' not found", op, name ? name : "");
|
|
2501
|
+
if (set_write_error(side, msg) != TF_OK) goto fail;
|
|
2502
|
+
goto fail;
|
|
2503
|
+
}
|
|
2504
|
+
left_cols[i] = lc;
|
|
2505
|
+
right_cols[i] = -1;
|
|
2506
|
+
key_names[i] = strdup(name ? name : "");
|
|
2507
|
+
if (!key_names[i]) goto fail;
|
|
2508
|
+
}
|
|
2509
|
+
if (set_map_init(&emitted, st->max_output_keys ? st->max_output_keys : 64) != TF_OK)
|
|
2510
|
+
goto fail;
|
|
2511
|
+
}
|
|
2512
|
+
|
|
2513
|
+
st->union_n_cols = union_n_cols;
|
|
2514
|
+
st->union_col_names = union_col_names;
|
|
2515
|
+
st->union_col_types = union_col_types;
|
|
2516
|
+
st->left_cols = left_cols;
|
|
2517
|
+
st->right_cols = right_cols;
|
|
2518
|
+
st->key_names = key_names;
|
|
2519
|
+
st->n_key_cols = n_key_cols;
|
|
2520
|
+
st->emitted = emitted;
|
|
2521
|
+
st->union_schema_ready = 1;
|
|
2522
|
+
return TF_OK;
|
|
2523
|
+
|
|
2524
|
+
fail:
|
|
2525
|
+
set_free_string_array(union_col_names, union_n_cols);
|
|
2526
|
+
free(union_col_types);
|
|
2527
|
+
free(left_cols);
|
|
2528
|
+
free(right_cols);
|
|
2529
|
+
set_free_string_array(key_names, n_key_cols);
|
|
2530
|
+
set_map_free(&emitted);
|
|
2531
|
+
return TF_ERROR;
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
static int union_capture_right_schema(set_state *st, const tf_batch *b,
|
|
2535
|
+
tf_side_channels *side) {
|
|
2536
|
+
const char *op = set_mode_name(st->mode);
|
|
2537
|
+
if (st->union_right_cols) return TF_OK;
|
|
2538
|
+
int *union_right_cols = tf_callocarray_checked(st->union_n_cols ? st->union_n_cols : 1, sizeof(int));
|
|
2539
|
+
int *key_right_cols = NULL;
|
|
2540
|
+
if (st->mode == 2) {
|
|
2541
|
+
key_right_cols = tf_callocarray_checked(st->n_key_cols ? st->n_key_cols : 1, sizeof(int));
|
|
2542
|
+
}
|
|
2543
|
+
if (!union_right_cols || (st->mode == 2 && !key_right_cols)) {
|
|
2544
|
+
free(union_right_cols);
|
|
2545
|
+
free(key_right_cols);
|
|
2546
|
+
return TF_ERROR;
|
|
2547
|
+
}
|
|
2548
|
+
for (size_t c = 0; c < st->union_n_cols; c++) {
|
|
2549
|
+
int rc = tf_batch_col_index(b, st->union_col_names[c]);
|
|
2550
|
+
if (rc < 0) {
|
|
2551
|
+
char msg[192];
|
|
2552
|
+
snprintf(msg, sizeof(msg), "%s: file column '%s' not found", op, st->union_col_names[c]);
|
|
2553
|
+
if (set_write_error(side, msg) != TF_OK) goto fail;
|
|
2554
|
+
goto fail;
|
|
2555
|
+
}
|
|
2556
|
+
if (b->col_types[rc] != st->union_col_types[c] &&
|
|
2557
|
+
!(st->union_col_types[c] == TF_TYPE_FLOAT64 && b->col_types[rc] == TF_TYPE_INT64)) {
|
|
2558
|
+
char msg[224];
|
|
2559
|
+
snprintf(msg, sizeof(msg), "%s: file column '%s' type does not match input schema",
|
|
2560
|
+
op, st->union_col_names[c]);
|
|
2561
|
+
if (set_write_error(side, msg) != TF_OK) goto fail;
|
|
2562
|
+
goto fail;
|
|
2563
|
+
}
|
|
2564
|
+
union_right_cols[c] = rc;
|
|
2565
|
+
}
|
|
2566
|
+
if (st->mode == 2) {
|
|
2567
|
+
for (size_t i = 0; i < st->n_key_cols; i++) {
|
|
2568
|
+
int rc = tf_batch_col_index(b, st->key_names[i]);
|
|
2569
|
+
if (rc < 0) {
|
|
2570
|
+
char msg[192];
|
|
2571
|
+
snprintf(msg, sizeof(msg), "%s: file column '%s' not found", op, st->key_names[i]);
|
|
2572
|
+
if (set_write_error(side, msg) != TF_OK) goto fail;
|
|
2573
|
+
goto fail;
|
|
2574
|
+
}
|
|
2575
|
+
key_right_cols[i] = rc;
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
st->union_right_cols = union_right_cols;
|
|
2579
|
+
if (st->mode == 2) {
|
|
2580
|
+
for (size_t i = 0; i < st->n_key_cols; i++) st->right_cols[i] = key_right_cols[i];
|
|
2581
|
+
}
|
|
2582
|
+
free(key_right_cols);
|
|
2583
|
+
return TF_OK;
|
|
2584
|
+
|
|
2585
|
+
fail:
|
|
2586
|
+
free(union_right_cols);
|
|
2587
|
+
free(key_right_cols);
|
|
2588
|
+
return TF_ERROR;
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
static int union_sorted_next_batch(set_state *st, tf_side_channels *side) {
|
|
2592
|
+
if (st->sorted_current) {
|
|
2593
|
+
tf_batch_free(st->sorted_current);
|
|
2594
|
+
st->sorted_current = NULL;
|
|
2595
|
+
}
|
|
2596
|
+
for (;;) {
|
|
2597
|
+
tf_batch *b = NULL;
|
|
2598
|
+
if (union_next_decoded_batch(st, &b, side) != TF_OK) return TF_ERROR;
|
|
2599
|
+
if (!b) {
|
|
2600
|
+
st->sorted_exhausted = 1;
|
|
2601
|
+
return TF_OK;
|
|
2602
|
+
}
|
|
2603
|
+
if (union_capture_right_schema(st, b, side) != TF_OK) {
|
|
2604
|
+
tf_batch_free(b);
|
|
2605
|
+
return TF_ERROR;
|
|
2606
|
+
}
|
|
2607
|
+
st->sorted_current = b;
|
|
2608
|
+
st->sorted_row = 0;
|
|
2609
|
+
if (b->n_rows > 0) return TF_OK;
|
|
2610
|
+
tf_batch_free(st->sorted_current);
|
|
2611
|
+
st->sorted_current = NULL;
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
static int union_sorted_load_row_if_needed(set_state *st, tf_side_channels *side) {
|
|
2616
|
+
if (st->sorted_have_row) return TF_OK;
|
|
2617
|
+
for (;;) {
|
|
2618
|
+
if (!st->sorted_current || st->sorted_row >= st->sorted_current->n_rows) {
|
|
2619
|
+
if (union_sorted_next_batch(st, side) != TF_OK) return TF_ERROR;
|
|
2620
|
+
if (st->sorted_exhausted) return TF_OK;
|
|
2621
|
+
continue;
|
|
2622
|
+
}
|
|
2623
|
+
|
|
2624
|
+
set_key_tuple cur = {0};
|
|
2625
|
+
if (set_key_tuple_set_from_row(&cur, st->sorted_current, st->sorted_row,
|
|
2626
|
+
st->right_cols, st->n_key_cols) != TF_OK) {
|
|
2627
|
+
set_key_tuple_clear(&cur);
|
|
2628
|
+
return TF_ERROR;
|
|
2629
|
+
}
|
|
2630
|
+
if (st->prev_lookup_key.valid) {
|
|
2631
|
+
int cmp = 0;
|
|
2632
|
+
if (set_key_tuple_compare(&st->prev_lookup_key, &cur, &cmp) != TF_OK) {
|
|
2633
|
+
set_key_tuple_clear(&cur);
|
|
2634
|
+
if (set_write_error(side, "union: file key types changed") != TF_OK)
|
|
2635
|
+
return TF_ERROR;
|
|
2636
|
+
return TF_ERROR;
|
|
2637
|
+
}
|
|
2638
|
+
if (cmp > 0) {
|
|
2639
|
+
set_key_tuple_clear(&cur);
|
|
2640
|
+
if (set_write_error(side, "union: file side is not sorted by key") != TF_OK)
|
|
2641
|
+
return TF_ERROR;
|
|
2642
|
+
return TF_ERROR;
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
if (set_key_tuple_copy(&st->prev_lookup_key, &cur) != TF_OK) {
|
|
2646
|
+
set_key_tuple_clear(&cur);
|
|
2647
|
+
return TF_ERROR;
|
|
2648
|
+
}
|
|
2649
|
+
set_key_tuple_clear(&cur);
|
|
2650
|
+
st->sorted_have_row = 1;
|
|
2651
|
+
return TF_OK;
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2655
|
+
static void union_sorted_consume_row(set_state *st) {
|
|
2656
|
+
if (!st || !st->sorted_have_row) return;
|
|
2657
|
+
st->sorted_row++;
|
|
2658
|
+
st->sorted_have_row = 0;
|
|
2659
|
+
}
|
|
2660
|
+
|
|
2661
|
+
static void union_sorted_clear_right_row(set_state *st) {
|
|
2662
|
+
if (st && st->union_sorted_right_row) {
|
|
2663
|
+
tf_batch_free(st->union_sorted_right_row);
|
|
2664
|
+
st->union_sorted_right_row = NULL;
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
|
|
2668
|
+
static int union_sorted_load_next_right_key(set_state *st, tf_side_channels *side) {
|
|
2669
|
+
set_key_tuple_clear(&st->current_lookup_key);
|
|
2670
|
+
union_sorted_clear_right_row(st);
|
|
2671
|
+
if (union_sorted_load_row_if_needed(st, side) != TF_OK) return TF_ERROR;
|
|
2672
|
+
if (st->sorted_exhausted && !st->sorted_have_row) return TF_OK;
|
|
2673
|
+
|
|
2674
|
+
if (set_key_tuple_set_from_row(&st->current_lookup_key, st->sorted_current,
|
|
2675
|
+
st->sorted_row, st->right_cols, st->n_key_cols) != TF_OK)
|
|
2676
|
+
return TF_ERROR;
|
|
2677
|
+
|
|
2678
|
+
st->union_sorted_right_row = set_create_output_like_union_schema(st, 1);
|
|
2679
|
+
if (!st->union_sorted_right_row) return TF_ERROR;
|
|
2680
|
+
for (size_t c = 0; c < st->union_n_cols; c++) {
|
|
2681
|
+
if (set_copy_cell(st->union_sorted_right_row, 0, c, st->sorted_current,
|
|
2682
|
+
st->sorted_row, st->union_right_cols[c], side, "union") != TF_OK)
|
|
2683
|
+
return TF_ERROR;
|
|
2684
|
+
}
|
|
2685
|
+
if (tf_batch_expose_row(st->union_sorted_right_row, 0) != TF_OK)
|
|
2686
|
+
return TF_ERROR;
|
|
2687
|
+
|
|
2688
|
+
while (st->sorted_have_row) {
|
|
2689
|
+
set_key_tuple row_key = {0};
|
|
2690
|
+
if (set_key_tuple_set_from_row(&row_key, st->sorted_current, st->sorted_row,
|
|
2691
|
+
st->right_cols, st->n_key_cols) != TF_OK) {
|
|
2692
|
+
set_key_tuple_clear(&row_key);
|
|
2693
|
+
return TF_ERROR;
|
|
2694
|
+
}
|
|
2695
|
+
int cmp = 0;
|
|
2696
|
+
if (set_key_tuple_compare(&st->current_lookup_key, &row_key, &cmp) != TF_OK) {
|
|
2697
|
+
set_key_tuple_clear(&row_key);
|
|
2698
|
+
if (set_write_error(side, "union: file key types changed") != TF_OK)
|
|
2699
|
+
return TF_ERROR;
|
|
2700
|
+
return TF_ERROR;
|
|
2701
|
+
}
|
|
2702
|
+
set_key_tuple_clear(&row_key);
|
|
2703
|
+
if (cmp != 0) break;
|
|
2704
|
+
st->union_file_rows++;
|
|
2705
|
+
if (st->max_lookup_rows > 0 && st->union_file_rows > st->max_lookup_rows) {
|
|
2706
|
+
if (set_limit_error(side, "union", "max_lookup_rows",
|
|
2707
|
+
st->max_lookup_rows, st->union_file_rows) != TF_OK)
|
|
2708
|
+
return TF_ERROR;
|
|
2709
|
+
return TF_ERROR;
|
|
2710
|
+
}
|
|
2711
|
+
union_sorted_consume_row(st);
|
|
2712
|
+
if (union_sorted_load_row_if_needed(st, side) != TF_OK) return TF_ERROR;
|
|
2713
|
+
if (st->sorted_exhausted && !st->sorted_have_row) break;
|
|
2714
|
+
}
|
|
2715
|
+
return TF_OK;
|
|
2716
|
+
}
|
|
2717
|
+
|
|
2718
|
+
static int union_sorted_ensure_right_key(set_state *st, tf_side_channels *side) {
|
|
2719
|
+
if (!st->current_lookup_key.valid && !st->sorted_exhausted)
|
|
2720
|
+
return union_sorted_load_next_right_key(st, side);
|
|
2721
|
+
return TF_OK;
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
static int union_sorted_note_emit(set_state *st, tf_side_channels *side) {
|
|
2725
|
+
if (st->max_output_keys > 0 && st->emitted.count >= st->max_output_keys) {
|
|
2726
|
+
if (set_limit_error(side, "union", "max_output_keys", st->max_output_keys,
|
|
2727
|
+
st->emitted.count + 1) != TF_OK)
|
|
2728
|
+
return TF_ERROR;
|
|
2729
|
+
return TF_ERROR;
|
|
2730
|
+
}
|
|
2731
|
+
st->emitted.count++;
|
|
2732
|
+
return TF_OK;
|
|
2733
|
+
}
|
|
2734
|
+
|
|
2735
|
+
static int union_sorted_append_left(set_state *st, tf_batch *ob, size_t *out_row,
|
|
2736
|
+
const tf_batch *in, size_t row,
|
|
2737
|
+
tf_side_channels *side) {
|
|
2738
|
+
if (union_sorted_note_emit(st, side) != TF_OK) return TF_ERROR;
|
|
2739
|
+
if (tf_batch_ensure_capacity(ob, *out_row + 1) != TF_OK) return TF_ERROR;
|
|
2740
|
+
if (tf_batch_copy_row(ob, *out_row, in, row) != TF_OK) return TF_ERROR;
|
|
2741
|
+
if (tf_batch_expose_row(ob, *out_row) != TF_OK) return TF_ERROR;
|
|
2742
|
+
(*out_row)++;
|
|
2743
|
+
return TF_OK;
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2746
|
+
static int union_sorted_append_right(set_state *st, tf_batch *ob, size_t *out_row,
|
|
2747
|
+
tf_side_channels *side) {
|
|
2748
|
+
if (!st->union_sorted_right_row) return TF_ERROR;
|
|
2749
|
+
if (union_sorted_note_emit(st, side) != TF_OK) return TF_ERROR;
|
|
2750
|
+
if (tf_batch_ensure_capacity(ob, *out_row + 1) != TF_OK) return TF_ERROR;
|
|
2751
|
+
if (tf_batch_copy_row(ob, *out_row, st->union_sorted_right_row, 0) != TF_OK) return TF_ERROR;
|
|
2752
|
+
if (tf_batch_expose_row(ob, *out_row) != TF_OK) return TF_ERROR;
|
|
2753
|
+
(*out_row)++;
|
|
2754
|
+
return TF_OK;
|
|
2755
|
+
}
|
|
2756
|
+
|
|
2757
|
+
static int union_process_sorted(tf_step *self, tf_batch *in, tf_batch **out,
|
|
2758
|
+
tf_side_channels *side) {
|
|
2759
|
+
set_state *st = self->state;
|
|
2760
|
+
*out = NULL;
|
|
2761
|
+
if (union_capture_left_schema(st, in, side) != TF_OK) return TF_ERROR;
|
|
2762
|
+
|
|
2763
|
+
tf_batch *ob = set_create_output_like_union_schema(st, in->n_rows > 0 ? in->n_rows : 1);
|
|
2764
|
+
if (!ob) return TF_ERROR;
|
|
2765
|
+
size_t out_row = 0;
|
|
2766
|
+
|
|
2767
|
+
for (size_t r = 0; r < in->n_rows; r++) {
|
|
2768
|
+
set_key_tuple left_key = {0};
|
|
2769
|
+
if (set_key_tuple_set_from_row(&left_key, in, r, st->left_cols, st->n_key_cols) != TF_OK) {
|
|
2770
|
+
set_key_tuple_clear(&left_key);
|
|
2771
|
+
tf_batch_free(ob);
|
|
2772
|
+
return TF_ERROR;
|
|
2773
|
+
}
|
|
2774
|
+
int duplicate_left = 0;
|
|
2775
|
+
if (st->prev_left_key.valid) {
|
|
2776
|
+
int cmp = 0;
|
|
2777
|
+
if (set_key_tuple_compare(&st->prev_left_key, &left_key, &cmp) != TF_OK) {
|
|
2778
|
+
set_key_tuple_clear(&left_key);
|
|
2779
|
+
tf_batch_free(ob);
|
|
2780
|
+
if (set_write_error(side, "union: left key types changed") != TF_OK) return TF_ERROR;
|
|
2781
|
+
return TF_ERROR;
|
|
2782
|
+
}
|
|
2783
|
+
if (cmp > 0) {
|
|
2784
|
+
set_key_tuple_clear(&left_key);
|
|
2785
|
+
tf_batch_free(ob);
|
|
2786
|
+
if (set_write_error(side, "union: left side is not sorted by key") != TF_OK)
|
|
2787
|
+
return TF_ERROR;
|
|
2788
|
+
return TF_ERROR;
|
|
2789
|
+
}
|
|
2790
|
+
duplicate_left = (cmp == 0);
|
|
2791
|
+
}
|
|
2792
|
+
if (set_key_tuple_copy(&st->prev_left_key, &left_key) != TF_OK) {
|
|
2793
|
+
set_key_tuple_clear(&left_key);
|
|
2794
|
+
tf_batch_free(ob);
|
|
2795
|
+
return TF_ERROR;
|
|
2796
|
+
}
|
|
2797
|
+
if (duplicate_left) {
|
|
2798
|
+
set_key_tuple_clear(&left_key);
|
|
2799
|
+
continue;
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
if (union_sorted_ensure_right_key(st, side) != TF_OK) {
|
|
2803
|
+
set_key_tuple_clear(&left_key);
|
|
2804
|
+
tf_batch_free(ob);
|
|
2805
|
+
return TF_ERROR;
|
|
2806
|
+
}
|
|
2807
|
+
while (st->current_lookup_key.valid) {
|
|
2808
|
+
int cmp = 0;
|
|
2809
|
+
if (set_key_tuple_compare(&st->current_lookup_key, &left_key, &cmp) != TF_OK) {
|
|
2810
|
+
set_key_tuple_clear(&left_key);
|
|
2811
|
+
tf_batch_free(ob);
|
|
2812
|
+
if (set_write_error(side, "union: input and file key types differ") != TF_OK)
|
|
2813
|
+
return TF_ERROR;
|
|
2814
|
+
return TF_ERROR;
|
|
2815
|
+
}
|
|
2816
|
+
if (cmp < 0) {
|
|
2817
|
+
if (union_sorted_append_right(st, ob, &out_row, side) != TF_OK) {
|
|
2818
|
+
set_key_tuple_clear(&left_key);
|
|
2819
|
+
tf_batch_free(ob);
|
|
2820
|
+
return TF_ERROR;
|
|
2821
|
+
}
|
|
2822
|
+
if (union_sorted_load_next_right_key(st, side) != TF_OK) {
|
|
2823
|
+
set_key_tuple_clear(&left_key);
|
|
2824
|
+
tf_batch_free(ob);
|
|
2825
|
+
return TF_ERROR;
|
|
2826
|
+
}
|
|
2827
|
+
continue;
|
|
2828
|
+
}
|
|
2829
|
+
if (cmp == 0) {
|
|
2830
|
+
if (union_sorted_append_left(st, ob, &out_row, in, r, side) != TF_OK) {
|
|
2831
|
+
set_key_tuple_clear(&left_key);
|
|
2832
|
+
tf_batch_free(ob);
|
|
2833
|
+
return TF_ERROR;
|
|
2834
|
+
}
|
|
2835
|
+
if (union_sorted_load_next_right_key(st, side) != TF_OK) {
|
|
2836
|
+
set_key_tuple_clear(&left_key);
|
|
2837
|
+
tf_batch_free(ob);
|
|
2838
|
+
return TF_ERROR;
|
|
2839
|
+
}
|
|
2840
|
+
set_key_tuple_clear(&left_key);
|
|
2841
|
+
goto next_left_row;
|
|
2842
|
+
}
|
|
2843
|
+
break;
|
|
2844
|
+
}
|
|
2845
|
+
if (union_sorted_append_left(st, ob, &out_row, in, r, side) != TF_OK) {
|
|
2846
|
+
set_key_tuple_clear(&left_key);
|
|
2847
|
+
tf_batch_free(ob);
|
|
2848
|
+
return TF_ERROR;
|
|
2849
|
+
}
|
|
2850
|
+
set_key_tuple_clear(&left_key);
|
|
2851
|
+
next_left_row:
|
|
2852
|
+
;
|
|
2853
|
+
}
|
|
2854
|
+
|
|
2855
|
+
if (out_row > 0) *out = ob;
|
|
2856
|
+
else tf_batch_free(ob);
|
|
2857
|
+
return TF_OK;
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
static int union_sorted_flush_next(tf_step *self, tf_batch **out, tf_side_channels *side) {
|
|
2861
|
+
set_state *st = self->state;
|
|
2862
|
+
*out = NULL;
|
|
2863
|
+
if (!st->union_schema_ready) return TF_OK;
|
|
2864
|
+
size_t cap = st->output_batch_rows > 0 ? st->output_batch_rows : SET_DEFAULT_OUTPUT_ROWS;
|
|
2865
|
+
tf_batch *ob = set_create_output_like_union_schema(st, cap);
|
|
2866
|
+
if (!ob) return TF_ERROR;
|
|
2867
|
+
size_t out_row = 0;
|
|
2868
|
+
while (out_row < cap) {
|
|
2869
|
+
if (union_sorted_ensure_right_key(st, side) != TF_OK) {
|
|
2870
|
+
tf_batch_free(ob);
|
|
2871
|
+
return TF_ERROR;
|
|
2872
|
+
}
|
|
2873
|
+
if (!st->current_lookup_key.valid) break;
|
|
2874
|
+
if (union_sorted_append_right(st, ob, &out_row, side) != TF_OK) {
|
|
2875
|
+
tf_batch_free(ob);
|
|
2876
|
+
return TF_ERROR;
|
|
2877
|
+
}
|
|
2878
|
+
if (union_sorted_load_next_right_key(st, side) != TF_OK) {
|
|
2879
|
+
tf_batch_free(ob);
|
|
2880
|
+
return TF_ERROR;
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
if (out_row > 0) *out = ob;
|
|
2884
|
+
else tf_batch_free(ob);
|
|
2885
|
+
return TF_OK;
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
static int union_process(tf_step *self, tf_batch *in, tf_batch **out,
|
|
2889
|
+
tf_side_channels *side) {
|
|
2890
|
+
set_state *st = self->state;
|
|
2891
|
+
const char *op = set_mode_name(st->mode);
|
|
2892
|
+
*out = NULL;
|
|
2893
|
+
if (st->use_spill) return set_process_spill(self, in, out, side);
|
|
2894
|
+
if (st->sorted) return union_process_sorted(self, in, out, side);
|
|
2895
|
+
if (union_capture_left_schema(st, in, side) != TF_OK) return TF_ERROR;
|
|
2896
|
+
|
|
2897
|
+
tf_batch *ob = set_create_output_like_union_schema(st, in->n_rows > 0 ? in->n_rows : 1);
|
|
2898
|
+
if (!ob) return TF_ERROR;
|
|
2899
|
+
size_t out_row = 0;
|
|
2900
|
+
for (size_t r = 0; r < in->n_rows; r++) {
|
|
2901
|
+
if (st->mode == 2) {
|
|
2902
|
+
char *key = format_set_key(in, r, st->left_cols, st->n_key_cols);
|
|
2903
|
+
if (!key) { tf_batch_free(ob); return TF_ERROR; }
|
|
2904
|
+
int inserted = 0;
|
|
2905
|
+
if (set_map_insert_owned(&st->emitted, key, st->max_output_keys,
|
|
2906
|
+
op, "max_output_keys", side, &inserted) != TF_OK) {
|
|
2907
|
+
tf_batch_free(ob);
|
|
2908
|
+
return TF_ERROR;
|
|
2909
|
+
}
|
|
2910
|
+
if (inserted && set_check_state_bytes(st, side) != TF_OK) {
|
|
2911
|
+
tf_batch_free(ob);
|
|
2912
|
+
return TF_ERROR;
|
|
2913
|
+
}
|
|
2914
|
+
if (!inserted) continue;
|
|
2915
|
+
}
|
|
2916
|
+
if (tf_batch_copy_row(ob, out_row, in, r) != TF_OK) {
|
|
2917
|
+
tf_batch_free(ob);
|
|
2918
|
+
return TF_ERROR;
|
|
2919
|
+
}
|
|
2920
|
+
if (tf_batch_expose_row(ob, out_row) != TF_OK) {
|
|
2921
|
+
tf_batch_free(ob);
|
|
2922
|
+
return TF_ERROR;
|
|
2923
|
+
}
|
|
2924
|
+
out_row++;
|
|
2925
|
+
}
|
|
2926
|
+
if (out_row > 0) *out = ob;
|
|
2927
|
+
else tf_batch_free(ob);
|
|
2928
|
+
return TF_OK;
|
|
2929
|
+
}
|
|
2930
|
+
|
|
2931
|
+
static int union_open_file(set_state *st, tf_side_channels *side) {
|
|
2932
|
+
const char *op = set_mode_name(st->mode);
|
|
2933
|
+
if (st->sorted_file || st->sorted_decoder) return TF_OK;
|
|
2934
|
+
st->sorted_file = tf_policy_fopen_read(st->file, st->validated_file);
|
|
2935
|
+
if (!st->sorted_file) {
|
|
2936
|
+
char msg[160];
|
|
2937
|
+
snprintf(msg, sizeof(msg), "%s: cannot open file", op);
|
|
2938
|
+
if (set_write_error(side, msg) != TF_OK) return TF_ERROR;
|
|
2939
|
+
return TF_ERROR;
|
|
2940
|
+
}
|
|
2941
|
+
if (st->max_lookup_bytes > 0) {
|
|
2942
|
+
if (fseek(st->sorted_file, 0, SEEK_END) == 0) {
|
|
2943
|
+
long sz = ftell(st->sorted_file);
|
|
2944
|
+
if (sz >= 0 && (size_t)sz > st->max_lookup_bytes) {
|
|
2945
|
+
if (set_limit_error(side, op, "max_lookup_bytes", st->max_lookup_bytes,
|
|
2946
|
+
(size_t)sz) != TF_OK)
|
|
2947
|
+
return TF_ERROR;
|
|
2948
|
+
return TF_ERROR;
|
|
2949
|
+
}
|
|
2950
|
+
fseek(st->sorted_file, 0, SEEK_SET);
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
st->sorted_decoder = tf_csv_decoder_create(NULL);
|
|
2954
|
+
if (!st->sorted_decoder) return TF_ERROR;
|
|
2955
|
+
return TF_OK;
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2958
|
+
static int union_next_decoded_batch(set_state *st, tf_batch **out,
|
|
2959
|
+
tf_side_channels *side) {
|
|
2960
|
+
const char *op = set_mode_name(st->mode);
|
|
2961
|
+
*out = NULL;
|
|
2962
|
+
for (;;) {
|
|
2963
|
+
if (st->sorted_batches && st->sorted_batch_index < st->sorted_n_batches) {
|
|
2964
|
+
*out = st->sorted_batches[st->sorted_batch_index++];
|
|
2965
|
+
return TF_OK;
|
|
2966
|
+
}
|
|
2967
|
+
sorted_set_free_pending_batches(st);
|
|
2968
|
+
if (st->sorted_flushed) {
|
|
2969
|
+
st->sorted_exhausted = 1;
|
|
2970
|
+
return TF_OK;
|
|
2971
|
+
}
|
|
2972
|
+
if (union_open_file(st, side) != TF_OK) return TF_ERROR;
|
|
2973
|
+
uint8_t buf[64 * 1024];
|
|
2974
|
+
size_t n = fread(buf, 1, sizeof(buf), st->sorted_file);
|
|
2975
|
+
tf_batch **batches = NULL;
|
|
2976
|
+
size_t n_batches = 0;
|
|
2977
|
+
int rc = TF_OK;
|
|
2978
|
+
if (n > 0) {
|
|
2979
|
+
rc = st->sorted_decoder->decode(st->sorted_decoder, buf, n, &batches, &n_batches, side);
|
|
2980
|
+
} else {
|
|
2981
|
+
if (ferror(st->sorted_file)) {
|
|
2982
|
+
char msg[160];
|
|
2983
|
+
snprintf(msg, sizeof(msg), "%s: failed reading file", op);
|
|
2984
|
+
if (set_write_error(side, msg) != TF_OK) return TF_ERROR;
|
|
2985
|
+
return TF_ERROR;
|
|
2986
|
+
}
|
|
2987
|
+
st->sorted_flushed = 1;
|
|
2988
|
+
rc = st->sorted_decoder->flush(st->sorted_decoder, &batches, &n_batches, side);
|
|
2989
|
+
}
|
|
2990
|
+
if (rc != TF_OK) {
|
|
2991
|
+
tf_batch_array_free(batches, n_batches);
|
|
2992
|
+
return TF_ERROR;
|
|
2993
|
+
}
|
|
2994
|
+
st->sorted_batches = batches;
|
|
2995
|
+
st->sorted_n_batches = n_batches;
|
|
2996
|
+
st->sorted_batch_index = 0;
|
|
2997
|
+
if (n_batches == 0) {
|
|
2998
|
+
free(batches);
|
|
2999
|
+
st->sorted_batches = NULL;
|
|
3000
|
+
if (st->sorted_flushed) {
|
|
3001
|
+
st->sorted_exhausted = 1;
|
|
3002
|
+
return TF_OK;
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
|
|
3008
|
+
static int union_flush_next(tf_step *self, tf_batch **out, tf_side_channels *side) {
|
|
3009
|
+
set_state *st = self->state;
|
|
3010
|
+
const char *op = set_mode_name(st->mode);
|
|
3011
|
+
*out = NULL;
|
|
3012
|
+
if (st->use_spill) return set_spill_output_next_batch(st, out, side);
|
|
3013
|
+
if (st->sorted) return union_sorted_flush_next(self, out, side);
|
|
3014
|
+
if (!st->union_schema_ready) return TF_OK;
|
|
3015
|
+
|
|
3016
|
+
for (;;) {
|
|
3017
|
+
tf_batch *b = NULL;
|
|
3018
|
+
if (union_next_decoded_batch(st, &b, side) != TF_OK) return TF_ERROR;
|
|
3019
|
+
if (!b) return TF_OK;
|
|
3020
|
+
if (union_capture_right_schema(st, b, side) != TF_OK) { tf_batch_free(b); return TF_ERROR; }
|
|
3021
|
+
|
|
3022
|
+
tf_batch *ob = set_create_output_like_union_schema(st, b->n_rows > 0 ? b->n_rows : 1);
|
|
3023
|
+
if (!ob) { tf_batch_free(b); return TF_ERROR; }
|
|
3024
|
+
size_t out_row = 0;
|
|
3025
|
+
for (size_t r = 0; r < b->n_rows; r++) {
|
|
3026
|
+
st->union_file_rows++;
|
|
3027
|
+
if (st->max_lookup_rows > 0 && st->union_file_rows > st->max_lookup_rows) {
|
|
3028
|
+
int err_rc = set_limit_error(side, op, "max_lookup_rows",
|
|
3029
|
+
st->max_lookup_rows, st->union_file_rows);
|
|
3030
|
+
tf_batch_free(ob);
|
|
3031
|
+
tf_batch_free(b);
|
|
3032
|
+
if (err_rc != TF_OK) return TF_ERROR;
|
|
3033
|
+
return TF_ERROR;
|
|
3034
|
+
}
|
|
3035
|
+
if (st->mode == 2) {
|
|
3036
|
+
char *key = format_set_key(b, r, st->right_cols, st->n_key_cols);
|
|
3037
|
+
if (!key) { tf_batch_free(ob); tf_batch_free(b); return TF_ERROR; }
|
|
3038
|
+
int inserted = 0;
|
|
3039
|
+
if (set_map_insert_owned(&st->emitted, key, st->max_output_keys,
|
|
3040
|
+
op, "max_output_keys", side, &inserted) != TF_OK) {
|
|
3041
|
+
tf_batch_free(ob);
|
|
3042
|
+
tf_batch_free(b);
|
|
3043
|
+
return TF_ERROR;
|
|
3044
|
+
}
|
|
3045
|
+
if (inserted && set_check_state_bytes(st, side) != TF_OK) {
|
|
3046
|
+
tf_batch_free(ob);
|
|
3047
|
+
tf_batch_free(b);
|
|
3048
|
+
return TF_ERROR;
|
|
3049
|
+
}
|
|
3050
|
+
if (!inserted) continue;
|
|
3051
|
+
}
|
|
3052
|
+
if (tf_batch_ensure_capacity(ob, out_row + 1) != TF_OK) {
|
|
3053
|
+
tf_batch_free(ob);
|
|
3054
|
+
tf_batch_free(b);
|
|
3055
|
+
return TF_ERROR;
|
|
3056
|
+
}
|
|
3057
|
+
for (size_t c = 0; c < st->union_n_cols; c++) {
|
|
3058
|
+
if (set_copy_cell(ob, out_row, c, b, r, st->union_right_cols[c], side, op) != TF_OK) {
|
|
3059
|
+
tf_batch_free(ob);
|
|
3060
|
+
tf_batch_free(b);
|
|
3061
|
+
return TF_ERROR;
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
if (tf_batch_expose_row(ob, out_row) != TF_OK) {
|
|
3065
|
+
tf_batch_free(ob);
|
|
3066
|
+
tf_batch_free(b);
|
|
3067
|
+
return TF_ERROR;
|
|
3068
|
+
}
|
|
3069
|
+
out_row++;
|
|
3070
|
+
}
|
|
3071
|
+
tf_batch_free(b);
|
|
3072
|
+
if (out_row > 0) { *out = ob; return TF_OK; }
|
|
3073
|
+
tf_batch_free(ob);
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
|
|
3077
|
+
static int union_flush(tf_step *self, tf_batch **out, tf_side_channels *side) {
|
|
3078
|
+
return union_flush_next(self, out, side);
|
|
3079
|
+
}
|
|
3080
|
+
|
|
3081
|
+
static size_t set_map_retained_bytes(const set_hash_map *m) {
|
|
3082
|
+
if (!m || !m->buckets) return 0;
|
|
3083
|
+
return m->key_bytes + m->n_buckets * sizeof(set_bucket);
|
|
3084
|
+
}
|
|
3085
|
+
|
|
3086
|
+
static size_t set_retained_state_bytes(const set_state *st) {
|
|
3087
|
+
if (!st) return 0;
|
|
3088
|
+
if (st->use_spill) {
|
|
3089
|
+
size_t bytes = 0;
|
|
3090
|
+
if (st->spill_left_buf) bytes += st->spill_left_buf->capacity * (sizeof(uint64_t) + 16);
|
|
3091
|
+
if (st->spill_lookup_buf) bytes += st->spill_lookup_buf->capacity * (sizeof(uint64_t) + 16);
|
|
3092
|
+
if (st->spill_out_buf) bytes += st->spill_out_buf->capacity * (sizeof(uint64_t) + 16);
|
|
3093
|
+
bytes += st->spill_n_left_readers * sizeof(set_spill_reader);
|
|
3094
|
+
bytes += st->spill_n_lookup_readers * sizeof(set_spill_reader);
|
|
3095
|
+
bytes += st->spill_n_out_readers * sizeof(set_spill_reader);
|
|
3096
|
+
bytes += st->spill_last_lookup_key ? strlen(st->spill_last_lookup_key) + 1 : 0;
|
|
3097
|
+
bytes += st->spill_last_left_key ? strlen(st->spill_last_left_key) + 1 : 0;
|
|
3098
|
+
return bytes;
|
|
3099
|
+
}
|
|
3100
|
+
return set_map_retained_bytes(&st->lookup) + set_map_retained_bytes(&st->emitted);
|
|
3101
|
+
}
|
|
3102
|
+
|
|
3103
|
+
static int set_check_state_bytes(const set_state *st, tf_side_channels *side) {
|
|
3104
|
+
if (!st || st->max_state_bytes == 0 || st->sorted || st->mode == 3 || st->use_spill) return TF_OK;
|
|
3105
|
+
size_t retained = set_retained_state_bytes(st);
|
|
3106
|
+
if (retained <= st->max_state_bytes) return TF_OK;
|
|
3107
|
+
|
|
3108
|
+
char msg[256];
|
|
3109
|
+
snprintf(msg, sizeof(msg),
|
|
3110
|
+
"%s: max_state_bytes=%zu exceeded while tracking set keys (%zu bytes retained)",
|
|
3111
|
+
set_mode_name(st->mode), st->max_state_bytes, retained);
|
|
3112
|
+
if (set_write_error(side, msg) != TF_OK) return TF_ERROR;
|
|
3113
|
+
return TF_ERROR;
|
|
3114
|
+
}
|
|
3115
|
+
|
|
3116
|
+
static int set_append_stats(tf_step *self, tf_buffer *out) {
|
|
3117
|
+
if (!self || !self->state || !out) return TF_ERROR;
|
|
3118
|
+
set_state *st = self->state;
|
|
3119
|
+
int bag_mode = st->mode == 4 || st->mode == 5;
|
|
3120
|
+
size_t lookup_keys = st->use_spill ? st->spill_lookup_keys : st->lookup.count;
|
|
3121
|
+
size_t lookup_key_bytes = st->use_spill ? st->spill_lookup_key_bytes : st->lookup.key_bytes;
|
|
3122
|
+
size_t emitted_keys = st->use_spill ? (bag_mode ? 0 : st->spill_distinct_rows) : st->emitted.count;
|
|
3123
|
+
size_t emitted_key_bytes = st->use_spill ? 0 : st->emitted.key_bytes;
|
|
3124
|
+
size_t retained = set_retained_state_bytes(st);
|
|
3125
|
+
char buf[512];
|
|
3126
|
+
snprintf(buf, sizeof(buf),
|
|
3127
|
+
",\"lookup_keys\":%zu,\"lookup_key_bytes\":%zu,"
|
|
3128
|
+
"\"emitted_keys\":%zu,\"emitted_key_bytes\":%zu,"
|
|
3129
|
+
"\"retained_state_bytes\":%zu,\"max_state_bytes\":%zu",
|
|
3130
|
+
lookup_keys, lookup_key_bytes, emitted_keys, emitted_key_bytes, retained, st->max_state_bytes);
|
|
3131
|
+
if (tf_buffer_write_str(out, buf) != TF_OK) return TF_ERROR;
|
|
3132
|
+
if (st->use_spill) {
|
|
3133
|
+
snprintf(buf, sizeof(buf),
|
|
3134
|
+
",\"spill_bytes\":%zu,\"spill_runs\":%zu,"
|
|
3135
|
+
"\"spill_output_batches\":%zu,\"spill_output_rows\":%zu",
|
|
3136
|
+
st->spill_bytes, st->spill_runs,
|
|
3137
|
+
st->spill_output_batches, st->spill_output_rows);
|
|
3138
|
+
if (tf_buffer_write_str(out, buf) != TF_OK) return TF_ERROR;
|
|
3139
|
+
if (bag_mode) {
|
|
3140
|
+
snprintf(buf, sizeof(buf), ",\"spill_kept_rows\":%zu", st->spill_kept_rows);
|
|
3141
|
+
} else {
|
|
3142
|
+
snprintf(buf, sizeof(buf), ",\"spill_distinct_rows\":%zu", st->spill_distinct_rows);
|
|
3143
|
+
}
|
|
3144
|
+
if (tf_buffer_write_str(out, buf) != TF_OK) return TF_ERROR;
|
|
3145
|
+
}
|
|
3146
|
+
return TF_OK;
|
|
3147
|
+
}
|
|
3148
|
+
|
|
3149
|
+
static int set_flush(tf_step *self, tf_batch **out, tf_side_channels *side) {
|
|
3150
|
+
set_state *st = self ? self->state : NULL;
|
|
3151
|
+
*out = NULL;
|
|
3152
|
+
if (!st || !st->use_spill) return TF_OK;
|
|
3153
|
+
return set_spill_output_next_batch(st, out, side);
|
|
3154
|
+
}
|
|
3155
|
+
|
|
3156
|
+
static int set_flush_next(tf_step *self, tf_batch **out, tf_side_channels *side) {
|
|
3157
|
+
set_state *st = self ? self->state : NULL;
|
|
3158
|
+
*out = NULL;
|
|
3159
|
+
if (!st || !st->use_spill) return TF_OK;
|
|
3160
|
+
return set_spill_output_next_batch(st, out, side);
|
|
3161
|
+
}
|
|
3162
|
+
|
|
3163
|
+
static void set_state_free(set_state *st) {
|
|
3164
|
+
if (!st) return;
|
|
3165
|
+
free(st->file);
|
|
3166
|
+
free(st->validated_file);
|
|
3167
|
+
if (st->columns) {
|
|
3168
|
+
for (size_t i = 0; i < st->n_columns; i++) free(st->columns[i]);
|
|
3169
|
+
free(st->columns);
|
|
3170
|
+
}
|
|
3171
|
+
if (st->key_names) {
|
|
3172
|
+
for (size_t i = 0; i < st->n_key_cols; i++) free(st->key_names[i]);
|
|
3173
|
+
free(st->key_names);
|
|
3174
|
+
}
|
|
3175
|
+
union_sorted_clear_right_row(st);
|
|
3176
|
+
if (st->union_col_names) {
|
|
3177
|
+
for (size_t i = 0; i < st->union_n_cols; i++) free(st->union_col_names[i]);
|
|
3178
|
+
free(st->union_col_names);
|
|
3179
|
+
}
|
|
3180
|
+
free(st->union_col_types);
|
|
3181
|
+
free(st->union_right_cols);
|
|
3182
|
+
set_spill_close_readers(st, 0);
|
|
3183
|
+
set_spill_close_readers(st, 1);
|
|
3184
|
+
set_spill_close_readers(st, 2);
|
|
3185
|
+
if (st->spill_schema_names) {
|
|
3186
|
+
for (size_t i = 0; i < st->spill_n_cols; i++) free(st->spill_schema_names[i]);
|
|
3187
|
+
free(st->spill_schema_names);
|
|
3188
|
+
}
|
|
3189
|
+
free(st->spill_schema_types);
|
|
3190
|
+
free(st->spill_key_types);
|
|
3191
|
+
if (st->spill_left_buf) tf_batch_free(st->spill_left_buf);
|
|
3192
|
+
if (st->spill_lookup_buf) tf_batch_free(st->spill_lookup_buf);
|
|
3193
|
+
if (st->spill_out_buf) tf_batch_free(st->spill_out_buf);
|
|
3194
|
+
free(st->spill_left_ordinals);
|
|
3195
|
+
free(st->spill_lookup_ordinals);
|
|
3196
|
+
free(st->spill_out_ordinals);
|
|
3197
|
+
set_spill_remove_paths(&st->spill_left_run_paths, &st->spill_n_left_runs, &st->spill_cap_left_runs);
|
|
3198
|
+
set_spill_remove_paths(&st->spill_lookup_run_paths, &st->spill_n_lookup_runs, &st->spill_cap_lookup_runs);
|
|
3199
|
+
set_spill_remove_paths(&st->spill_out_run_paths, &st->spill_n_out_runs, &st->spill_cap_out_runs);
|
|
3200
|
+
free(st->spill_last_lookup_key);
|
|
3201
|
+
free(st->spill_last_left_key);
|
|
3202
|
+
tf_spill_cleanup(st->spill);
|
|
3203
|
+
free(st->spill_dir);
|
|
3204
|
+
free(st->left_cols);
|
|
3205
|
+
free(st->right_cols);
|
|
3206
|
+
set_map_free(&st->lookup);
|
|
3207
|
+
set_map_free(&st->emitted);
|
|
3208
|
+
sorted_set_close(st);
|
|
3209
|
+
set_key_tuple_clear(&st->prev_lookup_key);
|
|
3210
|
+
set_key_tuple_clear(&st->current_lookup_key);
|
|
3211
|
+
set_key_tuple_clear(&st->prev_left_key);
|
|
3212
|
+
free(st);
|
|
3213
|
+
}
|
|
3214
|
+
|
|
3215
|
+
static void set_destroy(tf_step *self) {
|
|
3216
|
+
if (self) {
|
|
3217
|
+
set_state_free(self->state);
|
|
3218
|
+
free(self);
|
|
3219
|
+
}
|
|
3220
|
+
}
|
|
3221
|
+
|
|
3222
|
+
static int parse_positive_size_arg(const cJSON *args, const char *name,
|
|
3223
|
+
size_t max_value,
|
|
3224
|
+
size_t *out, const char *op_name) {
|
|
3225
|
+
return tf_json_get_size_arg(args, name, 1, max_value, out, op_name);
|
|
3226
|
+
}
|
|
3227
|
+
|
|
3228
|
+
static tf_step *set_create_common(const cJSON *args, int mode) {
|
|
3229
|
+
if (!args) return NULL;
|
|
3230
|
+
const char *op = mode == 0 ? "intersect" : (mode == 1 ? "setdiff" : (mode == 4 ? "intersect-all" : "setdiff-all"));
|
|
3231
|
+
cJSON *file_j = cJSON_GetObjectItemCaseSensitive(args, "file");
|
|
3232
|
+
if (!cJSON_IsString(file_j) || !file_j->valuestring || !file_j->valuestring[0]) {
|
|
3233
|
+
tf_set_last_error("set op: file is required");
|
|
3234
|
+
return NULL;
|
|
3235
|
+
}
|
|
3236
|
+
|
|
3237
|
+
set_state *st = calloc(1, sizeof(set_state));
|
|
3238
|
+
if (!st) return NULL;
|
|
3239
|
+
st->mode = mode;
|
|
3240
|
+
st->file = strdup(file_j->valuestring);
|
|
3241
|
+
if (!st->file) { set_state_free(st); return NULL; }
|
|
3242
|
+
const char *validated = tf_policy_validated_path_arg(args, "file");
|
|
3243
|
+
if (validated) {
|
|
3244
|
+
st->validated_file = strdup(validated);
|
|
3245
|
+
if (!st->validated_file) { set_state_free(st); return NULL; }
|
|
3246
|
+
}
|
|
3247
|
+
|
|
3248
|
+
|
|
3249
|
+
cJSON *cols = cJSON_GetObjectItemCaseSensitive(args, "columns");
|
|
3250
|
+
if (cols && cJSON_IsArray(cols)) {
|
|
3251
|
+
int n = cJSON_GetArraySize(cols);
|
|
3252
|
+
st->columns = tf_callocarray_checked(n > 0 ? (size_t)n : 1, sizeof(char *));
|
|
3253
|
+
if (!st->columns) { set_state_free(st); return NULL; }
|
|
3254
|
+
for (int i = 0; i < n; i++) {
|
|
3255
|
+
cJSON *item = cJSON_GetArrayItem(cols, i);
|
|
3256
|
+
if (!cJSON_IsString(item) || !item->valuestring || !item->valuestring[0]) {
|
|
3257
|
+
tf_set_last_error("set op: columns must be non-empty strings");
|
|
3258
|
+
set_state_free(st);
|
|
3259
|
+
return NULL;
|
|
3260
|
+
}
|
|
3261
|
+
st->columns[st->n_columns] = strdup(item->valuestring);
|
|
3262
|
+
if (!st->columns[st->n_columns]) { set_state_free(st); return NULL; }
|
|
3263
|
+
st->n_columns++;
|
|
3264
|
+
}
|
|
3265
|
+
}
|
|
3266
|
+
|
|
3267
|
+
cJSON *sorted_j = cJSON_GetObjectItemCaseSensitive(args, "sorted");
|
|
3268
|
+
if (sorted_j) {
|
|
3269
|
+
if (cJSON_IsTrue(sorted_j)) st->sorted = 1;
|
|
3270
|
+
else if (!cJSON_IsFalse(sorted_j)) {
|
|
3271
|
+
tf_set_last_error("set op: sorted must be boolean");
|
|
3272
|
+
set_state_free(st);
|
|
3273
|
+
return NULL;
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
|
|
3277
|
+
if (parse_positive_size_arg(args, "max_lookup_rows", TF_MAX_COUNT_ARG, &st->max_lookup_rows, op) < 0 ||
|
|
3278
|
+
parse_positive_size_arg(args, "max_lookup_keys", TF_MAX_COUNT_ARG, &st->max_lookup_keys, op) < 0 ||
|
|
3279
|
+
parse_positive_size_arg(args, "max_lookup_bytes", TF_MAX_STATE_BYTES, &st->max_lookup_bytes, op) < 0 ||
|
|
3280
|
+
parse_positive_size_arg(args, "max_output_keys", TF_MAX_COUNT_ARG, &st->max_output_keys, op) < 0 ||
|
|
3281
|
+
parse_positive_size_arg(args, "max_state_bytes", TF_MAX_STATE_BYTES, &st->max_state_bytes, op) < 0 ||
|
|
3282
|
+
parse_positive_size_arg(args, "spill_memory_bytes", TF_MAX_SPILL_MEMORY_BYTES, &st->spill_memory_bytes, op) < 0 ||
|
|
3283
|
+
parse_positive_size_arg(args, "spill_run_rows", TF_MAX_SPILL_RUN_ROWS, &st->configured_run_rows, op) < 0 ||
|
|
3284
|
+
parse_positive_size_arg(args, "spill_output_rows", TF_MAX_SPILL_OUTPUT_ROWS, &st->output_batch_rows, op) < 0) {
|
|
3285
|
+
set_state_free(st);
|
|
3286
|
+
return NULL;
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3289
|
+
if ((mode == 4 || mode == 5) && st->max_output_keys > 0) {
|
|
3290
|
+
tf_set_last_error("set op: max_output_keys is only valid for duplicate-eliminating set ops");
|
|
3291
|
+
set_state_free(st);
|
|
3292
|
+
return NULL;
|
|
3293
|
+
}
|
|
3294
|
+
cJSON *spill_j = cJSON_GetObjectItemCaseSensitive(args, "spill_dir");
|
|
3295
|
+
if (spill_j) {
|
|
3296
|
+
if (!cJSON_IsString(spill_j) || !spill_j->valuestring || !spill_j->valuestring[0]) {
|
|
3297
|
+
tf_set_last_error("set op: spill_dir must be a non-empty string");
|
|
3298
|
+
set_state_free(st);
|
|
3299
|
+
return NULL;
|
|
3300
|
+
}
|
|
3301
|
+
if (st->sorted) {
|
|
3302
|
+
tf_set_last_error("set op: spill_dir is only valid for unsorted set ops");
|
|
3303
|
+
set_state_free(st);
|
|
3304
|
+
return NULL;
|
|
3305
|
+
}
|
|
3306
|
+
st->spill_dir = strdup(spill_j->valuestring);
|
|
3307
|
+
if (!st->spill_dir) { set_state_free(st); return NULL; }
|
|
3308
|
+
if (tf_spill_session_create(st->spill_dir, &st->spill) != TF_OK) { set_state_free(st); return NULL; }
|
|
3309
|
+
st->use_spill = 1;
|
|
3310
|
+
}
|
|
3311
|
+
|
|
3312
|
+
tf_step *step = calloc(1, sizeof(tf_step));
|
|
3313
|
+
if (!step) { set_state_free(st); return NULL; }
|
|
3314
|
+
step->process = set_process;
|
|
3315
|
+
step->flush = set_flush;
|
|
3316
|
+
step->flush_next = st->use_spill ? set_flush_next : NULL;
|
|
3317
|
+
step->append_stats = set_append_stats;
|
|
3318
|
+
step->destroy = set_destroy;
|
|
3319
|
+
step->state = st;
|
|
3320
|
+
return step;
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
tf_step *tf_intersect_create(const cJSON *args) {
|
|
3324
|
+
return set_create_common(args, 0);
|
|
3325
|
+
}
|
|
3326
|
+
|
|
3327
|
+
tf_step *tf_setdiff_create(const cJSON *args) {
|
|
3328
|
+
return set_create_common(args, 1);
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
tf_step *tf_intersect_all_create(const cJSON *args) {
|
|
3332
|
+
return set_create_common(args, 4);
|
|
3333
|
+
}
|
|
3334
|
+
|
|
3335
|
+
tf_step *tf_setdiff_all_create(const cJSON *args) {
|
|
3336
|
+
return set_create_common(args, 5);
|
|
3337
|
+
}
|
|
3338
|
+
|
|
3339
|
+
|
|
3340
|
+
static tf_step *union_create_common(const cJSON *args, int all) {
|
|
3341
|
+
if (!args) return NULL;
|
|
3342
|
+
const char *op = all ? "union-all" : "union";
|
|
3343
|
+
cJSON *file_j = cJSON_GetObjectItemCaseSensitive(args, "file");
|
|
3344
|
+
if (!cJSON_IsString(file_j) || !file_j->valuestring || !file_j->valuestring[0]) {
|
|
3345
|
+
tf_set_last_error("union: file is required");
|
|
3346
|
+
return NULL;
|
|
3347
|
+
}
|
|
3348
|
+
set_state *st = calloc(1, sizeof(set_state));
|
|
3349
|
+
if (!st) return NULL;
|
|
3350
|
+
st->mode = all ? 3 : 2;
|
|
3351
|
+
st->file = strdup(file_j->valuestring);
|
|
3352
|
+
if (!st->file) { set_state_free(st); return NULL; }
|
|
3353
|
+
const char *validated = tf_policy_validated_path_arg(args, "file");
|
|
3354
|
+
if (validated) {
|
|
3355
|
+
st->validated_file = strdup(validated);
|
|
3356
|
+
if (!st->validated_file) { set_state_free(st); return NULL; }
|
|
3357
|
+
}
|
|
3358
|
+
|
|
3359
|
+
cJSON *sorted_j = cJSON_GetObjectItemCaseSensitive(args, "sorted");
|
|
3360
|
+
if (sorted_j) {
|
|
3361
|
+
if (cJSON_IsTrue(sorted_j)) {
|
|
3362
|
+
if (all) {
|
|
3363
|
+
tf_set_last_error("union-all: sorted=true is only valid for duplicate-eliminating union");
|
|
3364
|
+
set_state_free(st);
|
|
3365
|
+
return NULL;
|
|
3366
|
+
}
|
|
3367
|
+
st->sorted = 1;
|
|
3368
|
+
} else if (!cJSON_IsFalse(sorted_j)) {
|
|
3369
|
+
tf_set_last_error("union: sorted must be boolean");
|
|
3370
|
+
set_state_free(st);
|
|
3371
|
+
return NULL;
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
|
|
3375
|
+
cJSON *cols = cJSON_GetObjectItemCaseSensitive(args, "columns");
|
|
3376
|
+
if (cols && cJSON_IsArray(cols)) {
|
|
3377
|
+
int n = cJSON_GetArraySize(cols);
|
|
3378
|
+
st->columns = tf_callocarray_checked(n > 0 ? (size_t)n : 1, sizeof(char *));
|
|
3379
|
+
if (!st->columns) { set_state_free(st); return NULL; }
|
|
3380
|
+
for (int i = 0; i < n; i++) {
|
|
3381
|
+
cJSON *item = cJSON_GetArrayItem(cols, i);
|
|
3382
|
+
if (!cJSON_IsString(item) || !item->valuestring || !item->valuestring[0]) {
|
|
3383
|
+
tf_set_last_error("union: columns must be non-empty strings");
|
|
3384
|
+
set_state_free(st);
|
|
3385
|
+
return NULL;
|
|
3386
|
+
}
|
|
3387
|
+
st->columns[st->n_columns] = strdup(item->valuestring);
|
|
3388
|
+
if (!st->columns[st->n_columns]) { set_state_free(st); return NULL; }
|
|
3389
|
+
st->n_columns++;
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3392
|
+
|
|
3393
|
+
if (parse_positive_size_arg(args, "max_lookup_rows", TF_MAX_COUNT_ARG, &st->max_lookup_rows, op) < 0 ||
|
|
3394
|
+
parse_positive_size_arg(args, "max_lookup_bytes", TF_MAX_STATE_BYTES, &st->max_lookup_bytes, op) < 0 ||
|
|
3395
|
+
parse_positive_size_arg(args, "max_output_keys", TF_MAX_COUNT_ARG, &st->max_output_keys, op) < 0 ||
|
|
3396
|
+
parse_positive_size_arg(args, "max_state_bytes", TF_MAX_STATE_BYTES, &st->max_state_bytes, op) < 0 ||
|
|
3397
|
+
parse_positive_size_arg(args, "spill_memory_bytes", TF_MAX_SPILL_MEMORY_BYTES, &st->spill_memory_bytes, op) < 0 ||
|
|
3398
|
+
parse_positive_size_arg(args, "spill_run_rows", TF_MAX_SPILL_RUN_ROWS, &st->configured_run_rows, op) < 0 ||
|
|
3399
|
+
parse_positive_size_arg(args, "spill_output_rows", TF_MAX_SPILL_OUTPUT_ROWS, &st->output_batch_rows, op) < 0) {
|
|
3400
|
+
set_state_free(st);
|
|
3401
|
+
return NULL;
|
|
3402
|
+
}
|
|
3403
|
+
if (all && st->max_state_bytes > 0) {
|
|
3404
|
+
tf_set_last_error("union-all: max_state_bytes is only valid for duplicate-eliminating set ops");
|
|
3405
|
+
set_state_free(st);
|
|
3406
|
+
return NULL;
|
|
3407
|
+
}
|
|
3408
|
+
|
|
3409
|
+
cJSON *spill_j = cJSON_GetObjectItemCaseSensitive(args, "spill_dir");
|
|
3410
|
+
if (spill_j) {
|
|
3411
|
+
if (!cJSON_IsString(spill_j) || !spill_j->valuestring || !spill_j->valuestring[0]) {
|
|
3412
|
+
tf_set_last_error("union: spill_dir must be a non-empty string");
|
|
3413
|
+
set_state_free(st);
|
|
3414
|
+
return NULL;
|
|
3415
|
+
}
|
|
3416
|
+
if (all) {
|
|
3417
|
+
tf_set_last_error("union-all: spill_dir is only valid for duplicate-eliminating union");
|
|
3418
|
+
set_state_free(st);
|
|
3419
|
+
return NULL;
|
|
3420
|
+
}
|
|
3421
|
+
if (st->sorted) {
|
|
3422
|
+
tf_set_last_error("union: spill_dir is only valid for unsorted union");
|
|
3423
|
+
set_state_free(st);
|
|
3424
|
+
return NULL;
|
|
3425
|
+
}
|
|
3426
|
+
st->spill_dir = strdup(spill_j->valuestring);
|
|
3427
|
+
if (!st->spill_dir) { set_state_free(st); return NULL; }
|
|
3428
|
+
if (tf_spill_session_create(st->spill_dir, &st->spill) != TF_OK) { set_state_free(st); return NULL; }
|
|
3429
|
+
st->use_spill = 1;
|
|
3430
|
+
}
|
|
3431
|
+
|
|
3432
|
+
tf_step *step = calloc(1, sizeof(tf_step));
|
|
3433
|
+
if (!step) { set_state_free(st); return NULL; }
|
|
3434
|
+
step->process = union_process;
|
|
3435
|
+
step->flush = union_flush;
|
|
3436
|
+
step->flush_next = union_flush_next;
|
|
3437
|
+
step->append_stats = set_append_stats;
|
|
3438
|
+
step->destroy = set_destroy;
|
|
3439
|
+
step->state = st;
|
|
3440
|
+
return step;
|
|
3441
|
+
}
|
|
3442
|
+
|
|
3443
|
+
tf_step *tf_union_create(const cJSON *args) {
|
|
3444
|
+
return union_create_common(args, 0);
|
|
3445
|
+
}
|
|
3446
|
+
|
|
3447
|
+
tf_step *tf_union_all_create(const cJSON *args) {
|
|
3448
|
+
return union_create_common(args, 1);
|
|
3449
|
+
}
|