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_rleid.c
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* op_rleid.c - Consecutive run identifier.
|
|
3
|
+
*
|
|
4
|
+
* Config: {"columns": ["city", "status"], "result": "run_id"}
|
|
5
|
+
*
|
|
6
|
+
* Appends an int64 run id that starts at 1 and increments whenever the
|
|
7
|
+
* selected key changes from the previous row. State retained across batches is
|
|
8
|
+
* only the previous formatted key and the current id.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#include "internal.h"
|
|
12
|
+
#include "cJSON.h"
|
|
13
|
+
#include <stdlib.h>
|
|
14
|
+
#include <string.h>
|
|
15
|
+
#include <stdio.h>
|
|
16
|
+
#include <stdarg.h>
|
|
17
|
+
#include <stdint.h>
|
|
18
|
+
#include <limits.h>
|
|
19
|
+
|
|
20
|
+
typedef struct {
|
|
21
|
+
char *data;
|
|
22
|
+
size_t len;
|
|
23
|
+
size_t cap;
|
|
24
|
+
} rleid_buf;
|
|
25
|
+
|
|
26
|
+
typedef struct {
|
|
27
|
+
char **cols;
|
|
28
|
+
size_t n_cols;
|
|
29
|
+
char *result;
|
|
30
|
+
char *prev_key;
|
|
31
|
+
int seen;
|
|
32
|
+
int64_t current_id;
|
|
33
|
+
} rleid_state;
|
|
34
|
+
|
|
35
|
+
static int rleid_buf_init(rleid_buf *b) {
|
|
36
|
+
b->cap = 128;
|
|
37
|
+
b->len = 0;
|
|
38
|
+
b->data = tf_mallocarray_checked(b->cap, sizeof(char));
|
|
39
|
+
if (!b->data) return -1;
|
|
40
|
+
b->data[0] = '\0';
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static int rleid_buf_ensure(rleid_buf *b, size_t extra) {
|
|
45
|
+
size_t need = 0;
|
|
46
|
+
if (tf_size_add(b->len, extra, &need) != TF_OK ||
|
|
47
|
+
tf_size_add(need, 1, &need) != TF_OK) {
|
|
48
|
+
return -1;
|
|
49
|
+
}
|
|
50
|
+
if (need <= b->cap) return 0;
|
|
51
|
+
size_t new_cap = 0;
|
|
52
|
+
if (tf_size_grow_pow2(b->cap, need, 128, &new_cap) != TF_OK) return -1;
|
|
53
|
+
char *tmp = tf_reallocarray_checked(b->data, new_cap, sizeof(char));
|
|
54
|
+
if (!tmp) return -1;
|
|
55
|
+
b->data = tmp;
|
|
56
|
+
b->cap = new_cap;
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static int rleid_buf_appendn(rleid_buf *b, const char *s, size_t n) {
|
|
61
|
+
if (rleid_buf_ensure(b, n) != 0) return -1;
|
|
62
|
+
memcpy(b->data + b->len, s, n);
|
|
63
|
+
b->len += n;
|
|
64
|
+
b->data[b->len] = '\0';
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static int rleid_buf_append(rleid_buf *b, const char *s) {
|
|
69
|
+
return rleid_buf_appendn(b, s, strlen(s));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static int rleid_buf_appendf(rleid_buf *b, const char *fmt, ...) {
|
|
73
|
+
char tmp[128];
|
|
74
|
+
va_list ap;
|
|
75
|
+
va_start(ap, fmt);
|
|
76
|
+
int n = vsnprintf(tmp, sizeof(tmp), fmt, ap);
|
|
77
|
+
va_end(ap);
|
|
78
|
+
if (n < 0) return -1;
|
|
79
|
+
if ((size_t)n < sizeof(tmp)) return rleid_buf_appendn(b, tmp, (size_t)n);
|
|
80
|
+
|
|
81
|
+
char *dyn = malloc((size_t)n + 1);
|
|
82
|
+
if (!dyn) return -1;
|
|
83
|
+
va_start(ap, fmt);
|
|
84
|
+
vsnprintf(dyn, (size_t)n + 1, fmt, ap);
|
|
85
|
+
va_end(ap);
|
|
86
|
+
int rc = rleid_buf_appendn(b, dyn, (size_t)n);
|
|
87
|
+
free(dyn);
|
|
88
|
+
return rc;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static int rleid_set_error(tf_side_channels *side, const char *msg) {
|
|
92
|
+
return tf_side_write_error(side, msg);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
static int append_cell_key(rleid_buf *b, const tf_batch *in, size_t row, int ci) {
|
|
96
|
+
if (tf_batch_is_null(in, row, ci)) {
|
|
97
|
+
return rleid_buf_appendf(b, "c%d:t%d:N;", ci, (int)in->col_types[ci]);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
char num[96];
|
|
101
|
+
const char *val = NULL;
|
|
102
|
+
size_t len = 0;
|
|
103
|
+
switch (in->col_types[ci]) {
|
|
104
|
+
case TF_TYPE_STRING:
|
|
105
|
+
val = tf_batch_get_string(in, row, ci);
|
|
106
|
+
len = val ? strlen(val) : 0;
|
|
107
|
+
break;
|
|
108
|
+
case TF_TYPE_INT64:
|
|
109
|
+
snprintf(num, sizeof(num), "%lld", (long long)tf_batch_get_int64(in, row, ci));
|
|
110
|
+
val = num;
|
|
111
|
+
len = strlen(num);
|
|
112
|
+
break;
|
|
113
|
+
case TF_TYPE_FLOAT64:
|
|
114
|
+
snprintf(num, sizeof(num), "%.17g", tf_batch_get_float64(in, row, ci));
|
|
115
|
+
val = num;
|
|
116
|
+
len = strlen(num);
|
|
117
|
+
break;
|
|
118
|
+
case TF_TYPE_BOOL:
|
|
119
|
+
val = tf_batch_get_bool(in, row, ci) ? "true" : "false";
|
|
120
|
+
len = strlen(val);
|
|
121
|
+
break;
|
|
122
|
+
case TF_TYPE_DATE:
|
|
123
|
+
snprintf(num, sizeof(num), "%d", (int)tf_batch_get_date(in, row, ci));
|
|
124
|
+
val = num;
|
|
125
|
+
len = strlen(num);
|
|
126
|
+
break;
|
|
127
|
+
case TF_TYPE_TIMESTAMP:
|
|
128
|
+
snprintf(num, sizeof(num), "%lld", (long long)tf_batch_get_timestamp(in, row, ci));
|
|
129
|
+
val = num;
|
|
130
|
+
len = strlen(num);
|
|
131
|
+
break;
|
|
132
|
+
default:
|
|
133
|
+
val = "";
|
|
134
|
+
len = 0;
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (rleid_buf_appendf(b, "c%d:t%d:V%zu:", ci, (int)in->col_types[ci], len) != 0) return -1;
|
|
139
|
+
if (rleid_buf_appendn(b, val ? val : "", len) != 0) return -1;
|
|
140
|
+
return rleid_buf_append(b, ";");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static char *format_rleid_key(const tf_batch *in, size_t row, const int *col_indices, size_t n_cols) {
|
|
144
|
+
rleid_buf b;
|
|
145
|
+
if (rleid_buf_init(&b) != 0) return NULL;
|
|
146
|
+
for (size_t i = 0; i < n_cols; i++) {
|
|
147
|
+
if (append_cell_key(&b, in, row, col_indices[i]) != 0) {
|
|
148
|
+
free(b.data);
|
|
149
|
+
return NULL;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return b.data;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
static int resolve_columns(const rleid_state *st, const tf_batch *in, int **out_indices,
|
|
156
|
+
size_t *out_n, tf_side_channels *side) {
|
|
157
|
+
if (st->n_cols == 0) {
|
|
158
|
+
if (rleid_set_error(side, "rleid: columns must not be empty") != TF_OK) return TF_ERROR;
|
|
159
|
+
return TF_ERROR;
|
|
160
|
+
}
|
|
161
|
+
int *idx = malloc(st->n_cols * sizeof(int));
|
|
162
|
+
if (!idx) return TF_ERROR;
|
|
163
|
+
for (size_t i = 0; i < st->n_cols; i++) {
|
|
164
|
+
idx[i] = tf_batch_col_index(in, st->cols[i]);
|
|
165
|
+
if (idx[i] < 0) {
|
|
166
|
+
char msg[256];
|
|
167
|
+
snprintf(msg, sizeof(msg), "rleid: column '%s' not found", st->cols[i]);
|
|
168
|
+
int err_rc = rleid_set_error(side, msg);
|
|
169
|
+
free(idx);
|
|
170
|
+
if (err_rc != TF_OK) return TF_ERROR;
|
|
171
|
+
return TF_ERROR;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
*out_indices = idx;
|
|
175
|
+
*out_n = st->n_cols;
|
|
176
|
+
return TF_OK;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
static int rleid_process(tf_step *self, tf_batch *in, tf_batch **out,
|
|
180
|
+
tf_side_channels *side) {
|
|
181
|
+
rleid_state *st = self->state;
|
|
182
|
+
*out = NULL;
|
|
183
|
+
|
|
184
|
+
int *col_indices = NULL;
|
|
185
|
+
size_t n_keys = 0;
|
|
186
|
+
if (resolve_columns(st, in, &col_indices, &n_keys, side) != TF_OK) return TF_ERROR;
|
|
187
|
+
|
|
188
|
+
const char *extra_names[1] = {st->result};
|
|
189
|
+
tf_type extra_types[1] = {TF_TYPE_INT64};
|
|
190
|
+
tf_batch *ob = tf_batch_create(in->n_cols + 1, in->n_rows);
|
|
191
|
+
if (!ob) { free(col_indices); return TF_ERROR; }
|
|
192
|
+
if (tf_batch_clone_with_extra_cols(ob, in, extra_names, extra_types, 1) != TF_OK) {
|
|
193
|
+
tf_batch_free(ob);
|
|
194
|
+
free(col_indices);
|
|
195
|
+
return TF_ERROR;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
for (size_t r = 0; r < in->n_rows; r++) {
|
|
199
|
+
char *key = format_rleid_key(in, r, col_indices, n_keys);
|
|
200
|
+
if (!key) { tf_batch_free(ob); free(col_indices); return TF_ERROR; }
|
|
201
|
+
if (!st->seen || strcmp(key, st->prev_key) != 0) {
|
|
202
|
+
st->current_id++;
|
|
203
|
+
st->seen = 1;
|
|
204
|
+
free(st->prev_key);
|
|
205
|
+
st->prev_key = key;
|
|
206
|
+
} else {
|
|
207
|
+
free(key);
|
|
208
|
+
}
|
|
209
|
+
if (tf_batch_copy_row(ob, r, in, r) != TF_OK) {
|
|
210
|
+
tf_batch_free(ob);
|
|
211
|
+
free(col_indices);
|
|
212
|
+
return TF_ERROR;
|
|
213
|
+
}
|
|
214
|
+
if (tf_batch_set_int64(ob, r, in->n_cols, st->current_id) != TF_OK) {
|
|
215
|
+
tf_batch_free(ob);
|
|
216
|
+
free(col_indices);
|
|
217
|
+
return TF_ERROR;
|
|
218
|
+
}
|
|
219
|
+
if (tf_batch_expose_row(ob, r) != TF_OK) {
|
|
220
|
+
tf_batch_free(ob);
|
|
221
|
+
free(col_indices);
|
|
222
|
+
return TF_ERROR;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
free(col_indices);
|
|
227
|
+
*out = ob;
|
|
228
|
+
return TF_OK;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
static int rleid_flush(tf_step *self, tf_batch **out, tf_side_channels *side) {
|
|
232
|
+
(void)self;
|
|
233
|
+
(void)side;
|
|
234
|
+
*out = NULL;
|
|
235
|
+
return TF_OK;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
static void rleid_state_free(rleid_state *st) {
|
|
239
|
+
if (!st) return;
|
|
240
|
+
if (st->cols) {
|
|
241
|
+
for (size_t i = 0; i < st->n_cols; i++) free(st->cols[i]);
|
|
242
|
+
}
|
|
243
|
+
free(st->cols);
|
|
244
|
+
free(st->result);
|
|
245
|
+
free(st->prev_key);
|
|
246
|
+
free(st);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
static void rleid_destroy(tf_step *self) {
|
|
250
|
+
if (!self) return;
|
|
251
|
+
rleid_state_free(self->state);
|
|
252
|
+
free(self);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
tf_step *tf_rleid_create(const cJSON *args) {
|
|
256
|
+
if (!args) return NULL;
|
|
257
|
+
cJSON *cols = cJSON_GetObjectItemCaseSensitive(args, "columns");
|
|
258
|
+
if (!cJSON_IsArray(cols) || cJSON_GetArraySize(cols) <= 0) {
|
|
259
|
+
tf_set_last_error("rleid: columns must be a non-empty array");
|
|
260
|
+
return NULL;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
rleid_state *st = calloc(1, sizeof(rleid_state));
|
|
264
|
+
if (!st) return NULL;
|
|
265
|
+
|
|
266
|
+
int n = cJSON_GetArraySize(cols);
|
|
267
|
+
st->cols = calloc((size_t)n, sizeof(char *));
|
|
268
|
+
if (!st->cols) { rleid_state_free(st); return NULL; }
|
|
269
|
+
st->n_cols = (size_t)n;
|
|
270
|
+
for (int i = 0; i < n; i++) {
|
|
271
|
+
cJSON *item = cJSON_GetArrayItem(cols, i);
|
|
272
|
+
if (!cJSON_IsString(item) || !item->valuestring || item->valuestring[0] == '\0') {
|
|
273
|
+
tf_set_last_error("rleid: column names must be non-empty strings");
|
|
274
|
+
rleid_state_free(st);
|
|
275
|
+
return NULL;
|
|
276
|
+
}
|
|
277
|
+
st->cols[i] = strdup(item->valuestring);
|
|
278
|
+
if (!st->cols[i]) { rleid_state_free(st); return NULL; }
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
cJSON *res = cJSON_GetObjectItemCaseSensitive(args, "result");
|
|
282
|
+
if (cJSON_IsString(res) && (!res->valuestring || res->valuestring[0] == '\0')) {
|
|
283
|
+
tf_set_last_error("rleid: result must be a non-empty string");
|
|
284
|
+
rleid_state_free(st);
|
|
285
|
+
return NULL;
|
|
286
|
+
}
|
|
287
|
+
st->result = strdup(cJSON_IsString(res) && res->valuestring ? res->valuestring : "_rleid");
|
|
288
|
+
if (!st->result) { rleid_state_free(st); return NULL; }
|
|
289
|
+
|
|
290
|
+
tf_step *step = calloc(1, sizeof(tf_step));
|
|
291
|
+
if (!step) { rleid_state_free(st); return NULL; }
|
|
292
|
+
step->process = rleid_process;
|
|
293
|
+
step->flush = rleid_flush;
|
|
294
|
+
step->destroy = rleid_destroy;
|
|
295
|
+
step->state = st;
|
|
296
|
+
return step;
|
|
297
|
+
}
|