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.
Files changed (132) hide show
  1. package/LICENSE +177 -0
  2. package/NOTICE +8 -0
  3. package/README.md +272 -40
  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 +348 -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 +8 -11
  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_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 = calloc(in->n_cols, sizeof(int));
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 = n_id + 2;
44
- size_t max_rows = in->n_rows * n_value;
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
- tf_batch_set_schema(ob, oc++, in->col_names[c], in->col_types[c]);
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
- tf_batch_set_schema(ob, oc + 1, "value", TF_TYPE_STRING);
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
- tf_batch_ensure_capacity(ob, out_row + 1);
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
- size_t oidx = 0;
65
- for (size_t ic = 0; ic < in->n_cols; ic++) {
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
- /* Set value (convert to string) */
87
- if (tf_batch_is_null(in, r, c)) {
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->n_rows = ++out_row;
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 unpivot_destroy(tf_step *self) {
127
- unpivot_state *st = self->state;
131
+ static void unpivot_state_free(unpivot_state *st) {
128
132
  if (st) {
129
- for (size_t i = 0; i < st->n_cols; i++) free(st->cols[i]);
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)) return NULL;
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) return NULL;
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->cols = calloc(n, sizeof(char *));
146
- st->n_cols = n;
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)) st->cols[i] = strdup(item->valuestring);
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 = malloc(sizeof(tf_step));
153
- if (!step) { unpivot_destroy(&(tf_step){.state = st}); return NULL; }
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;