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.
Files changed (132) hide show
  1. package/LICENSE +177 -0
  2. package/NOTICE +8 -0
  3. package/README.md +443 -51
  4. package/app/assets/{index-pDFMluyz.js → index-BIAIKnrp.js} +1 -1
  5. package/app/index.html +1 -1
  6. package/binding.gyp +55 -3
  7. package/csrc/arena.c +7 -5
  8. package/csrc/batch.c +818 -71
  9. package/csrc/buffer.c +84 -8
  10. package/csrc/cJSON.c +262 -19
  11. package/csrc/cJSON.h +17 -1
  12. package/csrc/codec_csv.c +1074 -181
  13. package/csrc/codec_jsonl.c +830 -118
  14. package/csrc/codec_table.c +108 -78
  15. package/csrc/codec_text.c +286 -68
  16. package/csrc/compiler.c +31 -3
  17. package/csrc/config.h +21 -0
  18. package/csrc/dsl.c +4722 -485
  19. package/csrc/expr.c +363 -55
  20. package/csrc/expr.h +2 -0
  21. package/csrc/internal.h +316 -27
  22. package/csrc/ir.c +65 -18
  23. package/csrc/ir.h +41 -0
  24. package/csrc/ir_schema.c +20 -5
  25. package/csrc/ir_serialize.c +68 -6
  26. package/csrc/ir_sql.c +796 -185
  27. package/csrc/ir_validate.c +462 -6
  28. package/csrc/json_path.c +210 -0
  29. package/csrc/main.c +879 -30
  30. package/csrc/memory_estimate.c +477 -0
  31. package/csrc/op_acf.c +171 -21
  32. package/csrc/op_across.c +477 -0
  33. package/csrc/op_anomaly.c +167 -32
  34. package/csrc/op_assert.c +761 -0
  35. package/csrc/op_bin.c +168 -29
  36. package/csrc/op_cast.c +383 -55
  37. package/csrc/op_clip.c +30 -19
  38. package/csrc/op_date_trunc.c +208 -34
  39. package/csrc/op_datetime.c +259 -77
  40. package/csrc/op_derive.c +65 -97
  41. package/csrc/op_diff.c +146 -30
  42. package/csrc/op_ewma.c +149 -30
  43. package/csrc/op_explode.c +124 -26
  44. package/csrc/op_fill_down.c +125 -53
  45. package/csrc/op_fill_null.c +176 -31
  46. package/csrc/op_filter.c +89 -40
  47. package/csrc/op_frequency.c +571 -43
  48. package/csrc/op_grep.c +36 -18
  49. package/csrc/op_group_agg.c +1790 -119
  50. package/csrc/op_hash.c +48 -15
  51. package/csrc/op_head.c +21 -86
  52. package/csrc/op_interpolate.c +268 -62
  53. package/csrc/op_join.c +2700 -182
  54. package/csrc/op_json_extract.c +227 -0
  55. package/csrc/op_json_filter.c +384 -0
  56. package/csrc/op_json_flatten.c +293 -0
  57. package/csrc/op_json_schema.c +503 -0
  58. package/csrc/op_label_encode.c +328 -53
  59. package/csrc/op_lag.c +181 -0
  60. package/csrc/op_lead.c +141 -89
  61. package/csrc/op_normalize.c +363 -79
  62. package/csrc/op_onehot.c +345 -73
  63. package/csrc/op_pivot.c +1546 -162
  64. package/csrc/op_quarantine.c +189 -0
  65. package/csrc/op_registry.c +2062 -166
  66. package/csrc/op_rename.c +41 -50
  67. package/csrc/op_replace.c +270 -118
  68. package/csrc/op_rleid.c +297 -0
  69. package/csrc/op_rowid.c +559 -0
  70. package/csrc/op_sample.c +80 -23
  71. package/csrc/op_schema.c +1341 -0
  72. package/csrc/op_schema_infer.c +252 -0
  73. package/csrc/op_select.c +265 -65
  74. package/csrc/op_set.c +3449 -0
  75. package/csrc/op_skip.c +30 -87
  76. package/csrc/op_sort.c +670 -124
  77. package/csrc/op_source_name.c +120 -0
  78. package/csrc/op_split.c +65 -28
  79. package/csrc/op_split_data.c +41 -9
  80. package/csrc/op_stack.c +178 -222
  81. package/csrc/op_stats.c +206 -110
  82. package/csrc/op_step.c +217 -55
  83. package/csrc/op_tail.c +21 -12
  84. package/csrc/op_tee.c +338 -0
  85. package/csrc/op_top.c +260 -53
  86. package/csrc/op_trim.c +48 -19
  87. package/csrc/op_unique.c +1193 -150
  88. package/csrc/op_unpivot.c +100 -66
  89. package/csrc/op_validate.c +601 -24
  90. package/csrc/op_window.c +492 -51
  91. package/csrc/path_policy.c +85 -0
  92. package/csrc/pipeline.c +872 -99
  93. package/csrc/recipes.c +3 -1
  94. package/csrc/report.c +73 -30
  95. package/csrc/selector.c +1097 -0
  96. package/csrc/size_utils.c +352 -0
  97. package/csrc/spill.c +317 -0
  98. package/csrc/spill.h +21 -0
  99. package/csrc/tranfi.h +169 -1
  100. package/csrc/transform.h +209 -0
  101. package/csrc/transform_api.c +2237 -0
  102. package/csrc/transform_categorical.c +923 -0
  103. package/csrc/transform_internal.h +472 -0
  104. package/csrc/transform_json.c +3812 -0
  105. package/csrc/transform_numeric.c +1966 -0
  106. package/csrc/transform_sha256.c +154 -0
  107. package/csrc/transform_wasm.h +162 -0
  108. package/csrc/transform_wasm_api.c +1373 -0
  109. package/csrc/wasm_api.c +70 -9
  110. package/napi_api.c +219 -11
  111. package/napi_transform.c +1648 -0
  112. package/napi_transform.h +8 -0
  113. package/package.json +27 -11
  114. package/scripts/install-native.js +76 -0
  115. package/scripts/prepack.js +64 -0
  116. package/scripts/sync-csrc.js +23 -0
  117. package/src/cli.js +81 -41
  118. package/src/engines/duckdb.js +45 -12
  119. package/src/index.js +661 -42
  120. package/src/memory_policy.js +411 -0
  121. package/src/native.js +1 -5
  122. package/src/pipeline.js +454 -31
  123. package/src/recipe_json.js +80 -0
  124. package/src/server.js +10 -8
  125. package/src/transform.js +403 -0
  126. package/src/transform_error.js +10 -0
  127. package/src/wasm.js +6 -4
  128. package/wasm/index.js +498 -10
  129. package/wasm/tranfi_core.js +0 -0
  130. package/wasm/transform.js +1156 -0
  131. package/wasm/worker.js +786 -0
  132. package/csrc/plan.c +0 -206
