tranfi 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +177 -0
- package/NOTICE +8 -0
- package/README.md +272 -40
- 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 +348 -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 +8 -11
- 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/selector.c
ADDED
|
@@ -0,0 +1,1097 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* selector.c -- Column selector expansion for schema-aware row-local ops.
|
|
3
|
+
*
|
|
4
|
+
* Supported syntax is a compact, C-friendly subset of tidyselect:
|
|
5
|
+
* name, !name, -name
|
|
6
|
+
* starts_with(prefix), ends_with(suffix), contains(substr), matches(regex)
|
|
7
|
+
* where(type)
|
|
8
|
+
* all_of(name,...), any_of(name,...)
|
|
9
|
+
* start:end
|
|
10
|
+
* !selection, selection1 & selection2, selection1 | selection2, (selection)
|
|
11
|
+
*
|
|
12
|
+
* Pattern helpers are case-insensitive for parity with tidyselect defaults.
|
|
13
|
+
* all_of() is strict about missing names; any_of() is lenient.
|
|
14
|
+
* Ranges are strict exact-name endpoints over the incoming schema order.
|
|
15
|
+
* Boolean selector algebra is schema-only and never inspects row data.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#include "internal.h"
|
|
19
|
+
#include "cJSON.h"
|
|
20
|
+
#include <stdlib.h>
|
|
21
|
+
#include <string.h>
|
|
22
|
+
#include <strings.h>
|
|
23
|
+
#include <ctype.h>
|
|
24
|
+
#include <stdio.h>
|
|
25
|
+
#include <regex.h>
|
|
26
|
+
#include <limits.h>
|
|
27
|
+
|
|
28
|
+
#define TF_SELECTOR_MAX_DEPTH 256
|
|
29
|
+
|
|
30
|
+
static char *selector_strdup_range(const char *start, size_t len) {
|
|
31
|
+
char *out = malloc(len + 1);
|
|
32
|
+
if (!out) return NULL;
|
|
33
|
+
memcpy(out, start, len);
|
|
34
|
+
out[len] = '\0';
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static char *selector_trim_copy(const char *s) {
|
|
39
|
+
if (!s) return NULL;
|
|
40
|
+
while (isspace((unsigned char)*s)) s++;
|
|
41
|
+
size_t len = strlen(s);
|
|
42
|
+
while (len > 0 && isspace((unsigned char)s[len - 1])) len--;
|
|
43
|
+
return selector_strdup_range(s, len);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static void selector_set_error(char **error, const char *msg, const char *arg) {
|
|
47
|
+
if (!error) return;
|
|
48
|
+
if (!msg) msg = "selector error";
|
|
49
|
+
if (!arg) arg = "";
|
|
50
|
+
size_t n = strlen(msg) + strlen(arg) + 8;
|
|
51
|
+
char *buf = malloc(n);
|
|
52
|
+
if (!buf) return;
|
|
53
|
+
snprintf(buf, n, "%s%s%s", msg, *arg ? ": " : "", arg);
|
|
54
|
+
*error = buf;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static int selector_is_name_list_helper(const char *func) {
|
|
58
|
+
return func && (strcasecmp(func, "all_of") == 0 || strcasecmp(func, "any_of") == 0);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static const char *selector_find_range_colon(const char *s) {
|
|
62
|
+
if (!s) return NULL;
|
|
63
|
+
int paren = 0, bracket = 0, brace = 0;
|
|
64
|
+
char quote = '\0';
|
|
65
|
+
for (const char *p = s; *p; p++) {
|
|
66
|
+
char c = *p;
|
|
67
|
+
if (quote != '\0') {
|
|
68
|
+
if (c == quote) quote = '\0';
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (c == '\'' || c == '"') {
|
|
72
|
+
quote = c;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (c == '(') { paren++; continue; }
|
|
76
|
+
if (c == ')' && paren > 0) { paren--; continue; }
|
|
77
|
+
if (c == '[') { bracket++; continue; }
|
|
78
|
+
if (c == ']' && bracket > 0) { bracket--; continue; }
|
|
79
|
+
if (c == '{') { brace++; continue; }
|
|
80
|
+
if (c == '}' && brace > 0) { brace--; continue; }
|
|
81
|
+
if (c == ':' && paren == 0 && bracket == 0 && brace == 0 && p != s && p[1] != '\0') return p;
|
|
82
|
+
}
|
|
83
|
+
return NULL;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
static int selector_has_range_syntax(const char *s) {
|
|
87
|
+
return selector_find_range_colon(s) != NULL;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
static int selector_has_boolean_algebra(const char *raw) {
|
|
91
|
+
char *s = selector_trim_copy(raw);
|
|
92
|
+
if (!s) return 0;
|
|
93
|
+
char *p = s;
|
|
94
|
+
int has = 0;
|
|
95
|
+
if (*p == '(') {
|
|
96
|
+
has = 1;
|
|
97
|
+
goto done;
|
|
98
|
+
}
|
|
99
|
+
if ((*p == '!' || *p == '-') && p[1] != '\0') {
|
|
100
|
+
char *q = p + 1;
|
|
101
|
+
while (isspace((unsigned char)*q)) q++;
|
|
102
|
+
if (*q == '(') {
|
|
103
|
+
has = 1;
|
|
104
|
+
goto done;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
int paren = 0, bracket = 0, brace = 0;
|
|
109
|
+
char quote = '\0';
|
|
110
|
+
for (char *q = p; *q; q++) {
|
|
111
|
+
char c = *q;
|
|
112
|
+
if (quote != '\0') {
|
|
113
|
+
if (c == quote) quote = '\0';
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (c == '\'' || c == '"') {
|
|
117
|
+
quote = c;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (c == '(') { paren++; continue; }
|
|
121
|
+
if (c == ')' && paren > 0) { paren--; continue; }
|
|
122
|
+
if (c == '[') { bracket++; continue; }
|
|
123
|
+
if (c == ']' && bracket > 0) { bracket--; continue; }
|
|
124
|
+
if (c == '{') { brace++; continue; }
|
|
125
|
+
if (c == '}' && brace > 0) { brace--; continue; }
|
|
126
|
+
if ((c == '&' || c == '|') && paren == 0 && bracket == 0 && brace == 0) {
|
|
127
|
+
has = 1;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
done:
|
|
133
|
+
free(s);
|
|
134
|
+
return has;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
int tf_column_selector_has_syntax(const char *raw) {
|
|
138
|
+
char *s = selector_trim_copy(raw);
|
|
139
|
+
if (!s) return 0;
|
|
140
|
+
char *p = s;
|
|
141
|
+
if ((p[0] == '!' || p[0] == '-') && p[1] != '\0') {
|
|
142
|
+
free(s);
|
|
143
|
+
return 1;
|
|
144
|
+
}
|
|
145
|
+
int has = selector_has_boolean_algebra(p) ||
|
|
146
|
+
(strchr(p, '(') != NULL && p[strlen(p) ? strlen(p) - 1 : 0] == ')') ||
|
|
147
|
+
selector_has_range_syntax(p);
|
|
148
|
+
free(s);
|
|
149
|
+
return has;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
static int parse_selector(const char *raw, int *negated, char **func, char **arg, char **exact) {
|
|
153
|
+
*negated = 0;
|
|
154
|
+
*func = NULL;
|
|
155
|
+
*arg = NULL;
|
|
156
|
+
*exact = NULL;
|
|
157
|
+
|
|
158
|
+
char *s = selector_trim_copy(raw);
|
|
159
|
+
if (!s) return TF_ERROR;
|
|
160
|
+
char *p = s;
|
|
161
|
+
if ((p[0] == '!' || p[0] == '-') && p[1] != '\0') {
|
|
162
|
+
*negated = 1;
|
|
163
|
+
p++;
|
|
164
|
+
while (isspace((unsigned char)*p)) p++;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
size_t len = strlen(p);
|
|
168
|
+
char *lp = strchr(p, '(');
|
|
169
|
+
if (lp && len > 0 && p[len - 1] == ')') {
|
|
170
|
+
char *fname = selector_strdup_range(p, (size_t)(lp - p));
|
|
171
|
+
char *farg = selector_strdup_range(lp + 1, (size_t)((p + len - 1) - (lp + 1)));
|
|
172
|
+
if (!fname || !farg) { free(s); free(fname); free(farg); return TF_ERROR; }
|
|
173
|
+
char *fname_trim = selector_trim_copy(fname);
|
|
174
|
+
char *arg_trim = selector_trim_copy(farg);
|
|
175
|
+
free(fname);
|
|
176
|
+
free(farg);
|
|
177
|
+
if (!fname_trim || !arg_trim) { free(s); free(fname_trim); free(arg_trim); return TF_ERROR; }
|
|
178
|
+
size_t alen = strlen(arg_trim);
|
|
179
|
+
if (!selector_is_name_list_helper(fname_trim) &&
|
|
180
|
+
alen >= 2 && ((arg_trim[0] == '\'' && arg_trim[alen - 1] == '\'') ||
|
|
181
|
+
(arg_trim[0] == '"' && arg_trim[alen - 1] == '"'))) {
|
|
182
|
+
char *unquoted = selector_strdup_range(arg_trim + 1, alen - 2);
|
|
183
|
+
free(arg_trim);
|
|
184
|
+
arg_trim = unquoted;
|
|
185
|
+
if (!arg_trim) { free(s); free(fname_trim); return TF_ERROR; }
|
|
186
|
+
}
|
|
187
|
+
*func = fname_trim;
|
|
188
|
+
*arg = arg_trim;
|
|
189
|
+
free(s);
|
|
190
|
+
return TF_OK;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
*exact = strdup(p);
|
|
194
|
+
free(s);
|
|
195
|
+
return *exact ? TF_OK : TF_ERROR;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
static int starts_with_ci(const char *s, const char *prefix) {
|
|
199
|
+
size_t n = strlen(prefix);
|
|
200
|
+
return strncasecmp(s, prefix, n) == 0;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
static int ends_with_ci(const char *s, const char *suffix) {
|
|
204
|
+
size_t slen = strlen(s);
|
|
205
|
+
size_t xlen = strlen(suffix);
|
|
206
|
+
if (xlen > slen) return 0;
|
|
207
|
+
return strcasecmp(s + slen - xlen, suffix) == 0;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
static int contains_ci(const char *s, const char *needle) {
|
|
211
|
+
size_t nlen = strlen(needle);
|
|
212
|
+
if (nlen == 0) return 1;
|
|
213
|
+
for (const char *p = s; *p; p++) {
|
|
214
|
+
if (strncasecmp(p, needle, nlen) == 0) return 1;
|
|
215
|
+
}
|
|
216
|
+
return 0;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
static int type_matches(tf_type t, const char *name) {
|
|
220
|
+
if (strcasecmp(name, "numeric") == 0 || strcasecmp(name, "number") == 0)
|
|
221
|
+
return t == TF_TYPE_INT64 || t == TF_TYPE_FLOAT64;
|
|
222
|
+
if (strcasecmp(name, "int") == 0 || strcasecmp(name, "int64") == 0 ||
|
|
223
|
+
strcasecmp(name, "integer") == 0)
|
|
224
|
+
return t == TF_TYPE_INT64;
|
|
225
|
+
if (strcasecmp(name, "float") == 0 || strcasecmp(name, "float64") == 0 ||
|
|
226
|
+
strcasecmp(name, "double") == 0)
|
|
227
|
+
return t == TF_TYPE_FLOAT64;
|
|
228
|
+
if (strcasecmp(name, "string") == 0 || strcasecmp(name, "str") == 0)
|
|
229
|
+
return t == TF_TYPE_STRING;
|
|
230
|
+
if (strcasecmp(name, "bool") == 0 || strcasecmp(name, "boolean") == 0)
|
|
231
|
+
return t == TF_TYPE_BOOL;
|
|
232
|
+
if (strcasecmp(name, "date") == 0)
|
|
233
|
+
return t == TF_TYPE_DATE;
|
|
234
|
+
if (strcasecmp(name, "timestamp") == 0)
|
|
235
|
+
return t == TF_TYPE_TIMESTAMP;
|
|
236
|
+
if (strcasecmp(name, "temporal") == 0)
|
|
237
|
+
return t == TF_TYPE_DATE || t == TF_TYPE_TIMESTAMP;
|
|
238
|
+
return 0;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
static int selector_matches_column(const char *func, const char *arg,
|
|
242
|
+
const char *name, tf_type type,
|
|
243
|
+
regex_t *compiled) {
|
|
244
|
+
if (strcasecmp(func, "starts_with") == 0) return starts_with_ci(name, arg);
|
|
245
|
+
if (strcasecmp(func, "ends_with") == 0) return ends_with_ci(name, arg);
|
|
246
|
+
if (strcasecmp(func, "contains") == 0) return contains_ci(name, arg);
|
|
247
|
+
if (strcasecmp(func, "matches") == 0) return regexec(compiled, name, 0, NULL, 0) == 0;
|
|
248
|
+
if (strcasecmp(func, "where") == 0) return type_matches(type, arg);
|
|
249
|
+
return 0;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
static int selector_find_column(char **names, size_t n_cols, const char *name) {
|
|
253
|
+
if (!names || !name) return -1;
|
|
254
|
+
for (size_t i = 0; i < n_cols; i++) {
|
|
255
|
+
if (strcmp(names[i], name) == 0) return (int)i;
|
|
256
|
+
}
|
|
257
|
+
return -1;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
static TF_WARN_UNUSED int append_index(int *order, size_t *n_order, size_t cap,
|
|
261
|
+
int *included, int idx);
|
|
262
|
+
static void remove_index(int *order, size_t n_order, int *included, int idx);
|
|
263
|
+
|
|
264
|
+
static int selector_apply_range(const char *expr, int neg, char **names, size_t n_cols,
|
|
265
|
+
int *included, int *order, size_t *n_order,
|
|
266
|
+
char **error) {
|
|
267
|
+
const char *colon = selector_find_range_colon(expr);
|
|
268
|
+
if (!colon) return TF_ERROR;
|
|
269
|
+
char *left_raw = selector_strdup_range(expr, (size_t)(colon - expr));
|
|
270
|
+
char *right_raw = strdup(colon + 1);
|
|
271
|
+
if (!left_raw || !right_raw) {
|
|
272
|
+
free(left_raw); free(right_raw);
|
|
273
|
+
selector_set_error(error, "selector out of memory", NULL);
|
|
274
|
+
return TF_ERROR;
|
|
275
|
+
}
|
|
276
|
+
char *left = selector_trim_copy(left_raw);
|
|
277
|
+
char *right = selector_trim_copy(right_raw);
|
|
278
|
+
free(left_raw); free(right_raw);
|
|
279
|
+
if (!left || !right || left[0] == '\0' || right[0] == '\0') {
|
|
280
|
+
free(left); free(right);
|
|
281
|
+
selector_set_error(error, "invalid range selector", expr);
|
|
282
|
+
return TF_ERROR;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
int li = selector_find_column(names, n_cols, left);
|
|
286
|
+
int ri = selector_find_column(names, n_cols, right);
|
|
287
|
+
if (li < 0 || ri < 0) {
|
|
288
|
+
const char *missing = li < 0 ? left : right;
|
|
289
|
+
selector_set_error(error, "range endpoint not found", missing);
|
|
290
|
+
free(left); free(right);
|
|
291
|
+
return TF_ERROR;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
int step = li <= ri ? 1 : -1;
|
|
295
|
+
for (int idx = li;; idx += step) {
|
|
296
|
+
if (neg) remove_index(order, *n_order, included, idx);
|
|
297
|
+
else if (append_index(order, n_order, n_cols, included, idx) != TF_OK) {
|
|
298
|
+
selector_set_error(error, "selector index overflow", expr);
|
|
299
|
+
free(left); free(right);
|
|
300
|
+
return TF_ERROR;
|
|
301
|
+
}
|
|
302
|
+
if (idx == ri) break;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
free(left); free(right);
|
|
306
|
+
return TF_OK;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
static void selector_free_name_list(char **items, size_t n_items) {
|
|
310
|
+
if (!items) return;
|
|
311
|
+
for (size_t i = 0; i < n_items; i++) free(items[i]);
|
|
312
|
+
free(items);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
static int selector_append_name_item(char ***items, size_t *n_items, size_t *cap,
|
|
316
|
+
const char *start, size_t len) {
|
|
317
|
+
char *raw = selector_strdup_range(start, len);
|
|
318
|
+
if (!raw) return TF_ERROR;
|
|
319
|
+
char *trimmed = selector_trim_copy(raw);
|
|
320
|
+
free(raw);
|
|
321
|
+
if (!trimmed) return TF_ERROR;
|
|
322
|
+
|
|
323
|
+
size_t tlen = strlen(trimmed);
|
|
324
|
+
if (tlen == 0) {
|
|
325
|
+
free(trimmed);
|
|
326
|
+
return TF_ERROR;
|
|
327
|
+
}
|
|
328
|
+
if (trimmed[0] == '\'' || trimmed[0] == '"') {
|
|
329
|
+
char q = trimmed[0];
|
|
330
|
+
if (tlen < 2 || trimmed[tlen - 1] != q) {
|
|
331
|
+
free(trimmed);
|
|
332
|
+
return TF_ERROR;
|
|
333
|
+
}
|
|
334
|
+
char *unquoted = selector_strdup_range(trimmed + 1, tlen - 2);
|
|
335
|
+
free(trimmed);
|
|
336
|
+
trimmed = unquoted;
|
|
337
|
+
if (!trimmed) return TF_ERROR;
|
|
338
|
+
if (trimmed[0] == '\0') {
|
|
339
|
+
free(trimmed);
|
|
340
|
+
return TF_ERROR;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (*n_items == *cap) {
|
|
345
|
+
size_t needed = 0;
|
|
346
|
+
size_t next = 0;
|
|
347
|
+
if (tf_size_add(*n_items, 1, &needed) != TF_OK ||
|
|
348
|
+
tf_size_grow_pow2(*cap, needed, 4, &next) != TF_OK) {
|
|
349
|
+
free(trimmed);
|
|
350
|
+
return TF_ERROR;
|
|
351
|
+
}
|
|
352
|
+
char **grown = tf_reallocarray_checked(*items, next, sizeof(char *));
|
|
353
|
+
if (!grown) {
|
|
354
|
+
free(trimmed);
|
|
355
|
+
return TF_ERROR;
|
|
356
|
+
}
|
|
357
|
+
*items = grown;
|
|
358
|
+
*cap = next;
|
|
359
|
+
}
|
|
360
|
+
(*items)[(*n_items)++] = trimmed;
|
|
361
|
+
return TF_OK;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
static int selector_parse_name_list(const char *arg, char ***out_items, size_t *out_n) {
|
|
365
|
+
*out_items = NULL;
|
|
366
|
+
*out_n = 0;
|
|
367
|
+
if (!arg) return TF_ERROR;
|
|
368
|
+
|
|
369
|
+
char **items = NULL;
|
|
370
|
+
size_t n_items = 0, cap = 0;
|
|
371
|
+
const char *start = arg;
|
|
372
|
+
char quote = '\0';
|
|
373
|
+
for (const char *p = arg;; p++) {
|
|
374
|
+
char c = *p;
|
|
375
|
+
if (c == '\0') {
|
|
376
|
+
if (quote != '\0') {
|
|
377
|
+
selector_free_name_list(items, n_items);
|
|
378
|
+
return TF_ERROR;
|
|
379
|
+
}
|
|
380
|
+
if (selector_append_name_item(&items, &n_items, &cap, start, (size_t)(p - start)) != TF_OK) {
|
|
381
|
+
selector_free_name_list(items, n_items);
|
|
382
|
+
return TF_ERROR;
|
|
383
|
+
}
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
if (quote != '\0') {
|
|
387
|
+
if (c == quote) quote = '\0';
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
if (c == '\'' || c == '"') {
|
|
391
|
+
quote = c;
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
if (c == ',') {
|
|
395
|
+
if (selector_append_name_item(&items, &n_items, &cap, start, (size_t)(p - start)) != TF_OK) {
|
|
396
|
+
selector_free_name_list(items, n_items);
|
|
397
|
+
return TF_ERROR;
|
|
398
|
+
}
|
|
399
|
+
start = p + 1;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (n_items == 0) {
|
|
404
|
+
selector_free_name_list(items, n_items);
|
|
405
|
+
return TF_ERROR;
|
|
406
|
+
}
|
|
407
|
+
*out_items = items;
|
|
408
|
+
*out_n = n_items;
|
|
409
|
+
return TF_OK;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
static int append_index(int *order, size_t *n_order, size_t cap,
|
|
413
|
+
int *included, int idx) {
|
|
414
|
+
if (idx < 0) return TF_ERROR;
|
|
415
|
+
if ((size_t)idx >= cap) return TF_ERROR;
|
|
416
|
+
if (included[idx]) return TF_OK;
|
|
417
|
+
if (*n_order >= cap) return TF_ERROR;
|
|
418
|
+
included[idx] = 1;
|
|
419
|
+
order[(*n_order)++] = idx;
|
|
420
|
+
return TF_OK;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
static void remove_index(int *order, size_t n_order, int *included, int idx) {
|
|
424
|
+
if (idx < 0) return;
|
|
425
|
+
included[idx] = 0;
|
|
426
|
+
for (size_t i = 0; i < n_order; i++) {
|
|
427
|
+
if (order[i] == idx) order[i] = -1;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
typedef struct {
|
|
433
|
+
int *order;
|
|
434
|
+
size_t n_order;
|
|
435
|
+
int *included;
|
|
436
|
+
size_t n_cols;
|
|
437
|
+
} selector_result;
|
|
438
|
+
|
|
439
|
+
static int selector_result_init(selector_result *r, size_t n_cols) {
|
|
440
|
+
if (n_cols > (size_t)INT_MAX) return TF_ERROR;
|
|
441
|
+
r->n_cols = n_cols;
|
|
442
|
+
r->n_order = 0;
|
|
443
|
+
size_t alloc_cols = n_cols ? n_cols : 1;
|
|
444
|
+
r->order = tf_mallocarray_checked(alloc_cols, sizeof(int));
|
|
445
|
+
r->included = tf_callocarray_checked(alloc_cols, sizeof(int));
|
|
446
|
+
if (!r->order || !r->included) {
|
|
447
|
+
free(r->order);
|
|
448
|
+
free(r->included);
|
|
449
|
+
r->order = NULL;
|
|
450
|
+
r->included = NULL;
|
|
451
|
+
r->n_cols = 0;
|
|
452
|
+
return TF_ERROR;
|
|
453
|
+
}
|
|
454
|
+
return TF_OK;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
static void selector_result_free(selector_result *r) {
|
|
458
|
+
if (!r) return;
|
|
459
|
+
free(r->order);
|
|
460
|
+
free(r->included);
|
|
461
|
+
r->order = NULL;
|
|
462
|
+
r->included = NULL;
|
|
463
|
+
r->n_order = 0;
|
|
464
|
+
r->n_cols = 0;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
static int selector_result_union(const selector_result *left,
|
|
468
|
+
const selector_result *right,
|
|
469
|
+
selector_result *out) {
|
|
470
|
+
if (selector_result_init(out, left->n_cols) != TF_OK) return TF_ERROR;
|
|
471
|
+
for (size_t i = 0; i < left->n_order; i++) {
|
|
472
|
+
int idx = left->order[i];
|
|
473
|
+
if (idx >= 0 && left->included[idx] &&
|
|
474
|
+
append_index(out->order, &out->n_order, out->n_cols, out->included, idx) != TF_OK) {
|
|
475
|
+
selector_result_free(out);
|
|
476
|
+
return TF_ERROR;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
for (size_t i = 0; i < right->n_order; i++) {
|
|
480
|
+
int idx = right->order[i];
|
|
481
|
+
if (idx >= 0 && right->included[idx] &&
|
|
482
|
+
append_index(out->order, &out->n_order, out->n_cols, out->included, idx) != TF_OK) {
|
|
483
|
+
selector_result_free(out);
|
|
484
|
+
return TF_ERROR;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return TF_OK;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
static int selector_result_intersection(const selector_result *left,
|
|
491
|
+
const selector_result *right,
|
|
492
|
+
selector_result *out) {
|
|
493
|
+
if (selector_result_init(out, left->n_cols) != TF_OK) return TF_ERROR;
|
|
494
|
+
for (size_t i = 0; i < left->n_order; i++) {
|
|
495
|
+
int idx = left->order[i];
|
|
496
|
+
if (idx >= 0 && left->included[idx] && right->included[idx]) {
|
|
497
|
+
if (append_index(out->order, &out->n_order, out->n_cols, out->included, idx) != TF_OK) {
|
|
498
|
+
selector_result_free(out);
|
|
499
|
+
return TF_ERROR;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return TF_OK;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
static int selector_result_complement(const selector_result *in, selector_result *out) {
|
|
507
|
+
if (selector_result_init(out, in->n_cols) != TF_OK) return TF_ERROR;
|
|
508
|
+
for (size_t i = 0; i < in->n_cols; i++) {
|
|
509
|
+
if (!in->included[i] &&
|
|
510
|
+
append_index(out->order, &out->n_order, out->n_cols, out->included, (int)i) != TF_OK) {
|
|
511
|
+
selector_result_free(out);
|
|
512
|
+
return TF_ERROR;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return TF_OK;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
static int selector_resolve_atomic(const char *raw,
|
|
519
|
+
char **names, const tf_type *types, size_t n_cols,
|
|
520
|
+
selector_result *out,
|
|
521
|
+
char **error) {
|
|
522
|
+
selector_result base;
|
|
523
|
+
if (selector_result_init(&base, n_cols) != TF_OK) {
|
|
524
|
+
selector_set_error(error, "selector out of memory", NULL);
|
|
525
|
+
return TF_ERROR;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
int neg = 0;
|
|
529
|
+
char *func = NULL, *arg = NULL, *exact = NULL;
|
|
530
|
+
if (parse_selector(raw, &neg, &func, &arg, &exact) != TF_OK) {
|
|
531
|
+
selector_result_free(&base);
|
|
532
|
+
selector_set_error(error, "invalid selector", raw);
|
|
533
|
+
return TF_ERROR;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
regex_t compiled;
|
|
537
|
+
int have_regex = 0;
|
|
538
|
+
if (func && strcasecmp(func, "matches") == 0) {
|
|
539
|
+
int rc = regcomp(&compiled, arg, REG_EXTENDED | REG_NOSUB | REG_ICASE);
|
|
540
|
+
if (rc != 0) {
|
|
541
|
+
char msg[128];
|
|
542
|
+
regerror(rc, &compiled, msg, sizeof(msg));
|
|
543
|
+
free(func); free(arg); free(exact);
|
|
544
|
+
selector_result_free(&base);
|
|
545
|
+
selector_set_error(error, "invalid matches() regex", msg);
|
|
546
|
+
return TF_ERROR;
|
|
547
|
+
}
|
|
548
|
+
have_regex = 1;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if (exact) {
|
|
552
|
+
int exact_idx = selector_find_column(names, n_cols, exact);
|
|
553
|
+
if (exact_idx >= 0) {
|
|
554
|
+
if (append_index(base.order, &base.n_order, base.n_cols, base.included, exact_idx) != TF_OK) {
|
|
555
|
+
free(func); free(arg); free(exact);
|
|
556
|
+
if (have_regex) regfree(&compiled);
|
|
557
|
+
selector_result_free(&base);
|
|
558
|
+
selector_set_error(error, "selector index overflow", raw);
|
|
559
|
+
return TF_ERROR;
|
|
560
|
+
}
|
|
561
|
+
} else if (selector_has_range_syntax(exact)) {
|
|
562
|
+
if (selector_apply_range(exact, 0, names, n_cols, base.included,
|
|
563
|
+
base.order, &base.n_order, error) != TF_OK) {
|
|
564
|
+
free(func); free(arg); free(exact);
|
|
565
|
+
if (have_regex) regfree(&compiled);
|
|
566
|
+
selector_result_free(&base);
|
|
567
|
+
return TF_ERROR;
|
|
568
|
+
}
|
|
569
|
+
} else {
|
|
570
|
+
char *missing = exact;
|
|
571
|
+
free(func); free(arg);
|
|
572
|
+
if (have_regex) regfree(&compiled);
|
|
573
|
+
selector_result_free(&base);
|
|
574
|
+
selector_set_error(error, "column not found", missing);
|
|
575
|
+
free(missing);
|
|
576
|
+
return TF_ERROR;
|
|
577
|
+
}
|
|
578
|
+
} else if (func) {
|
|
579
|
+
if (selector_is_name_list_helper(func)) {
|
|
580
|
+
char **items = NULL;
|
|
581
|
+
size_t n_items = 0;
|
|
582
|
+
if (selector_parse_name_list(arg, &items, &n_items) != TF_OK) {
|
|
583
|
+
free(func); free(arg); free(exact);
|
|
584
|
+
if (have_regex) regfree(&compiled);
|
|
585
|
+
selector_result_free(&base);
|
|
586
|
+
selector_set_error(error, "invalid selector name list", raw);
|
|
587
|
+
return TF_ERROR;
|
|
588
|
+
}
|
|
589
|
+
int strict = strcasecmp(func, "all_of") == 0;
|
|
590
|
+
for (size_t i = 0; i < n_items; i++) {
|
|
591
|
+
int idx = selector_find_column(names, n_cols, items[i]);
|
|
592
|
+
if (idx < 0) {
|
|
593
|
+
if (strict) {
|
|
594
|
+
char *missing = strdup(items[i]);
|
|
595
|
+
selector_free_name_list(items, n_items);
|
|
596
|
+
free(func); free(arg); free(exact);
|
|
597
|
+
if (have_regex) regfree(&compiled);
|
|
598
|
+
selector_result_free(&base);
|
|
599
|
+
selector_set_error(error, "all_of column not found", missing ? missing : "");
|
|
600
|
+
free(missing);
|
|
601
|
+
return TF_ERROR;
|
|
602
|
+
}
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
if (append_index(base.order, &base.n_order, base.n_cols, base.included, idx) != TF_OK) {
|
|
606
|
+
selector_free_name_list(items, n_items);
|
|
607
|
+
free(func); free(arg); free(exact);
|
|
608
|
+
if (have_regex) regfree(&compiled);
|
|
609
|
+
selector_result_free(&base);
|
|
610
|
+
selector_set_error(error, "selector index overflow", raw);
|
|
611
|
+
return TF_ERROR;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
selector_free_name_list(items, n_items);
|
|
615
|
+
} else {
|
|
616
|
+
if (!(strcasecmp(func, "starts_with") == 0 || strcasecmp(func, "ends_with") == 0 ||
|
|
617
|
+
strcasecmp(func, "contains") == 0 || strcasecmp(func, "matches") == 0 ||
|
|
618
|
+
strcasecmp(func, "where") == 0)) {
|
|
619
|
+
free(func); free(arg); free(exact);
|
|
620
|
+
if (have_regex) regfree(&compiled);
|
|
621
|
+
selector_result_free(&base);
|
|
622
|
+
selector_set_error(error, "unknown selector helper", raw);
|
|
623
|
+
return TF_ERROR;
|
|
624
|
+
}
|
|
625
|
+
for (size_t i = 0; i < n_cols; i++) {
|
|
626
|
+
if (selector_matches_column(func, arg, names[i], types[i], &compiled)) {
|
|
627
|
+
if (append_index(base.order, &base.n_order, base.n_cols, base.included, (int)i) != TF_OK) {
|
|
628
|
+
free(func); free(arg); free(exact);
|
|
629
|
+
if (have_regex) regfree(&compiled);
|
|
630
|
+
selector_result_free(&base);
|
|
631
|
+
selector_set_error(error, "selector index overflow", raw);
|
|
632
|
+
return TF_ERROR;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
if (have_regex) regfree(&compiled);
|
|
640
|
+
free(func); free(arg); free(exact);
|
|
641
|
+
|
|
642
|
+
if (neg) {
|
|
643
|
+
selector_result comp;
|
|
644
|
+
if (selector_result_complement(&base, &comp) != TF_OK) {
|
|
645
|
+
selector_result_free(&base);
|
|
646
|
+
selector_set_error(error, "selector out of memory", NULL);
|
|
647
|
+
return TF_ERROR;
|
|
648
|
+
}
|
|
649
|
+
selector_result_free(&base);
|
|
650
|
+
*out = comp;
|
|
651
|
+
} else {
|
|
652
|
+
*out = base;
|
|
653
|
+
}
|
|
654
|
+
return TF_OK;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
typedef struct {
|
|
658
|
+
const char *s;
|
|
659
|
+
size_t len;
|
|
660
|
+
size_t pos;
|
|
661
|
+
char **names;
|
|
662
|
+
const tf_type *types;
|
|
663
|
+
size_t n_cols;
|
|
664
|
+
char **error;
|
|
665
|
+
} selector_expr_parser;
|
|
666
|
+
|
|
667
|
+
static void selector_expr_skip_ws(selector_expr_parser *p) {
|
|
668
|
+
while (p->pos < p->len && isspace((unsigned char)p->s[p->pos])) p->pos++;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
static int selector_expr_parse_or(selector_expr_parser *p, selector_result *out, unsigned depth);
|
|
672
|
+
|
|
673
|
+
static int selector_expr_depth_exceeded(selector_expr_parser *p, unsigned depth) {
|
|
674
|
+
if (depth <= TF_SELECTOR_MAX_DEPTH) return 0;
|
|
675
|
+
selector_set_error(p->error, "selector nesting too deep", NULL);
|
|
676
|
+
return 1;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
static int selector_expr_parse_primary(selector_expr_parser *p, selector_result *out, unsigned depth) {
|
|
680
|
+
if (selector_expr_depth_exceeded(p, depth)) return TF_ERROR;
|
|
681
|
+
selector_expr_skip_ws(p);
|
|
682
|
+
if (p->pos >= p->len) {
|
|
683
|
+
selector_set_error(p->error, "expected selector expression", NULL);
|
|
684
|
+
return TF_ERROR;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
if (p->s[p->pos] == '(') {
|
|
688
|
+
p->pos++;
|
|
689
|
+
selector_result inner;
|
|
690
|
+
if (selector_expr_parse_or(p, &inner, depth + 1) != TF_OK) return TF_ERROR;
|
|
691
|
+
selector_expr_skip_ws(p);
|
|
692
|
+
if (p->pos >= p->len || p->s[p->pos] != ')') {
|
|
693
|
+
selector_result_free(&inner);
|
|
694
|
+
selector_set_error(p->error, "unclosed selector expression", NULL);
|
|
695
|
+
return TF_ERROR;
|
|
696
|
+
}
|
|
697
|
+
p->pos++;
|
|
698
|
+
*out = inner;
|
|
699
|
+
return TF_OK;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
size_t start = p->pos;
|
|
703
|
+
int paren = 0, bracket = 0, brace = 0;
|
|
704
|
+
char quote = '\0';
|
|
705
|
+
while (p->pos < p->len) {
|
|
706
|
+
char c = p->s[p->pos];
|
|
707
|
+
if (quote != '\0') {
|
|
708
|
+
if (c == quote) quote = '\0';
|
|
709
|
+
p->pos++;
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
if (c == '\'' || c == '"') {
|
|
713
|
+
quote = c;
|
|
714
|
+
p->pos++;
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
if (c == '(') { paren++; p->pos++; continue; }
|
|
718
|
+
if (c == ')' && paren > 0) { paren--; p->pos++; continue; }
|
|
719
|
+
if (c == '[') { bracket++; p->pos++; continue; }
|
|
720
|
+
if (c == ']' && bracket > 0) { bracket--; p->pos++; continue; }
|
|
721
|
+
if (c == '{') { brace++; p->pos++; continue; }
|
|
722
|
+
if (c == '}' && brace > 0) { brace--; p->pos++; continue; }
|
|
723
|
+
if ((c == '&' || c == '|' || c == ')') && paren == 0 && bracket == 0 && brace == 0) break;
|
|
724
|
+
p->pos++;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
char *atom_raw = selector_strdup_range(p->s + start, p->pos - start);
|
|
728
|
+
char *atom = atom_raw ? selector_trim_copy(atom_raw) : NULL;
|
|
729
|
+
free(atom_raw);
|
|
730
|
+
if (!atom || atom[0] == '\0') {
|
|
731
|
+
free(atom);
|
|
732
|
+
selector_set_error(p->error, "expected selector expression", NULL);
|
|
733
|
+
return TF_ERROR;
|
|
734
|
+
}
|
|
735
|
+
int rc = selector_resolve_atomic(atom, p->names, p->types, p->n_cols, out, p->error);
|
|
736
|
+
free(atom);
|
|
737
|
+
return rc;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
static int selector_expr_parse_unary(selector_expr_parser *p, selector_result *out, unsigned depth) {
|
|
741
|
+
if (selector_expr_depth_exceeded(p, depth)) return TF_ERROR;
|
|
742
|
+
selector_expr_skip_ws(p);
|
|
743
|
+
if (p->pos < p->len && (p->s[p->pos] == '!' || p->s[p->pos] == '-')) {
|
|
744
|
+
p->pos++;
|
|
745
|
+
selector_result child;
|
|
746
|
+
if (selector_expr_parse_unary(p, &child, depth + 1) != TF_OK) return TF_ERROR;
|
|
747
|
+
selector_result comp;
|
|
748
|
+
if (selector_result_complement(&child, &comp) != TF_OK) {
|
|
749
|
+
selector_result_free(&child);
|
|
750
|
+
selector_set_error(p->error, "selector out of memory", NULL);
|
|
751
|
+
return TF_ERROR;
|
|
752
|
+
}
|
|
753
|
+
selector_result_free(&child);
|
|
754
|
+
*out = comp;
|
|
755
|
+
return TF_OK;
|
|
756
|
+
}
|
|
757
|
+
return selector_expr_parse_primary(p, out, depth);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
static int selector_expr_parse_and(selector_expr_parser *p, selector_result *out, unsigned depth) {
|
|
761
|
+
selector_result left;
|
|
762
|
+
if (selector_expr_parse_unary(p, &left, depth) != TF_OK) return TF_ERROR;
|
|
763
|
+
for (;;) {
|
|
764
|
+
selector_expr_skip_ws(p);
|
|
765
|
+
if (p->pos >= p->len || p->s[p->pos] != '&') break;
|
|
766
|
+
p->pos++;
|
|
767
|
+
selector_result right;
|
|
768
|
+
if (selector_expr_parse_unary(p, &right, depth) != TF_OK) {
|
|
769
|
+
selector_result_free(&left);
|
|
770
|
+
return TF_ERROR;
|
|
771
|
+
}
|
|
772
|
+
selector_result joined;
|
|
773
|
+
if (selector_result_intersection(&left, &right, &joined) != TF_OK) {
|
|
774
|
+
selector_result_free(&left);
|
|
775
|
+
selector_result_free(&right);
|
|
776
|
+
selector_set_error(p->error, "selector out of memory", NULL);
|
|
777
|
+
return TF_ERROR;
|
|
778
|
+
}
|
|
779
|
+
selector_result_free(&left);
|
|
780
|
+
selector_result_free(&right);
|
|
781
|
+
left = joined;
|
|
782
|
+
}
|
|
783
|
+
*out = left;
|
|
784
|
+
return TF_OK;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
static int selector_expr_parse_or(selector_expr_parser *p, selector_result *out, unsigned depth) {
|
|
788
|
+
selector_result left;
|
|
789
|
+
if (selector_expr_parse_and(p, &left, depth) != TF_OK) return TF_ERROR;
|
|
790
|
+
for (;;) {
|
|
791
|
+
selector_expr_skip_ws(p);
|
|
792
|
+
if (p->pos >= p->len || p->s[p->pos] != '|') break;
|
|
793
|
+
p->pos++;
|
|
794
|
+
selector_result right;
|
|
795
|
+
if (selector_expr_parse_and(p, &right, depth) != TF_OK) {
|
|
796
|
+
selector_result_free(&left);
|
|
797
|
+
return TF_ERROR;
|
|
798
|
+
}
|
|
799
|
+
selector_result joined;
|
|
800
|
+
if (selector_result_union(&left, &right, &joined) != TF_OK) {
|
|
801
|
+
selector_result_free(&left);
|
|
802
|
+
selector_result_free(&right);
|
|
803
|
+
selector_set_error(p->error, "selector out of memory", NULL);
|
|
804
|
+
return TF_ERROR;
|
|
805
|
+
}
|
|
806
|
+
selector_result_free(&left);
|
|
807
|
+
selector_result_free(&right);
|
|
808
|
+
left = joined;
|
|
809
|
+
}
|
|
810
|
+
*out = left;
|
|
811
|
+
return TF_OK;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
static int selector_eval_boolean_algebra(const char *raw,
|
|
815
|
+
char **names, const tf_type *types, size_t n_cols,
|
|
816
|
+
selector_result *out,
|
|
817
|
+
char **error) {
|
|
818
|
+
selector_expr_parser p = {
|
|
819
|
+
.s = raw ? raw : "",
|
|
820
|
+
.len = raw ? strlen(raw) : 0,
|
|
821
|
+
.pos = 0,
|
|
822
|
+
.names = names,
|
|
823
|
+
.types = types,
|
|
824
|
+
.n_cols = n_cols,
|
|
825
|
+
.error = error,
|
|
826
|
+
};
|
|
827
|
+
if (selector_expr_parse_or(&p, out, 0) != TF_OK) return TF_ERROR;
|
|
828
|
+
selector_expr_skip_ws(&p);
|
|
829
|
+
if (p.pos != p.len) {
|
|
830
|
+
selector_result_free(out);
|
|
831
|
+
selector_set_error(error, "invalid selector expression", raw);
|
|
832
|
+
return TF_ERROR;
|
|
833
|
+
}
|
|
834
|
+
return TF_OK;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
int tf_column_selectors_resolve(char **selectors, size_t n_selectors,
|
|
838
|
+
char **names, const tf_type *types, size_t n_cols,
|
|
839
|
+
int **out_indices, size_t *out_n,
|
|
840
|
+
char **error) {
|
|
841
|
+
*out_indices = NULL;
|
|
842
|
+
*out_n = 0;
|
|
843
|
+
if (!selectors || n_selectors == 0) {
|
|
844
|
+
selector_set_error(error, "selector list is empty", NULL);
|
|
845
|
+
return TF_ERROR;
|
|
846
|
+
}
|
|
847
|
+
if (n_cols > (size_t)INT_MAX) {
|
|
848
|
+
selector_set_error(error, "selector column count exceeds int index range", NULL);
|
|
849
|
+
return TF_ERROR;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
size_t alloc_cols = n_cols ? n_cols : 1;
|
|
853
|
+
int *included = tf_callocarray_checked(alloc_cols, sizeof(int));
|
|
854
|
+
int *order = tf_mallocarray_checked(alloc_cols, sizeof(int));
|
|
855
|
+
if (!included || !order) {
|
|
856
|
+
free(included); free(order);
|
|
857
|
+
selector_set_error(error, "selector out of memory", NULL);
|
|
858
|
+
return TF_ERROR;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
int has_positive = 0;
|
|
862
|
+
for (size_t i = 0; i < n_selectors; i++) {
|
|
863
|
+
if (selector_has_boolean_algebra(selectors[i])) {
|
|
864
|
+
has_positive = 1;
|
|
865
|
+
continue;
|
|
866
|
+
}
|
|
867
|
+
int neg = 0;
|
|
868
|
+
char *func = NULL, *arg = NULL, *exact = NULL;
|
|
869
|
+
if (parse_selector(selectors[i], &neg, &func, &arg, &exact) != TF_OK) {
|
|
870
|
+
free(included); free(order);
|
|
871
|
+
selector_set_error(error, "invalid selector", selectors[i]);
|
|
872
|
+
return TF_ERROR;
|
|
873
|
+
}
|
|
874
|
+
if (!neg) has_positive = 1;
|
|
875
|
+
free(func); free(arg); free(exact);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
size_t n_order = 0;
|
|
879
|
+
if (!has_positive) {
|
|
880
|
+
for (size_t i = 0; i < n_cols; i++) {
|
|
881
|
+
if (append_index(order, &n_order, n_cols, included, (int)i) != TF_OK) {
|
|
882
|
+
free(included); free(order);
|
|
883
|
+
selector_set_error(error, "selector index overflow", NULL);
|
|
884
|
+
return TF_ERROR;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
for (size_t si = 0; si < n_selectors; si++) {
|
|
890
|
+
if (selector_has_boolean_algebra(selectors[si])) {
|
|
891
|
+
selector_result expr;
|
|
892
|
+
if (selector_eval_boolean_algebra(selectors[si], names, types, n_cols, &expr, error) != TF_OK) {
|
|
893
|
+
free(included); free(order);
|
|
894
|
+
return TF_ERROR;
|
|
895
|
+
}
|
|
896
|
+
for (size_t i = 0; i < expr.n_order; i++) {
|
|
897
|
+
int idx = expr.order[i];
|
|
898
|
+
if (idx >= 0 && expr.included[idx] &&
|
|
899
|
+
append_index(order, &n_order, n_cols, included, idx) != TF_OK) {
|
|
900
|
+
selector_result_free(&expr);
|
|
901
|
+
free(included); free(order);
|
|
902
|
+
selector_set_error(error, "selector index overflow", selectors[si]);
|
|
903
|
+
return TF_ERROR;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
selector_result_free(&expr);
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
int neg = 0;
|
|
911
|
+
char *func = NULL, *arg = NULL, *exact = NULL;
|
|
912
|
+
if (parse_selector(selectors[si], &neg, &func, &arg, &exact) != TF_OK) {
|
|
913
|
+
free(included); free(order);
|
|
914
|
+
selector_set_error(error, "invalid selector", selectors[si]);
|
|
915
|
+
return TF_ERROR;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
regex_t compiled;
|
|
919
|
+
int have_regex = 0;
|
|
920
|
+
if (func && strcasecmp(func, "matches") == 0) {
|
|
921
|
+
int rc = regcomp(&compiled, arg, REG_EXTENDED | REG_NOSUB | REG_ICASE);
|
|
922
|
+
if (rc != 0) {
|
|
923
|
+
char msg[128];
|
|
924
|
+
regerror(rc, &compiled, msg, sizeof(msg));
|
|
925
|
+
free(func); free(arg); free(exact); free(included); free(order);
|
|
926
|
+
selector_set_error(error, "invalid matches() regex", msg);
|
|
927
|
+
return TF_ERROR;
|
|
928
|
+
}
|
|
929
|
+
have_regex = 1;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
if (exact) {
|
|
933
|
+
int exact_idx = selector_find_column(names, n_cols, exact);
|
|
934
|
+
if (exact_idx >= 0) {
|
|
935
|
+
if (neg) remove_index(order, n_order, included, exact_idx);
|
|
936
|
+
else if (append_index(order, &n_order, n_cols, included, exact_idx) != TF_OK) {
|
|
937
|
+
free(func); free(arg); free(exact);
|
|
938
|
+
if (have_regex) regfree(&compiled);
|
|
939
|
+
free(included); free(order);
|
|
940
|
+
selector_set_error(error, "selector index overflow", selectors[si]);
|
|
941
|
+
return TF_ERROR;
|
|
942
|
+
}
|
|
943
|
+
} else if (selector_has_range_syntax(exact)) {
|
|
944
|
+
if (selector_apply_range(exact, neg, names, n_cols, included, order, &n_order, error) != TF_OK) {
|
|
945
|
+
free(func); free(arg); free(exact);
|
|
946
|
+
if (have_regex) regfree(&compiled);
|
|
947
|
+
free(included); free(order);
|
|
948
|
+
return TF_ERROR;
|
|
949
|
+
}
|
|
950
|
+
} else {
|
|
951
|
+
char *missing = exact;
|
|
952
|
+
free(func); free(arg);
|
|
953
|
+
if (have_regex) regfree(&compiled);
|
|
954
|
+
free(included); free(order);
|
|
955
|
+
selector_set_error(error, "column not found", missing);
|
|
956
|
+
free(missing);
|
|
957
|
+
return TF_ERROR;
|
|
958
|
+
}
|
|
959
|
+
} else if (func) {
|
|
960
|
+
if (selector_is_name_list_helper(func)) {
|
|
961
|
+
char **items = NULL;
|
|
962
|
+
size_t n_items = 0;
|
|
963
|
+
if (selector_parse_name_list(arg, &items, &n_items) != TF_OK) {
|
|
964
|
+
free(func); free(arg); free(exact);
|
|
965
|
+
if (have_regex) regfree(&compiled);
|
|
966
|
+
free(included); free(order);
|
|
967
|
+
selector_set_error(error, "invalid selector name list", selectors[si]);
|
|
968
|
+
return TF_ERROR;
|
|
969
|
+
}
|
|
970
|
+
int strict = strcasecmp(func, "all_of") == 0;
|
|
971
|
+
for (size_t i = 0; i < n_items; i++) {
|
|
972
|
+
int idx = selector_find_column(names, n_cols, items[i]);
|
|
973
|
+
if (idx < 0) {
|
|
974
|
+
if (strict) {
|
|
975
|
+
char *missing = strdup(items[i]);
|
|
976
|
+
selector_free_name_list(items, n_items);
|
|
977
|
+
free(func); free(arg); free(exact);
|
|
978
|
+
if (have_regex) regfree(&compiled);
|
|
979
|
+
free(included); free(order);
|
|
980
|
+
selector_set_error(error, "all_of column not found", missing ? missing : "");
|
|
981
|
+
free(missing);
|
|
982
|
+
return TF_ERROR;
|
|
983
|
+
}
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
if (neg) remove_index(order, n_order, included, idx);
|
|
987
|
+
else if (append_index(order, &n_order, n_cols, included, idx) != TF_OK) {
|
|
988
|
+
selector_free_name_list(items, n_items);
|
|
989
|
+
free(func); free(arg); free(exact);
|
|
990
|
+
if (have_regex) regfree(&compiled);
|
|
991
|
+
free(included); free(order);
|
|
992
|
+
selector_set_error(error, "selector index overflow", selectors[si]);
|
|
993
|
+
return TF_ERROR;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
selector_free_name_list(items, n_items);
|
|
997
|
+
} else {
|
|
998
|
+
if (!(strcasecmp(func, "starts_with") == 0 || strcasecmp(func, "ends_with") == 0 ||
|
|
999
|
+
strcasecmp(func, "contains") == 0 || strcasecmp(func, "matches") == 0 ||
|
|
1000
|
+
strcasecmp(func, "where") == 0)) {
|
|
1001
|
+
free(func); free(arg); free(exact);
|
|
1002
|
+
if (have_regex) regfree(&compiled);
|
|
1003
|
+
free(included); free(order);
|
|
1004
|
+
selector_set_error(error, "unknown selector helper", selectors[si]);
|
|
1005
|
+
return TF_ERROR;
|
|
1006
|
+
}
|
|
1007
|
+
for (size_t i = 0; i < n_cols; i++) {
|
|
1008
|
+
if (selector_matches_column(func, arg, names[i], types[i], &compiled)) {
|
|
1009
|
+
if (neg) remove_index(order, n_order, included, (int)i);
|
|
1010
|
+
else if (append_index(order, &n_order, n_cols, included, (int)i) != TF_OK) {
|
|
1011
|
+
free(func); free(arg); free(exact);
|
|
1012
|
+
if (have_regex) regfree(&compiled);
|
|
1013
|
+
free(included); free(order);
|
|
1014
|
+
selector_set_error(error, "selector index overflow", selectors[si]);
|
|
1015
|
+
return TF_ERROR;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
if (have_regex) regfree(&compiled);
|
|
1023
|
+
free(func); free(arg); free(exact);
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
size_t final_n = 0;
|
|
1027
|
+
for (size_t i = 0; i < n_order; i++) {
|
|
1028
|
+
int idx = order[i];
|
|
1029
|
+
if (idx >= 0 && included[idx]) final_n++;
|
|
1030
|
+
}
|
|
1031
|
+
if (final_n == 0) {
|
|
1032
|
+
free(included); free(order);
|
|
1033
|
+
selector_set_error(error, "selectors resolved no columns", NULL);
|
|
1034
|
+
return TF_ERROR;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
int *out = tf_mallocarray_checked(final_n, sizeof(int));
|
|
1038
|
+
if (!out) {
|
|
1039
|
+
free(included); free(order);
|
|
1040
|
+
selector_set_error(error, "selector out of memory", NULL);
|
|
1041
|
+
return TF_ERROR;
|
|
1042
|
+
}
|
|
1043
|
+
size_t j = 0;
|
|
1044
|
+
for (size_t i = 0; i < n_order; i++) {
|
|
1045
|
+
int idx = order[i];
|
|
1046
|
+
if (idx >= 0 && included[idx]) out[j++] = idx;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
free(included);
|
|
1050
|
+
free(order);
|
|
1051
|
+
*out_indices = out;
|
|
1052
|
+
*out_n = final_n;
|
|
1053
|
+
return TF_OK;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
int tf_column_selectors_resolve_json(const cJSON *selectors,
|
|
1057
|
+
char **names, const tf_type *types, size_t n_cols,
|
|
1058
|
+
int **out_indices, size_t *out_n,
|
|
1059
|
+
char **error) {
|
|
1060
|
+
if (!selectors || !cJSON_IsArray(selectors)) {
|
|
1061
|
+
selector_set_error(error, "selector list must be an array", NULL);
|
|
1062
|
+
return TF_ERROR;
|
|
1063
|
+
}
|
|
1064
|
+
int n = cJSON_GetArraySize(selectors);
|
|
1065
|
+
if (n <= 0) {
|
|
1066
|
+
selector_set_error(error, "selector list is empty", NULL);
|
|
1067
|
+
return TF_ERROR;
|
|
1068
|
+
}
|
|
1069
|
+
char **items = tf_callocarray_checked((size_t)n, sizeof(char *));
|
|
1070
|
+
if (!items) {
|
|
1071
|
+
selector_set_error(error, "selector out of memory", NULL);
|
|
1072
|
+
return TF_ERROR;
|
|
1073
|
+
}
|
|
1074
|
+
for (int i = 0; i < n; i++) {
|
|
1075
|
+
cJSON *item = cJSON_GetArrayItem(selectors, i);
|
|
1076
|
+
if (!cJSON_IsString(item)) {
|
|
1077
|
+
free(items);
|
|
1078
|
+
selector_set_error(error, "selector item must be a string", NULL);
|
|
1079
|
+
return TF_ERROR;
|
|
1080
|
+
}
|
|
1081
|
+
items[i] = item->valuestring;
|
|
1082
|
+
}
|
|
1083
|
+
int rc = tf_column_selectors_resolve(items, (size_t)n, names, types, n_cols,
|
|
1084
|
+
out_indices, out_n, error);
|
|
1085
|
+
free(items);
|
|
1086
|
+
return rc;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
int tf_column_selectors_have_syntax_json(const cJSON *selectors) {
|
|
1090
|
+
if (!selectors || !cJSON_IsArray(selectors)) return 0;
|
|
1091
|
+
int n = cJSON_GetArraySize(selectors);
|
|
1092
|
+
for (int i = 0; i < n; i++) {
|
|
1093
|
+
cJSON *item = cJSON_GetArrayItem(selectors, i);
|
|
1094
|
+
if (cJSON_IsString(item) && tf_column_selector_has_syntax(item->valuestring)) return 1;
|
|
1095
|
+
}
|
|
1096
|
+
return 0;
|
|
1097
|
+
}
|