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_date_trunc.c
CHANGED
|
@@ -24,20 +24,38 @@ typedef enum {
|
|
|
24
24
|
TRUNC_SECOND,
|
|
25
25
|
} trunc_level;
|
|
26
26
|
|
|
27
|
+
typedef enum {
|
|
28
|
+
DATE_TRUNC_MISSING_ERROR,
|
|
29
|
+
DATE_TRUNC_MISSING_NULL,
|
|
30
|
+
DATE_TRUNC_MISSING_IGNORE,
|
|
31
|
+
} date_trunc_missing_policy;
|
|
32
|
+
|
|
33
|
+
typedef enum {
|
|
34
|
+
DATE_TRUNC_TYPE_FAIL,
|
|
35
|
+
DATE_TRUNC_TYPE_NULL,
|
|
36
|
+
} date_trunc_type_policy;
|
|
37
|
+
|
|
27
38
|
typedef struct {
|
|
28
39
|
char *column;
|
|
29
40
|
char *result;
|
|
30
41
|
trunc_level level;
|
|
42
|
+
date_trunc_missing_policy missing;
|
|
43
|
+
date_trunc_type_policy on_type_error;
|
|
31
44
|
} date_trunc_state;
|
|
32
45
|
|
|
33
|
-
static
|
|
34
|
-
if (
|
|
35
|
-
if (strcmp(s, "
|
|
36
|
-
if (strcmp(s, "
|
|
37
|
-
if (strcmp(s, "
|
|
38
|
-
if (strcmp(s, "
|
|
39
|
-
if (strcmp(s, "
|
|
40
|
-
return
|
|
46
|
+
static int parse_level(const char *s, trunc_level *out) {
|
|
47
|
+
if (!s) return TF_ERROR;
|
|
48
|
+
if (strcmp(s, "year") == 0) { *out = TRUNC_YEAR; return TF_OK; }
|
|
49
|
+
if (strcmp(s, "month") == 0) { *out = TRUNC_MONTH; return TF_OK; }
|
|
50
|
+
if (strcmp(s, "day") == 0) { *out = TRUNC_DAY; return TF_OK; }
|
|
51
|
+
if (strcmp(s, "hour") == 0) { *out = TRUNC_HOUR; return TF_OK; }
|
|
52
|
+
if (strcmp(s, "minute") == 0) { *out = TRUNC_MINUTE; return TF_OK; }
|
|
53
|
+
if (strcmp(s, "second") == 0) { *out = TRUNC_SECOND; return TF_OK; }
|
|
54
|
+
return TF_ERROR;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static int date_trunc_is_temporal_type(tf_type type) {
|
|
58
|
+
return type == TF_TYPE_STRING || type == TF_TYPE_DATE || type == TF_TYPE_TIMESTAMP;
|
|
41
59
|
}
|
|
42
60
|
|
|
43
61
|
/* Truncate a date (days since epoch) to the given level. */
|
|
@@ -71,71 +89,201 @@ static int parse_date_string(const char *s, int *y, int *m, int *d) {
|
|
|
71
89
|
return sscanf(s, "%d-%d-%d", y, m, d) == 3;
|
|
72
90
|
}
|
|
73
91
|
|
|
92
|
+
static int date_trunc_set_error(tf_side_channels *side, const char *msg) {
|
|
93
|
+
return tf_side_write_error(side, msg);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
static int date_trunc_set_col_error(tf_side_channels *side, const char *column, const char *suffix) {
|
|
97
|
+
char msg[512];
|
|
98
|
+
snprintf(msg, sizeof(msg), "date-trunc: column '%s' %s", column ? column : "", suffix);
|
|
99
|
+
return date_trunc_set_error(side, msg);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
static int date_trunc_parse_missing_policy(const cJSON *args, date_trunc_missing_policy *out) {
|
|
103
|
+
*out = DATE_TRUNC_MISSING_ERROR;
|
|
104
|
+
const cJSON *j = cJSON_GetObjectItemCaseSensitive(args, "missing");
|
|
105
|
+
if (!j) return TF_OK;
|
|
106
|
+
if (!cJSON_IsString(j)) {
|
|
107
|
+
tf_set_last_error("date-trunc: missing must be error, null, or ignore");
|
|
108
|
+
return TF_ERROR;
|
|
109
|
+
}
|
|
110
|
+
if (strcmp(j->valuestring, "error") == 0) *out = DATE_TRUNC_MISSING_ERROR;
|
|
111
|
+
else if (strcmp(j->valuestring, "null") == 0) *out = DATE_TRUNC_MISSING_NULL;
|
|
112
|
+
else if (strcmp(j->valuestring, "ignore") == 0) *out = DATE_TRUNC_MISSING_IGNORE;
|
|
113
|
+
else {
|
|
114
|
+
tf_set_last_error("date-trunc: missing must be error, null, or ignore");
|
|
115
|
+
return TF_ERROR;
|
|
116
|
+
}
|
|
117
|
+
return TF_OK;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static int date_trunc_parse_type_policy(const cJSON *args, date_trunc_type_policy *out) {
|
|
121
|
+
*out = DATE_TRUNC_TYPE_FAIL;
|
|
122
|
+
const cJSON *j = cJSON_GetObjectItemCaseSensitive(args, "on_type_error");
|
|
123
|
+
if (!j) return TF_OK;
|
|
124
|
+
if (!cJSON_IsString(j)) {
|
|
125
|
+
tf_set_last_error("date-trunc: on_type_error must be fail or null");
|
|
126
|
+
return TF_ERROR;
|
|
127
|
+
}
|
|
128
|
+
if (strcmp(j->valuestring, "fail") == 0) *out = DATE_TRUNC_TYPE_FAIL;
|
|
129
|
+
else if (strcmp(j->valuestring, "null") == 0) *out = DATE_TRUNC_TYPE_NULL;
|
|
130
|
+
else {
|
|
131
|
+
tf_set_last_error("date-trunc: on_type_error must be fail or null");
|
|
132
|
+
return TF_ERROR;
|
|
133
|
+
}
|
|
134
|
+
return TF_OK;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
static int date_trunc_passthrough(tf_batch *in, tf_batch **out) {
|
|
138
|
+
tf_batch *ob = tf_batch_create(in->n_cols, in->n_rows);
|
|
139
|
+
if (!ob) return TF_ERROR;
|
|
140
|
+
if (tf_batch_clone_schema(ob, in) != TF_OK) {
|
|
141
|
+
tf_batch_free(ob);
|
|
142
|
+
return TF_ERROR;
|
|
143
|
+
}
|
|
144
|
+
for (size_t r = 0; r < in->n_rows; r++) {
|
|
145
|
+
if (tf_batch_copy_row(ob, r, in, r) != TF_OK) {
|
|
146
|
+
tf_batch_free(ob);
|
|
147
|
+
return TF_ERROR;
|
|
148
|
+
}
|
|
149
|
+
if (tf_batch_expose_row(ob, r) != TF_OK) {
|
|
150
|
+
tf_batch_free(ob);
|
|
151
|
+
return TF_ERROR;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
*out = ob;
|
|
155
|
+
return TF_OK;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
static int date_trunc_null_output(date_trunc_state *st, tf_batch *in, int ci, tf_batch **out) {
|
|
159
|
+
int in_place = ci >= 0 && strcmp(st->result, st->column) == 0;
|
|
160
|
+
size_t result_col = in_place ? (size_t)ci : in->n_cols;
|
|
161
|
+
tf_batch *ob = NULL;
|
|
162
|
+
if (in_place) {
|
|
163
|
+
ob = tf_batch_create(in->n_cols, in->n_rows);
|
|
164
|
+
if (!ob) return TF_ERROR;
|
|
165
|
+
if (tf_batch_clone_schema(ob, in) != TF_OK) {
|
|
166
|
+
tf_batch_free(ob);
|
|
167
|
+
return TF_ERROR;
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
const char *extra_names[1] = {st->result};
|
|
171
|
+
tf_type extra_types[1] = {ci >= 0 ? in->col_types[ci] : TF_TYPE_STRING};
|
|
172
|
+
ob = tf_batch_create(in->n_cols + 1, in->n_rows);
|
|
173
|
+
if (!ob) return TF_ERROR;
|
|
174
|
+
if (tf_batch_clone_with_extra_cols(ob, in, extra_names, extra_types, 1) != TF_OK) {
|
|
175
|
+
tf_batch_free(ob);
|
|
176
|
+
return TF_ERROR;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
for (size_t r = 0; r < in->n_rows; r++) {
|
|
180
|
+
if (tf_batch_copy_row(ob, r, in, r) != TF_OK ||
|
|
181
|
+
tf_batch_set_null(ob, r, result_col) != TF_OK) {
|
|
182
|
+
tf_batch_free(ob);
|
|
183
|
+
return TF_ERROR;
|
|
184
|
+
}
|
|
185
|
+
if (tf_batch_expose_row(ob, r) != TF_OK) {
|
|
186
|
+
tf_batch_free(ob);
|
|
187
|
+
return TF_ERROR;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
*out = ob;
|
|
191
|
+
return TF_OK;
|
|
192
|
+
}
|
|
193
|
+
|
|
74
194
|
static int date_trunc_process(tf_step *self, tf_batch *in, tf_batch **out,
|
|
75
195
|
tf_side_channels *side) {
|
|
76
|
-
(void)side;
|
|
77
196
|
date_trunc_state *st = self->state;
|
|
78
197
|
*out = NULL;
|
|
79
198
|
|
|
80
199
|
int ci = tf_batch_col_index(in, st->column);
|
|
200
|
+
if (ci < 0) {
|
|
201
|
+
if (st->missing == DATE_TRUNC_MISSING_ERROR) {
|
|
202
|
+
if (date_trunc_set_col_error(side, st->column, "not found") != TF_OK) return TF_ERROR;
|
|
203
|
+
return TF_ERROR;
|
|
204
|
+
}
|
|
205
|
+
if (st->missing == DATE_TRUNC_MISSING_IGNORE) return date_trunc_passthrough(in, out);
|
|
206
|
+
return date_trunc_null_output(st, in, ci, out);
|
|
207
|
+
}
|
|
208
|
+
if (!date_trunc_is_temporal_type(in->col_types[ci])) {
|
|
209
|
+
if (st->on_type_error == DATE_TRUNC_TYPE_FAIL) {
|
|
210
|
+
if (date_trunc_set_col_error(side, st->column, "must be string, date, or timestamp") != TF_OK) return TF_ERROR;
|
|
211
|
+
return TF_ERROR;
|
|
212
|
+
}
|
|
213
|
+
return date_trunc_null_output(st, in, ci, out);
|
|
214
|
+
}
|
|
215
|
+
|
|
81
216
|
int in_place = (strcmp(st->result, st->column) == 0);
|
|
82
217
|
|
|
83
218
|
size_t out_cols = in_place ? in->n_cols : in->n_cols + 1;
|
|
84
219
|
tf_batch *ob = tf_batch_create(out_cols, in->n_rows);
|
|
85
220
|
if (!ob) return TF_ERROR;
|
|
86
221
|
|
|
87
|
-
for (size_t c = 0; c < in->n_cols; c++)
|
|
88
|
-
tf_batch_set_schema(ob, c, in->col_names[c], in->col_types[c]);
|
|
89
|
-
|
|
90
222
|
size_t result_col;
|
|
91
223
|
if (in_place) {
|
|
224
|
+
if (tf_batch_clone_schema(ob, in) != TF_OK) {
|
|
225
|
+
tf_batch_free(ob);
|
|
226
|
+
return TF_ERROR;
|
|
227
|
+
}
|
|
92
228
|
result_col = (size_t)ci;
|
|
93
229
|
} else {
|
|
230
|
+
const char *extra_names[1] = {st->result};
|
|
231
|
+
tf_type extra_types[1] = {in->col_types[ci]};
|
|
232
|
+
if (tf_batch_clone_with_extra_cols(ob, in, extra_names, extra_types, 1) != TF_OK) {
|
|
233
|
+
tf_batch_free(ob);
|
|
234
|
+
return TF_ERROR;
|
|
235
|
+
}
|
|
94
236
|
result_col = in->n_cols;
|
|
95
|
-
/* Output type matches input column type */
|
|
96
|
-
tf_type out_type = (ci >= 0) ? in->col_types[ci] : TF_TYPE_STRING;
|
|
97
|
-
tf_batch_set_schema(ob, result_col, st->result, out_type);
|
|
98
237
|
}
|
|
99
238
|
|
|
100
239
|
for (size_t r = 0; r < in->n_rows; r++) {
|
|
101
|
-
tf_batch_copy_row(ob, r, in, r)
|
|
240
|
+
if (tf_batch_copy_row(ob, r, in, r) != TF_OK) {
|
|
241
|
+
tf_batch_free(ob);
|
|
242
|
+
return TF_ERROR;
|
|
243
|
+
}
|
|
102
244
|
|
|
103
|
-
if (
|
|
104
|
-
if (!in_place
|
|
105
|
-
|
|
245
|
+
if (tf_batch_is_null(in, r, (size_t)ci)) {
|
|
246
|
+
if (!in_place && tf_batch_set_null(ob, r, result_col) != TF_OK) {
|
|
247
|
+
tf_batch_free(ob);
|
|
248
|
+
return TF_ERROR;
|
|
249
|
+
}
|
|
250
|
+
if (tf_batch_expose_row(ob, r) != TF_OK) { tf_batch_free(ob); return TF_ERROR; }
|
|
106
251
|
continue;
|
|
107
252
|
}
|
|
108
253
|
|
|
109
254
|
switch (in->col_types[ci]) {
|
|
110
255
|
case TF_TYPE_DATE: {
|
|
111
256
|
int32_t d = tf_batch_get_date(in, r, ci);
|
|
112
|
-
tf_batch_set_date(ob, r, result_col, trunc_date(d, st->level));
|
|
257
|
+
if (tf_batch_set_date(ob, r, result_col, trunc_date(d, st->level)) != TF_OK) { tf_batch_free(ob); return TF_ERROR; }
|
|
113
258
|
break;
|
|
114
259
|
}
|
|
115
260
|
case TF_TYPE_TIMESTAMP: {
|
|
116
261
|
int64_t ts = tf_batch_get_timestamp(in, r, ci);
|
|
117
|
-
tf_batch_set_timestamp(ob, r, result_col, trunc_timestamp(ts, st->level));
|
|
262
|
+
if (tf_batch_set_timestamp(ob, r, result_col, trunc_timestamp(ts, st->level)) != TF_OK) { tf_batch_free(ob); return TF_ERROR; }
|
|
118
263
|
break;
|
|
119
264
|
}
|
|
120
265
|
case TF_TYPE_STRING: {
|
|
121
266
|
const char *s = tf_batch_get_string(in, r, ci);
|
|
122
267
|
int y, m, d;
|
|
123
|
-
if (parse_date_string(s, &y, &m, &d)) {
|
|
268
|
+
if (s && parse_date_string(s, &y, &m, &d)) {
|
|
124
269
|
int32_t days = tf_date_from_ymd(y, m, d);
|
|
125
270
|
int32_t trunc = trunc_date(days, st->level);
|
|
126
|
-
char buf[
|
|
271
|
+
char buf[32];
|
|
127
272
|
tf_date_format(trunc, buf, sizeof(buf));
|
|
128
|
-
tf_batch_set_string(ob, r, result_col, buf);
|
|
273
|
+
if (tf_batch_set_string(ob, r, result_col, buf) != TF_OK) { tf_batch_free(ob); return TF_ERROR; }
|
|
129
274
|
} else {
|
|
130
|
-
tf_batch_set_string(ob, r, result_col, s);
|
|
275
|
+
if (tf_batch_set_string(ob, r, result_col, s ? s : "") != TF_OK) { tf_batch_free(ob); return TF_ERROR; }
|
|
131
276
|
}
|
|
132
277
|
break;
|
|
133
278
|
}
|
|
134
279
|
default:
|
|
135
|
-
|
|
136
|
-
|
|
280
|
+
tf_batch_free(ob);
|
|
281
|
+
return TF_ERROR;
|
|
282
|
+
}
|
|
283
|
+
if (tf_batch_expose_row(ob, r) != TF_OK) {
|
|
284
|
+
tf_batch_free(ob);
|
|
285
|
+
return TF_ERROR;
|
|
137
286
|
}
|
|
138
|
-
ob->n_rows = r + 1;
|
|
139
287
|
}
|
|
140
288
|
|
|
141
289
|
*out = ob;
|
|
@@ -146,9 +294,15 @@ static int date_trunc_flush(tf_step *self, tf_batch **out, tf_side_channels *sid
|
|
|
146
294
|
(void)self; (void)side; *out = NULL; return TF_OK;
|
|
147
295
|
}
|
|
148
296
|
|
|
297
|
+
static void date_trunc_state_free(date_trunc_state *st) {
|
|
298
|
+
if (!st) return;
|
|
299
|
+
free(st->column);
|
|
300
|
+
free(st->result);
|
|
301
|
+
free(st);
|
|
302
|
+
}
|
|
303
|
+
|
|
149
304
|
static void date_trunc_destroy(tf_step *self) {
|
|
150
|
-
|
|
151
|
-
if (st) { free(st->column); free(st->result); free(st); }
|
|
305
|
+
if (self) date_trunc_state_free(self->state);
|
|
152
306
|
free(self);
|
|
153
307
|
}
|
|
154
308
|
|
|
@@ -156,23 +310,43 @@ tf_step *tf_date_trunc_create(const cJSON *args) {
|
|
|
156
310
|
if (!args) return NULL;
|
|
157
311
|
cJSON *col_j = cJSON_GetObjectItemCaseSensitive(args, "column");
|
|
158
312
|
cJSON *trunc_j = cJSON_GetObjectItemCaseSensitive(args, "trunc");
|
|
159
|
-
if (!cJSON_IsString(col_j) || !cJSON_IsString(trunc_j))
|
|
313
|
+
if (!cJSON_IsString(col_j) || !col_j->valuestring[0] || !cJSON_IsString(trunc_j) || !trunc_j->valuestring[0]) {
|
|
314
|
+
tf_set_last_error("date-trunc: column and trunc are required");
|
|
315
|
+
return NULL;
|
|
316
|
+
}
|
|
160
317
|
|
|
161
318
|
date_trunc_state *st = calloc(1, sizeof(date_trunc_state));
|
|
162
319
|
if (!st) return NULL;
|
|
163
320
|
st->column = strdup(col_j->valuestring);
|
|
164
|
-
st->
|
|
321
|
+
if (!st->column) { free(st); return NULL; }
|
|
322
|
+
if (parse_level(trunc_j->valuestring, &st->level) != TF_OK) {
|
|
323
|
+
tf_set_last_error("date-trunc: trunc must be year, month, day, hour, minute, or second");
|
|
324
|
+
date_trunc_state_free(st);
|
|
325
|
+
return NULL;
|
|
326
|
+
}
|
|
165
327
|
|
|
166
328
|
cJSON *res_j = cJSON_GetObjectItemCaseSensitive(args, "result");
|
|
167
329
|
if (cJSON_IsString(res_j)) {
|
|
330
|
+
if (!res_j->valuestring[0]) {
|
|
331
|
+
tf_set_last_error("date-trunc: result cannot be empty");
|
|
332
|
+
date_trunc_state_free(st);
|
|
333
|
+
return NULL;
|
|
334
|
+
}
|
|
168
335
|
st->result = strdup(res_j->valuestring);
|
|
169
336
|
} else {
|
|
170
337
|
/* Default: overwrite in place */
|
|
171
338
|
st->result = strdup(st->column);
|
|
172
339
|
}
|
|
340
|
+
if (!st->result) { date_trunc_state_free(st); return NULL; }
|
|
341
|
+
|
|
342
|
+
if (date_trunc_parse_missing_policy(args, &st->missing) != TF_OK ||
|
|
343
|
+
date_trunc_parse_type_policy(args, &st->on_type_error) != TF_OK) {
|
|
344
|
+
date_trunc_state_free(st);
|
|
345
|
+
return NULL;
|
|
346
|
+
}
|
|
173
347
|
|
|
174
|
-
tf_step *step =
|
|
175
|
-
if (!step) {
|
|
348
|
+
tf_step *step = calloc(1, sizeof(tf_step));
|
|
349
|
+
if (!step) { date_trunc_state_free(st); return NULL; }
|
|
176
350
|
step->process = date_trunc_process;
|
|
177
351
|
step->flush = date_trunc_flush;
|
|
178
352
|
step->destroy = date_trunc_destroy;
|