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_unpivot.c
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
|
|
8
8
|
#include "internal.h"
|
|
9
9
|
#include "cJSON.h"
|
|
10
|
-
#include "date_utils.h"
|
|
11
10
|
#include <stdlib.h>
|
|
12
11
|
#include <string.h>
|
|
13
12
|
#include <stdio.h>
|
|
@@ -15,8 +14,17 @@
|
|
|
15
14
|
typedef struct {
|
|
16
15
|
char **cols;
|
|
17
16
|
size_t n_cols;
|
|
17
|
+
size_t max_output_rows_per_input_row;
|
|
18
|
+
size_t max_output_rows_per_batch;
|
|
18
19
|
} unpivot_state;
|
|
19
20
|
|
|
21
|
+
static int unpivot_set_cap_error(const char *name, size_t limit) {
|
|
22
|
+
char msg[160];
|
|
23
|
+
snprintf(msg, sizeof(msg), "unpivot: %s=%zu exceeded", name, limit);
|
|
24
|
+
tf_set_last_error(msg);
|
|
25
|
+
return TF_ERROR;
|
|
26
|
+
}
|
|
27
|
+
|
|
20
28
|
static int unpivot_process(tf_step *self, tf_batch *in, tf_batch **out,
|
|
21
29
|
tf_side_channels *side) {
|
|
22
30
|
(void)side;
|
|
@@ -24,7 +32,7 @@ static int unpivot_process(tf_step *self, tf_batch *in, tf_batch **out,
|
|
|
24
32
|
*out = NULL;
|
|
25
33
|
|
|
26
34
|
/* Determine which columns are value columns and which are id columns */
|
|
27
|
-
int *is_value =
|
|
35
|
+
int *is_value = tf_callocarray_checked(in->n_cols ? in->n_cols : 1, sizeof(int));
|
|
28
36
|
if (!is_value) return TF_ERROR;
|
|
29
37
|
size_t n_value = 0;
|
|
30
38
|
for (size_t c = 0; c < in->n_cols; c++) {
|
|
@@ -40,117 +48,143 @@ static int unpivot_process(tf_step *self, tf_batch *in, tf_batch **out,
|
|
|
40
48
|
if (n_value == 0) { free(is_value); return TF_OK; }
|
|
41
49
|
|
|
42
50
|
/* Output: id columns + "variable" + "value" */
|
|
43
|
-
size_t out_n_cols =
|
|
44
|
-
|
|
51
|
+
size_t out_n_cols = 0;
|
|
52
|
+
if (tf_size_add(n_id, 2, &out_n_cols) != TF_OK) { free(is_value); return TF_ERROR; }
|
|
53
|
+
if (n_value > st->max_output_rows_per_input_row) {
|
|
54
|
+
unpivot_set_cap_error("max_output_rows_per_input_row", st->max_output_rows_per_input_row);
|
|
55
|
+
free(is_value);
|
|
56
|
+
return TF_ERROR;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
size_t *id_cols = NULL;
|
|
60
|
+
if (n_id > 0) {
|
|
61
|
+
id_cols = tf_callocarray_checked(n_id, sizeof(size_t));
|
|
62
|
+
if (!id_cols) { free(is_value); return TF_ERROR; }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
size_t max_rows = 0;
|
|
66
|
+
if (tf_size_mul(in->n_rows, n_value, &max_rows) != TF_OK ||
|
|
67
|
+
max_rows > st->max_output_rows_per_batch) {
|
|
68
|
+
unpivot_set_cap_error("max_output_rows_per_batch", st->max_output_rows_per_batch);
|
|
69
|
+
free(id_cols);
|
|
70
|
+
free(is_value);
|
|
71
|
+
return TF_ERROR;
|
|
72
|
+
}
|
|
45
73
|
tf_batch *ob = tf_batch_create(out_n_cols, max_rows > 0 ? max_rows : 16);
|
|
46
|
-
if (!ob) { free(is_value); return TF_ERROR; }
|
|
74
|
+
if (!ob) { free(id_cols); free(is_value); return TF_ERROR; }
|
|
47
75
|
|
|
48
76
|
size_t oc = 0;
|
|
49
77
|
for (size_t c = 0; c < in->n_cols; c++) {
|
|
50
|
-
if (!is_value[c])
|
|
51
|
-
|
|
78
|
+
if (!is_value[c]) {
|
|
79
|
+
id_cols[oc] = c;
|
|
80
|
+
if (tf_batch_set_schema(ob, oc++, in->col_names[c], in->col_types[c]) != TF_OK) goto fail;
|
|
81
|
+
}
|
|
52
82
|
}
|
|
53
|
-
tf_batch_set_schema(ob, oc, "variable", TF_TYPE_STRING)
|
|
54
|
-
|
|
83
|
+
if (tf_batch_set_schema(ob, oc, "variable", TF_TYPE_STRING) != TF_OK ||
|
|
84
|
+
tf_batch_set_schema(ob, oc + 1, "value", TF_TYPE_STRING) != TF_OK) goto fail;
|
|
55
85
|
|
|
56
86
|
size_t out_row = 0;
|
|
57
87
|
for (size_t r = 0; r < in->n_rows; r++) {
|
|
58
88
|
for (size_t c = 0; c < in->n_cols; c++) {
|
|
59
89
|
if (!is_value[c]) continue;
|
|
60
90
|
|
|
61
|
-
|
|
91
|
+
if (out_row >= st->max_output_rows_per_batch) {
|
|
92
|
+
unpivot_set_cap_error("max_output_rows_per_batch", st->max_output_rows_per_batch);
|
|
93
|
+
goto fail;
|
|
94
|
+
}
|
|
95
|
+
size_t need_rows = 0;
|
|
96
|
+
if (tf_size_add(out_row, 1, &need_rows) != TF_OK ||
|
|
97
|
+
tf_batch_ensure_capacity(ob, need_rows) != TF_OK) goto fail;
|
|
62
98
|
|
|
63
99
|
/* Copy id columns */
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (is_value[ic]) continue;
|
|
67
|
-
if (tf_batch_is_null(in, r, ic))
|
|
68
|
-
tf_batch_set_null(ob, out_row, oidx);
|
|
69
|
-
else {
|
|
70
|
-
switch (in->col_types[ic]) {
|
|
71
|
-
case TF_TYPE_BOOL: tf_batch_set_bool(ob, out_row, oidx, tf_batch_get_bool(in, r, ic)); break;
|
|
72
|
-
case TF_TYPE_INT64: tf_batch_set_int64(ob, out_row, oidx, tf_batch_get_int64(in, r, ic)); break;
|
|
73
|
-
case TF_TYPE_FLOAT64: tf_batch_set_float64(ob, out_row, oidx, tf_batch_get_float64(in, r, ic)); break;
|
|
74
|
-
case TF_TYPE_STRING: tf_batch_set_string(ob, out_row, oidx, tf_batch_get_string(in, r, ic)); break;
|
|
75
|
-
case TF_TYPE_DATE: tf_batch_set_date(ob, out_row, oidx, tf_batch_get_date(in, r, ic)); break;
|
|
76
|
-
case TF_TYPE_TIMESTAMP: tf_batch_set_timestamp(ob, out_row, oidx, tf_batch_get_timestamp(in, r, ic)); break;
|
|
77
|
-
default: tf_batch_set_null(ob, out_row, oidx); break;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
oidx++;
|
|
81
|
-
}
|
|
100
|
+
if (n_id > 0 &&
|
|
101
|
+
tf_batch_copy_selected_row(ob, out_row, in, r, id_cols, n_id) != TF_OK) goto fail;
|
|
82
102
|
|
|
83
103
|
/* Set variable name */
|
|
84
|
-
tf_batch_set_string(ob, out_row, n_id, in->col_names[c]);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
tf_batch_set_null(ob, out_row, n_id + 1);
|
|
89
|
-
} else {
|
|
90
|
-
char buf[64];
|
|
91
|
-
switch (in->col_types[c]) {
|
|
92
|
-
case TF_TYPE_STRING: tf_batch_set_string(ob, out_row, n_id + 1, tf_batch_get_string(in, r, c)); break;
|
|
93
|
-
case TF_TYPE_INT64:
|
|
94
|
-
snprintf(buf, sizeof(buf), "%lld", (long long)tf_batch_get_int64(in, r, c));
|
|
95
|
-
tf_batch_set_string(ob, out_row, n_id + 1, buf); break;
|
|
96
|
-
case TF_TYPE_FLOAT64:
|
|
97
|
-
snprintf(buf, sizeof(buf), "%g", tf_batch_get_float64(in, r, c));
|
|
98
|
-
tf_batch_set_string(ob, out_row, n_id + 1, buf); break;
|
|
99
|
-
case TF_TYPE_BOOL:
|
|
100
|
-
tf_batch_set_string(ob, out_row, n_id + 1, tf_batch_get_bool(in, r, c) ? "true" : "false"); break;
|
|
101
|
-
case TF_TYPE_DATE:
|
|
102
|
-
tf_date_format(tf_batch_get_date(in, r, c), buf, sizeof(buf));
|
|
103
|
-
tf_batch_set_string(ob, out_row, n_id + 1, buf); break;
|
|
104
|
-
case TF_TYPE_TIMESTAMP:
|
|
105
|
-
tf_timestamp_format(tf_batch_get_timestamp(in, r, c), buf, sizeof(buf));
|
|
106
|
-
tf_batch_set_string(ob, out_row, n_id + 1, buf); break;
|
|
107
|
-
default:
|
|
108
|
-
tf_batch_set_null(ob, out_row, n_id + 1); break;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
104
|
+
if (tf_batch_set_string(ob, out_row, n_id, in->col_names[c]) != TF_OK) goto fail;
|
|
105
|
+
|
|
106
|
+
if (tf_batch_copy_cell_as_string(ob, out_row, n_id + 1, in, r, c) != TF_OK)
|
|
107
|
+
goto fail;
|
|
111
108
|
|
|
112
|
-
ob
|
|
109
|
+
if (tf_batch_expose_row(ob, out_row) != TF_OK) goto fail;
|
|
110
|
+
out_row++;
|
|
113
111
|
}
|
|
114
112
|
}
|
|
115
113
|
|
|
114
|
+
free(id_cols);
|
|
116
115
|
free(is_value);
|
|
117
116
|
if (out_row > 0) *out = ob;
|
|
118
117
|
else tf_batch_free(ob);
|
|
119
118
|
return TF_OK;
|
|
119
|
+
|
|
120
|
+
fail:
|
|
121
|
+
free(id_cols);
|
|
122
|
+
free(is_value);
|
|
123
|
+
tf_batch_free(ob);
|
|
124
|
+
return TF_ERROR;
|
|
120
125
|
}
|
|
121
126
|
|
|
122
127
|
static int unpivot_flush(tf_step *self, tf_batch **out, tf_side_channels *side) {
|
|
123
128
|
(void)self; (void)side; *out = NULL; return TF_OK;
|
|
124
129
|
}
|
|
125
130
|
|
|
126
|
-
static void
|
|
127
|
-
unpivot_state *st = self->state;
|
|
131
|
+
static void unpivot_state_free(unpivot_state *st) {
|
|
128
132
|
if (st) {
|
|
129
|
-
|
|
133
|
+
if (st->cols) {
|
|
134
|
+
for (size_t i = 0; i < st->n_cols; i++) free(st->cols[i]);
|
|
135
|
+
}
|
|
130
136
|
free(st->cols); free(st);
|
|
131
137
|
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
static void unpivot_destroy(tf_step *self) {
|
|
141
|
+
unpivot_state_free(self->state);
|
|
132
142
|
free(self);
|
|
133
143
|
}
|
|
134
144
|
|
|
135
145
|
tf_step *tf_unpivot_create(const cJSON *args) {
|
|
136
146
|
if (!args) return NULL;
|
|
137
147
|
cJSON *columns = cJSON_GetObjectItemCaseSensitive(args, "columns");
|
|
138
|
-
if (!columns || !cJSON_IsArray(columns))
|
|
148
|
+
if (!columns || !cJSON_IsArray(columns)) {
|
|
149
|
+
tf_set_last_error("unpivot: columns must be a non-empty array");
|
|
150
|
+
return NULL;
|
|
151
|
+
}
|
|
139
152
|
|
|
140
153
|
int n = cJSON_GetArraySize(columns);
|
|
141
|
-
if (n <= 0)
|
|
154
|
+
if (n <= 0) {
|
|
155
|
+
tf_set_last_error("unpivot: columns must be a non-empty array");
|
|
156
|
+
return NULL;
|
|
157
|
+
}
|
|
142
158
|
|
|
143
159
|
unpivot_state *st = calloc(1, sizeof(unpivot_state));
|
|
144
160
|
if (!st) return NULL;
|
|
145
|
-
st->
|
|
146
|
-
st->
|
|
161
|
+
st->max_output_rows_per_input_row = TF_MAX_EXPANDING_ROWS_PER_INPUT;
|
|
162
|
+
st->max_output_rows_per_batch = TF_MAX_OUTPUT_ROWS_PER_BATCH;
|
|
163
|
+
|
|
164
|
+
size_t parsed_size = 0;
|
|
165
|
+
int has_size = tf_json_get_size_arg(args, "max_output_rows_per_input_row", 1, TF_MAX_EXPANDING_ROWS_PER_INPUT, &parsed_size, "unpivot");
|
|
166
|
+
if (has_size < 0) { free(st); return NULL; }
|
|
167
|
+
if (has_size > 0) st->max_output_rows_per_input_row = parsed_size;
|
|
168
|
+
has_size = tf_json_get_size_arg(args, "max_output_rows_per_batch", 1, TF_MAX_OUTPUT_ROWS_PER_BATCH, &parsed_size, "unpivot");
|
|
169
|
+
if (has_size < 0) { free(st); return NULL; }
|
|
170
|
+
if (has_size > 0) st->max_output_rows_per_batch = parsed_size;
|
|
171
|
+
|
|
172
|
+
st->cols = tf_callocarray_checked((size_t)n, sizeof(char *));
|
|
173
|
+
if (!st->cols) { free(st); return NULL; }
|
|
174
|
+
st->n_cols = (size_t)n;
|
|
147
175
|
for (int i = 0; i < n; i++) {
|
|
148
176
|
cJSON *item = cJSON_GetArrayItem(columns, i);
|
|
149
|
-
if (cJSON_IsString(item)
|
|
177
|
+
if (!cJSON_IsString(item) || !item->valuestring || item->valuestring[0] == '\0') {
|
|
178
|
+
tf_set_last_error("unpivot: column names must be non-empty strings");
|
|
179
|
+
unpivot_state_free(st);
|
|
180
|
+
return NULL;
|
|
181
|
+
}
|
|
182
|
+
st->cols[i] = strdup(item->valuestring);
|
|
183
|
+
if (!st->cols[i]) { unpivot_state_free(st); return NULL; }
|
|
150
184
|
}
|
|
151
185
|
|
|
152
|
-
tf_step *step =
|
|
153
|
-
if (!step) {
|
|
186
|
+
tf_step *step = calloc(1, sizeof(tf_step));
|
|
187
|
+
if (!step) { unpivot_state_free(st); return NULL; }
|
|
154
188
|
step->process = unpivot_process;
|
|
155
189
|
step->flush = unpivot_flush;
|
|
156
190
|
step->destroy = unpivot_destroy;
|