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_step.c
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
#include <string.h>
|
|
12
12
|
#include <stdio.h>
|
|
13
13
|
#include <math.h>
|
|
14
|
+
#include <stdint.h>
|
|
14
15
|
|
|
15
16
|
typedef enum {
|
|
16
17
|
STEP_RUNNING_SUM,
|
|
@@ -23,6 +24,17 @@ typedef enum {
|
|
|
23
24
|
STEP_RATIO,
|
|
24
25
|
} step_func;
|
|
25
26
|
|
|
27
|
+
typedef enum {
|
|
28
|
+
STEP_MISSING_ERROR,
|
|
29
|
+
STEP_MISSING_NULL,
|
|
30
|
+
STEP_MISSING_IGNORE,
|
|
31
|
+
} step_missing_policy;
|
|
32
|
+
|
|
33
|
+
typedef enum {
|
|
34
|
+
STEP_TYPE_FAIL,
|
|
35
|
+
STEP_TYPE_NULL,
|
|
36
|
+
} step_type_policy;
|
|
37
|
+
|
|
26
38
|
typedef struct {
|
|
27
39
|
char *column;
|
|
28
40
|
char *result;
|
|
@@ -33,24 +45,94 @@ typedef struct {
|
|
|
33
45
|
size_t running_count;
|
|
34
46
|
double prev_val;
|
|
35
47
|
int has_prev;
|
|
48
|
+
step_missing_policy missing;
|
|
49
|
+
step_type_policy on_type_error;
|
|
36
50
|
} step_state;
|
|
37
51
|
|
|
38
|
-
static
|
|
39
|
-
if (
|
|
40
|
-
if (strcmp(s, "running-
|
|
41
|
-
if (strcmp(s, "running-
|
|
42
|
-
if (strcmp(s, "running-
|
|
43
|
-
if (strcmp(s, "running-
|
|
44
|
-
if (strcmp(s, "
|
|
45
|
-
if (strcmp(s, "
|
|
46
|
-
if (strcmp(s, "
|
|
47
|
-
|
|
52
|
+
static int parse_func(const char *s, step_func *out) {
|
|
53
|
+
if (!s || !out) return TF_ERROR;
|
|
54
|
+
if (strcmp(s, "running-sum") == 0 || strcmp(s, "cumsum") == 0) *out = STEP_RUNNING_SUM;
|
|
55
|
+
else if (strcmp(s, "running-avg") == 0 || strcmp(s, "cumavg") == 0) *out = STEP_RUNNING_AVG;
|
|
56
|
+
else if (strcmp(s, "running-min") == 0) *out = STEP_RUNNING_MIN;
|
|
57
|
+
else if (strcmp(s, "running-max") == 0) *out = STEP_RUNNING_MAX;
|
|
58
|
+
else if (strcmp(s, "running-count") == 0) *out = STEP_RUNNING_COUNT;
|
|
59
|
+
else if (strcmp(s, "delta") == 0) *out = STEP_DELTA;
|
|
60
|
+
else if (strcmp(s, "lag") == 0) *out = STEP_LAG;
|
|
61
|
+
else if (strcmp(s, "ratio") == 0) *out = STEP_RATIO;
|
|
62
|
+
else return TF_ERROR;
|
|
63
|
+
return TF_OK;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static int step_is_numeric_func(step_func func) {
|
|
67
|
+
return func != STEP_RUNNING_COUNT;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static int step_is_numeric_type(tf_type type) {
|
|
71
|
+
return type == TF_TYPE_INT64 || type == TF_TYPE_FLOAT64;
|
|
48
72
|
}
|
|
49
73
|
|
|
50
74
|
static double get_numeric(const tf_batch *b, size_t r, int ci) {
|
|
51
75
|
if (b->col_types[ci] == TF_TYPE_INT64) return (double)tf_batch_get_int64(b, r, ci);
|
|
52
|
-
|
|
53
|
-
|
|
76
|
+
return tf_batch_get_float64(b, r, ci);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
static void step_set_col_error(const char *column, const char *suffix) {
|
|
80
|
+
char msg[512];
|
|
81
|
+
snprintf(msg, sizeof(msg), "step: column '%s' %s", column ? column : "", suffix);
|
|
82
|
+
tf_set_last_error(msg);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static int step_parse_missing_policy(const cJSON *args, step_missing_policy *out) {
|
|
86
|
+
*out = STEP_MISSING_ERROR;
|
|
87
|
+
const cJSON *j = cJSON_GetObjectItemCaseSensitive(args, "missing");
|
|
88
|
+
if (!j) return TF_OK;
|
|
89
|
+
if (!cJSON_IsString(j)) {
|
|
90
|
+
tf_set_last_error("step: missing must be error, null, or ignore");
|
|
91
|
+
return TF_ERROR;
|
|
92
|
+
}
|
|
93
|
+
if (strcmp(j->valuestring, "error") == 0) *out = STEP_MISSING_ERROR;
|
|
94
|
+
else if (strcmp(j->valuestring, "null") == 0) *out = STEP_MISSING_NULL;
|
|
95
|
+
else if (strcmp(j->valuestring, "ignore") == 0) *out = STEP_MISSING_IGNORE;
|
|
96
|
+
else {
|
|
97
|
+
tf_set_last_error("step: missing must be error, null, or ignore");
|
|
98
|
+
return TF_ERROR;
|
|
99
|
+
}
|
|
100
|
+
return TF_OK;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
static int step_parse_type_policy(const cJSON *args, step_type_policy *out) {
|
|
104
|
+
*out = STEP_TYPE_FAIL;
|
|
105
|
+
const cJSON *j = cJSON_GetObjectItemCaseSensitive(args, "on_type_error");
|
|
106
|
+
if (!j) return TF_OK;
|
|
107
|
+
if (!cJSON_IsString(j)) {
|
|
108
|
+
tf_set_last_error("step: on_type_error must be fail or null");
|
|
109
|
+
return TF_ERROR;
|
|
110
|
+
}
|
|
111
|
+
if (strcmp(j->valuestring, "fail") == 0) *out = STEP_TYPE_FAIL;
|
|
112
|
+
else if (strcmp(j->valuestring, "null") == 0) *out = STEP_TYPE_NULL;
|
|
113
|
+
else {
|
|
114
|
+
tf_set_last_error("step: on_type_error must be fail or null");
|
|
115
|
+
return TF_ERROR;
|
|
116
|
+
}
|
|
117
|
+
return TF_OK;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static int step_passthrough(tf_batch *in, tf_batch **out) {
|
|
121
|
+
tf_batch *ob = tf_batch_create(in->n_cols, in->n_rows);
|
|
122
|
+
if (!ob) return TF_ERROR;
|
|
123
|
+
if (tf_batch_clone_schema(ob, in) != TF_OK) {
|
|
124
|
+
tf_batch_free(ob);
|
|
125
|
+
return TF_ERROR;
|
|
126
|
+
}
|
|
127
|
+
for (size_t r = 0; r < in->n_rows; r++) {
|
|
128
|
+
if (tf_batch_copy_row(ob, r, in, r) != TF_OK ||
|
|
129
|
+
tf_batch_expose_row(ob, r) != TF_OK) {
|
|
130
|
+
tf_batch_free(ob);
|
|
131
|
+
return TF_ERROR;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
*out = ob;
|
|
135
|
+
return TF_OK;
|
|
54
136
|
}
|
|
55
137
|
|
|
56
138
|
static int step_process(tf_step *self, tf_batch *in, tf_batch **out,
|
|
@@ -59,71 +141,129 @@ static int step_process(tf_step *self, tf_batch *in, tf_batch **out,
|
|
|
59
141
|
step_state *st = self->state;
|
|
60
142
|
*out = NULL;
|
|
61
143
|
|
|
144
|
+
int ci = tf_batch_col_index(in, st->column);
|
|
145
|
+
int force_null = 0;
|
|
146
|
+
if (ci < 0) {
|
|
147
|
+
if (st->missing == STEP_MISSING_ERROR) {
|
|
148
|
+
step_set_col_error(st->column, "not found");
|
|
149
|
+
return TF_ERROR;
|
|
150
|
+
}
|
|
151
|
+
if (st->missing == STEP_MISSING_IGNORE) {
|
|
152
|
+
return step_passthrough(in, out);
|
|
153
|
+
}
|
|
154
|
+
force_null = 1;
|
|
155
|
+
} else if (step_is_numeric_func(st->func) &&
|
|
156
|
+
!step_is_numeric_type(in->col_types[(size_t)ci])) {
|
|
157
|
+
if (st->on_type_error == STEP_TYPE_FAIL) {
|
|
158
|
+
step_set_col_error(st->column, "must be numeric");
|
|
159
|
+
return TF_ERROR;
|
|
160
|
+
}
|
|
161
|
+
force_null = 1;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const char *extra_names[1] = {st->result};
|
|
165
|
+
tf_type extra_types[1] = {TF_TYPE_FLOAT64};
|
|
62
166
|
tf_batch *ob = tf_batch_create(in->n_cols + 1, in->n_rows);
|
|
63
167
|
if (!ob) return TF_ERROR;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
int ci = tf_batch_col_index(in, st->column);
|
|
168
|
+
if (tf_batch_clone_with_extra_cols(ob, in, extra_names, extra_types, 1) != TF_OK) {
|
|
169
|
+
tf_batch_free(ob);
|
|
170
|
+
return TF_ERROR;
|
|
171
|
+
}
|
|
69
172
|
|
|
70
173
|
for (size_t r = 0; r < in->n_rows; r++) {
|
|
71
|
-
tf_batch_copy_row(ob, r, in, r)
|
|
174
|
+
if (tf_batch_copy_row(ob, r, in, r) != TF_OK) {
|
|
175
|
+
tf_batch_free(ob);
|
|
176
|
+
return TF_ERROR;
|
|
177
|
+
}
|
|
72
178
|
|
|
73
|
-
if (
|
|
74
|
-
tf_batch_set_null(ob, r, in->n_cols);
|
|
75
|
-
ob
|
|
179
|
+
if (force_null || tf_batch_is_null(in, r, (size_t)ci)) {
|
|
180
|
+
if (tf_batch_set_null(ob, r, in->n_cols) != TF_OK) { tf_batch_free(ob); return TF_ERROR; }
|
|
181
|
+
if (tf_batch_expose_row(ob, r) != TF_OK) { tf_batch_free(ob); return TF_ERROR; }
|
|
76
182
|
continue;
|
|
77
183
|
}
|
|
78
184
|
|
|
79
|
-
double val = get_numeric(in, r, ci);
|
|
185
|
+
double val = step_is_numeric_func(st->func) ? get_numeric(in, r, ci) : 0.0;
|
|
80
186
|
double result = 0;
|
|
187
|
+
double next_running_sum = st->running_sum;
|
|
188
|
+
double next_running_min = st->running_min;
|
|
189
|
+
double next_running_max = st->running_max;
|
|
190
|
+
size_t next_running_count = st->running_count;
|
|
191
|
+
double next_prev_val = st->prev_val;
|
|
192
|
+
int next_has_prev = st->has_prev;
|
|
193
|
+
int write_null = 0;
|
|
81
194
|
|
|
82
195
|
switch (st->func) {
|
|
83
196
|
case STEP_RUNNING_SUM:
|
|
84
|
-
|
|
85
|
-
result =
|
|
197
|
+
next_running_sum += val;
|
|
198
|
+
result = next_running_sum;
|
|
86
199
|
break;
|
|
87
200
|
case STEP_RUNNING_AVG:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
201
|
+
if (next_running_count == SIZE_MAX) { tf_batch_free(ob); return TF_ERROR; }
|
|
202
|
+
next_running_sum += val;
|
|
203
|
+
next_running_count++;
|
|
204
|
+
result = next_running_sum / next_running_count;
|
|
91
205
|
break;
|
|
92
206
|
case STEP_RUNNING_MIN:
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
207
|
+
if (next_running_count == SIZE_MAX) { tf_batch_free(ob); return TF_ERROR; }
|
|
208
|
+
if (next_running_count == 0 || val < next_running_min)
|
|
209
|
+
next_running_min = val;
|
|
210
|
+
next_running_count++;
|
|
211
|
+
result = next_running_min;
|
|
97
212
|
break;
|
|
98
213
|
case STEP_RUNNING_MAX:
|
|
99
|
-
if (
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
214
|
+
if (next_running_count == SIZE_MAX) { tf_batch_free(ob); return TF_ERROR; }
|
|
215
|
+
if (next_running_count == 0 || val > next_running_max)
|
|
216
|
+
next_running_max = val;
|
|
217
|
+
next_running_count++;
|
|
218
|
+
result = next_running_max;
|
|
103
219
|
break;
|
|
104
220
|
case STEP_RUNNING_COUNT:
|
|
105
|
-
|
|
106
|
-
|
|
221
|
+
if (next_running_count == SIZE_MAX) { tf_batch_free(ob); return TF_ERROR; }
|
|
222
|
+
next_running_count++;
|
|
223
|
+
result = (double)next_running_count;
|
|
107
224
|
break;
|
|
108
225
|
case STEP_DELTA:
|
|
109
226
|
if (st->has_prev) result = val - st->prev_val;
|
|
110
|
-
else
|
|
111
|
-
|
|
227
|
+
else write_null = 1;
|
|
228
|
+
next_prev_val = val;
|
|
229
|
+
next_has_prev = 1;
|
|
112
230
|
break;
|
|
113
231
|
case STEP_LAG:
|
|
114
232
|
if (st->has_prev) result = st->prev_val;
|
|
115
|
-
else
|
|
116
|
-
|
|
233
|
+
else write_null = 1;
|
|
234
|
+
next_prev_val = val;
|
|
235
|
+
next_has_prev = 1;
|
|
117
236
|
break;
|
|
118
237
|
case STEP_RATIO:
|
|
119
238
|
if (st->has_prev && st->prev_val != 0) result = val / st->prev_val;
|
|
120
|
-
else
|
|
121
|
-
|
|
239
|
+
else write_null = 1;
|
|
240
|
+
next_prev_val = val;
|
|
241
|
+
next_has_prev = 1;
|
|
122
242
|
break;
|
|
123
243
|
}
|
|
124
244
|
|
|
125
|
-
|
|
126
|
-
|
|
245
|
+
if (write_null) {
|
|
246
|
+
if (tf_batch_set_null(ob, r, in->n_cols) != TF_OK) { tf_batch_free(ob); return TF_ERROR; }
|
|
247
|
+
if (tf_batch_expose_row(ob, r) != TF_OK) { tf_batch_free(ob); return TF_ERROR; }
|
|
248
|
+
st->prev_val = next_prev_val;
|
|
249
|
+
st->has_prev = next_has_prev;
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (tf_batch_set_float64(ob, r, in->n_cols, result) != TF_OK) {
|
|
254
|
+
tf_batch_free(ob);
|
|
255
|
+
return TF_ERROR;
|
|
256
|
+
}
|
|
257
|
+
if (tf_batch_expose_row(ob, r) != TF_OK) {
|
|
258
|
+
tf_batch_free(ob);
|
|
259
|
+
return TF_ERROR;
|
|
260
|
+
}
|
|
261
|
+
st->running_sum = next_running_sum;
|
|
262
|
+
st->running_min = next_running_min;
|
|
263
|
+
st->running_max = next_running_max;
|
|
264
|
+
st->running_count = next_running_count;
|
|
265
|
+
st->prev_val = next_prev_val;
|
|
266
|
+
st->has_prev = next_has_prev;
|
|
127
267
|
}
|
|
128
268
|
|
|
129
269
|
*out = ob;
|
|
@@ -140,28 +280,50 @@ static void step_destroy(tf_step *self) {
|
|
|
140
280
|
free(self);
|
|
141
281
|
}
|
|
142
282
|
|
|
283
|
+
static char *step_default_result_name(const char *column, const char *func) {
|
|
284
|
+
char *suffix = tf_string_append_suffix_checked("_", func);
|
|
285
|
+
if (!suffix) return NULL;
|
|
286
|
+
char *result = tf_string_append_suffix_checked(column, suffix);
|
|
287
|
+
free(suffix);
|
|
288
|
+
return result;
|
|
289
|
+
}
|
|
290
|
+
|
|
143
291
|
tf_step *tf_step_create(const cJSON *args) {
|
|
144
292
|
if (!args) return NULL;
|
|
145
293
|
cJSON *col_j = cJSON_GetObjectItemCaseSensitive(args, "column");
|
|
146
294
|
cJSON *func_j = cJSON_GetObjectItemCaseSensitive(args, "func");
|
|
147
|
-
if (!cJSON_IsString(col_j) || !
|
|
295
|
+
if (!cJSON_IsString(col_j) || !col_j->valuestring[0] ||
|
|
296
|
+
!cJSON_IsString(func_j) || !func_j->valuestring[0]) {
|
|
297
|
+
tf_set_last_error("step: column and func are required");
|
|
298
|
+
return NULL;
|
|
299
|
+
}
|
|
148
300
|
|
|
149
|
-
step_state *st =
|
|
301
|
+
step_state *st = tf_callocarray_checked(1, sizeof(step_state));
|
|
150
302
|
if (!st) return NULL;
|
|
151
|
-
st->column =
|
|
152
|
-
st->
|
|
303
|
+
st->column = tf_strdup_checked(col_j->valuestring);
|
|
304
|
+
if (!st->column) { free(st); return NULL; }
|
|
305
|
+
if (parse_func(func_j->valuestring, &st->func) != TF_OK) {
|
|
306
|
+
tf_set_last_error("step: func must be running-sum, running-avg, running-min, running-max, running-count, delta, lag, or ratio");
|
|
307
|
+
free(st->column);
|
|
308
|
+
free(st);
|
|
309
|
+
return NULL;
|
|
310
|
+
}
|
|
311
|
+
if (step_parse_missing_policy(args, &st->missing) != TF_OK ||
|
|
312
|
+
step_parse_type_policy(args, &st->on_type_error) != TF_OK) {
|
|
313
|
+
free(st->column);
|
|
314
|
+
free(st);
|
|
315
|
+
return NULL;
|
|
316
|
+
}
|
|
153
317
|
|
|
154
318
|
cJSON *res_j = cJSON_GetObjectItemCaseSensitive(args, "result");
|
|
155
319
|
if (cJSON_IsString(res_j)) {
|
|
156
|
-
st->result =
|
|
320
|
+
st->result = tf_strdup_checked(res_j->valuestring);
|
|
157
321
|
} else {
|
|
158
|
-
|
|
159
|
-
char buf[256];
|
|
160
|
-
snprintf(buf, sizeof(buf), "%s_%s", st->column, func_j->valuestring);
|
|
161
|
-
st->result = strdup(buf);
|
|
322
|
+
st->result = step_default_result_name(st->column, func_j->valuestring);
|
|
162
323
|
}
|
|
324
|
+
if (!st->result) { free(st->column); free(st); return NULL; }
|
|
163
325
|
|
|
164
|
-
tf_step *step =
|
|
326
|
+
tf_step *step = tf_callocarray_checked(1, sizeof(tf_step));
|
|
165
327
|
if (!step) { free(st->column); free(st->result); free(st); return NULL; }
|
|
166
328
|
step->process = step_process;
|
|
167
329
|
step->flush = step_flush;
|
package/csrc/op_tail.c
CHANGED
|
@@ -26,18 +26,18 @@ static int tail_process(tf_step *self, tf_batch *in, tf_batch **out,
|
|
|
26
26
|
if (!st->has_schema) {
|
|
27
27
|
st->buf = tf_batch_create(in->n_cols, st->limit);
|
|
28
28
|
if (!st->buf) return TF_ERROR;
|
|
29
|
-
|
|
30
|
-
tf_batch_set_schema(st->buf, c, in->col_names[c], in->col_types[c]);
|
|
29
|
+
if (tf_batch_clone_schema(st->buf, in) != TF_OK) return TF_ERROR;
|
|
31
30
|
st->buf->n_rows = 0;
|
|
32
31
|
st->has_schema = 1;
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
for (size_t r = 0; r < in->n_rows; r++) {
|
|
36
35
|
size_t dst = st->head % st->limit;
|
|
37
|
-
tf_batch_copy_row(st->buf, dst, in, r);
|
|
36
|
+
if (tf_batch_copy_row(st->buf, dst, in, r) != TF_OK) return TF_ERROR;
|
|
38
37
|
st->head++;
|
|
39
38
|
st->count++;
|
|
40
|
-
if (st->buf->n_rows < st->limit
|
|
39
|
+
if (st->buf->n_rows < st->limit &&
|
|
40
|
+
tf_batch_expose_row(st->buf, st->buf->n_rows) != TF_OK) return TF_ERROR;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
return TF_OK;
|
|
@@ -53,15 +53,23 @@ static int tail_flush(tf_step *self, tf_batch **out, tf_side_channels *side) {
|
|
|
53
53
|
size_t n = st->buf->n_rows;
|
|
54
54
|
tf_batch *ob = tf_batch_create(st->buf->n_cols, n);
|
|
55
55
|
if (!ob) return TF_ERROR;
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
if (tf_batch_clone_schema(ob, st->buf) != TF_OK) {
|
|
57
|
+
tf_batch_free(ob);
|
|
58
|
+
return TF_ERROR;
|
|
59
|
+
}
|
|
58
60
|
|
|
59
61
|
/* Read from circular buffer in order */
|
|
60
62
|
size_t start = (st->head >= n) ? (st->head - n) % st->limit : 0;
|
|
61
63
|
for (size_t i = 0; i < n; i++) {
|
|
62
64
|
size_t src = (start + i) % st->limit;
|
|
63
|
-
tf_batch_copy_row(ob, i, st->buf, src)
|
|
64
|
-
|
|
65
|
+
if (tf_batch_copy_row(ob, i, st->buf, src) != TF_OK) {
|
|
66
|
+
tf_batch_free(ob);
|
|
67
|
+
return TF_ERROR;
|
|
68
|
+
}
|
|
69
|
+
if (tf_batch_expose_row(ob, i) != TF_OK) {
|
|
70
|
+
tf_batch_free(ob);
|
|
71
|
+
return TF_ERROR;
|
|
72
|
+
}
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
*out = ob;
|
|
@@ -79,14 +87,15 @@ static void tail_destroy(tf_step *self) {
|
|
|
79
87
|
|
|
80
88
|
tf_step *tf_tail_create(const cJSON *args) {
|
|
81
89
|
if (!args) return NULL;
|
|
82
|
-
|
|
83
|
-
|
|
90
|
+
size_t n = 0;
|
|
91
|
+
int has_n = tf_json_get_size_arg(args, "n", 1, TF_MAX_OUTPUT_ROWS_PER_BATCH, &n, "tail");
|
|
92
|
+
if (has_n <= 0) return NULL;
|
|
84
93
|
|
|
85
94
|
tail_state *st = calloc(1, sizeof(tail_state));
|
|
86
95
|
if (!st) return NULL;
|
|
87
|
-
st->limit =
|
|
96
|
+
st->limit = n;
|
|
88
97
|
|
|
89
|
-
tf_step *step =
|
|
98
|
+
tf_step *step = calloc(1, sizeof(tf_step));
|
|
90
99
|
if (!step) { free(st); return NULL; }
|
|
91
100
|
step->process = tail_process;
|
|
92
101
|
step->flush = tail_flush;
|