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/internal.h CHANGED
@@ -12,6 +12,71 @@
12
12
  #include <stdint.h>
13
13
  #include <stdbool.h>
14
14
 
15
+ typedef struct cJSON cJSON;
16
+
17
+ #define TF_MAX_BATCH_ROWS ((size_t)64u * 1024u)
18
+ #define TF_MAX_OUTPUT_ROWS_PER_BATCH ((size_t)1024u * 1024u)
19
+ #define TF_MAX_EXPANDING_ROWS_PER_INPUT TF_MAX_OUTPUT_ROWS_PER_BATCH
20
+ #define TF_MAX_EXPLODE_TOKENS_PER_ROW TF_MAX_OUTPUT_ROWS_PER_BATCH
21
+ #define TF_MAX_COLUMNS ((size_t)64u * 1024u)
22
+ #define TF_MAX_RECORD_BYTES ((size_t)1024u * 1024u * 1024u)
23
+ #define TF_MAX_CELL_BYTES ((size_t)64u * 1024u * 1024u)
24
+ #define TF_MAX_COLUMN_NAME_BYTES ((size_t)4096u)
25
+ #define TF_MAX_SCHEMA_BYTES ((size_t)16u * 1024u * 1024u)
26
+ #define TF_MAX_ERROR_BYTES ((size_t)64u * 1024u * 1024u)
27
+ #define TF_MAX_REGEX_PATTERN_BYTES ((size_t)4096u)
28
+ #define TF_MAX_REGEX_CELL_BYTES ((size_t)64u * 1024u)
29
+ #if SIZE_MAX > 9007199254740991ULL
30
+ #define TF_MAX_SAFE_SIZE_ARG ((size_t)9007199254740991ULL)
31
+ #else
32
+ #define TF_MAX_SAFE_SIZE_ARG ((size_t)SIZE_MAX)
33
+ #endif
34
+ #define TF_MAX_COUNT_ARG TF_MAX_SAFE_SIZE_ARG
35
+ #define TF_MAX_STATE_BYTES TF_MAX_SAFE_SIZE_ARG
36
+ #define TF_MAX_SPILL_MEMORY_BYTES TF_MAX_SAFE_SIZE_ARG
37
+ #define TF_MAX_SPILL_RUN_ROWS TF_MAX_COUNT_ARG
38
+ #define TF_MAX_SPILL_OUTPUT_ROWS TF_MAX_BATCH_ROWS
39
+ #define TF_MAX_AUDIT_RECORDS ((size_t)1000u * 1000u)
40
+ #define TF_MAX_TABLE_WIDTH ((size_t)4096u)
41
+ #define TF_MAX_TABLE_ROWS TF_MAX_COUNT_ARG
42
+ #define TF_MAX_WINDOW_SIZE TF_MAX_OUTPUT_ROWS_PER_BATCH
43
+ #define TF_FLOAT64_ROUNDTRIP_FORMAT "%.17g"
44
+
45
+ #if defined(__GNUC__) || defined(__clang__)
46
+ #define TF_WARN_UNUSED __attribute__((warn_unused_result))
47
+ #else
48
+ #define TF_WARN_UNUSED
49
+ #endif
50
+
51
+ TF_WARN_UNUSED int tf_format_float64(char *buf, size_t buf_size, double value);
52
+ TF_WARN_UNUSED int tf_size_add(size_t a, size_t b, size_t *out);
53
+ TF_WARN_UNUSED int tf_size_mul(size_t a, size_t b, size_t *out);
54
+ TF_WARN_UNUSED int tf_size_align(size_t x, size_t align, size_t *out);
55
+ TF_WARN_UNUSED int tf_size_grow_pow2(size_t current, size_t min_value, size_t min_capacity, size_t *out);
56
+ TF_WARN_UNUSED void *tf_mallocarray_checked(size_t count, size_t elem_size);
57
+ TF_WARN_UNUSED void *tf_callocarray_checked(size_t count, size_t elem_size);
58
+ TF_WARN_UNUSED void *tf_reallocarray_checked(void *ptr, size_t count, size_t elem_size);
59
+ TF_WARN_UNUSED char *tf_strdup_checked(const char *s);
60
+ TF_WARN_UNUSED char *tf_string_append_suffix_checked(const char *prefix, const char *suffix);
61
+ TF_WARN_UNUSED int tf_check_byte_limit(size_t n, size_t max_value,
62
+ const char *context, const char *name);
63
+ TF_WARN_UNUSED int tf_string_length_bounded(const char *s, size_t max_value,
64
+ size_t *out, const char *context,
65
+ const char *name);
66
+ TF_WARN_UNUSED int tf_json_get_size_arg(const cJSON *args, const char *name,
67
+ size_t min_value, size_t max_value,
68
+ size_t *out, const char *context);
69
+ TF_WARN_UNUSED int tf_json_size_value(const cJSON *item, const char *name,
70
+ size_t min_value, size_t max_value,
71
+ size_t *out, const char *context);
72
+ TF_WARN_UNUSED int tf_json_get_size_arg_any(const cJSON *args, const char *name,
73
+ const char *alt_name,
74
+ size_t min_value, size_t max_value,
75
+ size_t *out, const char *context);
76
+
77
+ typedef int (*tf_index_compare_fn)(const void *ctx, size_t a, size_t b);
78
+ void tf_sort_indices(size_t *indices, size_t n, tf_index_compare_fn compare, const void *ctx);
79
+
15
80
  /* ---- Arena allocator ---- */