package/csrc/op_bin.c CHANGED
@@ -10,59 +10,173 @@
10
10
  #include <stdlib.h>
11
11
  #include <string.h>
12
12
  #include <stdio.h>
13
+ #include <math.h>
14
+
15
+ typedef enum {
16
+ BIN_MISSING_ERROR,
17
+ BIN_MISSING_NULL,
18
+ BIN_MISSING_IGNORE,
19
+ } bin_missing_policy;
20
+
21
+ typedef enum {
22
+ BIN_TYPE_FAIL,
23
+ BIN_TYPE_NULL,
24
+ } bin_type_policy;
13
25
 
14
26
  typedef struct {
15
27
  char *column;
16
28
  double *boundaries;
17
29
  size_t n_boundaries;
30
+ bin_missing_policy missing;
31
+ bin_type_policy on_type_error;
18
32
  } bin_state;
19
33
 
34
+ static int bin_is_numeric_type(tf_type type) {
35
+ return type == TF_TYPE_INT64 || type == TF_TYPE_FLOAT64;
36
+ }
37
+
38
+ static double bin_get_numeric(const tf_batch *b, size_t r, int ci) {
39
+ if (b->col_types[ci] == TF_TYPE_INT64) return (double)tf_batch_get_int64(b, r, ci);
40
+ return tf_batch_get_float64(b, r, ci);
41
+ }
42
+
43
+ static void bin_set_col_error(const char *column, const char *suffix) {
44
+ char msg[512];
45
+ snprintf(msg, sizeof(msg), "bin: column '%s' %s", column ? column : "", suffix);
46
+ tf_set_last_error(msg);
47
+ }
48
+
49
+ static int bin_parse_missing_policy(const cJSON *args, bin_missing_policy *out) {
50
+ *out = BIN_MISSING_ERROR;
51
+ const cJSON *j = cJSON_GetObjectItemCaseSensitive(args, "missing");
52
+ if (!j) return TF_OK;
53
+ if (!cJSON_IsString(j)) {
54
+ tf_set_last_error("bin: missing must be error, null, or ignore");
55
+ return TF_ERROR;
56
+ }
57
+ if (strcmp(j->valuestring, "error") == 0) *out = BIN_MISSING_ERROR;
58
+ else if (strcmp(j->valuestring, "null") == 0) *out = BIN_MISSING_NULL;
59
+ else if (strcmp(j->valuestring, "ignore") == 0) *out = BIN_MISSING_IGNORE;
60
+ else {
61
+ tf_set_last_error("bin: missing must be error, null, or ignore");
62
+ return TF_ERROR;
63
+ }
64
+ return TF_OK;
65
+ }
66
+
67
+ static int bin_parse_type_policy(const cJSON *args, bin_type_policy *out) {
68
+ *out = BIN_TYPE_FAIL;
69
+ const cJSON *j = cJSON_GetObjectItemCaseSensitive(args, "on_type_error");
70
+ if (!j) return TF_OK;
71
+ if (!cJSON_IsString(j)) {
72
+ tf_set_last_error("bin: on_type_error must be fail or null");
73
+ return TF_ERROR;
74
+ }
75
+ if (strcmp(j->valuestring, "fail") == 0) *out = BIN_TYPE_FAIL;
76
+ else if (strcmp(j->valuestring, "null") == 0) *out = BIN_TYPE_NULL;
77
+ else {
78
+ tf_set_last_error("bin: on_type_error must be fail or null");
79
+ return TF_ERROR;
80
+ }
81
+ return TF_OK;
82
+ }
83
+
84
+ static int bin_passthrough(tf_batch *in, tf_batch **out) {
85
+ tf_batch *ob = tf_batch_create(in->n_cols, in->n_rows);
86
+ if (!ob) return TF_ERROR;
87
+ if (tf_batch_clone_schema(ob, in) != TF_OK) {
88
+ tf_batch_free(ob);
89
+ return TF_ERROR;
90
+ }
91
+ for (size_t r = 0; r < in->n_rows; r++) {
92
+ if (tf_batch_copy_row(ob, r, in, r) != TF_OK) {
93
+ tf_batch_free(ob);
94
+ return TF_ERROR;
95
+ }
96
+ if (tf_batch_expose_row(ob, r) != TF_OK) {
97
+ tf_batch_free(ob);
98
+ return TF_ERROR;
99
+ }
100
+ }
101
+ *out = ob;
102
+ return TF_OK;
103
+ }
104
+
20
105
  static int bin_process(tf_step *self, tf_batch *in, tf_batch **out,
21
106
  tf_side_channels *side) {
22
107
  (void)side;
23
108
  bin_state *st = self->state;
24
109
  *out = NULL;
25
110
 
26
- char bin_col_name[256];
27
- snprintf(bin_col_name, sizeof(bin_col_name), "%s_bin", st->column);
111
+ int ci = tf_batch_col_index(in, st->column);
112
+ int force_null = 0;
113
+ if (ci < 0) {
114
+ if (st->missing == BIN_MISSING_ERROR) {
115
+ bin_set_col_error(st->column, "not found");
116
+ return TF_ERROR;
117
+ }
118
+ if (st->missing == BIN_MISSING_IGNORE) return bin_passthrough(in, out);
119
+ force_null = 1;
120
+ } else if (!bin_is_numeric_type(in->col_types[ci])) {
121
+ if (st->on_type_error == BIN_TYPE_FAIL) {
122
+ bin_set_col_error(st->column, "must be numeric");
123
+ return TF_ERROR;
124
+ }
125
+ force_null = 1;
126
+ }
28
127
 
128
+ char *bin_col_name = tf_string_append_suffix_checked(st->column, "_bin");
129
+ if (!bin_col_name) return TF_ERROR;
130
+ const char *extra_names[1] = {bin_col_name};
131
+ tf_type extra_types[1] = {TF_TYPE_STRING};
29
132
  tf_batch *ob = tf_batch_create(in->n_cols + 1, in->n_rows);
30
- if (!ob) return TF_ERROR;
31
- for (size_t c = 0; c < in->n_cols; c++)
32
- tf_batch_set_schema(ob, c, in->col_names[c], in->col_types[c]);
33
- tf_batch_set_schema(ob, in->n_cols, bin_col_name, TF_TYPE_STRING);
34
-
35
- int ci = tf_batch_col_index(in, st->column);
133
+ if (!ob) {
134
+ free(bin_col_name);
135
+ return TF_ERROR;
136
+ }
137
+ if (tf_batch_clone_with_extra_cols(ob, in, extra_names, extra_types, 1) != TF_OK) {
138
+ free(bin_col_name);
139
+ tf_batch_free(ob);
140
+ return TF_ERROR;
141
+ }
142
+ free(bin_col_name);
36
143
 
37
144
  for (size_t r = 0; r < in->n_rows; r++) {
38
- tf_batch_copy_row(ob, r, in, r);
145
+ if (tf_batch_copy_row(ob, r, in, r) != TF_OK) {
146
+ tf_batch_free(ob);
147
+ return TF_ERROR;
148
+ }
39
149
 
40
- if (ci < 0 || tf_batch_is_null(in, r, ci)) {
41
- tf_batch_set_null(ob, r, in->n_cols);
150
+ if (force_null || tf_batch_is_null(in, r, ci)) {
151
+ if (tf_batch_set_null(ob, r, in->n_cols) != TF_OK) { tf_batch_free(ob); return TF_ERROR; }
42
152
  } else {
43
- double val = 0;
44
- if (in->col_types[ci] == TF_TYPE_INT64) val = (double)tf_batch_get_int64(in, r, ci);
45
- else if (in->col_types[ci] == TF_TYPE_FLOAT64) val = tf_batch_get_float64(in, r, ci);
153
+ double val = bin_get_numeric(in, r, ci);
46
154
 
47
155
  char label[64];
48
156
  if (st->n_boundaries == 0) {
49
- snprintf(label, sizeof(label), "%g", val);
157
+ snprintf(label, sizeof(label), TF_FLOAT64_ROUNDTRIP_FORMAT, val);
50
158
  } else if (val < st->boundaries[0]) {
51
- snprintf(label, sizeof(label), "<%g", st->boundaries[0]);
159
+ snprintf(label, sizeof(label), "<" TF_FLOAT64_ROUNDTRIP_FORMAT, st->boundaries[0]);
52
160
  } else {
53
161
  size_t b;
54
162
  for (b = 1; b < st->n_boundaries; b++) {
55
163
  if (val < st->boundaries[b]) break;
56
164
  }
57
165
  if (b < st->n_boundaries) {
58
- snprintf(label, sizeof(label), "%g-%g", st->boundaries[b - 1], st->boundaries[b]);
166
+ snprintf(label, sizeof(label), TF_FLOAT64_ROUNDTRIP_FORMAT "-" TF_FLOAT64_ROUNDTRIP_FORMAT, st->boundaries[b - 1], st->boundaries[b]);
59
167
  } else {
60
- snprintf(label, sizeof(label), "%g+", st->boundaries[st->n_boundaries - 1]);
168
+ snprintf(label, sizeof(label), TF_FLOAT64_ROUNDTRIP_FORMAT "+", st->boundaries[st->n_boundaries - 1]);
61
169
  }
62
170
  }
63
- tf_batch_set_string(ob, r, in->n_cols, label);
171
+ if (tf_batch_set_string(ob, r, in->n_cols, label) != TF_OK) {
172
+ tf_batch_free(ob);
173
+ return TF_ERROR;
174
+ }
175
+ }
176
+ if (tf_batch_expose_row(ob, r) != TF_OK) {
177
+ tf_batch_free(ob);
178
+ return TF_ERROR;
64
179
  }
65
- ob->n_rows = r + 1;
66
180
  }
67
181
 
68
182
  *out = ob;
@@ -82,24 +196,49 @@ static void bin_destroy(tf_step *self) {
82
196
  tf_step *tf_bin_create(const cJSON *args) {
83
197
  if (!args) return NULL;
84
198
  cJSON *col_j = cJSON_GetObjectItemCaseSensitive(args, "column");
85
- if (!cJSON_IsString(col_j)) return NULL;
199
+ if (!cJSON_IsString(col_j) || !col_j->valuestring[0]) {
200
+ tf_set_last_error("bin: column is required");
201
+ return NULL;
202
+ }
86
203
 
87
- bin_state *st = calloc(1, sizeof(bin_state));
204
+ bin_state *st = tf_callocarray_checked(1, sizeof(bin_state));
88
205
  if (!st) return NULL;
89
- st->column = strdup(col_j->valuestring);
206
+ st->column = tf_strdup_checked(col_j->valuestring);
207
+ if (!st->column) { free(st); return NULL; }
208
+ if (bin_parse_missing_policy(args, &st->missing) != TF_OK ||
209
+ bin_parse_type_policy(args, &st->on_type_error) != TF_OK) {
210
+ free(st->column);
211
+ free(st);
212
+ return NULL;
213
+ }
90
214
 
91
215
  cJSON *bounds = cJSON_GetObjectItemCaseSensitive(args, "boundaries");
92
- if (bounds && cJSON_IsArray(bounds)) {
93
- int n = cJSON_GetArraySize(bounds);
94
- st->boundaries = malloc(n * sizeof(double));
95
- st->n_boundaries = n;
216
+ if (!bounds || !cJSON_IsArray(bounds)) {
217
+ tf_set_last_error("bin: boundaries must be an array");
218
+ free(st->column);
219
+ free(st);
220
+ return NULL;
221
+ }
222
+ int n = cJSON_GetArraySize(bounds);
223
+ if (n > 0) {
224
+ st->boundaries = tf_mallocarray_checked((size_t)n, sizeof(double));
225
+ if (!st->boundaries) { free(st->column); free(st); return NULL; }
226
+ st->n_boundaries = (size_t)n;
96
227
  for (int i = 0; i < n; i++) {
97
228
  cJSON *item = cJSON_GetArrayItem(bounds, i);
98
- st->boundaries[i] = cJSON_IsNumber(item) ? item->valuedouble : 0;
229
+ if (!cJSON_IsNumber(item) || !isfinite(item->valuedouble)) {
230
+ tf_set_last_error("bin: boundaries must be finite, strictly increasing numbers");
231
+ free(st->column); free(st->boundaries); free(st); return NULL;
232
+ }
233
+ if (i > 0 && item->valuedouble <= st->boundaries[i - 1]) {
234
+ tf_set_last_error("bin: boundaries must be finite, strictly increasing numbers");
235
+ free(st->column); free(st->boundaries); free(st); return NULL;
236
+ }
237
+ st->boundaries[i] = item->valuedouble;
99
238
  }
100
239
  }
101
240
 
102
- tf_step *step = malloc(sizeof(tf_step));
241
+ tf_step *step = tf_callocarray_checked(1, sizeof(tf_step));
103
242
  if (!step) { free(st->column); free(st->boundaries); free(st); return NULL; }
104
243
  step->process = bin_process;
105
244
  step->flush = bin_flush;