tranfi 0.0.2 → 0.1.2
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/README.md +395 -0
- package/app/assets/index-6quYZ5Ap.css +5 -0
- package/app/assets/index-pDFMluyz.js +160 -0
- package/app/assets/materialdesignicons-webfont-B7mPwVP_.ttf +0 -0
- package/app/assets/materialdesignicons-webfont-CSr8KVlo.eot +0 -0
- package/app/assets/materialdesignicons-webfont-Dp5v-WZN.woff2 +0 -0
- package/app/assets/materialdesignicons-webfont-PXm3-2wK.woff +0 -0
- package/app/index.html +13 -0
- package/binding.gyp +69 -0
- package/csrc/arena.c +91 -0
- package/csrc/batch.c +229 -0
- package/csrc/buffer.c +78 -0
- package/csrc/cJSON.c +3143 -0
- package/csrc/cJSON.h +300 -0
- package/csrc/codec_csv.c +1058 -0
- package/csrc/codec_jsonl.c +374 -0
- package/csrc/codec_table.c +218 -0
- package/csrc/codec_text.c +229 -0
- package/csrc/compiler.c +102 -0
- package/csrc/date_utils.h +94 -0
- package/csrc/dsl.c +1180 -0
- package/csrc/dsl.h +22 -0
- package/csrc/expr.c +1245 -0
- package/csrc/expr.h +56 -0
- package/csrc/internal.h +250 -0
- package/csrc/ir.c +119 -0
- package/csrc/ir.h +167 -0
- package/csrc/ir_schema.c +60 -0
- package/csrc/ir_serialize.c +104 -0
- package/csrc/ir_sql.c +1211 -0
- package/csrc/ir_validate.c +120 -0
- package/csrc/main.c +392 -0
- package/csrc/op_acf.c +133 -0
- package/csrc/op_anomaly.c +120 -0
- package/csrc/op_bin.c +109 -0
- package/csrc/op_cast.c +195 -0
- package/csrc/op_clip.c +88 -0
- package/csrc/op_date_trunc.c +181 -0
- package/csrc/op_datetime.c +212 -0
- package/csrc/op_derive.c +248 -0
- package/csrc/op_diff.c +134 -0
- package/csrc/op_ewma.c +103 -0
- package/csrc/op_explode.c +108 -0
- package/csrc/op_fill_down.c +163 -0
- package/csrc/op_fill_null.c +123 -0
- package/csrc/op_filter.c +132 -0
- package/csrc/op_frequency.c +193 -0
- package/csrc/op_grep.c +163 -0
- package/csrc/op_group_agg.c +285 -0
- package/csrc/op_hash.c +126 -0
- package/csrc/op_head.c +149 -0
- package/csrc/op_interpolate.c +239 -0
- package/csrc/op_join.c +384 -0
- package/csrc/op_label_encode.c +144 -0
- package/csrc/op_lead.c +190 -0
- package/csrc/op_normalize.c +226 -0
- package/csrc/op_onehot.c +185 -0
- package/csrc/op_pivot.c +370 -0
- package/csrc/op_registry.c +1148 -0
- package/csrc/op_rename.c +138 -0
- package/csrc/op_replace.c +202 -0
- package/csrc/op_sample.c +101 -0
- package/csrc/op_select.c +140 -0
- package/csrc/op_skip.c +152 -0
- package/csrc/op_sort.c +273 -0
- package/csrc/op_split.c +114 -0
- package/csrc/op_split_data.c +87 -0
- package/csrc/op_stack.c +315 -0
- package/csrc/op_stats.c +779 -0
- package/csrc/op_step.c +171 -0
- package/csrc/op_tail.c +96 -0
- package/csrc/op_top.c +150 -0
- package/csrc/op_trim.c +109 -0
- package/csrc/op_unique.c +300 -0
- package/csrc/op_unpivot.c +159 -0
- package/csrc/op_validate.c +71 -0
- package/csrc/op_window.c +150 -0
- package/csrc/pipeline.c +315 -0
- package/csrc/plan.c +206 -0
- package/csrc/recipes.c +102 -0
- package/csrc/recipes.h +27 -0
- package/csrc/report.c +463 -0
- package/csrc/report.h +22 -0
- package/csrc/tranfi.h +123 -0
- package/csrc/wasm_api.c +157 -0
- package/napi_api.c +326 -0
- package/package.json +46 -57
- package/src/cli.js +193 -0
- package/src/engines/duckdb.js +109 -0
- package/src/index.js +306 -0
- package/src/native.js +22 -0
- package/src/pipeline.js +286 -0
- package/src/server.js +277 -0
- package/src/wasm.js +19 -0
- package/wasm/index.js +244 -0
- package/wasm/package.json +1 -0
- package/wasm/tranfi_core.js +0 -0
- package/LICENSE +0 -21
- package/dist/bundle.js +0 -1
- package/index.html +0 -18
- package/src/app.css +0 -169
- package/src/app.js +0 -203
- package/src/app.vue +0 -250
- package/src/bulma-input.vue +0 -110
- package/src/common-inputs.js +0 -28
- package/src/main.js +0 -20
- package/src/transforms.js +0 -166
- package/webpack.config.js +0 -108
package/csrc/op_pivot.c
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* op_pivot.c — Pivot (long to wide). Full-load: buffers all data.
|
|
3
|
+
*
|
|
4
|
+
* Config: {"name_column": "metric", "value_column": "value", "agg": "first"}
|
|
5
|
+
* Supported aggs: first, sum, count, avg, min, max.
|
|
6
|
+
*
|
|
7
|
+
* Pass-through columns = all columns except name_column and value_column.
|
|
8
|
+
* Output: pass-through columns + one column per unique value of name_column.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#include "internal.h"
|
|
12
|
+
#include "date_utils.h"
|
|
13
|
+
#include "cJSON.h"
|
|
14
|
+
#include <stdlib.h>
|
|
15
|
+
#include <string.h>
|
|
16
|
+
#include <stdio.h>
|
|
17
|
+
#include <float.h>
|
|
18
|
+
|
|
19
|
+
typedef enum {
|
|
20
|
+
PIVOT_FIRST, PIVOT_SUM, PIVOT_COUNT, PIVOT_AVG, PIVOT_MIN, PIVOT_MAX,
|
|
21
|
+
} pivot_agg;
|
|
22
|
+
|
|
23
|
+
typedef struct {
|
|
24
|
+
double *sums;
|
|
25
|
+
double *mins;
|
|
26
|
+
double *maxs;
|
|
27
|
+
size_t *counts;
|
|
28
|
+
int *has_first;
|
|
29
|
+
double *firsts;
|
|
30
|
+
} pivot_accum;
|
|
31
|
+
|
|
32
|
+
typedef struct {
|
|
33
|
+
char **keys;
|
|
34
|
+
size_t *pass_rows; /* first row index for each group (for pass-through values) */
|
|
35
|
+
pivot_accum *accums;
|
|
36
|
+
size_t count;
|
|
37
|
+
size_t cap;
|
|
38
|
+
} pivot_map;
|
|
39
|
+
|
|
40
|
+
typedef struct {
|
|
41
|
+
char *name_column;
|
|
42
|
+
char *value_column;
|
|
43
|
+
pivot_agg agg;
|
|
44
|
+
|
|
45
|
+
tf_batch *buf;
|
|
46
|
+
int has_schema;
|
|
47
|
+
|
|
48
|
+
char **unique_names;
|
|
49
|
+
size_t n_names;
|
|
50
|
+
size_t names_cap;
|
|
51
|
+
} pivot_state;
|
|
52
|
+
|
|
53
|
+
static pivot_agg parse_pivot_agg(const char *s) {
|
|
54
|
+
if (!s) return PIVOT_FIRST;
|
|
55
|
+
if (strcmp(s, "first") == 0) return PIVOT_FIRST;
|
|
56
|
+
if (strcmp(s, "sum") == 0) return PIVOT_SUM;
|
|
57
|
+
if (strcmp(s, "count") == 0) return PIVOT_COUNT;
|
|
58
|
+
if (strcmp(s, "avg") == 0) return PIVOT_AVG;
|
|
59
|
+
if (strcmp(s, "min") == 0) return PIVOT_MIN;
|
|
60
|
+
if (strcmp(s, "max") == 0) return PIVOT_MAX;
|
|
61
|
+
return PIVOT_FIRST;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static int find_unique_name(pivot_state *st, const char *name) {
|
|
65
|
+
for (size_t i = 0; i < st->n_names; i++) {
|
|
66
|
+
if (strcmp(st->unique_names[i], name) == 0) return (int)i;
|
|
67
|
+
}
|
|
68
|
+
return -1;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static int add_unique_name(pivot_state *st, const char *name) {
|
|
72
|
+
int idx = find_unique_name(st, name);
|
|
73
|
+
if (idx >= 0) return idx;
|
|
74
|
+
if (st->n_names >= st->names_cap) {
|
|
75
|
+
size_t new_cap = st->names_cap ? st->names_cap * 2 : 32;
|
|
76
|
+
st->unique_names = realloc(st->unique_names, new_cap * sizeof(char *));
|
|
77
|
+
if (!st->unique_names) return -1;
|
|
78
|
+
st->names_cap = new_cap;
|
|
79
|
+
}
|
|
80
|
+
st->unique_names[st->n_names] = strdup(name);
|
|
81
|
+
return (int)st->n_names++;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Build group key from pass-through columns */
|
|
85
|
+
static char *build_pivot_key(const tf_batch *b, size_t row,
|
|
86
|
+
int *pt_cols, size_t n_pt) {
|
|
87
|
+
size_t buf_cap = 256;
|
|
88
|
+
char *buf = malloc(buf_cap);
|
|
89
|
+
if (!buf) return NULL;
|
|
90
|
+
size_t buf_len = 0;
|
|
91
|
+
for (size_t k = 0; k < n_pt; k++) {
|
|
92
|
+
int c = pt_cols[k];
|
|
93
|
+
if (k > 0) buf[buf_len++] = '\x01';
|
|
94
|
+
char val_buf[64];
|
|
95
|
+
const char *val = "";
|
|
96
|
+
size_t val_len = 0;
|
|
97
|
+
if (tf_batch_is_null(b, row, c)) {
|
|
98
|
+
val = "\\N"; val_len = 2;
|
|
99
|
+
} else {
|
|
100
|
+
switch (b->col_types[c]) {
|
|
101
|
+
case TF_TYPE_STRING: val = tf_batch_get_string(b, row, c); val_len = strlen(val); break;
|
|
102
|
+
case TF_TYPE_INT64:
|
|
103
|
+
val_len = snprintf(val_buf, sizeof(val_buf), "%lld", (long long)tf_batch_get_int64(b, row, c));
|
|
104
|
+
val = val_buf; break;
|
|
105
|
+
case TF_TYPE_FLOAT64:
|
|
106
|
+
val_len = snprintf(val_buf, sizeof(val_buf), "%.17g", tf_batch_get_float64(b, row, c));
|
|
107
|
+
val = val_buf; break;
|
|
108
|
+
case TF_TYPE_BOOL:
|
|
109
|
+
val = tf_batch_get_bool(b, row, c) ? "T" : "F"; val_len = 1; break;
|
|
110
|
+
case TF_TYPE_DATE:
|
|
111
|
+
val_len = snprintf(val_buf, sizeof(val_buf), "%d", (int)tf_batch_get_date(b, row, c));
|
|
112
|
+
val = val_buf; break;
|
|
113
|
+
case TF_TYPE_TIMESTAMP:
|
|
114
|
+
val_len = snprintf(val_buf, sizeof(val_buf), "%lld", (long long)tf_batch_get_timestamp(b, row, c));
|
|
115
|
+
val = val_buf; break;
|
|
116
|
+
default: val = "\\N"; val_len = 2; break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
while (buf_len + val_len + 2 >= buf_cap) { buf_cap *= 2; buf = realloc(buf, buf_cap); }
|
|
120
|
+
memcpy(buf + buf_len, val, val_len);
|
|
121
|
+
buf_len += val_len;
|
|
122
|
+
}
|
|
123
|
+
buf[buf_len] = '\0';
|
|
124
|
+
return buf;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
static int find_or_add_pivot_group(pivot_map *map, const char *key,
|
|
128
|
+
size_t n_names, size_t src_row) {
|
|
129
|
+
for (size_t i = 0; i < map->count; i++) {
|
|
130
|
+
if (strcmp(map->keys[i], key) == 0) return (int)i;
|
|
131
|
+
}
|
|
132
|
+
if (map->count >= map->cap) {
|
|
133
|
+
size_t new_cap = map->cap ? map->cap * 2 : 64;
|
|
134
|
+
map->keys = realloc(map->keys, new_cap * sizeof(char *));
|
|
135
|
+
map->pass_rows = realloc(map->pass_rows, new_cap * sizeof(size_t));
|
|
136
|
+
map->accums = realloc(map->accums, new_cap * sizeof(pivot_accum));
|
|
137
|
+
map->cap = new_cap;
|
|
138
|
+
}
|
|
139
|
+
size_t idx = map->count++;
|
|
140
|
+
map->keys[idx] = strdup(key);
|
|
141
|
+
map->pass_rows[idx] = src_row;
|
|
142
|
+
pivot_accum *a = &map->accums[idx];
|
|
143
|
+
a->sums = calloc(n_names, sizeof(double));
|
|
144
|
+
a->mins = malloc(n_names * sizeof(double));
|
|
145
|
+
a->maxs = malloc(n_names * sizeof(double));
|
|
146
|
+
a->counts = calloc(n_names, sizeof(size_t));
|
|
147
|
+
a->has_first = calloc(n_names, sizeof(int));
|
|
148
|
+
a->firsts = calloc(n_names, sizeof(double));
|
|
149
|
+
for (size_t i = 0; i < n_names; i++) { a->mins[i] = DBL_MAX; a->maxs[i] = -DBL_MAX; }
|
|
150
|
+
return (int)idx;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
static double get_numeric_value(const tf_batch *b, size_t row, int col) {
|
|
154
|
+
if (tf_batch_is_null(b, row, col)) return 0.0;
|
|
155
|
+
switch (b->col_types[col]) {
|
|
156
|
+
case TF_TYPE_INT64: return (double)tf_batch_get_int64(b, row, col);
|
|
157
|
+
case TF_TYPE_FLOAT64: return tf_batch_get_float64(b, row, col);
|
|
158
|
+
case TF_TYPE_DATE: return (double)tf_batch_get_date(b, row, col);
|
|
159
|
+
case TF_TYPE_TIMESTAMP: return (double)tf_batch_get_timestamp(b, row, col);
|
|
160
|
+
case TF_TYPE_BOOL: return tf_batch_get_bool(b, row, col) ? 1.0 : 0.0;
|
|
161
|
+
default: return 0.0;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* Get name column value as string */
|
|
166
|
+
static const char *get_name_str(const tf_batch *b, size_t row, int col, char *buf, size_t buf_sz) {
|
|
167
|
+
if (tf_batch_is_null(b, row, col)) return NULL;
|
|
168
|
+
switch (b->col_types[col]) {
|
|
169
|
+
case TF_TYPE_STRING: return tf_batch_get_string(b, row, col);
|
|
170
|
+
case TF_TYPE_INT64: snprintf(buf, buf_sz, "%lld", (long long)tf_batch_get_int64(b, row, col)); return buf;
|
|
171
|
+
case TF_TYPE_FLOAT64: snprintf(buf, buf_sz, "%g", tf_batch_get_float64(b, row, col)); return buf;
|
|
172
|
+
case TF_TYPE_BOOL: return tf_batch_get_bool(b, row, col) ? "true" : "false";
|
|
173
|
+
case TF_TYPE_DATE: tf_date_format(tf_batch_get_date(b, row, col), buf, buf_sz); return buf;
|
|
174
|
+
case TF_TYPE_TIMESTAMP: tf_timestamp_format(tf_batch_get_timestamp(b, row, col), buf, buf_sz); return buf;
|
|
175
|
+
default: return NULL;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
static int pivot_process(tf_step *self, tf_batch *in, tf_batch **out,
|
|
180
|
+
tf_side_channels *side) {
|
|
181
|
+
(void)side;
|
|
182
|
+
pivot_state *st = self->state;
|
|
183
|
+
*out = NULL;
|
|
184
|
+
|
|
185
|
+
if (!st->has_schema) {
|
|
186
|
+
st->buf = tf_batch_create(in->n_cols, in->n_rows > 0 ? in->n_rows : 16);
|
|
187
|
+
if (!st->buf) return TF_ERROR;
|
|
188
|
+
for (size_t c = 0; c < in->n_cols; c++)
|
|
189
|
+
tf_batch_set_schema(st->buf, c, in->col_names[c], in->col_types[c]);
|
|
190
|
+
st->has_schema = 1;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* Track unique name values and buffer rows */
|
|
194
|
+
int name_ci = tf_batch_col_index(in, st->name_column);
|
|
195
|
+
for (size_t r = 0; r < in->n_rows; r++) {
|
|
196
|
+
size_t dst_row = st->buf->n_rows;
|
|
197
|
+
if (tf_batch_copy_row(st->buf, dst_row, in, r) != TF_OK) return TF_ERROR;
|
|
198
|
+
st->buf->n_rows = dst_row + 1;
|
|
199
|
+
|
|
200
|
+
if (name_ci >= 0 && !tf_batch_is_null(in, r, name_ci)) {
|
|
201
|
+
char nbuf[64];
|
|
202
|
+
const char *name = get_name_str(in, r, name_ci, nbuf, sizeof(nbuf));
|
|
203
|
+
if (name) add_unique_name(st, name);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return TF_OK;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
static int pivot_flush(tf_step *self, tf_batch **out, tf_side_channels *side) {
|
|
211
|
+
(void)side;
|
|
212
|
+
pivot_state *st = self->state;
|
|
213
|
+
*out = NULL;
|
|
214
|
+
|
|
215
|
+
if (!st->buf || st->buf->n_rows == 0 || st->n_names == 0) return TF_OK;
|
|
216
|
+
|
|
217
|
+
tf_batch *buf = st->buf;
|
|
218
|
+
int name_ci = tf_batch_col_index(buf, st->name_column);
|
|
219
|
+
int val_ci = tf_batch_col_index(buf, st->value_column);
|
|
220
|
+
if (name_ci < 0 || val_ci < 0) return TF_OK;
|
|
221
|
+
|
|
222
|
+
/* Determine pass-through columns */
|
|
223
|
+
size_t n_pt = 0;
|
|
224
|
+
int *pt_cols = malloc(buf->n_cols * sizeof(int));
|
|
225
|
+
if (!pt_cols) return TF_ERROR;
|
|
226
|
+
for (size_t c = 0; c < buf->n_cols; c++) {
|
|
227
|
+
if ((int)c != name_ci && (int)c != val_ci)
|
|
228
|
+
pt_cols[n_pt++] = (int)c;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/* Build group map */
|
|
232
|
+
pivot_map map = {0};
|
|
233
|
+
for (size_t r = 0; r < buf->n_rows; r++) {
|
|
234
|
+
char *key = build_pivot_key(buf, r, pt_cols, n_pt);
|
|
235
|
+
if (!key) { free(pt_cols); return TF_ERROR; }
|
|
236
|
+
int gi = find_or_add_pivot_group(&map, key, st->n_names, r);
|
|
237
|
+
free(key);
|
|
238
|
+
if (gi < 0) { free(pt_cols); return TF_ERROR; }
|
|
239
|
+
|
|
240
|
+
/* Get name index */
|
|
241
|
+
char nbuf[64];
|
|
242
|
+
const char *name = get_name_str(buf, r, name_ci, nbuf, sizeof(nbuf));
|
|
243
|
+
if (!name) continue;
|
|
244
|
+
int ni = find_unique_name(st, name);
|
|
245
|
+
if (ni < 0) continue;
|
|
246
|
+
|
|
247
|
+
double v = get_numeric_value(buf, r, val_ci);
|
|
248
|
+
pivot_accum *a = &map.accums[gi];
|
|
249
|
+
a->sums[ni] += v;
|
|
250
|
+
if (v < a->mins[ni]) a->mins[ni] = v;
|
|
251
|
+
if (v > a->maxs[ni]) a->maxs[ni] = v;
|
|
252
|
+
a->counts[ni]++;
|
|
253
|
+
if (!a->has_first[ni]) { a->firsts[ni] = v; a->has_first[ni] = 1; }
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/* Build output batch */
|
|
257
|
+
size_t n_out_cols = n_pt + st->n_names;
|
|
258
|
+
tf_batch *ob = tf_batch_create(n_out_cols, map.count);
|
|
259
|
+
if (!ob) { free(pt_cols); return TF_ERROR; }
|
|
260
|
+
|
|
261
|
+
/* Pass-through column schemas */
|
|
262
|
+
for (size_t k = 0; k < n_pt; k++)
|
|
263
|
+
tf_batch_set_schema(ob, k, buf->col_names[pt_cols[k]], buf->col_types[pt_cols[k]]);
|
|
264
|
+
|
|
265
|
+
/* Pivot column schemas */
|
|
266
|
+
tf_type pivot_type = TF_TYPE_FLOAT64;
|
|
267
|
+
if (st->agg == PIVOT_COUNT) pivot_type = TF_TYPE_INT64;
|
|
268
|
+
for (size_t k = 0; k < st->n_names; k++)
|
|
269
|
+
tf_batch_set_schema(ob, n_pt + k, st->unique_names[k], pivot_type);
|
|
270
|
+
|
|
271
|
+
/* Fill output */
|
|
272
|
+
for (size_t g = 0; g < map.count; g++) {
|
|
273
|
+
tf_batch_ensure_capacity(ob, g + 1);
|
|
274
|
+
|
|
275
|
+
/* Copy pass-through values from the first row of this group */
|
|
276
|
+
size_t src_row = map.pass_rows[g];
|
|
277
|
+
for (size_t k = 0; k < n_pt; k++) {
|
|
278
|
+
int sc = pt_cols[k];
|
|
279
|
+
if (tf_batch_is_null(buf, src_row, sc)) {
|
|
280
|
+
tf_batch_set_null(ob, g, k);
|
|
281
|
+
} else {
|
|
282
|
+
switch (buf->col_types[sc]) {
|
|
283
|
+
case TF_TYPE_BOOL: tf_batch_set_bool(ob, g, k, tf_batch_get_bool(buf, src_row, sc)); break;
|
|
284
|
+
case TF_TYPE_INT64: tf_batch_set_int64(ob, g, k, tf_batch_get_int64(buf, src_row, sc)); break;
|
|
285
|
+
case TF_TYPE_FLOAT64: tf_batch_set_float64(ob, g, k, tf_batch_get_float64(buf, src_row, sc)); break;
|
|
286
|
+
case TF_TYPE_STRING: tf_batch_set_string(ob, g, k, tf_batch_get_string(buf, src_row, sc)); break;
|
|
287
|
+
case TF_TYPE_DATE: tf_batch_set_date(ob, g, k, tf_batch_get_date(buf, src_row, sc)); break;
|
|
288
|
+
case TF_TYPE_TIMESTAMP: tf_batch_set_timestamp(ob, g, k, tf_batch_get_timestamp(buf, src_row, sc)); break;
|
|
289
|
+
default: tf_batch_set_null(ob, g, k); break;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/* Set pivot values */
|
|
295
|
+
pivot_accum *a = &map.accums[g];
|
|
296
|
+
for (size_t k = 0; k < st->n_names; k++) {
|
|
297
|
+
size_t oc = n_pt + k;
|
|
298
|
+
if (a->counts[k] == 0) {
|
|
299
|
+
tf_batch_set_null(ob, g, oc);
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
double v = 0;
|
|
303
|
+
switch (st->agg) {
|
|
304
|
+
case PIVOT_FIRST: v = a->firsts[k]; break;
|
|
305
|
+
case PIVOT_SUM: v = a->sums[k]; break;
|
|
306
|
+
case PIVOT_COUNT: tf_batch_set_int64(ob, g, oc, (int64_t)a->counts[k]); goto next_name;
|
|
307
|
+
case PIVOT_AVG: v = a->sums[k] / (double)a->counts[k]; break;
|
|
308
|
+
case PIVOT_MIN: v = a->mins[k]; break;
|
|
309
|
+
case PIVOT_MAX: v = a->maxs[k]; break;
|
|
310
|
+
}
|
|
311
|
+
tf_batch_set_float64(ob, g, oc, v);
|
|
312
|
+
next_name:;
|
|
313
|
+
}
|
|
314
|
+
ob->n_rows = g + 1;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/* Cleanup map */
|
|
318
|
+
for (size_t i = 0; i < map.count; i++) {
|
|
319
|
+
free(map.keys[i]);
|
|
320
|
+
free(map.accums[i].sums);
|
|
321
|
+
free(map.accums[i].mins);
|
|
322
|
+
free(map.accums[i].maxs);
|
|
323
|
+
free(map.accums[i].counts);
|
|
324
|
+
free(map.accums[i].has_first);
|
|
325
|
+
free(map.accums[i].firsts);
|
|
326
|
+
}
|
|
327
|
+
free(map.keys);
|
|
328
|
+
free(map.pass_rows);
|
|
329
|
+
free(map.accums);
|
|
330
|
+
free(pt_cols);
|
|
331
|
+
|
|
332
|
+
*out = ob;
|
|
333
|
+
return TF_OK;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
static void pivot_destroy(tf_step *self) {
|
|
337
|
+
pivot_state *st = self->state;
|
|
338
|
+
if (st) {
|
|
339
|
+
free(st->name_column);
|
|
340
|
+
free(st->value_column);
|
|
341
|
+
if (st->buf) tf_batch_free(st->buf);
|
|
342
|
+
for (size_t i = 0; i < st->n_names; i++) free(st->unique_names[i]);
|
|
343
|
+
free(st->unique_names);
|
|
344
|
+
free(st);
|
|
345
|
+
}
|
|
346
|
+
free(self);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
tf_step *tf_pivot_create(const cJSON *args) {
|
|
350
|
+
if (!args) return NULL;
|
|
351
|
+
cJSON *name_j = cJSON_GetObjectItemCaseSensitive(args, "name_column");
|
|
352
|
+
cJSON *val_j = cJSON_GetObjectItemCaseSensitive(args, "value_column");
|
|
353
|
+
if (!cJSON_IsString(name_j) || !cJSON_IsString(val_j)) return NULL;
|
|
354
|
+
|
|
355
|
+
pivot_state *st = calloc(1, sizeof(pivot_state));
|
|
356
|
+
if (!st) return NULL;
|
|
357
|
+
st->name_column = strdup(name_j->valuestring);
|
|
358
|
+
st->value_column = strdup(val_j->valuestring);
|
|
359
|
+
|
|
360
|
+
cJSON *agg_j = cJSON_GetObjectItemCaseSensitive(args, "agg");
|
|
361
|
+
st->agg = cJSON_IsString(agg_j) ? parse_pivot_agg(agg_j->valuestring) : PIVOT_FIRST;
|
|
362
|
+
|
|
363
|
+
tf_step *step = malloc(sizeof(tf_step));
|
|
364
|
+
if (!step) { pivot_destroy(&(tf_step){.state = st}); return NULL; }
|
|
365
|
+
step->process = pivot_process;
|
|
366
|
+
step->flush = pivot_flush;
|
|
367
|
+
step->destroy = pivot_destroy;
|
|
368
|
+
step->state = st;
|
|
369
|
+
return step;
|
|
370
|
+
}
|