16
81
 
17
82
  typedef struct tf_arena_block {
@@ -42,13 +107,21 @@ typedef struct tf_buffer {
42
107
  size_t read_pos; /* consumer read position */
43
108
  } tf_buffer;
44
109
 
45
- void tf_buffer_init(tf_buffer *b);
46
- int tf_buffer_write(tf_buffer *b, const uint8_t *data, size_t len);
110
+ void tf_buffer_init(tf_buffer *b);
111
+ TF_WARN_UNUSED int tf_buffer_write(tf_buffer *b, const uint8_t *data, size_t len);
47
112
  size_t tf_buffer_read(tf_buffer *b, uint8_t *out, size_t len);
48
113
  size_t tf_buffer_readable(const tf_buffer *b);
49
- void tf_buffer_compact(tf_buffer *b);
50
- int tf_buffer_write_str(tf_buffer *b, const char *s);
51
- void tf_buffer_free(tf_buffer *b);
114
+ void tf_buffer_compact(tf_buffer *b);
115
+ TF_WARN_UNUSED int tf_buffer_write_str(tf_buffer *b, const char *s);
116
+ TF_WARN_UNUSED int tf_buffer_write_line(tf_buffer *b, const char *s);
117
+ TF_WARN_UNUSED int tf_buffer_write_json_line(tf_buffer *b, const cJSON *obj);
118
+ void tf_buffer_free(tf_buffer *b);
119
+ TF_WARN_UNUSED int tf_json_add_string(cJSON *obj, const char *name, const char *value);
120
+ TF_WARN_UNUSED int tf_json_add_number(cJSON *obj, const char *name, double value);
121
+ TF_WARN_UNUSED int tf_json_add_bool(cJSON *obj, const char *name, int value);
122
+ TF_WARN_UNUSED int tf_json_add_null(cJSON *obj, const char *name);
123
+ TF_WARN_UNUSED int tf_json_add_item(cJSON *obj, const char *name, cJSON *item);
124
+ TF_WARN_UNUSED int tf_json_add_array_item(cJSON *arr, cJSON *item);
52
125
 
53
126
  /* ---- Columnar batch ---- */
54
127
  /* (tf_type is defined in ir.h, included above) */
@@ -62,20 +135,47 @@ typedef struct tf_batch {
62
135
  void **columns; /* array of typed column arrays */
63
136
  uint8_t **nulls; /* null bitmap per column (1 byte per row for simplicity) */
64
137
  tf_arena *arena; /* owns all memory for this batch */
138
+ size_t schema_name_bytes;
65
139
  } tf_batch;
66
140
 
141
+ typedef union tf_cell_value {
142
+ bool b;
143
+ int64_t i64;
144
+ double f64;
145
+ const char *str;
146
+ int32_t date;
147
+ } tf_cell_value;
148
+
149
+ /* Owned string variant used by spill rows before copying into a batch arena. */
150
+ typedef union tf_owned_cell_value {
151
+ uint8_t b;
152
+ int64_t i64;
153
+ double f64;
154
+ int32_t date;
155
+ char *str;
156
+ } tf_owned_cell_value;
157
+
67
158
  tf_batch *tf_batch_create(size_t n_cols, size_t capacity);
68
- int tf_batch_set_schema(tf_batch *b, size_t col, const char *name, tf_type type);
69
- int tf_batch_ensure_capacity(tf_batch *b, size_t min_rows);
159
+ TF_WARN_UNUSED int tf_batch_set_schema(tf_batch *b, size_t col, const char *name, tf_type type);
160
+ TF_WARN_UNUSED int tf_batch_ensure_capacity(tf_batch *b, size_t min_rows);
161
+ TF_WARN_UNUSED int tf_batch_expose_row(tf_batch *b, size_t row);
70
162
 
71
163
  /* Set a value in a specific cell. String values are copied into the arena. */
72
- void tf_batch_set_null(tf_batch *b, size_t row, size_t col);
73
- void tf_batch_set_bool(tf_batch *b, size_t row, size_t col, bool val);
74
- void tf_batch_set_int64(tf_batch *b, size_t row, size_t col, int64_t val);
75
- void tf_batch_set_float64(tf_batch *b, size_t row, size_t col, double val);
76
- void tf_batch_set_string(tf_batch *b, size_t row, size_t col, const char *val);
77
- void tf_batch_set_date(tf_batch *b, size_t row, size_t col, int32_t val);
78
- void tf_batch_set_timestamp(tf_batch *b, size_t row, size_t col, int64_t val);
164
+ TF_WARN_UNUSED int tf_batch_set_null(tf_batch *b, size_t row, size_t col);
165
+ TF_WARN_UNUSED int tf_batch_set_bool(tf_batch *b, size_t row, size_t col, bool val);
166
+ TF_WARN_UNUSED int tf_batch_set_int64(tf_batch *b, size_t row, size_t col, int64_t val);
167
+ TF_WARN_UNUSED int tf_batch_set_float64(tf_batch *b, size_t row, size_t col, double val);
168
+ TF_WARN_UNUSED int tf_batch_set_string(tf_batch *b, size_t row, size_t col, const char *val);
169
+ TF_WARN_UNUSED int tf_batch_set_string_len(tf_batch *b, size_t row, size_t col,
170
+ const char *val, size_t len);
171
+ TF_WARN_UNUSED int tf_batch_set_date(tf_batch *b, size_t row, size_t col, int32_t val);
172
+ TF_WARN_UNUSED int tf_batch_set_timestamp(tf_batch *b, size_t row, size_t col, int64_t val);
173
+ TF_WARN_UNUSED int tf_batch_set_cell_value(tf_batch *b, size_t row, size_t col,
174
+ tf_type type, int is_null,
175
+ const tf_cell_value *value);
176
+ TF_WARN_UNUSED int tf_batch_set_owned_cell_value(tf_batch *b, size_t row, size_t col,
177
+ tf_type type, int is_null,
178
+ const tf_owned_cell_value *value);
79
179
 
80
180
  /* Get values from a cell. */
81
181
  bool tf_batch_is_null(const tf_batch *b, size_t row, size_t col);
@@ -89,11 +189,113 @@ int64_t tf_batch_get_timestamp(const tf_batch *b, size_t row, size_t col);
89
189
  /* Find column index by name. Returns -1 if not found. */
90
190
  int tf_batch_col_index(const tf_batch *b, const char *name);
91
191
 
92
- /* Copy a single row from src to dst batch. */
93
- int tf_batch_copy_row(tf_batch *dst, size_t dst_row,
94
- const tf_batch *src, size_t src_row);
192
+ /* Schema and row-copy helpers. */
193
+ typedef enum tf_cell_string_format {
194
+ TF_CELL_STRING_ROUNDTRIP, /* round-trip floats, ISO dates/timestamps */
195
+ TF_CELL_STRING_HUMAN, /* short floats, ISO dates/timestamps */
196
+ TF_CELL_STRING_NUMERIC_TIME /* round-trip floats, numeric dates/timestamps */
197
+ } tf_cell_string_format;
198
+
199
+ TF_WARN_UNUSED int tf_batch_clone_schema(tf_batch *dst, const tf_batch *src);
200
+ TF_WARN_UNUSED int tf_batch_clone_with_extra_cols(tf_batch *dst, const tf_batch *src,
201
+ const char **names, const tf_type *types,
202
+ size_t n_extra);
203
+ TF_WARN_UNUSED int tf_batch_copy_cell(tf_batch *dst, size_t dst_row, size_t dst_col,
204
+ const tf_batch *src, size_t src_row, size_t src_col);
205
+ TF_WARN_UNUSED int tf_batch_copy_cell_index(tf_batch *dst, size_t dst_row, size_t dst_col,
206
+ const tf_batch *src, size_t src_row, int src_col);
207
+ TF_WARN_UNUSED int tf_batch_format_cell_as_string(const tf_batch *src, size_t src_row, size_t src_col,
208
+ tf_cell_string_format format,
209
+ char *buf, size_t buf_size,
210
+ const char **out);
211
+ TF_WARN_UNUSED int tf_batch_copy_cell_as_string(tf_batch *dst, size_t dst_row, size_t dst_col,
212
+ const tf_batch *src, size_t src_row, size_t src_col);
213
+ TF_WARN_UNUSED int tf_batch_copy_selected_row(tf_batch *dst, size_t dst_row,
214
+ const tf_batch *src, size_t src_row,
215
+ const size_t *cols, size_t n_cols);
216
+ TF_WARN_UNUSED int tf_batch_copy_selected_columns(tf_batch *dst,
217
+ const tf_batch *src,
218
+ const size_t *cols,
219
+ size_t n_cols);
220
+ TF_WARN_UNUSED int tf_batch_copy_row(tf_batch *dst, size_t dst_row,
221
+ const tf_batch *src, size_t src_row);
222
+ TF_WARN_UNUSED int tf_batch_clone_with_selected_extra_cols(tf_batch *dst,
223
+ const tf_batch *src,
224
+ const tf_batch *extra,
225
+ const int *extra_cols,
226
+ size_t n_extra);
227
+ TF_WARN_UNUSED int tf_batch_append_row(tf_batch *dst, const tf_batch *src,
228
+ size_t src_row);
229
+ TF_WARN_UNUSED int tf_batch_append_row_with_selected_extra(tf_batch *dst,
230
+ const tf_batch *left,
231
+ size_t left_row,
232
+ const tf_batch *extra,
233
+ size_t extra_row,
234
+ const int *extra_cols,
235
+ size_t n_extra);
236
+ TF_WARN_UNUSED int tf_batch_append_row_with_null_extra(tf_batch *dst,
237
+ const tf_batch *left,
238
+ size_t left_row,
239
+ size_t n_extra);
95
240
 
96
241
  void tf_batch_free(tf_batch *b);
242
+ void tf_batch_array_free_items(tf_batch **batches, size_t n_batches);
243
+ void tf_batch_array_free(tf_batch **batches, size_t n_batches);
244
+
245
+ /* ---- Audit / side-channel row serialization helpers ---- */
246
+
247
+ typedef struct tf_audit_options {
248
+ int include_row;
249
+ char **columns;
250
+ size_t n_columns;
251
+ char **redact_columns;
252
+ size_t n_redact_columns;
253
+ char **hash_columns;
254
+ size_t n_hash_columns;
255
+ size_t max_bytes;
256
+ size_t max_cell_bytes;
257
+ } tf_audit_options;
258
+
259
+ void tf_audit_options_init(tf_audit_options *opts, int default_include_row);
260
+ int tf_audit_options_parse(tf_audit_options *opts, const cJSON *args, const char *context);
261
+ void tf_audit_options_free(tf_audit_options *opts);
262
+ int tf_audit_column_is_redacted(const tf_audit_options *opts, const char *column);
263
+ int tf_audit_column_is_hashed(const tf_audit_options *opts, const char *column);
264
+ int tf_audit_hash_string(const char *value, char *out, size_t out_size);
265
+ const char *tf_audit_format_string_for_column(const tf_audit_options *opts, const char *column,
266
+ const char *value, char *buf, size_t buf_size);
267
+ TF_WARN_UNUSED char *tf_audit_format_string_dup_for_column(const tf_audit_options *opts,
268
+ const char *column,
269
+ const char *value);
270
+ cJSON *tf_audit_string_to_json_for_column(const tf_audit_options *opts, const char *column,
271
+ const char *value);
272
+ TF_WARN_UNUSED int tf_json_add_audit_string(cJSON *obj, const char *name,
273
+ const tf_audit_options *opts,
274
+ const char *column, const char *value);
275
+ cJSON *tf_audit_cell_to_json(const tf_batch *b, size_t row, size_t col, const tf_audit_options *opts);
276
+ cJSON *tf_audit_row_to_json(const tf_batch *b, size_t row, const tf_audit_options *opts);
277
+
278
+ /* ---- Column selector helpers ---- */
279
+
280
+ int tf_json_path_validate(const char *path);
281
+ const cJSON *tf_json_path_resolve(const cJSON *root, const char *path);
282
+
283
+ int tf_column_selector_has_syntax(const char *raw);
284
+ int tf_column_selectors_have_syntax_json(const cJSON *selectors);
285
+ int tf_column_selectors_resolve(char **selectors, size_t n_selectors,
286
+ char **names, const tf_type *types, size_t n_cols,
287
+ int **out_indices, size_t *out_n,
288
+ char **error);
289
+ int tf_column_selectors_resolve_json(const cJSON *selectors,
290
+ char **names, const tf_type *types, size_t n_cols,
291
+ int **out_indices, size_t *out_n,
292
+ char **error);
293
+
294
+ int tf_estimate_step_state_bytes(const tf_ir_node *node, size_t *out,
295
+ char *reason, size_t reason_size);
296
+ int tf_estimate_key_state_plan_bytes(const tf_ir_plan *ir, size_t *out,
297
+ const tf_ir_node **failed_node,
298
+ char *reason, size_t reason_size);
97
299
 
98
300
  /* ---- Step interface (transforms) ---- */
99
301
 
@@ -105,8 +307,18 @@ typedef struct tf_side_channels {
105
307
  tf_buffer *errors;
106
308
  tf_buffer *stats;
107
309
  tf_buffer *samples;
310
+ const char *source_name;
108
311
  } tf_side_channels;
109
312
 
313
+ TF_WARN_UNUSED int tf_side_write_error(tf_side_channels *side, const char *msg);
314
+
315
+ /* Hidden IR args added by the built-in workspace_root resolver. */
316
+ #define TF_POLICY_VALIDATED_FILE_PATH_ARG "_tf_validated_file_path"
317
+ #define TF_POLICY_VALIDATED_RULES_FILE_PATH_ARG "_tf_validated_rules_file_path"
318
+
319
+ const char *tf_policy_validated_path_arg(const cJSON *args, const char *path_arg);
320
+ FILE *tf_policy_fopen_read(const char *path, const char *validated_path);
321
+
110
322
  typedef struct tf_step {
111
323
  /* Process one input batch, produce zero or one output batch.
112
324
  * *out is set to a new batch (caller frees) or NULL if filtered away. */
@@ -114,11 +326,38 @@ typedef struct tf_step {
114
326
  tf_side_channels *side);
115
327
  /* Flush any buffered state. */
116
328
  int (*flush)(struct tf_step *self, tf_batch **out, tf_side_channels *side);
329
+ /* Optional continuation for flush-latent steps that emit multiple batches. */
330
+ int (*flush_next)(struct tf_step *self, tf_batch **out, tf_side_channels *side);
331
+ /* Optional JSON stats hook. Appends comma-prefixed fields to step_stats. */
332
+ int (*append_stats)(struct tf_step *self, tf_buffer *out);
117
333
  /* Free all resources. */
118
334
  void (*destroy)(struct tf_step *self);
119
335
  void *state;
120
336
  } tf_step;
121
337
 
338
+ #define TF_STEP_WARN_BLOCKING (1u << 0)
339
+ #define TF_STEP_WARN_FLUSH_LATENT (1u << 1)
340
+ #define TF_STEP_WARN_UNBOUNDED_STATE (1u << 2)
341
+ #define TF_STEP_WARN_DATA_DEPENDENT_SCHEMA (1u << 3)
342
+
343
+ typedef struct tf_step_run_stats {
344
+ char *op;
345
+ char *state_estimate;
346
+ char *state_bytes_reason;
347
+ char *execution_target;
348
+ size_t node_index;
349
+ size_t state_bytes_estimate;
350
+ int has_state_bytes_estimate;
351
+ uint32_t warnings;
352
+ tf_memory_class memory_class;
353
+ tf_emit_class emit_class;
354
+ tf_schema_class schema_class;
355
+ size_t batches_in;
356
+ size_t batches_out;
357
+ size_t rows_in;
358
+ size_t rows_out;
359
+ } tf_step_run_stats;
360
+
122
361
  /* ---- Decoder interface (bytes → batches) ---- */
123
362
 
124
363
  typedef struct tf_decoder {
@@ -128,9 +367,10 @@ typedef struct tf_decoder {
128
367
  * Caller frees the array and each batch.
129
368
  */
130
369
  int (*decode)(struct tf_decoder *self, const uint8_t *data, size_t len,
131
- tf_batch ***out, size_t *n_out);
370
+ tf_batch ***out, size_t *n_out, tf_side_channels *side);
132
371
  /* Flush remaining data (e.g. last partial line). */
133
- int (*flush)(struct tf_decoder *self, tf_batch ***out, size_t *n_out);
372
+ int (*flush)(struct tf_decoder *self, tf_batch ***out, size_t *n_out,
373
+ tf_side_channels *side);
134
374
  void (*destroy)(struct tf_decoder *self);
135
375
  void *state;
136
376
  } tf_decoder;
@@ -154,21 +394,41 @@ struct tf_pipeline {
154
394
  tf_decoder *decoder;
155
395
  tf_step **steps;
156
396
  size_t n_steps;
397
+ tf_step_run_stats *step_stats;
398
+ size_t n_step_stats;
157
399
  tf_encoder *encoder;
158
400
  tf_buffer output[TF_NUM_CHANNELS];
401
+ tf_pipeline_sink_fn sinks[TF_NUM_CHANNELS];
402
+ void *sink_users[TF_NUM_CHANNELS];
403
+ tf_pipeline_batch_sink_fn batch_sink;
404
+ void *batch_sink_user;
405
+ int encode_output;
406
+ tf_pipeline_progress_fn progress_cb;
407
+ void *progress_user;
408
+ size_t progress_interval_rows;
409
+ size_t progress_last_rows_in;
410
+ size_t progress_last_rows_out;
159
411
  tf_side_channels side;
160
412
  size_t rows_in;
161
413
  size_t rows_out;
414
+ size_t batches_in;
415
+ size_t batches_out;
162
416
  size_t bytes_in;
163
417
  size_t bytes_out;
418
+ char *source_name;
164
419
  char *error;
165
420
  int finished;
421
+ int finish_started;
422
+ int finish_phase;
423
+ tf_batch **finish_batches;
424
+ size_t finish_n_batches;
425
+ size_t finish_batch_index;
426
+ size_t finish_step_index;
427
+ int finish_step_in_next;
166
428
  };
167
429
 
168
430
  /* ---- Codec constructors (used by plan parser) ---- */
169
431
 
170
- typedef struct cJSON cJSON;
171
-
172
432
  tf_decoder *tf_csv_decoder_create(const cJSON *args);
173
433
  tf_encoder *tf_csv_encoder_create(const cJSON *args);
174
434
  tf_decoder *tf_jsonl_decoder_create(const cJSON *args);
@@ -180,14 +440,23 @@ tf_encoder *tf_text_encoder_create(const cJSON *args);
180
440
 
181
441
  tf_step *tf_filter_create(const cJSON *args);
182
442
  tf_step *tf_select_create(const cJSON *args);
443
+ tf_step *tf_relocate_create(const cJSON *args);
183
444
  tf_step *tf_rename_create(const cJSON *args);
184
445
  tf_step *tf_head_create(const cJSON *args);
185
446
  tf_step *tf_skip_create(const cJSON *args);
186
447
  tf_step *tf_derive_create(const cJSON *args);
448
+ tf_step *tf_source_name_create(const cJSON *args);
449
+ tf_step *tf_across_create(const cJSON *args);
187
450
  tf_step *tf_stats_create(const cJSON *args);
451
+ tf_step *tf_scan_create(const cJSON *args);
188
452
  tf_step *tf_unique_create(const cJSON *args);
189
453
  tf_step *tf_sort_create(const cJSON *args);
190
454
  tf_step *tf_validate_create(const cJSON *args);
455
+ tf_step *tf_assert_create(const cJSON *args);
456
+ tf_step *tf_quarantine_create(const cJSON *args);
457
+ tf_step *tf_schema_create(const cJSON *args);
458
+ tf_step *tf_schema_infer_create(const cJSON *args);
459
+ tf_step *tf_tee_create(const cJSON *args);
191
460
  tf_step *tf_trim_create(const cJSON *args);
192
461
  tf_step *tf_fill_null_create(const cJSON *args);
193
462
  tf_step *tf_cast_create(const cJSON *args);
@@ -198,20 +467,46 @@ tf_step *tf_bin_create(const cJSON *args);
198
467
  tf_step *tf_fill_down_create(const cJSON *args);
199
468
  tf_step *tf_step_create(const cJSON *args);
200
469
  tf_step *tf_window_create(const cJSON *args);
470
+ tf_step *tf_rolling_sum_create(const cJSON *args);
471
+ tf_step *tf_rolling_mean_create(const cJSON *args);
472
+ tf_step *tf_rolling_min_create(const cJSON *args);
473
+ tf_step *tf_rolling_max_create(const cJSON *args);
474
+ tf_step *tf_rolling_any_create(const cJSON *args);
475
+ tf_step *tf_rolling_all_create(const cJSON *args);
201
476
  tf_step *tf_explode_create(const cJSON *args);
202
477
  tf_step *tf_split_create(const cJSON *args);
203
478
  tf_step *tf_unpivot_create(const cJSON *args);
204
479
  tf_step *tf_tail_create(const cJSON *args);
205
480
  tf_step *tf_top_create(const cJSON *args);
481
+ tf_step *tf_top_k_create(const cJSON *args);
482
+ tf_step *tf_bottom_k_create(const cJSON *args);
483
+ tf_step *tf_slice_min_create(const cJSON *args);
484
+ tf_step *tf_slice_max_create(const cJSON *args);
206
485
  tf_step *tf_sample_create(const cJSON *args);
207
486
  tf_step *tf_group_agg_create(const cJSON *args);
208
487
  tf_step *tf_frequency_create(const cJSON *args);
209
488
  tf_step *tf_datetime_create(const cJSON *args);
210
489
  tf_step *tf_grep_create(const cJSON *args);
490
+ tf_step *tf_json_extract_create(const cJSON *args);
491
+ tf_step *tf_json_filter_create(const cJSON *args);
492
+ tf_step *tf_json_schema_create(const cJSON *args);
493
+ tf_step *tf_json_flatten_create(const cJSON *args);
211
494
  tf_step *tf_pivot_create(const cJSON *args);
212
495
  tf_step *tf_join_create(const cJSON *args);
496
+ tf_step *tf_semi_join_create(const cJSON *args);
497
+ tf_step *tf_anti_join_create(const cJSON *args);
498
+ tf_step *tf_intersect_create(const cJSON *args);
499
+ tf_step *tf_setdiff_create(const cJSON *args);
500
+ tf_step *tf_intersect_all_create(const cJSON *args);
501
+ tf_step *tf_setdiff_all_create(const cJSON *args);
502
+ tf_step *tf_union_create(const cJSON *args);
503
+ tf_step *tf_union_all_create(const cJSON *args);
213
504
  tf_step *tf_stack_create(const cJSON *args);
214
505
  tf_step *tf_lead_create(const cJSON *args);
506
+ tf_step *tf_lag_create(const cJSON *args);
507
+ tf_step *tf_shift_create(const cJSON *args);
508
+ tf_step *tf_rowid_create(const cJSON *args);
509
+ tf_step *tf_rleid_create(const cJSON *args);
215
510
  tf_step *tf_date_trunc_create(const cJSON *args);
216
511
  tf_step *tf_onehot_create(const cJSON *args);
217
512
  tf_step *tf_label_encode_create(const cJSON *args);
@@ -237,12 +532,6 @@ int tf_expr_eval_val(const tf_expr *e, const tf_batch *batch, size_t row
237
532
  tf_eval_result *result);
238
533
  void tf_expr_free(tf_expr *e);
239
534
 
240
- /* ---- Plan parser ---- */
241
-
242
- int tf_plan_parse(const char *json, size_t len,
243
- tf_decoder **decoder, tf_step ***steps, size_t *n_steps,
244
- tf_encoder **encoder, char **error);
245
-
246
535
  /* ---- Global error ---- */
247
536
 
248
537
  void tf_set_last_error(const char *msg);
package/csrc/ir.c CHANGED
@@ -2,7 +2,7 @@
2
2
  * ir.c — IR plan construction, cloning, and memory management.
3
3
  */
4
4
 
5
- #include "ir.h"
5
+ #include "internal.h"
6
6
  #include "cJSON.h"
7
7
  #include <stdlib.h>
8
8
  #include <string.h>
@@ -11,8 +11,10 @@
11
11
 
12
12
  void tf_schema_free(tf_schema *s) {
13
13
  if (!s) return;
14
- for (size_t i = 0; i < s->n_cols; i++) {
15
- free(s->col_names[i]);
14
+ if (s->col_names) {
15
+ for (size_t i = 0; i < s->n_cols; i++) {
16
+ free(s->col_names[i]);
17
+ }
16
18
  }
17
19
  free(s->col_names);
18
20
  free(s->col_types);
@@ -22,20 +24,38 @@ void tf_schema_free(tf_schema *s) {
22
24
  s->known = false;
23
25
  }
24
26
 
25
- void tf_schema_copy(tf_schema *dst, const tf_schema *src) {
27
+ int tf_schema_copy_checked(tf_schema *dst, const tf_schema *src) {
28
+ if (!dst) return TF_ERROR;
29
+ dst->known = false;
30
+ dst->n_cols = 0;
31
+ dst->col_names = NULL;
32
+ dst->col_types = NULL;
33
+ if (!src) return TF_OK;
34
+
26
35
  dst->known = src->known;
27
36
  dst->n_cols = src->n_cols;
28
- if (src->n_cols == 0 || !src->known) {
29
- dst->col_names = NULL;
30
- dst->col_types = NULL;
31
- return;
32
- }
37
+ if (src->n_cols == 0 || !src->known) return TF_OK;
38
+
33
39
  dst->col_names = calloc(src->n_cols, sizeof(char *));
34
40
  dst->col_types = calloc(src->n_cols, sizeof(tf_type));
41
+ if (!dst->col_names || !dst->col_types) {
42
+ tf_schema_free(dst);
43
+ return TF_ERROR;
44
+ }
35
45
  for (size_t i = 0; i < src->n_cols; i++) {
36
- dst->col_names[i] = strdup(src->col_names[i]);
46
+ dst->col_names[i] = strdup(src->col_names[i] ? src->col_names[i] : "");
47
+ if (!dst->col_names[i]) {
48
+ tf_schema_free(dst);
49
+ return TF_ERROR;
50
+ }
37
51
  dst->col_types[i] = src->col_types[i];
38
52
  }
53
+ dst->known = true;
54
+ return TF_OK;
55
+ }
56
+
57
+ void tf_schema_copy(tf_schema *dst, const tf_schema *src) {
58
+ (void)tf_schema_copy_checked(dst, src);
39
59
  }
40
60
 
41
61
  /* ---- IR node helpers ---- */
@@ -59,9 +79,12 @@ tf_ir_plan *tf_ir_plan_create(void) {
59
79
  }
60
80
 
61
81
  int tf_ir_plan_add_node(tf_ir_plan *plan, const char *op, cJSON *args) {
82
+ if (!plan || !op) return -1;
62
83
  if (plan->n_nodes >= plan->capacity) {
63
- size_t new_cap = plan->capacity * 2;
64
- tf_ir_node *new_nodes = realloc(plan->nodes, new_cap * sizeof(tf_ir_node));
84
+ size_t new_cap = 0;
85
+ if (tf_size_mul(plan->capacity, 2, &new_cap) != TF_OK) return -1;
86
+ tf_ir_node *new_nodes = tf_reallocarray_checked(plan->nodes, new_cap,
87
+ sizeof(tf_ir_node));
65
88
  if (!new_nodes) return -1;
66
89
  memset(new_nodes + plan->capacity, 0,
67
90
  (new_cap - plan->capacity) * sizeof(tf_ir_node));
@@ -69,10 +92,18 @@ int tf_ir_plan_add_node(tf_ir_plan *plan, const char *op, cJSON *args) {
69
92
  plan->capacity = new_cap;
70
93
  }
71
94
 
95
+ char *op_copy = strdup(op);
96
+ cJSON *args_copy = args ? cJSON_Duplicate(args, 1) : cJSON_CreateObject();
97
+ if (!op_copy || !args_copy) {
98
+ free(op_copy);
99
+ if (args_copy) cJSON_Delete(args_copy);
100
+ return -1;
101
+ }
102
+
72
103
  tf_ir_node *node = &plan->nodes[plan->n_nodes];
73
104
  memset(node, 0, sizeof(tf_ir_node));
74
- node->op = strdup(op);
75
- node->args = args ? cJSON_Duplicate(args, 1) : cJSON_CreateObject();
105
+ node->op = op_copy;
106
+ node->args = args_copy;
76
107
  node->index = plan->n_nodes;
77
108
 
78
109
  plan->n_nodes++;
@@ -93,16 +124,32 @@ tf_ir_plan *tf_ir_plan_clone(const tf_ir_plan *plan) {
93
124
  }
94
125
  /* Copy schemas if they were inferred */
95
126
  tf_ir_node *dst = &clone->nodes[i];
96
- tf_schema_copy(&dst->input_schema, &src->input_schema);
97
- tf_schema_copy(&dst->output_schema, &src->output_schema);
127
+ if (tf_schema_copy_checked(&dst->input_schema, &src->input_schema) != TF_OK ||
128
+ tf_schema_copy_checked(&dst->output_schema, &src->output_schema) != TF_OK) {
129
+ tf_ir_plan_free(clone);
130
+ return NULL;
131
+ }
98
132
  dst->caps = src->caps;
133
+ dst->memory_class = src->memory_class;
134
+ dst->emit_class = src->emit_class;
135
+ dst->schema_class = src->schema_class;
136
+ dst->state_estimate = src->state_estimate;
99
137
  }
100
138
 
101
- tf_schema_copy(&clone->final_schema, &plan->final_schema);
139
+ if (tf_schema_copy_checked(&clone->final_schema, &plan->final_schema) != TF_OK) {
140
+ tf_ir_plan_free(clone);
141
+ return NULL;
142
+ }
102
143
  clone->plan_caps = plan->plan_caps;
103
144
  clone->validated = plan->validated;
104
145
  clone->schema_inferred = plan->schema_inferred;
105
- if (plan->error) clone->error = strdup(plan->error);
146
+ if (plan->error) {
147
+ clone->error = strdup(plan->error);
148
+ if (!clone->error) {
149
+ tf_ir_plan_free(clone);
150
+ return NULL;
151
+ }
152
+ }
106
153
 
107
154
  return clone;
108
155
  }