#include "lang/internal.h"
#include "io/parquet.h"
#include "lang/eval.h"
#include "lang/env.h"
#include "ops/ops.h"
#include "ops/internal.h"
#include "ops/hash.h"
#include "ops/glob.h"
#include "ops/idxop.h"
#include "ops/agg_engine.h"
#include "core/runtime.h"
#include "ops/rowsel.h"
#include "ops/fused_group.h"
#include "ops/fused_topk.h"
#include "ops/hll.h"
#include "ops/cdfuse.h"
#include "ops/temporal.h"
#include "core/profile.h"
#include "table/sym.h"
#include "table/domain.h"
#include "table/dict.h"
#include "mem/heap.h"
#include <string.h>
#include <math.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stdatomic.h>
static ray_t* dict_get(ray_t* dict, const char* key) {
if (!dict || dict->type != RAY_DICT) return NULL;
size_t key_len = strlen(key);
ray_t* keys = ray_dict_keys(dict);
ray_t* vals = ray_dict_vals(dict);
if (!keys || keys->type != RAY_SYM || !vals || vals->type != RAY_LIST)
return NULL;
ray_t** vptrs = (ray_t**)ray_data(vals);
for (int64_t i = 0; i < keys->len; i++) {
ray_t* s = ray_sym_vec_cell(keys, i);
if (s && ray_str_len(s) == key_len &&
memcmp(ray_str_ptr(s), key, key_len) == 0)
return vptrs[i];
}
return NULL;
}
static int64_t dict_key_id(ray_t* dict, const char* key) {
if (!dict || dict->type != RAY_DICT) return -1;
size_t key_len = strlen(key);
ray_t* keys = ray_dict_keys(dict);
if (!keys || keys->type != RAY_SYM) return -1;
for (int64_t i = 0; i < keys->len; i++) {
ray_t* s = ray_sym_vec_cell(keys, i);
if (s && ray_str_len(s) == key_len &&
memcmp(ray_str_ptr(s), key, key_len) == 0)
return ray_sym_intern(key, key_len);
}
return -1;
}
_Static_assert(sizeof(ray_t) % 8 == 0, "dict view carve layout");
static void dict_pair_view(ray_t* d, ray_t** hdr_out, ray_t*** view_out,
int64_t* out_n) {
*hdr_out = NULL; *view_out = NULL; *out_n = 0;
if (!d || d->type != RAY_DICT) return;
ray_t* keys = ray_dict_keys(d);
ray_t* vals = ray_dict_vals(d);
if (!keys || keys->type != RAY_SYM || !vals || vals->type != RAY_LIST) return;
int64_t n = keys->len;
size_t bytes = (size_t)n * sizeof(ray_t)
+ (size_t)n * 2 * sizeof(ray_t*);
uint8_t* blk = (uint8_t*)scratch_alloc(hdr_out, bytes ? bytes : 8);
if (!blk) { *out_n = -1; return; }
ray_t* keybuf = (ray_t*)blk;
ray_t** elems = (ray_t**)(blk + (size_t)n * sizeof(ray_t));
ray_t** vptrs = (ray_t**)ray_data(vals);
for (int64_t i = 0; i < n; i++) {
memset(&keybuf[i], 0, sizeof(ray_t));
keybuf[i].type = -RAY_SYM;
keybuf[i].i64 = sym_cell_runtime_id(keys, i);
elems[i*2] = &keybuf[i];
elems[i*2+1] = vptrs[i];
}
*view_out = elems; *out_n = 2 * n;
}
#define DICT_VIEW_DECL(name) \
ray_t* name##_hdr = NULL; \
ray_t** name = NULL; \
int64_t name##_n = 0
#define DICT_VIEW_OPEN(d, name) \
dict_pair_view((d), &name##_hdr, &name, &name##_n)
#define DICT_VIEW_OVERFLOW(name) ((name##_n) < 0)
#define DICT_VIEW_CLOSE(name) do { scratch_free(name##_hdr); name##_hdr = NULL; } while (0)
static ray_t* groups_to_pair_list(ray_t* d) {
if (!d || d->type != RAY_DICT) return ray_error("type", "group: expected groups dict, got %s", d ? ray_type_name(d->type) : "null");
ray_t* keys = ray_dict_keys(d);
ray_t* vals = ray_dict_vals(d);
int64_t n = keys ? keys->len : 0;
ray_t* out = ray_list_new(n * 2);
if (!out || RAY_IS_ERR(out)) return out ? out : ray_error("oom", NULL);
ray_t** vptrs = (vals && vals->type == RAY_LIST) ? (ray_t**)ray_data(vals) : NULL;
for (int64_t i = 0; i < n; i++) {
ray_t* k = NULL;
if (!keys) {
k = NULL;
} else if (keys->type == RAY_LIST) {
k = ((ray_t**)ray_data(keys))[i];
if (k) ray_retain(k);
} else {
void* base = ray_data(keys);
switch (keys->type) {
case RAY_SYM: k = ray_sym(sym_cell_runtime_id(keys, i)); break;
case RAY_I64:
case RAY_TIMESTAMP: k = ray_i64(((int64_t*)base)[i]); break;
case RAY_I32:
case RAY_DATE:
case RAY_TIME: k = ray_i32(((int32_t*)base)[i]); break;
case RAY_I16: k = ray_i16(((int16_t*)base)[i]); break;
case RAY_BOOL:
case RAY_U8: k = ray_u8(((uint8_t*)base)[i]); break;
case RAY_F64: k = ray_f64(((double*)base)[i]); break;
case RAY_STR: { size_t sl = 0; const char* sp = ray_str_vec_get(keys, i, &sl);
k = ray_str(sp ? sp : "", sp ? sl : 0); break; }
case RAY_GUID: k = ray_guid(((uint8_t*)base) + i * 16); break;
default: k = NULL; break;
}
}
out = ray_list_append(out, k);
if (k) ray_release(k);
ray_t* v = vptrs ? vptrs[i] : NULL;
out = ray_list_append(out, v);
}
return out;
}
typedef struct {
ray_t* col;
int8_t base_type;
ray_t** segs;
ray_t* mc_keys;
const int64_t* mc_counts;
int64_t n_segs;
int64_t seg_idx;
int64_t seg_start;
int64_t seg_end;
} query_key_reader_t;
static bool query_key_reader_init(query_key_reader_t* r, ray_t* col) {
memset(r, 0, sizeof(*r));
r->col = col;
r->seg_end = INT64_MAX;
if (!col) return false;
if (RAY_IS_PARTED(col->type)) {
r->base_type = (int8_t)RAY_PARTED_BASETYPE(col->type);
r->segs = (ray_t**)ray_data(col);
r->n_segs = col->len;
r->seg_end = (r->n_segs > 0 && r->segs[0]) ? r->segs[0]->len : 0;
return true;
}
if (col->type == RAY_MAPCOMMON) {
ray_t** ptrs = (ray_t**)ray_data(col);
r->mc_keys = ptrs[0];
ray_t* counts = ptrs[1];
if (!r->mc_keys || !counts || counts->type != RAY_I64) return false;
r->base_type = r->mc_keys->type;
r->mc_counts = (const int64_t*)ray_data(counts);
r->n_segs = r->mc_keys->len;
r->seg_end = (r->n_segs > 0) ? r->mc_counts[0] : 0;
return true;
}
r->base_type = col->type;
return true;
}
static bool query_key_reader_read(query_key_reader_t* r, int64_t row,
int64_t* out, uint8_t* is_null) {
if (!r || !r->col || !out || !is_null) return false;
*is_null = 0;
*out = 0;
if (!RAY_IS_PARTED(r->col->type) && r->col->type != RAY_MAPCOMMON) {
*is_null = ray_vec_is_null(r->col, row);
if (*is_null) return true;
if (r->base_type == RAY_F64) memcpy(out, &((double*)ray_data(r->col))[row], 8);
else *out = read_col_i64(ray_data(r->col), row, r->base_type, r->col->attrs);
return true;
}
while (row >= r->seg_end && r->seg_idx + 1 < r->n_segs) {
r->seg_start = r->seg_end;
r->seg_idx++;
int64_t len = 0;
if (r->col->type == RAY_MAPCOMMON) len = r->mc_counts[r->seg_idx];
else if (r->segs[r->seg_idx]) len = r->segs[r->seg_idx]->len;
r->seg_end += len;
}
if (row < r->seg_start || row >= r->seg_end) return false;
if (r->col->type == RAY_MAPCOMMON) {
if (r->base_type == RAY_F64)
memcpy(out, (const char*)ray_data(r->mc_keys) + (size_t)r->seg_idx * 8, 8);
else
*out = read_col_i64(ray_data(r->mc_keys), r->seg_idx,
r->base_type, r->mc_keys->attrs);
return true;
}
ray_t* seg = r->segs[r->seg_idx];
if (!seg) return false;
int64_t local = row - r->seg_start;
*is_null = ray_vec_is_null(seg, local);
if (*is_null) return true;
if (r->base_type == RAY_F64) memcpy(out, &((double*)ray_data(seg))[local], 8);
else *out = read_col_i64(ray_data(seg), local, r->base_type, seg->attrs);
return true;
}
typedef ray_op_t* (*dag_binary_ctor)(ray_graph_t*, ray_op_t*, ray_op_t*);
typedef ray_op_t* (*dag_unary_ctor)(ray_graph_t*, ray_op_t*);
static bool dag_numeric_type_admitted(int8_t t) {
if (t <= 0) return true;
if (RAY_IS_PARTED(t)) t = (int8_t)RAY_PARTED_BASETYPE(t);
return t == RAY_BOOL || t == RAY_U8 || t == RAY_I16 ||
t == RAY_I32 || t == RAY_I64 || t == RAY_F32 || t == RAY_F64;
}
static bool dag_pow_type_admitted(int8_t t) {
return dag_numeric_type_admitted(t);
}
static bool dag_type_is_temporal(int8_t t) {
if (t <= 0) return false;
if (RAY_IS_PARTED(t)) t = (int8_t)RAY_PARTED_BASETYPE(t);
return t == RAY_DATE || t == RAY_TIME || t == RAY_TIMESTAMP;
}
static const char* dag_arith_verb(const char* fname, size_t fname_len) {
if (fname_len == 1) {
switch (fname[0]) {
case '+': return "add";
case '-': return "subtract";
case '*': return "multiply";
case '/': return "divide";
case '%': return "mod";
}
}
return fname_len == 3 && memcmp(fname, "pow", 3) == 0 ? "pow" : "div";
}
static bool dag_arith_rejects_sym(const char* fname, size_t fname_len,
int8_t lt, int8_t rt) {
bool arith = (fname_len == 1 && (fname[0] == '+' || fname[0] == '-' ||
fname[0] == '*' || fname[0] == '/' ||
fname[0] == '%')) ||
(fname_len == 3 && (memcmp(fname, "div", 3) == 0 ||
memcmp(fname, "pow", 3) == 0));
return arith && (lt == RAY_SYM || rt == RAY_SYM);
}
static bool dag_temporal_arith_needs_eval(const char* name, size_t len,
int8_t left_type, int8_t right_type) {
if (!dag_type_is_temporal(left_type) && !dag_type_is_temporal(right_type))
return false;
return len == 1 && (name[0] == '+' || name[0] == '-' || name[0] == '*');
}
static bool dag_cmp_rejects_temporal_float(const char* fname, size_t fname_len,
int8_t lt, int8_t rt) {
bool cmp = (fname_len == 1 && (fname[0] == '>' || fname[0] == '<')) ||
(fname_len == 2 && fname[1] == '=' &&
(fname[0] == '>' || fname[0] == '<' ||
fname[0] == '=' || fname[0] == '!'));
if (!cmp) return false;
if (RAY_IS_PARTED(lt)) lt = (int8_t)RAY_PARTED_BASETYPE(lt);
if (RAY_IS_PARTED(rt)) rt = (int8_t)RAY_PARTED_BASETYPE(rt);
bool lf = lt == RAY_F64 || lt == RAY_F32;
bool rf = rt == RAY_F64 || rt == RAY_F32;
return (dag_type_is_temporal(lt) && rf) || (lf && dag_type_is_temporal(rt));
}
static bool dag_bool_operand_ok(ray_graph_t* g, ray_op_t* a, bool is_and) {
if (a->opcode == OP_CONST || a->out_type <= 0 || a->out_type == RAY_BOOL)
return true;
if (!g->compile_err)
g->compile_err = ray_error("type", "%s: operand must be boolean, got %s",
is_and ? "and" : "or", ray_type_name(a->out_type));
return false;
}
static bool dag_unary_numeric_name(const char* name, size_t len) {
if (len == 3)
return memcmp(name, "sin", 3) == 0 ||
memcmp(name, "cos", 3) == 0 ||
memcmp(name, "tan", 3) == 0;
if (len == 4)
return memcmp(name, "asin", 4) == 0 ||
memcmp(name, "acos", 4) == 0 ||
memcmp(name, "atan", 4) == 0;
if (len == 6)
return memcmp(name, "signum", 6) == 0;
if (len == 10)
return memcmp(name, "reciprocal", 10) == 0;
return false;
}
static dag_binary_ctor resolve_binary_dag(int64_t sym_id) {
ray_t* s = ray_sym_str(sym_id);
if (!s) return NULL;
const char* name = ray_str_ptr(s);
size_t len = ray_str_len(s);
if (len == 1) {
switch (name[0]) {
case '+': return ray_add;
case '-': return ray_sub;
case '*': return ray_mul;
case '/': return ray_div;
case '%': return ray_mod;
case '>': return ray_gt;
case '<': return ray_lt;
}
} else if (len == 2) {
if (name[0] == '>' && name[1] == '=') return ray_ge;
if (name[0] == '<' && name[1] == '=') return ray_le;
if (name[0] == '=' && name[1] == '=') return ray_eq;
if (name[0] == '!' && name[1] == '=') return ray_ne;
if (name[0] == 'o' && name[1] == 'r') return ray_or;
if (name[0] == 'i' && name[1] == 'n') return ray_in;
} else if (len == 3) {
if (memcmp(name, "and", 3) == 0) return ray_and;
if (memcmp(name, "div", 3) == 0) return ray_idiv;
if (memcmp(name, "pow", 3) == 0) return ray_pow_op;
} else if (len == 4) {
if (memcmp(name, "like", 4) == 0) return ray_like;
} else if (len == 5) {
if (memcmp(name, "ilike", 5) == 0) return ray_ilike;
} else if (len == 6) {
if (memcmp(name, "not-in", 6) == 0) return ray_not_in;
} else if (len == 8) {
if (memcmp(name, "str-find", 8) == 0) return ray_str_find_op;
}
return NULL;
}
static dag_unary_ctor resolve_unary_dag(int64_t sym_id) {
ray_t* s = ray_sym_str(sym_id);
if (!s) return NULL;
const char* name = ray_str_ptr(s);
size_t len = ray_str_len(s);
if (len == 3) {
if (memcmp(name, "lag", 3) == 0) return ray_lag_op;
if (memcmp(name, "neg", 3) == 0) return ray_neg;
if (memcmp(name, "not", 3) == 0) return ray_not;
if (memcmp(name, "abs", 3) == 0) return ray_abs;
if (memcmp(name, "exp", 3) == 0) return ray_exp_op;
if (memcmp(name, "log", 3) == 0) return ray_log_op;
if (memcmp(name, "sin", 3) == 0) return ray_sin_op;
if (memcmp(name, "cos", 3) == 0) return ray_cos_op;
if (memcmp(name, "tan", 3) == 0) return ray_tan_op;
} else if (len == 4) {
if (memcmp(name, "lead", 4) == 0) return ray_lead_op;
if (memcmp(name, "sums", 4) == 0) return ray_sums_op;
if (memcmp(name, "avgs", 4) == 0) return ray_avgs_op;
if (memcmp(name, "mins", 4) == 0) return ray_mins_op;
if (memcmp(name, "maxs", 4) == 0) return ray_maxs_op;
if (memcmp(name, "prds", 4) == 0) return ray_prds_op;
if (memcmp(name, "asin", 4) == 0) return ray_asin_op;
if (memcmp(name, "acos", 4) == 0) return ray_acos_op;
if (memcmp(name, "atan", 4) == 0) return ray_atan_op;
if (memcmp(name, "ceil", 4) == 0) return ray_ceil_op;
if (memcmp(name, "sqrt", 4) == 0) return ray_sqrt_op;
if (memcmp(name, "trim", 4) == 0) return ray_trim_op;
if (memcmp(name, "nil?", 4) == 0) return ray_isnull;
} else if (len == 5) {
if (memcmp(name, "fills", 5) == 0) return ray_fills_op;
if (memcmp(name, "floor", 5) == 0) return ray_floor_op;
if (memcmp(name, "round", 5) == 0) return ray_round_op;
if (memcmp(name, "upper", 5) == 0) return ray_upper;
if (memcmp(name, "lower", 5) == 0) return ray_lower;
} else if (len == 6) {
if (memcmp(name, "deltas", 6) == 0) return ray_deltas_op;
if (memcmp(name, "ratios", 6) == 0) return ray_ratios_op;
if (memcmp(name, "differ", 6) == 0) return ray_differ_op;
if (memcmp(name, "strlen", 6) == 0) return ray_strlen;
if (memcmp(name, "signum", 6) == 0) return ray_signum_op;
} else if (len == 10) {
if (memcmp(name, "reciprocal", 10) == 0) return ray_reciprocal_op;
}
return NULL;
}
static uint16_t resolve_moving_opcode(int64_t sym_id) {
ray_t* s = ray_sym_str(sym_id);
if (!s) return 0;
const char* name = ray_str_ptr(s);
size_t len = ray_str_len(s);
if (len == 4) {
if (memcmp(name, "msum", 4) == 0) return OP_MSUM;
if (memcmp(name, "mavg", 4) == 0) return OP_MAVG;
if (memcmp(name, "mmin", 4) == 0) return OP_MMIN;
if (memcmp(name, "mmax", 4) == 0) return OP_MMAX;
if (memcmp(name, "mvar", 4) == 0) return OP_MVAR;
if (memcmp(name, "mdev", 4) == 0) return OP_MDEV;
} else if (len == 6) {
if (memcmp(name, "mcount", 6) == 0) return OP_MCOUNT;
}
return 0;
}
static bool const_op_i64(ray_graph_t* g, ray_op_t* op, int64_t* out) {
if (!op || op->opcode != OP_CONST) return false;
ray_op_ext_t* ext = find_ext(g, op->id);
if (!ext || !ext->literal || RAY_IS_ERR(ext->literal)) return false;
ray_t* v = ext->literal;
switch (v->type) {
case -RAY_I64: *out = v->i64; return !RAY_ATOM_IS_NULL(v);
case -RAY_I32: *out = (int64_t)v->i32; return !RAY_ATOM_IS_NULL(v);
case -RAY_I16: *out = (int64_t)v->i16; return !RAY_ATOM_IS_NULL(v);
case -RAY_U8:
case -RAY_BOOL: *out = (int64_t)v->u8; return !RAY_ATOM_IS_NULL(v);
default: return false;
}
}
static uint16_t resolve_agg_opcode(int64_t sym_id) {
ray_t* s = ray_sym_str(sym_id);
if (!s) return 0;
const char* name = ray_str_ptr(s);
size_t len = ray_str_len(s);
if (len == 3 && memcmp(name, "sum", 3) == 0) return OP_SUM;
if (len == 3 && memcmp(name, "avg", 3) == 0) return OP_AVG;
if (len == 3 && memcmp(name, "min", 3) == 0) return OP_MIN;
if (len == 3 && memcmp(name, "max", 3) == 0) return OP_MAX;
if (len == 3 && memcmp(name, "dev", 3) == 0) return OP_STDDEV_POP;
if (len == 3 && memcmp(name, "var", 3) == 0) return OP_VAR;
if (len == 3 && memcmp(name, "all", 3) == 0) return OP_ALL;
if (len == 3 && memcmp(name, "any", 3) == 0) return OP_ANY;
if (len == 3 && memcmp(name, "cov", 3) == 0) return OP_COV;
if (len == 4 && memcmp(name, "prod", 4) == 0) return OP_PROD;
if (len == 4 && memcmp(name, "last", 4) == 0) return OP_LAST;
if (len == 4 && memcmp(name, "scov", 4) == 0) return OP_SCOV;
if (len == 4 && memcmp(name, "wsum", 4) == 0) return OP_WSUM;
if (len == 4 && memcmp(name, "wavg", 4) == 0) return OP_WAVG;
if (len == 5 && memcmp(name, "count", 5) == 0) return OP_COUNT;
if (len == 5 && memcmp(name, "first", 5) == 0) return OP_FIRST;
if (len == 6 && memcmp(name, "stddev",6) == 0) return OP_STDDEV;
if (len == 7 && memcmp(name, "dev_pop", 7) == 0) return OP_STDDEV_POP;
if (len == 7 && memcmp(name, "var_pop", 7) == 0) return OP_VAR_POP;
if (len == 10 && memcmp(name, "stddev_pop", 10) == 0) return OP_STDDEV_POP;
if (len == 12 && memcmp(name, "pearson_corr", 12) == 0) return OP_PEARSON_CORR;
if (len == 3 && memcmp(name, "med", 3) == 0) return OP_MEDIAN;
if (len == 6 && memcmp(name, "median", 6) == 0) return OP_MEDIAN;
if (len == 3 && memcmp(name, "top", 3) == 0) return OP_TOP_N;
if (len == 3 && memcmp(name, "bot", 3) == 0) return OP_BOT_N;
if (len == 4 && memcmp(name, "mode", 4) == 0) return OP_MODE;
if (len == 8 && memcmp(name, "quantile", 8) == 0) return OP_QUANTILE;
if (len == 10 && memcmp(name, "percentile", 10) == 0) return OP_QUANTILE;
return 0;
}
static ray_t* select_aggs_from_metadata(ray_t* tbl, ray_t** dict_elems, int64_t dict_n,
int64_t from_id) {
int64_t nrows = ray_table_nrows(tbl);
if (nrows <= 0) return NULL;
int64_t n_out = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
if (dict_elems[i]->i64 == from_id) continue;
ray_t* e = dict_elems[i + 1];
if (!e || e->type != RAY_LIST || ray_len(e) != 2) return NULL;
ray_t** el = (ray_t**)ray_data(e);
if (el[0]->type != -RAY_SYM || (el[0]->attrs & ATTR_QUOTED)) return NULL;
if (el[1]->type != -RAY_SYM || (el[1]->attrs & ATTR_QUOTED)) return NULL;
ray_t* col = ray_table_get_col(tbl, el[1]->i64);
if (!col || !ray_is_vec(col) || RAY_IS_PARTED(col->type) || col->type == RAY_MAPCOMMON ||
(col->attrs & RAY_ATTR_SLICE) || col->len != nrows)
return NULL;
uint16_t op = resolve_agg_opcode(el[0]->i64);
bool int_col = col->type == RAY_I64 || col->type == RAY_I32 ||
col->type == RAY_I16 || col->type == RAY_U8;
int64_t zs, zn, zh; uint64_t zl;
switch (op) {
case OP_COUNT: break;
case OP_MIN: case OP_MAX: {
if (ray_index_kind(col) != RAY_IDX_CHUNK_ZONE) return NULL;
ray_index_t* ix = ray_index_payload(col->index);
if (ix->built_for_len != col->len || ix->u.chunk_zone.is_f64 ||
!ix->u.chunk_zone.mins || !ix->u.chunk_zone.maxs) return NULL;
break;
}
case OP_SUM:
if (!int_col || !ray_zone_int_sum(col, &zs, &zn)) return NULL;
break;
case OP_AVG:
if (!int_col || !ray_zone_int_sum128(col, &zh, &zl, &zn)) return NULL;
break;
default: return NULL;
}
n_out++;
}
if (n_out == 0) return NULL;
ray_t* res = ray_table_new(n_out);
if (!res || RAY_IS_ERR(res)) return NULL;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id) continue;
ray_t** el = (ray_t**)ray_data(dict_elems[i + 1]);
ray_t* col = ray_table_get_col(tbl, el[1]->i64);
uint16_t op = resolve_agg_opcode(el[0]->i64);
ray_t* atom = op == OP_COUNT ? ray_i64(nrows)
: op == OP_MIN ? ray_min_fn(col)
: op == OP_MAX ? ray_max_fn(col)
: op == OP_SUM ? ray_sum_fn(col)
: ray_avg_fn(col);
if (!atom || RAY_IS_ERR(atom) || !ray_is_atom(atom)) {
if (atom && RAY_IS_ERR(atom)) ray_error_free(atom); else if (atom) ray_release(atom);
ray_release(res);
return NULL;
}
ray_t* v = ray_vec_new(-atom->type, 1);
if (v && !RAY_IS_ERR(v)) v = ray_vec_append(v, &atom->i64);
if (v && !RAY_IS_ERR(v) && RAY_ATOM_IS_NULL(atom)) ray_vec_set_null(v, 0, true);
ray_release(atom);
if (!v || RAY_IS_ERR(v)) { ray_release(res); return NULL; }
res = ray_table_add_col(res, kid, v);
ray_release(v);
if (!res || RAY_IS_ERR(res)) return NULL;
}
return res;
}
static bool agg_name_is_percentile(int64_t sym_id) {
ray_t* s = ray_sym_str(sym_id);
return s && ray_str_len(s) == 10 &&
memcmp(ray_str_ptr(s), "percentile", 10) == 0;
}
static int64_t encode_agg_f64_param(double v) {
int64_t bits = 0;
memcpy(&bits, &v, sizeof(bits));
return bits;
}
static bool quantile_literal_prob(ray_t* prob_expr, bool percentile,
double* out_q) {
if (!prob_expr || !ray_is_atom(prob_expr) ||
(prob_expr->type == -RAY_SYM && !(prob_expr->attrs & ATTR_QUOTED)) ||
!is_numeric(prob_expr) || RAY_ATOM_IS_NULL(prob_expr))
return false;
double raw = as_f64(prob_expr);
double scale = percentile ? 100.0 : 1.0;
if (!__builtin_isfinite(raw) || raw < 0.0 || raw > scale)
return false;
if (out_q) *out_q = raw / scale;
return true;
}
static bool group_keys_have_bool(ray_t* by_expr, ray_t* tbl) {
if (!by_expr || !tbl || RAY_IS_ERR(tbl) || tbl->type != RAY_TABLE) return true;
if (by_expr->type == -RAY_SYM && !(by_expr->attrs & ATTR_QUOTED)) {
ray_t* c = ray_table_get_col(tbl, by_expr->i64);
return !c || c->type == RAY_BOOL;
}
if (ray_is_vec(by_expr) && by_expr->type == RAY_SYM) {
const int64_t* ids = (const int64_t*)ray_data(by_expr);
for (int64_t i = 0; i < ray_len(by_expr); i++) {
ray_t* c = ray_table_get_col(tbl, ids[i]);
if (!c || c->type == RAY_BOOL) return true;
}
return false;
}
return true;
}
static bool table_is_parted(ray_t* tbl) {
if (!tbl || RAY_IS_ERR(tbl) || tbl->type != RAY_TABLE) return true;
int64_t nc = ray_table_ncols(tbl);
for (int64_t c = 0; c < nc; c++) {
ray_t* col = ray_table_get_col_idx(tbl, c);
if (col && (RAY_IS_PARTED(col->type) || col->type == RAY_MAPCOMMON))
return true;
}
return false;
}
typedef enum { TAKE_PRE_NONE = 0, TAKE_PRE_ATOM, TAKE_PRE_RANGE } take_pre_kind_t;
typedef struct {
take_pre_kind_t kind;
int64_t a, b;
} take_pre_t;
static ray_t* apply_sort_take(ray_t* result, ray_t** dict_elems, int64_t dict_n,
int64_t asc_id, int64_t desc_id, int64_t take_id,
const take_pre_t* take_pre) {
if (!result || RAY_IS_ERR(result)) return result;
bool has_sort = false;
ray_t* take_val_expr = NULL;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == asc_id || kid == desc_id) has_sort = true;
if (kid == take_id) take_val_expr = dict_elems[i + 1];
}
if (!has_sort && !take_val_expr) return result;
if (!has_sort && take_val_expr) {
ray_t* tv;
if (take_pre && take_pre->kind == TAKE_PRE_ATOM) {
tv = ray_i64(take_pre->a);
} else if (take_pre && take_pre->kind == TAKE_PRE_RANGE) {
tv = ray_vec_new(RAY_I64, 2);
if (tv && !RAY_IS_ERR(tv)) {
((int64_t*)ray_data(tv))[0] = take_pre->a;
((int64_t*)ray_data(tv))[1] = take_pre->b;
tv->len = 2;
}
} else {
tv = ray_eval(take_val_expr);
}
if (!tv || RAY_IS_ERR(tv)) {
ray_release(result);
return tv ? tv : ray_error("domain", "select: failed to evaluate `take:`");
}
if (ray_is_atom(tv) && (tv->type == -RAY_I64 || tv->type == -RAY_I32)) {
int64_t atom_n = (tv->type == -RAY_I64) ? tv->i64 : tv->i32;
ray_release(tv);
if (atom_n == INT64_MIN) {
ray_release(result);
return ray_error("type", "select: take: count is null or out of range");
}
int64_t nrows = (result->type == RAY_TABLE)
? ray_table_nrows(result)
: (ray_is_vec(result) ? result->len : 0);
int64_t start, amount;
if (atom_n >= 0) {
start = 0;
amount = atom_n < nrows ? atom_n : nrows;
} else {
int64_t want = -atom_n;
amount = want < nrows ? want : nrows;
start = nrows - amount;
}
ray_t* rng = ray_vec_new(RAY_I64, 2);
if (!rng || RAY_IS_ERR(rng)) {
ray_release(result);
return rng ? rng : ray_error("oom", NULL);
}
((int64_t*)ray_data(rng))[0] = start;
((int64_t*)ray_data(rng))[1] = amount;
rng->len = 2;
ray_t* sliced = ray_take_fn(result, rng);
ray_release(result);
ray_release(rng);
return sliced;
}
if (ray_is_vec(tv) && (tv->type == RAY_I64 || tv->type == RAY_I32) && tv->len == 2) {
ray_t* sliced = ray_take_fn(result, tv);
ray_release(result);
ray_release(tv);
return sliced;
}
int8_t tv_t = tv->type;
ray_release(tv);
ray_release(result);
return ray_error("domain", "select: `take:` must be an integer atom or a 2-element integer range, got %s", ray_type_name(tv_t));
}
if (has_sort && take_val_expr && result->type == RAY_TABLE) {
enum { TOPK_MAX_KEYS = 16 };
int64_t key_syms[TOPK_MAX_KEYS];
uint8_t key_descs[TOPK_MAX_KEYS];
uint8_t n_keys = 0;
int bad_clause = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
uint8_t is_desc = 0;
if (kid == asc_id) is_desc = 0;
else if (kid == desc_id) is_desc = 1;
else continue;
ray_t* val = dict_elems[i + 1];
if (!val) { bad_clause = 1; break; }
if (val->type == -RAY_SYM) {
if (n_keys >= TOPK_MAX_KEYS) { bad_clause = 1; break; }
key_syms[n_keys] = val->i64;
key_descs[n_keys] = is_desc;
n_keys++;
} else if (ray_is_vec(val) && val->type == RAY_SYM) {
for (int64_t c = 0; c < val->len; c++) {
if (n_keys >= TOPK_MAX_KEYS) { bad_clause = 1; break; }
key_syms[n_keys] = sym_cell_runtime_id(val, c);
key_descs[n_keys] = is_desc;
n_keys++;
}
if (bad_clause) break;
} else {
bad_clause = 1;
break;
}
}
if (!bad_clause && n_keys > 0) {
ray_t* tv = ray_eval(take_val_expr);
if (tv && !RAY_IS_ERR(tv) && ray_is_atom(tv) &&
(tv->type == -RAY_I64 || tv->type == -RAY_I32)) {
int64_t k = (tv->type == -RAY_I64) ? tv->i64 : tv->i32;
ray_release(tv);
int64_t nrows = ray_table_nrows(result);
if (k > 0 && k < nrows && k <= FPK_MAX_K) {
int has_list = 0;
int64_t ncols = ray_table_ncols(result);
for (int64_t c = 0; c < ncols; c++) {
ray_t* col = ray_table_get_col_idx(result, c);
if (col && col->type == RAY_LIST) { has_list = 1; break; }
}
if (!has_list) {
ray_t* topk = NULL;
if (n_keys == 1) {
ray_t* sort_col = ray_table_get_col(result, key_syms[0]);
if (sort_col) {
topk = ray_topk_table(result, sort_col,
key_descs[0],
sort_nulls_first(key_descs[0]), k);
}
} else {
ray_t* key_cols[TOPK_MAX_KEYS];
uint8_t nfs[TOPK_MAX_KEYS];
int ok = 1;
for (uint32_t i = 0; i < n_keys; i++) {
key_cols[i] = ray_table_get_col(result, key_syms[i]);
nfs[i] = sort_nulls_first(key_descs[i]);
if (!key_cols[i]) { ok = 0; break; }
}
if (ok) {
topk = ray_topk_table_multi(result, key_cols,
key_descs, nfs, n_keys, k);
}
}
if (topk && !RAY_IS_ERR(topk)) {
ray_release(result);
return topk;
}
if (topk && RAY_IS_ERR(topk)) ray_release(topk);
}
}
} else if (tv) {
ray_release(tv);
}
}
}
ray_graph_t* g = ray_graph_new(result);
if (!g) return result;
ray_op_t* root = ray_const_table(g, result);
if (has_sort) {
int64_t n_sort_max = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid != asc_id && kid != desc_id) continue;
ray_t* val = dict_elems[i + 1];
if (val->type == -RAY_SYM) n_sort_max += 1;
else if (ray_is_vec(val) && val->type == RAY_SYM) n_sort_max += ray_len(val);
}
if (n_sort_max < 1) n_sort_max = 1;
ray_t* sortk_hdr = NULL;
ray_op_t** sort_keys = (ray_op_t**)scratch_alloc(&sortk_hdr,
(size_t)n_sort_max * (sizeof(ray_op_t*) + 1));
if (!sort_keys) { ray_graph_free(g); ray_release(result); return ray_error("oom", NULL); }
uint8_t* sort_descs = (uint8_t*)(sort_keys + n_sort_max);
int64_t n_sort = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
uint8_t is_desc = 0;
if (kid == asc_id) is_desc = 0;
else if (kid == desc_id) is_desc = 1;
else continue;
ray_t* val = dict_elems[i + 1];
if (val->type == -RAY_SYM) {
ray_t* s = ray_sym_str(val->i64);
sort_keys[n_sort] = ray_scan(g, ray_str_ptr(s));
sort_descs[n_sort] = is_desc;
n_sort++;
} else if (ray_is_vec(val) && val->type == RAY_SYM) {
for (int64_t c = 0; c < val->len; c++) {
ray_t* s = ray_sym_vec_cell(val, c);
sort_keys[n_sort] = ray_scan(g, ray_str_ptr(s));
sort_descs[n_sort] = is_desc;
n_sort++;
}
}
}
if (n_sort > 0)
root = ray_sort_op(g, root, sort_keys, sort_descs, NULL, n_sort);
scratch_free(sortk_hdr);
}
ray_t* take_range = NULL;
int take_is_atom = 0;
int64_t atom_n = 0;
if (take_val_expr) {
ray_t* tv = ray_eval(take_val_expr);
if (!tv || RAY_IS_ERR(tv)) {
ray_graph_free(g); ray_release(result);
return tv ? tv : ray_error("domain", "select: failed to evaluate `take:`");
}
if (ray_is_atom(tv) && (tv->type == -RAY_I64 || tv->type == -RAY_I32)) {
atom_n = (tv->type == -RAY_I64) ? tv->i64 : tv->i32;
take_is_atom = 1;
ray_release(tv);
} else if (ray_is_vec(tv) && (tv->type == RAY_I64 || tv->type == RAY_I32) && tv->len == 2) {
take_range = tv;
} else {
int8_t tv_t = tv->type;
ray_release(tv); ray_graph_free(g); ray_release(result);
return ray_error("domain", "select: `take:` must be an integer atom or a 2-element integer range, got %s", ray_type_name(tv_t));
}
}
root = ray_optimize(g, root);
ray_t* sorted = ray_execute(g, root);
ray_graph_free(g);
ray_release(result);
if (take_is_atom && sorted && !RAY_IS_ERR(sorted)) {
int64_t nrows = (sorted->type == RAY_TABLE)
? ray_table_nrows(sorted)
: (ray_is_vec(sorted) ? sorted->len : 0);
if (atom_n == INT64_MIN) {
ray_release(sorted);
return ray_error("type", "select: take: count is null or out of range");
}
int64_t start, amount;
if (atom_n >= 0) {
start = 0;
amount = atom_n < nrows ? atom_n : nrows;
} else {
int64_t want = -atom_n;
amount = want < nrows ? want : nrows;
start = nrows - amount;
}
ray_t* rng = ray_vec_new(RAY_I64, 2);
if (!rng || RAY_IS_ERR(rng)) {
ray_release(sorted);
return rng ? rng : ray_error("oom", NULL);
}
((int64_t*)ray_data(rng))[0] = start;
((int64_t*)ray_data(rng))[1] = amount;
rng->len = 2;
ray_t* sliced = ray_take_fn(sorted, rng);
ray_release(sorted);
ray_release(rng);
return sliced;
}
if (take_range && sorted && !RAY_IS_ERR(sorted)) {
ray_t* sliced = ray_take_fn(sorted, take_range);
ray_release(sorted);
ray_release(take_range);
return sliced;
}
if (take_range) ray_release(take_range);
return sorted;
}
static bool unsorted_positive_take_limit(ray_t** dict_elems, int64_t dict_n,
int64_t asc_id, int64_t desc_id,
int64_t take_id, int64_t nrows,
int64_t* out_nrows) {
bool has_sort = false;
ray_t* take_val_expr = NULL;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == asc_id || kid == desc_id) has_sort = true;
if (kid == take_id) take_val_expr = dict_elems[i + 1];
}
if (has_sort || !take_val_expr) return false;
ray_t* tv = ray_eval(take_val_expr);
if (!tv || RAY_IS_ERR(tv)) {
if (tv && !RAY_IS_ERR(tv)) ray_release(tv);
return false;
}
bool ok = false;
int64_t limit = nrows;
if (ray_is_atom(tv) && (tv->type == -RAY_I64 || tv->type == -RAY_I32)) {
int64_t k = (tv->type == -RAY_I64) ? tv->i64 : tv->i32;
if (k >= 0) {
limit = k < nrows ? k : nrows;
ok = true;
}
}
ray_release(tv);
if (ok && out_nrows) *out_nrows = limit;
return ok;
}
static ray_op_t* cexpr_env_lookup(ray_graph_t* g, int64_t sym) {
for (int i = g->cexpr_env_top - 1; i >= 0; i--)
if (g->cexpr_env[i].sym == sym)
return &g->nodes[g->cexpr_env[i].node_id];
return NULL;
}
static bool cexpr_env_push(ray_graph_t* g, int64_t sym, ray_op_t* node) {
if (g->cexpr_env_top >= 32) return false;
g->cexpr_env[g->cexpr_env_top].sym = sym;
g->cexpr_env[g->cexpr_env_top].node_id = node->id;
g->cexpr_env_top++;
return true;
}
static void cexpr_env_pop(ray_graph_t* g, int n) {
g->cexpr_env_top -= n;
if (g->cexpr_env_top < 0) g->cexpr_env_top = 0;
}
static ray_op_t* sel_alias_lookup(ray_graph_t* g, int64_t sym) {
for (int i = g->sel_alias_n - 1; i >= 0; i--)
if (g->sel_alias_syms[i] == sym)
return &g->nodes[g->sel_alias_ids[i]];
return NULL;
}
static ray_op_t* select_sort_key_op(ray_graph_t* g, ray_op_t* root, int64_t sym, const char* name) {
if (root && root->opcode == OP_SELECT) {
ray_op_ext_t* se = find_ext(g, root->id);
if (se && se->base.opcode == OP_SELECT)
for (uint32_t c = 0; c < se->sort.n_cols; c++) {
ray_op_t* col = &g->nodes[se->sort.columns[c]];
if (col->opcode != OP_SCAN) continue;
ray_op_ext_t* ce = find_ext(g, col->id);
if (ce && ce->base.opcode == OP_SCAN && ce->sym == sym) return col;
}
}
return ray_scan(g, name);
}
static int64_t select_scan_output(ray_graph_t* g, ray_op_t** col_ops, int64_t nc, int64_t sym) {
for (int64_t c = 0; c < nc; c++) {
if (!col_ops[c] || col_ops[c]->opcode != OP_SCAN) continue;
ray_op_ext_t* ce = find_ext(g, col_ops[c]->id);
if (ce && ce->base.opcode == OP_SCAN && ce->sym == sym) return c;
}
return -1;
}
static ray_t* graph_take_compile_err(ray_graph_t* g) {
ray_t* e = g->compile_err;
g->compile_err = NULL;
return e;
}
static int const_str_expr_len(ray_t* expr, size_t* out_len) {
if (!expr || !out_len) return 0;
if (expr->type == -RAY_STR && !(expr->attrs & ATTR_QUOTED)) {
*out_len += ray_str_len(expr);
return 1;
}
if (expr->type != RAY_LIST || ray_len(expr) < 2) return 0;
ray_t** elems = (ray_t**)ray_data(expr);
if (!elems[0] || elems[0]->type != -RAY_SYM) return 0;
ray_t* head = ray_sym_str(elems[0]->i64);
if (!head || ray_str_len(head) != 6
|| memcmp(ray_str_ptr(head), "concat", 6) != 0)
return 0;
for (int64_t i = 1; i < ray_len(expr); i++)
if (!const_str_expr_len(elems[i], out_len)) return 0;
return 1;
}
static void const_str_expr_copy(ray_t* expr, char* dst, size_t* off) {
if (expr->type == -RAY_STR) {
size_t len = ray_str_len(expr);
memcpy(dst + *off, ray_str_ptr(expr), len);
*off += len;
return;
}
ray_t** elems = (ray_t**)ray_data(expr);
for (int64_t i = 1; i < ray_len(expr); i++)
const_str_expr_copy(elems[i], dst, off);
}
static ray_op_t* compile_const_str_expr(ray_graph_t* g, ray_t* expr) {
size_t len = 0;
if (!const_str_expr_len(expr, &len)) return NULL;
if (len == 0) return ray_const_str(g, "", 0);
char stack_buf[256];
char* buf = stack_buf;
ray_t* heap_buf = NULL;
if (len > sizeof(stack_buf)) {
heap_buf = ray_alloc(len);
if (!heap_buf) return NULL;
buf = (char*)ray_data(heap_buf);
}
size_t off = 0;
const_str_expr_copy(expr, buf, &off);
ray_op_t* out = ray_const_str(g, buf, len);
if (heap_buf) ray_release(heap_buf);
return out;
}
ray_op_t* compile_expr_dag(ray_graph_t* g, ray_t* expr) {
if (!expr) return NULL;
if (expr->type == -RAY_I64 && !RAY_ATOM_IS_NULL(expr))
return ray_const_i64(g, expr->i64);
if (expr->type == -RAY_F64 && !RAY_ATOM_IS_NULL(expr))
return ray_const_f64(g, expr->f64);
if (expr->type == -RAY_BOOL && !RAY_ATOM_IS_NULL(expr))
return ray_const_bool(g, expr->b8);
if (expr->type == -RAY_STR && !RAY_ATOM_IS_NULL(expr)) {
const char *ptr = ray_str_ptr(expr);
size_t len = ray_str_len(expr);
return ray_const_str(g, ptr, len);
}
if (expr->type == -RAY_SYM && !(expr->attrs & ATTR_QUOTED)) {
ray_op_t* bound = cexpr_env_lookup(g, expr->i64);
if (bound) return bound;
ray_op_t* alias = sel_alias_lookup(g, expr->i64);
if (alias) return alias;
ray_t* local = ray_env_get_lexical_local(expr->i64);
if (local) {
if (ray_is_atom(local)) return ray_const_atom(g, local);
if (ray_is_vec(local)) return ray_const_vec(g, local);
return NULL;
}
ray_t* s = ray_sym_str(expr->i64);
if (!s) return NULL;
if (ray_sym_is_dotted(expr->i64)) {
const int64_t* segs;
int nsegs = ray_sym_segs(expr->i64, &segs);
if (nsegs < 2) return NULL;
if (!g->table || g->table->type != RAY_TABLE) return NULL;
if (!ray_table_get_col(g->table, segs[0])) return NULL;
ray_t* head_name = ray_sym_str(segs[0]);
if (!head_name) return NULL;
ray_op_t* op = ray_scan(g, ray_str_ptr(head_name));
if (!op) return NULL;
for (int i = 1; i < nsegs; i++) {
int field = ray_temporal_field_from_sym(segs[i]);
if (field >= 0) {
op = ray_extract(g, op, field);
if (!op) return NULL;
continue;
}
int trunc_kind = ray_temporal_trunc_from_sym(segs[i]);
if (trunc_kind >= 0) {
op = ray_date_trunc(g, op, trunc_kind);
if (!op) return NULL;
continue;
}
return NULL;
}
return op;
}
if (g->table && g->table->type == RAY_TABLE &&
ray_table_get_col(g->table, expr->i64))
return ray_scan(g, ray_str_ptr(s));
ray_t* gv = ray_env_get(expr->i64);
if (gv) {
if (ray_is_atom(gv)) return ray_const_atom(g, gv);
if (ray_is_vec(gv)) return ray_const_vec(g, gv);
}
ray_op_t* missing = ray_scan(g, ray_str_ptr(s));
int64_t dist_sym = ray_sym_find("_dist", 5);
bool exempt = false;
if (missing && expr->i64 == dist_sym) {
for (uint32_t i = 0; i < g->node_count && !exempt; i++)
if (g->nodes[i].opcode == OP_ANN_RERANK ||
g->nodes[i].opcode == OP_KNN_RERANK)
exempt = true;
}
if (missing && !exempt) {
missing->flags |= OP_FLAG_INVALID_SCAN;
ray_op_ext_t* ext = find_ext(g, missing->id);
if (ext) ext->base.flags |= OP_FLAG_INVALID_SCAN;
}
return missing;
}
if (expr->type == -RAY_SYM) {
ray_op_t* alias = sel_alias_lookup(g, expr->i64);
if (alias) return alias;
if (g->table && g->table->type == RAY_TABLE &&
ray_table_get_col(g->table, expr->i64)) {
ray_t* s = ray_sym_str(expr->i64);
if (s) return ray_scan(g, ray_str_ptr(s));
}
return ray_const_atom(g, expr);
}
if (ray_is_atom(expr) && !(expr->attrs & ATTR_QUOTED))
return ray_const_atom(g, expr);
if (ray_is_vec(expr) && !(expr->attrs & ATTR_QUOTED))
return ray_const_vec(g, expr);
if (expr->type == RAY_LIST) {
int64_t n = ray_len(expr);
if (n == 0) return NULL;
ray_t** elems = (ray_t**)ray_data(expr);
ray_t* head = elems[0];
if (head->type == RAY_LIST) {
int64_t hn = ray_len(head);
if (hn != 3) return NULL;
ray_t** hel = (ray_t**)ray_data(head);
if (hel[0]->type != -RAY_SYM) return NULL;
ray_t* hname_str = ray_sym_str(hel[0]->i64);
if (!hname_str || ray_str_len(hname_str) != 2 ||
memcmp(ray_str_ptr(hname_str), "fn", 2) != 0) return NULL;
ray_t* formals = hel[1];
ray_t* body = hel[2];
if (!ray_is_vec(formals) || formals->type != RAY_SYM) return NULL;
int64_t nf = formals->len;
if (n - 1 != nf) return NULL;
if (nf > 16) return NULL;
if (g->cexpr_env_top + (int)nf > 32) return NULL;
uint32_t actual_ids[16];
for (int64_t i = 0; i < nf; i++) {
ray_op_t* a = compile_expr_dag(g, elems[i + 1]);
if (!a) return NULL;
actual_ids[i] = a->id;
}
int64_t* fids = (int64_t*)ray_data(formals);
int pushed = 0;
for (int64_t i = 0; i < nf; i++) {
if (g->cexpr_env_top >= 32) {
cexpr_env_pop(g, pushed);
return NULL;
}
g->cexpr_env[g->cexpr_env_top].sym = fids[i];
g->cexpr_env[g->cexpr_env_top].node_id = actual_ids[i];
g->cexpr_env_top++;
pushed++;
}
ray_op_t* result = compile_expr_dag(g, body);
cexpr_env_pop(g, pushed);
return result;
}
if (head->type == -RAY_SYM && !(head->attrs & ATTR_QUOTED) &&
cexpr_env_lookup(g, head->i64) == NULL &&
!(g->table && g->table->type == RAY_TABLE &&
ray_table_get_col(g->table, head->i64))) {
ray_t* gv = ray_env_get(head->i64);
if (gv && gv->type == RAY_LAMBDA) {
ray_t* formals = LAMBDA_PARAMS(gv);
ray_t* body_lst = LAMBDA_BODY(gv);
if (formals && body_lst && body_lst->type == RAY_LIST &&
ray_len(body_lst) == 1 &&
ray_is_vec(formals) && formals->type == RAY_SYM) {
int64_t nf = formals->len;
if (n - 1 == nf && nf <= 16 &&
g->cexpr_env_top + (int)nf <= 32) {
ray_t* body = ((ray_t**)ray_data(body_lst))[0];
uint32_t actual_ids[16];
for (int64_t i = 0; i < nf; i++) {
ray_op_t* a = compile_expr_dag(g, elems[i + 1]);
if (!a) return NULL;
actual_ids[i] = a->id;
}
int64_t* fids = (int64_t*)ray_data(formals);
int pushed = 0;
for (int64_t i = 0; i < nf; i++) {
g->cexpr_env[g->cexpr_env_top].sym = fids[i];
g->cexpr_env[g->cexpr_env_top].node_id = actual_ids[i];
g->cexpr_env_top++;
pushed++;
}
int saved_aliases = g->sel_alias_n;
g->sel_alias_n = 0;
ray_op_t* result = compile_expr_dag(g, body);
g->sel_alias_n = saved_aliases;
cexpr_env_pop(g, pushed);
return result;
}
}
}
}
if (head->type != -RAY_SYM) return NULL;
int64_t fn_sym = head->i64;
ray_t* fn_name_str = ray_sym_str(fn_sym);
const char* fname = fn_name_str ? ray_str_ptr(fn_name_str) : NULL;
size_t fname_len = fn_name_str ? ray_str_len(fn_name_str) : 0;
if (fname_len == 5 && memcmp(fname, "quote", 5) == 0) {
if (n != 2) return NULL;
ray_t* q = elems[1];
if (!q) return NULL;
if (q->type == -RAY_SYM) return ray_const_atom(g, q);
if (ray_is_vec(q)) return ray_const_vec(g, q);
if (ray_is_atom(q)) return ray_const_atom(g, q);
return NULL;
}
if (fname_len == 4 && memcmp(fname, "xbar", 4) == 0) {
if (n != 3) return NULL;
ray_op_t* col = compile_expr_dag(g, elems[1]);
if (!col) return NULL;
uint32_t col_id = col->id;
ray_op_t* bucket = compile_expr_dag(g, elems[2]);
if (!bucket) return NULL;
uint32_t bucket_id = bucket->id;
col = &g->nodes[col_id];
int8_t col_t = col->out_type;
if (RAY_IS_PARTED(col_t)) col_t = (int8_t)RAY_PARTED_BASETYPE(col_t);
int8_t temporal = dag_type_is_temporal(col_t) ? col_t : 0;
if (temporal) {
int8_t bt = bucket->out_type;
if (RAY_IS_PARTED(bt)) bt = (int8_t)RAY_PARTED_BASETYPE(bt);
if (bt == RAY_F64 || bt == RAY_F32) return NULL;
if (temporal == RAY_TIMESTAMP && bt == RAY_TIME) {
ray_op_t* scale = ray_const_i64(g, 1000000LL);
if (!scale) return NULL;
bucket = &g->nodes[bucket_id];
bucket = ray_mul(g, bucket, scale);
if (!bucket) return NULL;
bucket_id = bucket->id;
col = &g->nodes[col_id];
}
}
ray_op_t* m = ray_mod(g, col, bucket);
if (!m) return NULL;
col = &g->nodes[col_id];
ray_op_t* r = ray_sub(g, col, m);
if (!r || !temporal) return r;
return ray_cast(g, r, temporal);
}
if (fname_len == 2 && memcmp(fname, "if", 2) == 0) {
if (n != 4) return NULL;
ray_op_t* c = compile_expr_dag(g, elems[1]);
if (!c) return NULL;
uint32_t c_id = c->id;
g->if_arm_depth++;
ray_op_t* t = compile_expr_dag(g, elems[2]);
uint32_t t_id = t ? t->id : 0;
ray_op_t* e = t ? compile_expr_dag(g, elems[3]) : NULL;
g->if_arm_depth--;
if (!t || !e) return NULL;
c = &g->nodes[c_id];
t = &g->nodes[t_id];
return ray_if(g, c, t, e);
}
if (fname_len == 6 && memcmp(fname, "substr", 6) == 0) {
if (n != 4) return NULL;
ray_op_t* str = compile_expr_dag(g, elems[1]);
if (!str) return NULL;
uint32_t str_id = str->id;
ray_op_t* start = compile_expr_dag(g, elems[2]);
if (!start) return NULL;
uint32_t start_id = start->id;
ray_op_t* ln = compile_expr_dag(g, elems[3]);
if (!ln) return NULL;
str = &g->nodes[str_id];
start = &g->nodes[start_id];
return ray_substr(g, str, start, ln);
}
if (fname_len == 7 && memcmp(fname, "replace", 7) == 0) {
if (n != 4) return NULL;
ray_op_t* str = compile_expr_dag(g, elems[1]);
if (!str) return NULL;
uint32_t str_id = str->id;
ray_op_t* from = compile_expr_dag(g, elems[2]);
if (!from) return NULL;
uint32_t from_id = from->id;
ray_op_t* to = compile_expr_dag(g, elems[3]);
if (!to) return NULL;
str = &g->nodes[str_id];
from = &g->nodes[from_id];
return ray_replace(g, str, from, to);
}
if (fname_len == 6 && memcmp(fname, "concat", 6) == 0) {
ray_op_t* folded = compile_const_str_expr(g, expr);
if (folded) return folded;
if (n < 2 || n - 1 > 16) return NULL;
uint32_t arg_ids[16];
for (int64_t i = 1; i < n; i++) {
ray_op_t* a = compile_expr_dag(g, elems[i]);
if (!a) return NULL;
arg_ids[i - 1] = a->id;
}
ray_op_t* args[16];
for (int64_t i = 0; i < n - 1; i++)
args[i] = &g->nodes[arg_ids[i]];
return ray_concat(g, args, (int)(n - 1));
}
if (fname_len == 2 && memcmp(fname, "as", 2) == 0) {
if (n != 3) return NULL;
ray_t* type_expr = elems[1];
if (type_expr->type != -RAY_SYM) return NULL;
int8_t tgt = -1;
ray_t* ts = ray_sym_str(type_expr->i64);
if (ts) {
const char* tn = ray_str_ptr(ts);
size_t tl = ray_str_len(ts);
if (tl == 3 && memcmp(tn, "I64", 3) == 0) tgt = RAY_I64;
else if (tl == 3 && memcmp(tn, "F64", 3) == 0) tgt = RAY_F64;
else if (tl == 3 && memcmp(tn, "I32", 3) == 0) tgt = RAY_I32;
else if (tl == 3 && memcmp(tn, "I16", 3) == 0) tgt = RAY_I16;
else if (tl == 3 && memcmp(tn, "F32", 3) == 0) tgt = RAY_F32;
else if (tl == 2 && memcmp(tn, "U8", 2) == 0) tgt = RAY_U8;
else if (tl == 4 && memcmp(tn, "BOOL", 4) == 0) tgt = RAY_BOOL;
}
if (tgt < 0) return NULL;
ray_op_t* col = compile_expr_dag(g, elems[2]);
if (!col) return NULL;
return ray_cast(g, col, tgt);
}
if (fname_len == 6 && memcmp(fname, "within", 6) == 0) {
if (n != 3) return NULL;
ray_op_t* col = compile_expr_dag(g, elems[1]);
if (!col) return NULL;
uint32_t col_id = col->id;
uint32_t lo_id = 0, hi_id = 0;
ray_t* range_ast = elems[2];
int handled = 0;
if (range_ast->type == RAY_LIST && ray_len(range_ast) == 3) {
ray_t** re = (ray_t**)ray_data(range_ast);
if (re[0]->type == -RAY_SYM) {
ray_t* hs = ray_sym_str(re[0]->i64);
size_t hl = hs ? ray_str_len(hs) : 0;
const char* hp = hs ? ray_str_ptr(hs) : NULL;
if ((hl == 4 && memcmp(hp, "list", 4) == 0) ||
(hl == 6 && memcmp(hp, "enlist", 6) == 0)) {
ray_op_t* lo = compile_expr_dag(g, re[1]);
if (!lo) return NULL;
lo_id = lo->id;
ray_op_t* hi = compile_expr_dag(g, re[2]);
if (!hi) return NULL;
hi_id = hi->id;
handled = 1;
}
}
}
if (!handled) {
ray_op_t* rng = compile_expr_dag(g, range_ast);
if (!rng || rng->opcode != OP_CONST) return NULL;
ray_op_ext_t* rext = find_ext(g, rng->id);
if (!rext || !rext->literal) return NULL;
ray_t* rv = rext->literal;
if (!ray_is_vec(rv) || rv->len != 2) return NULL;
ray_t* lo_idx = ray_i64(0);
ray_t* hi_idx = ray_i64(1);
ray_t* lo_atom = ray_at_fn(rv, lo_idx);
ray_t* hi_atom = ray_at_fn(rv, hi_idx);
ray_release(lo_idx);
ray_release(hi_idx);
if (!lo_atom || RAY_IS_ERR(lo_atom) ||
!hi_atom || RAY_IS_ERR(hi_atom)) {
if (lo_atom) ray_release(lo_atom);
if (hi_atom) ray_release(hi_atom);
return NULL;
}
ray_op_t* lo = ray_const_atom(g, lo_atom);
ray_op_t* hi = ray_const_atom(g, hi_atom);
ray_release(lo_atom);
ray_release(hi_atom);
if (!lo || !hi) return NULL;
lo_id = lo->id;
hi_id = hi->id;
}
ray_op_t* ge = ray_ge(g, &g->nodes[col_id], &g->nodes[lo_id]);
if (!ge) return NULL;
uint32_t ge_id = ge->id;
ray_op_t* le = ray_le(g, &g->nodes[col_id], &g->nodes[hi_id]);
if (!le) return NULL;
return ray_and(g, &g->nodes[ge_id], le);
}
if (n == 2) {
int64_t field = -1;
if (fname_len == 4 && memcmp(fname, "year", 4) == 0) field = RAY_EXTRACT_YEAR;
else if (fname_len == 5 && memcmp(fname, "month", 5) == 0) field = RAY_EXTRACT_MONTH;
else if (fname_len == 3 && memcmp(fname, "day", 3) == 0) field = RAY_EXTRACT_DAY;
else if (fname_len == 4 && memcmp(fname, "hour", 4) == 0) field = RAY_EXTRACT_HOUR;
else if (fname_len == 6 && memcmp(fname, "minute",6) == 0) field = RAY_EXTRACT_MINUTE;
else if (fname_len == 6 && memcmp(fname, "second",6) == 0) field = RAY_EXTRACT_SECOND;
else if (fname_len == 9 && memcmp(fname, "dayofweek",9) == 0) field = RAY_EXTRACT_DOW;
else if (fname_len == 9 && memcmp(fname, "dayofyear",9) == 0) field = RAY_EXTRACT_DOY;
if (field >= 0) {
ray_op_t* col = compile_expr_dag(g, elems[1]);
if (!col) return NULL;
return ray_extract(g, col, field);
}
}
if (fname_len == 2 && memcmp(fname, "do", 2) == 0) {
if (n < 2) return NULL;
return compile_expr_dag(g, elems[n - 1]);
}
if (fname_len == 3 && memcmp(fname, "let", 3) == 0) {
if (n != 4) return NULL;
ray_t* var_expr = elems[1];
if (var_expr->type != -RAY_SYM) return NULL;
int64_t var_sym = var_expr->i64;
ray_op_t* val_op = compile_expr_dag(g, elems[2]);
if (!val_op) return NULL;
if (!cexpr_env_push(g, var_sym, val_op)) return NULL;
ray_op_t* body_op = compile_expr_dag(g, elems[3]);
cexpr_env_pop(g, 1);
return body_op;
}
if (fname_len == 4 && memcmp(fname, "cond", 4) == 0) {
if (n < 2) return NULL;
uint32_t chain_id = UINT32_MAX;
for (int64_t i = n - 1; i >= 1; i--) {
ray_t* clause = elems[i];
if (clause->type != RAY_LIST || ray_len(clause) != 2) return NULL;
ray_t** cpair = (ray_t**)ray_data(clause);
int is_else = 0;
if (cpair[0]->type == -RAY_SYM) {
ray_t* ns = ray_sym_str(cpair[0]->i64);
if (ns && ray_str_len(ns) == 4 &&
memcmp(ray_str_ptr(ns), "else", 4) == 0)
is_else = 1;
}
if (is_else) {
if (i != n - 1) return NULL;
g->if_arm_depth++;
ray_op_t* c = compile_expr_dag(g, cpair[1]);
g->if_arm_depth--;
if (!c) return NULL;
chain_id = c->id;
} else {
if (chain_id == UINT32_MAX) return NULL;
ray_op_t* pred = compile_expr_dag(g, cpair[0]);
if (!pred) return NULL;
uint32_t pred_id = pred->id;
g->if_arm_depth++;
ray_op_t* body = compile_expr_dag(g, cpair[1]);
g->if_arm_depth--;
if (!body) return NULL;
pred = &g->nodes[pred_id];
ray_op_t* chain = &g->nodes[chain_id];
ray_op_t* r = ray_if(g, pred, body, chain);
if (!r) return NULL;
chain_id = r->id;
}
}
if (chain_id == UINT32_MAX) return NULL;
return &g->nodes[chain_id];
}
if (n == 2) {
bool is_and1 = (fname_len == 3 && memcmp(fname, "and", 3) == 0);
bool is_or1 = (fname_len == 2 && memcmp(fname, "or", 2) == 0);
if (is_and1 || is_or1) {
return compile_expr_dag(g, elems[1]);
}
}
if (n >= 4) {
bool is_and = (fname_len == 3 && memcmp(fname, "and", 3) == 0);
bool is_or = (fname_len == 2 && memcmp(fname, "or", 2) == 0);
if (is_and || is_or) {
int64_t k = n - 1;
if (k > 64) return NULL;
uint32_t arg_ids[64];
for (int64_t i = 0; i < k; i++) {
ray_op_t* a = compile_expr_dag(g, elems[i + 1]);
if (!a) return NULL;
if (!dag_bool_operand_ok(g, a, is_and)) return NULL;
arg_ids[i] = a->id;
}
dag_binary_ctor ctor = is_and ? ray_and : ray_or;
int64_t cnt = k;
while (cnt > 1) {
int64_t out = 0;
for (int64_t i = 0; i + 1 < cnt; i += 2) {
ray_op_t* l = &g->nodes[arg_ids[i]];
ray_op_t* r = &g->nodes[arg_ids[i + 1]];
ray_op_t* combined = ctor(g, l, r);
if (!combined) return NULL;
arg_ids[out++] = combined->id;
}
if (cnt & 1)
arg_ids[out++] = arg_ids[cnt - 1];
cnt = out;
}
return &g->nodes[arg_ids[0]];
}
}
if (n == 3) {
uint16_t mov_op = resolve_moving_opcode(fn_sym);
if (mov_op) {
ray_op_t* wop = compile_expr_dag(g, elems[1]);
int64_t window = 0;
if (!const_op_i64(g, wop, &window) || window < 1)
return NULL;
ray_op_t* arg = compile_expr_dag(g, elems[2]);
if (!arg) return NULL;
switch (mov_op) {
case OP_MSUM: return ray_msum_op(g, arg, window);
case OP_MAVG: return ray_mavg_op(g, arg, window);
case OP_MMIN: return ray_mmin_op(g, arg, window);
case OP_MMAX: return ray_mmax_op(g, arg, window);
case OP_MCOUNT: return ray_mcount_op(g, arg, window);
case OP_MVAR: return ray_mvar_op(g, arg, window);
case OP_MDEV: return ray_mdev_op(g, arg, window);
default: return NULL;
}
}
dag_binary_ctor ctor = resolve_binary_dag(fn_sym);
if (ctor) {
ray_op_t* left = compile_expr_dag(g, elems[1]);
if (!left) return NULL;
uint32_t left_id = left->id;
ray_op_t* right = compile_expr_dag(g, elems[2]);
if (!right) return NULL;
left = &g->nodes[left_id];
if ((ctor == ray_and || ctor == ray_or) &&
(!dag_bool_operand_ok(g, left, ctor == ray_and) ||
!dag_bool_operand_ok(g, right, ctor == ray_and)))
return NULL;
if (dag_temporal_arith_needs_eval(fname, fname_len,
left->out_type,
right->out_type))
return NULL;
if (g->if_arm_depth == 0 &&
dag_arith_rejects_sym(fname, fname_len,
left->out_type, right->out_type)) {
if (!g->compile_err)
g->compile_err = ray_error("type", "cannot %s %s and %s",
dag_arith_verb(fname, fname_len),
ray_type_name((int8_t)-left->out_type),
ray_type_name((int8_t)-right->out_type));
return NULL;
}
if (g->if_arm_depth == 0 &&
dag_cmp_rejects_temporal_float(fname, fname_len,
left->out_type, right->out_type)) {
if (!g->compile_err)
g->compile_err = ray_error("type", "cannot compare %s and %s",
ray_type_name((int8_t)-left->out_type),
ray_type_name((int8_t)-right->out_type));
return NULL;
}
if (fname_len == 3 && memcmp(fname, "pow", 3) == 0 &&
(!dag_pow_type_admitted(left->out_type) ||
!dag_pow_type_admitted(right->out_type)))
return NULL;
return ctor(g, left, right);
}
}
if (n == 2) {
dag_unary_ctor uctor = resolve_unary_dag(fn_sym);
if (uctor) {
ray_op_t* arg = compile_expr_dag(g, elems[1]);
if (!arg) return NULL;
if (dag_unary_numeric_name(fname, fname_len) &&
!dag_numeric_type_admitted(arg->out_type))
return NULL;
return uctor(g, arg);
}
uint16_t agg_op = resolve_agg_opcode(fn_sym);
if (agg_op) {
ray_op_t* arg = compile_expr_dag(g, elems[1]);
if (!arg) return NULL;
if (arg->out_type > 0 && !agg_type_admitted(agg_op, arg->out_type))
return NULL;
switch (agg_op) {
case OP_SUM: return ray_sum(g, arg);
case OP_AVG: return ray_avg(g, arg);
case OP_MIN: return ray_min_op(g, arg);
case OP_MAX: return ray_max_op(g, arg);
case OP_COUNT: return ray_count(g, arg);
case OP_ALL: return ray_all(g, arg);
case OP_ANY: return ray_any(g, arg);
case OP_FIRST: return ray_first(g, arg);
case OP_LAST: return ray_last(g, arg);
case OP_PROD: return ray_prod(g, arg);
case OP_STDDEV: return ray_stddev(g, arg);
case OP_STDDEV_POP: return ray_stddev_pop(g, arg);
case OP_VAR: return ray_var(g, arg);
case OP_VAR_POP: return ray_var_pop(g, arg);
case OP_MEDIAN: return ray_median(g, arg);
default: return NULL;
}
}
}
}
return NULL;
}
static _Thread_local ray_t* g_active_query_table = NULL;
static _Thread_local int64_t g_active_query_row = -1;
static _Thread_local ray_t* g_active_query_row_tbl = NULL;
static _Thread_local int32_t g_active_query_row_depth = 0;
ray_t* ray_active_query_table(void) { return g_active_query_table; }
static ray_t* bind_all_columns(ray_t* tbl) {
ray_t* prev = g_active_query_table;
g_active_query_table = tbl;
int64_t ncols = ray_table_ncols(tbl);
for (int64_t c = 0; c < ncols; c++) {
int64_t cn = ray_table_col_name(tbl, c);
ray_t* cv = ray_table_get_col_idx(tbl, c);
if (cv) ray_env_set_query_local(cn, cv);
}
return prev;
}
static ray_t* eval_where_mask(ray_t* where_expr, ray_t* tbl, const char* label) {
if (ray_env_push_query_scope() != RAY_OK) return ray_error("oom", NULL);
ray_t* _aqt = bind_all_columns(tbl);
ray_t* mask = ray_eval(where_expr);
if (mask && !RAY_IS_ERR(mask))
mask = ray_lazy_materialize(mask);
g_active_query_table = _aqt;
ray_env_pop_scope();
if (!mask)
return ray_error("domain", "%s: WHERE predicate evaluation failed", label);
if (RAY_IS_ERR(mask))
return mask;
return ray_where_mask_coerce(mask, ray_table_nrows(tbl), label);
}
static ray_op_t* compile_where_predicate(ray_graph_t* g, ray_t* tbl,
ray_t* where_expr,
const char* label,
ray_t** out_err) {
if (out_err) *out_err = NULL;
ray_op_t* pred = compile_expr_dag(g, where_expr);
if (pred) return pred;
if (g->compile_err) {
if (out_err) *out_err = graph_take_compile_err(g);
else { ray_release(g->compile_err); g->compile_err = NULL; }
return NULL;
}
ray_t* mask = eval_where_mask(where_expr, tbl, label);
if (!mask || RAY_IS_ERR(mask)) {
if (out_err) *out_err = mask ? mask :
ray_error("domain", "%s: WHERE predicate evaluation failed", label);
return NULL;
}
pred = ray_const_vec(g, mask);
ray_release(mask);
if (!pred && out_err) *out_err = ray_error("oom", NULL);
return pred;
}
static int is_agg_expr(ray_t* expr);
static int expr_refs_row_column(ray_t* expr, ray_t* tbl) {
if (!expr) return 0;
if (expr->type == -RAY_SYM && !(expr->attrs & ATTR_QUOTED)) {
if (ray_env_has_lexical_local(expr->i64)) return 0;
if (ray_table_get_col(tbl, expr->i64)) return 1;
if (ray_sym_is_dotted(expr->i64)) {
const int64_t* segs;
int nsegs = ray_sym_segs(expr->i64, &segs);
if (nsegs >= 1 && ray_table_get_col(tbl, segs[0])) return 1;
}
return 0;
}
if (expr->type == RAY_LIST) {
if (is_agg_expr(expr)) return 0;
ray_t** elems = (ray_t**)ray_data(expr);
int64_t n = ray_len(expr);
if (n == 0) return 0;
if (elems[0]->type == RAY_LIST) return 0;
for (int64_t i = 1; i < n; i++)
if (expr_refs_row_column(elems[i], tbl)) return 1;
}
return 0;
}
static int is_agg_expr(ray_t* expr) {
if (!expr || expr->type != RAY_LIST) return 0;
if (expr->type == RAY_DICT) return 0;
int64_t n = ray_len(expr);
if (n < 2) return 0;
ray_t** elems = (ray_t**)ray_data(expr);
if (elems[0]->type != -RAY_SYM) return 0;
return resolve_agg_opcode(elems[0]->i64) != 0;
}
static int expr_contains_agg(ray_t* expr) {
if (!expr) return 0;
if (expr->type != RAY_LIST) return 0;
if (is_agg_expr(expr)) return 1;
ray_t** elems = (ray_t**)ray_data(expr);
int64_t n = ray_len(expr);
for (int64_t i = 0; i < n; i++)
if (expr_contains_agg(elems[i])) return 1;
return 0;
}
static int is_group_dag_agg_expr(ray_t* expr);
static bool simplify_agg_idiom(ray_t* val_expr, ray_t* tbl,
uint16_t* out_op, ray_t** out_arg);
static int sg_agg_expr_ok(ray_t* expr) {
if (!expr || !is_list(expr) || ray_len(expr) < 2) return 0;
ray_t** e = (ray_t**)ray_data(expr);
if (!e[0] || e[0]->type != -RAY_SYM) return 0;
uint16_t op = resolve_agg_opcode(e[0]->i64);
if (!op) return 0;
if (op == OP_COUNT) return 1;
ray_t* arg = e[1];
bool arg_bare = arg && arg->type == -RAY_SYM &&
!(arg->attrs & ATTR_QUOTED);
if (agg_is_binary_agg(op)) {
if (ray_len(expr) < 3) return 0;
ray_t* arg2 = e[2];
return arg_bare && arg2 && arg2->type == -RAY_SYM &&
!(arg2->attrs & ATTR_QUOTED);
}
if (op == OP_STDDEV || op == OP_STDDEV_POP ||
op == OP_VAR || op == OP_VAR_POP)
return arg_bare;
if (op != OP_SUM && op != OP_AVG) return 0;
if (arg_bare) return 1;
if (arg && is_list(arg) && ray_len(arg) == 3) {
ray_t** m = (ray_t**)ray_data(arg);
static int64_t s_mul = -1;
if (s_mul < 0) s_mul = ray_sym_intern("*", 1);
if (m[0] && m[0]->type == -RAY_SYM && m[0]->i64 == s_mul &&
m[1] && m[1]->type == -RAY_SYM && !(m[1]->attrs & ATTR_QUOTED) &&
m[2] && m[2]->type == -RAY_SYM && !(m[2]->attrs & ATTR_QUOTED))
return 1;
}
return 0;
}
static int hidden_agg_shape_ok(ray_t* expr) {
int64_t n = ray_len(expr);
if (n == 2) return 1;
if (n != 3) return 0;
ray_t** e = (ray_t**)ray_data(expr);
uint16_t op = resolve_agg_opcode(e[0]->i64);
if (agg_is_binary_agg(op))
return e[1] && e[1]->type == -RAY_SYM && !(e[1]->attrs & ATTR_QUOTED) &&
e[2] && e[2]->type == -RAY_SYM && !(e[2]->attrs & ATTR_QUOTED);
return op == OP_QUANTILE;
}
static bool agg_arith_head_is_control(int64_t sym) {
static const char* const forms[] = { "if", "while", "times",
"do", "let", "set", "try" };
ray_t* s = ray_sym_str(sym);
if (!s) return false;
const char* p = ray_str_ptr(s);
size_t l = ray_str_len(s);
for (size_t i = 0; i < sizeof forms / sizeof forms[0]; i++)
if (l == strlen(forms[i]) && memcmp(p, forms[i], l) == 0) return true;
return false;
}
static ray_t* agg_arith_rewrite(ray_t* expr, ray_t* tbl,
ray_t** hexprs, int64_t* hnames,
int* n_hidden, int cap, int* ok) {
if (!*ok || !expr) { *ok = 0; return NULL; }
if (expr->type == RAY_LIST && is_group_dag_agg_expr(expr) &&
hidden_agg_shape_ok(expr)) {
ray_t** el = (ray_t**)ray_data(expr);
if (expr_contains_agg(el[1])) { *ok = 0; return NULL; }
if (ray_len(expr) > 2 && expr_contains_agg(el[2])) { *ok = 0; return NULL; }
if (*n_hidden >= cap) { *ok = 0; return NULL; }
char buf[24];
int bn = snprintf(buf, sizeof buf, "__ha%d", *n_hidden);
int64_t hn = ray_sym_intern(buf, (size_t)bn);
hexprs[*n_hidden] = expr;
hnames[*n_hidden] = hn;
(*n_hidden)++;
return ray_sym(hn);
}
if (expr->type != RAY_LIST) { *ok = 0; return NULL; }
int64_t n = ray_len(expr);
ray_t** el = (ray_t**)ray_data(expr);
if (n > 0 && el[0] && el[0]->type == -RAY_SYM &&
el[0]->i64 == ray_sym_intern("fn", 2)) { *ok = 0; return NULL; }
if (n > 0 && el[0] && el[0]->type == -RAY_SYM &&
!(el[0]->attrs & ATTR_QUOTED) && agg_arith_head_is_control(el[0]->i64))
{ *ok = 0; return NULL; }
ray_t* out = ray_list_new(0);
if (!out || RAY_IS_ERR(out)) { *ok = 0; return out && RAY_IS_ERR(out) ? NULL : NULL; }
for (int64_t i = 0; i < n && *ok; i++) {
ray_t* child = el[i];
ray_t* rep = NULL;
int rep_owned = 0;
if (child && child->type == RAY_LIST) {
rep = agg_arith_rewrite(child, tbl, hexprs, hnames,
n_hidden, cap, ok);
rep_owned = 1;
} else if (child && child->type == -RAY_SYM &&
!(child->attrs & ATTR_QUOTED) && i > 0) {
if (ray_sym_is_dotted(child->i64) ||
ray_table_get_col(tbl, child->i64)) { *ok = 0; }
else rep = child;
} else {
rep = child;
}
if (*ok && rep) {
out = ray_list_append(out, rep);
if (RAY_IS_ERR(out)) *ok = 0;
}
if (rep_owned && rep) ray_release(rep);
}
if (!*ok) { if (out && !RAY_IS_ERR(out)) ray_release(out); return NULL; }
return out;
}
static ray_t* try_decompose_agg_arith(ray_t* val_expr, ray_t* tbl,
ray_t** hexprs, int64_t* hnames,
int* n_hidden, int cap) {
if (!val_expr || val_expr->type != RAY_LIST) return NULL;
if (is_group_dag_agg_expr(val_expr)) return NULL;
if (!expr_contains_agg(val_expr)) return NULL;
int ok = 1;
int saved = *n_hidden;
ray_t* rw = agg_arith_rewrite(val_expr, tbl, hexprs, hnames,
n_hidden, cap, &ok);
if (!ok || !rw || *n_hidden == saved) {
*n_hidden = saved;
if (rw && !RAY_IS_ERR(rw)) ray_release(rw);
return NULL;
}
return rw;
}
static int is_decomposable_agg_compound(ray_t* expr, ray_t* tbl) {
enum { PROBE_CAP = 64 };
ray_t* hexprs[PROBE_CAP];
int64_t hnames[PROBE_CAP];
int n_hidden = 0;
ray_t* rw = try_decompose_agg_arith(expr, tbl, hexprs, hnames,
&n_hidden, PROBE_CAP);
if (!rw) return 0;
ray_release(rw);
return 1;
}
static int expr_contains_call_named(ray_t* expr, const char* name, size_t name_len) {
if (!expr) return 0;
if (expr->type != RAY_LIST) return 0;
ray_t** elems = (ray_t**)ray_data(expr);
int64_t n = ray_len(expr);
if (n <= 0) return 0;
ray_t* head = elems[0];
if (head && head->type == -RAY_SYM) {
ray_t* s = ray_sym_str(head->i64);
if (s && ray_str_len(s) == name_len &&
memcmp(ray_str_ptr(s), name, name_len) == 0)
return 1;
}
for (int64_t i = 0; i < n; i++)
if (expr_contains_call_named(elems[i], name, name_len))
return 1;
return 0;
}
static ray_t* query_materialize_parted_col(ray_t* col);
static bool table_has_parted_columns(ray_t* tbl);
static ray_t* query_materialize_parted_table(ray_t* tbl);
static ray_t* select_fallback_passthrough_col(ray_t* expr, ray_t* tbl, int64_t nrows) {
if (!expr || expr->type != -RAY_SYM || (expr->attrs & ATTR_QUOTED)) return NULL;
if (ray_env_has_lexical_local(expr->i64)) return NULL;
ray_t* col = ray_table_get_col(tbl, expr->i64);
if (!col || col->type < 0) return NULL;
if (RAY_IS_PARTED(col->type) || col->type == RAY_MAPCOMMON ||
(col->attrs & RAY_ATTR_SLICE) || ray_len(col) != nrows)
return NULL;
ray_retain(col);
return col;
}
static int is_whole_column_projection(ray_t* expr) {
if (!expr || expr->type != RAY_LIST) return 0;
if (ray_len(expr) < 2) return 0;
ray_t* head = ((ray_t**)ray_data(expr))[0];
if (!head || head->type != -RAY_SYM) return 0;
ray_t* s = ray_sym_str(head->i64);
if (!s) return 0;
size_t l = ray_str_len(s);
const char* p = ray_str_ptr(s);
return (l == 8 && memcmp(p, "distinct", 8) == 0) ||
(l == 7 && memcmp(p, "reverse", 7) == 0) ||
(l == 3 && memcmp(p, "asc", 3) == 0) ||
(l == 4 && memcmp(p, "desc", 4) == 0);
}
static int is_group_dag_agg_expr(ray_t* expr) {
if (!is_agg_expr(expr)) return 0;
ray_t** elems = (ray_t**)ray_data(expr);
uint16_t op = resolve_agg_opcode(elems[0]->i64);
if (op == OP_QUANTILE) {
if (ray_len(expr) < 3) return 0;
if (!quantile_literal_prob(elems[2],
agg_name_is_percentile(elems[0]->i64),
NULL))
return 0;
}
return !expr_contains_call_named(elems[1], "distinct", 8);
}
static bool expr_literal_is_temporal(ray_t* expr) {
if (!expr) return false;
int8_t t = expr->type < 0 ? (int8_t)-expr->type : expr->type;
return dag_type_is_temporal(t);
}
static bool expr_dag_result_is_temporal(ray_t* expr, ray_t* tbl) {
if (expr_literal_is_temporal(expr)) return true;
ray_graph_t* pg = ray_graph_new(tbl);
if (!pg) return false;
ray_op_t* op = compile_expr_dag(pg, expr);
bool temporal = op && dag_type_is_temporal(op->out_type);
ray_graph_free(pg);
return temporal;
}
static bool expr_contains_temporal_arith(ray_t* expr, ray_t* tbl) {
if (!expr || expr->type != RAY_LIST) return false;
ray_t** elems = (ray_t**)ray_data(expr);
int64_t n = ray_len(expr);
if (n >= 3 && elems[0] && elems[0]->type == -RAY_SYM) {
ray_t* name = ray_sym_str(elems[0]->i64);
size_t len = name ? ray_str_len(name) : 0;
const char* s = name ? ray_str_ptr(name) : NULL;
if (len == 1 && (s[0] == '+' || s[0] == '-' || s[0] == '*')) {
if (expr_dag_result_is_temporal(elems[1], tbl) ||
expr_dag_result_is_temporal(elems[2], tbl))
return true;
}
}
for (int64_t i = 0; i < n; i++)
if (expr_contains_temporal_arith(elems[i], tbl))
return true;
return false;
}
static int is_group_dag_agg_expr_dag_safe(ray_t* expr, ray_t* tbl) {
if (!is_group_dag_agg_expr(expr)) return 0;
ray_t** elems = (ray_t**)ray_data(expr);
uint16_t op = resolve_agg_opcode(elems[0]->i64);
ray_t* agg_arg = elems[1];
{
uint16_t new_op;
ray_t* new_arg;
if (simplify_agg_idiom(expr, tbl, &new_op, &new_arg)) {
op = new_op;
agg_arg = new_arg;
}
}
if (expr_contains_temporal_arith(agg_arg, tbl))
return 0;
if (agg_is_binary_agg(op)) {
if (ray_len(expr) < 3) return 1;
if (expr_contains_temporal_arith(elems[2], tbl))
return 0;
}
return 1;
}
static int is_single_group_key_projection(ray_t* by_expr, ray_t* val_expr) {
int64_t key_id = -1;
if (by_expr && by_expr->type == -RAY_SYM && !(by_expr->attrs & ATTR_QUOTED)) {
key_id = by_expr->i64;
} else if (by_expr && by_expr->type == RAY_SYM && ray_len(by_expr) == 1) {
key_id = ((int64_t*)ray_data(by_expr))[0];
}
return key_id >= 0 &&
val_expr &&
val_expr->type == -RAY_SYM &&
!(val_expr->attrs & ATTR_QUOTED) &&
val_expr->i64 == key_id;
}
static int atom_i64_const(ray_t* v, int64_t* out) {
if (!v || !ray_is_atom(v) ||
(v->type == -RAY_SYM && !(v->attrs & ATTR_QUOTED)) ||
RAY_ATOM_IS_NULL(v))
return 0;
switch (v->type) {
case -RAY_BOOL:
case -RAY_U8: *out = v->u8; return 1;
case -RAY_I16: *out = v->i16; return 1;
case -RAY_I32:
case -RAY_DATE:
case -RAY_TIME: *out = v->i32; return 1;
case -RAY_I64:
case -RAY_TIMESTAMP: *out = v->i64; return 1;
default: return 0;
}
}
static int expr_affine_of_sym(ray_t* expr, int64_t sym, int64_t* bias) {
if (!expr) return 0;
if (expr->type == -RAY_SYM && !(expr->attrs & ATTR_QUOTED) &&
expr->i64 == sym) {
*bias = 0;
return 1;
}
if (expr->type != RAY_LIST || ray_len(expr) != 3) return 0;
ray_t** e = (ray_t**)ray_data(expr);
if (!e[0] || e[0]->type != -RAY_SYM) return 0;
ray_t* op = ray_sym_str(e[0]->i64);
if (!op || ray_str_len(op) != 1) return 0;
char opc = ray_str_ptr(op)[0];
int lhs_sym = e[1] && e[1]->type == -RAY_SYM &&
!(e[1]->attrs & ATTR_QUOTED) && e[1]->i64 == sym;
int rhs_sym = e[2] && e[2]->type == -RAY_SYM &&
!(e[2]->attrs & ATTR_QUOTED) && e[2]->i64 == sym;
int64_t c = 0;
if (opc == '+') {
if (lhs_sym && atom_i64_const(e[2], &c)) {
*bias = c;
return 1;
}
if (rhs_sym && atom_i64_const(e[1], &c)) {
*bias = c;
return 1;
}
} else if (opc == '-') {
if (lhs_sym && atom_i64_const(e[2], &c)) {
*bias = -c;
return 1;
}
}
return 0;
}
static int key_type_i64_projectable(int8_t t) {
return t == RAY_BOOL || t == RAY_U8 || t == RAY_I16 ||
t == RAY_I32 || t == RAY_I64 || t == RAY_DATE ||
t == RAY_TIME || t == RAY_TIMESTAMP;
}
static int64_t key_col_read_i64(ray_t* col, int64_t row) {
const void* d = ray_data(col);
switch (col->type) {
case RAY_BOOL:
case RAY_U8: return ((const uint8_t*)d)[row];
case RAY_I16: return ((const int16_t*)d)[row];
case RAY_I32:
case RAY_DATE:
case RAY_TIME: return ((const int32_t*)d)[row];
case RAY_I64:
case RAY_TIMESTAMP: return ((const int64_t*)d)[row];
default: return 0;
}
}
static int8_t query_col_base_type(ray_t* col) {
if (!col) return 0;
if (RAY_IS_PARTED(col->type)) return (int8_t)RAY_PARTED_BASETYPE(col->type);
if (col->type == RAY_MAPCOMMON) {
ray_t** p = (ray_t**)ray_data(col);
return (p && p[0]) ? p[0]->type : 0;
}
return col->type;
}
static int query_key_type_streamable(int8_t t) {
return key_type_i64_projectable(t) || t == RAY_SYM ||
t == RAY_F64 || t == RAY_F32;
}
static bool parse_gt_name_i64(ray_t* expr, int64_t* out_name, int64_t* out_threshold) {
if (!expr || expr->type != RAY_LIST || ray_len(expr) != 3)
return false;
ray_t** e = (ray_t**)ray_data(expr);
if (!e[0] || e[0]->type != -RAY_SYM)
return false;
ray_t* op = ray_sym_str(e[0]->i64);
if (!op || ray_str_len(op) != 1 || ray_str_ptr(op)[0] != '>')
return false;
if (!e[1] || e[1]->type != -RAY_SYM || (e[1]->attrs & ATTR_QUOTED))
return false;
if (!e[2] || !ray_is_atom(e[2]) ||
(e[2]->type == -RAY_SYM && !(e[2]->attrs & ATTR_QUOTED)))
return false;
int64_t threshold;
switch (e[2]->type) {
case -RAY_I64: threshold = e[2]->i64; break;
case -RAY_I32:
case -RAY_DATE:
case -RAY_TIME: threshold = e[2]->i32; break;
case -RAY_I16: threshold = e[2]->i16; break;
case -RAY_U8:
case -RAY_BOOL: threshold = e[2]->u8; break;
default: return false;
}
*out_name = e[1]->i64;
*out_threshold = threshold;
return true;
}
static bool can_defer_single_key_where(ray_t* by_expr, ray_t* where_expr,
ray_t* tbl) {
if (!by_expr || !where_expr || !tbl ||
by_expr->type != -RAY_SYM || (by_expr->attrs & ATTR_QUOTED) ||
where_expr->type != RAY_LIST || ray_len(where_expr) != 3)
return false;
ray_t** e = (ray_t**)ray_data(where_expr);
if (!e[0] || e[0]->type != -RAY_SYM) return false;
ray_t* op = ray_sym_str(e[0]->i64);
if (!op) return false;
size_t op_len = ray_str_len(op);
const char* op_s = ray_str_ptr(op);
bool cmp = (op_len == 1 && (op_s[0] == '<' || op_s[0] == '>')) ||
(op_len == 2 &&
((op_s[0] == '=' && op_s[1] == '=') ||
(op_s[0] == '!' && op_s[1] == '=') ||
(op_s[0] == '<' && op_s[1] == '=') ||
(op_s[0] == '>' && op_s[1] == '=')));
if (!cmp) return false;
ray_t* lhs = e[1];
ray_t* rhs = e[2];
bool lhs_key = lhs && lhs->type == -RAY_SYM &&
!(lhs->attrs & ATTR_QUOTED) &&
lhs->i64 == by_expr->i64;
bool rhs_key = rhs && rhs->type == -RAY_SYM &&
!(rhs->attrs & ATTR_QUOTED) &&
rhs->i64 == by_expr->i64;
if (lhs_key == rhs_key) return false;
ray_t* atom = lhs_key ? rhs : lhs;
if (!atom || !ray_is_atom(atom) ||
(atom->type == -RAY_SYM && !(atom->attrs & ATTR_QUOTED)) ||
RAY_ATOM_IS_NULL(atom))
return false;
ray_t* key_col = ray_table_get_col(tbl, by_expr->i64);
if (!key_col) return false;
int8_t kt = key_col->type;
if (!RAY_IS_PARTED(kt)) return false;
if (RAY_IS_PARTED(kt)) kt = (int8_t)RAY_PARTED_BASETYPE(kt);
return kt == RAY_SYM || kt == RAY_BOOL || kt == RAY_U8 ||
kt == RAY_I16 || kt == RAY_I32 || kt == RAY_I64 ||
kt == RAY_DATE || kt == RAY_TIME || kt == RAY_TIMESTAMP ||
kt == RAY_F32 || kt == RAY_F64;
}
static ray_t* filter_group_result(ray_t* result, ray_t* where_expr) {
if (!result || RAY_IS_ERR(result) || !where_expr) return result;
if (result->type != RAY_TABLE) return result;
if (ray_is_lazy(result)) {
result = ray_lazy_materialize(result);
if (!result || RAY_IS_ERR(result)) return result;
}
ray_graph_t* fg = ray_graph_new(result);
if (!fg) {
ray_release(result);
return ray_error("oom", NULL);
}
ray_op_t* root = ray_const_table(fg, result);
ray_t* pred_err = NULL;
ray_op_t* pred = compile_where_predicate(fg, result, where_expr,
"select having", &pred_err);
if (!pred) {
ray_graph_free(fg);
ray_release(result);
return pred_err ? pred_err :
ray_error("domain", "select having: failed to evaluate group filter predicate");
}
root = ray_filter(fg, root, pred);
root = ray_optimize(fg, root);
ray_t* filtered = ray_execute(fg, root);
if (filtered && !RAY_IS_ERR(filtered) && ray_is_lazy(filtered))
filtered = ray_lazy_materialize(filtered);
ray_graph_free(fg);
ray_release(result);
return filtered ? filtered : ray_error("domain", "select having: group filter produced no result");
}
static bool match_group_count_emit_filter(ray_t* from_expr, ray_t* where_expr,
ray_group_emit_filter_t* out) {
int64_t filter_name, threshold;
if (!parse_gt_name_i64(where_expr, &filter_name, &threshold))
return false;
if (!from_expr || from_expr->type != RAY_LIST || ray_len(from_expr) != 2)
return false;
ray_t** fe = (ray_t**)ray_data(from_expr);
if (!fe[0] || fe[0]->type != -RAY_SYM)
return false;
ray_t* fname = ray_sym_str(fe[0]->i64);
if (!fname || ray_str_len(fname) != 6 ||
memcmp(ray_str_ptr(fname), "select", 6) != 0)
return false;
ray_t* inner = fe[1];
if (!inner || inner->type != RAY_DICT)
return false;
ray_t* by = dict_get(inner, "by");
if (!by) return false;
DICT_VIEW_DECL(iv);
DICT_VIEW_OPEN(inner, iv);
if (DICT_VIEW_OVERFLOW(iv)) { DICT_VIEW_CLOSE(iv); return false; }
int64_t from_id = dict_key_id(inner, "from");
int64_t where_id = dict_key_id(inner, "where");
int64_t by_id = dict_key_id(inner, "by");
int64_t take_id = dict_key_id(inner, "take");
int64_t asc_id = dict_key_id(inner, "asc");
int64_t desc_id = dict_key_id(inner, "desc");
uint8_t agg_index = 0;
for (int64_t i = 0; i + 1 < iv_n; i += 2) {
int64_t kid = iv[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id)
continue;
ray_t* val = iv[i + 1];
if (!is_group_dag_agg_expr(val))
continue;
ray_t** ae = (ray_t**)ray_data(val);
uint16_t op = resolve_agg_opcode(ae[0]->i64);
if (kid == filter_name && op == OP_COUNT) {
out->enabled = 1;
out->agg_index = agg_index;
out->min_count_exclusive = threshold;
out->desc = 1;
DICT_VIEW_CLOSE(iv);
return true;
}
agg_index++;
}
DICT_VIEW_CLOSE(iv);
return false;
}
static bool positive_take_i64(ray_t* expr, int64_t* out) {
if (!expr) return false;
ray_t* v = ray_eval(expr);
if (!v || RAY_IS_ERR(v)) return false;
bool ok = false;
int64_t n = 0;
if (v->type == -RAY_I64) { n = v->i64; ok = n > 0; }
else if (v->type == -RAY_I32) { n = v->i32; ok = n > 0; }
ray_release(v);
if (!ok) return false;
*out = n;
return true;
}
static bool match_group_desc_count_take(ray_t** dict_elems, int64_t dict_n,
int64_t from_id, int64_t where_id,
int64_t by_id, int64_t take_id,
int64_t asc_id, int64_t desc_id,
ray_group_emit_filter_t* out) {
ray_t* take_expr = NULL;
int64_t order_name = -1;
uint8_t want_desc = 1;
bool seen_dir = false;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == take_id) take_expr = dict_elems[i + 1];
else if (kid == desc_id || kid == asc_id) {
if (seen_dir) return false;
seen_dir = true;
ray_t* v = dict_elems[i + 1];
if (!v || v->type != -RAY_SYM) return false;
order_name = v->i64;
want_desc = (kid == desc_id) ? 1 : 0;
}
}
int64_t take_n = 0;
if (order_name < 0 || !positive_take_i64(take_expr, &take_n))
return false;
if (take_n > 1024) return false;
uint8_t agg_index = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id)
continue;
ray_t* val = dict_elems[i + 1];
if (!is_group_dag_agg_expr(val))
continue;
ray_t** ae = (ray_t**)ray_data(val);
uint16_t op = resolve_agg_opcode(ae[0]->i64);
if (kid == order_name &&
(op == OP_COUNT || op == OP_SUM ||
op == OP_MIN || op == OP_MAX)) {
out->enabled = 1;
out->agg_index = agg_index;
out->min_count_exclusive = 0;
out->top_count_take = take_n;
out->agg_op = op;
out->desc = want_desc;
return true;
}
agg_index++;
}
return false;
}
static int is_aggr_unary_call(ray_t* expr) {
if (!expr || expr->type != RAY_LIST) return 0;
int64_t n = ray_len(expr);
if (n < 2) return 0;
ray_t** elems = (ray_t**)ray_data(expr);
if (elems[0]->type != -RAY_SYM) return 0;
ray_t* fn_obj = ray_env_get(elems[0]->i64);
if (!fn_obj || fn_obj->type != RAY_UNARY) return 0;
return (fn_obj->attrs & RAY_FN_AGGR) != 0;
}
static int is_streaming_aggr_unary_call(ray_t* expr) {
if (!is_aggr_unary_call(expr)) return 0;
ray_t** elems = (ray_t**)ray_data(expr);
return !expr_contains_call_named(elems[1], "distinct", 8);
}
static int is_plain_count_expr(ray_t* expr) {
if (!expr || expr->type != RAY_LIST) return 0;
int64_t n = ray_len(expr);
if (n < 2) return 0;
ray_t** elems = (ray_t**)ray_data(expr);
if (!elems[0] || elems[0]->type != -RAY_SYM) return 0;
if (resolve_agg_opcode(elems[0]->i64) != OP_COUNT) return 0;
return !expr_contains_call_named(elems[1], "distinct", 8);
}
static bool simplify_agg_idiom(ray_t* val_expr, ray_t* tbl,
uint16_t* op_out, ray_t** arg_out) {
if (!val_expr || val_expr->type != RAY_LIST || ray_len(val_expr) < 2) return false;
ray_t** outer = (ray_t**)ray_data(val_expr);
if (!outer[0] || outer[0]->type != -RAY_SYM) return false;
ray_t* outer_nm = ray_sym_str(outer[0]->i64);
if (!outer_nm) return false;
const char* op_s = ray_str_ptr(outer_nm);
size_t op_n = ray_str_len(outer_nm);
ray_t* inner = outer[1];
if (!inner || inner->type != RAY_LIST || ray_len(inner) < 2) return false;
ray_t** inner_e = (ray_t**)ray_data(inner);
if (!inner_e[0] || inner_e[0]->type != -RAY_SYM) return false;
ray_t* inner_nm = ray_sym_str(inner_e[0]->i64);
if (!inner_nm) return false;
const char* wrap_s = ray_str_ptr(inner_nm);
size_t wrap_n = ray_str_len(inner_nm);
ray_t* col_expr = inner_e[1];
bool wrap_is_asc = (wrap_n == 3 && memcmp(wrap_s, "asc", 3) == 0);
bool wrap_is_desc = (wrap_n == 4 && memcmp(wrap_s, "desc", 4) == 0);
bool wrap_is_reverse = (wrap_n == 7 && memcmp(wrap_s, "reverse", 7) == 0);
if (!wrap_is_asc && !wrap_is_desc && !wrap_is_reverse) return false;
if (op_n == 5 && memcmp(op_s, "count", 5) == 0) {
*op_out = OP_COUNT;
*arg_out = col_expr;
return true;
}
if (!wrap_is_asc) return false;
bool is_first = (op_n == 5 && memcmp(op_s, "first", 5) == 0);
bool is_last = (op_n == 4 && memcmp(op_s, "last", 4) == 0);
if (!is_first && !is_last) return false;
if (!col_expr || col_expr->type != -RAY_SYM || (col_expr->attrs & ATTR_QUOTED))
return false;
ray_t* col = ray_table_get_col(tbl, col_expr->i64);
if (!col || ray_vec_has_nulls(col)) return false;
*op_out = is_first ? OP_MIN : OP_MAX;
*arg_out = col_expr;
return true;
}
static ray_t* match_count_distinct(ray_t* expr) {
if (!expr || expr->type != RAY_LIST) return NULL;
int64_t n = ray_len(expr);
if (n != 2) return NULL;
ray_t** elems = (ray_t**)ray_data(expr);
if (!elems[0] || elems[0]->type != -RAY_SYM) return NULL;
ray_t* nm = ray_sym_str(elems[0]->i64);
if (!nm || ray_str_len(nm) != 5 ||
memcmp(ray_str_ptr(nm), "count", 5) != 0) return NULL;
ray_t* inner = elems[1];
if (!inner || inner->type != RAY_LIST) return NULL;
int64_t in_n = ray_len(inner);
if (in_n != 2) return NULL;
ray_t** in_elems = (ray_t**)ray_data(inner);
if (!in_elems[0] || in_elems[0]->type != -RAY_SYM) return NULL;
ray_t* dnm = ray_sym_str(in_elems[0]->i64);
if (!dnm || ray_str_len(dnm) != 8 ||
memcmp(ray_str_ptr(dnm), "distinct", 8) != 0) return NULL;
return in_elems[1];
}
static int64_t select_output_count(ray_t** dict_elems, int64_t dict_n) {
int64_t from_id = ray_sym_intern("from", 4);
int64_t where_id = ray_sym_intern("where", 5);
int64_t by_id = ray_sym_intern("by", 2);
int64_t take_id = ray_sym_intern("take", 4);
int64_t asc_id = ray_sym_intern("asc", 3);
int64_t desc_id = ray_sym_intern("desc", 4);
int64_t nearest_id = ray_sym_intern("nearest", 7);
int64_t n = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id ||
kid == nearest_id) continue;
n++;
}
return n;
}
static int64_t count_agg_subexprs(ray_t* expr) {
if (!expr) return 0;
if (is_group_dag_agg_expr(expr)) return 1;
if (expr->type != RAY_LIST) return 0;
ray_t** e = (ray_t**)ray_data(expr);
int64_t n = ray_len(expr), c = 0;
for (int64_t i = 0; i < n; i++) c += count_agg_subexprs(e[i]);
return c;
}
static int collect_col_refs(ray_t* expr, ray_t* tbl,
int64_t* out_syms, int max_out, int n) {
if (!expr || n >= max_out) return n;
if (expr->type == -RAY_SYM && !(expr->attrs & ATTR_QUOTED)) {
int64_t want = -1;
if (ray_table_get_col(tbl, expr->i64)) {
want = expr->i64;
} else if (ray_sym_is_dotted(expr->i64)) {
const int64_t* segs;
int nsegs = ray_sym_segs(expr->i64, &segs);
if (nsegs >= 1 && ray_table_get_col(tbl, segs[0])) want = segs[0];
}
if (want >= 0) {
for (int i = 0; i < n; i++) if (out_syms[i] == want) return n;
if (n < max_out) out_syms[n++] = want;
}
return n;
}
if (expr->type == RAY_LIST) {
ray_t** elems = (ray_t**)ray_data(expr);
int64_t cnt = ray_len(expr);
for (int64_t i = 0; i < cnt && n < max_out; i++)
n = collect_col_refs(elems[i], tbl, out_syms, max_out, n);
}
return n;
}
static bool derived_key_head_ok(int64_t head) {
static const char* const ok[] = {
"let", "if", "cond", "and", "or", "not",
"==", "!=", "<", "<=", ">", ">=", "+", "-", "*", "/", "%",
"abs", "neg", "floor", "ceil", "round", "xbar", "as",
"substr", "str-find", "strlen", "within", "nil?", "upper", "lower", "like",
};
ray_t* name = ray_sym_str(head);
if (!name || RAY_IS_ERR(name)) return false;
size_t n = ray_str_len(name);
const char* q = ray_str_ptr(name);
for (size_t i = 0; i < sizeof(ok) / sizeof(ok[0]); i++)
if (strlen(ok[i]) == n && memcmp(ok[i], q, n) == 0) return true;
return false;
}
static bool derived_key_expr_ok(ray_t* e, ray_t* tbl, int64_t col_sym, int64_t* bound, int nbound) {
if (!e) return false;
if (e->type == -RAY_SYM) {
if (e->attrs & ATTR_QUOTED) return ray_table_get_col(tbl, e->i64) == NULL;
if (e->i64 == col_sym) return true;
for (int i = 0; i < nbound; i++) if (bound[i] == e->i64) return true;
return false;
}
if (e->type != RAY_LIST) return true;
int64_t n = ray_len(e);
if (n < 1) return false;
ray_t** el = (ray_t**)ray_data(e);
if (el[0]->type != -RAY_SYM || (el[0]->attrs & ATTR_QUOTED) || !derived_key_head_ok(el[0]->i64))
return false;
for (int i = 0; i < nbound; i++) if (bound[i] == el[0]->i64) return false;
{
ray_t* gv = ray_env_get(el[0]->i64);
if (gv && gv->type == RAY_LAMBDA) return false;
}
ray_t* hs = ray_sym_str(el[0]->i64);
size_t hl = ray_str_len(hs);
const char* hp = ray_str_ptr(hs);
if (hl == 3 && memcmp(hp, "let", 3) == 0) {
if (n != 4 || el[1]->type != -RAY_SYM || nbound >= 32) return false;
if (!derived_key_expr_ok(el[2], tbl, col_sym, bound, nbound)) return false;
bound[nbound] = el[1]->i64;
return derived_key_expr_ok(el[3], tbl, col_sym, bound, nbound + 1);
}
if (hl == 4 && memcmp(hp, "cond", 4) == 0) {
for (int64_t i = 1; i < n; i++) {
if (el[i]->type != RAY_LIST || ray_len(el[i]) != 2) return false;
ray_t** cp = (ray_t**)ray_data(el[i]);
bool is_else = cp[0]->type == -RAY_SYM && !(cp[0]->attrs & ATTR_QUOTED) &&
ray_str_len(ray_sym_str(cp[0]->i64)) == 4 &&
memcmp(ray_str_ptr(ray_sym_str(cp[0]->i64)), "else", 4) == 0;
if (!is_else && !derived_key_expr_ok(cp[0], tbl, col_sym, bound, nbound)) return false;
if (!derived_key_expr_ok(cp[1], tbl, col_sym, bound, nbound)) return false;
}
return true;
}
for (int64_t i = 1; i < n; i++)
if (!derived_key_expr_ok(el[i], tbl, col_sym, bound, nbound)) return false;
return true;
}
static int64_t derived_key_name(ray_t* by_expr) {
if (by_expr && by_expr->type == RAY_LIST && by_expr->len >= 2) {
ray_t** be = (ray_t**)ray_data(by_expr);
for (int64_t i = by_expr->len - 1; i >= 1; i--)
if (be[i]->type == -RAY_SYM && !(be[i]->attrs & ATTR_QUOTED)) return be[i]->i64;
}
return ray_sym_intern("key", 3);
}
static ray_t* group_key_eval(ray_t* v, ray_t* tbl) {
int64_t ncols_max = ray_table_ncols(tbl);
if (ncols_max < 1) ncols_max = 1;
ray_t* refs_hdr = NULL;
int64_t* ref_syms = (int64_t*)scratch_alloc(&refs_hdr,
(size_t)ncols_max * (sizeof(int64_t) + sizeof(ray_t*)));
if (!ref_syms) return ray_error("oom", NULL);
ray_t** materialized_refs = (ray_t**)(ref_syms + ncols_max);
int n_refs = collect_col_refs(v, tbl, ref_syms, (int)ncols_max, 0);
for (int ri = 0; ri < n_refs; ri++) materialized_refs[ri] = NULL;
ray_t* col_vec = NULL;
for (int ri = 0; ri < n_refs && !col_vec; ri++) {
ray_t* ref_col = ray_table_get_col(tbl, ref_syms[ri]);
if (ref_col && (RAY_IS_PARTED(ref_col->type) ||
ref_col->type == RAY_MAPCOMMON)) {
ray_t* flat = query_materialize_parted_col(ref_col);
if (!flat || RAY_IS_ERR(flat)) {
col_vec = flat ? flat : ray_error("oom", NULL);
break;
}
materialized_refs[ri] = flat;
ray_env_set_query_local(ref_syms[ri], flat);
}
}
if (!col_vec) {
ray_graph_t* key_graph = ray_graph_new(tbl);
if (!key_graph) col_vec = ray_error("oom", NULL);
else {
ray_op_t* key_op = compile_expr_dag(key_graph, v);
if (key_op) {
key_op = ray_optimize(key_graph, key_op);
col_vec = ray_execute(key_graph, key_op);
if (!col_vec) col_vec = ray_error("domain", "by-dict key execution failed");
}
ray_graph_free(key_graph);
}
if (!col_vec) col_vec = ray_eval(v);
if (ray_is_lazy(col_vec))
col_vec = ray_lazy_materialize(col_vec);
}
for (int ri = 0; ri < n_refs; ri++) {
if (materialized_refs[ri]) {
ray_t* ref_col = ray_table_get_col(tbl, ref_syms[ri]);
if (ref_col) ray_env_set_query_local(ref_syms[ri], ref_col);
ray_release(materialized_refs[ri]);
}
}
scratch_free(refs_hdr);
return col_vec;
}
#define DERIVED_KEY_MAX_DOMAIN (64LL * 1024 * 1024)
#define DERIVED_KEY_CHUNK ((int64_t)RAY_MORSEL_ELEMS * RAY_DISPATCH_MORSELS * 256)
#ifdef DEBUG
static int64_t g_derived_key_chunk_test = 0;
void ray_derived_key_chunk_set_for_test(int64_t rows) {
g_derived_key_chunk_test = rows > 0 ? rows : 0;
}
#endif
static int64_t derived_key_chunk_rows(void) {
#ifdef DEBUG
if (g_derived_key_chunk_test > 0) return g_derived_key_chunk_test;
#endif
return DERIVED_KEY_CHUNK;
}
typedef struct {
const void* dv;
uint8_t dattrs;
int64_t lo;
const ray_sym_domain_raw_t* raw;
const char** ptr;
uint32_t* len;
uint32_t* off;
ray_str_t* dst;
char* pool;
ray_t* const* rstr;
uint32_t rcnt;
atomic_int late;
} dk_chunk_ctx_t;
static void dk_chunk_scan_fn(void* vctx, uint32_t wid, int64_t start, int64_t end) {
(void)wid;
dk_chunk_ctx_t* c = (dk_chunk_ctx_t*)vctx;
int late = 0;
for (int64_t i = start; i < end; i++) {
int64_t pos = ray_read_sym(c->dv, c->lo + i, RAY_SYM, c->dattrs);
if (c->rstr) {
ray_t* a = (pos >= 0 && pos < (int64_t)c->rcnt) ? c->rstr[pos] : NULL;
if (a && ray_str_len(a) <= UINT32_MAX) {
c->ptr[i] = ray_str_ptr(a);
c->len[i] = (uint32_t)ray_str_len(a);
} else {
c->ptr[i] = NULL;
c->len[i] = UINT32_MAX;
late++;
}
} else if (pos >= 0 && pos < c->raw->count) {
size_t sl = 0;
c->ptr[i] = ray_sym_domain_raw_str(c->raw, pos, &sl);
c->len[i] = (uint32_t)sl;
} else {
c->ptr[i] = NULL;
c->len[i] = UINT32_MAX;
late++;
}
}
if (late) atomic_fetch_add_explicit(&c->late, late, memory_order_relaxed);
}
static void dk_chunk_fill_fn(void* vctx, uint32_t wid, int64_t start, int64_t end) {
(void)wid;
const dk_chunk_ctx_t* c = (const dk_chunk_ctx_t*)vctx;
for (int64_t i = start; i < end; i++) {
ray_str_t* d = &c->dst[i];
uint32_t l = c->len[i];
memset(d, 0, sizeof(*d));
d->len = l;
if (l == 0) continue;
if (l <= RAY_STR_INLINE_MAX) {
memcpy(d->data, c->ptr[i], l);
} else {
memcpy(c->pool + c->off[i], c->ptr[i], l);
memcpy(d->prefix, c->ptr[i], 4);
d->pool_off = c->off[i];
}
}
}
static bool dk_chunk_views(const void* dv, uint8_t dattrs, int64_t lo, int64_t n,
const ray_sym_domain_raw_t* raw, struct ray_sym_domain_s* dom,
const char** ptr, uint32_t* len) {
dk_chunk_ctx_t c;
memset(&c, 0, sizeof(c));
c.dv = dv; c.dattrs = dattrs; c.lo = lo; c.raw = raw;
c.ptr = ptr;
c.len = len;
if (dom == ray_sym_runtime_domain()) {
ray_t** rs = NULL; uint32_t rc = 0;
ray_sym_strings_borrow(&rs, &rc);
c.rstr = rs; c.rcnt = rc;
}
atomic_store_explicit(&c.late, 0, memory_order_relaxed);
ray_pool_t* pool = ray_pool_get();
if (ray_pool_par_dispatch_ok(pool, n, RAY_PARALLEL_THRESHOLD))
ray_pool_dispatch(pool, dk_chunk_scan_fn, &c, n);
else
dk_chunk_scan_fn(&c, 0, 0, n);
if (atomic_load_explicit(&c.late, memory_order_relaxed)) {
for (int64_t i = 0; i < n; i++) {
if (len[i] != UINT32_MAX) continue;
int64_t pos = ray_read_sym(dv, lo + i, RAY_SYM, dattrs);
ray_t* a = ray_sym_domain_str(dom, pos);
size_t sl = a ? ray_str_len(a) : 0;
if (sl > UINT32_MAX) return false;
ptr[i] = a ? ray_str_ptr(a) : "";
len[i] = (uint32_t)sl;
}
}
return true;
}
static ray_t* derived_key_chunk_strs(const void* dv, uint8_t dattrs, int64_t lo, int64_t n,
const ray_sym_domain_raw_t* raw,
struct ray_sym_domain_s* dom) {
ray_t* aux_hdr = NULL;
size_t ptr_sz = (size_t)n * sizeof(const char*);
size_t u32_sz = (size_t)n * sizeof(uint32_t);
char* mem = (char*)scratch_alloc(&aux_hdr, ptr_sz + 2 * u32_sz);
if (!mem) return NULL;
dk_chunk_ctx_t c;
memset(&c, 0, sizeof(c));
c.ptr = (const char**)mem;
c.len = (uint32_t*)(mem + ptr_sz);
c.off = (uint32_t*)(mem + ptr_sz + u32_sz);
if (!dk_chunk_views(dv, dattrs, lo, n, raw, dom, c.ptr, c.len)) { scratch_free(aux_hdr); return NULL; }
ray_pool_t* pool = ray_pool_get();
bool par = ray_pool_par_dispatch_ok(pool, n, RAY_PARALLEL_THRESHOLD);
uint64_t total = 0;
for (int64_t i = 0; i < n; i++) {
if (c.len[i] <= RAY_STR_INLINE_MAX) continue;
c.off[i] = (uint32_t)total;
total += c.len[i];
if (total > UINT32_MAX) { scratch_free(aux_hdr); return NULL; }
}
ray_t* sv = ray_vec_new(RAY_STR, n);
if (!sv || RAY_IS_ERR(sv)) { if (sv) ray_error_free(sv); scratch_free(aux_hdr); return NULL; }
sv->len = n;
if (total > 0) {
ray_t* sp = ray_alloc((size_t)total);
if (!sp || RAY_IS_ERR(sp)) { ray_release(sv); scratch_free(aux_hdr); return NULL; }
sp->type = RAY_U8;
sp->len = (int64_t)total;
sv->str_pool = sp;
c.pool = (char*)ray_data(sp);
}
c.dst = (ray_str_t*)ray_data(sv);
if (par) ray_pool_dispatch(pool, dk_chunk_fill_fn, &c, n);
else dk_chunk_fill_fn(&c, 0, 0, n);
scratch_free(aux_hdr);
return sv;
}
#define DK_PART_BITS 6
#define DK_NPART (1 << DK_PART_BITS)
typedef struct dk_item_s {
const char* p;
uint32_t h;
uint32_t l;
int32_t row;
int32_t pad;
} dk_item_t;
typedef struct {
const char* const* sp;
const uint32_t* sl;
const uint32_t* hash;
int64_t n;
int64_t nt;
int64_t* cnt;
struct dk_item_s* items;
const int64_t* pbase;
const int64_t* tbase;
uint64_t* tab;
int32_t* rep;
int32_t* ord;
const int64_t* did;
int64_t* out;
int64_t* rcnt;
const char** dstr;
size_t* dlen;
uint32_t* dhsh;
} dk_dedupe_ctx_t;
static inline uint32_t dk_part_of(uint32_t h) { return h >> (32 - DK_PART_BITS); }
static void dk_count_fn(void* vctx, uint32_t wid, int64_t t0, int64_t t1) {
(void)wid;
dk_dedupe_ctx_t* c = (dk_dedupe_ctx_t*)vctx;
for (int64_t t = t0; t < t1; t++) {
int64_t* k = c->cnt + t * DK_NPART;
memset(k, 0, DK_NPART * sizeof(int64_t));
int64_t lo = c->n * t / c->nt, hi = c->n * (t + 1) / c->nt;
for (int64_t i = lo; i < hi; i++) k[dk_part_of(c->hash[i])]++;
}
}
static void dk_scatter_fn(void* vctx, uint32_t wid, int64_t t0, int64_t t1) {
(void)wid;
dk_dedupe_ctx_t* c = (dk_dedupe_ctx_t*)vctx;
for (int64_t t = t0; t < t1; t++) {
int64_t* k = c->cnt + t * DK_NPART;
int64_t lo = c->n * t / c->nt, hi = c->n * (t + 1) / c->nt;
for (int64_t i = lo; i < hi; i++) {
uint32_t h = c->hash[i];
dk_item_t* it = &c->items[k[dk_part_of(h)]++];
it->p = c->sp[i];
it->h = h;
it->l = c->sl[i];
it->row = (int32_t)i;
it->pad = 0;
}
}
}
static void dk_dedupe_fn(void* vctx, uint32_t wid, int64_t p0, int64_t p1) {
(void)wid;
dk_dedupe_ctx_t* c = (dk_dedupe_ctx_t*)vctx;
for (int64_t p = p0; p < p1; p++) {
int64_t lo = c->pbase[p], hi = c->pbase[p + 1];
if (lo == hi) continue;
uint64_t* tab = c->tab + c->tbase[p];
uint64_t mask = (uint64_t)(c->tbase[p + 1] - c->tbase[p]) - 1;
memset(tab, 0xff, (size_t)(mask + 1) * sizeof(uint64_t));
const dk_item_t* items = c->items + lo;
for (int64_t k = 0; k < hi - lo; k++) {
const dk_item_t* it = &items[k];
uint32_t h = it->h;
uint32_t l = it->l;
uint64_t s = ((uint64_t)h * 0x9E3779B97F4A7C15ull >> 32) & mask;
for (;;) {
uint64_t e = tab[s];
if (e == UINT64_MAX) {
tab[s] = (uint64_t)h << 32 | (uint32_t)k;
c->rep[it->row] = it->row;
break;
}
const dk_item_t* rt = &items[(uint32_t)e];
if ((uint32_t)(e >> 32) == h && rt->l == l &&
(l == 0 || memcmp(rt->p, it->p, l) == 0)) {
c->rep[it->row] = rt->row;
break;
}
s = (s + 1) & mask;
}
}
}
}
static void dk_reps_count_fn(void* vctx, uint32_t wid, int64_t t0, int64_t t1) {
(void)wid;
const dk_dedupe_ctx_t* c = (const dk_dedupe_ctx_t*)vctx;
for (int64_t t = t0; t < t1; t++) {
int64_t lo = c->n * t / c->nt, hi = c->n * (t + 1) / c->nt, k = 0;
for (int64_t i = lo; i < hi; i++) k += c->rep[i] == (int32_t)i;
c->rcnt[t] = k;
}
}
static void dk_reps_list_fn(void* vctx, uint32_t wid, int64_t t0, int64_t t1) {
(void)wid;
const dk_dedupe_ctx_t* c = (const dk_dedupe_ctx_t*)vctx;
for (int64_t t = t0; t < t1; t++) {
int64_t lo = c->n * t / c->nt, hi = c->n * (t + 1) / c->nt, d = c->rcnt[t];
for (int64_t i = lo; i < hi; i++) {
if (c->rep[i] != (int32_t)i) continue;
c->ord[i] = (int32_t)d;
c->dstr[d] = c->sp[i]; c->dlen[d] = c->sl[i]; c->dhsh[d] = c->hash[i];
d++;
}
}
}
static void dk_assign_fn(void* vctx, uint32_t wid, int64_t lo, int64_t hi) {
(void)wid;
const dk_dedupe_ctx_t* c = (const dk_dedupe_ctx_t*)vctx;
for (int64_t i = lo; i < hi; i++) c->out[i] = c->did[c->ord[c->rep[i]]];
}
static bool dk_intern_views(const char* const* sp, const uint32_t* sl, const uint32_t* hash,
int64_t n, struct ray_sym_domain_s* kdom, int64_t* out) {
if (n <= 0) return true;
ray_pool_t* pool = ray_pool_get();
bool par = ray_pool_par_dispatch_ok(pool, n, RAY_PARALLEL_THRESHOLD);
int64_t nt = 1;
if (par) {
nt = (int64_t)ray_pool_total_workers(pool) * 4;
if (nt > n / 4096) nt = n / 4096;
if (nt < 1) nt = 1;
}
size_t cnt_sz = (size_t)nt * DK_NPART * sizeof(int64_t);
size_t base_sz = (size_t)(DK_NPART + 1) * sizeof(int64_t);
size_t i32_sz = (size_t)n * sizeof(int32_t);
ray_t *hdr = NULL, *ihdr = NULL, *thdr = NULL, *dhdr = NULL;
char* mem = (char*)scratch_alloc(&hdr, cnt_sz + 2 * base_sz + 2 * i32_sz);
dk_item_t* items = (dk_item_t*)scratch_alloc(&ihdr, (size_t)n * sizeof(dk_item_t));
if (!mem || !items) { scratch_free(hdr); scratch_free(ihdr); return false; }
int64_t* cnt = (int64_t*)mem; mem += cnt_sz;
int64_t* pbase = (int64_t*)mem; mem += base_sz;
int64_t* tbase = (int64_t*)mem; mem += base_sz;
int32_t* rep = (int32_t*)mem; mem += i32_sz;
int32_t* ord = (int32_t*)mem;
dk_dedupe_ctx_t c = {
.sp = sp, .sl = sl, .hash = hash, .n = n, .nt = nt, .cnt = cnt, .items = items,
.pbase = pbase, .tbase = tbase, .rep = rep, .ord = ord, .out = out,
};
if (par) ray_pool_dispatch_n(pool, dk_count_fn, &c, (uint32_t)nt);
else dk_count_fn(&c, 0, 0, nt);
int64_t run = 0, tsz = 0;
for (int p = 0; p < DK_NPART; p++) {
pbase[p] = run;
tbase[p] = tsz;
int64_t pc = 0;
for (int64_t t = 0; t < nt; t++) {
int64_t k = cnt[t * DK_NPART + p];
cnt[t * DK_NPART + p] = run + pc;
pc += k;
}
run += pc;
int64_t slots = 16;
while (slots < 2 * pc) slots <<= 1;
tsz += pc ? slots : 0;
}
pbase[DK_NPART] = run;
tbase[DK_NPART] = tsz;
c.tab = (uint64_t*)scratch_alloc(&thdr, (size_t)(tsz ? tsz : 1) * sizeof(uint64_t));
if (!c.tab) { scratch_free(ihdr); scratch_free(hdr); return false; }
if (par) ray_pool_dispatch_n(pool, dk_scatter_fn, &c, (uint32_t)nt);
else dk_scatter_fn(&c, 0, 0, nt);
if (par) ray_pool_dispatch_n(pool, dk_dedupe_fn, &c, DK_NPART);
else dk_dedupe_fn(&c, 0, 0, DK_NPART);
ray_profile_tick("derived key: keys deduplicated");
scratch_free(thdr);
scratch_free(ihdr);
c.rcnt = cnt;
if (par) ray_pool_dispatch_n(pool, dk_reps_count_fn, &c, (uint32_t)nt);
else dk_reps_count_fn(&c, 0, 0, nt);
int64_t nd = 0;
for (int64_t t = 0; t < nt; t++) { int64_t k = cnt[t]; cnt[t] = nd; nd += k; }
size_t dstr_sz = (size_t)nd * sizeof(const char*);
size_t dlen_sz = (size_t)nd * sizeof(size_t);
size_t did_sz = (size_t)nd * sizeof(int64_t);
char* dm = (char*)scratch_alloc(&dhdr, dstr_sz + dlen_sz + did_sz + (size_t)nd * sizeof(uint32_t));
if (!dm) { scratch_free(hdr); return false; }
const char** dstr = (const char**)dm; dm += dstr_sz;
size_t* dlen = (size_t*)dm; dm += dlen_sz;
int64_t* did = (int64_t*)dm; dm += did_sz;
uint32_t* dhsh = (uint32_t*)dm;
c.dstr = dstr; c.dlen = dlen; c.dhsh = dhsh;
if (par) ray_pool_dispatch_n(pool, dk_reps_list_fn, &c, (uint32_t)nt);
else dk_reps_list_fn(&c, 0, 0, nt);
ray_profile_tick("derived key: representatives listed");
bool ok = ray_sym_domain_intern_batch(kdom, nd, dstr, dlen, dhsh, did);
ray_profile_tick("derived key: key domain interned");
if (ok) {
c.did = did;
if (par) ray_pool_dispatch(pool, dk_assign_fn, &c, n);
else dk_assign_fn(&c, 0, 0, n);
ray_profile_tick("derived key: keys interned");
}
scratch_free(dhdr);
scratch_free(hdr);
return ok;
}
typedef struct {
const ray_str_t* desc;
const char* pool;
const char** sp;
uint32_t* sl;
uint32_t* hash;
} dk_hash_ctx_t;
static void dk_hash_fn(void* vctx, uint32_t wid, int64_t start, int64_t end) {
(void)wid;
const dk_hash_ctx_t* c = (const dk_hash_ctx_t*)vctx;
for (int64_t i = start; i < end; i++) {
const ray_str_t* d = &c->desc[i];
const char* p = ray_str_t_ptr(d, c->pool);
c->sp[i] = p;
c->sl[i] = d->len;
c->hash[i] = (uint32_t)ray_hash_bytes(p, d->len);
}
}
static bool derived_key_intern_chunk(ray_t* kc, int64_t n, struct ray_sym_domain_s* kdom,
int64_t* out) {
dk_hash_ctx_t hc;
memset(&hc, 0, sizeof(hc));
str_resolve(kc, &hc.desc, &hc.pool);
ray_t* hdr = NULL;
char* mem = (char*)scratch_alloc(&hdr, (size_t)n * (sizeof(const char*) + 2 * sizeof(uint32_t)));
if (!mem) return false;
hc.sp = (const char**)mem;
hc.sl = (uint32_t*)(mem + (size_t)n * sizeof(const char*));
hc.hash = hc.sl + n;
ray_pool_t* rp = ray_pool_get();
if (ray_pool_par_dispatch_ok(rp, n, RAY_PARALLEL_THRESHOLD))
ray_pool_dispatch(rp, dk_hash_fn, &hc, n);
else
dk_hash_fn(&hc, 0, 0, n);
bool ok = dk_intern_views(hc.sp, hc.sl, hc.hash, n, kdom, out);
scratch_free(hdr);
return ok;
}
static bool derived_key_intern_sym_cells(ray_t* kc, int64_t n, struct ray_sym_domain_s* kdom,
int64_t* out) {
ray_t* hdr = NULL;
char* mem = (char*)scratch_alloc(&hdr, (size_t)n * (sizeof(const char*) + 2 * sizeof(uint32_t)));
if (!mem) return false;
const char** sp = (const char**)mem;
uint32_t* sl = (uint32_t*)(mem + (size_t)n * sizeof(const char*));
uint32_t* hash = sl + n;
for (int64_t i = 0; i < n; i++) {
ray_t* a = ray_sym_vec_cell(kc, i);
size_t l = a ? ray_str_len(a) : 0;
if (l > UINT32_MAX) { scratch_free(hdr); return false; }
sp[i] = a ? ray_str_ptr(a) : "";
sl[i] = (uint32_t)l;
hash[i] = (uint32_t)ray_hash_bytes(sp[i], l);
}
bool ok = dk_intern_views(sp, sl, hash, n, kdom, out);
scratch_free(hdr);
return ok;
}
typedef struct {
const void* cd;
uint8_t attrs;
int64_t dn;
const int32_t* pos;
const int64_t* key;
int64_t* out;
bool sparse;
} dk_rows_ctx_t;
static void dk_spread_fn(void* vctx, uint32_t wid, int64_t start, int64_t end) {
(void)wid;
const dk_rows_ctx_t* c = (const dk_rows_ctx_t*)vctx;
if (c->sparse) {
if (c->key) {
for (int64_t r = start; r < end; r++) {
int32_t s = c->pos[ray_read_sym(c->cd, r, RAY_SYM, c->attrs)];
c->out[r] = c->key[s >= 0 ? s : 0];
}
} else {
for (int64_t r = start; r < end; r++) {
int32_t s = c->pos[ray_read_sym(c->cd, r, RAY_SYM, c->attrs)];
c->out[r] = s >= 0 ? s : 0;
}
}
return;
}
if (c->key) {
for (int64_t r = start; r < end; r++)
c->out[r] = c->key[c->pos[ray_read_sym(c->cd, r, RAY_SYM, c->attrs)]];
} else {
for (int64_t r = start; r < end; r++)
c->out[r] = c->pos[ray_read_sym(c->cd, r, RAY_SYM, c->attrs)];
}
}
typedef struct {
ray_t* sel;
int64_t nrows;
int64_t seg, n_segs;
const uint8_t* flags;
const uint32_t* offs;
const uint16_t* idx;
int64_t base;
int64_t r, end;
int64_t k, kend;
int mode;
} dk_sel_it_t;
static void dk_sel_it_init(dk_sel_it_t* it, ray_t* sel, int64_t nrows) {
memset(it, 0, sizeof(*it));
it->sel = sel; it->nrows = nrows; it->seg = -1;
if (!sel) { it->mode = 1; it->r = 0; it->end = nrows; return; }
ray_rowsel_t* m = ray_rowsel_meta(sel);
it->n_segs = m->n_segs;
it->flags = ray_rowsel_flags(sel);
it->offs = ray_rowsel_offsets(sel);
it->idx = ray_rowsel_idx(sel);
}
static inline int64_t dk_sel_it_next(dk_sel_it_t* it) {
for (;;) {
if (it->mode == 1) { if (it->r < it->end) return it->r++; }
else if (it->mode == 2) { if (it->k < it->kend) return it->base + it->idx[it->k++]; }
if (!it->sel || ++it->seg >= it->n_segs) { it->mode = 0; return -1; }
uint8_t f = it->flags[it->seg];
it->base = it->seg * RAY_MORSEL_ELEMS;
if (f == RAY_SEL_NONE) { it->mode = 0; continue; }
if (f == RAY_SEL_ALL) {
it->mode = 1; it->r = it->base;
it->end = it->base + RAY_MORSEL_ELEMS;
if (it->end > it->nrows) it->end = it->nrows;
} else {
it->mode = 2; it->k = it->offs[it->seg]; it->kend = it->offs[it->seg + 1];
}
}
}
enum { DKP_STR = 0, DKP_I64, DKP_BOOL };
enum {
DKO_COL, DKO_LSTR, DKO_LIT,
DKO_EQ, DKO_NE, DKO_LT, DKO_LE, DKO_GT, DKO_GE, DKO_SEQ, DKO_SNE,
DKO_ADD, DKO_SUB, DKO_MUL, DKO_AND, DKO_OR, DKO_NOT,
DKO_NILS, DKO_NILI, DKO_IF, DKO_FIND, DKO_SUBSTR, DKO_STRLEN,
};
#define DKP_MAX_INS 128
#define DKP_MAX_PATS 16
#define DKP_MAX_LET 32
typedef struct {
uint8_t op, kind;
uint16_t a, b, c;
int64_t imm;
const char* sp;
uint32_t sl;
} dkp_ins_t;
typedef struct {
int n, col, npat, res, nex;
uint8_t ex[DKP_MAX_INS];
dkp_ins_t ins[DKP_MAX_INS];
ray_strpat_t pat[DKP_MAX_PATS];
} dkp_prog_t;
typedef struct { const char* p; int64_t v; } dkp_reg_t;
static int dkp_emit(dkp_prog_t* P, int op, int kind, int a, int b, int c) {
if (P->n >= DKP_MAX_INS) return -1;
dkp_ins_t* x = &P->ins[P->n];
memset(x, 0, sizeof(*x));
x->op = (uint8_t)op; x->kind = (uint8_t)kind;
x->a = (uint16_t)a; x->b = (uint16_t)b; x->c = (uint16_t)c;
return P->n++;
}
static int dkp_lit(dkp_prog_t* P, int kind, int64_t v) {
int r = dkp_emit(P, DKO_LIT, kind, 0, 0, 0);
if (r >= 0) P->ins[r].imm = v;
return r;
}
static bool dkp_sym_is(ray_t* e, const char* name) {
if (!e || e->type != -RAY_SYM) return false;
ray_t* s = ray_sym_str(e->i64);
size_t n = strlen(name);
return s && ray_str_len(s) == n && memcmp(ray_str_ptr(s), name, n) == 0;
}
static int dkp_compile(dkp_prog_t* P, ray_t* e, int64_t col, int64_t* bsym, int* breg, int nb) {
if (!e) return -1;
#define DKP_K(r) (P->ins[(r)].kind)
switch (e->type) {
case -RAY_SYM:
if (e->attrs & ATTR_QUOTED) return -1;
for (int i = nb - 1; i >= 0; i--) if (bsym[i] == e->i64) return breg[i];
if (e->i64 != col) return -1;
if (P->col < 0) P->col = dkp_emit(P, DKO_COL, DKP_STR, 0, 0, 0);
return P->col;
case -RAY_I64: return dkp_lit(P, DKP_I64, e->i64);
case -RAY_BOOL: return dkp_lit(P, DKP_BOOL, e->b8 ? 1 : 0);
case -RAY_STR: {
size_t l = ray_str_len(e);
if (l > UINT32_MAX) return -1;
int r = dkp_emit(P, DKO_LSTR, DKP_STR, 0, 0, 0);
if (r >= 0) { P->ins[r].sp = ray_str_ptr(e); P->ins[r].sl = (uint32_t)l; }
return r;
}
case RAY_LIST: break;
default: return -1;
}
int64_t n = ray_len(e);
if (n < 1) return -1;
ray_t** el = (ray_t**)ray_data(e);
ray_t* h = el[0];
if (!h || h->type != -RAY_SYM || (h->attrs & ATTR_QUOTED)) return -1;
ray_t* hs = ray_sym_str(h->i64);
if (!hs) return -1;
const char* hp = ray_str_ptr(hs);
size_t hl = ray_str_len(hs);
#define DKP_IS(lit) (hl == sizeof(lit) - 1 && memcmp(hp, lit, hl) == 0)
#define DKP_ARG(i) dkp_compile(P, el[(i)], col, bsym, breg, nb)
if (DKP_IS("let")) {
if (n != 4 || !el[1] || el[1]->type != -RAY_SYM || nb >= DKP_MAX_LET) return -1;
int v = DKP_ARG(2);
if (v < 0) return -1;
bsym[nb] = el[1]->i64;
breg[nb] = v;
return dkp_compile(P, el[3], col, bsym, breg, nb + 1);
}
if (DKP_IS("if")) {
if (n != 4) return -1;
int c = DKP_ARG(1), t = DKP_ARG(2), f = DKP_ARG(3);
if (c < 0 || t < 0 || f < 0 || DKP_K(c) != DKP_BOOL || DKP_K(t) != DKP_K(f)) return -1;
return dkp_emit(P, DKO_IF, DKP_K(t), c, t, f);
}
if (DKP_IS("cond")) {
int acc = -1;
for (int64_t i = n - 1; i >= 1; i--) {
ray_t* cl = el[i];
if (!cl || cl->type != RAY_LIST || ray_len(cl) != 2) return -1;
ray_t** cp = (ray_t**)ray_data(cl);
if (dkp_sym_is(cp[0], "else")) {
if (i != n - 1) return -1;
acc = dkp_compile(P, cp[1], col, bsym, breg, nb);
if (acc < 0) return -1;
continue;
}
if (acc < 0) return -1;
int c = dkp_compile(P, cp[0], col, bsym, breg, nb);
int t = dkp_compile(P, cp[1], col, bsym, breg, nb);
if (c < 0 || t < 0 || DKP_K(c) != DKP_BOOL || DKP_K(t) != DKP_K(acc)) return -1;
acc = dkp_emit(P, DKO_IF, DKP_K(t), c, t, acc);
if (acc < 0) return -1;
}
return acc;
}
if (DKP_IS("and") || DKP_IS("or")) {
if (n < 2 || n > 65) return -1;
int op = DKP_IS("and") ? DKO_AND : DKO_OR;
int acc = DKP_ARG(1);
if (acc < 0 || DKP_K(acc) != DKP_BOOL) return -1;
for (int64_t i = 2; i < n; i++) {
int r = DKP_ARG(i);
if (r < 0 || DKP_K(r) != DKP_BOOL) return -1;
acc = dkp_emit(P, op, DKP_BOOL, acc, r, 0);
if (acc < 0) return -1;
}
return acc;
}
if (DKP_IS("not")) {
if (n != 2) return -1;
int a = DKP_ARG(1);
if (a < 0 || DKP_K(a) != DKP_BOOL) return -1;
return dkp_emit(P, DKO_NOT, DKP_BOOL, a, 0, 0);
}
if (DKP_IS("nil?")) {
if (n != 2) return -1;
int a = DKP_ARG(1);
if (a < 0 || DKP_K(a) == DKP_BOOL) return -1;
return dkp_emit(P, DKP_K(a) == DKP_STR ? DKO_NILS : DKO_NILI, DKP_BOOL, a, 0, 0);
}
if (DKP_IS("strlen")) {
if (n != 2) return -1;
int a = DKP_ARG(1);
if (a < 0 || DKP_K(a) != DKP_STR) return -1;
return dkp_emit(P, DKO_STRLEN, DKP_I64, a, 0, 0);
}
if (DKP_IS("str-find")) {
if (n != 3 || !el[2] || el[2]->type != -RAY_STR || P->npat >= DKP_MAX_PATS) return -1;
int a = DKP_ARG(1);
if (a < 0 || DKP_K(a) != DKP_STR) return -1;
if (!ray_strpat_compile(ray_str_ptr(el[2]), ray_str_len(el[2]), &P->pat[P->npat])) return -1;
int r = dkp_emit(P, DKO_FIND, DKP_I64, a, 0, 0);
if (r >= 0) P->ins[r].imm = P->npat++;
return r;
}
if (DKP_IS("substr")) {
if (n != 4) return -1;
int a = DKP_ARG(1), b = DKP_ARG(2), c = DKP_ARG(3);
if (a < 0 || b < 0 || c < 0 || DKP_K(a) != DKP_STR || DKP_K(b) != DKP_I64 ||
DKP_K(c) != DKP_I64) return -1;
return dkp_emit(P, DKO_SUBSTR, DKP_STR, a, b, c);
}
if (DKP_IS("within")) {
ray_t* rg = n == 3 ? el[2] : NULL;
if (!rg || !ray_is_vec(rg) || rg->type != RAY_I64 || rg->len != 2 ||
(rg->attrs & RAY_ATTR_SLICE)) return -1;
int x = DKP_ARG(1);
if (x < 0 || DKP_K(x) != DKP_I64) return -1;
const int64_t* rv = (const int64_t*)ray_data(rg);
int lo = dkp_lit(P, DKP_I64, rv[0]);
int hi = dkp_lit(P, DKP_I64, rv[1]);
if (lo < 0 || hi < 0) return -1;
int ge = dkp_emit(P, DKO_GE, DKP_BOOL, x, lo, 0);
int le = dkp_emit(P, DKO_LE, DKP_BOOL, x, hi, 0);
if (ge < 0 || le < 0) return -1;
return dkp_emit(P, DKO_AND, DKP_BOOL, ge, le, 0);
}
int bop = DKP_IS("==") ? DKO_EQ : DKP_IS("!=") ? DKO_NE : DKP_IS("<") ? DKO_LT :
DKP_IS("<=") ? DKO_LE : DKP_IS(">") ? DKO_GT : DKP_IS(">=") ? DKO_GE :
DKP_IS("+") ? DKO_ADD : DKP_IS("-") ? DKO_SUB : DKP_IS("*") ? DKO_MUL : -1;
if (bop < 0 || n != 3) return -1;
int a = DKP_ARG(1), b = DKP_ARG(2);
if (a < 0 || b < 0 || DKP_K(a) != DKP_K(b)) return -1;
if (DKP_K(a) == DKP_STR) {
if (bop != DKO_EQ && bop != DKO_NE) return -1;
return dkp_emit(P, bop == DKO_EQ ? DKO_SEQ : DKO_SNE, DKP_BOOL, a, b, 0);
}
if (DKP_K(a) != DKP_I64) return -1;
return dkp_emit(P, bop, bop >= DKO_ADD ? DKP_I64 : DKP_BOOL, a, b, 0);
#undef DKP_ARG
#undef DKP_IS
#undef DKP_K
}
static bool dkp_build(dkp_prog_t* P, ray_t* by_expr, int64_t col_sym) {
memset(P, 0, sizeof(*P));
P->col = -1;
int64_t bsym[DKP_MAX_LET];
int breg[DKP_MAX_LET];
int r = dkp_compile(P, by_expr, col_sym, bsym, breg, 0);
P->res = r;
for (int k = 0; k < P->n; k++)
if (P->ins[k].op != DKO_LIT && P->ins[k].op != DKO_LSTR) P->ex[P->nex++] = (uint8_t)k;
return r >= 0 && P->ins[r].kind == DKP_STR;
}
static inline int64_t dkp_find_literal(const char* s, size_t sn, const char* nd, size_t nl) {
if (nl == 0) return 0;
if (nl > sn) return NULL_I64;
const char* hay = s;
size_t rem = sn;
while (rem >= nl) {
const char* hit = (const char*)memchr(hay, nd[0], rem - nl + 1);
if (!hit) return NULL_I64;
if (memcmp(hit + 1, nd + 1, nl - 1) == 0) return (int64_t)(hit - s);
rem -= (size_t)(hit - hay) + 1;
hay = hit + 1;
}
return NULL_I64;
}
#define DKP_BATCH 64
static void dkp_run_batch(const dkp_prog_t* P, dkp_reg_t* R, const char* const* sp,
const uint32_t* sl, int m) {
for (int j = 0; j < P->nex; j++) {
int k = P->ex[j];
const dkp_ins_t* x = &P->ins[k];
dkp_reg_t* o = R + (size_t)k * DKP_BATCH;
const dkp_reg_t* a = R + (size_t)x->a * DKP_BATCH;
const dkp_reg_t* b = R + (size_t)x->b * DKP_BATCH;
const dkp_reg_t* c = R + (size_t)x->c * DKP_BATCH;
switch (x->op) {
case DKO_COL:
for (int t = 0; t < m; t++) { o[t].p = sp[t]; o[t].v = sl[t]; }
break;
case DKO_EQ: for (int t = 0; t < m; t++) o[t].v = a[t].v == b[t].v; break;
case DKO_NE: for (int t = 0; t < m; t++) o[t].v = a[t].v != b[t].v; break;
case DKO_LT: for (int t = 0; t < m; t++) o[t].v = a[t].v < b[t].v; break;
case DKO_LE: for (int t = 0; t < m; t++) o[t].v = a[t].v <= b[t].v; break;
case DKO_GT: for (int t = 0; t < m; t++) o[t].v = a[t].v > b[t].v; break;
case DKO_GE: for (int t = 0; t < m; t++) o[t].v = a[t].v >= b[t].v; break;
case DKO_SEQ:
case DKO_SNE: {
int64_t want = x->op == DKO_SEQ;
for (int t = 0; t < m; t++) {
bool eq = a[t].v == b[t].v &&
(a[t].v == 0 || memcmp(a[t].p, b[t].p, (size_t)a[t].v) == 0);
o[t].v = eq ? want : !want;
}
break;
}
case DKO_ADD:
for (int t = 0; t < m; t++)
o[t].v = (a[t].v == NULL_I64 || b[t].v == NULL_I64) ? NULL_I64
: (int64_t)((uint64_t)a[t].v + (uint64_t)b[t].v);
break;
case DKO_SUB:
for (int t = 0; t < m; t++)
o[t].v = (a[t].v == NULL_I64 || b[t].v == NULL_I64) ? NULL_I64
: (int64_t)((uint64_t)a[t].v - (uint64_t)b[t].v);
break;
case DKO_MUL:
for (int t = 0; t < m; t++)
o[t].v = (a[t].v == NULL_I64 || b[t].v == NULL_I64) ? NULL_I64
: (int64_t)((uint64_t)a[t].v * (uint64_t)b[t].v);
break;
case DKO_AND: for (int t = 0; t < m; t++) o[t].v = a[t].v & b[t].v; break;
case DKO_OR: for (int t = 0; t < m; t++) o[t].v = a[t].v | b[t].v; break;
case DKO_NOT: for (int t = 0; t < m; t++) o[t].v = !a[t].v; break;
case DKO_NILS: for (int t = 0; t < m; t++) o[t].v = a[t].v == 0; break;
case DKO_NILI: for (int t = 0; t < m; t++) o[t].v = a[t].v == NULL_I64; break;
case DKO_IF: for (int t = 0; t < m; t++) o[t] = a[t].v ? b[t] : c[t]; break;
case DKO_FIND: {
const ray_strpat_t* pt = &P->pat[x->imm];
if (pt->literal) {
for (int t = 0; t < m; t++)
o[t].v = dkp_find_literal(a[t].p, (size_t)a[t].v, pt->pat, pt->pat_len);
} else {
for (int t = 0; t < m; t++) {
size_t pos = 0;
o[t].v = ray_strpat_find(pt, a[t].p, (size_t)a[t].v, &pos) ? (int64_t)pos : NULL_I64;
}
}
break;
}
case DKO_SUBSTR:
for (int t = 0; t < m; t++) {
int64_t st = b[t].v, ln = c[t].v, l = a[t].v;
o[t].p = a[t].p; o[t].v = 0;
if (st == NULL_I64 || ln == NULL_I64) continue;
st -= 1;
if (st < 0) st = 0;
if (st >= l) continue;
if (ln < 0 || ln > l - st) ln = l - st;
if (ln <= 0) continue;
o[t].p = a[t].p + st;
o[t].v = ln;
}
break;
case DKO_STRLEN: for (int t = 0; t < m; t++) o[t].v = a[t].v ? a[t].v : NULL_I64; break;
default: break;
}
}
}
typedef struct {
const dkp_prog_t* prog;
const char** sp;
uint32_t* sl;
uint32_t* hash;
dkp_reg_t* regs;
} dkp_run_ctx_t;
static void dkp_run_fn(void* vctx, uint32_t wid, int64_t start, int64_t end) {
const dkp_run_ctx_t* c = (const dkp_run_ctx_t*)vctx;
const dkp_prog_t* P = c->prog;
dkp_reg_t* R = c->regs + (size_t)wid * (size_t)P->n * DKP_BATCH;
const dkp_reg_t* res = R + (size_t)P->res * DKP_BATCH;
for (int k = 0; k < P->n; k++) {
dkp_reg_t* o = R + (size_t)k * DKP_BATCH;
if (P->ins[k].op == DKO_LIT)
for (int t = 0; t < DKP_BATCH; t++) o[t].v = P->ins[k].imm;
else if (P->ins[k].op == DKO_LSTR)
for (int t = 0; t < DKP_BATCH; t++) { o[t].p = P->ins[k].sp; o[t].v = P->ins[k].sl; }
}
for (int64_t i = start; i < end; i += DKP_BATCH) {
int m = end - i < DKP_BATCH ? (int)(end - i) : DKP_BATCH;
dkp_run_batch(P, R, c->sp + i, c->sl + i, m);
for (int t = 0; t < m; t++) {
const char* p = res[t].v ? res[t].p : "";
uint32_t l = (uint32_t)res[t].v;
c->sp[i + t] = p;
c->sl[i + t] = l;
c->hash[i + t] = (uint32_t)ray_hash_bytes(p, l);
}
}
}
static bool dkp_chunk_keys(const dkp_prog_t* P, const void* dv, uint8_t dattrs, int64_t lo,
int64_t n, const ray_sym_domain_raw_t* raw,
struct ray_sym_domain_s* dom, struct ray_sym_domain_s* kdom,
int64_t* kd) {
ray_pool_t* pool = ray_pool_get();
bool par = ray_pool_par_dispatch_ok(pool, n, RAY_PARALLEL_THRESHOLD);
uint32_t nwork = par ? ray_pool_total_workers(pool) : 1;
ray_t *hdr = NULL, *rhdr = NULL;
char* mem = (char*)scratch_alloc(&hdr, (size_t)n * (sizeof(const char*) + 2 * sizeof(uint32_t)));
dkp_reg_t* regs = (dkp_reg_t*)scratch_alloc(&rhdr, (size_t)nwork * (size_t)P->n *
DKP_BATCH * sizeof(dkp_reg_t));
if (!mem || !regs) { scratch_free(hdr); scratch_free(rhdr); return false; }
dkp_run_ctx_t c = { .prog = P, .regs = regs };
c.sp = (const char**)mem;
c.sl = (uint32_t*)(mem + (size_t)n * sizeof(const char*));
c.hash = c.sl + n;
bool ok = dk_chunk_views(dv, dattrs, lo, n, raw, dom, c.sp, c.sl);
if (ok) {
if (par) ray_pool_dispatch(pool, dkp_run_fn, &c, n);
else dkp_run_fn(&c, 0, 0, n);
ray_profile_tick("derived key: program over values");
ok = dk_intern_views(c.sp, c.sl, c.hash, n, kdom, kd);
}
scratch_free(rhdr);
scratch_free(hdr);
return ok;
}
static ray_t* derived_key_str_chunks(ray_t* by_expr, int64_t col_sym, ray_t* dom_vec,
struct ray_sym_domain_s* dom, int64_t du) {
if (!dom || du <= 0) return NULL;
ray_sym_domain_raw_t raw = {0};
(void)ray_sym_domain_raw_pin(dom, &raw);
int8_t sym_out = 0;
{
ray_t* probe = ray_table_new(0);
if (probe && !RAY_IS_ERR(probe)) probe = ray_table_add_col(probe, col_sym, dom_vec);
if (!probe || RAY_IS_ERR(probe)) { if (probe) ray_error_free(probe); goto unpin_null; }
ray_graph_t* gp = ray_graph_new(probe);
if (gp) {
ray_op_t* kop = compile_expr_dag(gp, by_expr);
if (kop && kop->opcode != OP_CONST) sym_out = kop->out_type;
ray_graph_free(gp);
}
ray_release(probe);
}
if (sym_out != RAY_SYM) goto unpin_null;
ray_t* key_dom = ray_vec_new(RAY_SYM, du);
if (!key_dom || RAY_IS_ERR(key_dom)) { if (key_dom) ray_error_free(key_dom); goto unpin_null; }
key_dom->len = du;
int64_t* kd = (int64_t*)ray_data(key_dom);
const void* dv = ray_data(dom_vec);
{
struct ray_sym_domain_s* kdom = ray_sym_domain_new();
if (!kdom) { ray_release(key_dom); goto unpin_null; }
key_dom->sym_domain = kdom;
}
struct ray_sym_domain_s* kdom = key_dom->sym_domain;
dkp_prog_t prog;
bool use_prog = dkp_build(&prog, by_expr, col_sym);
const int64_t chunk = derived_key_chunk_rows();
for (int64_t lo = 0; lo < du; lo += chunk) {
int64_t n = du - lo < chunk ? du - lo : chunk;
if (use_prog) {
if (!dkp_chunk_keys(&prog, dv, dom_vec->attrs, lo, n, &raw, dom, kdom, kd + lo)) goto fail;
continue;
}
ray_t* sv = derived_key_chunk_strs(dv, dom_vec->attrs, lo, n, &raw, dom);
if (!sv) goto fail;
ray_t* mini = ray_table_new(0);
if (mini && !RAY_IS_ERR(mini)) mini = ray_table_add_col(mini, col_sym, sv);
ray_release(sv);
if (!mini || RAY_IS_ERR(mini)) { if (mini) ray_error_free(mini); goto fail; }
ray_t* kc = NULL;
ray_graph_t* g2 = ray_graph_new(mini);
if (g2) {
ray_op_t* kop = compile_expr_dag(g2, by_expr);
if (kop) kop = ray_optimize(g2, kop);
if (kop) kc = ray_execute(g2, kop);
ray_graph_free(g2);
}
ray_release(mini);
if (kc && !RAY_IS_ERR(kc) && ray_is_lazy(kc)) kc = ray_lazy_materialize(kc);
if (!kc || RAY_IS_ERR(kc)) { if (kc) ray_error_free(kc); goto fail; }
if (!ray_is_vec(kc) || kc->len != n) { ray_release(kc); goto fail; }
ray_profile_tick("derived key: chunk DAG");
if (kc->type == RAY_STR) {
if (!derived_key_intern_chunk(kc, n, kdom, kd + lo)) { ray_release(kc); goto fail; }
} else if (RAY_IS_SYM(kc->type)) {
if (!derived_key_intern_sym_cells(kc, n, kdom, kd + lo)) { ray_release(kc); goto fail; }
} else {
ray_release(kc);
goto fail;
}
ray_release(kc);
}
ray_sym_domain_raw_unpin(dom);
return key_dom;
fail:
ray_release(key_dom);
unpin_null:
ray_sym_domain_raw_unpin(dom);
return NULL;
}
enum { DKV_COUNT = 1, DKV_MIN, DKV_MAX, DKV_SUM_LEN, DKV_AVG_LEN };
#define DKV_MAX_AGGS 32
static ray_t* dkv_sym(const char* name) { return ray_sym(ray_sym_intern(name, strlen(name))); }
static ray_t* dkv_call(const char* head, ray_t* a, ray_t* b) {
ray_t* l = ray_list_new(b ? 3 : 2);
if (!l || RAY_IS_ERR(l)) { if (a) ray_release(a); if (b) ray_release(b); return NULL; }
ray_t* h = dkv_sym(head);
l = ray_list_append(l, h); ray_release(h);
l = ray_list_append(l, a); ray_release(a);
if (b) { l = ray_list_append(l, b); ray_release(b); }
if (!l || RAY_IS_ERR(l)) { if (l) ray_error_free(l); return NULL; }
return l;
}
static ray_t* dkv_select(const int64_t* keys, ray_t** vals, int64_t n) {
ray_t* kv = ray_sym_vec_new(RAY_SYM_W64, n);
ray_t* vl = ray_list_new(n);
if (!kv || RAY_IS_ERR(kv) || !vl || RAY_IS_ERR(vl)) {
for (int64_t i = 0; i < n; i++) if (vals[i]) ray_release(vals[i]);
if (kv && !RAY_IS_ERR(kv)) ray_release(kv);
if (vl && !RAY_IS_ERR(vl)) ray_release(vl);
return NULL;
}
kv->len = n;
for (int64_t i = 0; i < n; i++) {
((int64_t*)ray_data(kv))[i] = keys[i];
vl = ray_list_append(vl, vals[i]);
ray_release(vals[i]);
}
ray_t* d = ray_dict_new(kv, vl);
if (!d || RAY_IS_ERR(d)) { if (d) ray_error_free(d); return NULL; }
ray_t* sel = ray_list_new(2);
if (!sel || RAY_IS_ERR(sel)) { ray_release(d); return NULL; }
ray_t* h = dkv_sym("select");
sel = ray_list_append(sel, h); ray_release(h);
sel = ray_list_append(sel, d); ray_release(d);
return sel;
}
static bool dkv_is_col(ray_t* e, int64_t col) {
return e && e->type == -RAY_SYM && !(e->attrs & ATTR_QUOTED) && e->i64 == col;
}
static int dkv_agg_kind(ray_t* v, int64_t col) {
if (!v || v->type != RAY_LIST || ray_len(v) != 2) return 0;
ray_t** e = (ray_t**)ray_data(v);
if (!e[0] || e[0]->type != -RAY_SYM) return 0;
ray_t* hs = ray_sym_str(e[0]->i64);
if (!hs) return 0;
const char* h = ray_str_ptr(hs); size_t hl = ray_str_len(hs);
bool is = false;
#define DKV_IS(lit) (hl == sizeof(lit) - 1 && memcmp(h, lit, hl) == 0)
if (DKV_IS("count") || DKV_IS("min") || DKV_IS("max")) {
if (!dkv_is_col(e[1], col)) return 0;
return DKV_IS("count") ? DKV_COUNT : DKV_IS("min") ? DKV_MIN : DKV_MAX;
}
is = DKV_IS("sum") || DKV_IS("avg");
if (!is) return 0;
ray_t* a = e[1];
if (!a || a->type != RAY_LIST || ray_len(a) != 2) return 0;
ray_t** ae = (ray_t**)ray_data(a);
if (!ae[0] || ae[0]->type != -RAY_SYM) return 0;
ray_t* as = ray_sym_str(ae[0]->i64);
if (!as || ray_str_len(as) != 6 || memcmp(ray_str_ptr(as), "strlen", 6) != 0) return 0;
if (!dkv_is_col(ae[1], col)) return 0;
return DKV_IS("sum") ? DKV_SUM_LEN : DKV_AVG_LEN;
#undef DKV_IS
}
typedef struct {
const void* hc;
uint8_t attrs;
const int64_t* cnt;
int64_t* nn;
int64_t* lenw;
struct ray_sym_domain_s* dom;
ray_sym_domain_raw_t raw;
bool raw_ok;
ray_t* const* rstr;
uint32_t rcnt;
atomic_int late;
} dkv_len_ctx_t;
static void dkv_len_fn(void* vctx, uint32_t wid, int64_t lo, int64_t hi) {
(void)wid;
dkv_len_ctx_t* c = (dkv_len_ctx_t*)vctx;
int late = 0;
for (int64_t i = lo; i < hi; i++) {
int64_t pos = ray_read_sym(c->hc, i, RAY_SYM, c->attrs);
c->nn[i] = pos > 0 ? c->cnt[i] : 0;
if (pos <= 0) { c->lenw[i] = 0; continue; }
if (c->rstr) {
ray_t* a = pos < (int64_t)c->rcnt ? c->rstr[pos] : NULL;
if (a) c->lenw[i] = c->cnt[i] * (int64_t)ray_str_len(a);
else { c->lenw[i] = INT64_MIN; late++; }
} else if (c->raw_ok && pos < c->raw.count) {
size_t l; (void)ray_sym_domain_raw_str(&c->raw, pos, &l);
c->lenw[i] = c->cnt[i] * (int64_t)l;
} else {
c->lenw[i] = INT64_MIN;
late++;
}
}
if (late) atomic_fetch_add_explicit(&c->late, late, memory_order_relaxed);
}
typedef struct {
const void* cd;
uint8_t attrs;
int64_t dom_count;
uint64_t* bits;
const int64_t* wrank;
const int64_t* hnd;
void* ncd;
uint8_t nattrs;
int64_t* nnd;
atomic_int bad;
} dkv_order_ctx_t;
static void dkv_order_mark_fn(void* vctx, uint32_t wid, int64_t lo, int64_t hi) {
(void)wid;
dkv_order_ctx_t* c = (dkv_order_ctx_t*)vctx;
for (int64_t i = lo; i < hi; i++) {
int64_t pos = ray_read_sym(c->cd, i, RAY_SYM, c->attrs);
if (pos < 0 || pos >= c->dom_count) {
atomic_store_explicit(&c->bad, 1, memory_order_relaxed);
return;
}
atomic_fetch_or_explicit((_Atomic(uint64_t)*)&c->bits[pos >> 6],
(uint64_t)1 << (pos & 63), memory_order_relaxed);
}
}
static void dkv_order_scatter_fn(void* vctx, uint32_t wid, int64_t lo, int64_t hi) {
(void)wid;
const dkv_order_ctx_t* c = (const dkv_order_ctx_t*)vctx;
for (int64_t i = lo; i < hi; i++) {
int64_t pos = ray_read_sym(c->cd, i, RAY_SYM, c->attrs);
int64_t w = pos >> 6;
int64_t r = c->wrank[w] + __builtin_popcountll(c->bits[w] & (((uint64_t)1 << (pos & 63)) - 1));
ray_write_sym(c->ncd, r, (uint64_t)pos, RAY_SYM, c->nattrs);
c->nnd[r] = c->hnd[i];
}
}
static ray_t* dkv_order_by_position(ray_t* H, int64_t dom_count) {
ray_t* Hc = ray_table_get_col_idx(H, 0);
ray_t* Hn = ray_table_get_col_idx(H, 1);
int64_t du = ray_table_nrows(H);
if (dom_count <= 0 || du <= 1) return NULL;
int64_t nw = (dom_count + 63) / 64;
ray_t *bh = NULL, *rh = NULL;
uint64_t* bits = (uint64_t*)scratch_calloc(&bh, (size_t)nw * sizeof(uint64_t));
int64_t* wrank = (int64_t*)scratch_alloc(&rh, (size_t)nw * sizeof(int64_t));
if (!bits || !wrank) { scratch_free(bh); scratch_free(rh); return NULL; }
dkv_order_ctx_t c;
memset(&c, 0, sizeof(c));
c.cd = ray_data(Hc); c.attrs = Hc->attrs; c.dom_count = dom_count;
c.bits = bits; c.wrank = wrank;
atomic_store_explicit(&c.bad, 0, memory_order_relaxed);
ray_pool_t* pool = ray_pool_get();
bool par = ray_pool_par_dispatch_ok(pool, du, RAY_PARALLEL_THRESHOLD);
if (par) ray_pool_dispatch(pool, dkv_order_mark_fn, &c, du);
else dkv_order_mark_fn(&c, 0, 0, du);
if (atomic_load_explicit(&c.bad, memory_order_relaxed)) { scratch_free(bh); scratch_free(rh); return NULL; }
int64_t run = 0;
for (int64_t w = 0; w < nw; w++) {
wrank[w] = run;
run += __builtin_popcountll(bits[w]);
}
ray_t* nc = ray_sym_vec_new(Hc->attrs & RAY_SYM_W_MASK, du);
ray_t* nn = ray_vec_new(RAY_I64, du);
if (!nc || RAY_IS_ERR(nc) || !nn || RAY_IS_ERR(nn)) {
if (nc && !RAY_IS_ERR(nc)) ray_release(nc);
if (nn && !RAY_IS_ERR(nn)) ray_release(nn);
scratch_free(bh); scratch_free(rh);
return NULL;
}
ray_sym_vec_adopt_domain(nc, Hc);
nc->len = du; nn->len = du;
c.ncd = ray_data(nc);
c.nattrs = nc->attrs;
c.nnd = (int64_t*)ray_data(nn);
c.hnd = (const int64_t*)ray_data(Hn);
if (par) ray_pool_dispatch(pool, dkv_order_scatter_fn, &c, du);
else dkv_order_scatter_fn(&c, 0, 0, du);
if (Hc->attrs & RAY_ATTR_HAS_NULLS) nc->attrs |= RAY_ATTR_HAS_NULLS;
scratch_free(bh); scratch_free(rh);
ray_t* out = ray_table_new(2);
if (!out || RAY_IS_ERR(out)) { ray_release(nc); ray_release(nn); return NULL; }
out = ray_table_add_col(out, ray_table_col_name(H, 0), nc);
out = ray_table_add_col(out, ray_table_col_name(H, 1), nn);
ray_release(nc); ray_release(nn);
if (!out || RAY_IS_ERR(out)) return NULL;
return out;
}
static ray_t* derived_key_vocab_aggs(ray_t* tbl, ray_t* by_expr, ray_t* where_expr,
ray_t** dict_elems, int64_t dict_n,
int64_t from_id, int64_t by_id, int64_t where_id,
int64_t take_id, int64_t asc_id, int64_t desc_id,
int64_t nearest_id) {
if (!by_expr || by_expr->type != RAY_LIST || !tbl) return NULL;
int64_t ref_syms[2];
if (collect_col_refs(by_expr, tbl, ref_syms, 2, 0) != 1) return NULL;
int64_t col = ref_syms[0];
int64_t bound[32];
if (!derived_key_expr_ok(by_expr, tbl, col, bound, 0)) return NULL;
ray_t* C = ray_table_get_col(tbl, col);
int64_t nrows = ray_table_nrows(tbl);
if (!C || C->type != RAY_SYM || !ray_is_vec(C) || C->len != nrows || nrows < 65536) return NULL;
struct ray_sym_domain_s* dom = ray_sym_vec_domain(C);
if (!dom) return NULL;
int64_t alias[DKV_MAX_AGGS]; int kind[DKV_MAX_AGGS]; int n_aggs = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == by_id || kid == where_id || kid == take_id ||
kid == asc_id || kid == desc_id) continue;
if (kid == nearest_id) return NULL;
if (n_aggs >= DKV_MAX_AGGS) return NULL;
int k = dkv_agg_kind(dict_elems[i + 1], col);
if (!k) return NULL;
alias[n_aggs] = kid; kind[n_aggs] = k; n_aggs++;
}
if (n_aggs == 0) return NULL;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid != asc_id && kid != desc_id) continue;
ray_t* v = dict_elems[i + 1];
int64_t nk = (v && v->type == -RAY_SYM) ? 1 : (v && v->type == RAY_SYM) ? ray_len(v) : -1;
if (nk < 0) return NULL;
for (int64_t j = 0; j < nk; j++) {
int64_t sid = (v->type == -RAY_SYM) ? v->i64 : sym_cell_runtime_id(v, j);
bool ok = false;
for (int a = 0; a < n_aggs; a++) if (alias[a] == sid) ok = true;
if (!ok) return NULL;
}
}
int64_t s_t = ray_sym_intern("__dkv_t", 7), s_cnt = ray_sym_intern("__dkv_cnt", 9);
int64_t s_s = ray_sym_intern("__dkv_s", 7), s_k = ray_sym_intern("__dkv_k", 7);
int64_t s_ref = ray_sym_intern("__dkv_ref", 9), s_nn = ray_sym_intern("__dkv_nn", 8);
int64_t s_lenw = ray_sym_intern("__dkv_lenw", 10);
ray_t* H = NULL;
{
int64_t keys[4]; ray_t* vals[4]; int64_t n = 0;
keys[n] = from_id; vals[n++] = ray_sym(s_t);
keys[n] = by_id; vals[n++] = ray_sym(col);
keys[n] = s_cnt; vals[n++] = dkv_call("count", ray_sym(col), NULL);
if (where_expr) { keys[n] = where_id; ray_retain(where_expr); vals[n++] = where_expr; }
for (int64_t i = 0; i < n; i++) if (!vals[i]) { for (int64_t j = 0; j < n; j++) if (vals[j]) ray_release(vals[j]); return NULL; }
ray_t* q = dkv_select(keys, vals, n);
if (!q) return NULL;
if (ray_env_push_query_scope() != RAY_OK) { ray_release(q); return NULL; }
ray_env_set_query_local(s_t, tbl);
H = ray_eval(q);
ray_env_pop_scope();
ray_release(q);
}
if (!H || RAY_IS_ERR(H) || H->type != RAY_TABLE || ray_table_ncols(H) != 2) {
if (H && !RAY_IS_ERR(H)) ray_release(H); else if (H) ray_error_free(H);
return NULL;
}
ray_t* Hc = ray_table_get_col_idx(H, 0);
ray_t* Hn = ray_table_get_col_idx(H, 1);
int64_t du = ray_table_nrows(H);
if (!Hc || !Hn || Hc->type != RAY_SYM || Hn->type != RAY_I64 || du <= 0 ||
ray_sym_vec_domain(Hc) != dom) { ray_release(H); return NULL; }
{
ray_t* Ho = dkv_order_by_position(H, ray_sym_domain_count(dom));
if (Ho) {
ray_release(H);
H = Ho;
Hc = ray_table_get_col_idx(H, 0);
Hn = ray_table_get_col_idx(H, 1);
}
}
ray_profile_tick("derived key: distinct values");
ray_t* key_dom = derived_key_str_chunks(by_expr, col, Hc, dom, du);
if (!key_dom || RAY_IS_ERR(key_dom)) { if (key_dom) ray_error_free(key_dom); ray_release(H); return NULL; }
ray_t* S = NULL;
{
ray_t* nn = ray_vec_new(RAY_I64, du);
ray_t* lenw = ray_vec_new(RAY_I64, du);
if (!nn || RAY_IS_ERR(nn) || !lenw || RAY_IS_ERR(lenw)) {
if (nn && !RAY_IS_ERR(nn)) ray_release(nn);
if (lenw && !RAY_IS_ERR(lenw)) ray_release(lenw);
ray_release(key_dom); ray_release(H); return NULL;
}
nn->len = du; lenw->len = du;
dkv_len_ctx_t lc = {
.hc = ray_data(Hc), .attrs = Hc->attrs, .cnt = (const int64_t*)ray_data(Hn),
.nn = (int64_t*)ray_data(nn), .lenw = (int64_t*)ray_data(lenw), .dom = dom,
};
lc.raw_ok = ray_sym_domain_raw_pin(dom, &lc.raw);
if (dom == ray_sym_runtime_domain()) {
ray_t** rs = NULL; uint32_t rcnt = 0;
ray_sym_strings_borrow(&rs, &rcnt);
lc.rstr = rs; lc.rcnt = rcnt;
}
atomic_store_explicit(&lc.late, 0, memory_order_relaxed);
ray_pool_t* pool = ray_pool_get();
if (ray_pool_par_dispatch_ok(pool, du, RAY_PARALLEL_THRESHOLD))
ray_pool_dispatch(pool, dkv_len_fn, &lc, du);
else
dkv_len_fn(&lc, 0, 0, du);
if (atomic_load_explicit(&lc.late, memory_order_relaxed)) {
for (int64_t i = 0; i < du; i++) {
if (lc.lenw[i] != INT64_MIN) continue;
int64_t pos = ray_read_sym(lc.hc, i, RAY_SYM, lc.attrs);
ray_t* a = ray_sym_domain_str(dom, pos);
lc.lenw[i] = lc.cnt[i] * (a ? (int64_t)ray_str_len(a) : 0);
}
}
S = ray_table_new(5);
if (S && !RAY_IS_ERR(S)) S = ray_table_add_col(S, s_k, key_dom);
if (S && !RAY_IS_ERR(S)) S = ray_table_add_col(S, s_cnt, Hn);
if (S && !RAY_IS_ERR(S)) S = ray_table_add_col(S, s_ref, Hc);
if (S && !RAY_IS_ERR(S)) S = ray_table_add_col(S, s_nn, nn);
if (S && !RAY_IS_ERR(S)) S = ray_table_add_col(S, s_lenw, lenw);
ray_release(nn); ray_release(lenw);
}
ray_release(key_dom);
ray_release(H);
if (!S || RAY_IS_ERR(S)) { if (S) ray_error_free(S); return NULL; }
ray_profile_tick("derived key: per-value table");
ray_t* R = NULL;
{
int64_t keys[DKV_MAX_AGGS + 8]; ray_t* vals[DKV_MAX_AGGS + 8]; int64_t n = 0;
bool bad = false;
for (int64_t i = 0; i + 1 < dict_n && !bad; i += 2) {
int64_t kid = dict_elems[i]->i64;
ray_t* v = NULL;
if (kid == from_id) v = ray_sym(s_s);
else if (kid == by_id) v = ray_sym(s_k);
else if (kid == where_id) continue;
else if (kid == take_id || kid == asc_id || kid == desc_id) { v = dict_elems[i + 1]; ray_retain(v); }
else {
int k = 0;
for (int a = 0; a < n_aggs; a++) if (alias[a] == kid) k = kind[a];
switch (k) {
case DKV_COUNT: v = dkv_call("sum", ray_sym(s_cnt), NULL); break;
case DKV_MIN: v = dkv_call("min", ray_sym(s_ref), NULL); break;
case DKV_MAX: v = dkv_call("max", ray_sym(s_ref), NULL); break;
case DKV_SUM_LEN: v = dkv_call("sum", ray_sym(s_lenw), NULL); break;
case DKV_AVG_LEN: v = dkv_call("/", dkv_call("sum", ray_sym(s_lenw), NULL),
dkv_call("sum", ray_sym(s_nn), NULL)); break;
default: v = NULL;
}
}
if (!v) { bad = true; break; }
keys[n] = kid; vals[n++] = v;
}
if (bad) { for (int64_t j = 0; j < n; j++) ray_release(vals[j]); ray_release(S); return NULL; }
ray_t* q = dkv_select(keys, vals, n);
if (!q) { ray_release(S); return NULL; }
if (ray_env_push_query_scope() != RAY_OK) { ray_release(q); ray_release(S); return NULL; }
ray_env_set_query_local(s_s, S);
R = ray_eval(q);
ray_env_pop_scope();
ray_release(q);
}
ray_release(S);
if (!R || RAY_IS_ERR(R)) { if (R) ray_error_free(R); return NULL; }
if (ray_is_lazy(R)) R = ray_lazy_materialize(R);
if (!R || RAY_IS_ERR(R) || R->type != RAY_TABLE || ray_table_ncols(R) == 0) {
if (R && !RAY_IS_ERR(R)) ray_release(R); else if (R) ray_error_free(R);
return NULL;
}
int64_t rc = ray_table_ncols(R);
if (rc != n_aggs + 1) { ray_release(R); return NULL; }
ray_t* O = ray_table_new(rc);
if (!O || RAY_IS_ERR(O)) { if (O) ray_error_free(O); ray_release(R); return NULL; }
O = ray_table_add_col(O, ray_table_col_name(R, 0), ray_table_get_col_idx(R, 0));
for (int a = 0; a < n_aggs && O && !RAY_IS_ERR(O); a++) {
ray_t* col = ray_table_get_col(R, alias[a]);
if (!col) { ray_release(O); ray_release(R); return NULL; }
O = ray_table_add_col(O, alias[a], col);
}
ray_release(R);
if (!O || RAY_IS_ERR(O)) { if (O) ray_error_free(O); return NULL; }
int64_t kname = derived_key_name(by_expr);
for (int64_t c = 1; c < rc; c++)
if (ray_table_col_name(O, c) == kname) { kname = ray_sym_intern("key", 3); break; }
ray_table_set_col_name(O, 0, kname);
bool plain = true;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == take_id || kid == asc_id || kid == desc_id) plain = false;
}
int64_t orows = ray_table_nrows(O);
ray_t* okey = ray_table_get_col_idx(O, 0);
if (plain && orows > 1 && okey && okey->type == RAY_SYM) {
struct ray_sym_domain_s* kdom = ray_sym_vec_domain(okey);
int64_t kn = ray_sym_domain_count(kdom);
ray_t *shdr = NULL, *ihdr = NULL;
int64_t* slot = (int64_t*)scratch_alloc(&shdr, (size_t)(kn > 0 ? kn : 1) * sizeof(int64_t));
int64_t* idx = (int64_t*)scratch_alloc(&ihdr, (size_t)orows * sizeof(int64_t));
bool ok = slot && idx;
if (ok) {
memset(slot, 0xff, (size_t)kn * sizeof(int64_t));
const void* kd = ray_data(okey);
for (int64_t i = 0; i < orows && ok; i++) {
int64_t pos = ray_read_sym(kd, i, RAY_SYM, okey->attrs);
if (pos < 0 || pos >= kn || slot[pos] >= 0) ok = false;
else slot[pos] = i;
}
}
if (ok) {
int64_t m = 0;
for (int64_t pos = 0; pos < kn; pos++) if (slot[pos] >= 0) idx[m++] = slot[pos];
ok = m == orows;
}
if (ok) {
ray_t* P = ray_table_new(rc);
for (int64_t c = 0; c < rc && P && !RAY_IS_ERR(P); c++) {
ray_t* src = ray_table_get_col_idx(O, c);
ray_t* g = src ? gather_by_idx(src, idx, orows) : NULL;
if (!g || RAY_IS_ERR(g)) { if (g) ray_error_free(g); ray_release(P); P = NULL; break; }
P = ray_table_add_col(P, ray_table_col_name(O, c), g);
ray_release(g);
}
if (P && !RAY_IS_ERR(P)) { ray_release(O); O = P; }
else if (P) ray_error_free(P);
}
scratch_free(shdr); scratch_free(ihdr);
}
agg_route_note_key_domain();
return O;
}
static ray_t* derived_key_over_sym_domain(ray_t* by_expr, ray_t* tbl, ray_t* sel) {
if (!by_expr || by_expr->type != RAY_LIST || !tbl) return NULL;
int64_t ref_syms[2];
int nref = collect_col_refs(by_expr, tbl, ref_syms, 2, 0);
if (nref != 1) return NULL;
int64_t bound[32];
if (!derived_key_expr_ok(by_expr, tbl, ref_syms[0], bound, 0)) return NULL;
ray_t* C = ray_table_get_col(tbl, ref_syms[0]);
int64_t nrows = ray_table_nrows(tbl);
if (!C || C->type != RAY_SYM || !ray_is_vec(C) || C->len != nrows || nrows < 4096)
return NULL;
int64_t sel_rows = nrows;
if (sel) {
ray_rowsel_t* sm = ray_rowsel_meta(sel);
if (!sm || sm->nrows != nrows) return NULL;
sel_rows = sm->total_pass;
if (sel_rows < 4096) return NULL;
}
struct ray_sym_domain_s* dom = ray_sym_vec_domain(C);
int64_t dn = dom ? ray_sym_domain_count(dom) : 0;
if (dn <= 0 || dn > DERIVED_KEY_MAX_DOMAIN || dn > 16 * nrows) return NULL;
const void* cd = ray_data(C);
{
const int64_t probe_rows = sel_rows < 65536 ? sel_rows : 65536;
int64_t PROBE_SLOTS = 1024;
while (PROBE_SLOTS < 2 * probe_rows) PROBE_SLOTS <<= 1;
ray_t* set_hdr = NULL;
int64_t* set = (int64_t*)scratch_alloc(&set_hdr, (size_t)PROBE_SLOTS * sizeof(int64_t));
if (!set) return NULL;
memset(set, 0xff, (size_t)PROBE_SLOTS * sizeof(int64_t));
int64_t seen = 0;
dk_sel_it_t pit;
dk_sel_it_init(&pit, sel, nrows);
for (int64_t r = 0; r < probe_rows; r++) {
int64_t row = dk_sel_it_next(&pit);
if (row < 0) break;
int64_t id = ray_read_sym(cd, row, C->type, C->attrs);
if (id < 0 || id >= dn) { seen = probe_rows; break; }
uint64_t h = (((uint64_t)id * 0x9E3779B97F4A7C15ull) >> 32) & (uint64_t)(PROBE_SLOTS - 1);
while (set[h] >= 0 && set[h] != id) h = (h + 1) & (uint64_t)(PROBE_SLOTS - 1);
if (set[h] < 0) { set[h] = id; seen++; }
}
scratch_free(set_hdr);
if (seen * 4 > probe_rows * 3) return NULL;
}
ray_t* pos_hdr = NULL;
int32_t* pos = (int32_t*)scratch_alloc(&pos_hdr, (size_t)dn * sizeof(int32_t));
if (!pos) return NULL;
memset(pos, 0xff, (size_t)dn * sizeof(int32_t));
ray_t* dom_vec = ray_sym_vec_new(ray_sym_dict_width(dn), dn);
if (!dom_vec || RAY_IS_ERR(dom_vec)) { if (dom_vec) ray_error_free(dom_vec); scratch_free(pos_hdr); return NULL; }
ray_sym_vec_adopt_domain(dom_vec, C);
int64_t du = 0, du_max = sel_rows / 2;
if (du_max > INT32_MAX) du_max = INT32_MAX;
bool ok = true;
dk_sel_it_t it;
dk_sel_it_init(&it, sel, nrows);
for (;;) {
int64_t r = dk_sel_it_next(&it);
if (r < 0) break;
int64_t id = ray_read_sym(cd, r, C->type, C->attrs);
if (id < 0 || id >= dn) { ok = false; break; }
if (pos[id] < 0) {
if (du >= du_max) { ok = false; break; }
pos[id] = (int32_t)du;
write_col_i64(ray_data(dom_vec), du, id, dom_vec->type, dom_vec->attrs);
du++;
}
}
if (!ok || du == 0) { ray_release(dom_vec); scratch_free(pos_hdr); return NULL; }
dom_vec->len = du;
dk_rows_ctx_t rc;
memset(&rc, 0, sizeof(rc));
rc.cd = cd; rc.attrs = C->attrs; rc.dn = dn;
rc.sparse = sel != NULL;
ray_pool_t* rpool = ray_pool_get();
bool rows_par = ray_pool_par_dispatch_ok(rpool, nrows, RAY_PARALLEL_THRESHOLD);
ray_t* key_dom = derived_key_str_chunks(by_expr, ref_syms[0], dom_vec, dom, du);
if (key_dom && RAY_IS_ERR(key_dom)) { ray_error_free(key_dom); key_dom = NULL; }
if (key_dom) {
ray_release(dom_vec);
} else {
ray_t* mini = ray_table_new(0);
if (mini && !RAY_IS_ERR(mini)) mini = ray_table_add_col(mini, ref_syms[0], dom_vec);
ray_release(dom_vec);
if (!mini || RAY_IS_ERR(mini)) { if (mini) ray_error_free(mini); scratch_free(pos_hdr); return NULL; }
ray_graph_t* g2 = ray_graph_new(mini);
if (g2) {
ray_op_t* kop = compile_expr_dag(g2, by_expr);
if (kop) kop = ray_optimize(g2, kop);
if (kop) key_dom = ray_execute(g2, kop);
ray_graph_free(g2);
}
ray_release(mini);
if (key_dom && !RAY_IS_ERR(key_dom) && ray_is_lazy(key_dom)) key_dom = ray_lazy_materialize(key_dom);
if (!key_dom || RAY_IS_ERR(key_dom)) { if (key_dom) ray_error_free(key_dom); scratch_free(pos_hdr); return NULL; }
if (!ray_is_vec(key_dom) || key_dom->len != du) { ray_release(key_dom); scratch_free(pos_hdr); return NULL; }
}
if (!ray_is_vec(key_dom) || key_dom->len != du) { ray_release(key_dom); scratch_free(pos_hdr); return NULL; }
ray_t* spread = NULL;
if (key_dom->type == RAY_SYM && (key_dom->attrs & RAY_SYM_W_MASK) == RAY_SYM_W64) {
spread = ray_sym_vec_new(RAY_SYM_W64, nrows);
if (!spread || RAY_IS_ERR(spread)) { if (spread) ray_error_free(spread); ray_release(key_dom); scratch_free(pos_hdr); return NULL; }
spread->len = nrows;
ray_sym_vec_adopt_domain(spread, key_dom);
rc.pos = pos; rc.key = (const int64_t*)ray_data(key_dom); rc.out = (int64_t*)ray_data(spread);
if (rows_par) ray_pool_dispatch(rpool, dk_spread_fn, &rc, nrows);
else dk_spread_fn(&rc, 0, 0, nrows);
scratch_free(pos_hdr);
ray_release(key_dom);
} else {
ray_t* ids = ray_vec_new(RAY_I64, nrows);
if (!ids || RAY_IS_ERR(ids)) { if (ids) ray_error_free(ids); ray_release(key_dom); scratch_free(pos_hdr); return NULL; }
ids->len = nrows;
rc.pos = pos; rc.key = NULL; rc.out = (int64_t*)ray_data(ids);
if (rows_par) ray_pool_dispatch(rpool, dk_spread_fn, &rc, nrows);
else dk_spread_fn(&rc, 0, 0, nrows);
scratch_free(pos_hdr);
spread = ray_at_fn(key_dom, ids);
ray_release(ids);
ray_release(key_dom);
if (spread && !RAY_IS_ERR(spread) && ray_is_lazy(spread)) spread = ray_lazy_materialize(spread);
if (!spread || RAY_IS_ERR(spread)) { if (spread) ray_error_free(spread); return NULL; }
if (!ray_is_vec(spread) || spread->len != nrows) { ray_release(spread); return NULL; }
}
agg_route_note_key_domain();
return spread;
}
static ray_t* bind_col_slice(int64_t sym, ray_t* col, ray_t* idx_list) {
ray_t* slice = NULL;
if (col && ray_is_vec(col) && idx_list &&
idx_list->type == RAY_I64 && ray_is_vec(idx_list)) {
const int64_t* idx_data = (const int64_t*)ray_data(idx_list);
slice = gather_by_idx(col, (int64_t*)idx_data, ray_len(idx_list));
}
if (!slice) slice = ray_at_fn(col, idx_list);
if (!slice || RAY_IS_ERR(slice)) {
return slice ? slice : ray_error("oom", NULL);
}
ray_env_set_query_local(sym, slice);
ray_release(slice);
return NULL;
}
static ray_t* typed_vec_to_list(ray_t* tv, int64_t fill, int64_t n_groups) {
ray_t* list_col = ray_alloc(n_groups * sizeof(ray_t*));
if (!list_col) return ray_error("oom", NULL);
list_col->type = RAY_LIST;
list_col->len = 0;
ray_t** out = (ray_t**)ray_data(list_col);
for (int64_t k = 0; k < fill; k++) {
int allocated = 0;
ray_t* atom = collection_elem(tv, k, &allocated);
if (!allocated && atom) ray_retain(atom);
out[k] = atom;
list_col->len = k + 1;
}
return list_col;
}
typedef ray_t* (*idx_feeder_fn)(int64_t gi, void* state);
static ray_t* nonagg_eval_per_group_core(ray_t* expr, ray_t* tbl,
idx_feeder_fn feeder, void* fstate,
int64_t n_groups) {
int64_t ncols_max = ray_table_ncols(tbl);
if (ncols_max < 1) ncols_max = 1;
ray_t* refs_hdr = NULL;
int64_t* col_syms = (int64_t*)scratch_alloc(&refs_hdr,
(size_t)ncols_max * (sizeof(int64_t) + sizeof(ray_t*)));
if (!col_syms) return ray_error("oom", NULL);
int n_cols = collect_col_refs(expr, tbl, col_syms, (int)ncols_max, 0);
ray_t** cols = (ray_t**)(col_syms + ncols_max);
for (int i = 0; i < n_cols; i++)
cols[i] = ray_table_get_col(tbl, col_syms[i]);
if (ray_env_push_query_scope() != RAY_OK) { scratch_free(refs_hdr); return ray_error("oom", NULL); }
ray_t* _aqt = g_active_query_table;
g_active_query_table = tbl;
ray_t* result = NULL;
int direct_typed = 0;
int8_t typed_t = 0;
if (n_groups == 0) {
ray_t* empty_idx = ray_vec_new(RAY_I64, 0);
int8_t vt = 0;
if (empty_idx && !RAY_IS_ERR(empty_idx)) {
empty_idx->len = 0;
int bound = 1;
for (int i = 0; i < n_cols && bound; i++) {
ray_t* err = bind_col_slice(col_syms[i], cols[i], empty_idx);
if (err) { ray_error_free(err); bound = 0; }
}
if (bound) {
ray_t* cell = ray_eval(expr);
if (cell && !RAY_IS_ERR(cell) && ray_is_lazy(cell)) cell = ray_lazy_materialize(cell);
if (cell && !RAY_IS_ERR(cell)) {
int8_t t = cell->type;
if (t < 0 && t != -RAY_SYM && t != -RAY_STR) vt = (int8_t)(-t);
ray_release(cell);
} else if (cell) ray_error_free(cell);
}
ray_release(empty_idx);
} else if (empty_idx) ray_error_free(empty_idx);
result = vt ? ray_vec_new(vt, 0) : ray_list_new(0);
if (result && !RAY_IS_ERR(result)) result->len = 0;
g_active_query_table = _aqt;
ray_env_pop_scope();
scratch_free(refs_hdr);
return result ? result : ray_error("oom", NULL);
}
for (int64_t gi = 0; gi < n_groups; gi++) {
ray_t* idx_list = feeder(gi, fstate);
if (!idx_list) {
g_active_query_table = _aqt;
ray_env_pop_scope();
if (result) ray_release(result);
scratch_free(refs_hdr);
return ray_error("oom", NULL);
}
for (int i = 0; i < n_cols; i++) {
ray_t* err = bind_col_slice(col_syms[i], cols[i], idx_list);
if (err) {
g_active_query_table = _aqt;
ray_env_pop_scope();
if (result) ray_release(result);
scratch_free(refs_hdr);
return err;
}
}
ray_t* cell = ray_eval(expr);
if (!cell || RAY_IS_ERR(cell)) {
g_active_query_table = _aqt;
ray_env_pop_scope();
if (result) ray_release(result);
scratch_free(refs_hdr);
return cell ? cell : ray_error("domain", "select by: per-group expression evaluation failed");
}
if (ray_is_lazy(cell)) {
cell = ray_lazy_materialize(cell);
if (!cell || RAY_IS_ERR(cell)) {
g_active_query_table = _aqt;
ray_env_pop_scope();
if (result) ray_release(result);
scratch_free(refs_hdr);
return cell ? cell : ray_error("domain", "select by: failed to materialize per-group cell");
}
}
if (gi == 0) {
int8_t t = cell->type;
int collapsable = (t < 0);
if (collapsable && t == -RAY_STR) {
result = ray_vec_new(RAY_STR, n_groups);
if (!result || RAY_IS_ERR(result)) {
g_active_query_table = _aqt;
ray_env_pop_scope(); ray_release(cell);
scratch_free(refs_hdr);
return result ? result : ray_error("oom", NULL);
}
const char* sp = ray_str_ptr(cell);
result = ray_str_vec_append(result, sp ? sp : "", sp ? ray_str_len(cell) : 0);
if (!result || RAY_IS_ERR(result)) {
g_active_query_table = _aqt;
ray_env_pop_scope(); ray_release(cell);
scratch_free(refs_hdr);
return result ? result : ray_error("oom", NULL);
}
direct_typed = 1; typed_t = t;
ray_release(cell);
} else if (collapsable) {
int8_t vt = (int8_t)(-t);
result = ray_vec_new(vt, n_groups);
if (!result || RAY_IS_ERR(result)) {
g_active_query_table = _aqt;
ray_env_pop_scope(); ray_release(cell);
scratch_free(refs_hdr);
return result ? result : ray_error("oom", NULL);
}
result->len = n_groups;
if (store_typed_elem(result, 0, cell) == 0) {
direct_typed = 1; typed_t = t;
ray_release(cell);
} else {
ray_release(result); result = NULL;
collapsable = 0;
}
}
if (!collapsable) {
result = ray_alloc(n_groups * sizeof(ray_t*));
if (!result) {
g_active_query_table = _aqt;
ray_env_pop_scope(); ray_release(cell);
scratch_free(refs_hdr);
return ray_error("oom", NULL);
}
result->type = RAY_LIST;
result->len = 0;
((ray_t**)ray_data(result))[0] = cell;
result->len = 1;
}
continue;
}
if (direct_typed) {
int stored = 0;
if (cell->type == typed_t) {
if (typed_t == -RAY_STR) {
const char* sp = ray_str_ptr(cell);
ray_t* nv = ray_str_vec_append(result, sp ? sp : "", sp ? ray_str_len(cell) : 0);
if (!nv || RAY_IS_ERR(nv)) {
g_active_query_table = _aqt;
ray_env_pop_scope(); ray_release(cell);
scratch_free(refs_hdr);
return nv ? nv : ray_error("oom", NULL);
}
result = nv; stored = 1;
} else {
stored = (store_typed_elem(result, gi, cell) == 0);
}
}
if (stored) {
ray_release(cell);
} else {
ray_t* list_col = typed_vec_to_list(result, gi, n_groups);
ray_release(result);
if (RAY_IS_ERR(list_col)) {
g_active_query_table = _aqt;
ray_env_pop_scope(); ray_release(cell);
scratch_free(refs_hdr);
return list_col;
}
result = list_col;
((ray_t**)ray_data(result))[gi] = cell;
result->len = gi + 1;
direct_typed = 0;
}
} else {
((ray_t**)ray_data(result))[gi] = cell;
result->len = gi + 1;
}
}
g_active_query_table = _aqt;
ray_env_pop_scope();
scratch_free(refs_hdr);
return result;
}
typedef struct { ray_t** items; } groups_state_t;
static ray_t* groups_idx_feed(int64_t gi, void* st) {
groups_state_t* s = (groups_state_t*)st;
return s->items[gi * 2 + 1];
}
static ray_t* nonagg_eval_per_group(ray_t* expr, ray_t* tbl,
ray_t* groups, int64_t n_groups) {
groups_state_t st = { .items = (ray_t**)ray_data(groups) };
return nonagg_eval_per_group_core(expr, tbl, groups_idx_feed, &st, n_groups);
}
typedef struct {
const int64_t* idx_buf;
const int64_t* offsets;
const int64_t* grp_cnt;
ray_t* scratch;
} buf_state_t;
static ray_t* buf_idx_feed(int64_t gi, void* st) {
buf_state_t* s = (buf_state_t*)st;
int64_t cnt = s->grp_cnt[gi];
s->scratch->len = cnt;
if (cnt > 0) {
memcpy(ray_data(s->scratch), &s->idx_buf[s->offsets[gi]],
(size_t)cnt * sizeof(int64_t));
}
return s->scratch;
}
static ray_t* nonagg_eval_per_group_buf(ray_t* expr, ray_t* tbl,
const int64_t* idx_buf,
const int64_t* offsets,
const int64_t* grp_cnt,
int64_t n_groups) {
int64_t max_cnt = 0;
for (int64_t gi = 0; gi < n_groups; gi++)
if (grp_cnt[gi] > max_cnt) max_cnt = grp_cnt[gi];
ray_t* scratch = ray_vec_new(RAY_I64, max_cnt > 0 ? max_cnt : 1);
if (!scratch || RAY_IS_ERR(scratch))
return scratch ? scratch : ray_error("oom", NULL);
buf_state_t st = { idx_buf, offsets, grp_cnt, scratch };
ray_t* res = nonagg_eval_per_group_core(expr, tbl, buf_idx_feed, &st, n_groups);
ray_release(scratch);
return res;
}
ray_t* ray_active_query_literal(int64_t sym) {
ray_t* qt = g_active_query_table;
if (!qt || qt->type != RAY_TABLE) return NULL;
ray_t* col = ray_table_get_col(qt, sym);
if (!col) return NULL;
if (g_active_query_row < 0 || qt != g_active_query_row_tbl ||
g_active_query_row >= ray_len(col) ||
ray_env_query_scope_above(g_active_query_row_depth)) {
ray_retain(col); return col;
}
int allocated = 0;
ray_t* cell = collection_elem(col, g_active_query_row, &allocated);
if (!cell || RAY_IS_ERR(cell)) return cell;
if (!allocated) ray_retain(cell);
return cell;
}
static ray_t* eval_expr_per_row(ray_t* expr, ray_t* tbl, int64_t nrows) {
int64_t ncols_max = ray_table_ncols(tbl);
if (ncols_max < 1) ncols_max = 1;
ray_t* refs_hdr = NULL;
int64_t* col_syms = (int64_t*)scratch_alloc(&refs_hdr,
(size_t)ncols_max * (sizeof(int64_t) + sizeof(ray_t*)));
if (!col_syms) return ray_error("oom", NULL);
int n_cols = collect_col_refs(expr, tbl, col_syms, (int)ncols_max, 0);
ray_t** cols = (ray_t**)(col_syms + ncols_max);
for (int i = 0; i < n_cols; i++)
cols[i] = ray_table_get_col(tbl, col_syms[i]);
if (ray_env_push_query_scope() != RAY_OK) { scratch_free(refs_hdr); return ray_error("oom", NULL); }
ray_t* _aqt = g_active_query_table;
int64_t _aqr = g_active_query_row;
ray_t* _aqrt = g_active_query_row_tbl;
int32_t _aqrd = g_active_query_row_depth;
g_active_query_table = tbl;
g_active_query_row_tbl = tbl;
g_active_query_row_depth = ray_env_scope_depth();
ray_t* result = NULL;
int direct_typed = 0;
int8_t typed_t = 0;
for (int64_t row = 0; row < nrows; row++) {
for (int i = 0; i < n_cols; i++) {
int allocated = 0;
ray_t* arg = collection_elem(cols[i], row, &allocated);
if (!arg || RAY_IS_ERR(arg)) {
g_active_query_table = _aqt; g_active_query_row = _aqr; g_active_query_row_tbl = _aqrt; g_active_query_row_depth = _aqrd;
ray_env_pop_scope();
if (result) ray_release(result);
scratch_free(refs_hdr);
return arg ? arg : ray_error("domain", "select: failed to read column cell for per-row eval");
}
ray_env_set_query_local(col_syms[i], arg);
if (allocated) ray_release(arg);
}
g_active_query_row = row;
ray_t* cell = ray_eval(expr);
if (!cell || RAY_IS_ERR(cell)) {
g_active_query_table = _aqt; g_active_query_row = _aqr; g_active_query_row_tbl = _aqrt; g_active_query_row_depth = _aqrd;
ray_env_pop_scope();
if (result) ray_release(result);
scratch_free(refs_hdr);
return cell ? cell : ray_error("domain", "select: per-row expression evaluation failed");
}
if (row == 0) {
int8_t t = cell->type;
int collapsable = (t < 0);
if (collapsable && t == -RAY_STR) {
result = ray_vec_new(RAY_STR, nrows);
if (!result || RAY_IS_ERR(result)) {
g_active_query_table = _aqt; g_active_query_row = _aqr; g_active_query_row_tbl = _aqrt; g_active_query_row_depth = _aqrd;
ray_env_pop_scope();
ray_release(cell);
scratch_free(refs_hdr);
return result ? result : ray_error("oom", NULL);
}
const char* sp = ray_str_ptr(cell);
result = ray_str_vec_append(result, sp ? sp : "", sp ? ray_str_len(cell) : 0);
if (!result || RAY_IS_ERR(result)) {
g_active_query_table = _aqt; g_active_query_row = _aqr; g_active_query_row_tbl = _aqrt; g_active_query_row_depth = _aqrd;
ray_env_pop_scope();
ray_release(cell);
scratch_free(refs_hdr);
return result ? result : ray_error("oom", NULL);
}
direct_typed = 1; typed_t = t;
ray_release(cell);
} else if (collapsable) {
result = ray_vec_new((int8_t)-t, nrows);
if (!result || RAY_IS_ERR(result)) {
g_active_query_table = _aqt; g_active_query_row = _aqr; g_active_query_row_tbl = _aqrt; g_active_query_row_depth = _aqrd;
ray_env_pop_scope();
ray_release(cell);
scratch_free(refs_hdr);
return result ? result : ray_error("oom", NULL);
}
result->len = nrows;
if (store_typed_elem(result, 0, cell) == 0) {
direct_typed = 1;
typed_t = t;
ray_release(cell);
} else {
ray_release(result);
result = NULL;
collapsable = 0;
}
}
if (!collapsable) {
result = ray_alloc(nrows * sizeof(ray_t*));
if (!result) {
g_active_query_table = _aqt; g_active_query_row = _aqr; g_active_query_row_tbl = _aqrt; g_active_query_row_depth = _aqrd;
ray_env_pop_scope();
ray_release(cell);
scratch_free(refs_hdr);
return ray_error("oom", NULL);
}
result->type = RAY_LIST;
result->len = 1;
((ray_t**)ray_data(result))[0] = cell;
}
continue;
}
if (direct_typed) {
int stored = 0;
if (cell->type == typed_t) {
if (typed_t == -RAY_STR) {
const char* sp = ray_str_ptr(cell);
ray_t* nv = ray_str_vec_append(result, sp ? sp : "", sp ? ray_str_len(cell) : 0);
if (!nv || RAY_IS_ERR(nv)) {
g_active_query_table = _aqt; g_active_query_row = _aqr; g_active_query_row_tbl = _aqrt; g_active_query_row_depth = _aqrd;
ray_env_pop_scope();
ray_release(cell);
scratch_free(refs_hdr);
return nv ? nv : ray_error("oom", NULL);
}
result = nv; stored = 1;
} else {
stored = (store_typed_elem(result, row, cell) == 0);
}
}
if (stored) {
ray_release(cell);
} else {
ray_t* list_col = typed_vec_to_list(result, row, nrows);
ray_release(result);
if (RAY_IS_ERR(list_col)) {
g_active_query_table = _aqt; g_active_query_row = _aqr; g_active_query_row_tbl = _aqrt; g_active_query_row_depth = _aqrd;
ray_env_pop_scope();
ray_release(cell);
scratch_free(refs_hdr);
return list_col;
}
result = list_col;
((ray_t**)ray_data(result))[row] = cell;
result->len = row + 1;
direct_typed = 0;
}
} else {
((ray_t**)ray_data(result))[row] = cell;
result->len = row + 1;
}
}
g_active_query_table = _aqt; g_active_query_row = _aqr; g_active_query_row_tbl = _aqrt; g_active_query_row_depth = _aqrd;
ray_env_pop_scope();
scratch_free(refs_hdr);
if (!result) {
result = ray_alloc(0);
if (!result) return ray_error("oom", NULL);
result->type = RAY_LIST;
result->len = 0;
}
return result;
}
static ray_t* eval_expr_whole_column(ray_t* expr, ray_t* tbl) {
if (ray_env_push_query_scope() != RAY_OK) return ray_error("oom", NULL);
ray_t* _aqt = bind_all_columns(tbl);
int64_t _aqr = g_active_query_row;
g_active_query_row = -1;
ray_t* result = ray_eval(expr);
if (result && !RAY_IS_ERR(result))
result = ray_lazy_materialize(result);
g_active_query_table = _aqt;
g_active_query_row = _aqr;
ray_env_pop_scope();
if (!result)
return ray_error("domain", "select: whole-column expression evaluation failed");
return result;
}
static ray_t* empty_agg_column(ray_t* fn_name, ray_t* src) {
ray_t* out = NULL;
ray_t* fn_obj = fn_name ? ray_env_get(fn_name->i64) : NULL;
if (fn_obj && fn_obj->type == RAY_UNARY && src && ray_is_vec(src) && src->type != RAY_LIST) {
ray_t* empty = ray_vec_new(src->type, 0);
if (empty && !RAY_IS_ERR(empty)) {
empty->len = 0;
ray_t* v = ((ray_unary_fn)(uintptr_t)fn_obj->i64)(empty);
if (v && !RAY_IS_ERR(v) && ray_is_lazy(v)) v = ray_lazy_materialize(v);
if (v && !RAY_IS_ERR(v)) {
int8_t t = v->type;
if (t < 0 && t != -RAY_SYM && t != -RAY_STR) out = ray_vec_new((int8_t)(-t), 0);
ray_release(v);
} else if (v) ray_error_free(v);
ray_release(empty);
} else if (empty) ray_error_free(empty);
}
if (!out || RAY_IS_ERR(out)) { if (out) ray_error_free(out); out = ray_list_new(0); }
if (out && !RAY_IS_ERR(out)) out->len = 0;
return out;
}
static ray_t* empty_unary_group_result(ray_unary_fn fn, ray_t* source) {
if (!source) return ray_error("domain", "aggregation source missing");
ray_t* input = ray_group_gather(source, NULL, 0);
if (!input || RAY_IS_ERR(input)) return input ? input : ray_error("oom", NULL);
ray_t* value = fn(input);
if (value && !RAY_IS_ERR(value) && ray_is_lazy(value)) value = ray_lazy_materialize(value);
if (!value || RAY_IS_ERR(value)) {
ray_release(input);
if (!value) return ray_error("oom", NULL);
const char* code = ray_err_code(value);
if (strcmp(code, "domain") && strcmp(code, "type")) return value;
ray_release(value);
ray_t* out = ray_list_new(0);
return out ? out : ray_error("oom", NULL);
}
ray_t* out;
if (ray_is_atom(value) && -value->type == input->type) {
out = input; input = NULL;
} else out = ray_is_atom(value) ? ray_vec_new(-value->type, 0) : ray_list_new(0);
ray_release(input); ray_release(value);
return out ? out : ray_error("oom", NULL);
}
static ray_t* aggr_unary_per_group_buf(ray_t* expr, ray_t* tbl,
const int64_t* idx_buf,
const int64_t* offsets,
const int64_t* grp_cnt,
int64_t n_groups) {
ray_t** elems = (ray_t**)ray_data(expr);
ray_t* fn_name = elems[0];
ray_t* col_expr = elems[1];
ray_t* fn_obj = ray_env_get(fn_name->i64);
if (!fn_obj || fn_obj->type != RAY_UNARY)
return ray_error("type", "select by: aggregate must be a unary function, got %s", fn_obj ? ray_type_name(fn_obj->type) : "null");
ray_unary_fn uf = (ray_unary_fn)(uintptr_t)fn_obj->i64;
ray_t* src = NULL;
if (col_expr->type == -RAY_SYM && !(col_expr->attrs & ATTR_QUOTED)) {
src = ray_table_get_col(tbl, col_expr->i64);
if (src) ray_retain(src);
}
if (!src) {
if (ray_env_push_query_scope() != RAY_OK) return ray_error("oom", NULL);
ray_t* _aqt = bind_all_columns(tbl);
src = ray_eval(col_expr);
g_active_query_table = _aqt;
ray_env_pop_scope();
if (!src || RAY_IS_ERR(src)) return src ? src : ray_error("domain", "select by: failed to evaluate aggregation source");
if (ray_is_lazy(src)) {
src = ray_lazy_materialize(src);
if (!src || RAY_IS_ERR(src)) return src ? src : ray_error("domain", "select by: failed to materialize aggregation source");
}
}
if (n_groups == 0) {
ray_t* out = empty_unary_group_result(uf, src);
ray_release(src); return out;
}
int64_t max_cnt = 0;
for (int64_t gi = 0; gi < n_groups; gi++)
if (grp_cnt[gi] > max_cnt) max_cnt = grp_cnt[gi];
ray_t* idx_vec = ray_vec_new(RAY_I64, max_cnt > 0 ? max_cnt : 1);
if (!idx_vec || RAY_IS_ERR(idx_vec)) {
ray_release(src);
return idx_vec ? idx_vec : ray_error("oom", NULL);
}
ray_t* agg_vec = NULL;
int8_t agg_atom_t = 0;
for (int64_t gi = 0; gi < n_groups; gi++) {
idx_vec->len = grp_cnt[gi];
if (grp_cnt[gi] > 0) {
memcpy(ray_data(idx_vec), &idx_buf[offsets[gi]],
(size_t)grp_cnt[gi] * sizeof(int64_t));
}
ray_t* subset = ray_at_fn(src, idx_vec);
if (!subset || RAY_IS_ERR(subset)) continue;
ray_t* agg_val = uf(subset);
ray_release(subset);
if (agg_val && !RAY_IS_ERR(agg_val) && ray_is_lazy(agg_val))
agg_val = ray_lazy_materialize(agg_val);
if (!agg_val || RAY_IS_ERR(agg_val)) continue;
if (!agg_vec) {
agg_atom_t = agg_val->type;
int8_t vt = (int8_t)(-agg_atom_t);
agg_vec = ray_vec_new(vt, n_groups);
if (!agg_vec || RAY_IS_ERR(agg_vec)) {
ray_release(agg_val); ray_release(idx_vec); ray_release(src);
return agg_vec ? agg_vec : ray_error("oom", NULL);
}
agg_vec->len = n_groups;
}
if (agg_val->type != agg_atom_t || store_typed_elem(agg_vec, gi, agg_val) != 0) {
ray_release(agg_val);
ray_release(idx_vec); ray_release(src);
ray_release(agg_vec);
return nonagg_eval_per_group_buf(expr, tbl, idx_buf, offsets, grp_cnt, n_groups);
}
ray_release(agg_val);
}
ray_release(idx_vec);
if (!agg_vec) {
if (n_groups == 0) agg_vec = empty_agg_column(fn_name, src);
else {
agg_vec = ray_vec_new(RAY_I64, n_groups);
if (agg_vec && !RAY_IS_ERR(agg_vec)) agg_vec->len = n_groups;
}
}
ray_release(src);
return agg_vec;
}
static int is_med_call(ray_t* expr) {
if (!expr || expr->type != RAY_LIST) return 0;
if (ray_len(expr) != 2) return 0;
ray_t** elems = (ray_t**)ray_data(expr);
if (!elems[0] || elems[0]->type != -RAY_SYM) return 0;
ray_t* nm = ray_sym_str(elems[0]->i64);
if (!nm) return 0;
return ray_str_len(nm) == 3 && memcmp(ray_str_ptr(nm), "med", 3) == 0;
}
static ray_t* aggr_med_per_group_buf(ray_t* expr, ray_t* tbl,
const int64_t* idx_buf,
const int64_t* offsets,
const int64_t* grp_cnt,
int64_t n_groups) {
ray_t** elems = (ray_t**)ray_data(expr);
ray_t* col_expr = elems[1];
ray_t* src = NULL;
if (col_expr->type == -RAY_SYM && !(col_expr->attrs & ATTR_QUOTED)) {
src = ray_table_get_col(tbl, col_expr->i64);
if (src) ray_retain(src);
}
if (!src) {
if (ray_env_push_query_scope() != RAY_OK) return ray_error("oom", NULL);
ray_t* _aqt = bind_all_columns(tbl);
src = ray_eval(col_expr);
g_active_query_table = _aqt;
ray_env_pop_scope();
if (!src || RAY_IS_ERR(src)) return src ? src : ray_error("domain", "select by: failed to evaluate median source column");
}
ray_t* out = ray_median_per_group_buf(src, idx_buf, offsets, grp_cnt, n_groups);
ray_release(src);
return out;
}
typedef struct {
int8_t in_type;
uint8_t in_attrs;
const void* base;
uint8_t esz;
bool is_f64;
const int64_t* idx_buf;
const int64_t* offsets;
const int64_t* grp_cnt;
int64_t* odata;
int64_t sym_cap_bound;
_Atomic(int32_t) oom;
} cdpg_buf_par_ctx_t;
#define CDPG_BUF_HASH_K1 0x9E3779B97F4A7C15ULL
#define CDPG_BUF_INSERT(VAL_EXPR) do { \
int64_t _ins_v = (int64_t)(VAL_EXPR); \
if (RAY_UNLIKELY(_ins_v == 0)) { \
if (!saw_zero) { saw_zero = 1; distinct++; } \
break; \
} \
uint64_t _ins_h = (uint64_t)_ins_v * CDPG_BUF_HASH_K1; \
_ins_h ^= _ins_h >> 33; \
uint64_t _ins_slot = _ins_h & mask; \
for (;;) { \
int64_t _ins_cur = set[_ins_slot]; \
if (_ins_cur == 0) { \
set[_ins_slot] = _ins_v; \
distinct++; \
break; \
} \
if (_ins_cur == _ins_v) break; \
_ins_slot = (_ins_slot + 1) & mask; \
} \
} while (0)
static void cdpg_buf_par_fn(void* vctx, uint32_t worker_id,
int64_t start, int64_t end) {
(void)worker_id;
cdpg_buf_par_ctx_t* ctx = (cdpg_buf_par_ctx_t*)vctx;
if (atomic_load_explicit(&ctx->oom, memory_order_relaxed)) return;
for (int64_t gi = start; gi < end; gi++) {
int64_t cnt = ctx->grp_cnt[gi];
if (cnt == 0) { ctx->odata[gi] = 0; continue; }
const int64_t* idxs = &ctx->idx_buf[ctx->offsets[gi]];
uint64_t cap = (uint64_t)cnt * 2;
if (cap < 32) cap = 32;
uint64_t c = 1;
while (c && c < cap) c <<= 1;
if (!c) {
atomic_store_explicit(&ctx->oom, 1, memory_order_relaxed);
return;
}
cap = c;
if (ctx->sym_cap_bound && cap > (uint64_t)ctx->sym_cap_bound)
cap = (uint64_t)ctx->sym_cap_bound;
uint64_t mask = cap - 1;
ray_t* set_hdr = NULL;
int64_t* set = (int64_t*)scratch_calloc(&set_hdr,
(size_t)cap * sizeof(int64_t));
if (!set) {
atomic_store_explicit(&ctx->oom, 1, memory_order_relaxed);
return;
}
int64_t distinct = 0;
int saw_zero = 0;
if (ctx->is_f64) {
const double* d = (const double*)ctx->base;
for (int64_t i = 0; i < cnt; i++) {
int64_t r = idxs[i];
CDPG_BUF_INSERT(canon_f64_key(d[r]));
}
} else if (ctx->esz == 8) {
const int64_t* d = (const int64_t*)ctx->base;
for (int64_t i = 0; i < cnt; i++)
CDPG_BUF_INSERT(d[idxs[i]]);
} else if (ctx->esz == 4) {
const int32_t* d = (const int32_t*)ctx->base;
for (int64_t i = 0; i < cnt; i++)
CDPG_BUF_INSERT((int64_t)d[idxs[i]]);
} else if (ctx->esz == 2) {
const int16_t* d = (const int16_t*)ctx->base;
for (int64_t i = 0; i < cnt; i++)
CDPG_BUF_INSERT((int64_t)d[idxs[i]]);
} else {
const uint8_t* d = (const uint8_t*)ctx->base;
for (int64_t i = 0; i < cnt; i++) {
CDPG_BUF_INSERT((int64_t)d[idxs[i]]);
}
}
scratch_free(set_hdr);
ctx->odata[gi] = distinct;
}
}
#undef CDPG_BUF_INSERT
#define SLICE_SERIAL_ROWS (1 << 17)
#define SLICE_HIST_CELLS (1 << 23)
typedef struct {
const int64_t* row_gid;
int64_t nrows;
int64_t n_groups;
int64_t* hist;
int64_t* idx_buf;
const int64_t* seg_lo;
const uint8_t* sel_flg;
const uint32_t* sel_offs;
const uint16_t* sel_idx;
} slice_build_ctx_t;
static inline void slice_build_task(const slice_build_ctx_t* c, int64_t t, bool scatter) {
int64_t* h = c->hist + t * c->n_groups;
const int64_t* row_gid = c->row_gid;
int64_t* out = c->idx_buf;
for (int64_t seg = c->seg_lo[t]; seg < c->seg_lo[t + 1]; seg++) {
int64_t s_lo = seg * RAY_MORSEL_ELEMS;
uint8_t f = c->sel_flg ? c->sel_flg[seg] : RAY_SEL_ALL;
if (f == RAY_SEL_NONE) continue;
if (f == RAY_SEL_ALL) {
int64_t s_hi = s_lo + RAY_MORSEL_ELEMS;
if (s_hi > c->nrows) s_hi = c->nrows;
for (int64_t r = s_lo; r < s_hi; r++) {
int64_t gi = row_gid[r];
if (gi < 0) continue;
if (scatter) out[h[gi]++] = r;
else h[gi]++;
}
continue;
}
for (uint32_t i = c->sel_offs[seg]; i < c->sel_offs[seg + 1]; i++) {
int64_t r = s_lo + c->sel_idx[i];
int64_t gi = row_gid[r];
if (gi < 0) continue;
if (scatter) out[h[gi]++] = r;
else h[gi]++;
}
}
}
static void slice_count_fn(void* vctx, uint32_t worker_id, int64_t t0, int64_t t1) {
(void)worker_id;
for (int64_t t = t0; t < t1; t++) slice_build_task((const slice_build_ctx_t*)vctx, t, false);
}
static void slice_scatter_fn(void* vctx, uint32_t worker_id, int64_t t0, int64_t t1) {
(void)worker_id;
for (int64_t t = t0; t < t1; t++) slice_build_task((const slice_build_ctx_t*)vctx, t, true);
}
static bool slice_build(const int64_t* row_gid, int64_t nrows, ray_t* sel,
int64_t n_groups, int64_t* grp_cnt, int64_t* offsets,
int64_t* pos, ray_t** idx_hdr) {
*idx_hdr = NULL;
int64_t n_segs = (nrows + RAY_MORSEL_ELEMS - 1) / RAY_MORSEL_ELEMS;
slice_build_ctx_t c = { .row_gid = row_gid, .nrows = nrows, .n_groups = n_groups };
int64_t visited = nrows;
if (sel) {
ray_rowsel_t* sm = ray_rowsel_meta(sel);
if ((int64_t)sm->n_segs < n_segs) n_segs = sm->n_segs;
c.sel_flg = ray_rowsel_flags(sel);
c.sel_offs = ray_rowsel_offsets(sel);
c.sel_idx = ray_rowsel_idx(sel);
visited = sm->total_pass;
}
ray_pool_t* pool = ray_pool_get();
int64_t n_tasks = 1;
if (pool && visited >= SLICE_SERIAL_ROWS && n_groups > 0 &&
ray_pool_par_dispatch_ok(pool, visited, SLICE_SERIAL_ROWS)) {
n_tasks = ray_pool_total_workers(pool);
if (n_tasks * n_groups > SLICE_HIST_CELLS) n_tasks = SLICE_HIST_CELLS / n_groups;
if (n_tasks > n_segs) n_tasks = n_segs;
if (n_tasks < 2) n_tasks = 1;
}
ray_t* hist_hdr = NULL;
ray_t* seg_hdr = NULL;
int64_t* hist = (int64_t*)scratch_calloc(&hist_hdr,
(size_t)n_tasks * (size_t)(n_groups > 0 ? n_groups : 1) * sizeof(int64_t));
int64_t* seg_lo = (int64_t*)scratch_alloc(&seg_hdr, (size_t)(n_tasks + 1) * sizeof(int64_t));
if (!hist || !seg_lo) {
if (hist_hdr) scratch_free(hist_hdr);
if (seg_hdr) scratch_free(seg_hdr);
return false;
}
seg_lo[0] = 0;
if (n_tasks > 1 && c.sel_flg) {
int64_t t = 1, acc = 0;
for (int64_t seg = 0; seg < n_segs && t < n_tasks; seg++) {
uint8_t f = c.sel_flg[seg];
if (f == RAY_SEL_ALL) {
int64_t s_hi = (seg + 1) * RAY_MORSEL_ELEMS;
acc += (s_hi > nrows ? nrows : s_hi) - seg * RAY_MORSEL_ELEMS;
} else if (f != RAY_SEL_NONE) {
acc += c.sel_offs[seg + 1] - c.sel_offs[seg];
}
while (t < n_tasks && acc * n_tasks >= visited * t) seg_lo[t++] = seg + 1;
}
while (t < n_tasks) seg_lo[t++] = n_segs;
} else {
for (int64_t t = 1; t < n_tasks; t++) seg_lo[t] = n_segs * t / n_tasks;
}
seg_lo[n_tasks] = n_segs;
c.hist = hist;
c.seg_lo = seg_lo;
if (n_tasks > 1) ray_pool_dispatch_n(pool, slice_count_fn, &c, (uint32_t)n_tasks);
else slice_count_fn(&c, 0, 0, 1);
memset(grp_cnt, 0, (size_t)n_groups * sizeof(int64_t));
for (int64_t t = 0; t < n_tasks; t++) {
const int64_t* h = hist + t * n_groups;
for (int64_t gi = 0; gi < n_groups; gi++) grp_cnt[gi] += h[gi];
}
int64_t total = 0;
for (int64_t gi = 0; gi < n_groups; gi++) { offsets[gi] = total; total += grp_cnt[gi]; }
if (n_groups > 0) memcpy(pos, offsets, (size_t)n_groups * sizeof(int64_t));
for (int64_t t = 0; t < n_tasks; t++) {
int64_t* h = hist + t * n_groups;
for (int64_t gi = 0; gi < n_groups; gi++) { int64_t k = h[gi]; h[gi] = pos[gi]; pos[gi] += k; }
}
*idx_hdr = ray_alloc((size_t)(total > 0 ? total : 1) * sizeof(int64_t));
if (!*idx_hdr) { scratch_free(hist_hdr); scratch_free(seg_hdr); return false; }
c.idx_buf = (int64_t*)ray_data(*idx_hdr);
if (n_tasks > 1) ray_pool_dispatch_n(pool, slice_scatter_fn, &c, (uint32_t)n_tasks);
else slice_scatter_fn(&c, 0, 0, 1);
scratch_free(hist_hdr);
scratch_free(seg_hdr);
return true;
}
static ray_t* query_materialize_parted_col(ray_t* col) {
if (!col) return NULL;
if (col->type == RAY_MAPCOMMON) return materialize_mapcommon(col);
if (!RAY_IS_PARTED(col->type)) {
ray_retain(col);
return col;
}
int8_t base = (int8_t)RAY_PARTED_BASETYPE(col->type);
ray_t** segs = (ray_t**)ray_data(col);
int64_t total = ray_parted_nrows(col);
if (base == RAY_STR) return parted_flatten_str(segs, col->len, total);
if (base == RAY_LIST) return parted_flatten_list(segs, col->len, total);
uint8_t attrs = (base == RAY_SYM) ? parted_sym_max_attrs(segs, col->len) : 0;
ray_t* flat = typed_vec_new(base, attrs, total);
if (!flat || RAY_IS_ERR(flat)) return flat ? flat : ray_error("oom", NULL);
flat->len = total;
int64_t off = 0;
uint8_t seg_nulls = 0;
for (int64_t s = 0; s < col->len; s++) {
ray_t* seg = segs[s];
if (!seg || seg->len <= 0) continue;
parted_copy_cells(ray_data(flat), base, attrs, off, seg, 0, seg->len);
seg_nulls |= seg->attrs & RAY_ATTR_HAS_NULLS;
off += seg->len;
}
flat->attrs |= seg_nulls;
if (flat->type == RAY_SYM) ray_sym_vec_adopt_domain(flat, sym_domain_rep(col));
return flat;
}
static ray_t* try_count_distinct_v2_rewrite(
ray_t* tbl,
ray_t* by_expr,
ray_t* where_expr,
ray_t** dict_elems, int64_t dict_n,
int64_t from_id, int64_t where_id, int64_t by_id,
int64_t take_id, int64_t asc_id, int64_t desc_id,
int64_t nearest_id)
{
if (!tbl || tbl->type != RAY_TABLE) return NULL;
int64_t K_syms[15];
int n_K = 0;
if (by_expr && by_expr->type == -RAY_SYM &&
!(by_expr->attrs & ATTR_QUOTED)) {
K_syms[n_K++] = by_expr->i64;
} else if (by_expr && by_expr->type == RAY_SYM && ray_is_vec(by_expr) &&
!(by_expr->attrs & ATTR_QUOTED)) {
int64_t n = ray_len(by_expr);
if (n == 0 || n > 15) return NULL;
for (int64_t i = 0; i < n; i++)
K_syms[n_K++] = ray_read_sym(ray_data(by_expr), i, by_expr->type, by_expr->attrs);
} else if (by_expr && by_expr->type == RAY_DICT) {
DICT_VIEW_DECL(byv);
DICT_VIEW_OPEN(by_expr, byv);
if (DICT_VIEW_OVERFLOW(byv)) { DICT_VIEW_CLOSE(byv); return NULL; }
int64_t pairs = byv_n / 2;
if (pairs == 0 || pairs > 15) { DICT_VIEW_CLOSE(byv); return NULL; }
for (int64_t i = 0; i < pairs; i++) {
ray_t* v = byv[i * 2 + 1];
if (!v || v->type != -RAY_SYM || (v->attrs & ATTR_QUOTED)) {
DICT_VIEW_CLOSE(byv);
return NULL;
}
K_syms[n_K++] = v->i64;
}
DICT_VIEW_CLOSE(byv);
} else {
return NULL;
}
int64_t cd_X_sym = -1;
int64_t cd_c_sym = -1;
int n_cd = 0, n_other = 0;
int64_t desc_col_sym = -1;
int64_t asc_col_sym = -1;
int has_take = 0;
int64_t take_n = -1;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
ray_t* val = dict_elems[i + 1];
if (kid == from_id || kid == where_id || kid == by_id ||
kid == nearest_id) continue;
if (kid == take_id) {
int64_t v;
if (atom_i64_const(val, &v) && v > 0) {
has_take = 1;
take_n = v;
} else {
return NULL;
}
continue;
}
if (kid == asc_id) {
if (val && val->type == -RAY_SYM && !(val->attrs & ATTR_QUOTED))
asc_col_sym = val->i64;
else return NULL;
continue;
}
if (kid == desc_id) {
if (val && val->type == -RAY_SYM && !(val->attrs & ATTR_QUOTED))
desc_col_sym = val->i64;
else return NULL;
continue;
}
ray_t* cd_inner = match_count_distinct(val);
if (cd_inner && cd_inner->type == -RAY_SYM &&
!(cd_inner->attrs & ATTR_QUOTED))
{
cd_X_sym = cd_inner->i64;
cd_c_sym = kid;
n_cd++;
} else if (val && val->type == -RAY_SYM &&
!(val->attrs & ATTR_QUOTED)) {
int matched = 0;
for (int j = 0; j < n_K; j++)
if (K_syms[j] == val->i64) { matched = 1; break; }
if (!matched) n_other++;
} else {
n_other++;
}
}
if (n_cd != 1 || n_other > 0) return NULL;
if (cd_X_sym < 0 || cd_c_sym < 0) return NULL;
if (desc_col_sym >= 0 && desc_col_sym != cd_c_sym) return NULL;
if (asc_col_sym >= 0 && asc_col_sym != cd_c_sym) return NULL;
if (desc_col_sym >= 0 && asc_col_sym >= 0) return NULL;
ray_t* K_cols[15];
for (int j = 0; j < n_K; j++) {
K_cols[j] = ray_table_get_col(tbl, K_syms[j]);
if (!K_cols[j]) return NULL;
int8_t kct_j = K_cols[j]->type;
if (RAY_IS_PARTED(kct_j) || kct_j == RAY_MAPCOMMON) return NULL;
int kct_ok_j = (kct_j == RAY_SYM || kct_j == RAY_BOOL || kct_j == RAY_U8 ||
kct_j == RAY_I16 || kct_j == RAY_I32 || kct_j == RAY_I64 ||
kct_j == RAY_DATE || kct_j == RAY_TIME || kct_j == RAY_TIMESTAMP || kct_j == RAY_F32 || kct_j == RAY_F64 ||
kct_j == RAY_STR || kct_j == RAY_GUID || kct_j == RAY_LIST);
if (!kct_ok_j) return NULL;
}
ray_t* X_col = ray_table_get_col(tbl, cd_X_sym);
if (!X_col) return NULL;
int8_t xct = X_col->type;
if (RAY_IS_PARTED(xct) || xct == RAY_MAPCOMMON) return NULL;
int xct_ok = (xct == RAY_SYM || xct == RAY_BOOL || xct == RAY_U8 ||
xct == RAY_I16 || xct == RAY_I32 || xct == RAY_I64 ||
xct == RAY_DATE || xct == RAY_TIME || xct == RAY_TIMESTAMP || xct == RAY_F32 || xct == RAY_F64 ||
xct == RAY_STR || xct == RAY_GUID || xct == RAY_LIST);
if (!xct_ok) return NULL;
if (where_expr && !ray_fused_group_supported(where_expr, tbl))
return NULL;
if (n_K == 1 && !where_expr) {
int64_t nrows = ray_table_nrows(tbl);
if (ray_pool_par_dispatch_ok(ray_pool_get(), nrows, CDF_MIN_ROWS)) {
ray_t* fr = ray_cd_fused(K_cols[0], X_col, nrows);
if (fr) {
if (RAY_IS_ERR(fr)) return fr;
ray_t* fk = ray_table_get_col_idx(fr, 0);
ray_t* fu = ray_table_get_col_idx(fr, 1);
int64_t ng = fk ? fk->len : 0;
ray_t* kv = col_vec_new(K_cols[0], ng > 0 ? ng : 1);
if (!kv || RAY_IS_ERR(kv)) {
ray_release(fr);
return kv ? kv : ray_error("oom", NULL);
}
if (kv->type == RAY_SYM)
ray_sym_vec_adopt_domain(kv, sym_domain_rep(K_cols[0]));
kv->len = ng;
{
const int64_t* src = (const int64_t*)ray_data(fk);
void* dst = ray_data(kv);
for (int64_t i = 0; i < ng; i++) {
if (kv->type == RAY_F32) { uint32_t bits = (uint32_t)src[i]; memcpy((float*)dst + i, &bits, 4); }
else if (kv->type == RAY_F64) memcpy((double*)dst + i, &src[i], 8);
else write_col_i64(dst, i, src[i], kv->type, kv->attrs);
}
if (ray_vec_may_have_nulls(K_cols[0])) kv->attrs |= RAY_ATTR_HAS_NULLS;
}
ray_t* out = ray_table_new(2);
if (out && !RAY_IS_ERR(out))
out = ray_table_add_col(out, K_syms[0], kv);
if (out && !RAY_IS_ERR(out))
out = ray_table_add_col(out, cd_c_sym, fu);
ray_release(kv);
ray_release(fr);
if (!out) return ray_error("oom", NULL);
if (RAY_IS_ERR(out)) return out;
return apply_sort_take(out, dict_elems, dict_n,
asc_id, desc_id, take_id, NULL);
}
}
}
ray_graph_t* g_in = ray_graph_new(tbl);
if (!g_in) return NULL;
ray_t* K_names[15];
ray_op_t* K_scans[15];
for (int j = 0; j < n_K; j++) {
K_names[j] = ray_sym_str(K_syms[j]);
if (!K_names[j]) { ray_graph_free(g_in); return NULL; }
K_scans[j] = ray_scan(g_in, ray_str_ptr(K_names[j]));
if (!K_scans[j]) { ray_graph_free(g_in); return NULL; }
}
ray_t* X_name = ray_sym_str(cd_X_sym);
if (!X_name) { ray_graph_free(g_in); return NULL; }
ray_op_t* X_scan = ray_scan(g_in, ray_str_ptr(X_name));
if (!X_scan) { ray_graph_free(g_in); return NULL; }
ray_t* ki_hdr = NULL;
ray_op_t** keys_in = (ray_op_t**)scratch_alloc(&ki_hdr,
(size_t)(n_K + 1) * sizeof(ray_op_t*));
if (!keys_in) { ray_graph_free(g_in); return ray_error("oom", NULL); }
for (int j = 0; j < n_K; j++) keys_in[j] = K_scans[j];
keys_in[n_K] = X_scan;
uint16_t agg_ops_in[1] = { OP_COUNT };
ray_op_t* agg_ins_in[1] = { K_scans[0] };
if (where_expr) {
ray_op_t* pred = compile_expr_dag(g_in, where_expr);
if (!pred) { scratch_free(ki_hdr); ray_graph_free(g_in); return NULL; }
ray_op_t* froot = ray_filter(g_in, ray_const_table(g_in, tbl), pred);
if (!froot) { scratch_free(ki_hdr); ray_graph_free(g_in); return NULL; }
froot = ray_optimize(g_in, froot);
ray_t* fres = exec_node(g_in, froot);
if (!fres || RAY_IS_ERR(fres)) {
if (g_in->selection) { ray_release(g_in->selection); g_in->selection = NULL; }
scratch_free(ki_hdr);
ray_graph_free(g_in);
return (fres && RAY_IS_ERR(fres)) ? fres : NULL;
}
}
ray_op_t* inner = ray_group(g_in, keys_in, n_K + 1,
agg_ops_in, agg_ins_in, 1);
if (!inner) { scratch_free(ki_hdr); ray_graph_free(g_in); return NULL; }
ray_t* dedup = ray_execute(g_in, inner);
scratch_free(ki_hdr);
ray_graph_free(g_in);
if (!dedup) return NULL;
if (RAY_IS_ERR(dedup)) return dedup;
if (dedup->type != RAY_TABLE) { ray_release(dedup); return NULL; }
ray_graph_t* g_out = ray_graph_new(dedup);
if (!g_out) { ray_release(dedup); return ray_error("oom", NULL); }
ray_op_t* K_scans2[15];
for (int j = 0; j < n_K; j++) {
K_scans2[j] = ray_scan(g_out, ray_str_ptr(K_names[j]));
if (!K_scans2[j]) { ray_graph_free(g_out); ray_release(dedup); return NULL; }
}
ray_op_t* keys_out[15];
for (int j = 0; j < n_K; j++) keys_out[j] = K_scans2[j];
uint16_t agg_ops_out[1] = { OP_COUNT };
ray_op_t* agg_ins_out[1] = { K_scans2[0] };
ray_group_emit_filter_t prev_emit = ray_group_emit_filter_get();
ray_group_emit_filter_t emit_f = {0};
int emit_set = 0;
if (desc_col_sym == cd_c_sym && has_take && take_n > 0) {
emit_f.enabled = true;
emit_f.agg_index = 0;
emit_f.top_count_take = take_n;
emit_f.min_count_exclusive = 0;
emit_f.desc = 1;
emit_f.target_depth = __VM ? __VM->eval_depth : 0;
ray_group_emit_filter_set(emit_f);
emit_set = 1;
}
ray_op_t* outer = ray_group(g_out, keys_out, n_K,
agg_ops_out, agg_ins_out, 1);
if (!outer) {
if (emit_set) ray_group_emit_filter_set(prev_emit);
ray_graph_free(g_out);
ray_release(dedup);
return ray_error("oom", NULL);
}
ray_t* result = ray_execute(g_out, outer);
if (emit_set) ray_group_emit_filter_set(prev_emit);
ray_graph_free(g_out);
ray_release(dedup);
if (!result || RAY_IS_ERR(result)) return result;
if (result->type != RAY_TABLE) return result;
{
int64_t nc = ray_table_ncols(result);
for (int64_t ci = 0; ci < nc; ci++) {
int64_t cn = ray_table_col_name(result, ci);
int is_key = 0;
for (int j = 0; j < n_K; j++) if (cn == K_syms[j]) { is_key = 1; break; }
if (!is_key && cn != cd_c_sym) {
ray_table_set_col_name(result, ci, cd_c_sym);
break;
}
}
}
return apply_sort_take(result, dict_elems, dict_n, asc_id, desc_id, take_id, NULL);
}
static ray_t* count_distinct_per_group_buf(ray_t* inner_expr, ray_t* tbl,
const int64_t* idx_buf,
const int64_t* offsets,
const int64_t* grp_cnt,
int64_t n_groups) {
ray_t* src = NULL;
if (inner_expr && inner_expr->type == -RAY_SYM &&
!(inner_expr->attrs & ATTR_QUOTED)) {
src = ray_table_get_col(tbl, inner_expr->i64);
if (src) ray_retain(src);
}
if (!src) {
if (ray_env_push_query_scope() != RAY_OK) return ray_error("oom", NULL);
ray_t* _aqt = bind_all_columns(tbl);
src = ray_eval(inner_expr);
g_active_query_table = _aqt;
ray_env_pop_scope();
if (!src || RAY_IS_ERR(src)) return src ? src : ray_error("domain", "select by: failed to evaluate count-distinct source");
}
if (src && !RAY_IS_ERR(src) && (RAY_IS_PARTED(src->type) || src->type == RAY_MAPCOMMON)) {
ray_t* flat = query_materialize_parted_col(src);
ray_release(src);
src = flat;
if (!src || RAY_IS_ERR(src)) return src ? src : ray_error("oom", NULL);
}
if (src->type == RAY_STR || src->type == RAY_GUID || src->type == RAY_LIST) {
ray_t* wide = agg_count_distinct_indexed(src, idx_buf, offsets, grp_cnt, n_groups);
ray_release(src);
return wide;
}
ray_t* out = ray_vec_new(RAY_I64, n_groups);
if (!out || RAY_IS_ERR(out)) {
ray_release(src);
return out ? out : ray_error("oom", NULL);
}
out->len = n_groups;
int64_t* odata = (int64_t*)ray_data(out);
{
int8_t st = src->type;
bool flat_ok = (st == RAY_BOOL || st == RAY_U8 ||
st == RAY_I16 || st == RAY_I32 || st == RAY_I64 ||
st == RAY_F64 || st == RAY_DATE || st == RAY_TIME ||
st == RAY_TIMESTAMP || RAY_IS_SYM(st));
ray_pool_t* pool = ray_pool_get();
if (flat_ok && pool && ray_pool_total_workers(pool) >= 2 && n_groups >= 4) {
int64_t sym_cap_bound = 0;
if (RAY_IS_SYM(st)) {
int64_t dc = ray_sym_domain_count(ray_sym_vec_domain(src));
if (dc > 0 && dc <= 65536) {
uint64_t b = 32; while (b < (uint64_t)dc * 2) b <<= 1;
sym_cap_bound = (int64_t)b;
}
}
cdpg_buf_par_ctx_t pctx = {
.in_type = st,
.in_attrs = src->attrs,
.base = ray_data(src),
.esz = ray_sym_elem_size(st, src->attrs),
.is_f64 = (st == RAY_F64),
.idx_buf = idx_buf,
.offsets = offsets,
.grp_cnt = grp_cnt,
.odata = odata,
.sym_cap_bound = sym_cap_bound,
.oom = 0,
};
ray_group_dispatch(cdpg_buf_par_fn, &pctx, grp_cnt, n_groups);
if (!atomic_load_explicit(&pctx.oom, memory_order_relaxed)) {
ray_release(src);
return out;
}
}
}
for (int64_t gi = 0; gi < n_groups; gi++) {
int64_t cnt = grp_cnt[gi];
if (cnt == 0) { odata[gi] = 0; continue; }
ray_t* subset = gather_by_idx(src,
(int64_t*)&idx_buf[offsets[gi]], cnt);
if (!subset || RAY_IS_ERR(subset)) {
ray_t* err = subset ? subset : ray_error("oom", NULL);
ray_release(src); ray_release(out);
return err;
}
ray_t* cv = exec_count_distinct(NULL, NULL, subset);
ray_release(subset);
if (!cv || RAY_IS_ERR(cv)) {
ray_t* err = cv ? cv : ray_error("oom", NULL);
ray_release(src); ray_release(out);
return err;
}
odata[gi] = (cv->type == -RAY_I64) ? cv->i64
: (cv->type == -RAY_I32) ? (int64_t)cv->i32 : 0;
ray_release(cv);
}
ray_release(src);
return out;
}
static ray_t* count_distinct_per_group_groups(ray_t* inner_expr, ray_t* tbl,
ray_t* groups, int64_t n_groups) {
{
if (!groups || groups->type != RAY_LIST || n_groups < 0)
return ray_error("type", "count distinct: expected a groups list, got %s", groups ? ray_type_name(groups->type) : "null");
ray_t** items0 = (ray_t**)ray_data(groups);
int64_t total = 0;
for (int64_t gi = 0; gi < n_groups; gi++) {
ray_t* idx_list = items0[gi * 2 + 1];
total += idx_list ? ray_len(idx_list) : 0;
}
ray_t *idx_hdr = NULL, *off_hdr = NULL, *cnt_hdr = NULL;
int64_t* idx_buf = (int64_t*)scratch_alloc(&idx_hdr,
(size_t)(total > 0 ? total : 1) * sizeof(int64_t));
int64_t* offsets = (int64_t*)scratch_alloc(&off_hdr,
(size_t)(n_groups > 0 ? n_groups : 1) * sizeof(int64_t));
int64_t* counts = (int64_t*)scratch_alloc(&cnt_hdr,
(size_t)(n_groups > 0 ? n_groups : 1) * sizeof(int64_t));
if (!idx_buf || !offsets || !counts) {
if (idx_hdr) scratch_free(idx_hdr);
if (off_hdr) scratch_free(off_hdr);
if (cnt_hdr) scratch_free(cnt_hdr);
return ray_error("oom", NULL);
}
int64_t pos = 0;
for (int64_t gi = 0; gi < n_groups; gi++) {
ray_t* idx_list = items0[gi * 2 + 1];
int64_t cnt = idx_list ? ray_len(idx_list) : 0;
offsets[gi] = pos;
counts[gi] = cnt;
if (cnt > 0) {
if (idx_list->type == RAY_I64) {
memcpy(idx_buf + pos, ray_data(idx_list),
(size_t)cnt * sizeof(int64_t));
} else {
for (int64_t k = 0; k < cnt; k++) {
int alloc = 0;
ray_t* e = collection_elem(idx_list, k, &alloc);
idx_buf[pos + k] = e ? as_i64(e) : 0;
if (alloc && e) ray_release(e);
}
}
}
pos += cnt;
}
ray_t* out = count_distinct_per_group_buf(
inner_expr, tbl, idx_buf, offsets, counts, n_groups);
scratch_free(idx_hdr);
scratch_free(off_hdr);
scratch_free(cnt_hdr);
return out;
}
ray_t* src = NULL;
if (inner_expr && inner_expr->type == -RAY_SYM &&
!(inner_expr->attrs & ATTR_QUOTED)) {
src = ray_table_get_col(tbl, inner_expr->i64);
if (src) ray_retain(src);
}
if (!src) {
if (ray_env_push_query_scope() != RAY_OK) return ray_error("oom", NULL);
ray_t* _aqt = bind_all_columns(tbl);
src = ray_eval(inner_expr);
g_active_query_table = _aqt;
ray_env_pop_scope();
if (!src || RAY_IS_ERR(src)) return src ? src : ray_error("domain", "select by: failed to evaluate count-distinct source");
}
if (src && !RAY_IS_ERR(src) && (RAY_IS_PARTED(src->type) || src->type == RAY_MAPCOMMON)) {
ray_t* flat = query_materialize_parted_col(src);
ray_release(src);
src = flat;
if (!src || RAY_IS_ERR(src)) return src ? src : ray_error("oom", NULL);
}
ray_t* out = ray_vec_new(RAY_I64, n_groups);
if (!out || RAY_IS_ERR(out)) { ray_release(src); return out ? out : ray_error("oom", NULL); }
out->len = n_groups;
int64_t* odata = (int64_t*)ray_data(out);
ray_t** items = (ray_t**)ray_data(groups);
for (int64_t gi = 0; gi < n_groups; gi++) {
ray_t* idx_list = items[gi * 2 + 1];
if (!idx_list) { odata[gi] = 0; continue; }
int64_t cnt = ray_len(idx_list);
if (cnt == 0) { odata[gi] = 0; continue; }
ray_t* subset = NULL;
ray_t* tmp_hdr = NULL;
if (idx_list->type == RAY_I64) {
subset = gather_by_idx(src, (int64_t*)ray_data(idx_list), cnt);
} else {
int64_t* tmp = (int64_t*)scratch_alloc(&tmp_hdr,
(size_t)cnt * sizeof(int64_t));
if (!tmp) {
ray_release(src); ray_release(out);
return ray_error("oom", NULL);
}
for (int64_t k = 0; k < cnt; k++) {
int alloc = 0;
ray_t* e = collection_elem(idx_list, k, &alloc);
tmp[k] = e ? as_i64(e) : 0;
if (alloc && e) ray_release(e);
}
subset = gather_by_idx(src, tmp, cnt);
scratch_free(tmp_hdr);
}
if (!subset || RAY_IS_ERR(subset)) {
ray_t* err = subset ? subset : ray_error("oom", NULL);
ray_release(src); ray_release(out);
return err;
}
ray_t* cv = exec_count_distinct(NULL, NULL, subset);
ray_release(subset);
if (!cv || RAY_IS_ERR(cv)) {
ray_t* err = cv ? cv : ray_error("oom", NULL);
ray_release(src); ray_release(out);
return err;
}
odata[gi] = (cv->type == -RAY_I64) ? cv->i64
: (cv->type == -RAY_I32) ? (int64_t)cv->i32 : 0;
ray_release(cv);
}
ray_release(src);
return out;
}
static inline int64_t key_read_i64(const void* d, int64_t idx,
int8_t bt, uint8_t attrs) {
switch (bt) {
case RAY_BOOL:
case RAY_U8: return ((const uint8_t*)d)[idx];
case RAY_I16: return ((const int16_t*)d)[idx];
case RAY_I32:
case RAY_DATE:
case RAY_TIME: return ((const int32_t*)d)[idx];
case RAY_I64:
case RAY_TIMESTAMP: return ((const int64_t*)d)[idx];
case RAY_F32: { uint32_t u;
memcpy(&u, &((const float*)d)[idx], 4);
return (int64_t)u; }
case RAY_F64: { int64_t u;
memcpy(&u, &((const double*)d)[idx], 8);
return u; }
case RAY_SYM: return ray_read_sym(d, idx, bt, attrs);
default: return 0;
}
}
typedef struct {
const int64_t* hk_keys;
const int32_t* hk_gid_p1;
const int64_t* hk_gid64;
uint64_t mask;
const void* orig_key_data;
int8_t okt;
uint8_t okt_attrs;
int64_t* row_gid;
ray_t* selection;
const uint8_t* sel_flg;
const uint32_t* sel_offs;
const uint16_t* sel_idx;
uint32_t sel_n_segs;
} rgid_probe_ctx_t;
static void rgid_probe_fn(void* ctx_, uint32_t worker_id,
int64_t start, int64_t end) {
(void)worker_id;
rgid_probe_ctx_t* x = (rgid_probe_ctx_t*)ctx_;
int use_i64 = (x->hk_gid64 != NULL);
uint64_t mask = x->mask;
if (x->selection) {
const uint8_t* flg = x->sel_flg;
const uint32_t* offs = x->sel_offs;
const uint16_t* lidx = x->sel_idx;
uint32_t seg_lo = (uint32_t)(start / RAY_MORSEL_ELEMS);
uint32_t seg_hi = (uint32_t)((end + RAY_MORSEL_ELEMS - 1) / RAY_MORSEL_ELEMS);
if (seg_hi > x->sel_n_segs) seg_hi = x->sel_n_segs;
for (uint32_t seg = seg_lo; seg < seg_hi; seg++) {
int64_t s_lo = (int64_t)seg * RAY_MORSEL_ELEMS;
int64_t s_hi = s_lo + RAY_MORSEL_ELEMS;
if (s_lo < start) s_lo = start;
if (s_hi > end) s_hi = end;
uint8_t f = flg[seg];
if (f == RAY_SEL_NONE) {
for (int64_t r = s_lo; r < s_hi; r++) x->row_gid[r] = -1;
continue;
}
if (f == RAY_SEL_ALL) {
for (int64_t r = s_lo; r < s_hi; r++) {
int64_t rv = key_read_i64(x->orig_key_data, r, x->okt, x->okt_attrs);
uint64_t h = (uint64_t)rv * 0x9E3779B97F4A7C15ULL;
h ^= h >> 33;
uint64_t sl = h & mask;
int64_t found = -1;
for (;;) {
int64_t cur_p1 = use_i64 ? x->hk_gid64[sl]
: (int64_t)x->hk_gid_p1[sl];
if (cur_p1 == 0) break;
if (x->hk_keys[sl] == rv) { found = cur_p1 - 1; break; }
sl = (sl + 1) & mask;
}
x->row_gid[r] = found;
}
continue;
}
uint8_t in_seg[RAY_MORSEL_ELEMS / 8] = {0};
uint32_t off = offs[seg];
uint32_t cnt = offs[seg + 1] - off;
for (uint32_t i = 0; i < cnt; i++) {
uint16_t loc = lidx[off + i];
in_seg[loc >> 3] |= (uint8_t)(1u << (loc & 7));
}
int64_t base = (int64_t)seg * RAY_MORSEL_ELEMS;
for (int64_t r = s_lo; r < s_hi; r++) {
uint16_t loc = (uint16_t)(r - base);
if (!(in_seg[loc >> 3] & (1u << (loc & 7)))) {
x->row_gid[r] = -1;
continue;
}
int64_t rv = key_read_i64(x->orig_key_data, r, x->okt, x->okt_attrs);
uint64_t h = (uint64_t)rv * 0x9E3779B97F4A7C15ULL;
h ^= h >> 33;
uint64_t sl = h & mask;
int64_t found = -1;
for (;;) {
int64_t cur_p1 = use_i64 ? x->hk_gid64[sl]
: (int64_t)x->hk_gid_p1[sl];
if (cur_p1 == 0) break;
if (x->hk_keys[sl] == rv) { found = cur_p1 - 1; break; }
sl = (sl + 1) & mask;
}
x->row_gid[r] = found;
}
}
return;
}
for (int64_t r = start; r < end; r++) {
int64_t rv = key_read_i64(x->orig_key_data, r, x->okt, x->okt_attrs);
uint64_t h = (uint64_t)rv * 0x9E3779B97F4A7C15ULL;
h ^= h >> 33;
uint64_t s = h & mask;
int64_t found = -1;
for (;;) {
int64_t cur_p1 = use_i64 ? x->hk_gid64[s] : (int64_t)x->hk_gid_p1[s];
if (cur_p1 == 0) break;
if (x->hk_keys[s] == rv) { found = cur_p1 - 1; break; }
s = (s + 1) & mask;
}
x->row_gid[r] = found;
}
}
typedef struct {
const int32_t* slot_gid_p1;
uint64_t mask;
const ray_str_t* gdesc;
const char* gpool;
const ray_str_t* sdesc;
const char* spool;
int64_t* row_gid;
} rgid_probe_str_ctx_t;
typedef struct { const int32_t* scan_codes; const int32_t* code_to_gid; int64_t n_distinct; int64_t* row_gid; } dictgid_ctx_t;
static void dictgid_fn(void* ctx_, uint32_t worker_id, int64_t start, int64_t end) {
(void)worker_id;
dictgid_ctx_t* c = (dictgid_ctx_t*)ctx_;
for (int64_t r = start; r < end; r++) {
int32_t cc = c->scan_codes[r];
c->row_gid[r] = (cc >= 0 && cc < c->n_distinct) ? c->code_to_gid[cc] : -1;
}
}
static void rgid_probe_str_fn(void* ctx_, uint32_t worker_id,
int64_t start, int64_t end) {
(void)worker_id;
rgid_probe_str_ctx_t* x = (rgid_probe_str_ctx_t*)ctx_;
uint64_t mask = x->mask;
for (int64_t r = start; r < end; r++) {
uint64_t h = ray_str_t_hash(&x->sdesc[r], x->spool);
uint64_t s = h & mask;
int64_t found = -1;
for (;;) {
int32_t g_p1 = x->slot_gid_p1[s];
if (g_p1 == 0) break;
int32_t g = g_p1 - 1;
if (ray_str_t_eq(&x->gdesc[g], x->gpool, &x->sdesc[r], x->spool)) {
found = g; break;
}
s = (s + 1) & mask;
}
x->row_gid[r] = found;
}
}
static int can_atom_broadcast(ray_t* a) {
if (!a || !ray_is_atom(a)) return 0;
if (a->type == -RAY_SYM && !(a->attrs & ATTR_QUOTED)) return 0;
int8_t vt = (int8_t)(-a->type);
switch (vt) {
case RAY_BOOL: case RAY_U8:
case RAY_I16: case RAY_I32:
case RAY_I64: case RAY_F32: case RAY_F64: case RAY_GUID: case RAY_STR:
case RAY_DATE: case RAY_TIME: case RAY_TIMESTAMP:
case RAY_SYM:
return 1;
default:
return 0;
}
}
static ray_t* atom_broadcast_vec(ray_t* a, int64_t n) {
if (!a || !ray_is_atom(a) || n < 0) return NULL;
int8_t vec_type = (int8_t)(-a->type);
if (vec_type <= 0) return NULL;
uint8_t sym_w = 0;
if (vec_type == RAY_SYM) {
uint64_t id = (uint64_t)a->i64;
sym_w = id <= 0xFFu ? RAY_SYM_W8
: id <= 0xFFFFu ? RAY_SYM_W16
: id <= 0xFFFFFFFFu ? RAY_SYM_W32
: RAY_SYM_W64;
}
ray_t* v;
if (vec_type == RAY_SYM) {
v = ray_sym_vec_new(sym_w, n);
} else {
v = ray_vec_new(vec_type, n);
}
if (!v || RAY_IS_ERR(v)) return NULL;
v->len = n;
if (n == 0) return v;
void* dst = ray_data(v);
switch (vec_type) {
case RAY_BOOL:
case RAY_U8: {
memset(dst, a->b8, (size_t)n);
break;
}
case RAY_I16: {
int16_t val = a->i16;
int16_t* d = (int16_t*)dst;
for (int64_t i = 0; i < n; i++) d[i] = val;
break;
}
case RAY_I32:
case RAY_DATE:
case RAY_TIME: {
int32_t val = a->i32;
int32_t* d = (int32_t*)dst;
for (int64_t i = 0; i < n; i++) d[i] = val;
break;
}
case RAY_I64:
case RAY_TIMESTAMP: {
int64_t val = a->i64;
int64_t* d = (int64_t*)dst;
for (int64_t i = 0; i < n; i++) d[i] = val;
break;
}
case RAY_F32: {
float* d = dst;
for (int64_t i = 0; i < n; i++) d[i] = (float)a->f64;
break;
}
case RAY_GUID:
for (int64_t i = 0; i < n; i++)
memcpy((char*)dst + (size_t)i * 16, ray_data(a->obj), 16);
break;
case RAY_STR:
for (int64_t i = 0; i < n; i++) {
ray_t* next = ray_str_vec_set(v, i, ray_str_ptr(a), ray_str_len(a));
if (!next || RAY_IS_ERR(next)) { ray_release(v); return NULL; }
v = next;
}
break;
case RAY_F64: {
double val = a->f64;
double* d = (double*)dst;
for (int64_t i = 0; i < n; i++) d[i] = val;
break;
}
case RAY_SYM: {
if (sym_w == RAY_SYM_W8) {
memset(dst, (uint8_t)a->i64, (size_t)n);
} else if (sym_w == RAY_SYM_W16) {
uint16_t val = (uint16_t)a->i64;
uint16_t* d = (uint16_t*)dst;
for (int64_t i = 0; i < n; i++) d[i] = val;
} else if (sym_w == RAY_SYM_W32) {
uint32_t val = (uint32_t)a->i64;
uint32_t* d = (uint32_t*)dst;
for (int64_t i = 0; i < n; i++) d[i] = val;
} else {
int64_t val = a->i64;
int64_t* d = (int64_t*)dst;
for (int64_t i = 0; i < n; i++) d[i] = val;
}
break;
}
default:
ray_release(v);
return NULL;
}
if (RAY_ATOM_IS_NULL(a)) {
v->attrs |= RAY_ATTR_HAS_NULLS;
}
return v;
}
static ray_t* scalar_result_col(ray_t* value) {
if (!value || RAY_IS_ERR(value)) return value ? value : ray_error("domain", "select: scalar aggregate produced no result");
if (ray_is_atom(value)) {
ray_t* col = atom_broadcast_vec(value, 1);
if (col) return col;
ray_t* boxed = ray_alloc(sizeof(ray_t*));
if (!boxed) return ray_error("oom", NULL);
boxed->type = RAY_LIST;
boxed->len = 1;
((ray_t**)ray_data(boxed))[0] = value;
ray_retain(value);
return boxed;
}
if (ray_len(value) != 1)
return ray_error("length", "select: scalar aggregate produced %lld rows", (long long)ray_len(value));
ray_retain(value);
return value;
}
static ray_t* eval_scalar_agg_outputs(ray_t** dict_elems, int64_t dict_n,
ray_t* tbl,
int64_t from_id, int64_t where_id,
int64_t by_id, int64_t take_id,
int64_t asc_id, int64_t desc_id,
int64_t nearest_id) {
ray_t* result = ray_table_new(0);
if (!result || RAY_IS_ERR(result))
return result ? result : ray_error("oom", NULL);
int64_t nrows = ray_table_nrows(tbl);
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id ||
kid == nearest_id) continue;
ray_t* val_expr = dict_elems[i + 1];
if (!is_agg_expr(val_expr)) {
ray_release(result);
return ray_error("domain", "select: scalar aggregate fallback saw non-aggregate output");
}
ray_t** agg_elems = (ray_t**)ray_data(val_expr);
if (ray_len(val_expr) != 2) {
ray_release(result);
return ray_error("domain", "select: scalar aggregate fallback only supports unary aggregates");
}
ray_t* fn_obj = ray_env_get(agg_elems[0]->i64);
if (!fn_obj || fn_obj->type != RAY_UNARY) {
ray_release(result);
return ray_error("type", "select: aggregate must be a unary function, got %s",
fn_obj ? ray_type_name(fn_obj->type) : "null");
}
ray_t* src = is_whole_column_projection(agg_elems[1])
? eval_expr_whole_column(agg_elems[1], tbl)
: eval_expr_per_row(agg_elems[1], tbl, nrows);
if (!src || RAY_IS_ERR(src)) {
ray_release(result);
return src ? src : ray_error("domain", "select: failed to evaluate aggregation source");
}
ray_unary_fn uf = (ray_unary_fn)(uintptr_t)fn_obj->i64;
ray_t* agg_value = uf(src);
ray_release(src);
if (agg_value && !RAY_IS_ERR(agg_value) && ray_is_lazy(agg_value))
agg_value = ray_lazy_materialize(agg_value);
if (!agg_value || RAY_IS_ERR(agg_value)) {
ray_release(result);
return agg_value ? agg_value : ray_error("domain", "select: scalar aggregate evaluation failed");
}
ray_t* col = scalar_result_col(agg_value);
ray_release(agg_value);
if (!col || RAY_IS_ERR(col)) {
ray_release(result);
return col ? col : ray_error("domain", "select: failed to materialize scalar aggregate");
}
result = ray_table_add_col(result, kid, col);
ray_release(col);
if (!result || RAY_IS_ERR(result))
return result ? result : ray_error("oom", NULL);
}
return result;
}
ray_t* ray_select(ray_t** args, int64_t n);
static ray_t* ray_select_impl(ray_t** args, int64_t n, bool aliases_resolved);
ray_t* ray_update(ray_t** args, int64_t n);
ray_t* ray_insert(ray_t** args, int64_t n);
ray_t* ray_upsert(ray_t** args, int64_t n);
static ray_t* try_stream_parted_order_by(
ray_t* dict, ray_t* tbl, ray_t** dict_elems, int64_t dict_n,
int64_t from_id, int64_t where_id, int64_t by_id, int64_t take_id,
int64_t asc_id, int64_t desc_id, int64_t nearest_id);
static ray_t* try_stream_parted_order_by_topk(
ray_t* dict, ray_t* tbl, ray_t** dict_elems, int64_t dict_n,
int64_t from_id, int64_t where_id, int64_t by_id, int64_t take_id,
int64_t asc_id, int64_t desc_id, int64_t nearest_id);
ray_t* ray_select_fn(ray_t** args, int64_t n) {
return ray_select(args, n);
}
static uint8_t win_kind_from_name(const char* s, size_t len) {
switch (len) {
case 3:
if (memcmp(s, "sum", 3) == 0) return RAY_WIN_SUM;
if (memcmp(s, "avg", 3) == 0) return RAY_WIN_AVG;
if (memcmp(s, "min", 3) == 0) return RAY_WIN_MIN;
if (memcmp(s, "max", 3) == 0) return RAY_WIN_MAX;
break;
case 4:
if (memcmp(s, "last", 4) == 0) return RAY_WIN_LAST_VALUE;
break;
case 5:
if (memcmp(s, "count", 5) == 0) return RAY_WIN_COUNT;
if (memcmp(s, "first", 5) == 0) return RAY_WIN_FIRST_VALUE;
break;
default: break;
}
return 255;
}
ray_t* ray_window_fn(ray_t** args, int64_t n) {
if (n < 1)
return ray_error("arity", "window: expects a query dict, got %lld args", (long long)n);
ray_t* dict = args[0];
if (!dict || dict->type != RAY_DICT)
return ray_error("type", "window: argument must be a dict, got %s",
dict ? ray_type_name(dict->type) : "null");
ray_t* from_expr = dict_get(dict, "from");
if (!from_expr)
return ray_error("domain", "window: missing `from:` clause");
ray_t* tbl = ray_eval(from_expr);
if (!tbl || RAY_IS_ERR(tbl))
return tbl ? tbl : ray_error("domain", "window: `from:` evaluation failed");
if (tbl->type != RAY_TABLE) {
int8_t t = tbl->type; ray_release(tbl);
return ray_error("type", "window: `from:` must evaluate to a table, got %s",
ray_type_name(t));
}
ray_t* funcs_expr = dict_get(dict, "funcs");
if (!funcs_expr || funcs_expr->type != RAY_DICT) {
ray_release(tbl);
return ray_error("domain", "window: missing or invalid `funcs:` clause (must be a dict)");
}
DICT_VIEW_DECL(fv);
DICT_VIEW_OPEN(funcs_expr, fv);
if (DICT_VIEW_OVERFLOW(fv)) {
DICT_VIEW_CLOSE(fv); ray_release(tbl);
return ray_error("oom", NULL);
}
int n_funcs = (int)(fv_n / 2);
if (n_funcs == 0) {
DICT_VIEW_CLOSE(fv); ray_release(tbl);
return ray_error("domain", "window: `funcs:` must have at least 1 entry, got %d", n_funcs);
}
ray_t* frame_expr = dict_get(dict, "frame");
uint8_t frame_start = RAY_BOUND_UNBOUNDED_PRECEDING;
uint8_t frame_end = RAY_BOUND_UNBOUNDED_FOLLOWING;
int64_t frame_start_n = 0;
if (frame_expr) {
if (frame_expr->type == -RAY_SYM) {
ray_t* fs = ray_sym_str(frame_expr->i64);
if (fs) {
const char* fsp = ray_str_ptr(fs);
size_t fsl = ray_str_len(fs);
if (fsl == 7 && memcmp(fsp, "running", 7) == 0)
frame_end = RAY_BOUND_CURRENT_ROW;
else if (!(fsl == 5 && memcmp(fsp, "whole", 5) == 0)) {
DICT_VIEW_CLOSE(fv); ray_release(tbl);
return ray_error("domain", "window: frame must be 'whole, 'running, or a positive row count");
}
}
} else {
int64_t width = 0;
switch (frame_expr->type) {
case -RAY_I64: width = frame_expr->i64; break;
case -RAY_I32: width = frame_expr->i32; break;
case -RAY_I16: width = frame_expr->i16; break;
case -RAY_U8:
case -RAY_BOOL: width = frame_expr->u8; break;
default:
DICT_VIEW_CLOSE(fv); ray_release(tbl);
return ray_error("domain", "window: frame must be 'whole, 'running, or a positive row count");
}
if (width < 1) {
DICT_VIEW_CLOSE(fv); ray_release(tbl);
return ray_error("range", "window: trailing frame row count must be >= 1");
}
frame_start = RAY_BOUND_N_PRECEDING;
frame_end = RAY_BOUND_CURRENT_ROW;
frame_start_n = width - 1;
}
}
ray_graph_t* g = ray_graph_new(tbl);
if (!g) { DICT_VIEW_CLOSE(fv); ray_release(tbl); return ray_error("oom", NULL); }
ray_op_t* tbl_node = ray_const_table(g, tbl);
ray_t* pe = dict_get(dict, "part");
int64_t n_part_max = (pe && pe->type == RAY_SYM) ? pe->len : 1;
if (n_part_max < 1) n_part_max = 1;
ray_t* part_hdr = NULL;
ray_op_t** part_ops = (ray_op_t**)scratch_alloc(&part_hdr, (size_t)n_part_max * sizeof(ray_op_t*));
if (!part_ops) { DICT_VIEW_CLOSE(fv); ray_graph_free(g); ray_release(tbl); return ray_error("oom", NULL); }
int64_t n_part = 0;
if (pe) {
if (pe->type == -RAY_SYM && !(pe->attrs & ATTR_QUOTED)) {
ray_t* s = ray_sym_str(pe->i64);
if (s) part_ops[n_part++] = ray_scan(g, ray_str_ptr(s));
} else if (pe->type == RAY_SYM) {
for (int64_t i = 0; i < pe->len; i++) {
int64_t sid = sym_cell_runtime_id(pe, i);
ray_t* s = ray_sym_str(sid);
if (s) part_ops[n_part++] = ray_scan(g, ray_str_ptr(s));
}
}
}
ray_t* oe = dict_get(dict, "order");
int64_t n_order_max = (oe && oe->type == RAY_SYM) ? oe->len : 1;
if (n_order_max < 1) n_order_max = 1;
ray_t* order_hdr = NULL;
ray_op_t** order_ops = (ray_op_t**)scratch_alloc(&order_hdr,
(size_t)n_order_max * (sizeof(ray_op_t*) + 1));
if (!order_ops) {
DICT_VIEW_CLOSE(fv); scratch_free(part_hdr); ray_graph_free(g); ray_release(tbl);
return ray_error("oom", NULL);
}
uint8_t* order_descs = (uint8_t*)(order_ops + n_order_max);
memset(order_descs, 0, (size_t)n_order_max);
int64_t n_order = 0;
if (oe) {
if (oe->type == -RAY_SYM && !(oe->attrs & ATTR_QUOTED)) {
ray_t* s = ray_sym_str(oe->i64);
if (s) order_ops[n_order++] = ray_scan(g, ray_str_ptr(s));
} else if (oe->type == RAY_SYM) {
for (int64_t i = 0; i < oe->len; i++) {
int64_t sid = sym_cell_runtime_id(oe, i);
ray_t* s = ray_sym_str(sid);
if (s) order_ops[n_order++] = ray_scan(g, ray_str_ptr(s));
}
}
}
ray_t* funcs_hdr = NULL;
size_t func_ptr_sz = (size_t)n_funcs * sizeof(ray_op_t*);
size_t func_i64_sz = (size_t)n_funcs * sizeof(int64_t);
void* func_scratch = scratch_alloc(&funcs_hdr, func_ptr_sz + func_i64_sz + (size_t)n_funcs);
if (!func_scratch) {
DICT_VIEW_CLOSE(fv); scratch_free(order_hdr); scratch_free(part_hdr); ray_graph_free(g); ray_release(tbl);
return ray_error("oom", NULL);
}
ray_op_t** func_inputs = (ray_op_t**)func_scratch;
int64_t* func_params = (int64_t*)((char*)func_scratch + func_ptr_sz);
uint8_t* func_kinds = (uint8_t*)((char*)func_scratch + func_ptr_sz + func_i64_sz);
ray_t* outname_hdr = NULL;
int64_t* out_name_ids = (int64_t*)scratch_alloc(&outname_hdr, (size_t)n_funcs * sizeof(int64_t));
if (!out_name_ids) {
DICT_VIEW_CLOSE(fv); scratch_free(funcs_hdr); scratch_free(order_hdr); scratch_free(part_hdr);
ray_graph_free(g); ray_release(tbl);
return ray_error("oom", NULL);
}
for (int fi = 0; fi < n_funcs; fi++) {
ray_t* key_atom = fv[fi * 2];
ray_t* val_expr = fv[fi * 2 + 1];
out_name_ids[fi] = (key_atom && key_atom->type == -RAY_SYM) ? key_atom->i64 : -1;
if (!val_expr || val_expr->type != RAY_LIST || ray_len(val_expr) < 2) {
DICT_VIEW_CLOSE(fv); scratch_free(outname_hdr); scratch_free(funcs_hdr); scratch_free(order_hdr); scratch_free(part_hdr);
ray_graph_free(g); ray_release(tbl);
return ray_error("domain",
"window: `funcs:` value must be a function call like (min v), got wrong shape at entry %d",
fi);
}
ray_t** fel = (ray_t**)ray_data(val_expr);
ray_t* fn_expr = fel[0];
ray_t* col_expr = fel[1];
if (!fn_expr || fn_expr->type != -RAY_SYM) {
DICT_VIEW_CLOSE(fv); scratch_free(outname_hdr); scratch_free(funcs_hdr); scratch_free(order_hdr); scratch_free(part_hdr);
ray_graph_free(g); ray_release(tbl);
return ray_error("domain", "window: function name must be a symbol");
}
ray_t* fn_s = ray_sym_str(fn_expr->i64);
if (!fn_s) {
DICT_VIEW_CLOSE(fv); scratch_free(outname_hdr); scratch_free(funcs_hdr); scratch_free(order_hdr); scratch_free(part_hdr);
ray_graph_free(g); ray_release(tbl);
return ray_error("domain", "window: unknown function");
}
uint8_t kind = win_kind_from_name(ray_str_ptr(fn_s), ray_str_len(fn_s));
if (kind == 255) {
DICT_VIEW_CLOSE(fv); scratch_free(outname_hdr); scratch_free(funcs_hdr); scratch_free(order_hdr); scratch_free(part_hdr);
ray_graph_free(g); ray_release(tbl);
return ray_error("domain", "window: unsupported window function '%s'", ray_str_ptr(fn_s));
}
if ((kind == RAY_WIN_FIRST_VALUE || kind == RAY_WIN_LAST_VALUE) && n_order == 0) {
DICT_VIEW_CLOSE(fv); scratch_free(outname_hdr); scratch_free(funcs_hdr); scratch_free(order_hdr); scratch_free(part_hdr);
ray_graph_free(g); ray_release(tbl);
return ray_error("domain", "window: first/last require order:");
}
func_kinds[fi] = kind;
func_params[fi] = 0;
if (!col_expr || col_expr->type != -RAY_SYM) {
DICT_VIEW_CLOSE(fv); scratch_free(outname_hdr); scratch_free(funcs_hdr); scratch_free(order_hdr); scratch_free(part_hdr);
ray_graph_free(g); ray_release(tbl);
return ray_error("domain", "window: function input must be a column name symbol");
}
ray_t* col_s = ray_sym_str(col_expr->i64);
if (!col_s) {
DICT_VIEW_CLOSE(fv); scratch_free(outname_hdr); scratch_free(funcs_hdr); scratch_free(order_hdr); scratch_free(part_hdr);
ray_graph_free(g); ray_release(tbl);
return ray_error("domain", "window: unknown input column");
}
func_inputs[fi] = ray_scan(g, ray_str_ptr(col_s));
}
DICT_VIEW_CLOSE(fv);
ray_op_t* win_op = ray_window_op(g, tbl_node,
n_part ? part_ops : NULL, (uint32_t)n_part,
n_order ? order_ops : NULL,
n_order ? order_descs : NULL, (uint32_t)n_order,
func_kinds, func_inputs, func_params,
(uint32_t)n_funcs,
RAY_FRAME_ROWS,
frame_start, frame_end,
frame_start_n, 0);
scratch_free(funcs_hdr); scratch_free(order_hdr); scratch_free(part_hdr);
if (!win_op) {
scratch_free(outname_hdr); ray_graph_free(g); ray_release(tbl);
return ray_error("oom", NULL);
}
win_op = ray_optimize(g, win_op);
ray_t* result = ray_execute(g, win_op);
ray_graph_free(g);
ray_release(tbl);
if (!result || RAY_IS_ERR(result)) { scratch_free(outname_hdr); return result; }
int64_t base_ncols = ray_table_ncols(result) - n_funcs;
for (int fi = 0; fi < n_funcs; fi++) {
if (out_name_ids[fi] >= 0)
ray_table_set_col_name(result, base_ncols + fi, out_name_ids[fi]);
}
scratch_free(outname_hdr);
return result;
}
typedef enum {
COUNT_CMP_EQ = 1,
COUNT_CMP_NE,
COUNT_CMP_LT,
COUNT_CMP_LE,
COUNT_CMP_GT,
COUNT_CMP_GE,
} count_cmp_op_t;
typedef struct {
const ray_t* col;
int64_t rhs;
count_cmp_op_t op;
int64_t* counts;
} count_compare_ctx_t;
static inline int64_t count_atom_i64(ray_t* a) {
if (a->type == -RAY_BOOL) return (int64_t)a->b8;
if (a->type == -RAY_U8) return (int64_t)a->u8;
if (a->type == -RAY_I16) return (int64_t)a->i16;
if (a->type == -RAY_I32 || a->type == -RAY_DATE || a->type == -RAY_TIME)
return (int64_t)a->i32;
if (a->type == -RAY_I64 || a->type == -RAY_TIMESTAMP || a->type == -RAY_SYM)
return a->i64;
return 0;
}
static inline int count_compare_i64(int64_t lhs, int64_t rhs, count_cmp_op_t op) {
switch (op) {
case COUNT_CMP_EQ: return lhs == rhs;
case COUNT_CMP_NE: return lhs != rhs;
case COUNT_CMP_LT: return lhs < rhs;
case COUNT_CMP_LE: return lhs <= rhs;
case COUNT_CMP_GT: return lhs > rhs;
case COUNT_CMP_GE: return lhs >= rhs;
default: return 0;
}
}
static count_cmp_op_t count_cmp_flip(count_cmp_op_t op) {
switch (op) {
case COUNT_CMP_LT: return COUNT_CMP_GT;
case COUNT_CMP_LE: return COUNT_CMP_GE;
case COUNT_CMP_GT: return COUNT_CMP_LT;
case COUNT_CMP_GE: return COUNT_CMP_LE;
default: return op;
}
}
static void count_compare_task(void* vctx, uint32_t worker_id, int64_t start, int64_t end) {
count_compare_ctx_t* ctx = (count_compare_ctx_t*)vctx;
const ray_t* col = ctx->col;
const void* data = ray_data((ray_t*)col);
int64_t rhs = ctx->rhs;
count_cmp_op_t op = ctx->op;
int64_t local = 0;
switch (col->type) {
case RAY_BOOL:
case RAY_U8: {
const uint8_t* x = (const uint8_t*)data;
if ((op == COUNT_CMP_EQ || op == COUNT_CMP_NE) && rhs >= 0 && rhs <= 255) {
uint8_t needle = (uint8_t)rhs;
if (op == COUNT_CMP_EQ) {
for (int64_t i = start; i < end; i++)
local += x[i] == needle;
} else {
for (int64_t i = start; i < end; i++)
local += x[i] != needle;
}
break;
}
for (int64_t i = start; i < end; i++)
local += count_compare_i64((int64_t)x[i], rhs, op);
break;
}
case RAY_I16: {
const int16_t* x = (const int16_t*)data;
for (int64_t i = start; i < end; i++) local += count_compare_i64((int64_t)x[i], rhs, op);
break;
}
case RAY_I32:
case RAY_DATE:
case RAY_TIME: {
const int32_t* x = (const int32_t*)data;
for (int64_t i = start; i < end; i++) local += count_compare_i64((int64_t)x[i], rhs, op);
break;
}
case RAY_I64:
case RAY_TIMESTAMP: {
const int64_t* x = (const int64_t*)data;
for (int64_t i = start; i < end; i++) local += count_compare_i64(x[i], rhs, op);
break;
}
case RAY_SYM: {
uint8_t esz = col_esz(col);
for (int64_t i = start; i < end; i++)
local += count_compare_i64(read_by_esz(data, i, esz), rhs, op);
break;
}
default:
break;
}
ctx->counts[worker_id] += local;
}
static int try_count_simple_compare(ray_t* tbl, ray_t* where_expr, int64_t* out_count) {
if (!tbl || !where_expr || where_expr->type != RAY_LIST || ray_len(where_expr) != 3)
return 0;
ray_t** we = (ray_t**)ray_data(where_expr);
if (!we[0] || we[0]->type != -RAY_SYM) return 0;
ray_t* op_name = ray_sym_str(we[0]->i64);
if (!op_name) return 0;
const char* op_s = ray_str_ptr(op_name);
size_t op_len = ray_str_len(op_name);
count_cmp_op_t op = 0;
if (op_len == 1) {
if (op_s[0] == '<') op = COUNT_CMP_LT;
else if (op_s[0] == '>') op = COUNT_CMP_GT;
} else if (op_len == 2) {
if (op_s[0] == '=' && op_s[1] == '=') op = COUNT_CMP_EQ;
else if (op_s[0] == '!' && op_s[1] == '=') op = COUNT_CMP_NE;
else if (op_s[0] == '<' && op_s[1] == '=') op = COUNT_CMP_LE;
else if (op_s[0] == '>' && op_s[1] == '=') op = COUNT_CMP_GE;
}
if (!op) return 0;
ray_t* col_expr = we[1];
ray_t* rhs_expr = we[2];
if (col_expr && ray_is_atom(col_expr) &&
!(col_expr->type == -RAY_SYM && !(col_expr->attrs & ATTR_QUOTED)) &&
rhs_expr && rhs_expr->type == -RAY_SYM && !(rhs_expr->attrs & ATTR_QUOTED)) {
col_expr = we[2];
rhs_expr = we[1];
op = count_cmp_flip(op);
}
if (!col_expr || col_expr->type != -RAY_SYM || (col_expr->attrs & ATTR_QUOTED))
return 0;
if (!rhs_expr || !ray_is_atom(rhs_expr) ||
(rhs_expr->type == -RAY_SYM && !(rhs_expr->attrs & ATTR_QUOTED)) ||
RAY_ATOM_IS_NULL(rhs_expr))
return 0;
if (rhs_expr->type == -RAY_SYM && (rhs_expr->attrs & ATTR_QUOTED) &&
ray_table_get_col(tbl, rhs_expr->i64))
return 0;
ray_t* col = ray_table_get_col(tbl, col_expr->i64);
if (!col || col->len != ray_table_nrows(tbl)) return 0;
if (ray_vec_may_have_nulls(col)) return 0;
switch (col->type) {
case RAY_BOOL:
case RAY_U8:
case RAY_I16:
case RAY_I32:
case RAY_I64:
case RAY_DATE:
case RAY_TIME:
case RAY_TIMESTAMP:
if (!is_numeric(rhs_expr) && !is_temporal(rhs_expr)) return 0;
break;
case RAY_SYM:
if (rhs_expr->type != -RAY_SYM) return 0;
break;
default:
return 0;
}
if (rhs_expr->type == -RAY_F64 || rhs_expr->type == -RAY_F32) return 0;
bool rhs_ns = false;
if (is_temporal(rhs_expr) && rhs_expr->type != -col->type) {
if (col->type != RAY_TIMESTAMP) return 0;
rhs_ns = true;
}
int64_t rhs = count_atom_i64(rhs_expr);
if (rhs_ns) {
int64_t unit = rhs_expr->type == -RAY_DATE ? 86400000000000LL : 1000000LL;
if (__builtin_mul_overflow((int64_t)rhs_expr->i32, unit, &rhs))
rhs = rhs_expr->i32 < 0 ? INT64_MIN + 1 : INT64_MAX;
}
if (col->type == RAY_SYM &&
ray_sym_vec_domain(col) != ray_sym_runtime_domain()) {
ray_t* s = ray_sym_str(rhs);
rhs = s ? ray_sym_vec_lookup(col, ray_str_ptr(s), ray_str_len(s)) : -1;
}
ray_pool_t* pool = ray_pool_get();
uint32_t nworkers = pool ? ray_pool_total_workers(pool) : 1;
ray_t* counts_block = ray_alloc((size_t)nworkers * sizeof(int64_t));
if (!counts_block) return 0;
int64_t* counts = (int64_t*)ray_data(counts_block);
memset(counts, 0, (size_t)nworkers * sizeof(int64_t));
count_compare_ctx_t ctx = {
.col = col,
.rhs = rhs,
.op = op,
.counts = counts,
};
int64_t nrows = col->len;
if (pool && nrows >= RAY_PARALLEL_THRESHOLD)
ray_pool_dispatch(pool, count_compare_task, &ctx, nrows);
else
count_compare_task(&ctx, 0, 0, nrows);
int64_t total = 0;
for (uint32_t i = 0; i < nworkers; i++) total += counts[i];
ray_release(counts_block);
*out_count = total;
return 1;
}
static ray_t* where_mask_spread(ray_t* m, int64_t tn, const char* label) {
if (!m || RAY_IS_ERR(m)) return m;
if (m->type == RAY_BOOL && m->len == tn) return m;
return ray_where_mask_coerce(m, tn, label);
}
ray_t* ray_try_count_select_expr(ray_t* expr, int* handled) {
if (handled) *handled = 0;
if (!expr || expr->type != RAY_LIST || ray_len(expr) != 2) return NULL;
ray_t** elems = (ray_t**)ray_data(expr);
if (!elems[0] || elems[0]->type != -RAY_SYM) return NULL;
ray_t* name = ray_sym_str(elems[0]->i64);
if (!name || ray_str_len(name) != 6 ||
memcmp(ray_str_ptr(name), "select", 6) != 0)
return NULL;
ray_t* dict = elems[1];
if (!dict || dict->type != RAY_DICT) return NULL;
ray_t* from_expr = dict_get(dict, "from");
ray_t* where_expr = dict_get(dict, "where");
if (!from_expr) return NULL;
if (where_expr && where_expr->type == RAY_LIST && ray_len(where_expr) >= 3) {
ray_t** we = (ray_t**)ray_data(where_expr);
if (we[0] && we[0]->type == -RAY_SYM) {
ray_t* wn = ray_sym_str(we[0]->i64);
if (wn && ray_str_len(wn) == 3 &&
memcmp(ray_str_ptr(wn), "and", 3) == 0)
return NULL;
}
}
int64_t from_id = dict_key_id(dict, "from");
int64_t where_id = dict_key_id(dict, "where");
int64_t by_id = dict_key_id(dict, "by");
int64_t take_id = dict_key_id(dict, "take");
int64_t asc_id = dict_key_id(dict, "asc");
int64_t desc_id = dict_key_id(dict, "desc");
int64_t nearest_id = dict_key_id(dict, "nearest");
DICT_VIEW_DECL(dv);
DICT_VIEW_OPEN(dict, dv);
if (DICT_VIEW_OVERFLOW(dv)) { DICT_VIEW_CLOSE(dv); return NULL; }
for (int64_t i = 0; i + 1 < dv_n; i += 2) {
int64_t kid = dv[i]->i64;
if (kid == by_id || kid == take_id || kid == asc_id ||
kid == desc_id || kid == nearest_id)
{ DICT_VIEW_CLOSE(dv); return NULL; }
if (kid != from_id && kid != where_id)
{ DICT_VIEW_CLOSE(dv); return NULL; }
}
DICT_VIEW_CLOSE(dv);
ray_t* tbl = ray_eval(from_expr);
if (!tbl || RAY_IS_ERR(tbl)) return tbl ? tbl : ray_error("type", "select count: `from:` evaluation failed");
if (tbl->type != RAY_TABLE) {
int8_t tbl_t = tbl->type;
ray_release(tbl);
return ray_error("type", "select count: `from:` must be a table, got %s", ray_type_name(tbl_t));
}
if (!where_expr) {
if (handled) *handled = 1;
int64_t nrows = ray_table_nrows(tbl);
ray_release(tbl);
return ray_i64(nrows);
}
int64_t direct_count = 0;
if (try_count_simple_compare(tbl, where_expr, &direct_count)) {
if (handled) *handled = 1;
ray_release(tbl);
return ray_i64(direct_count);
}
ray_graph_t* g = ray_graph_new(tbl);
if (!g) {
ray_release(tbl);
return ray_error("oom", NULL);
}
ray_op_t* pred = compile_expr_dag(g, where_expr);
if (!pred && g->compile_err) {
ray_t* cerr = graph_take_compile_err(g);
ray_graph_free(g);
ray_release(tbl);
return cerr;
}
if (!pred) {
ray_t* pred_vec = eval_where_mask(where_expr, tbl, "select count");
if (!pred_vec || RAY_IS_ERR(pred_vec)) {
ray_graph_free(g);
ray_release(tbl);
return pred_vec ? pred_vec :
ray_error("type", "select count: WHERE predicate evaluation failed");
}
if (handled) *handled = 1;
int64_t nrows = ray_table_nrows(tbl);
ray_t* sel = ray_rowsel_from_pred(pred_vec);
if (sel) {
ray_rowsel_t* sm = ray_rowsel_meta(sel);
nrows = sm ? sm->total_pass : 0;
ray_release(sel);
}
ray_release(pred_vec);
ray_graph_free(g);
ray_release(tbl);
return ray_i64(nrows);
}
int has_scan = 0;
ray_op_t* stk[64];
int sp = 0;
stk[sp++] = pred;
while (sp > 0) {
ray_op_t* cur = stk[--sp];
if (cur->opcode == OP_SCAN) { has_scan = 1; break; }
for (uint8_t a = 0; a < cur->arity && sp < 64; a++) {
ray_op_t* ch = op_child(g, cur, a);
if (ch) stk[sp++] = ch;
}
}
if (!has_scan) {
ray_graph_free(g);
ray_release(tbl);
return NULL;
}
int64_t pre_nrows = ray_table_nrows(tbl);
{
bool all_pass = false;
ray_t* fsel = exec_pred_to_selection(g, pred, pre_nrows, &all_pass);
if (all_pass || fsel) {
int64_t nrows = pre_nrows;
if (fsel) {
ray_rowsel_t* sm = ray_rowsel_meta(fsel);
nrows = sm ? sm->total_pass : 0;
ray_release(fsel);
}
if (handled) *handled = 1;
ray_graph_free(g);
ray_release(tbl);
return ray_i64(nrows);
}
}
ray_t* pred_vec = exec_node(g, pred);
if (!pred_vec || RAY_IS_ERR(pred_vec)) {
ray_graph_free(g);
ray_release(tbl);
return pred_vec ? pred_vec : ray_error("type", "select count: WHERE predicate evaluation failed");
}
int64_t tbl_nrows = ray_table_nrows(tbl);
pred_vec = where_mask_spread(pred_vec, tbl_nrows, "select count");
if (!pred_vec || RAY_IS_ERR(pred_vec)) {
ray_graph_free(g);
ray_release(tbl);
return pred_vec ? pred_vec : ray_error("oom", NULL);
}
if (pred_vec->type != RAY_BOOL || pred_vec->len != tbl_nrows) {
int8_t pred_t = pred_vec->type;
ray_release(pred_vec);
ray_graph_free(g);
ray_release(tbl);
return ray_error("type", "select count: WHERE must produce a bool mask matching row count, got %s", ray_type_name(pred_t));
}
if (handled) *handled = 1;
int64_t nrows = tbl_nrows;
ray_t* sel = ray_rowsel_from_pred(pred_vec);
if (sel) {
ray_rowsel_t* sm = ray_rowsel_meta(sel);
nrows = sm ? sm->total_pass : 0;
ray_release(sel);
}
ray_release(pred_vec);
ray_graph_free(g);
ray_release(tbl);
return ray_i64(nrows);
}
static int collect_col_refs_set(ray_t* expr, ray_t* tbl,
int64_t* out_syms, int max_out, int n) {
if (!expr || n >= max_out) return n;
if (expr->type == -RAY_SYM && !(expr->attrs & ATTR_QUOTED)) {
int64_t want = -1;
if (ray_table_get_col(tbl, expr->i64)) {
want = expr->i64;
} else if (ray_sym_is_dotted(expr->i64)) {
const int64_t* segs;
int nsegs = ray_sym_segs(expr->i64, &segs);
if (nsegs >= 1 && ray_table_get_col(tbl, segs[0])) want = segs[0];
}
if (want >= 0) {
for (int i = 0; i < n; i++) if (out_syms[i] == want) return n;
if (n < max_out) out_syms[n++] = want;
}
return n;
}
if (expr->type == RAY_LIST) {
ray_t** elems = (ray_t**)ray_data(expr);
int64_t cnt = ray_len(expr);
for (int64_t i = 0; i < cnt && n < max_out; i++)
n = collect_col_refs_set(elems[i], tbl, out_syms, max_out, n);
return n;
}
if (expr->type == RAY_DICT) {
DICT_VIEW_DECL(dv);
DICT_VIEW_OPEN(expr, dv);
if (DICT_VIEW_OVERFLOW(dv)) { DICT_VIEW_CLOSE(dv); return n; }
for (int64_t i = 0; i + 1 < dv_n && n < max_out; i += 2)
n = collect_col_refs_set(dv[i + 1], tbl, out_syms, max_out, n);
DICT_VIEW_CLOSE(dv);
return n;
}
if (expr->type == RAY_SYM) {
int64_t len = ray_len(expr);
for (int64_t i = 0; i < len && n < max_out; i++) {
int64_t s = sym_cell_runtime_id(expr, i);
if (ray_table_get_col(tbl, s)) {
int dup = 0;
for (int j = 0; j < n; j++) if (out_syms[j] == s) { dup = 1; break; }
if (!dup && n < max_out) out_syms[n++] = s;
}
}
return n;
}
return n;
}
static ray_t* project_table_cols(ray_t* src_tbl, const int64_t* keep_syms,
int n_keep) {
ray_t* nt = ray_table_new(n_keep);
if (!nt || RAY_IS_ERR(nt)) return nt ? nt : ray_error("oom", NULL);
for (int i = 0; i < n_keep; i++) {
ray_t* col = ray_table_get_col(src_tbl, keep_syms[i]);
if (!col) { ray_release(nt); return ray_error("domain", "select: projected column not found"); }
ray_t* nt2 = ray_table_add_col(nt, keep_syms[i], col);
if (!nt2 || RAY_IS_ERR(nt2)) {
if (nt2 && nt2 != nt) ray_release(nt2);
else ray_release(nt);
return nt2 ? nt2 : ray_error("oom", NULL);
}
nt = nt2;
}
return nt;
}
static int collect_syms(ray_t* e, int64_t* out, int max, int n) {
if (!e || n >= max) return n;
if (e->type == -RAY_SYM) {
if (e->attrs & ATTR_QUOTED) return n;
for (int i = 0; i < n; i++) if (out[i] == e->i64) return n;
out[n++] = e->i64;
return n;
}
if (e->type == RAY_LIST) {
ray_t** el = (ray_t**)ray_data(e);
int64_t len = ray_len(e);
for (int64_t i = 0; i < len && n < max; i++) n = collect_syms(el[i], out, max, n);
} else if (e->type == RAY_DICT) {
ray_t* vals = ray_dict_vals(e);
if (vals && vals->type == RAY_LIST) {
ray_t** v = (ray_t**)ray_data(vals);
int64_t len = ray_len(vals);
for (int64_t i = 0; i < len && n < max; i++) n = collect_syms(v[i], out, max, n);
}
}
return n;
}
static int64_t count_syms(ray_t* e) {
if (!e) return 0;
if (e->type == -RAY_SYM) return (e->attrs & ATTR_QUOTED) ? 0 : 1;
if (e->type == RAY_LIST) {
ray_t** el = (ray_t**)ray_data(e);
int64_t len = ray_len(e), c = 0;
for (int64_t i = 0; i < len; i++) c += count_syms(el[i]);
return c;
}
if (e->type == RAY_DICT) {
ray_t* vals = ray_dict_vals(e);
if (vals && vals->type == RAY_LIST) {
ray_t** v = (ray_t**)ray_data(vals);
int64_t len = ray_len(vals), c = 0;
for (int64_t i = 0; i < len; i++) c += count_syms(v[i]);
return c;
}
}
return 0;
}
static int64_t proj_keep_bound(ray_t* dict) {
ray_t* keys = ray_dict_keys(dict);
ray_t* vals = ray_dict_vals(dict);
if (!keys || keys->type != RAY_SYM || !vals || vals->type != RAY_LIST) return 1;
int64_t from_id = dict_key_id(dict, "from");
int64_t n = keys->len;
ray_t** vptrs = (ray_t**)ray_data(vals);
int64_t cnt = 0;
for (int64_t i = 0; i < n; i++) {
if (sym_cell_runtime_id(keys, i) == from_id) continue;
cnt += count_syms(vptrs[i]);
}
return cnt + 1;
}
static bool proj_compute_keep(ray_t* dict, int64_t* out, int max, int* out_n) {
int64_t from_id = dict_key_id(dict, "from");
int64_t where_id = dict_key_id(dict, "where");
int64_t by_id = dict_key_id(dict, "by");
int64_t take_id = dict_key_id(dict, "take");
int64_t asc_id = dict_key_id(dict, "asc");
int64_t desc_id = dict_key_id(dict, "desc");
int64_t nearest_id = dict_key_id(dict, "nearest");
DICT_VIEW_DECL(pv);
DICT_VIEW_OPEN(dict, pv);
if (DICT_VIEW_OVERFLOW(pv)) { DICT_VIEW_CLOSE(pv); return false; }
int n = 0;
bool has_value_expr = false;
for (int64_t i = 0; i + 1 < pv_n; i += 2) {
int64_t kid = pv[i]->i64;
if (kid == from_id) continue;
if (kid != where_id && kid != by_id && kid != take_id &&
kid != asc_id && kid != desc_id && kid != nearest_id)
has_value_expr = true;
n = collect_syms(pv[i + 1], out, max, n);
}
DICT_VIEW_CLOSE(pv);
if (!has_value_expr) return false;
if (n >= max) return false;
*out_n = n;
return true;
}
static int filt_compact_keep(ray_t* dict, ray_t* by_expr, ray_t* tbl,
int64_t from_id, int64_t where_id, int64_t by_id,
int64_t* out, int max) {
ray_vm_t* vm = __VM;
if (!vm || !vm->proj_active || vm->eval_depth != vm->proj_depth + 1)
return 0;
if (!by_expr || by_expr->type != RAY_SYM || ray_len(by_expr) <= 1)
return 0;
DICT_VIEW_DECL(pv);
DICT_VIEW_OPEN(dict, pv);
if (DICT_VIEW_OVERFLOW(pv)) { DICT_VIEW_CLOSE(pv); return 0; }
for (int64_t i = 0; i + 1 < pv_n; i += 2) {
int64_t kid = pv[i]->i64;
if (kid != from_id && kid != where_id && kid != by_id) { DICT_VIEW_CLOSE(pv); return 0; }
}
DICT_VIEW_CLOSE(pv);
int64_t nk = ray_len(by_expr);
const int64_t* key_syms = (const int64_t*)ray_data(by_expr);
for (int64_t k = 0; k < nk; k++) {
ray_t* kc = ray_table_get_col(tbl, key_syms[k]);
if (!kc) return 0;
switch (kc->type) {
case RAY_I64: case RAY_I32: case RAY_I16: case RAY_U8:
case RAY_BOOL: case RAY_DATE: case RAY_TIME:
case RAY_TIMESTAMP: case RAY_SYM: case RAY_STR: break;
default: return 0;
}
}
int64_t nc = ray_table_ncols(tbl);
for (int64_t c = 0; c < nc; c++) {
ray_t* cc = ray_table_get_col_idx(tbl, c);
if (!cc) return 0;
switch (cc->type) {
case RAY_I64: case RAY_I32: case RAY_I16: case RAY_U8:
case RAY_BOOL: case RAY_DATE: case RAY_TIME:
case RAY_TIMESTAMP: case RAY_SYM: case RAY_F64:
case RAY_GUID: case RAY_STR: case RAY_LIST: break;
default: return 0;
}
}
int n = 0;
for (int64_t k = 0; k < nk && n < max; k++) {
bool dup = false;
for (int j = 0; j < n; j++) if (out[j] == key_syms[k]) { dup = true; break; }
if (!dup) out[n++] = key_syms[k];
}
for (int j = 0; j < vm->proj_keep_n && n < max; j++) {
int64_t s = vm->proj_keep[j];
bool dup = false;
for (int q = 0; q < n; q++) if (out[q] == s) { dup = true; break; }
if (!dup) out[n++] = s;
}
return n;
}
static bool temporal_materialize_pure(ray_t* expr, ray_t* tbl) {
if (!expr) return false;
if (ray_is_atom(expr)) {
if (expr->type == -RAY_SYM && !(expr->attrs & ATTR_QUOTED)) {
ray_t* col = ray_table_get_col(tbl, expr->i64);
return col && !RAY_IS_PARTED(col->type) && col->type != RAY_MAPCOMMON;
}
return true;
}
if (expr->type != RAY_LIST || expr->len != 3) return false;
ray_t** es = ray_data(expr);
if (!es[0] || es[0]->type != -RAY_SYM) return false;
ray_t* name = ray_sym_str(es[0]->i64);
if (!name || ray_str_len(name) != 1) return false;
char c = ray_str_ptr(name)[0];
return (c == '+' || c == '-' || c == '*') &&
temporal_materialize_pure(es[1], tbl) && temporal_materialize_pure(es[2], tbl);
}
static ray_t* try_temporal_group_materialize(ray_t* dict, ray_t* tbl) {
if (!dict_get(dict, "by")) return NULL;
ray_t* vals = ray_dict_vals(dict);
ray_t* keys = ray_dict_keys(dict);
ray_t* rewritten = NULL;
ray_t* extended = NULL;
uint32_t seq = 0;
ray_t* replacement = NULL;
ray_t* failure = NULL;
#define MAT_CHECK(value) do { if (!(value) || RAY_IS_ERR(value)) { failure = (value); (value) = NULL; goto oom; } } while (0)
for (int64_t i = 0; i < ray_dict_len(dict); i++) {
ray_t* expr = ray_list_get(vals, i);
if (!expr || !is_group_dag_agg_expr(expr)) continue;
ray_t** es = ray_data(expr);
uint16_t kind = resolve_agg_opcode(es[0]->i64);
int argc = agg_is_binary_agg(kind) ? 2 : 1;
replacement = NULL;
for (int arg = 1; arg <= argc; arg++) {
if (arg >= expr->len || !expr_contains_temporal_arith(es[arg], tbl) ||
!temporal_materialize_pure(es[arg], tbl)) continue;
if (ray_env_push_query_scope() != RAY_OK) goto oom;
ray_t* previous = bind_all_columns(tbl);
ray_t* value = ray_eval(es[arg]);
g_active_query_table = previous;
ray_env_pop_scope();
if (value && !RAY_IS_ERR(value) && ray_is_lazy(value)) value = ray_lazy_materialize(value);
if (!value || RAY_IS_ERR(value)) {
if (replacement) ray_release(replacement);
if (rewritten) ray_release(rewritten);
if (extended) ray_release(extended);
return value ? value : ray_error("oom", NULL);
}
if (!ray_is_vec(value) || value->len != ray_table_nrows(tbl)) { ray_release(value); continue; }
if (!extended) { extended = tbl; ray_retain(extended); }
char name[48]; int64_t sym;
do {
int len = snprintf(name, sizeof(name), "_temporal_agg_%u", seq++);
sym = ray_sym_intern(name, (size_t)len);
} while (ray_table_get_col(extended, sym));
extended = ray_table_add_col(extended, sym, value); ray_release(value);
MAT_CHECK(extended);
if (!replacement) {
replacement = ray_list_new(expr->len);
MAT_CHECK(replacement);
for (int64_t j = 0; j < expr->len; j++) {
replacement = ray_list_append(replacement, es[j]); MAT_CHECK(replacement);
}
}
ray_t* ref = ray_sym(sym);
MAT_CHECK(ref);
replacement = ray_list_set(replacement, arg, ref); ray_release(ref);
MAT_CHECK(replacement);
}
if (replacement) {
if (!rewritten) { rewritten = dict; ray_retain(rewritten); }
int alloc = 0;
ray_t* key = collection_elem(keys, i, &alloc);
rewritten = ray_dict_upsert(rewritten, key, replacement);
if (alloc) ray_release(key);
ray_release(replacement); replacement = NULL;
MAT_CHECK(rewritten);
}
}
if (!rewritten) { if (extended) ray_release(extended); return NULL; }
ray_t* from = ray_sym(ray_sym_intern("from", 4));
rewritten = ray_dict_upsert(rewritten, from, extended);
ray_release(from); ray_release(extended); extended = NULL;
MAT_CHECK(rewritten);
ray_t* result = ray_select_impl(&rewritten, 1, true);
ray_release(rewritten);
return result;
oom:
if (replacement) ray_release(replacement);
if (rewritten) ray_release(rewritten);
if (extended) ray_release(extended);
return failure ? failure : ray_error("oom", NULL);
#undef MAT_CHECK
}
static ray_t* select_fallback_bind_alias(ray_t* tbl, int64_t kid, ray_t* col) {
int64_t ncols = ray_table_ncols(tbl);
for (int64_t c = 0; c < ncols; c++) {
if (ray_table_col_name(tbl, c) != kid) continue;
ray_t* t2 = ray_cow(tbl);
if (!t2 || RAY_IS_ERR(t2)) { ray_release(tbl); return t2; }
ray_table_set_col_idx(t2, c, col);
if (ray_table_get_col_idx(t2, c) != col) { ray_release(t2); return ray_error("oom", NULL); }
return t2;
}
return ray_table_add_col(tbl, kid, col);
}
static ray_t* select_fallback_broadcast(ray_t* v, int64_t n) {
if (!v || RAY_IS_ERR(v)) return v;
if (ray_is_atom(v)) {
ray_t* out = atom_broadcast_vec(v, n);
if (!out) out = ray_error("type", "select: cannot broadcast a %s value to a column",
ray_type_name(v->type));
ray_release(v);
return out;
}
if (v->type <= 0 || !ray_is_vec(v) || v->len != 1 || n == 1) return v;
ray_t* n_obj = make_i64(n);
ray_t* wide = n_obj ? ray_take_fn(v, n_obj) : ray_error("oom", NULL);
if (n_obj) ray_release(n_obj);
ray_release(v);
return wide;
}
static bool select_alias_skip_form(ray_t* head) {
if (!head || head->type != -RAY_SYM || (head->attrs & ATTR_QUOTED)) return false;
ray_t* s = ray_sym_str(head->i64);
if (!s) return false;
const char* p = ray_str_ptr(s);
size_t l = ray_str_len(s);
return (l == 2 && memcmp(p, "fn", 2) == 0) ||
(l == 3 && memcmp(p, "let", 3) == 0) ||
(l == 5 && memcmp(p, "quote", 5) == 0);
}
static int select_fallback_has_agg(ray_t* expr) {
if (!expr || expr->type != RAY_LIST) return 0;
int64_t n = ray_len(expr);
if (n == 0) return 0;
ray_t** el = (ray_t**)ray_data(expr);
if (select_alias_skip_form(el[0])) return 0;
if (is_agg_expr(expr)) return 1;
for (int64_t i = 0; i < n; i++)
if (select_fallback_has_agg(el[i])) return 1;
return 0;
}
static int select_fallback_scalar_leaves(ray_t* expr, const int64_t* scalars,
int64_t ns, int* via) {
if (!expr) return 0;
if (ray_is_atom(expr)) {
if (expr->type != -RAY_SYM || (expr->attrs & ATTR_QUOTED)) return 1;
for (int64_t i = 0; i < ns; i++)
if (scalars[i] == expr->i64) { *via = 1; return 1; }
return 0;
}
if (expr->type != RAY_LIST) return 0;
int64_t n = ray_len(expr);
if (n == 0) return 0;
ray_t** el = (ray_t**)ray_data(expr);
if (el[0]->type != -RAY_SYM || select_alias_skip_form(el[0])) return 0;
for (int64_t i = 1; i < n; i++)
if (!select_fallback_scalar_leaves(el[i], scalars, ns, via)) return 0;
return 1;
}
static int select_fallback_scalar_proj(ray_t* expr, const int64_t* scalars, int64_t ns) {
int via = 0;
return select_fallback_scalar_leaves(expr, scalars, ns, &via) &&
(via || ray_is_atom(expr));
}
static bool select_alias_head_is_agg(ray_t* head) {
if (!head || head->type != -RAY_SYM || (head->attrs & ATTR_QUOTED)) return false;
if (resolve_agg_opcode(head->i64) != 0) return true;
ray_t* s = ray_sym_str(head->i64);
return s && ray_str_len(s) == 8 && memcmp(ray_str_ptr(s), "distinct", 8) == 0;
}
static ray_t* grouped_output_nested_agg(ray_t* expr, bool inside_agg) {
if (!expr || expr->type != RAY_LIST || expr->len < 1) return NULL;
ray_t** el = (ray_t**)ray_data(expr);
if (select_alias_skip_form(el[0])) return NULL;
bool agg = is_agg_expr(expr) != 0;
if (agg && inside_agg) return expr;
for (int64_t i = 1; i < expr->len; i++) {
ray_t* hit = grouped_output_nested_agg(el[i], inside_agg || agg);
if (hit) return hit;
}
return NULL;
}
enum { SEL_ALIAS_ROW = 0, SEL_ALIAS_GROUP = 1 };
#define SEL_ALIAS_MAX_NODES 65536
typedef struct {
int64_t n_derived;
int64_t* derived_names;
ray_t** derived_exprs;
int64_t n_hidden;
int64_t* hidden_names;
bool sort_after;
ray_t* hdr;
} select_alias_plan_t;
static void select_alias_plan_free(select_alias_plan_t* p) {
for (int64_t i = 0; i < p->n_derived; i++)
if (p->derived_exprs[i]) ray_release(p->derived_exprs[i]);
if (p->hdr) scratch_free(p->hdr);
memset(p, 0, sizeof(*p));
}
static bool select_sym_is(ray_t* s, const char* name, size_t len) {
if (!s || s->type != -RAY_SYM || (s->attrs & ATTR_QUOTED)) return false;
ray_t* str = ray_sym_str(s->i64);
return str && ray_str_len(str) == len && memcmp(ray_str_ptr(str), name, len) == 0;
}
static bool select_lambda_form(ray_t* expr) {
if (!expr || expr->type != RAY_LIST || expr->len < 3) return false;
ray_t** el = (ray_t**)ray_data(expr);
return select_sym_is(el[0], "fn", 2) && ray_is_vec(el[1]) && el[1]->type == RAY_SYM;
}
static int64_t count_agg_calls(ray_t* expr) {
if (!expr || expr->type != RAY_LIST) return 0;
int64_t c = is_agg_expr(expr) ? 1 : 0;
ray_t** e = (ray_t**)ray_data(expr);
for (int64_t i = 0; i < expr->len; i++) c += count_agg_calls(e[i]);
return c;
}
static bool select_expr_reads_col(ray_t* expr, ray_t* tbl, const int64_t* shadow, int n_shadow) {
if (!expr) return false;
if (expr->type == -RAY_SYM) {
for (int i = 0; i < n_shadow; i++) if (shadow[i] == expr->i64) return false;
return ray_table_get_col(tbl, expr->i64) != NULL;
}
if (expr->type != RAY_LIST || expr->len < 1) return false;
ray_t** el = (ray_t**)ray_data(expr);
if (select_sym_is(el[0], "quote", 5)) return false;
if (select_lambda_form(expr)) {
int64_t nf = ray_len(el[1]);
ray_t* hdr = NULL;
int64_t* merged = (int64_t*)scratch_alloc(&hdr, (size_t)(n_shadow + nf + 1) * sizeof(int64_t));
if (!merged) return true;
for (int i = 0; i < n_shadow; i++) merged[i] = shadow[i];
for (int64_t i = 0; i < nf; i++) merged[n_shadow + i] = sym_cell_runtime_id(el[1], i);
bool r = false;
for (int64_t i = 2; i < expr->len && !r; i++)
r = select_expr_reads_col(el[i], tbl, merged, n_shadow + (int)nf);
scratch_free(hdr);
return r;
}
for (int64_t i = 0; i < expr->len; i++)
if (select_expr_reads_col(el[i], tbl, shadow, n_shadow)) return true;
return false;
}
static int64_t expr_node_count(ray_t* expr, int64_t cap) {
if (!expr) return 0;
if (expr->type != RAY_LIST) return 1;
int64_t n = 1;
ray_t** el = (ray_t**)ray_data(expr);
for (int64_t i = 0; i < expr->len && n < cap; i++)
n += expr_node_count(el[i], cap - n);
return n;
}
typedef ray_t* (*select_alias_map_fn)(ray_t* e, void* ctx);
static ray_t* select_alias_map_list(ray_t* expr, int64_t from, select_alias_map_fn fn, void* ctx) {
ray_t** el = (ray_t**)ray_data(expr);
ray_t* out = NULL;
for (int64_t i = from; i < expr->len; i++) {
ray_t* e2 = fn(el[i], ctx);
if (!e2 || RAY_IS_ERR(e2)) { if (out) ray_release(out); return e2 ? e2 : ray_error("oom", NULL); }
if (e2 != el[i] && !out) {
out = ray_list_new(expr->len);
if (!out || RAY_IS_ERR(out)) { ray_release(e2); return out ? out : ray_error("oom", NULL); }
for (int64_t j = 0; j < i; j++) {
out = ray_list_append(out, el[j]);
if (!out || RAY_IS_ERR(out)) { ray_release(e2); return out ? out : ray_error("oom", NULL); }
}
}
if (out) {
out = ray_list_append(out, e2);
ray_release(e2);
if (!out || RAY_IS_ERR(out)) return out ? out : ray_error("oom", NULL);
} else {
ray_release(e2);
}
}
if (!out) { ray_retain(expr); return expr; }
return out;
}
typedef struct { ray_t* tbl; } select_fallback_prereduce_ctx_t;
static ray_t* select_fallback_prereduce(ray_t* expr, void* vctx) {
select_fallback_prereduce_ctx_t* c = (select_fallback_prereduce_ctx_t*)vctx;
if (!expr || expr->type != RAY_LIST || expr->len < 1) { ray_retain(expr); return expr; }
ray_t** el = (ray_t**)ray_data(expr);
if (select_alias_skip_form(el[0])) { ray_retain(expr); return expr; }
if (!is_agg_expr(expr))
return select_alias_map_list(expr, el[0]->type == RAY_LIST ? 0 : 1,
select_fallback_prereduce, c);
ray_t* v = eval_expr_whole_column(expr, c->tbl);
if (!v || RAY_IS_ERR(v)) return v ? v : ray_error("oom", NULL);
if (v->type > 0 && v->type != RAY_LIST && ray_is_vec(v) && v->len == 1) {
int allocated = 0;
ray_t* a = collection_elem(v, 0, &allocated);
if (a && !RAY_IS_ERR(a) && !allocated) ray_retain(a);
ray_release(v);
if (!a || RAY_IS_ERR(a)) return a ? a : ray_error("oom", NULL);
v = a;
}
if (v->type == -RAY_SYM) {
ray_t* q = ray_sym(v->i64);
ray_release(v);
if (!q || RAY_IS_ERR(q)) return q ? q : ray_error("oom", NULL);
q->attrs |= ATTR_QUOTED;
v = q;
} else if (v->type != RAY_LIST) {
return v;
}
ray_t* head = ray_sym(ray_sym_intern("quote", 5));
ray_t* wrap = head && !RAY_IS_ERR(head) ? ray_list_new(2) : NULL;
if (wrap && !RAY_IS_ERR(wrap)) wrap = ray_list_append(wrap, head);
if (wrap && !RAY_IS_ERR(wrap)) wrap = ray_list_append(wrap, v);
if (head && !RAY_IS_ERR(head)) ray_release(head);
ray_release(v);
if (!wrap || RAY_IS_ERR(wrap)) return wrap ? wrap : ray_error("oom", NULL);
return wrap;
}
typedef struct {
const int64_t* names;
ray_t** exprs;
const uint8_t* kinds;
int n_alias;
ray_t* tbl;
bool in_agg;
const int64_t* shadow;
int n_shadow;
bool changed;
bool refs_group;
bool refs_row;
const int64_t* keys;
int n_keys;
} select_alias_subst_ctx_t;
static bool select_sym_in(int64_t sym, const int64_t* syms, int n) {
for (int i = 0; i < n; i++) if (syms[i] == sym) return true;
return false;
}
static ray_t* select_alias_subst(ray_t* expr, void* vctx) {
select_alias_subst_ctx_t* c = (select_alias_subst_ctx_t*)vctx;
if (!expr) return NULL;
if (expr->type == -RAY_SYM) {
for (int i = 0; i < c->n_shadow; i++)
if (c->shadow[i] == expr->i64) { ray_retain(expr); return expr; }
if (c->in_agg && ray_table_get_col(c->tbl, expr->i64)) { ray_retain(expr); return expr; }
for (int i = c->n_alias - 1; i >= 0; i--) {
if (c->names[i] != expr->i64) continue;
if (c->kinds[i] == SEL_ALIAS_GROUP) {
if (c->in_agg) {
ray_t* s = ray_sym_str(expr->i64);
return ray_error("domain",
"select by: `%.*s` is an aggregate of the group and cannot be aggregated again",
s ? (int)ray_str_len(s) : 1, s ? ray_str_ptr(s) : "?");
}
c->refs_group = true;
ray_retain(expr); return expr;
}
c->changed = true;
if (!c->in_agg && select_expr_reads_col(c->exprs[i], c->tbl, c->keys, c->n_keys)) c->refs_row = true;
ray_retain(c->exprs[i]);
return c->exprs[i];
}
if (!c->in_agg && !select_sym_in(expr->i64, c->keys, c->n_keys) &&
ray_table_get_col(c->tbl, expr->i64)) c->refs_row = true;
ray_retain(expr); return expr;
}
if (expr->type != RAY_LIST || expr->len < 1) { ray_retain(expr); return expr; }
ray_t** el = (ray_t**)ray_data(expr);
if (select_sym_is(el[0], "let", 3) || select_sym_is(el[0], "quote", 5)) { ray_retain(expr); return expr; }
if (select_lambda_form(expr)) {
int64_t nf = ray_len(el[1]);
ray_t* sh_hdr = NULL;
int64_t* merged = (int64_t*)scratch_alloc(&sh_hdr, (size_t)(c->n_shadow + nf + 1) * sizeof(int64_t));
if (!merged) return ray_error("oom", NULL);
for (int i = 0; i < c->n_shadow; i++) merged[i] = c->shadow[i];
for (int64_t i = 0; i < nf; i++) merged[c->n_shadow + i] = sym_cell_runtime_id(el[1], i);
select_alias_subst_ctx_t inner = *c;
inner.shadow = merged; inner.n_shadow = c->n_shadow + (int)nf;
ray_t* out = select_alias_map_list(expr, 2, select_alias_subst, &inner);
scratch_free(sh_hdr);
c->changed |= inner.changed; c->refs_group |= inner.refs_group; c->refs_row |= inner.refs_row;
return out;
}
select_alias_subst_ctx_t inner = *c;
inner.in_agg = c->in_agg || select_alias_head_is_agg(el[0]);
ray_t* out = select_alias_map_list(expr, el[0]->type == RAY_LIST ? 0 : 1,
select_alias_subst, &inner);
c->changed |= inner.changed; c->refs_group |= inner.refs_group; c->refs_row |= inner.refs_row;
return out;
}
typedef struct {
ray_t** engine;
select_alias_plan_t* plan;
const int64_t* shadow;
int n_shadow;
} select_extract_ctx_t;
static bool select_expr_mentions(ray_t* expr, const int64_t* syms, int n) {
if (!expr) return false;
if (expr->type == -RAY_SYM) {
for (int i = 0; i < n; i++) if (syms[i] == expr->i64) return true;
return false;
}
if (expr->type != RAY_LIST) return false;
ray_t** el = (ray_t**)ray_data(expr);
for (int64_t i = 0; i < expr->len; i++)
if (select_expr_mentions(el[i], syms, n)) return true;
return false;
}
static ray_t* select_extract_aggs(ray_t* expr, void* vctx) {
select_extract_ctx_t* c = (select_extract_ctx_t*)vctx;
if (!expr || expr->type != RAY_LIST || expr->len < 1) { ray_retain(expr); return expr; }
ray_t** el = (ray_t**)ray_data(expr);
if (select_sym_is(el[0], "let", 3) || select_sym_is(el[0], "quote", 5)) { ray_retain(expr); return expr; }
if (select_lambda_form(expr)) {
int64_t nf = ray_len(el[1]);
ray_t* hdr = NULL;
int64_t* merged = (int64_t*)scratch_alloc(&hdr, (size_t)(c->n_shadow + nf + 1) * sizeof(int64_t));
if (!merged) return ray_error("oom", NULL);
for (int i = 0; i < c->n_shadow; i++) merged[i] = c->shadow[i];
for (int64_t i = 0; i < nf; i++) merged[c->n_shadow + i] = sym_cell_runtime_id(el[1], i);
select_extract_ctx_t inner = *c;
inner.shadow = merged; inner.n_shadow = c->n_shadow + (int)nf;
ray_t* out = select_alias_map_list(expr, 2, select_extract_aggs, &inner);
scratch_free(hdr);
return out;
}
if (is_agg_expr(expr) && !select_expr_mentions(expr, c->shadow, c->n_shadow)) {
char buf[24];
int bn = snprintf(buf, sizeof buf, "__ad%lld", (long long)c->plan->n_hidden);
int64_t hn = ray_sym_intern(buf, (size_t)bn);
ray_t* key = ray_sym(hn);
if (!key || RAY_IS_ERR(key)) return key ? key : ray_error("oom", NULL);
*c->engine = ray_dict_upsert(*c->engine, key, expr);
ray_release(key);
if (!*c->engine || RAY_IS_ERR(*c->engine)) { ray_t* e = *c->engine; *c->engine = NULL; return e ? e : ray_error("oom", NULL); }
c->plan->hidden_names[c->plan->n_hidden++] = hn;
return ray_sym(hn);
}
return select_alias_map_list(expr, el[0]->type == RAY_LIST ? 0 : 1, select_extract_aggs, c);
}
static bool select_expr_mentions_key(ray_t* expr, ray_t* keys, int64_t n) {
if (!expr) return false;
if (expr->type == -RAY_SYM) {
for (int64_t i = 0; i < n; i++) if (sym_cell_runtime_id(keys, i) == expr->i64) return true;
return false;
}
if (expr->type != RAY_LIST) return false;
ray_t** el = (ray_t**)ray_data(expr);
for (int64_t i = 0; i < expr->len; i++)
if (select_expr_mentions_key(el[i], keys, n)) return true;
return false;
}
static ray_t* select_plan_grouped_aliases(ray_t* dict, ray_t* tbl, select_alias_plan_t* plan) {
memset(plan, 0, sizeof(*plan));
if (!dict || dict->type != RAY_DICT || !dict_get(dict, "by")) return NULL;
ray_t* keys = ray_dict_keys(dict);
ray_t* vals = ray_dict_vals(dict);
if (!keys || keys->type != RAY_SYM || !vals || vals->type != RAY_LIST) return NULL;
int64_t nd = ray_dict_len(dict);
{
bool any = false;
for (int64_t i = 1; i < nd && !any; i++)
any = select_expr_mentions_key(((ray_t**)ray_data(vals))[i], keys, i);
if (!any) return NULL;
}
static const char* const reserved[] = { "from", "where", "by", "take", "asc", "desc", "nearest" };
int64_t rid[7];
for (int i = 0; i < 7; i++) rid[i] = ray_sym_intern(reserved[i], strlen(reserved[i]));
int64_t hidden_max = 1;
for (int64_t i = 0; i < nd; i++)
hidden_max += count_agg_calls(((ray_t**)ray_data(vals))[i]);
ray_t* work_hdr = NULL;
int64_t* names = (int64_t*)scratch_alloc(&work_hdr,
(size_t)(nd > 0 ? nd : 1) * (sizeof(int64_t) + sizeof(ray_t*) + sizeof(uint8_t)));
if (!names) return ray_error("oom", NULL);
ray_t** exprs = (ray_t**)(names + nd);
uint8_t* kinds = (uint8_t*)(exprs + nd);
plan->derived_names = (int64_t*)scratch_alloc(&plan->hdr,
(size_t)(nd > 0 ? nd : 1) * (sizeof(int64_t) + sizeof(ray_t*)) + (size_t)hidden_max * sizeof(int64_t));
if (!plan->derived_names) { scratch_free(work_hdr); return ray_error("oom", NULL); }
plan->derived_exprs = (ray_t**)(plan->derived_names + nd);
plan->hidden_names = (int64_t*)(plan->derived_exprs + nd);
ray_t* by_expr = dict_get(dict, "by");
ray_t* keys_hdr = NULL;
int64_t* key_syms = NULL;
int n_key_syms = 0;
{
int64_t nk = 0;
if (by_expr && by_expr->type == -RAY_SYM) nk = 1;
else if (by_expr && ray_is_vec(by_expr) && by_expr->type == RAY_SYM) nk = ray_len(by_expr);
else if (by_expr && by_expr->type == RAY_DICT) nk = ray_dict_len(by_expr);
key_syms = (int64_t*)scratch_alloc(&keys_hdr, (size_t)(nk > 0 ? nk : 1) * sizeof(int64_t));
if (!key_syms) { scratch_free(work_hdr); select_alias_plan_free(plan); return ray_error("oom", NULL); }
if (by_expr && by_expr->type == -RAY_SYM) key_syms[n_key_syms++] = by_expr->i64;
else if (by_expr && ray_is_vec(by_expr) && by_expr->type == RAY_SYM)
for (int64_t k = 0; k < nk; k++) key_syms[n_key_syms++] = sym_cell_runtime_id(by_expr, k);
else if (by_expr && by_expr->type == RAY_DICT) {
ray_t* bk = ray_dict_keys(by_expr);
if (bk && bk->type == RAY_SYM)
for (int64_t k = 0; k < nk; k++) key_syms[n_key_syms++] = sym_cell_runtime_id(bk, k);
}
}
int n_alias = 0;
ray_t* engine = NULL;
ray_t* err = NULL;
for (int64_t i = 0; i < nd && !err; i++) {
int64_t kid = sym_cell_runtime_id(keys, i);
bool is_reserved = false;
for (int r = 0; r < 7; r++) if (rid[r] == kid) { is_reserved = true; break; }
if (is_reserved) continue;
ray_t* v = ((ray_t**)ray_data(vals))[i];
select_alias_subst_ctx_t sc = { names, exprs, kinds, n_alias, tbl, false, NULL, 0, false, false, false, key_syms, n_key_syms };
ray_t* v1 = select_alias_subst(v, &sc);
if (!v1 || RAY_IS_ERR(v1)) { err = v1 ? v1 : ray_error("oom", NULL); break; }
if (sc.refs_group && sc.refs_row) {
ray_t* s = ray_sym_str(kid);
ray_release(v1);
err = ray_error("domain", "select by: output `%.*s` combines a source column with a per-group value; aggregate the column",
s ? (int)ray_str_len(s) : 1, s ? ray_str_ptr(s) : "?");
break;
}
if (sc.changed && expr_node_count(v1, SEL_ALIAS_MAX_NODES) >= SEL_ALIAS_MAX_NODES) {
ray_t* s = ray_sym_str(kid);
ray_release(v1);
err = ray_error("limit", "select by: output `%.*s` grows too large once its aliases are expanded",
s ? (int)ray_str_len(s) : 1, s ? ray_str_ptr(s) : "?");
break;
}
ray_t* key = ray_sym(kid);
if (!key || RAY_IS_ERR(key)) { ray_release(v1); err = key ? key : ray_error("oom", NULL); break; }
if (sc.refs_group) {
if (!engine) { engine = dict; ray_retain(engine); }
engine = ray_dict_remove(engine, key);
if (!engine || RAY_IS_ERR(engine)) { ray_release(key); ray_release(v1); err = engine ? engine : ray_error("oom", NULL); engine = NULL; break; }
select_extract_ctx_t xc = { &engine, plan, NULL, 0 };
ray_t* v2 = select_extract_aggs(v1, &xc);
ray_release(v1);
if (!v2 || RAY_IS_ERR(v2)) { ray_release(key); err = v2 ? v2 : ray_error("oom", NULL); break; }
plan->derived_names[plan->n_derived] = kid;
plan->derived_exprs[plan->n_derived] = v2;
plan->n_derived++;
names[n_alias] = kid; exprs[n_alias] = v2; kinds[n_alias] = SEL_ALIAS_GROUP;
n_alias++;
ray_release(key);
continue;
}
if (sc.changed) {
if (!engine) { engine = dict; ray_retain(engine); }
engine = ray_dict_upsert(engine, key, v1);
if (!engine || RAY_IS_ERR(engine)) { ray_release(key); ray_release(v1); err = engine ? engine : ray_error("oom", NULL); engine = NULL; break; }
}
ray_release(key);
names[n_alias] = kid;
exprs[n_alias] = v1;
kinds[n_alias] = expr_contains_agg(v1) ? SEL_ALIAS_GROUP : SEL_ALIAS_ROW;
n_alias++;
ray_release(v1);
}
scratch_free(work_hdr);
scratch_free(keys_hdr);
if (err) { if (engine) ray_release(engine); select_alias_plan_free(plan); return err; }
if (!engine) { select_alias_plan_free(plan); return NULL; }
if (plan->n_derived > 0) {
for (int r = 3; r <= 5; r++) {
ray_t* key = ray_sym(rid[r]);
if (!key || RAY_IS_ERR(key)) { ray_release(engine); select_alias_plan_free(plan); return key ? key : ray_error("oom", NULL); }
if (dict_get(dict, reserved[r])) plan->sort_after = true;
engine = ray_dict_remove(engine, key);
ray_release(key);
if (!engine || RAY_IS_ERR(engine)) { select_alias_plan_free(plan); return engine ? engine : ray_error("oom", NULL); }
}
}
ray_t* from_key = ray_sym(rid[0]);
if (!from_key || RAY_IS_ERR(from_key)) { ray_release(engine); select_alias_plan_free(plan); return from_key ? from_key : ray_error("oom", NULL); }
engine = ray_dict_upsert(engine, from_key, tbl);
ray_release(from_key);
if (!engine || RAY_IS_ERR(engine)) { select_alias_plan_free(plan); return engine ? engine : ray_error("oom", NULL); }
return engine;
}
static bool select_derived_needs_rows(ray_t* expr) {
if (!expr || expr->type != RAY_LIST || expr->len < 1) return false;
ray_t** el = (ray_t**)ray_data(expr);
if (el[0]->type != -RAY_SYM || (el[0]->attrs & ATTR_QUOTED)) return true;
if (agg_arith_head_is_control(el[0]->i64) || select_sym_is(el[0], "cond", 4)) return true;
ray_t* gv = ray_env_get(el[0]->i64);
if (gv && gv->type == RAY_LAMBDA) return true;
for (int64_t i = 1; i < expr->len; i++)
if (select_derived_needs_rows(el[i])) return true;
return false;
}
static ray_t* select_apply_derived(ray_t* result, ray_t* dict, select_alias_plan_t* plan) {
if (!result || RAY_IS_ERR(result)) return result;
if (ray_is_lazy(result)) result = ray_lazy_materialize(result);
if (!result || RAY_IS_ERR(result)) return result;
if (result->type != RAY_TABLE) return result;
int64_t n_groups = ray_table_nrows(result);
for (int64_t d = 0; d < plan->n_derived; d++) {
ray_t* expr = plan->derived_exprs[d];
ray_t* col = select_derived_needs_rows(expr)
? eval_expr_per_row(expr, result, n_groups)
: eval_expr_whole_column(expr, result);
if (!col || RAY_IS_ERR(col)) { ray_release(result); return col ? col : ray_error("oom", NULL); }
if (ray_is_lazy(col)) col = ray_lazy_materialize(col);
if (!col || RAY_IS_ERR(col)) { ray_release(result); return col ? col : ray_error("oom", NULL); }
if (!(ray_is_vec(col) || col->type == RAY_LIST) || ray_len(col) != n_groups) {
ray_t* nm = ray_sym_str(plan->derived_names[d]);
ray_release(col); ray_release(result);
return ray_error("domain", "select by: output `%.*s` did not evaluate to one value per group",
nm ? (int)ray_str_len(nm) : 1, nm ? ray_str_ptr(nm) : "?");
}
result = select_fallback_bind_alias(result, plan->derived_names[d], col);
ray_release(col);
if (!result || RAY_IS_ERR(result)) return result ? result : ray_error("oom", NULL);
}
DICT_VIEW_DECL(dv);
DICT_VIEW_OPEN(dict, dv);
if (DICT_VIEW_OVERFLOW(dv)) { DICT_VIEW_CLOSE(dv); ray_release(result); return ray_error("oom", NULL); }
int64_t from_id = ray_sym_intern("from", 4), where_id = ray_sym_intern("where", 5), by_id = ray_sym_intern("by", 2);
int64_t take_id = ray_sym_intern("take", 4), asc_id = ray_sym_intern("asc", 3), desc_id = ray_sym_intern("desc", 4);
int64_t nearest_id = ray_sym_intern("nearest", 7);
int64_t ncols = ray_table_ncols(result);
ray_t* out = ray_table_new(ncols);
if (!out || RAY_IS_ERR(out)) { DICT_VIEW_CLOSE(dv); ray_release(result); return out ? out : ray_error("oom", NULL); }
for (int64_t c = 0; c < ncols && out && !RAY_IS_ERR(out); c++) {
int64_t cn = ray_table_col_name(result, c);
bool is_output = false, is_hidden = false;
for (int64_t i = 0; i + 1 < dv_n; i += 2)
if (dv[i]->i64 == cn && cn != from_id && cn != where_id && cn != by_id && cn != take_id && cn != asc_id && cn != desc_id && cn != nearest_id) { is_output = true; break; }
for (int64_t h = 0; h < plan->n_hidden; h++) if (plan->hidden_names[h] == cn) { is_hidden = true; break; }
if (is_output || is_hidden) continue;
out = ray_table_add_col(out, cn, ray_table_get_col_idx(result, c));
}
for (int64_t i = 0; i + 1 < dv_n && out && !RAY_IS_ERR(out); i += 2) {
int64_t cn = dv[i]->i64;
if (cn == from_id || cn == where_id || cn == by_id || cn == take_id || cn == asc_id || cn == desc_id || cn == nearest_id) continue;
ray_t* col = ray_table_get_col(result, cn);
if (!col) continue;
out = ray_table_add_col(out, cn, col);
}
if (!out || RAY_IS_ERR(out)) { DICT_VIEW_CLOSE(dv); ray_release(result); return out ? out : ray_error("oom", NULL); }
ray_release(result);
result = out;
if (plan->sort_after)
result = apply_sort_take(result, dv, dv_n, asc_id, desc_id, take_id, NULL);
DICT_VIEW_CLOSE(dv);
return result;
}
ray_t* ray_select(ray_t** args, int64_t n) {
return ray_select_impl(args, n, false);
}
#define SELECT_COMPUTED_KEY "__by_key"
static ray_t* select_plan_computed_key(ray_t* dict, ray_t* tbl, int64_t* key_name) {
ray_t* by_expr = dict_get(dict, "by");
if (!by_expr || by_expr->type != RAY_LIST || ray_len(by_expr) < 1) return NULL;
ray_t* keys = ray_dict_keys(dict);
ray_t* vals = ray_dict_vals(dict);
if (!keys || keys->type != RAY_SYM || !vals || vals->type != RAY_LIST) return NULL;
static const char* const reserved[] = { "from", "where", "by", "take", "asc", "desc", "nearest" };
int64_t rid[7];
for (int i = 0; i < 7; i++) rid[i] = ray_sym_intern(reserved[i], strlen(reserved[i]));
int64_t nd = ray_dict_len(dict);
int64_t hidden = ray_sym_intern(SELECT_COMPUTED_KEY, strlen(SELECT_COMPUTED_KEY));
int64_t kname = derived_key_name(by_expr);
bool needs_eval = false;
for (int64_t i = 0; i < nd; i++) {
int64_t kid = sym_cell_runtime_id(keys, i);
bool clause = false;
for (int r = 0; r < 7; r++) if (kid == rid[r]) clause = true;
if (clause) continue;
if (kid == kname) kname = ray_sym_intern("key", 3);
ray_t* v = ((ray_t**)ray_data(vals))[i];
if (is_single_group_key_projection(by_expr, v)) continue;
if (is_group_dag_agg_expr(v)) continue;
if (is_decomposable_agg_compound(v, tbl)) continue;
needs_eval = true;
}
if (!needs_eval || ray_table_get_col(tbl, hidden)) return NULL;
ray_t* bk = ray_vec_new(RAY_SYM, 1);
ray_t* bv = ray_list_new(1);
if (bk && !RAY_IS_ERR(bk)) { ((int64_t*)ray_data(bk))[0] = hidden; bk->len = 1; }
if (bv && !RAY_IS_ERR(bv)) bv = ray_list_append(bv, by_expr);
ray_t* by_dict = ray_dict_new(bk, bv);
if (!by_dict || RAY_IS_ERR(by_dict)) return by_dict ? by_dict : ray_error("oom", NULL);
ray_t* engine = dict;
ray_retain(engine);
ray_t* k_by = ray_sym(rid[2]);
engine = k_by && !RAY_IS_ERR(k_by) ? ray_dict_upsert(engine, k_by, by_dict) : engine;
if (k_by) ray_release(k_by);
ray_release(by_dict);
ray_t* k_from = ray_sym(rid[0]);
if (engine && !RAY_IS_ERR(engine) && k_from && !RAY_IS_ERR(k_from))
engine = ray_dict_upsert(engine, k_from, tbl);
if (k_from) ray_release(k_from);
for (int r = 4; r <= 5 && engine && !RAY_IS_ERR(engine); r++) {
ray_t* sv = dict_get(engine, reserved[r]);
ray_t* nv = NULL;
if (sv && sv->type == -RAY_SYM && sv->i64 == kname) {
nv = ray_sym(hidden);
if (nv && !RAY_IS_ERR(nv)) nv->attrs = sv->attrs;
} else if (sv && sv->type == RAY_SYM) {
int64_t n = ray_len(sv);
bool hit = false;
for (int64_t j = 0; j < n; j++) if (sym_cell_runtime_id(sv, j) == kname) hit = true;
if (hit) {
nv = ray_vec_new(RAY_SYM, n);
if (nv && !RAY_IS_ERR(nv)) {
for (int64_t j = 0; j < n; j++) {
int64_t id = sym_cell_runtime_id(sv, j);
((int64_t*)ray_data(nv))[j] = id == kname ? hidden : id;
}
nv->len = n;
}
}
}
if (!nv) continue;
if (RAY_IS_ERR(nv)) { ray_release(engine); return nv; }
ray_t* k_s = ray_sym(rid[r]);
if (!k_s || RAY_IS_ERR(k_s)) { ray_release(nv); ray_release(engine); return k_s ? k_s : ray_error("oom", NULL); }
engine = ray_dict_upsert(engine, k_s, nv);
ray_release(k_s);
ray_release(nv);
}
if (!engine || RAY_IS_ERR(engine)) return engine ? engine : ray_error("oom", NULL);
*key_name = kname;
return engine;
}
static ray_t* select_name_computed_key(ray_t* r, int64_t key_name) {
if (r && !RAY_IS_ERR(r) && ray_is_lazy(r)) r = ray_lazy_materialize(r);
if (!r || RAY_IS_ERR(r) || r->type != RAY_TABLE) return r;
int64_t hidden = ray_sym_intern(SELECT_COMPUTED_KEY, strlen(SELECT_COMPUTED_KEY));
int64_t nc = ray_table_ncols(r);
for (int64_t c = 0; c < nc; c++)
if (ray_table_col_name(r, c) == hidden) { ray_table_set_col_name(r, c, key_name); break; }
return r;
}
static ray_t* ray_select_impl(ray_t** args, int64_t n, bool aliases_resolved) {
if (n < 1) return ray_error("arity", "select: expects a query dict, got %lld args", (long long)n);
ray_t* dict = args[0];
if (!dict || dict->type != RAY_DICT)
return ray_error("type", "select: query must be a dict, got %s", dict ? ray_type_name(dict->type) : "null");
ray_t* from_expr = dict_get(dict, "from");
if (!from_expr) return ray_error("domain", "select: missing `from:` clause");
ray_t* where_expr = dict_get(dict, "where");
ray_group_emit_filter_t prev_emit_filter = ray_group_emit_filter_get();
ray_group_emit_filter_t emit_filter = {0};
bool emit_filter_set = match_group_count_emit_filter(
from_expr, where_expr, &emit_filter);
if (emit_filter_set) {
emit_filter.target_depth = (__VM ? __VM->eval_depth : 0) + 1;
ray_group_emit_filter_set(emit_filter);
}
int64_t proj_hdr_keys = proj_keep_bound(dict);
ray_t* proj_hdr = NULL;
int64_t* proj_buf = (int64_t*)scratch_alloc(&proj_hdr,
(size_t)(proj_hdr_keys > 0 ? proj_hdr_keys : 1) * sizeof(int64_t));
int proj_n = 0;
ray_vm_t* pvm = __VM;
int32_t pj_d = pvm ? pvm->proj_depth : 0;
const int64_t* pj_k = pvm ? pvm->proj_keep : NULL;
int pj_n = pvm ? pvm->proj_keep_n : 0;
bool pj_a = pvm ? pvm->proj_active : false;
if (pvm && proj_buf && proj_compute_keep(dict, proj_buf, (int)proj_hdr_keys, &proj_n)) {
pvm->proj_keep = proj_buf; pvm->proj_keep_n = proj_n;
pvm->proj_depth = pvm->eval_depth; pvm->proj_active = true;
}
ray_t* tbl = ray_eval(from_expr);
if (pvm) { pvm->proj_keep = pj_k; pvm->proj_keep_n = pj_n;
pvm->proj_depth = pj_d; pvm->proj_active = pj_a; }
scratch_free(proj_hdr);
if (emit_filter_set)
ray_group_emit_filter_set(prev_emit_filter);
if (RAY_IS_ERR(tbl)) return tbl;
if (tbl->type == RAY_DICT) {
bool complete;
ray_t* scan = ray_parquet_select_source(tbl,dict,&complete);
if (scan) { ray_release(tbl); tbl = scan; }
if (complete) return tbl;
if (RAY_IS_ERR(tbl)) return tbl;
}
if (tbl->type != RAY_TABLE) { int8_t tbl_t = tbl->type; ray_release(tbl); return ray_error("type", "select: `from:` must evaluate to a table, got %s", ray_type_name(tbl_t)); }
if (!aliases_resolved) {
select_alias_plan_t plan;
ray_t* engine = select_plan_grouped_aliases(dict, tbl, &plan);
if (engine && RAY_IS_ERR(engine)) { ray_release(tbl); return engine; }
if (engine) {
ray_t* r = ray_select_impl(&engine, 1, true);
if (plan.n_derived > 0) r = select_apply_derived(r, dict, &plan);
select_alias_plan_free(&plan);
ray_release(engine);
ray_release(tbl);
return r;
}
}
{
int64_t key_name = -1;
ray_t* engine = select_plan_computed_key(dict, tbl, &key_name);
if (engine && RAY_IS_ERR(engine)) { ray_release(tbl); return engine; }
if (engine) {
ray_t* r = ray_select_impl(&engine, 1, true);
ray_release(engine);
ray_release(tbl);
return select_name_computed_key(r, key_name);
}
}
ray_t* temporal_result = try_temporal_group_materialize(dict, tbl);
if (temporal_result) { ray_release(tbl); return temporal_result; }
ray_t* by_expr = dict_get(dict, "by");
ray_t* take_expr = dict_get(dict, "take");
ray_t* nearest_expr = dict_get(dict, "nearest");
DICT_VIEW_DECL(dv);
DICT_VIEW_OPEN(dict, dv);
if (DICT_VIEW_OVERFLOW(dv)) {
ray_release(tbl);
DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
int64_t dict_n = dv_n;
ray_t** dict_elems = dv;
int64_t from_id = dict_key_id(dict, "from");
int64_t where_id = dict_key_id(dict, "where");
int64_t by_id = dict_key_id(dict, "by");
int64_t take_id = dict_key_id(dict, "take");
int64_t asc_id = dict_key_id(dict, "asc");
int64_t desc_id = dict_key_id(dict, "desc");
int64_t nearest_id = dict_key_id(dict, "nearest");
bool has_sort = false;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == asc_id || kid == desc_id) { has_sort = true; break; }
}
if (nearest_expr) {
if (has_sort) {
ray_release(tbl);
DICT_VIEW_CLOSE(dv); return ray_error("domain",
"select: `nearest` cannot be combined with asc/desc");
}
if (by_expr) {
ray_release(tbl);
DICT_VIEW_CLOSE(dv); return ray_error("domain",
"select: `nearest` cannot be combined with `by`");
}
}
if (!nearest_expr) {
ray_t* rw = try_count_distinct_v2_rewrite(
tbl, by_expr, where_expr, dict_elems, dict_n,
from_id, where_id, by_id, take_id, asc_id, desc_id, nearest_id);
if (rw) {
ray_release(tbl);
DICT_VIEW_CLOSE(dv); return rw;
}
}
int n_out = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid != from_id && kid != where_id && kid != by_id &&
kid != take_id && kid != asc_id && kid != desc_id &&
kid != nearest_id)
n_out++;
}
if (n_out == 0 && !where_expr && !by_expr && !take_expr && !has_sort && !nearest_expr)
{ DICT_VIEW_CLOSE(dv); return tbl; }
if (!where_expr && !by_expr && !take_expr && !has_sort && !nearest_expr && n_out > 0 &&
tbl->type == RAY_TABLE) {
ray_t* meta_res = select_aggs_from_metadata(tbl, dict_elems, dict_n, from_id);
if (meta_res) {
ray_release(tbl);
DICT_VIEW_CLOSE(dv);
return meta_res;
}
}
if (has_sort && !by_expr && !take_expr && !nearest_expr) {
ray_t* streamed = try_stream_parted_order_by(
dict, tbl, dict_elems, dict_n,
from_id, where_id, by_id, take_id, asc_id, desc_id, nearest_id);
if (streamed) {
ray_release(tbl);
DICT_VIEW_CLOSE(dv);
return streamed;
}
}
if (has_sort && take_expr && !by_expr && !nearest_expr) {
ray_t* streamed = try_stream_parted_order_by_topk(
dict, tbl, dict_elems, dict_n,
from_id, where_id, by_id, take_id, asc_id, desc_id, nearest_id);
if (streamed) {
ray_release(tbl);
DICT_VIEW_CLOSE(dv);
return streamed;
}
}
if (take_expr && (has_sort || where_expr) && !by_expr && !nearest_expr) {
if (!where_expr || ray_fused_topk_supported(where_expr, tbl)) {
int64_t sort_key_syms[16];
uint8_t sort_descs[16];
uint8_t n_sort_keys = 0;
int bad_clause = 0;
int64_t out_syms[256];
int64_t out_aliases[256];
uint8_t n_out_syms = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
ray_t* v = dict_elems[i + 1];
if (kid == from_id || kid == where_id || kid == take_id ||
kid == nearest_id) continue;
if (kid == asc_id || kid == desc_id) {
uint8_t is_desc = (kid == desc_id) ? 1 : 0;
if (v && v->type == -RAY_SYM && !(v->attrs & ATTR_QUOTED)) {
if (n_sort_keys >= 16) { bad_clause = 1; break; }
sort_key_syms[n_sort_keys] = v->i64;
sort_descs[n_sort_keys] = is_desc;
n_sort_keys++;
} else if (v && v->type == RAY_SYM) {
int64_t nv = ray_len(v);
if (n_sort_keys + nv > 16) { bad_clause = 1; break; }
for (int64_t kk = 0; kk < nv; kk++) {
sort_key_syms[n_sort_keys] =
sym_cell_runtime_id(v, kk);
sort_descs[n_sort_keys] = is_desc;
n_sort_keys++;
}
} else {
bad_clause = 1; break;
}
continue;
}
if (kid == by_id) { bad_clause = 1; break; }
if (n_out_syms >= 255) { bad_clause = 1; break; }
if (v && v->type == -RAY_SYM && !(v->attrs & ATTR_QUOTED)) {
bool alias_ref = false;
for (uint8_t o = 0; o < n_out_syms; o++)
if (out_aliases[o] == v->i64 && out_syms[o] != v->i64) alias_ref = true;
if (alias_ref) { bad_clause = 1; break; }
}
if (v && v->type == -RAY_SYM && !(v->attrs & ATTR_QUOTED)) {
ray_t* oc = ray_table_get_col(tbl, v->i64);
if (!oc) { bad_clause = 1; break; }
int8_t ot = oc->type;
if (RAY_IS_PARTED(ot) || ot == RAY_MAPCOMMON)
{ bad_clause = 1; break; }
if (!ray_is_vec(oc))
{ bad_clause = 1; break; }
out_syms[n_out_syms] = v->i64;
out_aliases[n_out_syms] = kid;
n_out_syms++;
} else {
bad_clause = 1;
break;
}
}
if (!bad_clause && n_out_syms == 0 && n_out == 0) {
int64_t nc = ray_table_ncols(tbl);
if (nc > 255) {
bad_clause = 1;
} else {
for (int64_t c = 0; c < nc; c++) {
ray_t* oc = ray_table_get_col_idx(tbl, c);
if (!oc) { bad_clause = 1; break; }
int8_t ot = oc->type;
if (RAY_IS_PARTED(ot) || ot == RAY_MAPCOMMON)
{ bad_clause = 1; break; }
if (!ray_is_vec(oc))
{ bad_clause = 1; break; }
int64_t cn = ray_table_col_name(tbl, c);
out_syms[n_out_syms] = cn;
out_aliases[n_out_syms] = cn;
n_out_syms++;
}
}
}
if (!bad_clause) {
for (uint32_t i = 0; i < n_sort_keys && !bad_clause; i++) {
ray_t* kc = ray_table_get_col(tbl, sort_key_syms[i]);
if (!kc) bad_clause = 1;
}
}
if (n_sort_keys == 0 && !(ray_is_atom(take_expr) &&
(take_expr->type == -RAY_I64 || take_expr->type == -RAY_I32)))
bad_clause = 1;
if (!bad_clause && n_out_syms > 0) {
ray_t* tv = ray_eval(take_expr);
if (tv && !RAY_IS_ERR(tv) && ray_is_atom(tv) &&
(tv->type == -RAY_I64 || tv->type == -RAY_I32)) {
int64_t k = (tv->type == -RAY_I64) ? tv->i64 : tv->i32;
ray_release(tv);
bool sorted_asc = false;
if (n_sort_keys == 1 && !sort_descs[0]) {
ray_t* kc = ray_table_get_col(tbl, sort_key_syms[0]);
sorted_asc = kc && (kc->attrs & RAY_ATTR_SORTED) &&
!(kc->attrs & RAY_ATTR_SLICE) &&
!ray_vec_may_have_nulls(kc);
}
if (where_expr && (n_sort_keys == 0 || (sorted_asc && k > 0)) &&
k != 0 && k != INT64_MIN &&
(k < 0 ? -k : k) <= FPK_MAX_K &&
(k < 0 ? -k : k) < ray_table_nrows(tbl)) {
ray_t* res = ray_fused_take_select(tbl, where_expr, k,
out_syms, out_aliases,
n_out_syms);
if (res && !RAY_IS_ERR(res)) {
ray_release(tbl);
DICT_VIEW_CLOSE(dv); return res;
}
if (res && RAY_IS_ERR(res)) ray_release(res);
}
if (n_sort_keys > 0 && k > 0 && k <= FPK_MAX_K && k < ray_table_nrows(tbl)) {
ray_t* res = ray_fused_topk_select(tbl, where_expr,
sort_key_syms,
sort_descs,
n_sort_keys, k,
out_syms,
out_aliases,
n_out_syms);
if (res && !RAY_IS_ERR(res)) {
ray_release(tbl);
DICT_VIEW_CLOSE(dv); return res;
}
if (res && RAY_IS_ERR(res)) ray_release(res);
}
} else if (tv) {
ray_release(tv);
}
}
}
}
ray_t* by_sym_vec_owned = NULL;
bool parted_bydict_deferred = false;
bool computed_single_key = false;
int n_hidden_sort = 0;
ray_t* deferred_bydict = NULL;
int64_t deferred_nk = 0;
int64_t dep_key_base_sym = -1;
int64_t* dep_key_names = NULL;
int64_t* dep_key_biases = NULL;
ray_t* dep_src_hdr = NULL;
int64_t* dep_src_names = NULL;
int64_t* dep_src_biases = NULL;
int64_t n_dep_keys = 0;
if (by_expr && by_expr->type == -RAY_SYM && (by_expr->attrs & ATTR_QUOTED)) {
ray_t* nref = ray_sym(by_expr->i64);
if (nref && !RAY_IS_ERR(nref)) {
by_sym_vec_owned = nref;
by_expr = nref;
} else if (nref) {
ray_release(nref);
}
}
ray_t* saved_selection = NULL;
ray_t* post_group_where_expr = NULL;
DICT_VIEW_DECL(byv);
if (by_expr && by_expr->type == RAY_DICT) {
ray_t* by_dict_ast = by_expr;
DICT_VIEW_OPEN(by_expr, byv);
if (DICT_VIEW_OVERFLOW(byv)) {
ray_release(tbl);
DICT_VIEW_CLOSE(dv); DICT_VIEW_CLOSE(byv); return ray_error("oom", NULL);
}
int64_t dlen = byv_n;
int64_t nk = dlen / 2;
if (nk == 0) {
ray_release(tbl);
DICT_VIEW_CLOSE(dv); DICT_VIEW_CLOSE(byv); return ray_error("domain", "by-dict must have at least 1 key");
}
ray_t** d_elems = byv;
int64_t base_sym = -1;
int64_t base_key_name = -1;
bool dep_candidate = true;
for (int64_t i = 0; i < nk && dep_candidate; i++) {
ray_t* k = d_elems[i * 2];
ray_t* v = d_elems[i * 2 + 1];
if (!k || k->type != -RAY_SYM) {
dep_candidate = false;
break;
}
if (v && v->type == -RAY_SYM && !(v->attrs & ATTR_QUOTED)) {
if (base_sym < 0) {
base_sym = v->i64;
base_key_name = k->i64;
}
if (v->i64 == base_sym)
continue;
}
}
if (dep_candidate && base_sym >= 0) {
if (base_key_name != base_sym)
dep_candidate = false;
}
if (dep_candidate && base_sym >= 0) {
ray_t* base_col = ray_table_get_col(tbl, base_sym);
dep_candidate = base_col && key_type_i64_projectable(base_col->type) &&
!ray_vec_has_nulls(base_col);
}
ray_t* local_dep_hdr = NULL;
int64_t* local_dep_names = NULL;
int64_t* local_dep_biases = NULL;
int64_t local_n_dep = 0;
if (dep_candidate && base_sym >= 0) {
local_dep_names = (int64_t*)scratch_alloc(&local_dep_hdr,
(size_t)nk * 2 * sizeof(int64_t));
if (!local_dep_names) {
ray_release(tbl);
DICT_VIEW_CLOSE(dv); DICT_VIEW_CLOSE(byv); return ray_error("oom", NULL);
}
local_dep_biases = local_dep_names + nk;
for (int64_t i = 0; i < nk && dep_candidate; i++) {
ray_t* k = d_elems[i * 2];
ray_t* v = d_elems[i * 2 + 1];
bool duplicate_key = false;
for (int64_t j = 0; j < i && !duplicate_key; j++)
if (d_elems[j * 2]->i64 == k->i64) duplicate_key = true;
if (duplicate_key) {
dep_candidate = false;
break;
}
bool already_in_tbl = (ray_table_get_col(tbl, k->i64) != NULL);
bool trivial_self = (v->type == -RAY_SYM && v->i64 == k->i64);
if (already_in_tbl && !trivial_self) {
dep_candidate = false;
break;
}
int64_t bias = 0;
if (!expr_affine_of_sym(v, base_sym, &bias)) {
dep_candidate = false;
break;
}
if (k->i64 != base_key_name || bias != 0) {
local_dep_names[local_n_dep] = k->i64;
local_dep_biases[local_n_dep] = bias;
local_n_dep++;
}
}
}
if (dep_candidate && base_sym >= 0 && local_n_dep > 0) {
by_sym_vec_owned = ray_vec_new(RAY_SYM, 1);
if (!by_sym_vec_owned || RAY_IS_ERR(by_sym_vec_owned)) {
scratch_free(local_dep_hdr);
ray_release(tbl);
DICT_VIEW_CLOSE(dv); DICT_VIEW_CLOSE(byv); return ray_error("oom", NULL);
}
((int64_t*)ray_data(by_sym_vec_owned))[0] = base_key_name;
by_sym_vec_owned->len = 1;
by_expr = by_sym_vec_owned;
dep_key_base_sym = base_key_name;
n_dep_keys = local_n_dep;
dep_src_hdr = local_dep_hdr;
dep_src_names = local_dep_names;
dep_src_biases = local_dep_biases;
goto by_dict_done;
}
scratch_free(local_dep_hdr);
bool by_input_parted = false;
{
int64_t in_nc = ray_table_ncols(tbl);
for (int64_t c = 0; c < in_nc; c++) {
ray_t* col = ray_table_get_col_idx(tbl, c);
if (col && (RAY_IS_PARTED(col->type) ||
col->type == RAY_MAPCOMMON)) {
by_input_parted = true; break;
}
}
}
if (by_input_parted && n_out > 0) {
bool defer_ok = true;
for (int64_t i = 0; i + 1 < dict_n && defer_ok; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id ||
kid == nearest_id) continue;
if (!is_group_dag_agg_expr(dict_elems[i + 1])) defer_ok = false;
}
for (int64_t i = 0; i < nk && defer_ok; i++) {
ray_t* k = d_elems[i * 2];
ray_t* v = d_elems[i * 2 + 1];
if (!k || k->type != -RAY_SYM) { defer_ok = false; break; }
for (int64_t j = 0; j < i; j++)
if (d_elems[j * 2]->i64 == k->i64) { defer_ok = false; break; }
if (!defer_ok) break;
bool trivial_self = (v && v->type == -RAY_SYM && v->i64 == k->i64);
if (ray_table_get_col(tbl, k->i64) && !trivial_self) {
defer_ok = false; break;
}
if (v && v->type == RAY_LIST) continue;
if (v && v->type == -RAY_SYM && !(v->attrs & ATTR_QUOTED)) {
ray_t* src = ray_table_get_col(tbl, v->i64);
if (!src ||
!query_key_type_streamable(query_col_base_type(src))) {
defer_ok = false; break;
}
continue;
}
defer_ok = false;
}
if (defer_ok) {
by_sym_vec_owned = ray_vec_new(RAY_SYM, nk);
if (!by_sym_vec_owned || RAY_IS_ERR(by_sym_vec_owned)) {
ray_release(tbl);
DICT_VIEW_CLOSE(dv); DICT_VIEW_CLOSE(byv); return ray_error("oom", NULL);
}
int64_t* sv = (int64_t*)ray_data(by_sym_vec_owned);
for (int64_t i = 0; i < nk; i++) sv[i] = d_elems[i * 2]->i64;
by_sym_vec_owned->len = nk;
by_expr = by_sym_vec_owned;
parted_bydict_deferred = true;
deferred_bydict = by_dict_ast;
deferred_nk = nk;
goto by_dict_done;
}
}
bool has_computed_by_val = false;
for (int64_t i = 0; i < nk; i++) {
ray_t* k = d_elems[i * 2];
ray_t* v = d_elems[i * 2 + 1];
if (!k || k->type != -RAY_SYM) continue;
bool bare_ref = v && v->type == -RAY_SYM &&
!(v->attrs & ATTR_QUOTED) &&
ray_table_get_col(tbl, v->i64) != NULL;
if (!(v && v->type == -RAY_SYM && v->i64 == k->i64) && !bare_ref) {
has_computed_by_val = true;
break;
}
}
ray_group_emit_filter_t prefilter_top_count;
memset(&prefilter_top_count, 0, sizeof(prefilter_top_count));
bool prefilter_top_n_match =
match_group_desc_count_take(dict_elems, dict_n, from_id, where_id,
by_id, take_id, asc_id, desc_id,
&prefilter_top_count);
bool prefilter_computed_by = has_computed_by_val;
bool prefilter_multi_key_where = false;
if (prefilter_top_n_match && !has_computed_by_val && nk >= 2) {
int64_t count_sym = ray_sym_intern("count", 5);
for (int64_t i = 0; i + 1 < dict_n &&
!prefilter_multi_key_where; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id ||
kid == nearest_id) continue;
ray_t* val = dict_elems[i + 1];
if (!is_group_dag_agg_expr(val)) continue;
ray_t** ae = (ray_t**)ray_data(val);
if (!ae[0] || ae[0]->type != -RAY_SYM) continue;
if (ae[0]->i64 == count_sym) continue;
prefilter_multi_key_where = true;
}
}
if (where_expr && (prefilter_computed_by || prefilter_multi_key_where)) {
int64_t ncols_max = ray_table_ncols(tbl);
if (ncols_max < 1) ncols_max = 1;
ray_t* keep_hdr = NULL;
int64_t* keep_syms = (int64_t*)scratch_alloc(&keep_hdr,
(size_t)ncols_max * sizeof(int64_t));
if (!keep_syms) { ray_release(tbl); DICT_VIEW_CLOSE(dv); DICT_VIEW_CLOSE(byv); return ray_error("oom", NULL); }
int n_keep = 0;
n_keep = collect_col_refs_set(where_expr, tbl,
keep_syms, (int)ncols_max, n_keep);
for (int64_t i = 0; i + 1 < dict_n && n_keep < ncols_max; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == take_id ||
kid == nearest_id) continue;
n_keep = collect_col_refs_set(dict_elems[i + 1], tbl,
keep_syms, (int)ncols_max, n_keep);
}
int can_project = (n_keep > 0 && n_keep < ncols_max);
ray_t* narrow_tbl = NULL;
if (can_project) {
narrow_tbl = project_table_cols(tbl, keep_syms, n_keep);
if (!narrow_tbl || RAY_IS_ERR(narrow_tbl)) {
if (narrow_tbl) ray_release(narrow_tbl);
narrow_tbl = NULL;
can_project = 0;
}
}
scratch_free(keep_hdr);
ray_t* prefilter_input = can_project ? narrow_tbl : tbl;
ray_graph_t* fg = ray_graph_new(prefilter_input);
if (!fg) {
if (narrow_tbl) ray_release(narrow_tbl);
ray_release(tbl);
DICT_VIEW_CLOSE(dv); DICT_VIEW_CLOSE(byv); return ray_error("oom", NULL);
}
ray_op_t* froot = ray_const_table(fg, prefilter_input);
ray_t* pred_err = NULL;
ray_op_t* pred = compile_where_predicate(fg, prefilter_input,
where_expr, "select",
&pred_err);
if (!pred) {
ray_graph_free(fg);
if (narrow_tbl) ray_release(narrow_tbl);
ray_release(tbl);
DICT_VIEW_CLOSE(dv); DICT_VIEW_CLOSE(byv);
return pred_err ? pred_err :
ray_error("domain", "select: failed to evaluate `where:` predicate");
}
froot = ray_filter(fg, froot, pred);
ray_t* filtered = ray_execute(fg, froot);
ray_graph_free(fg);
if (narrow_tbl) ray_release(narrow_tbl);
if (!filtered || RAY_IS_ERR(filtered)) {
ray_release(tbl);
DICT_VIEW_CLOSE(dv); DICT_VIEW_CLOSE(byv); return filtered ? filtered : ray_error("domain", "select: `where:` filter produced no result");
}
if (ray_is_lazy(filtered))
filtered = ray_lazy_materialize(filtered);
if (!filtered || RAY_IS_ERR(filtered)) {
ray_release(tbl);
DICT_VIEW_CLOSE(dv); DICT_VIEW_CLOSE(byv); return filtered ? filtered : ray_error("domain", "select: failed to materialize filtered result");
}
ray_release(tbl);
tbl = filtered;
where_expr = NULL;
}
ray_env_push_query_scope();
int64_t in_ncols = ray_table_ncols(tbl);
for (int64_t c = 0; c < in_ncols; c++) {
int64_t cn = ray_table_col_name(tbl, c);
ray_t* cv = ray_table_get_col_idx(tbl, c);
if (cv) ray_env_set_query_local(cn, cv);
}
by_sym_vec_owned = ray_vec_new(RAY_SYM, nk);
if (!by_sym_vec_owned || RAY_IS_ERR(by_sym_vec_owned)) {
ray_env_pop_scope();
ray_release(tbl);
DICT_VIEW_CLOSE(dv); DICT_VIEW_CLOSE(byv); return ray_error("oom", NULL);
}
int64_t* sv_data = (int64_t*)ray_data(by_sym_vec_owned);
by_sym_vec_owned->len = nk;
bool failed = false;
ray_t* fail_err = NULL;
int64_t expected_len = ray_table_nrows(tbl);
for (int64_t i = 0; i < nk; i++) {
ray_t* k = d_elems[i * 2];
ray_t* v = d_elems[i * 2 + 1];
if (!k || k->type != -RAY_SYM) {
fail_err = ray_error("domain", "by-dict key must be a symbol name");
failed = true; break;
}
bool duplicate_key = false;
for (int64_t j = 0; j < i && !duplicate_key; j++)
if (d_elems[j * 2]->i64 == k->i64) duplicate_key = true;
if (duplicate_key) {
fail_err = ray_error("domain", "by-dict has duplicate key");
failed = true; break;
}
bool already_in_tbl = (ray_table_get_col(tbl, k->i64) != NULL);
bool trivial_self = (v->type == -RAY_SYM && v->i64 == k->i64);
if (already_in_tbl && !trivial_self) {
fail_err = ray_error("domain",
"by-dict alias shadows an existing input column");
failed = true; break;
}
if (trivial_self) {
sv_data[i] = k->i64;
continue;
}
ray_t* col_vec = group_key_eval(v, tbl);
if (!col_vec || RAY_IS_ERR(col_vec)) {
fail_err = col_vec ? col_vec : ray_error("domain", "by-dict val eval");
failed = true; break;
}
if (!ray_is_vec(col_vec) || ray_len(col_vec) != expected_len) {
ray_release(col_vec);
fail_err = ray_error("length", "by-dict val must be a column vector");
failed = true; break;
}
ray_t* new_tbl = ray_table_add_col(tbl, k->i64, col_vec);
ray_release(col_vec);
if (!new_tbl || RAY_IS_ERR(new_tbl)) {
fail_err = new_tbl ? new_tbl : ray_error("oom", NULL);
failed = true; break;
}
tbl = new_tbl;
ray_env_set_query_local(k->i64, col_vec);
sv_data[i] = k->i64;
}
ray_env_pop_scope();
if (failed) {
ray_release(by_sym_vec_owned);
ray_release(tbl);
DICT_VIEW_CLOSE(dv); DICT_VIEW_CLOSE(byv); return fail_err;
}
by_expr = by_sym_vec_owned;
}
by_dict_done:
DICT_VIEW_CLOSE(byv);
ray_graph_t* g = ray_graph_new(tbl);
if (!g) {
scratch_free(dep_src_hdr);
if (by_sym_vec_owned) ray_release(by_sym_vec_owned);
ray_release(tbl); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
ray_op_t* root = ray_const_table(g, tbl);
int64_t n_out_max = select_output_count(dict_elems, dict_n);
int64_t hidden_max = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id ||
kid == nearest_id) continue;
hidden_max += count_agg_subexprs(dict_elems[i + 1]);
}
if (n_out_max < 1) n_out_max = 1;
if (hidden_max < 1) hidden_max = 1;
int64_t nk_max = (by_expr && ray_is_vec(by_expr) && by_expr->type == RAY_SYM)
? ray_len(by_expr) : 1;
if (nk_max < 1) nk_max = 1;
int64_t aggs_max = n_out_max + hidden_max;
ray_t* sel_slots_hdr = NULL;
int64_t* nonagg_names = (int64_t*)scratch_alloc(&sel_slots_hdr,
(size_t)n_out_max * (3 * sizeof(int64_t) + 2 * sizeof(ray_t*)) +
(size_t)hidden_max * (sizeof(ray_t*) + sizeof(int64_t)) +
(size_t)nk_max * sizeof(ray_op_t*) +
(size_t)aggs_max * (2 * sizeof(ray_op_t*) + 2 * sizeof(int64_t) + sizeof(uint16_t)) +
(size_t)2 * (size_t)n_dep_keys * sizeof(int64_t) +
(size_t)(nk_max + 2 * aggs_max) * sizeof(uint32_t));
if (!nonagg_names) {
scratch_free(dep_src_hdr);
if (by_sym_vec_owned) ray_release(by_sym_vec_owned);
ray_graph_free(g); ray_release(tbl); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
ray_t** nonagg_exprs = (ray_t**)(nonagg_names + n_out_max);
int64_t* compound_names = (int64_t*)(nonagg_exprs + n_out_max);
int64_t* compound_pos = (int64_t*)(compound_names + n_out_max);
ray_t** compound_rw = (ray_t**)(compound_pos + n_out_max);
ray_t** hidden_agg_exprs = (ray_t**)(compound_rw + n_out_max);
int64_t* hidden_agg_names = (int64_t*)(hidden_agg_exprs + hidden_max);
ray_op_t** key_ops = (ray_op_t**)(hidden_agg_names + hidden_max);
ray_op_t** agg_ins = key_ops + nk_max;
ray_op_t** agg_ins2 = agg_ins + aggs_max;
int64_t* agg_k = (int64_t*)(agg_ins2 + aggs_max);
int64_t* agg_names = agg_k + aggs_max;
dep_key_names = agg_names + aggs_max;
dep_key_biases = dep_key_names + n_dep_keys;
uint32_t* key_ids = (uint32_t*)(dep_key_biases + n_dep_keys);
uint32_t* agg_in_ids = key_ids + nk_max;
uint32_t* agg_in2_ids = agg_in_ids + aggs_max;
uint16_t* agg_ops = (uint16_t*)(agg_in2_ids + aggs_max);
for (int64_t i = 0; i < n_dep_keys; i++) {
dep_key_names[i] = dep_src_names[i];
dep_key_biases[i] = dep_src_biases[i];
}
scratch_free(dep_src_hdr); dep_src_hdr = NULL;
int64_t n_nonaggs = 0;
int n_hidden_aggs = 0;
int n_compound = 0;
int n_aggs_real = 0;
int synth_count_col = 0;
int has_list_agg = 0;
if (where_expr && by_expr && !nearest_expr &&
can_defer_single_key_where(by_expr, where_expr, tbl)) {
post_group_where_expr = where_expr;
where_expr = NULL;
}
if (where_expr) {
int and_chained = 0;
if (where_expr->type == RAY_LIST
&& ray_len(where_expr) >= 3)
{
ray_t** elems = (ray_t**)ray_data(where_expr);
ray_t* head = elems[0];
if (head && head->type == -RAY_SYM) {
ray_t* hs = ray_sym_str(head->i64);
if (hs && ray_str_len(hs) == 3
&& memcmp(ray_str_ptr(hs), "and", 3) == 0)
{
int64_t k = ray_len(where_expr) - 1;
int all_ok = 1;
int reorder_safe = 1;
ray_op_t* compiled[64];
int cost[64];
if (k > 64) all_ok = 0;
for (int64_t i = 0; i < k && all_ok; i++) {
ray_op_t* p = compile_expr_dag(g, elems[i + 1]);
if (!p || p->opcode == OP_CONST) { all_ok = 0; break; }
int has_scan = 0;
int c = 0;
ray_op_t* stk[64];
int sp = 0;
stk[sp++] = p;
while (sp > 0) {
ray_op_t* cur = stk[--sp];
if (cur->opcode == OP_SCAN) has_scan = 1;
switch (cur->opcode) {
case OP_LIKE:
c += 50;
break;
case OP_IN: case OP_NOT_IN:
c += 20;
break;
case OP_NOT:
c += 1;
break;
case OP_EQ: case OP_NE: case OP_LT: case OP_LE:
case OP_GT: case OP_GE: case OP_AND: case OP_OR:
c += 5;
break;
case OP_SCAN: case OP_CONST:
break;
default:
c += 5;
reorder_safe = 0;
break;
}
for (uint8_t a = 0; a < cur->arity && sp < 64; a++) {
ray_op_t* ch = op_child(g, cur, a);
if (ch) stk[sp++] = ch;
}
}
if (!has_scan) { all_ok = 0; break; }
compiled[i] = p;
cost[i] = c;
}
if (all_ok) {
int order[64];
for (int64_t i = 0; i < k; i++) order[i] = (int)i;
if (reorder_safe) {
for (int64_t i = 0; i < k - 1; i++) {
int min_i = (int)i;
for (int64_t j = i + 1; j < k; j++)
if (cost[order[j]] < cost[order[min_i]])
min_i = (int)j;
if (min_i != (int)i) {
int tmp = order[i];
order[i] = order[min_i];
order[min_i] = tmp;
}
}
}
for (int64_t i = 0; i < k; i++)
root = ray_filter(g, root, compiled[order[i]]);
and_chained = 1;
}
}
}
}
if (!and_chained) {
ray_t* pred_err = NULL;
ray_op_t* pred = compile_where_predicate(g, tbl, where_expr,
"select", &pred_err);
if (!pred) {
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv);
return pred_err ? pred_err :
ray_error("domain", "select: failed to evaluate `where:` predicate");
}
root = ray_filter(g, root, pred);
}
}
float* nearest_query_owned = NULL;
ray_t* nearest_handle_owned = NULL;
if (nearest_expr) {
if (nearest_expr->type != RAY_LIST || ray_len(nearest_expr) < 3) {
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain",
"nearest: expected (ann <handle> <query> [ef]) or (knn <col> <query> [metric])");
}
int64_t nlen = ray_len(nearest_expr);
ray_t** nlist = (ray_t**)ray_data(nearest_expr);
ray_t* head = nlist[0];
if (head->type != -RAY_SYM) {
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain",
"nearest: first element must be the symbol `ann` or `knn`");
}
int64_t ann_sym_id = ray_sym_intern("ann", 3);
int64_t knn_sym_id = ray_sym_intern("knn", 3);
int64_t k_req = 10;
if (take_expr) {
ray_t* tv = ray_eval(take_expr);
if (!tv || RAY_IS_ERR(tv)) {
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return tv ? tv : ray_error("domain", "nearest: failed to evaluate `take:`");
}
if (tv->type == -RAY_I64) k_req = tv->i64;
else if (tv->type == -RAY_I32) k_req = tv->i32;
else {
ray_release(tv);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type", "nearest: take must be an integer atom");
}
ray_release(tv);
if (k_req <= 0) {
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain", "nearest: take must be positive");
}
}
ray_t* qvec = ray_eval(nlist[2]);
if (!qvec || RAY_IS_ERR(qvec)) {
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return qvec ? qvec : ray_error("domain", "nearest: failed to evaluate query vector");
}
if (!ray_is_vec(qvec) ||
(qvec->type != RAY_F32 && qvec->type != RAY_F64 &&
qvec->type != RAY_I32 && qvec->type != RAY_I64)) {
ray_release(qvec);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type", "nearest: query must be a numeric vector");
}
int32_t dim = (int32_t)qvec->len;
if (dim <= 0) {
ray_release(qvec);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("length", "nearest: query vector is empty");
}
nearest_query_owned = (float*)ray_calloc_raw((size_t)dim * sizeof(float));
if (!nearest_query_owned) {
ray_release(qvec);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
switch (qvec->type) {
case RAY_F32:
memcpy(nearest_query_owned, ray_data(qvec), (size_t)dim * sizeof(float));
break;
case RAY_F64: {
double* s = (double*)ray_data(qvec);
for (int32_t j = 0; j < dim; j++) nearest_query_owned[j] = (float)s[j];
break;
}
case RAY_I32: {
int32_t* s = (int32_t*)ray_data(qvec);
for (int32_t j = 0; j < dim; j++) nearest_query_owned[j] = (float)s[j];
break;
}
case RAY_I64: {
int64_t* s = (int64_t*)ray_data(qvec);
for (int32_t j = 0; j < dim; j++) nearest_query_owned[j] = (float)s[j];
break;
}
}
ray_release(qvec);
if (head->i64 == ann_sym_id) {
ray_t* hobj = ray_eval(nlist[1]);
if (!hobj || RAY_IS_ERR(hobj)) {
ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return hobj ? hobj : ray_error("domain", "nearest (ann): failed to evaluate HNSW handle");
}
if (hobj->type != -RAY_I64 || !(hobj->attrs & RAY_ATTR_HNSW)) {
ray_release(hobj); ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type",
"nearest (ann): first arg must be an HNSW handle (from hnsw-build)");
}
ray_hnsw_t* idx = (ray_hnsw_t*)(uintptr_t)hobj->i64;
if (!idx) {
ray_release(hobj); ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type",
"nearest (ann): HNSW handle has been freed");
}
if (idx->dim != dim) {
ray_release(hobj); ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("length",
"nearest (ann): query dim does not match index dim");
}
int32_t ef = HNSW_DEFAULT_EF_S;
if (nlen >= 4) {
ray_t* ev = ray_eval(nlist[3]);
if (!ev || RAY_IS_ERR(ev)) {
ray_release(hobj); ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ev ? ev : ray_error("domain",
"nearest (ann): ef expression failed to evaluate");
}
if (ev->type == -RAY_I64) ef = (int32_t)ev->i64;
else if (ev->type == -RAY_I32) ef = ev->i32;
else {
ray_release(ev); ray_release(hobj);
ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type",
"nearest (ann): ef must be an integer atom");
}
ray_release(ev);
}
if ((int64_t)ef < k_req) ef = (int32_t)k_req;
root = ray_ann_rerank(g, root, idx, nearest_query_owned, dim, k_req, ef);
nearest_handle_owned = hobj;
} else if (head->i64 == knn_sym_id) {
ray_t* col_expr = nlist[1];
if (col_expr->type != -RAY_SYM) {
ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type",
"nearest (knn): first arg must be an unquoted column name");
}
int64_t col_sym = col_expr->i64;
ray_hnsw_metric_t metric = RAY_HNSW_COSINE;
if (nlen >= 4) {
ray_t* mv = nlist[3];
if (mv && mv->type == -RAY_SYM) {
int64_t mid = mv->i64;
if (mid == ray_sym_find("l2", 2)) metric = RAY_HNSW_L2;
else if (mid == ray_sym_find("ip", 2)) metric = RAY_HNSW_IP;
else if (mid == ray_sym_find("cosine", 6)) metric = RAY_HNSW_COSINE;
else {
ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain",
"nearest (knn): metric must be 'cosine, 'l2, or 'ip");
}
}
}
root = ray_knn_rerank(g, root, col_sym, nearest_query_owned, dim, k_req, metric);
} else {
ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain",
"nearest: expected `ann` or `knn` as the first element");
}
if (!root) {
if (nearest_handle_owned) ray_release(nearest_handle_owned);
ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
if (n_out == 0) {
int64_t src_ncols = ray_table_ncols(tbl);
if (src_ncols > 255) {
if (nearest_handle_owned) ray_release(nearest_handle_owned);
ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("limit",
"nearest: implicit projection exceeds 255 source columns — "
"specify output columns explicitly");
}
if (src_ncols > 0) {
ray_op_t** col_ops = (ray_op_t**)ray_calloc_raw(
(size_t)src_ncols * sizeof(ray_op_t*));
if (!col_ops) {
if (nearest_handle_owned) ray_release(nearest_handle_owned);
ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
int nc = 0;
bool scan_err = false;
for (int64_t c = 0; c < src_ncols; c++) {
int64_t name_id = ray_table_col_name(tbl, c);
ray_t* s = ray_sym_str(name_id);
if (!s) continue;
ray_op_t* scan_op = ray_scan(g, ray_str_ptr(s));
if (!scan_op) { scan_err = true; break; }
col_ops[nc++] = scan_op;
}
if (scan_err) {
ray_free_raw(col_ops);
if (nearest_handle_owned) ray_release(nearest_handle_owned);
ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
root = ray_select_op(g, root, col_ops, nc);
ray_free_raw(col_ops);
if (!root) {
if (nearest_handle_owned) ray_release(nearest_handle_owned);
ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
}
}
}
ray_group_emit_filter_t pre_top_emit = {0};
bool pre_top_emit_matched = false;
if (by_expr) {
ray_group_emit_filter_t cur_emit = ray_group_emit_filter_get();
if (!cur_emit.enabled &&
match_group_desc_count_take(dict_elems, dict_n, from_id, where_id,
by_id, take_id, asc_id, desc_id,
&pre_top_emit))
pre_top_emit_matched = true;
}
if (by_expr) {
int64_t by_key_sym = -1;
if (by_expr->type == -RAY_SYM && !(by_expr->attrs & ATTR_QUOTED))
by_key_sym = by_expr->i64;
else if (by_expr->type == RAY_SYM && ray_len(by_expr) == 1)
by_key_sym = ((int64_t*)ray_data(by_expr))[0];
int any_nonagg = 0;
int any_true_nonagg = 0;
if (n_out > 0) {
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id) continue;
if (is_single_group_key_projection(by_expr, dict_elems[i + 1]))
continue;
if (is_group_dag_agg_expr(dict_elems[i + 1])) continue;
if (is_decomposable_agg_compound(dict_elems[i + 1], tbl)) continue;
any_nonagg = 1;
if (can_atom_broadcast(dict_elems[i + 1])) continue;
if (!match_count_distinct(dict_elems[i + 1])) { any_true_nonagg = 1; break; }
}
}
int use_eval_group = 0;
if (by_key_sym >= 0) {
ray_t* key_col = ray_table_get_col(tbl, by_key_sym);
if (key_col) {
int8_t kct = key_col->type;
if (RAY_IS_PARTED(kct)) kct = (int8_t)RAY_PARTED_BASETYPE(kct);
if (kct == RAY_LIST && (any_nonagg || n_out == 0 ||
RAY_IS_PARTED(key_col->type) || !ray_agg_engine_v2))
use_eval_group = 1;
else if (kct == RAY_GUID && (any_nonagg || n_out == 0))
use_eval_group = 1;
else if (kct == RAY_STR && (any_true_nonagg || n_out == 0))
use_eval_group = 1;
}
}
if (!use_eval_group && by_expr->type == RAY_SYM && ray_len(by_expr) > 1) {
int64_t nk = ray_len(by_expr);
int64_t* sym_ids = (int64_t*)ray_data(by_expr);
for (int64_t k = 0; k < nk; k++) {
ray_t* key_col = ray_table_get_col(tbl, sym_ids[k]);
if (!key_col) continue;
int8_t kct = key_col->type;
if (RAY_IS_PARTED(kct)) kct = (int8_t)RAY_PARTED_BASETYPE(kct);
if (kct == RAY_LIST && (any_nonagg || n_out == 0 ||
RAY_IS_PARTED(key_col->type) || !ray_agg_engine_v2)) {
use_eval_group = 1;
break;
}
}
if (!use_eval_group && n_out == 0)
use_eval_group = 1;
}
if (!use_eval_group && any_nonagg) {
int single_scalar_key = 0;
if (by_expr->type == -RAY_SYM && !(by_expr->attrs & ATTR_QUOTED)) {
single_scalar_key = 1;
} else if (by_expr->type == RAY_SYM && ray_len(by_expr) == 1) {
single_scalar_key = 1;
}
if (!single_scalar_key) use_eval_group = 1;
}
if (use_eval_group) {
ray_t* eval_tbl = tbl;
if (where_expr) {
root = ray_optimize(g, root);
ray_vm_t* fvm = __VM;
ray_t* filt_hdr = NULL;
int64_t* filt_buf = NULL;
const int64_t* sv_fk = fvm ? fvm->filt_keep : NULL;
int sv_fn = fvm ? fvm->filt_keep_n : 0;
int32_t sv_fd = fvm ? fvm->filt_depth : 0;
if (fvm) {
int64_t filt_cap = (by_expr ? ray_len(by_expr) : 0) + fvm->proj_keep_n;
if (filt_cap < 1) filt_cap = 1;
filt_buf = (int64_t*)scratch_alloc(&filt_hdr, (size_t)filt_cap * sizeof(int64_t));
if (filt_buf) {
int fk_n = filt_compact_keep(dict, by_expr, tbl,
from_id, where_id, by_id,
filt_buf, (int)filt_cap);
if (fk_n > 0) {
fvm->filt_keep = filt_buf; fvm->filt_keep_n = fk_n;
fvm->filt_depth = fvm->eval_depth;
}
}
}
ray_t* fres = ray_execute(g, root);
if (fvm) { fvm->filt_keep = sv_fk; fvm->filt_keep_n = sv_fn;
fvm->filt_depth = sv_fd; }
scratch_free(filt_hdr);
ray_graph_free(g); g = NULL;
if (!fres || RAY_IS_ERR(fres)) { ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return fres ? fres : ray_error("domain", "select: `where:` filter produced no result"); }
if (ray_is_lazy(fres)) fres = ray_lazy_materialize(fres);
if (!fres || RAY_IS_ERR(fres)) { ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return fres ? fres : ray_error("domain", "select: failed to materialize filtered result"); }
eval_tbl = fres;
} else {
ray_graph_free(g); g = NULL;
}
if (by_key_sym < 0 && by_expr->type == RAY_SYM && ray_len(by_expr) > 1) {
int64_t nk = ray_len(by_expr);
int64_t* key_syms = (int64_t*)ray_data(by_expr);
int64_t nrows = ray_table_nrows(eval_tbl);
if (nk <= 0) {
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain", "eval-level multi-key groupby requires at least one key");
}
ray_t* keycols_hdr = NULL;
ray_t** key_cols = (ray_t**)scratch_alloc(&keycols_hdr, (size_t)nk * sizeof(ray_t*));
if (!key_cols) {
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
for (int64_t k = 0; k < nk; k++) {
key_cols[k] = ray_table_get_col(eval_tbl, key_syms[k]);
if (!key_cols[k]) {
scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain", "group key column not found");
}
}
bool distinct_only = true;
for (int64_t i = 0; i + 1 < dict_n && distinct_only; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid != from_id && kid != where_id && kid != by_id)
distinct_only = false;
}
for (int64_t k = 0; k < nk && distinct_only; k++) {
switch (key_cols[k]->type) {
case RAY_I64: case RAY_I32: case RAY_I16: case RAY_U8:
case RAY_BOOL: case RAY_DATE: case RAY_TIME:
case RAY_TIMESTAMP: case RAY_SYM: case RAY_STR: break;
default: distinct_only = false; break;
}
}
int64_t dchk_nc = ray_table_ncols(eval_tbl);
for (int64_t c = 0; c < dchk_nc && distinct_only; c++) {
ray_t* cc = ray_table_get_col_idx(eval_tbl, c);
if (!cc) { distinct_only = false; break; }
switch (cc->type) {
case RAY_I64: case RAY_I32: case RAY_I16: case RAY_U8:
case RAY_BOOL: case RAY_DATE: case RAY_TIME:
case RAY_TIMESTAMP: case RAY_SYM: case RAY_F64:
case RAY_GUID: case RAY_STR: case RAY_LIST: break;
default: distinct_only = false; break;
}
}
if (distinct_only) {
const int64_t* keep = NULL; int keep_n = 0;
if (__VM && __VM->proj_active &&
__VM->eval_depth == __VM->proj_depth + 1) {
keep = __VM->proj_keep; keep_n = __VM->proj_keep_n;
__VM->proj_active = false;
}
ray_t* dres = agg_select_distinct(eval_tbl, key_cols, key_syms,
(uint32_t)nk, nrows, keep, keep_n);
scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return dres;
}
int64_t pre_take_groups = nrows;
bool has_pre_take = unsorted_positive_take_limit(
dict_elems, dict_n, asc_id, desc_id, take_id,
nrows, &pre_take_groups);
if (has_pre_take && pre_take_groups <= 1024) {
ray_t* keyrd_hdr = NULL;
query_key_reader_t* key_readers = (query_key_reader_t*)scratch_alloc(
&keyrd_hdr, (size_t)nk * sizeof(query_key_reader_t));
int64_t cnt_out_max = select_output_count(dict_elems, dict_n);
ray_t* cntnames_hdr = NULL;
int64_t* count_names = (int64_t*)scratch_alloc(
&cntnames_hdr, (size_t)(cnt_out_max > 0 ? cnt_out_max : 1) * sizeof(int64_t));
if (!key_readers || !count_names) {
scratch_free(keyrd_hdr); scratch_free(cntnames_hdr);
scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
for (int64_t k = 0; k < nk; k++) {
if (!query_key_reader_init(&key_readers[k], key_cols[k])) {
scratch_free(keyrd_hdr); scratch_free(cntnames_hdr);
scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type", "unsupported group key");
}
}
int n_count_out = 0;
bool count_only = true;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id) continue;
ray_t* val_expr_item = dict_elems[i + 1];
if (!is_plain_count_expr(val_expr_item)) {
count_only = false;
break;
}
count_names[n_count_out++] = kid;
}
if (count_only && n_count_out > 0) {
int64_t cap = pre_take_groups;
ray_t* vals_hdr = ray_alloc((size_t)cap * (size_t)nk * sizeof(int64_t));
ray_t* null_hdr = ray_alloc((size_t)cap * (size_t)nk);
ray_t* cnt_hdr = ray_alloc((size_t)cap * sizeof(int64_t));
if (!vals_hdr || !null_hdr || !cnt_hdr) {
if (vals_hdr) ray_free(vals_hdr);
if (null_hdr) ray_free(null_hdr);
if (cnt_hdr) ray_free(cnt_hdr);
scratch_free(keyrd_hdr); scratch_free(cntnames_hdr); scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
int64_t* key_vals = (int64_t*)ray_data(vals_hdr);
uint8_t* key_null = (uint8_t*)ray_data(null_hdr);
int64_t* counts = (int64_t*)ray_data(cnt_hdr);
memset(key_null, 0, (size_t)cap * (size_t)nk);
memset(counts, 0, (size_t)cap * sizeof(int64_t));
ray_t* rowbuf_hdr = NULL;
int64_t* rv = (int64_t*)scratch_alloc(&rowbuf_hdr,
(size_t)nk * (sizeof(int64_t) + 1));
if (!rv) {
ray_free(vals_hdr); ray_free(null_hdr); ray_free(cnt_hdr);
scratch_free(keyrd_hdr); scratch_free(cntnames_hdr); scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
uint8_t* rn = (uint8_t*)(rv + nk);
int64_t found = 0;
for (int64_t r = 0; r < nrows; r++) {
for (int64_t k = 0; k < nk; k++) {
if (!query_key_reader_read(&key_readers[k], r, &rv[k], &rn[k])) {
ray_free(vals_hdr); ray_free(null_hdr); ray_free(cnt_hdr);
scratch_free(rowbuf_hdr); scratch_free(keyrd_hdr); scratch_free(cntnames_hdr); scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type", "unsupported group key");
}
}
int64_t gi = -1;
for (int64_t gidx = 0; gidx < found; gidx++) {
bool match = true;
for (int64_t k = 0; k < nk; k++) {
size_t off = (size_t)gidx * (size_t)nk + (size_t)k;
if (key_null[off] != rn[k] ||
(!rn[k] && key_vals[off] != rv[k])) {
match = false;
break;
}
}
if (match) { gi = gidx; break; }
}
if (gi < 0 && found < cap) {
gi = found++;
for (int64_t k = 0; k < nk; k++) {
size_t off = (size_t)gi * (size_t)nk + (size_t)k;
key_vals[off] = rv[k];
key_null[off] = rn[k];
}
}
if (gi >= 0) counts[gi]++;
}
ray_t* result = ray_table_new(nk + n_count_out);
if (!result || RAY_IS_ERR(result)) {
ray_free(vals_hdr); ray_free(null_hdr); ray_free(cnt_hdr);
scratch_free(rowbuf_hdr); scratch_free(keyrd_hdr); scratch_free(cntnames_hdr); scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result ? result : ray_error("oom", NULL);
}
for (int64_t k = 0; k < nk; k++) {
ray_t* src = key_cols[k];
int8_t kt = src->type;
if (RAY_IS_PARTED(kt)) kt = (int8_t)RAY_PARTED_BASETYPE(kt);
else if (kt == RAY_MAPCOMMON) kt = key_readers[k].base_type;
ray_t* key_vec = (kt == RAY_SYM)
? ray_sym_vec_new(
(src->type == RAY_SYM)
? (src->attrs & RAY_SYM_W_MASK)
: RAY_SYM_W64,
found)
: ray_vec_new(kt, found);
if (!key_vec || RAY_IS_ERR(key_vec)) {
ray_release(result);
ray_free(vals_hdr); ray_free(null_hdr); ray_free(cnt_hdr);
scratch_free(rowbuf_hdr); scratch_free(keyrd_hdr); scratch_free(cntnames_hdr); scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return key_vec ? key_vec : ray_error("oom", NULL);
}
if (kt == RAY_SYM)
ray_sym_vec_adopt_domain(key_vec, sym_domain_rep(src));
key_vec->len = found;
for (int64_t gi = 0; gi < found; gi++) {
size_t off = (size_t)gi * (size_t)nk + (size_t)k;
ray_t* atom = NULL;
switch (kt) {
case RAY_SYM: atom = ray_sym(key_vals[off]); break;
case RAY_I64:
case RAY_TIMESTAMP: atom = ray_i64(key_vals[off]); break;
case RAY_I32:
case RAY_DATE:
case RAY_TIME: atom = ray_i32((int32_t)key_vals[off]); break;
case RAY_I16: atom = ray_i16((int16_t)key_vals[off]); break;
case RAY_BOOL:
case RAY_U8: atom = ray_u8((uint8_t)key_vals[off]); break;
case RAY_F64: {
double dv;
memcpy(&dv, &key_vals[off], 8);
atom = ray_f64(dv);
break;
}
default: atom = ray_i64(key_vals[off]); break;
}
if (atom) {
if (key_null[off]) atom->aux[0] |= 1;
store_typed_elem(key_vec, gi, atom);
ray_release(atom);
}
}
result = ray_table_add_col(result, key_syms[k], key_vec);
ray_release(key_vec);
if (RAY_IS_ERR(result)) {
ray_free(vals_hdr); ray_free(null_hdr); ray_free(cnt_hdr);
scratch_free(rowbuf_hdr); scratch_free(keyrd_hdr); scratch_free(cntnames_hdr); scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result;
}
}
for (int ai = 0; ai < n_count_out; ai++) {
ray_t* cv = ray_vec_new(RAY_I64, found);
if (!cv || RAY_IS_ERR(cv)) {
ray_release(result);
ray_free(vals_hdr); ray_free(null_hdr); ray_free(cnt_hdr);
scratch_free(rowbuf_hdr); scratch_free(keyrd_hdr); scratch_free(cntnames_hdr); scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return cv ? cv : ray_error("oom", NULL);
}
cv->len = found;
memcpy(ray_data(cv), counts, (size_t)found * sizeof(int64_t));
result = ray_table_add_col(result, count_names[ai], cv);
ray_release(cv);
if (RAY_IS_ERR(result)) {
ray_free(vals_hdr); ray_free(null_hdr); ray_free(cnt_hdr);
scratch_free(rowbuf_hdr); scratch_free(keyrd_hdr); scratch_free(cntnames_hdr); scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result;
}
}
ray_free(vals_hdr); ray_free(null_hdr); ray_free(cnt_hdr);
scratch_free(rowbuf_hdr); scratch_free(keyrd_hdr); scratch_free(cntnames_hdr); scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result;
}
scratch_free(keyrd_hdr); scratch_free(cntnames_hdr);
}
ray_t* composite_keys = ray_list_new(nrows);
if (!composite_keys || RAY_IS_ERR(composite_keys)) {
scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return composite_keys ? composite_keys : ray_error("oom", NULL);
}
for (int64_t r = 0; r < nrows; r++) {
ray_t* row_key = ray_list_new(nk);
if (!row_key || RAY_IS_ERR(row_key)) {
ray_release(composite_keys);
scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return row_key ? row_key : ray_error("oom", NULL);
}
for (int64_t k = 0; k < nk; k++) {
int alloc = 0;
ray_t* cell = collection_elem(key_cols[k], r, &alloc);
if (!cell || RAY_IS_ERR(cell)) {
ray_release(row_key);
ray_release(composite_keys);
scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return cell ? cell : ray_error("domain", "select by: failed to read composite group key cell");
}
row_key = ray_list_append(row_key, cell);
if (alloc) ray_release(cell);
if (!row_key || RAY_IS_ERR(row_key)) {
ray_release(composite_keys);
scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return row_key ? row_key : ray_error("oom", NULL);
}
}
composite_keys = ray_list_append(composite_keys, row_key);
ray_release(row_key);
if (!composite_keys || RAY_IS_ERR(composite_keys)) {
scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return composite_keys ? composite_keys : ray_error("oom", NULL);
}
}
ray_t* groups_dict = ray_group_indices_fn(composite_keys);
ray_release(composite_keys);
if (!groups_dict || RAY_IS_ERR(groups_dict)) {
scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return groups_dict ? groups_dict : ray_error("domain", "select by: failed to compute composite groups");
}
ray_t* groups = groups_to_pair_list(groups_dict);
ray_release(groups_dict);
if (!groups || RAY_IS_ERR(groups)) {
scratch_free(keycols_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return groups ? groups : ray_error("domain", "select by: failed to build groups pair list");
}
int64_t n_groups = ray_len(groups) / 2;
int64_t out_groups = n_groups;
bool take_preapplied = unsorted_positive_take_limit(
dict_elems, dict_n, asc_id, desc_id, take_id,
n_groups, &out_groups);
int n_agg_out = 0;
int64_t agg_nout_max = select_output_count(dict_elems, dict_n);
if (agg_nout_max < 1) agg_nout_max = 1;
ray_t* aggnames_hdr = NULL;
int64_t* agg_names = (int64_t*)scratch_alloc(&aggnames_hdr, (size_t)agg_nout_max * sizeof(int64_t));
ray_t* aggres_hdr = NULL;
ray_t** agg_results = (ray_t**)scratch_calloc(&aggres_hdr, (size_t)agg_nout_max * sizeof(ray_t*));
if (!agg_names || !agg_results) {
scratch_free(aggnames_hdr); scratch_free(aggres_hdr); scratch_free(keycols_hdr);
ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id) continue;
ray_t* val_expr_item = dict_elems[i + 1];
ray_t* cd_inner = match_count_distinct(val_expr_item);
if (cd_inner) {
ray_t* per_group = count_distinct_per_group_groups(
cd_inner, eval_tbl, groups, out_groups);
if (!per_group || RAY_IS_ERR(per_group)) {
for (int ai = 0; ai < n_agg_out; ai++) if (agg_results[ai]) ray_release(agg_results[ai]);
scratch_free(aggnames_hdr); scratch_free(aggres_hdr); scratch_free(keycols_hdr);
ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return per_group ? per_group : ray_error("domain", "select by: count-distinct aggregation failed");
}
agg_names[n_agg_out] = kid;
agg_results[n_agg_out] = per_group;
n_agg_out++;
continue;
}
if (is_streaming_aggr_unary_call(val_expr_item)) {
ray_t** agg_elems = (ray_t**)ray_data(val_expr_item);
ray_t* agg_fn_name = agg_elems[0];
ray_t* agg_col_expr = agg_elems[1];
ray_t* src_col_val = NULL;
if (agg_col_expr->type == -RAY_SYM && !(agg_col_expr->attrs & ATTR_QUOTED)) {
src_col_val = ray_table_get_col(eval_tbl, agg_col_expr->i64);
if (src_col_val) ray_retain(src_col_val);
}
if (!src_col_val) {
src_col_val = ray_eval(agg_col_expr);
if (src_col_val && ray_is_lazy(src_col_val))
src_col_val = ray_lazy_materialize(src_col_val);
if (!src_col_val || RAY_IS_ERR(src_col_val)) {
for (int ai = 0; ai < n_agg_out; ai++) if (agg_results[ai]) ray_release(agg_results[ai]);
scratch_free(aggnames_hdr); scratch_free(aggres_hdr); scratch_free(keycols_hdr);
ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return src_col_val ? src_col_val : ray_error("domain", "select by: failed to evaluate aggregation source column");
}
}
ray_t* agg_vec = NULL;
ray_t** grp_items = (ray_t**)ray_data(groups);
if (is_med_call(val_expr_item)) {
ray_t* ix_hdr = NULL;
ray_t* off_hdr = NULL;
ray_t* cnt_hdr = NULL;
int64_t total = 0;
for (int64_t gi = 0; gi < out_groups; gi++)
total += ray_len(grp_items[gi * 2 + 1]);
int64_t* ix = (int64_t*)scratch_alloc(&ix_hdr,
(size_t)(total > 0 ? total : 1) * sizeof(int64_t));
int64_t* off = (int64_t*)scratch_alloc(&off_hdr,
(size_t)(out_groups > 0 ? out_groups : 1) * sizeof(int64_t));
int64_t* cnt = (int64_t*)scratch_alloc(&cnt_hdr,
(size_t)(out_groups > 0 ? out_groups : 1) * sizeof(int64_t));
if (ix && off && cnt) {
int64_t pos = 0;
for (int64_t gi = 0; gi < out_groups; gi++) {
ray_t* idx_list = grp_items[gi * 2 + 1];
int64_t c = ray_len(idx_list);
off[gi] = pos;
cnt[gi] = c;
if (c > 0)
memcpy(ix + pos, ray_data(idx_list),
(size_t)c * sizeof(int64_t));
pos += c;
}
agg_vec = ray_median_per_group_buf(
src_col_val, ix, off, cnt, out_groups);
}
if (ix_hdr) scratch_free(ix_hdr);
if (off_hdr) scratch_free(off_hdr);
if (cnt_hdr) scratch_free(cnt_hdr);
}
if (!agg_vec) {
for (int64_t gi = 0; gi < out_groups; gi++) {
ray_t* idx_list = grp_items[gi * 2 + 1];
ray_t* subset = ray_at_fn(src_col_val, idx_list);
if (!subset || RAY_IS_ERR(subset)) continue;
ray_t* agg_val = NULL;
ray_t* fn_obj = ray_env_get(agg_fn_name->i64);
if (fn_obj && fn_obj->type == RAY_UNARY) {
ray_unary_fn uf = (ray_unary_fn)(uintptr_t)fn_obj->i64;
agg_val = uf(subset);
}
ray_release(subset);
if (agg_val && !RAY_IS_ERR(agg_val) && ray_is_lazy(agg_val))
agg_val = ray_lazy_materialize(agg_val);
if (!agg_val || RAY_IS_ERR(agg_val)) continue;
if (!agg_vec) {
int8_t vt = -(agg_val->type);
agg_vec = ray_vec_new(vt, out_groups);
if (!agg_vec || RAY_IS_ERR(agg_vec)) { ray_release(agg_val); break; }
agg_vec->len = out_groups;
}
store_typed_elem(agg_vec, gi, agg_val);
ray_release(agg_val);
}
}
if (!agg_vec && out_groups == 0) agg_vec = empty_agg_column(agg_fn_name, src_col_val);
ray_release(src_col_val);
agg_names[n_agg_out] = kid;
agg_results[n_agg_out] = agg_vec;
n_agg_out++;
} else {
ray_t* per_group = nonagg_eval_per_group(val_expr_item, eval_tbl, groups, n_groups);
if (!per_group || RAY_IS_ERR(per_group)) {
for (int ai = 0; ai < n_agg_out; ai++) if (agg_results[ai]) ray_release(agg_results[ai]);
scratch_free(aggnames_hdr); scratch_free(aggres_hdr); scratch_free(keycols_hdr);
ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return per_group ? per_group : ray_error("domain", "select by: per-group projection evaluation failed");
}
agg_names[n_agg_out] = kid;
agg_results[n_agg_out] = per_group;
n_agg_out++;
}
}
ray_t* result = ray_table_new(nk + n_agg_out);
if (!result || RAY_IS_ERR(result)) {
for (int ai = 0; ai < n_agg_out; ai++) if (agg_results[ai]) ray_release(agg_results[ai]);
scratch_free(aggnames_hdr); scratch_free(aggres_hdr); scratch_free(keycols_hdr);
ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result ? result : ray_error("oom", NULL);
}
ray_t** grp_items = (ray_t**)ray_data(groups);
for (int64_t k = 0; k < nk; k++) {
ray_t* src = key_cols[k];
int8_t kt = src->type;
if (RAY_IS_PARTED(kt)) kt = (int8_t)RAY_PARTED_BASETYPE(kt);
ray_t* key_vec = NULL;
if (kt == RAY_STR) {
key_vec = ray_vec_new(RAY_STR, out_groups);
for (int64_t gi = 0; gi < out_groups && key_vec && !RAY_IS_ERR(key_vec); gi++) {
ray_t* row_key = grp_items[gi * 2];
ray_t* cell = (row_key && row_key->type == RAY_LIST && k < row_key->len)
? ((ray_t**)ray_data(row_key))[k] : NULL;
const char* sp = cell ? ray_str_ptr(cell) : "";
size_t slen = cell ? ray_str_len(cell) : 0;
key_vec = ray_str_vec_append(key_vec, sp ? sp : "", sp ? slen : 0);
}
} else {
key_vec = (kt == RAY_SYM)
? ray_sym_vec_new(src->attrs & RAY_SYM_W_MASK, out_groups)
: ray_vec_new(kt, out_groups);
if (key_vec && !RAY_IS_ERR(key_vec)) {
key_vec->len = out_groups;
memset(ray_data(key_vec), 0, (size_t)out_groups * ray_sym_elem_size(kt, key_vec->attrs));
for (int64_t gi = 0; gi < out_groups; gi++) {
ray_t* row_key = grp_items[gi * 2];
ray_t* cell = (row_key && row_key->type == RAY_LIST && k < row_key->len)
? ((ray_t**)ray_data(row_key))[k] : NULL;
if (cell) store_typed_elem(key_vec, gi, cell);
}
}
}
if (!key_vec || RAY_IS_ERR(key_vec)) {
for (int ai = 0; ai < n_agg_out; ai++) if (agg_results[ai]) ray_release(agg_results[ai]);
scratch_free(aggnames_hdr); scratch_free(aggres_hdr); scratch_free(keycols_hdr);
ray_release(result); ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return key_vec ? key_vec : ray_error("oom", NULL);
}
result = ray_table_add_col(result, key_syms[k], key_vec);
ray_release(key_vec);
if (RAY_IS_ERR(result)) {
for (int ai = 0; ai < n_agg_out; ai++) if (agg_results[ai]) ray_release(agg_results[ai]);
scratch_free(aggnames_hdr); scratch_free(aggres_hdr); scratch_free(keycols_hdr);
ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result;
}
}
for (int ai = 0; ai < n_agg_out; ai++) {
if (agg_results[ai]) {
result = ray_table_add_col(result, agg_names[ai], agg_results[ai]);
ray_release(agg_results[ai]);
if (RAY_IS_ERR(result)) { scratch_free(aggnames_hdr); scratch_free(aggres_hdr); scratch_free(keycols_hdr); ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result; }
}
}
scratch_free(aggnames_hdr); scratch_free(aggres_hdr); scratch_free(keycols_hdr);
if (n_out == 0 && out_groups > 0) {
ray_t* fi_hdr = ray_alloc((size_t)out_groups * sizeof(int64_t));
if (!fi_hdr) {
ray_release(result); ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
int64_t* fi = (int64_t*)ray_data(fi_hdr);
for (int64_t gi = 0; gi < out_groups; gi++) {
ray_t* il = grp_items[gi * 2 + 1];
fi[gi] = (il && ray_len(il) > 0) ? ((int64_t*)ray_data(il))[0] : 0;
}
int64_t nc = ray_table_ncols(eval_tbl);
for (int64_t c = 0; c < nc && !RAY_IS_ERR(result); c++) {
int64_t cn = ray_table_col_name(eval_tbl, c);
bool is_key = false;
for (int64_t k = 0; k < nk; k++) if (key_syms[k] == cn) { is_key = true; break; }
if (is_key) continue;
ray_t* sc = ray_table_get_col_idx(eval_tbl, c);
if (!sc) continue;
ray_t* dst = NULL;
if (sc->type == RAY_STR) {
dst = ray_vec_new(RAY_STR, out_groups);
bool src_has_nulls = ray_vec_may_have_nulls(sc);
for (int64_t gi = 0; gi < out_groups && dst && !RAY_IS_ERR(dst); gi++) {
if (src_has_nulls && ray_vec_is_null(sc, fi[gi])) {
dst = ray_str_vec_append(dst, "", 0);
if (dst && !RAY_IS_ERR(dst)) ray_vec_set_null(dst, dst->len - 1, true);
} else {
size_t slen = 0;
const char* sp = ray_str_vec_get(sc, fi[gi], &slen);
dst = ray_str_vec_append(dst, sp ? sp : "", sp ? slen : 0);
}
}
} else if (sc->type == RAY_LIST) {
dst = ray_alloc((size_t)out_groups * sizeof(ray_t*));
if (dst) {
dst->type = RAY_LIST; dst->len = out_groups;
ray_t** dout = (ray_t**)ray_data(dst);
ray_t** sitems = (ray_t**)ray_data(sc);
for (int64_t gi = 0; gi < out_groups; gi++) { dout[gi] = sitems[fi[gi]]; ray_retain(dout[gi]); }
}
} else {
dst = ray_vec_new(sc->type, out_groups);
if (dst && !RAY_IS_ERR(dst)) {
dst->len = out_groups;
for (int64_t gi = 0; gi < out_groups; gi++) {
int a = 0; ray_t* v = collection_elem(sc, fi[gi], &a);
store_typed_elem(dst, gi, v);
if (a) ray_release(v);
}
}
}
if (!dst || RAY_IS_ERR(dst)) {
if (dst) ray_release(dst);
ray_free(fi_hdr); ray_release(result); ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
result = ray_table_add_col(result, cn, dst);
ray_release(dst);
}
ray_free(fi_hdr);
if (RAY_IS_ERR(result)) {
ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result;
}
}
ray_release(groups);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
if (take_preapplied) {
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result;
}
result = apply_sort_take(result, dict_elems, dict_n,
asc_id, desc_id, take_id, NULL);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result;
}
int64_t key_out_name = by_key_sym;
if (by_key_sym < 0 && by_expr->type != RAY_SYM) {
int64_t kn = ray_table_nrows(eval_tbl);
ray_t* kv = NULL;
if (ray_env_push_query_scope() != RAY_OK) kv = ray_error("oom", NULL);
else {
int64_t enc = ray_table_ncols(eval_tbl);
for (int64_t c = 0; c < enc; c++) {
ray_t* cv = ray_table_get_col_idx(eval_tbl, c);
if (cv) ray_env_set_query_local(ray_table_col_name(eval_tbl, c), cv);
}
kv = group_key_eval(by_expr, eval_tbl);
ray_env_pop_scope();
}
if (kv && !RAY_IS_ERR(kv) && ray_is_atom(kv)) {
ray_t* wide = atom_broadcast_vec(kv, kn);
ray_release(kv);
kv = wide ? wide : ray_error("type", "by: cannot broadcast the key");
}
if (kv && !RAY_IS_ERR(kv) && (!ray_is_vec(kv) || ray_len(kv) != kn)) {
int64_t got = ray_len(kv);
ray_release(kv);
kv = ray_error("length", "by: key has %lld rows, the table %lld",
(long long)got, (long long)kn);
}
if (!kv || RAY_IS_ERR(kv)) {
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return kv ? kv : ray_error("domain", "by: key produced no result");
}
by_key_sym = ray_sym_intern("__by_key", 8);
if (ray_table_get_col(eval_tbl, by_key_sym)) {
ray_release(kv);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv);
return ray_error("domain", "by: the input has a column named __by_key");
}
ray_retain(eval_tbl);
ray_t* keyed = ray_table_add_col(eval_tbl, by_key_sym, kv);
ray_release(kv);
if (eval_tbl != tbl) ray_release(eval_tbl);
if (!keyed || RAY_IS_ERR(keyed)) {
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return keyed ? keyed : ray_error("oom", NULL);
}
eval_tbl = keyed;
key_out_name = derived_key_name(by_expr);
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id) continue;
if (kid == key_out_name) { key_out_name = ray_sym_intern("key", 3); break; }
}
}
if (by_key_sym < 0) {
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("nyi", "eval-level groupby requires scalar key");
}
ray_t* key_col = ray_table_get_col(eval_tbl, by_key_sym);
if (n_out == 0 && key_col && key_col->type == RAY_GUID) {
int64_t n = key_col->len;
const uint8_t* kb = (const uint8_t*)ray_data(key_col);
uint32_t cap = 64;
while ((uint64_t)cap < (uint64_t)n * 2 && cap < (1u << 28)) cap <<= 1;
uint32_t mask = cap - 1;
ray_t* ht_hdr = ray_alloc((size_t)cap * sizeof(uint32_t));
if (!ht_hdr) { if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL); }
uint32_t* ht = (uint32_t*)ray_data(ht_hdr);
memset(ht, 0xFF, (size_t)cap * sizeof(uint32_t));
int64_t fi_cap = n < 1024 ? 1024 : (n < (1 << 20) ? n : (1 << 20));
if (fi_cap < 256) fi_cap = 256;
ray_t* fi_hdr = ray_alloc((size_t)fi_cap * sizeof(int64_t));
if (!fi_hdr) { ray_free(ht_hdr); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL); }
int64_t* fi = (int64_t*)ray_data(fi_hdr);
int64_t ngroups = 0;
for (int64_t i = 0; i < n; i++) {
if ((i & 65535) == 0) {
if (ray_interrupted()) {
ray_free(fi_hdr);
ray_free(ht_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("cancel", "interrupted");
}
ray_progress_update("select", "by: first-of-group",
(uint64_t)i, (uint64_t)n);
}
const uint8_t* cur = kb + (size_t)i * 16;
uint64_t h; memcpy(&h, cur, 8); h ^= h >> 33; h *= 0xff51afd7ed558ccdULL;
uint32_t slot = (uint32_t)(h & mask);
uint32_t gi = UINT32_MAX;
while (ht[slot] != UINT32_MAX) {
uint32_t cand = ht[slot];
if (memcmp(kb + (size_t)fi[cand] * 16, cur, 16) == 0) { gi = cand; break; }
slot = (slot + 1) & mask;
}
if (gi == UINT32_MAX) {
if (ngroups >= fi_cap) {
int64_t new_cap = fi_cap * 2;
ray_t* new_hdr = ray_alloc((size_t)new_cap * sizeof(int64_t));
if (!new_hdr) { ray_free(fi_hdr); ray_free(ht_hdr); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL); }
memcpy(ray_data(new_hdr), fi, (size_t)ngroups * sizeof(int64_t));
ray_free(fi_hdr);
fi_hdr = new_hdr;
fi = (int64_t*)ray_data(fi_hdr);
fi_cap = new_cap;
}
fi[ngroups] = i;
ht[slot] = (uint32_t)ngroups;
ngroups++;
}
}
ray_free(ht_hdr);
int64_t nc_src = ray_table_ncols(eval_tbl);
ray_t* res = ray_table_new(nc_src);
ray_t* first_err = NULL;
if (!res || RAY_IS_ERR(res)) {
first_err = res && RAY_IS_ERR(res) ? res : ray_error("oom", NULL);
res = NULL;
goto fog_cleanup;
}
for (int64_t pass = 0; pass < nc_src + 1 && !first_err; pass++) {
int64_t cn;
if (pass == 0) cn = by_key_sym;
else {
cn = ray_table_col_name(eval_tbl, pass - 1);
if (cn == by_key_sym) continue;
}
ray_t* sc = ray_table_get_col(eval_tbl, cn);
if (!sc) continue;
ray_t* dst = NULL;
int8_t sct = sc->type;
if (RAY_IS_PARTED(sct)) sct = (int8_t)RAY_PARTED_BASETYPE(sct);
if (sct == RAY_STR) {
dst = ray_vec_new(RAY_STR, ngroups);
for (int64_t gi = 0; gi < ngroups && dst && !RAY_IS_ERR(dst); gi++) {
size_t slen = 0;
const char* sp = ray_str_vec_get(sc, fi[gi], &slen);
dst = ray_str_vec_append(dst, sp ? sp : "", sp ? slen : 0);
}
} else if (sct == RAY_LIST) {
dst = ray_list_new((int32_t)ngroups);
if (dst && !RAY_IS_ERR(dst)) {
ray_t** sitems = (ray_t**)ray_data(sc);
ray_t** dout = (ray_t**)ray_data(dst);
for (int64_t gi = 0; gi < ngroups; gi++) {
dout[gi] = sitems[fi[gi]];
ray_retain(dout[gi]);
}
dst->len = ngroups;
}
} else if (sct == RAY_SYM) {
dst = ray_sym_vec_new(sc->attrs & RAY_SYM_W_MASK, ngroups);
if (dst && !RAY_IS_ERR(dst)) {
ray_sym_vec_adopt_domain(dst, sym_domain_rep(sc));
dst->len = ngroups;
uint8_t esz = ray_sym_elem_size(sct, dst->attrs);
const char* sb = (const char*)ray_data(sc);
char* db = (char*)ray_data(dst);
bool src_has_nulls = ray_vec_may_have_nulls(sc);
if (src_has_nulls) {
for (int64_t gi = 0; gi < ngroups; gi++) {
memcpy(db + (size_t)gi * esz,
sb + (size_t)fi[gi] * esz, esz);
if (ray_vec_is_null(sc, fi[gi]))
ray_vec_set_null(dst, gi, true);
}
} else {
for (int64_t gi = 0; gi < ngroups; gi++)
memcpy(db + (size_t)gi * esz,
sb + (size_t)fi[gi] * esz, esz);
}
}
} else {
dst = ray_vec_new(sct, ngroups);
if (dst && !RAY_IS_ERR(dst)) {
dst->len = ngroups;
uint8_t esz = ray_sym_elem_size(sct, sc->attrs);
const char* sb = (const char*)ray_data(sc);
char* db = (char*)ray_data(dst);
bool src_has_nulls = ray_vec_may_have_nulls(sc);
if (src_has_nulls) {
for (int64_t gi = 0; gi < ngroups; gi++) {
memcpy(db + (size_t)gi * esz,
sb + (size_t)fi[gi] * esz, esz);
if (ray_vec_is_null(sc, fi[gi]))
ray_vec_set_null(dst, gi, true);
}
} else {
for (int64_t gi = 0; gi < ngroups; gi++)
memcpy(db + (size_t)gi * esz,
sb + (size_t)fi[gi] * esz, esz);
}
}
}
if (!dst || RAY_IS_ERR(dst)) {
first_err = (dst && RAY_IS_ERR(dst)) ? dst : ray_error("oom", NULL);
if (dst && !RAY_IS_ERR(dst)) ray_release(dst);
break;
}
res = ray_table_add_col(res, cn, dst);
ray_release(dst);
if (RAY_IS_ERR(res)) { first_err = res; res = NULL; break; }
}
fog_cleanup:
ray_free(fi_hdr);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
if (first_err) {
if (res) ray_release(res);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return first_err;
}
res = apply_sort_take(res, dict_elems, dict_n,
asc_id, desc_id, take_id, NULL);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return res;
}
ray_t* groups_dict = ray_group_indices_fn(key_col);
if (RAY_IS_ERR(groups_dict)) { if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return groups_dict; }
ray_t* groups = groups_to_pair_list(groups_dict);
ray_release(groups_dict);
if (RAY_IS_ERR(groups)) { if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return groups; }
int64_t gn = ray_len(groups);
int64_t n_groups = gn / 2;
if (n_groups == 0 && n_out == 0) {
ray_release(groups);
int64_t nc0 = ray_table_ncols(eval_tbl);
ray_t* empty = ray_table_new(nc0);
if (!RAY_IS_ERR(empty)) {
{ ray_t* sc = ray_table_get_col(eval_tbl, by_key_sym);
if (sc) {
ray_t* ev = ray_group_gather(sc, NULL, 0);
if (ev && !RAY_IS_ERR(ev)) { empty = ray_table_add_col(empty, by_key_sym, ev); ray_release(ev); }
}
}
for (int64_t c = 0; c < nc0; c++) {
int64_t cn = ray_table_col_name(eval_tbl, c);
if (cn == by_key_sym) continue;
ray_t* sc = ray_table_get_col_idx(eval_tbl, c);
ray_t* ev = (sc->type == RAY_STR) ? ray_vec_new(RAY_STR, 0) :
(sc->type == RAY_LIST) ? ray_list_new(0) :
ray_vec_new(sc->type, 0);
if (ev && !RAY_IS_ERR(ev)) { empty = ray_table_add_col(empty, cn, ev); ray_release(ev); }
}
}
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return empty;
}
int n_agg_out = 0;
int64_t agg_nout_max = select_output_count(dict_elems, dict_n);
if (agg_nout_max < 1) agg_nout_max = 1;
ray_t* aggnames_hdr = NULL;
int64_t* agg_names = (int64_t*)scratch_alloc(&aggnames_hdr, (size_t)agg_nout_max * sizeof(int64_t));
ray_t* aggres_hdr = NULL;
ray_t** agg_results = (ray_t**)scratch_calloc(&aggres_hdr, (size_t)agg_nout_max * sizeof(ray_t*));
if (!agg_names || !agg_results) {
scratch_free(aggnames_hdr); scratch_free(aggres_hdr);
ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id || kid == take_id || kid == asc_id || kid == desc_id) continue;
ray_t* val_expr_item = dict_elems[i + 1];
{
ray_t* cd_inner = match_count_distinct(val_expr_item);
if (cd_inner) {
ray_t* per_group = count_distinct_per_group_groups(
cd_inner, eval_tbl, groups, n_groups);
if (!per_group || RAY_IS_ERR(per_group)) {
for (int ai = 0; ai < n_agg_out; ai++) { if (agg_results[ai]) ray_release(agg_results[ai]); }
scratch_free(aggnames_hdr); scratch_free(aggres_hdr);
ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return per_group ? per_group : ray_error("domain", "select by: count-distinct aggregation failed");
}
agg_names[n_agg_out] = kid;
agg_results[n_agg_out] = per_group;
n_agg_out++;
continue;
}
}
if (is_streaming_aggr_unary_call(val_expr_item)) {
ray_t** agg_elems = (ray_t**)ray_data(val_expr_item);
ray_t* agg_fn_name = agg_elems[0];
ray_t* agg_col_expr = agg_elems[1];
ray_t* src_col_val = NULL;
if (agg_col_expr->type == -RAY_SYM && !(agg_col_expr->attrs & ATTR_QUOTED)) {
src_col_val = ray_table_get_col(eval_tbl, agg_col_expr->i64);
if (src_col_val) ray_retain(src_col_val);
}
if (!src_col_val) {
src_col_val = ray_eval(agg_col_expr);
if (src_col_val && !RAY_IS_ERR(src_col_val) && ray_is_lazy(src_col_val))
src_col_val = ray_lazy_materialize(src_col_val);
if (RAY_IS_ERR(src_col_val)) {
for (int ai = 0; ai < n_agg_out; ai++) { if (agg_results[ai]) ray_release(agg_results[ai]); }
scratch_free(aggnames_hdr); scratch_free(aggres_hdr);
ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return src_col_val;
}
}
ray_t* agg_vec = NULL;
ray_t** grp_items = (ray_t**)ray_data(groups);
if (n_groups == 0) {
ray_t* fn_obj = ray_env_get(agg_fn_name->i64);
agg_vec = fn_obj && fn_obj->type == RAY_UNARY
? empty_unary_group_result((ray_unary_fn)(uintptr_t)fn_obj->i64, src_col_val)
: ray_error("type", "aggregate must be a unary function");
if (!agg_vec || RAY_IS_ERR(agg_vec)) {
ray_release(src_col_val);
for (int ai = 0; ai < n_agg_out; ai++) ray_release(agg_results[ai]);
scratch_free(aggnames_hdr); scratch_free(aggres_hdr);
ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv);
return agg_vec ? agg_vec : ray_error("oom", NULL);
}
}
if (n_groups > 0 && is_med_call(val_expr_item)) {
ray_t* ix_hdr = NULL;
ray_t* off_hdr = NULL;
ray_t* cnt_hdr = NULL;
int64_t total = 0;
for (int64_t gi = 0; gi < n_groups; gi++)
total += ray_len(grp_items[gi * 2 + 1]);
int64_t* ix = (int64_t*)scratch_alloc(&ix_hdr,
(size_t)(total > 0 ? total : 1) * sizeof(int64_t));
int64_t* off = (int64_t*)scratch_alloc(&off_hdr,
(size_t)n_groups * sizeof(int64_t));
int64_t* cnt = (int64_t*)scratch_alloc(&cnt_hdr,
(size_t)n_groups * sizeof(int64_t));
if (ix && off && cnt) {
int64_t pos = 0;
for (int64_t gi = 0; gi < n_groups; gi++) {
ray_t* idx_list = grp_items[gi * 2 + 1];
int64_t c = ray_len(idx_list);
off[gi] = pos;
cnt[gi] = c;
if (c > 0)
memcpy(ix + pos, ray_data(idx_list),
(size_t)c * sizeof(int64_t));
pos += c;
}
agg_vec = ray_median_per_group_buf(
src_col_val, ix, off, cnt, n_groups);
}
if (ix_hdr) scratch_free(ix_hdr);
if (off_hdr) scratch_free(off_hdr);
if (cnt_hdr) scratch_free(cnt_hdr);
if (agg_vec && !RAY_IS_ERR(agg_vec)) {
ray_release(src_col_val);
agg_names[n_agg_out] = kid;
agg_results[n_agg_out] = agg_vec;
n_agg_out++;
continue;
}
agg_vec = NULL;
}
for (int64_t gi = 0; gi < n_groups; gi++) {
ray_t* idx_list = grp_items[gi * 2 + 1];
ray_t* subset = ray_at_fn(src_col_val, idx_list);
if (RAY_IS_ERR(subset)) continue;
ray_t* agg_val = NULL;
ray_t* fn_obj = ray_env_get(agg_fn_name->i64);
if (fn_obj && fn_obj->type == RAY_UNARY) {
ray_unary_fn uf = (ray_unary_fn)(uintptr_t)fn_obj->i64;
agg_val = uf(subset);
}
ray_release(subset);
if (agg_val && !RAY_IS_ERR(agg_val) && ray_is_lazy(agg_val))
agg_val = ray_lazy_materialize(agg_val);
if (!agg_val || RAY_IS_ERR(agg_val)) continue;
if (!agg_vec) {
int8_t vt = -(agg_val->type);
agg_vec = ray_vec_new(vt, n_groups);
if (RAY_IS_ERR(agg_vec)) { ray_release(agg_val); break; }
agg_vec->len = n_groups;
}
store_typed_elem(agg_vec, gi, agg_val);
ray_release(agg_val);
}
if (!agg_vec && n_groups == 0) agg_vec = empty_agg_column(agg_fn_name, src_col_val);
ray_release(src_col_val);
agg_names[n_agg_out] = kid;
agg_results[n_agg_out] = agg_vec;
n_agg_out++;
} else {
if (is_agg_expr(val_expr_item)) {
ray_t* per_group = nonagg_eval_per_group(
val_expr_item, eval_tbl, groups, n_groups);
if (RAY_IS_ERR(per_group)) {
for (int ai = 0; ai < n_agg_out; ai++) { if (agg_results[ai]) ray_release(agg_results[ai]); }
scratch_free(aggnames_hdr); scratch_free(aggres_hdr);
ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return per_group;
}
agg_names[n_agg_out] = kid;
agg_results[n_agg_out] = per_group;
n_agg_out++;
continue;
}
if (ray_env_push_query_scope() != RAY_OK) {
for (int ai = 0; ai < n_agg_out; ai++) { if (agg_results[ai]) ray_release(agg_results[ai]); }
scratch_free(aggnames_hdr); scratch_free(aggres_hdr);
ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
ray_t* _aqt = bind_all_columns(eval_tbl);
ray_t* full_val = ray_eval(val_expr_item);
g_active_query_table = _aqt;
ray_env_pop_scope();
if (RAY_IS_ERR(full_val)) {
for (int ai = 0; ai < n_agg_out; ai++) { if (agg_results[ai]) ray_release(agg_results[ai]); }
scratch_free(aggnames_hdr); scratch_free(aggres_hdr);
ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return full_val;
}
ray_t* list_col = ray_alloc(n_groups * sizeof(ray_t*));
if (!list_col || RAY_IS_ERR(list_col)) {
ray_release(full_val);
for (int ai = 0; ai < n_agg_out; ai++) { if (agg_results[ai]) ray_release(agg_results[ai]); }
scratch_free(aggnames_hdr); scratch_free(aggres_hdr);
ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
list_col->type = RAY_LIST;
list_col->len = 0;
ray_t** list_out = (ray_t**)ray_data(list_col);
int64_t eval_nrows = ray_table_nrows(eval_tbl);
int refs_column = expr_refs_row_column(val_expr_item, eval_tbl);
int is_indexable =
ray_is_vec(full_val) || full_val->type == RAY_LIST;
int full_is_row_aligned =
is_indexable && full_val->len == eval_nrows;
if (refs_column && !full_is_row_aligned) {
ray_release(full_val);
ray_release(list_col);
ray_t* per_group = nonagg_eval_per_group(
val_expr_item, eval_tbl, groups, n_groups);
if (RAY_IS_ERR(per_group)) {
for (int ai = 0; ai < n_agg_out; ai++) { if (agg_results[ai]) ray_release(agg_results[ai]); }
scratch_free(aggnames_hdr); scratch_free(aggres_hdr);
ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return per_group;
}
agg_names[n_agg_out] = kid;
agg_results[n_agg_out] = per_group;
n_agg_out++;
continue;
}
ray_t** gi_items = (ray_t**)ray_data(groups);
for (int64_t gi = 0; gi < n_groups; gi++) {
ray_t* idx_list = gi_items[gi * 2 + 1];
ray_t* cell;
if (full_is_row_aligned) {
cell = gather_by_idx(full_val,
(int64_t*)ray_data(idx_list), idx_list->len);
} else {
ray_retain(full_val);
cell = full_val;
}
list_out[gi] = cell;
list_col->len = gi + 1;
}
ray_release(full_val);
agg_names[n_agg_out] = kid;
agg_results[n_agg_out] = list_col;
n_agg_out++;
}
}
ray_t* result = ray_table_new(1 + n_agg_out);
if (RAY_IS_ERR(result)) { for (int ai = 0; ai < n_agg_out; ai++) { if (agg_results[ai]) ray_release(agg_results[ai]); } scratch_free(aggnames_hdr); scratch_free(aggres_hdr); ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result; }
ray_t** grp_items = (ray_t**)ray_data(groups);
ray_t* key_col_src = ray_table_get_col(eval_tbl, by_key_sym);
{
int8_t ktype = key_col_src ? key_col_src->type : RAY_I64;
if (RAY_IS_PARTED(ktype)) ktype = (int8_t)RAY_PARTED_BASETYPE(ktype);
ray_t* key_vec;
if (ktype == RAY_STR) {
key_vec = ray_vec_new(RAY_STR, n_groups);
for (int64_t gi = 0; gi < n_groups && key_vec && !RAY_IS_ERR(key_vec); gi++) {
ray_t* k = grp_items[gi * 2];
const char* sp = ray_str_ptr(k);
size_t slen = ray_str_len(k);
key_vec = ray_str_vec_append(key_vec, sp ? sp : "", sp ? slen : 0);
}
} else if (ktype == RAY_LIST) {
key_vec = ray_list_new(n_groups);
for (int64_t gi = 0; gi < n_groups && key_vec && !RAY_IS_ERR(key_vec); gi++)
key_vec = ray_list_append(key_vec, grp_items[gi * 2]);
} else {
uint8_t kattrs = key_col_src ? key_col_src->attrs : 0;
if (ktype == RAY_SYM)
key_vec = ray_sym_vec_new(kattrs & RAY_SYM_W_MASK, n_groups);
else
key_vec = ray_vec_new(ktype, n_groups);
if (key_vec && !RAY_IS_ERR(key_vec)) {
key_vec->len = n_groups;
memset(ray_data(key_vec), 0, (size_t)n_groups * ray_sym_elem_size(ktype, key_vec->attrs));
for (int64_t gi = 0; gi < n_groups; gi++)
store_typed_elem(key_vec, gi, grp_items[gi * 2]);
}
}
if (!key_vec || RAY_IS_ERR(key_vec)) {
for (int i = 0; i < n_agg_out; i++) { if (agg_results[i]) ray_release(agg_results[i]); }
scratch_free(aggnames_hdr); scratch_free(aggres_hdr);
ray_release(result); ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return key_vec ? key_vec : ray_error("oom", NULL);
}
result = ray_table_add_col(result, key_out_name, key_vec);
ray_release(key_vec);
}
for (int i = 0; i < n_agg_out; i++) {
if (agg_results[i])
result = ray_table_add_col(result, agg_names[i], agg_results[i]);
if (agg_results[i]) ray_release(agg_results[i]);
}
scratch_free(aggnames_hdr); scratch_free(aggres_hdr);
if (n_agg_out == 0 && n_groups > 0) {
ray_t** gi_items = (ray_t**)ray_data(groups);
int64_t fi_stack[256];
ray_t* fi_hdr = NULL;
int64_t* fi = (n_groups <= 256) ? fi_stack : NULL;
if (!fi) {
fi_hdr = ray_alloc((size_t)n_groups * sizeof(int64_t));
if (!fi_hdr) { ray_release(result); ray_release(groups); if (eval_tbl != tbl) ray_release(eval_tbl); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL); }
fi = (int64_t*)ray_data(fi_hdr);
}
for (int64_t gi = 0; gi < n_groups; gi++) {
ray_t* il = gi_items[gi * 2 + 1];
int a = 0; ray_t* i0 = collection_elem(il, 0, &a);
fi[gi] = as_i64(i0);
if (a) ray_release(i0);
}
int64_t nc = ray_table_ncols(eval_tbl);
for (int64_t c = 0; c < nc && !RAY_IS_ERR(result); c++) {
int64_t cn = ray_table_col_name(eval_tbl, c);
if (cn == by_key_sym) continue;
ray_t* sc = ray_table_get_col_idx(eval_tbl, c);
ray_t* dst = NULL;
if (sc->type == RAY_STR) {
dst = ray_vec_new(RAY_STR, n_groups);
bool src_has_nulls = ray_vec_may_have_nulls(sc);
if (src_has_nulls) {
for (int64_t gi = 0; gi < n_groups && dst && !RAY_IS_ERR(dst); gi++) {
if (ray_vec_is_null(sc, fi[gi])) {
dst = ray_str_vec_append(dst, "", 0);
if (dst && !RAY_IS_ERR(dst))
ray_vec_set_null(dst, dst->len - 1, true);
} else {
size_t slen = 0;
const char* sp = ray_str_vec_get(sc, fi[gi], &slen);
dst = ray_str_vec_append(dst, sp ? sp : "", sp ? slen : 0);
}
}
} else {
for (int64_t gi = 0; gi < n_groups && dst && !RAY_IS_ERR(dst); gi++) {
size_t slen = 0;
const char* sp = ray_str_vec_get(sc, fi[gi], &slen);
dst = ray_str_vec_append(dst, sp ? sp : "", sp ? slen : 0);
}
}
} else if (sc->type == RAY_LIST) {
dst = ray_alloc(n_groups * sizeof(ray_t*));
if (dst) {
dst->type = RAY_LIST; dst->len = n_groups;
ray_t** dout = (ray_t**)ray_data(dst);
ray_t** sitems = (ray_t**)ray_data(sc);
for (int64_t gi = 0; gi < n_groups; gi++) { dout[gi] = sitems[fi[gi]]; ray_retain(dout[gi]); }
}
} else {
dst = ray_vec_new(sc->type, n_groups);
if (dst && !RAY_IS_ERR(dst)) {
dst->len = n_groups;
for (int64_t gi = 0; gi < n_groups; gi++) {
int a = 0; ray_t* v = collection_elem(sc, fi[gi], &a);
store_typed_elem(dst, gi, v);
if (a) ray_release(v);
}
}
}
if (!dst || RAY_IS_ERR(dst)) {
if (dst) ray_release(dst);
ray_release(result);
result = ray_error("oom", NULL);
break;
}
result = ray_table_add_col(result, cn, dst);
ray_release(dst);
}
if (fi_hdr) ray_free(fi_hdr);
}
ray_release(groups);
if (eval_tbl != tbl) ray_release(eval_tbl);
ray_release(tbl);
result = apply_sort_take(result, dict_elems, dict_n,
asc_id, desc_id, take_id, NULL);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result;
}
int table_is_parted = 0;
{
int64_t ncols = ray_table_ncols(tbl);
for (int64_t c = 0; c < ncols; c++) {
ray_t* col = ray_table_get_col_idx(tbl, c);
if (col && RAY_IS_PARTED(col->type)) { table_is_parted = 1; break; }
}
}
int has_nonagg_needing_flat = 0;
int n_grp_agg_outputs = 0;
int has_cd_output = 0;
int sg_shapes_ok = 1;
{
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id) continue;
ray_t* expr = dict_elems[i + 1];
if (is_single_group_key_projection(by_expr, expr))
continue;
if (grouped_output_nested_agg(expr, false)) {
ray_t* nm = ray_sym_str(kid);
for (int ci = 0; ci < n_compound; ci++)
ray_release(compound_rw[ci]);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv);
return ray_error("domain",
"select by: output `%.*s` aggregates an aggregate of the group",
nm ? (int)ray_str_len(nm) : 1, nm ? ray_str_ptr(nm) : "?");
}
if (is_group_dag_agg_expr_dag_safe(expr, tbl)) {
n_grp_agg_outputs++;
if (!sg_agg_expr_ok(expr)) sg_shapes_ok = 0;
continue;
}
ray_t* cd_inner = match_count_distinct(expr);
int is_simple_cd = cd_inner && cd_inner->type == -RAY_SYM &&
!(cd_inner->attrs & ATTR_QUOTED);
if (is_simple_cd) { has_cd_output = 1; continue; }
if (n_compound < n_out_max) {
int h_before = n_hidden_aggs;
ray_t* rw = try_decompose_agg_arith(
expr, tbl, hidden_agg_exprs, hidden_agg_names,
&n_hidden_aggs, (int)hidden_max);
if (rw) {
for (int hi = h_before; hi < n_hidden_aggs; hi++)
if (!sg_agg_expr_ok(hidden_agg_exprs[hi]))
sg_shapes_ok = 0;
compound_pos[n_compound] = i;
compound_names[n_compound] = kid;
compound_rw[n_compound] = rw;
n_compound++;
n_grp_agg_outputs++;
continue;
}
}
has_nonagg_needing_flat = 1;
}
}
if (has_nonagg_needing_flat && table_is_parted) {
ray_t* flat_tbl = ray_table_new(ray_table_ncols(tbl));
if (!flat_tbl || RAY_IS_ERR(flat_tbl)) {
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return flat_tbl ? flat_tbl : ray_error("oom", NULL);
}
int64_t ncols = ray_table_ncols(tbl);
for (int64_t c = 0; c < ncols; c++) {
ray_t* col = ray_table_get_col_idx(tbl, c);
int64_t name = ray_table_col_name(tbl, c);
ray_t* flat_col = query_materialize_parted_col(col);
if (!flat_col || RAY_IS_ERR(flat_col)) {
ray_release(flat_tbl); ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return flat_col ? flat_col : ray_error("oom", NULL);
}
flat_tbl = ray_table_add_col(flat_tbl, name, flat_col);
ray_release(flat_col);
if (!flat_tbl || RAY_IS_ERR(flat_tbl)) {
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return flat_tbl ? flat_tbl : ray_error("oom", NULL);
}
}
ray_graph_free(g);
ray_release(tbl);
tbl = flat_tbl;
g = ray_graph_new(tbl);
if (!g) { ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL); }
root = ray_const_table(g, tbl);
if (where_expr) {
ray_t* pred_err = NULL;
ray_op_t* pred = compile_where_predicate(g, tbl, where_expr,
"select", &pred_err);
if (!pred) {
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv);
return pred_err ? pred_err :
ray_error("domain", "select: failed to evaluate `where:` predicate");
}
root = ray_filter(g, root, pred);
}
table_is_parted = 0;
}
if (!parted_bydict_deferred && by_expr->type == RAY_LIST) {
ray_t* vres = derived_key_vocab_aggs(tbl, by_expr, where_expr, dict_elems, dict_n,
from_id, by_id, where_id, take_id,
asc_id, desc_id, nearest_id);
if (vres) {
ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv);
return vres;
}
}
if (where_expr) {
bool can_fuse = !has_nonagg_needing_flat &&
(!table_is_parted || !has_cd_output);
if (can_fuse) {
root = ray_optimize(g, root);
bool sg_armed = false;
if (!has_cd_output && n_grp_agg_outputs > 0 && sg_shapes_ok) {
int64_t by_name = -1;
if (by_expr->type == -RAY_SYM && !(by_expr->attrs & ATTR_QUOTED))
by_name = by_expr->i64;
else if (by_expr->type == RAY_SYM && ray_len(by_expr) == 1)
by_name = ((int64_t*)ray_data(by_expr))[0];
ray_t* by_col = (by_name >= 0)
? ray_table_get_col(tbl, by_name) : NULL;
if (by_col)
sg_armed = ray_slice_group_probe(g, root, by_col);
}
if (sg_armed) goto where_done;
ray_t* fres = exec_node(g, root);
if (!fres || RAY_IS_ERR(fres)) {
if (g->selection) {
ray_release(g->selection);
g->selection = NULL;
}
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return fres ? fres : ray_error("domain", "select: `where:` filter produced no result");
}
ray_release(fres);
if (g->selection) {
saved_selection = g->selection;
ray_retain(saved_selection);
}
} else {
root = ray_optimize(g, root);
ray_t* fres = ray_execute(g, root);
ray_graph_free(g); g = NULL;
if (!fres || RAY_IS_ERR(fres)) { ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return fres ? fres : ray_error("domain", "select: `where:` filter produced no result"); }
if (ray_is_lazy(fres)) fres = ray_lazy_materialize(fres);
if (!fres || RAY_IS_ERR(fres)) { ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return fres ? fres : ray_error("domain", "select: failed to materialize filtered result"); }
ray_release(tbl);
tbl = fres;
g = ray_graph_new(tbl);
if (!g) { ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL); }
root = ray_const_table(g, tbl);
}
where_done: ;
}
int64_t n_keys = 0;
if (parted_bydict_deferred) {
DICT_VIEW_DECL(dfv);
DICT_VIEW_OPEN(deferred_bydict, dfv);
if (DICT_VIEW_OVERFLOW(dfv)) {
DICT_VIEW_CLOSE(dfv); ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
for (int64_t i = 0; i < deferred_nk && n_keys < nk_max; i++) {
key_ops[n_keys] = compile_expr_dag(g, dfv[i * 2 + 1]);
if (!key_ops[n_keys]) { ray_t* cerr = graph_take_compile_err(g); DICT_VIEW_CLOSE(dfv); ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return cerr ? cerr : ray_error("domain", "select by: failed to compile group key expression"); }
n_keys++;
}
DICT_VIEW_CLOSE(dfv);
} else if (by_expr->type == RAY_SYM) {
int64_t nk = ray_len(by_expr);
for (int64_t i = 0; i < nk && n_keys < nk_max; i++) {
ray_t* name_str = ray_sym_vec_cell(by_expr, i);
if (!name_str) { ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain", "select by: unknown group key symbol"); }
key_ops[n_keys] = ray_scan(g, ray_str_ptr(name_str));
if (!key_ops[n_keys]) { ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain", "select by: group key column not found"); }
n_keys++;
}
} else {
ray_t* dom_key = derived_key_over_sym_domain(by_expr, tbl, g->selection);
if (dom_key) {
key_ops[0] = ray_const_vec(g, dom_key);
ray_release(dom_key);
} else {
key_ops[0] = compile_expr_dag(g, by_expr);
}
computed_single_key = (key_ops[0] != NULL && by_expr->type == RAY_LIST);
if (!key_ops[0]) { ray_t* cerr = graph_take_compile_err(g); ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return cerr ? cerr : ray_error("domain", "select by: failed to compile group key expression"); }
n_keys = 1;
}
for (int64_t ki = 0; ki < n_keys; ki++)
key_ids[ki] = key_ops[ki]->id;
int64_t n_aggs = 0;
int has_binary_agg = 0;
int has_agg_k = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id || kid == take_id || kid == asc_id || kid == desc_id) continue;
ray_t* val_expr = dict_elems[i + 1];
if (is_group_dag_agg_expr_dag_safe(val_expr, tbl)) {
ray_t** agg_elems = (ray_t**)ray_data(val_expr);
uint16_t op = resolve_agg_opcode(agg_elems[0]->i64);
ray_t* agg_arg = agg_elems[1];
{
uint16_t new_op;
ray_t* new_arg;
if (simplify_agg_idiom(val_expr, tbl, &new_op, &new_arg)) {
op = new_op;
agg_arg = new_arg;
}
}
agg_ops[n_aggs] = op;
agg_ins[n_aggs] = compile_expr_dag(g, agg_arg);
agg_in_ids[n_aggs] = agg_ins[n_aggs] ? agg_ins[n_aggs]->id : RAY_OP_NONE;
if (!agg_ins[n_aggs]) {
ray_t* cerr = graph_take_compile_err(g);
ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv);
return cerr ? cerr : ray_error("domain", "select by: failed to compile aggregation argument");
}
agg_names[n_aggs] = kid;
if (agg_ins[n_aggs]->out_type > 0 &&
!agg_type_admitted(op, agg_ins[n_aggs]->out_type)) {
int8_t in_t = agg_ins[n_aggs]->out_type;
ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type", "select by: aggregation does not admit input type %s", ray_type_name(in_t));
}
agg_ins2[n_aggs] = NULL;
agg_in2_ids[n_aggs] = RAY_OP_NONE;
agg_k[n_aggs] = 0;
if (agg_is_binary_agg(op)) {
if (ray_len(val_expr) < 3) { ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("arity", "select by: binary aggregation requires two column arguments"); }
agg_ins2[n_aggs] = compile_expr_dag(g, agg_elems[2]);
agg_in2_ids[n_aggs] = agg_ins2[n_aggs] ? agg_ins2[n_aggs]->id : RAY_OP_NONE;
if (!agg_ins2[n_aggs]) { ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain", "select by: failed to compile binary aggregation second argument"); }
if (agg_ins2[n_aggs]->out_type > 0 &&
!agg_type_admitted(op, agg_ins2[n_aggs]->out_type)) {
int8_t in_t = agg_ins2[n_aggs]->out_type;
ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type", "select by: binary aggregation does not admit second input type %s", ray_type_name(in_t));
}
has_binary_agg = 1;
} else if (op == OP_TOP_N || op == OP_BOT_N) {
has_list_agg = 1;
if (ray_len(val_expr) < 3) { ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("arity", "select by: top/bot aggregation requires a K argument"); }
ray_t* k_expr = agg_elems[2];
int64_t k_val;
if (k_expr->type == -RAY_I64) k_val = k_expr->i64;
else if (k_expr->type == -RAY_I32) k_val = (int64_t)(int32_t)k_expr->i64;
else { ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type", "top/bot K must be integer literal"); }
if (k_val < 1) { ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("range", "top/bot K must be >= 1"); }
if (k_val > 1024) { ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("range", "top/bot K capped at 1024"); }
agg_k[n_aggs] = k_val;
has_agg_k = 1;
} else if (op == OP_QUANTILE) {
if (ray_len(val_expr) < 3) { ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("arity", "select by: quantile requires a probability argument"); }
double q_val = 0.0;
if (!quantile_literal_prob(agg_elems[2],
agg_name_is_percentile(agg_elems[0]->i64),
&q_val)) {
ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv);
return ray_error("domain", "select by: quantile probability must be a non-null numeric literal in range");
}
agg_k[n_aggs] = encode_agg_f64_param(q_val);
has_agg_k = 1;
}
n_aggs++;
} else {
if (is_single_group_key_projection(by_expr, val_expr))
continue;
{
bool pre_compound = false;
for (int ci = 0; ci < n_compound; ci++)
if (compound_pos[ci] == i) { pre_compound = true; break; }
if (pre_compound) continue;
}
nonagg_names[n_nonaggs] = kid;
nonagg_exprs[n_nonaggs] = val_expr;
n_nonaggs++;
}
}
n_aggs_real = n_aggs;
for (int hi = 0; hi < n_hidden_aggs; hi++) {
ray_t** he = (ray_t**)ray_data(hidden_agg_exprs[hi]);
uint16_t hop = resolve_agg_opcode(he[0]->i64);
agg_ops[n_aggs] = hop;
agg_ins[n_aggs] = compile_expr_dag(g, he[1]);
agg_in_ids[n_aggs] = agg_ins[n_aggs] ? agg_ins[n_aggs]->id : RAY_OP_NONE;
if (!agg_ins[n_aggs]) {
for (int ci = 0; ci < n_compound; ci++)
ray_release(compound_rw[ci]);
ray_t* cerr = graph_take_compile_err(g);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv);
return cerr ? cerr : ray_error("domain", "select by: failed to compile aggregation argument");
}
if (agg_ins[n_aggs]->out_type > 0 &&
!agg_type_admitted(hop, agg_ins[n_aggs]->out_type)) {
int8_t in_t = agg_ins[n_aggs]->out_type;
for (int ci = 0; ci < n_compound; ci++)
ray_release(compound_rw[ci]);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type", "select by: aggregation does not admit input type %s", ray_type_name(in_t));
}
agg_ins2[n_aggs] = NULL;
agg_in2_ids[n_aggs] = RAY_OP_NONE;
agg_k[n_aggs] = 0;
if (agg_is_binary_agg(hop)) {
agg_ins2[n_aggs] = compile_expr_dag(g, he[2]);
agg_in2_ids[n_aggs] = agg_ins2[n_aggs] ? agg_ins2[n_aggs]->id : RAY_OP_NONE;
if (!agg_ins2[n_aggs]) {
for (int ci = 0; ci < n_compound; ci++)
ray_release(compound_rw[ci]);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain", "select by: failed to compile binary aggregation second argument");
}
if (agg_ins2[n_aggs]->out_type > 0 &&
!agg_type_admitted(hop, agg_ins2[n_aggs]->out_type)) {
int8_t in_t = agg_ins2[n_aggs]->out_type;
for (int ci = 0; ci < n_compound; ci++)
ray_release(compound_rw[ci]);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type", "select by: binary aggregation does not admit second input type %s", ray_type_name(in_t));
}
has_binary_agg = 1;
} else if (hop == OP_TOP_N || hop == OP_BOT_N) {
has_list_agg = 1;
if (ray_len(hidden_agg_exprs[hi]) < 3) {
for (int ci = 0; ci < n_compound; ci++)
ray_release(compound_rw[ci]);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("arity", "select by: top/bot aggregation requires a K argument");
}
int64_t k_val = 0;
ray_t* k_expr = he[2];
if (k_expr->type == -RAY_I64) k_val = k_expr->i64;
else if (k_expr->type == -RAY_I32) k_val = (int64_t)(int32_t)k_expr->i64;
else {
for (int ci = 0; ci < n_compound; ci++)
ray_release(compound_rw[ci]);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type", "top/bot K must be integer literal");
}
if (k_val < 1 || k_val > 1024) {
for (int ci = 0; ci < n_compound; ci++)
ray_release(compound_rw[ci]);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("range", "top/bot K must be between 1 and 1024");
}
agg_k[n_aggs] = k_val;
has_agg_k = 1;
} else if (hop == OP_QUANTILE) {
if (ray_len(hidden_agg_exprs[hi]) < 3) {
for (int ci = 0; ci < n_compound; ci++)
ray_release(compound_rw[ci]);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("arity", "select by: quantile requires a probability argument");
}
double q_val = 0.0;
if (!quantile_literal_prob(he[2],
agg_name_is_percentile(he[0]->i64),
&q_val)) {
for (int ci = 0; ci < n_compound; ci++)
ray_release(compound_rw[ci]);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain", "select by: quantile probability must be a non-null numeric literal in range");
}
agg_k[n_aggs] = encode_agg_f64_param(q_val);
has_agg_k = 1;
}
n_aggs++;
}
for (int64_t ki = 0; ki < n_keys; ki++)
key_ops[ki] = &g->nodes[key_ids[ki]];
for (int64_t ai = 0; ai < n_aggs; ai++) {
agg_ins[ai] = &g->nodes[agg_in_ids[ai]];
agg_ins2[ai] = agg_in2_ids[ai] != RAY_OP_NONE ? &g->nodes[agg_in2_ids[ai]] : NULL;
}
if (n_aggs > 0 || n_nonaggs > 0) {
if (n_aggs > 0) {
if (has_agg_k || has_binary_agg) {
root = ray_group_build(g, key_ops, n_keys, agg_ops,
agg_ins,
has_binary_agg ? agg_ins2 : NULL,
has_agg_k ? agg_k : NULL,
n_aggs);
} else {
root = ray_group(g, key_ops, n_keys, agg_ops, agg_ins, n_aggs);
}
} else {
uint16_t cnt_op = OP_COUNT;
ray_op_t* cnt_in = key_ops[0];
root = ray_group(g, key_ops, n_keys, &cnt_op, &cnt_in, 1);
synth_count_col = 1;
}
} else {
ray_t* filtered_tbl = tbl;
if (where_expr) {
root = ray_optimize(g, root);
ray_t* fres = ray_execute(g, root);
ray_graph_free(g); g = NULL;
if (!fres || RAY_IS_ERR(fres)) { ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return fres ? fres : ray_error("domain", "select: `where:` filter produced no result"); }
if (ray_is_lazy(fres)) fres = ray_lazy_materialize(fres);
if (!fres || RAY_IS_ERR(fres)) { ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return fres ? fres : ray_error("domain", "select: failed to materialize filtered result"); }
filtered_tbl = fres;
g = ray_graph_new(filtered_tbl);
if (!g) { if (filtered_tbl != tbl) ray_release(filtered_tbl); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL); }
n_keys = 0;
if (by_expr->type == RAY_SYM) {
int64_t nk = ray_len(by_expr);
for (int64_t i = 0; i < nk && n_keys < nk_max; i++) {
ray_t* ns = ray_sym_vec_cell(by_expr, i);
if (ns) key_ops[n_keys++] = ray_scan(g, ray_str_ptr(ns));
}
} else {
key_ops[0] = compile_expr_dag(g, by_expr);
if (key_ops[0]) n_keys = 1;
}
}
uint16_t cnt_op = OP_COUNT;
ray_op_t* cnt_in = key_ops[0];
root = ray_group(g, key_ops, n_keys, &cnt_op, &cnt_in, 1);
root = ray_optimize(g, root);
ray_t* grouped = ray_execute(g, root);
ray_graph_free(g); g = NULL;
if (!grouped || RAY_IS_ERR(grouped)) { if (filtered_tbl != tbl) ray_release(filtered_tbl); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return grouped; }
if (ray_is_lazy(grouped)) grouped = ray_lazy_materialize(grouped);
int64_t n_groups = ray_table_nrows(grouped);
int64_t key_sym = -1;
if (by_expr->type == -RAY_SYM && !(by_expr->attrs & ATTR_QUOTED)
&& !ray_sym_is_dotted(by_expr->i64))
key_sym = by_expr->i64;
else if (by_expr->type == RAY_SYM && ray_len(by_expr) == 1)
key_sym = ((int64_t*)ray_data(by_expr))[0];
if (n_groups == 0) {
ray_release(grouped);
int64_t nc0 = ray_table_ncols(filtered_tbl);
ray_t* empty = ray_table_new(nc0 + 1);
if (!RAY_IS_ERR(empty)) {
int64_t empty_key_name = key_sym;
ray_t* empty_key_vec = NULL;
if (key_sym >= 0) {
ray_t* sc = ray_table_get_col(filtered_tbl, key_sym);
if (sc) {
empty_key_vec = ray_group_gather(sc, NULL, 0);
}
} else {
int64_t ck_name = -1;
int64_t ck_full = -1;
int64_t ck_head = -1;
if (by_expr->type == -RAY_SYM && !(by_expr->attrs & ATTR_QUOTED)) {
ck_full = by_expr->i64;
if (ray_sym_is_dotted(by_expr->i64)) {
const int64_t* segs;
int nsegs = ray_sym_segs(by_expr->i64, &segs);
if (nsegs > 0) { ck_name = segs[nsegs - 1]; ck_head = segs[0]; }
} else {
ck_name = by_expr->i64;
}
} else if (by_expr->type == RAY_LIST && by_expr->len >= 2) {
ray_t** be = (ray_t**)ray_data(by_expr);
for (int64_t i = by_expr->len - 1; i >= 1; i--) {
if (be[i]->type == -RAY_SYM && !(be[i]->attrs & ATTR_QUOTED)) {
ck_name = be[i]->i64;
break;
}
}
}
if (ck_name < 0) ck_name = ray_sym_intern("key", 3);
if (ck_head >= 0 && ck_full >= 0 && ck_name != ck_full) {
for (int64_t c = 0; c < nc0; c++) {
int64_t cn = ray_table_col_name(filtered_tbl, c);
if (cn == ck_name && cn != ck_head) {
ck_name = ck_full;
break;
}
}
}
empty_key_name = ck_name;
ray_env_push_query_scope();
for (int64_t c = 0; c < nc0; c++) {
ray_env_set_query_local(ray_table_col_name(filtered_tbl, c),
ray_table_get_col_idx(filtered_tbl, c));
}
ray_t* ck_vec = ray_eval(by_expr);
ray_env_pop_scope();
if (ck_vec && !RAY_IS_ERR(ck_vec) && ray_is_vec(ck_vec)) {
int8_t kt = ck_vec->type;
empty_key_vec = (kt == RAY_STR)
? ray_vec_new(RAY_STR, 0)
: (kt == RAY_LIST)
? ray_list_new(0)
: ray_vec_new(kt, 0);
}
if (ck_vec && !RAY_IS_ERR(ck_vec)) ray_release(ck_vec);
}
if (empty_key_vec && !RAY_IS_ERR(empty_key_vec)) {
empty = ray_table_add_col(empty, empty_key_name, empty_key_vec);
ray_release(empty_key_vec);
}
for (int64_t c = 0; c < nc0; c++) {
int64_t cn = ray_table_col_name(filtered_tbl, c);
if (cn == empty_key_name) continue;
ray_t* sc = ray_table_get_col_idx(filtered_tbl, c);
ray_t* ev = (sc->type == RAY_STR) ? ray_vec_new(RAY_STR, 0) :
(sc->type == RAY_LIST) ? ray_list_new(0) :
ray_vec_new(sc->type, 0);
if (!RAY_IS_ERR(ev)) { empty = ray_table_add_col(empty, cn, ev); ray_release(ev); }
}
}
if (filtered_tbl != tbl) ray_release(filtered_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return empty;
}
if (key_sym < 0) {
ray_release(grouped);
int64_t tbl_ncols = ray_table_ncols(filtered_tbl);
ray_env_push_query_scope();
for (int64_t c = 0; c < tbl_ncols; c++) {
int64_t cn = ray_table_col_name(filtered_tbl, c);
ray_t* cv = ray_table_get_col_idx(filtered_tbl, c);
ray_env_set_query_local(cn, cv);
}
ray_t* computed_key = derived_key_over_sym_domain(by_expr, filtered_tbl, NULL);
if (!computed_key) computed_key = ray_eval(by_expr);
ray_env_pop_scope();
if (!computed_key || RAY_IS_ERR(computed_key)) {
if (filtered_tbl != tbl) ray_release(filtered_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return computed_key ? computed_key : ray_error("domain", "select by: failed to evaluate group key expression");
}
ray_t* groups2_dict = ray_group_indices_fn(computed_key);
if (!groups2_dict || RAY_IS_ERR(groups2_dict)) {
ray_release(computed_key);
if (filtered_tbl != tbl) ray_release(filtered_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return groups2_dict ? groups2_dict : ray_error("domain", "select by: failed to compute groups");
}
ray_t* groups2 = groups_to_pair_list(groups2_dict);
ray_release(groups2_dict);
if (RAY_IS_ERR(groups2)) {
ray_release(computed_key);
if (filtered_tbl != tbl) ray_release(filtered_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return groups2;
}
int64_t ng2 = ray_len(groups2) / 2;
if (ng2 == 0) { ray_release(groups2); ray_release(computed_key); if (filtered_tbl != tbl) ray_release(filtered_tbl); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_table_new(0); }
ray_t** gi2 = (ray_t**)ray_data(groups2);
int64_t fi2_stack[256];
ray_t* fi2_hdr = NULL;
int64_t* fi2 = fi2_stack;
if (ng2 > 256) {
fi2_hdr = ray_alloc((size_t)ng2 * sizeof(int64_t));
if (!fi2_hdr) {
ray_release(groups2); ray_release(computed_key);
if (filtered_tbl != tbl) ray_release(filtered_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
fi2 = (int64_t*)ray_data(fi2_hdr);
}
for (int64_t g2 = 0; g2 < ng2; g2++) {
int alloc2 = 0;
ray_t* i02 = collection_elem(gi2[g2 * 2 + 1], 0, &alloc2);
fi2[g2] = as_i64(i02);
if (alloc2) ray_release(i02);
}
int64_t ckey_name = -1;
int64_t ckey_full = -1;
int64_t ckey_head = -1;
if (by_expr->type == -RAY_SYM && !(by_expr->attrs & ATTR_QUOTED)) {
ckey_full = by_expr->i64;
if (ray_sym_is_dotted(by_expr->i64)) {
const int64_t* segs;
int nsegs = ray_sym_segs(by_expr->i64, &segs);
if (nsegs > 0) {
ckey_name = segs[nsegs - 1];
ckey_head = segs[0];
}
} else {
ckey_name = by_expr->i64;
}
} else if (by_expr->type == RAY_LIST && by_expr->len >= 2) {
ray_t** be = (ray_t**)ray_data(by_expr);
for (int64_t i = by_expr->len - 1; i >= 1; i--) {
if (be[i]->type == -RAY_SYM && !(be[i]->attrs & ATTR_QUOTED)) {
ckey_name = be[i]->i64;
break;
}
}
}
if (ckey_name < 0) ckey_name = ray_sym_intern("key", 3);
if (ckey_head >= 0 && ckey_full >= 0 && ckey_name != ckey_full) {
for (int64_t c = 0; c < tbl_ncols; c++) {
int64_t cn = ray_table_col_name(filtered_tbl, c);
if (cn == ckey_name && cn != ckey_head) {
ckey_name = ckey_full;
break;
}
}
}
ray_t* res2 = ray_table_new(tbl_ncols + 1);
if (ray_is_vec(computed_key)) {
ray_t* kv = ray_vec_new(computed_key->type, ng2);
if (!RAY_IS_ERR(kv)) {
kv->len = ng2;
for (int64_t g2 = 0; g2 < ng2; g2++) {
int a2 = 0;
ray_t* v2 = collection_elem(computed_key, fi2[g2], &a2);
store_typed_elem(kv, g2, v2);
if (a2) ray_release(v2);
}
res2 = ray_table_add_col(res2, ckey_name, kv);
ray_release(kv);
}
}
for (int64_t c = 0; c < tbl_ncols; c++) {
int64_t cn = ray_table_col_name(filtered_tbl, c);
if (cn == ckey_name) continue;
ray_t* sc = ray_table_get_col_idx(filtered_tbl, c);
ray_t* dc = ray_vec_new(sc->type, ng2);
dc->len = ng2;
for (int64_t g2 = 0; g2 < ng2; g2++) { int a2 = 0; ray_t* v2 = collection_elem(sc, fi2[g2], &a2); store_typed_elem(dc, g2, v2); if (a2) ray_release(v2); }
res2 = ray_table_add_col(res2, cn, dc); ray_release(dc);
}
if (fi2_hdr) ray_free(fi2_hdr);
ray_release(groups2); ray_release(computed_key);
if (filtered_tbl != tbl) ray_release(filtered_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return res2;
}
ray_t* orig_key_col = ray_table_get_col(filtered_tbl, key_sym);
int64_t nrows_orig = orig_key_col ? orig_key_col->len : 0;
ray_t* grp_key_col = ray_table_get_col(grouped, key_sym);
int8_t kt = orig_key_col ? orig_key_col->type : 0;
int64_t gk_stack[256];
ray_t* gk_heap_hdr = NULL;
int64_t* gk_vals = gk_stack;
if (n_groups > 256) {
gk_heap_hdr = ray_alloc((size_t)n_groups * sizeof(int64_t));
if (!gk_heap_hdr) { ray_release(grouped); if (filtered_tbl != tbl) ray_release(filtered_tbl); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL); }
gk_vals = (int64_t*)ray_data(gk_heap_hdr);
}
uint8_t gk_null_stack[256];
ray_t* gk_null_hdr = NULL;
uint8_t* gk_null = gk_null_stack;
if (n_groups > 256) {
gk_null_hdr = ray_alloc((size_t)n_groups * sizeof(uint8_t));
if (!gk_null_hdr) {
if (gk_heap_hdr) ray_free(gk_heap_hdr);
ray_release(grouped);
if (filtered_tbl != tbl) ray_release(filtered_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
gk_null = (uint8_t*)ray_data(gk_null_hdr);
}
memset(gk_null, 0, (size_t)n_groups * sizeof(uint8_t));
if (grp_key_col) {
bool gk_has_nulls = ray_vec_may_have_nulls(grp_key_col);
if (gk_has_nulls) {
for (int64_t gi = 0; gi < n_groups; gi++) {
if (kt == RAY_F64) {
double dv = clear_neg_zero(((double*)ray_data(grp_key_col))[gi]);
memcpy(&gk_vals[gi], &dv, 8);
} else
gk_vals[gi] = read_col_i64(ray_data(grp_key_col), gi, kt, grp_key_col->attrs);
if (ray_vec_is_null(grp_key_col, gi))
gk_null[gi] = 1;
}
} else {
for (int64_t gi = 0; gi < n_groups; gi++) {
if (kt == RAY_F64) {
double dv = clear_neg_zero(((double*)ray_data(grp_key_col))[gi]);
memcpy(&gk_vals[gi], &dv, 8);
} else
gk_vals[gi] = read_col_i64(ray_data(grp_key_col), gi, kt, grp_key_col->attrs);
}
}
}
ray_release(grouped);
int64_t first_idx_stack[256];
ray_t* fi_heap_hdr = NULL;
int64_t* first_idx = first_idx_stack;
if (n_groups > 256) {
fi_heap_hdr = ray_alloc((size_t)n_groups * sizeof(int64_t));
if (!fi_heap_hdr) { if (gk_heap_hdr) ray_free(gk_heap_hdr); if (filtered_tbl != tbl) ray_release(filtered_tbl); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL); }
first_idx = (int64_t*)ray_data(fi_heap_hdr);
}
for (int64_t gi = 0; gi < n_groups; gi++) first_idx[gi] = -1;
{
uint32_t fi_cap = 64;
while ((uint64_t)fi_cap < (uint64_t)n_groups * 2 && fi_cap < (1u << 30))
fi_cap <<= 1;
uint32_t fi_mask = fi_cap - 1;
ray_t* fi_ht_hdr = ray_alloc((size_t)fi_cap * sizeof(uint32_t));
if (!fi_ht_hdr) {
if (gk_heap_hdr) ray_free(gk_heap_hdr);
if (fi_heap_hdr) ray_free(fi_heap_hdr);
if (filtered_tbl != tbl) ray_release(filtered_tbl);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
uint32_t* fi_ht = (uint32_t*)ray_data(fi_ht_hdr);
memset(fi_ht, 0xFF, (size_t)fi_cap * sizeof(uint32_t));
for (int64_t gi = 0; gi < n_groups; gi++) {
uint64_t h;
if (gk_null[gi]) {
h = ray_hash_i64((int64_t)0xDEADBEEFCAFEBABEULL);
} else if (kt == RAY_F64) {
double dv;
memcpy(&dv, &gk_vals[gi], 8);
h = ray_hash_f64(dv);
} else {
h = ray_hash_i64(gk_vals[gi]);
}
uint32_t slot = (uint32_t)(h & fi_mask);
while (fi_ht[slot] != UINT32_MAX) slot = (slot + 1) & fi_mask;
fi_ht[slot] = (uint32_t)gi;
}
bool orig_nulls_flag = orig_key_col
&& ray_vec_may_have_nulls(orig_key_col);
int64_t found = 0;
for (int64_t r = 0; r < nrows_orig && found < n_groups; r++) {
bool r_null = orig_nulls_flag && ray_vec_is_null(orig_key_col, r);
int64_t ov;
if (kt == RAY_F64) {
double dv = clear_neg_zero(((double*)ray_data(orig_key_col))[r]);
memcpy(&ov, &dv, 8);
} else ov = read_col_i64(ray_data(orig_key_col), r, kt, orig_key_col->attrs);
uint64_t h;
if (r_null) {
h = ray_hash_i64((int64_t)0xDEADBEEFCAFEBABEULL);
} else if (kt == RAY_F64) {
double dv;
memcpy(&dv, &ov, 8);
h = ray_hash_f64(dv);
} else {
h = ray_hash_i64(ov);
}
uint32_t slot = (uint32_t)(h & fi_mask);
while (fi_ht[slot] != UINT32_MAX) {
uint32_t cand = fi_ht[slot];
bool match = (r_null && gk_null[cand])
|| (!r_null && !gk_null[cand] && gk_vals[cand] == ov);
if (match) {
if (first_idx[cand] < 0) {
first_idx[cand] = r;
found++;
}
break;
}
slot = (slot + 1) & fi_mask;
}
}
ray_free(fi_ht_hdr);
}
if (gk_null_hdr) ray_free(gk_null_hdr);
if (gk_heap_hdr) ray_free(gk_heap_hdr);
int64_t ncols = ray_table_ncols(filtered_tbl);
ray_t* result = ray_table_new(ncols);
if (RAY_IS_ERR(result)) { if (fi_heap_hdr) ray_free(fi_heap_hdr); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result; }
ray_t* key_vec_src = ray_table_get_col(filtered_tbl, key_sym);
if (key_vec_src->type == RAY_STR) {
ray_t* key_vec_dst = ray_vec_new(RAY_STR, n_groups);
if (!key_vec_dst || RAY_IS_ERR(key_vec_dst)) { if (fi_heap_hdr) ray_free(fi_heap_hdr); ray_release(tbl); ray_release(result); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return key_vec_dst ? key_vec_dst : ray_error("oom", NULL); }
for (int64_t gi = 0; gi < n_groups; gi++) {
size_t slen = 0;
const char* sp = ray_str_vec_get(key_vec_src, first_idx[gi], &slen);
key_vec_dst = ray_str_vec_append(key_vec_dst, sp ? sp : "", sp ? slen : 0);
if (RAY_IS_ERR(key_vec_dst)) { if (fi_heap_hdr) ray_free(fi_heap_hdr); ray_release(tbl); ray_release(result); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return key_vec_dst; }
}
result = ray_table_add_col(result, key_sym, key_vec_dst);
ray_release(key_vec_dst);
} else {
ray_t* key_vec_dst = ray_vec_new(key_vec_src->type, n_groups);
if (RAY_IS_ERR(key_vec_dst)) { if (fi_heap_hdr) ray_free(fi_heap_hdr); ray_release(tbl); ray_release(result); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return key_vec_dst; }
key_vec_dst->len = n_groups;
for (int64_t gi = 0; gi < n_groups; gi++) {
int alloc = 0;
ray_t* val = collection_elem(key_vec_src, first_idx[gi], &alloc);
store_typed_elem(key_vec_dst, gi, val);
if (alloc) ray_release(val);
}
result = ray_table_add_col(result, key_sym, key_vec_dst);
ray_release(key_vec_dst);
}
for (int64_t c = 0; c < ncols; c++) {
int64_t col_name = ray_table_col_name(filtered_tbl, c);
if (col_name == key_sym) continue;
ray_t* src_col = ray_table_get_col_idx(filtered_tbl, c);
int8_t ct = src_col->type;
if (ct == RAY_STR) {
ray_t* dst = ray_vec_new(RAY_STR, n_groups);
if (!dst || RAY_IS_ERR(dst)) { if (fi_heap_hdr) ray_free(fi_heap_hdr); ray_release(tbl); ray_release(result); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return dst ? dst : ray_error("oom", NULL); }
for (int64_t gi = 0; gi < n_groups; gi++) {
size_t slen = 0;
const char* sp = ray_str_vec_get(src_col, first_idx[gi], &slen);
dst = ray_str_vec_append(dst, sp ? sp : "", sp ? slen : 0);
if (RAY_IS_ERR(dst)) { if (fi_heap_hdr) ray_free(fi_heap_hdr); ray_release(tbl); ray_release(result); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return dst; }
}
result = ray_table_add_col(result, col_name, dst);
ray_release(dst);
} else if (ct == RAY_LIST) {
ray_t* dst = ray_alloc(n_groups * sizeof(ray_t*));
if (!dst) { if (fi_heap_hdr) ray_free(fi_heap_hdr); ray_release(tbl); ray_release(result); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL); }
dst->type = RAY_LIST;
dst->len = n_groups;
ray_t** dout = (ray_t**)ray_data(dst);
ray_t** src_items = (ray_t**)ray_data(src_col);
for (int64_t gi = 0; gi < n_groups; gi++) {
dout[gi] = src_items[first_idx[gi]];
ray_retain(dout[gi]);
}
result = ray_table_add_col(result, col_name, dst);
ray_release(dst);
} else {
ray_t* dst = ray_vec_new(ct, n_groups);
if (RAY_IS_ERR(dst)) { if (fi_heap_hdr) ray_free(fi_heap_hdr); ray_release(tbl); ray_release(result); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return dst; }
dst->len = n_groups;
for (int64_t gi = 0; gi < n_groups; gi++) {
int alloc = 0;
ray_t* val = collection_elem(src_col, first_idx[gi], &alloc);
store_typed_elem(dst, gi, val);
if (alloc) ray_release(val);
}
result = ray_table_add_col(result, col_name, dst);
ray_release(dst);
}
if (RAY_IS_ERR(result)) { if (fi_heap_hdr) ray_free(fi_heap_hdr); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result; }
}
if (fi_heap_hdr) ray_free(fi_heap_hdr);
if (filtered_tbl != tbl) ray_release(filtered_tbl);
ray_release(tbl);
result = apply_sort_take(result, dict_elems, dict_n,
asc_id, desc_id, take_id, NULL);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result;
}
} else if (n_out > 0) {
int has_agg = 0;
int has_nonagg_out = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id || kid == nearest_id) continue;
if (is_agg_expr(dict_elems[i + 1])) has_agg = 1;
else has_nonagg_out = 1;
}
if (has_agg && !has_nonagg_out && !nearest_expr) {
if (where_expr) {
root = ray_optimize(g, root);
ray_t* fres = exec_node(g, root);
if (!fres || RAY_IS_ERR(fres)) {
if (g->selection) {
ray_release(g->selection);
g->selection = NULL;
}
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return fres ? fres : ray_error("domain", "select: `where:` filter produced no result");
}
ray_release(fres);
}
int64_t s_max = select_output_count(dict_elems, dict_n);
if (s_max < 1) s_max = 1;
ray_t* sagg_hdr = NULL;
ray_op_t** s_agg_ins = (ray_op_t**)scratch_alloc(&sagg_hdr,
(size_t)s_max * (2 * sizeof(ray_op_t*) + sizeof(uint16_t)));
if (!s_agg_ins) {
if (g->selection) { ray_release(g->selection); g->selection = NULL; }
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
ray_op_t** s_agg_ins2 = s_agg_ins + s_max;
uint16_t* s_agg_ops = (uint16_t*)(s_agg_ins2 + s_max);
int64_t s_n_aggs = 0;
int s_has_binary = 0;
int s_use_eval_fallback = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id || kid == nearest_id) continue;
ray_t* val_expr = dict_elems[i + 1];
ray_t** agg_elems = (ray_t**)ray_data(val_expr);
uint16_t op = resolve_agg_opcode(agg_elems[0]->i64);
s_agg_ops[s_n_aggs] = op;
s_agg_ins[s_n_aggs] = compile_expr_dag(g, agg_elems[1]);
s_agg_ins2[s_n_aggs] = NULL;
if (!s_agg_ins[s_n_aggs]) {
s_use_eval_fallback = 1;
break;
}
if (s_agg_ins[s_n_aggs]->out_type > 0 &&
!agg_type_admitted(op, s_agg_ins[s_n_aggs]->out_type)) {
int8_t in_t = s_agg_ins[s_n_aggs]->out_type;
if (g->selection) { ray_release(g->selection); g->selection = NULL; }
ray_graph_free(g); ray_release(tbl);
scratch_free(sagg_hdr);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type", "select: aggregation does not admit input type %s", ray_type_name(in_t));
}
if (agg_is_binary_agg(op)) {
if (ray_len(val_expr) < 3) {
if (g->selection) { ray_release(g->selection); g->selection = NULL; }
ray_graph_free(g); ray_release(tbl);
scratch_free(sagg_hdr);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("arity", "select: binary aggregation requires two column arguments");
}
s_agg_ins2[s_n_aggs] = compile_expr_dag(g, agg_elems[2]);
if (!s_agg_ins2[s_n_aggs]) {
s_use_eval_fallback = 1;
break;
}
if (s_agg_ins2[s_n_aggs]->out_type > 0 &&
!agg_type_admitted(op, s_agg_ins2[s_n_aggs]->out_type)) {
int8_t in_t = s_agg_ins2[s_n_aggs]->out_type;
if (g->selection) { ray_release(g->selection); g->selection = NULL; }
ray_graph_free(g); ray_release(tbl);
scratch_free(sagg_hdr);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type", "select: binary aggregation does not admit second input type %s", ray_type_name(in_t));
}
s_has_binary = 1;
}
s_n_aggs++;
}
if (s_use_eval_fallback) {
ray_t* eval_tbl = tbl;
int release_eval_tbl = 0;
if (g->selection) {
eval_tbl = sel_compact(g, tbl, g->selection, NULL, 0);
if (!eval_tbl || RAY_IS_ERR(eval_tbl)) {
if (g->selection) { ray_release(g->selection); g->selection = NULL; }
ray_graph_free(g); ray_release(tbl);
scratch_free(sagg_hdr);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return eval_tbl ? eval_tbl : ray_error("oom", NULL);
}
release_eval_tbl = 1;
}
ray_t* result = eval_scalar_agg_outputs(dict_elems, dict_n, eval_tbl,
from_id, where_id, by_id,
take_id, asc_id, desc_id,
nearest_id);
if (release_eval_tbl) ray_release(eval_tbl);
if (g->selection) { ray_release(g->selection); g->selection = NULL; }
ray_graph_free(g); ray_release(tbl);
scratch_free(sagg_hdr);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result;
}
if (s_has_binary)
root = ray_group_build(g, NULL, 0, s_agg_ops, s_agg_ins,
s_agg_ins2, NULL, s_n_aggs);
else
root = ray_group(g, NULL, 0, s_agg_ops, s_agg_ins, s_n_aggs);
scratch_free(sagg_hdr);
} else {
int64_t nc_max = select_output_count(dict_elems, dict_n);
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid != asc_id && kid != desc_id) continue;
ray_t* val = dict_elems[i + 1];
if (val->type == -RAY_SYM) nc_max += 1;
else if (ray_is_vec(val) && val->type == RAY_SYM) nc_max += ray_len(val);
}
if (nc_max < 1) nc_max = 1;
ray_t* colops_hdr = NULL;
ray_op_t** col_ops = (ray_op_t**)scratch_alloc(&colops_hdr,
(size_t)nc_max * (sizeof(ray_op_t*) + sizeof(int64_t) + sizeof(uint32_t)));
if (!col_ops) {
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
int64_t nc = 0;
int use_eval_fallback = 0;
int64_t* alias_syms = (int64_t*)(col_ops + nc_max);
uint32_t* alias_ids = (uint32_t*)(alias_syms + nc_max);
g->sel_alias_syms = alias_syms;
g->sel_alias_ids = alias_ids;
g->sel_alias_n = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id || kid == take_id || kid == asc_id || kid == desc_id || kid == nearest_id) continue;
col_ops[nc] = compile_expr_dag(g, dict_elems[i + 1]);
if (!col_ops[nc]) {
use_eval_fallback = 1;
break;
}
alias_syms[nc] = kid;
alias_ids[nc] = col_ops[nc]->id;
nc++;
g->sel_alias_n = (int)nc;
}
g->sel_alias_syms = NULL;
g->sel_alias_ids = NULL;
g->sel_alias_n = 0;
if (!use_eval_fallback && has_sort) {
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid != asc_id && kid != desc_id) continue;
ray_t* val = dict_elems[i + 1];
int64_t nk = val->type == -RAY_SYM ? 1
: (ray_is_vec(val) && val->type == RAY_SYM) ? val->len : 0;
for (int64_t k = 0; k < nk; k++) {
int64_t ks = val->type == -RAY_SYM ? val->i64 : sym_cell_runtime_id(val, k);
if (select_scan_output(g, col_ops, nc, ks) >= 0) continue;
if (!ray_table_get_col(tbl, ks) || nc >= nc_max) continue;
ray_t* nm = ray_sym_str(ks);
ray_op_t* sc = nm ? ray_scan(g, ray_str_ptr(nm)) : NULL;
if (!sc) continue;
col_ops[nc] = sc;
alias_syms[nc] = ks;
alias_ids[nc] = sc->id;
nc++;
n_hidden_sort++;
}
}
}
if (use_eval_fallback) {
if (g->compile_err) {
ray_t* cerr = graph_take_compile_err(g);
if (nearest_handle_owned) ray_release(nearest_handle_owned);
if (nearest_query_owned) ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(colops_hdr);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return cerr;
}
if (where_expr) {
root = ray_optimize(g, root);
ray_t* fres = ray_execute(g, root);
ray_graph_free(g); g = NULL;
if (fres && !RAY_IS_ERR(fres) && ray_is_lazy(fres))
fres = ray_lazy_materialize(fres);
if (!fres || RAY_IS_ERR(fres)) {
if (nearest_handle_owned) ray_release(nearest_handle_owned);
if (nearest_query_owned) ray_free_raw(nearest_query_owned);
ray_release(tbl);
scratch_free(colops_hdr);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return fres ? fres : ray_error("domain", "select: `where:` filter produced no result");
}
ray_release(tbl);
tbl = fres;
g = ray_graph_new(tbl);
if (!g) {
if (nearest_handle_owned) ray_release(nearest_handle_owned);
if (nearest_query_owned) ray_free_raw(nearest_query_owned);
ray_release(tbl);
scratch_free(colops_hdr);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
}
if (table_has_parted_columns(tbl)) {
ray_t* flat = query_materialize_parted_table(tbl);
if (!flat || RAY_IS_ERR(flat)) {
if (nearest_handle_owned) ray_release(nearest_handle_owned);
if (nearest_query_owned) ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(colops_hdr);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return flat ? flat : ray_error("oom", NULL);
}
ray_release(tbl);
tbl = flat;
ray_graph_free(g);
g = ray_graph_new(tbl);
if (!g) {
if (nearest_handle_owned) ray_release(nearest_handle_owned);
if (nearest_query_owned) ray_free_raw(nearest_query_owned);
ray_release(tbl);
scratch_free(colops_hdr);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
}
ray_t* result = ray_table_new(0);
if (!result || RAY_IS_ERR(result)) {
if (nearest_handle_owned) ray_release(nearest_handle_owned);
if (nearest_query_owned) ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(colops_hdr);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result ? result : ray_error("oom", NULL);
}
int64_t nrows = ray_table_nrows(tbl);
int any_row_proj = 0, any_agg = 0;
ray_t* scal_hdr = NULL;
int64_t* scalars = (int64_t*)scratch_alloc(&scal_hdr,
(size_t)(dict_n / 2 + 1) * sizeof(int64_t));
if (!scalars) {
ray_release(result);
if (nearest_handle_owned) ray_release(nearest_handle_owned);
if (nearest_query_owned) ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(colops_hdr);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
int64_t n_scalars = 0;
for (int64_t i = 0; i + 1 < dict_n && !any_row_proj; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id ||
kid == nearest_id) continue;
ray_t* e = dict_elems[i + 1];
int has_agg = select_fallback_has_agg(e);
any_agg |= has_agg;
any_row_proj = has_agg ? expr_refs_row_column(e, tbl)
: !select_fallback_scalar_proj(e, scalars, n_scalars);
if (!any_row_proj) scalars[n_scalars++] = kid;
}
scratch_free(scal_hdr);
if (!any_agg) any_row_proj = 1;
int64_t bcast_len = any_row_proj ? nrows : 1;
int64_t out_len = -1;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id ||
kid == nearest_id) continue;
ray_t* expr = dict_elems[i + 1];
int whole_verb = is_whole_column_projection(expr);
int one_value = !whole_verb &&
(!any_row_proj ||
(select_fallback_has_agg(expr) && !expr_refs_row_column(expr, tbl)));
ray_t* reduced = NULL;
if (!whole_verb && !one_value && select_fallback_has_agg(expr)) {
select_fallback_prereduce_ctx_t pc = { tbl };
reduced = select_fallback_prereduce(expr, &pc);
if (reduced && !RAY_IS_ERR(reduced)) expr = reduced;
}
ray_t* col;
if (reduced && RAY_IS_ERR(reduced)) {
col = reduced;
} else {
col = select_fallback_passthrough_col(expr, tbl, nrows);
if (!col) {
int at_row0 = one_value && !whole_verb &&
!ray_is_atom(expr) &&
!select_fallback_has_agg(expr);
col = at_row0 ? eval_expr_per_row(expr, tbl, 1)
: (whole_verb || one_value)
? eval_expr_whole_column(expr, tbl)
: eval_expr_per_row(expr, tbl, nrows);
if (one_value) col = select_fallback_broadcast(col, bcast_len);
}
if (reduced) ray_release(reduced);
}
if (!col || RAY_IS_ERR(col)) {
ray_t* err = col ? col : ray_error("domain", "select: failed to evaluate output column expression");
ray_release(result);
if (nearest_handle_owned) ray_release(nearest_handle_owned);
if (nearest_query_owned) ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(colops_hdr);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return err;
}
int64_t col_len = ray_len(col);
if (out_len < 0) {
out_len = col_len;
} else if (col_len != out_len) {
ray_release(col);
ray_release(result);
if (nearest_handle_owned) ray_release(nearest_handle_owned);
if (nearest_query_owned) ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(colops_hdr);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("length", "select: output column length %lld does not match %lld", (long long)col_len, (long long)out_len);
}
if (col_len == bcast_len) {
ray_t* bound = select_fallback_bind_alias(tbl, kid, col);
if (!bound || RAY_IS_ERR(bound)) {
ray_release(col);
ray_release(result);
if (nearest_handle_owned) ray_release(nearest_handle_owned);
if (nearest_query_owned) ray_free_raw(nearest_query_owned);
ray_graph_free(g);
scratch_free(colops_hdr);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return bound ? bound : ray_error("oom", NULL);
}
tbl = bound;
}
result = ray_table_add_col(result, kid, col);
ray_release(col);
if (RAY_IS_ERR(result)) {
if (nearest_handle_owned) ray_release(nearest_handle_owned);
if (nearest_query_owned) ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
scratch_free(colops_hdr);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result;
}
}
if (nearest_handle_owned) ray_release(nearest_handle_owned);
if (nearest_query_owned) ray_free_raw(nearest_query_owned);
ray_graph_free(g); ray_release(tbl);
result = apply_sort_take(result, dict_elems, dict_n,
asc_id, desc_id, take_id, NULL);
scratch_free(colops_hdr);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result;
} else {
root = ray_select_op(g, root, col_ops, nc);
scratch_free(colops_hdr);
}
}
}
if (has_sort && !by_expr) {
int64_t n_sort_max = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid != asc_id && kid != desc_id) continue;
ray_t* val = dict_elems[i + 1];
if (val->type == -RAY_SYM) n_sort_max += 1;
else if (ray_is_vec(val) && val->type == RAY_SYM) n_sort_max += ray_len(val);
}
if (n_sort_max < 1) n_sort_max = 1;
ray_t* sortk_hdr = NULL;
ray_op_t** sort_keys = (ray_op_t**)scratch_alloc(&sortk_hdr,
(size_t)n_sort_max * (sizeof(ray_op_t*) + 1));
if (!sort_keys) { ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL); }
uint8_t* sort_descs = (uint8_t*)(sort_keys + n_sort_max);
int64_t n_sort = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
uint8_t is_desc = 0;
if (kid == asc_id) is_desc = 0;
else if (kid == desc_id) is_desc = 1;
else continue;
ray_t* val = dict_elems[i + 1];
if (val->type == -RAY_SYM) {
ray_t* s = ray_sym_str(val->i64);
sort_keys[n_sort] = select_sort_key_op(g, root, val->i64, ray_str_ptr(s));
sort_descs[n_sort] = is_desc;
n_sort++;
} else if (ray_is_vec(val) && val->type == RAY_SYM) {
for (int64_t c = 0; c < val->len; c++) {
ray_t* s = ray_sym_vec_cell(val, c);
sort_keys[n_sort] = select_sort_key_op(g, root, sym_cell_runtime_id(val, c), ray_str_ptr(s));
sort_descs[n_sort] = is_desc;
n_sort++;
}
} else {
scratch_free(sortk_hdr);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain", "select: asc/desc value must be a column name or symbol list, got %s", ray_type_name(val->type));
}
}
if (n_sort > 0)
root = ray_sort_op(g, root, sort_keys, sort_descs, NULL, n_sort);
scratch_free(sortk_hdr);
}
ray_t* take_range = NULL;
take_pre_t take_pre = {0};
bool group_take_push = (take_expr && by_expr && !nearest_expr && !has_sort
&& !post_group_where_expr && !has_list_agg
&& !group_keys_have_bool(by_expr, tbl)
&& !table_is_parted(tbl));
if (take_expr && !nearest_expr && (!by_expr || group_take_push)) {
ray_t* tv = ray_eval(take_expr);
if (!tv || RAY_IS_ERR(tv)) { ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return tv ? tv : ray_error("domain", "select: failed to evaluate `take:`"); }
if (ray_is_atom(tv) && (tv->type == -RAY_I64 || tv->type == -RAY_I32)) {
int64_t n_take = (tv->type == -RAY_I64) ? tv->i64 : tv->i32;
if (n_take == INT64_MIN) {
ray_release(tv);
ray_graph_free(g); ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv);
return ray_error("type", "select: take: count is null or out of range");
}
if (group_take_push) {
take_pre.kind = TAKE_PRE_ATOM;
take_pre.a = n_take;
}
ray_release(tv);
if (group_take_push) {
if (n_take > 0) root = ray_head(g, root, n_take);
} else if (n_take >= 0)
root = ray_head(g, root, n_take);
else
root = ray_tail(g, root, -n_take);
} else if (ray_is_vec(tv) && (tv->type == RAY_I64 || tv->type == RAY_I32) && tv->len == 2) {
if (group_take_push) {
const int64_t* rv = (const int64_t*)ray_data(tv);
take_pre.kind = TAKE_PRE_RANGE;
if (tv->type == RAY_I64) { take_pre.a = rv[0]; take_pre.b = rv[1]; }
else { const int32_t* r32 = (const int32_t*)rv;
take_pre.a = r32[0]; take_pre.b = r32[1]; }
ray_release(tv);
if (take_pre.a >= 0 && take_pre.b > 0 && take_pre.a <= INT64_MAX - take_pre.b)
root = ray_head(g, root, take_pre.a + take_pre.b);
} else take_range = tv;
} else {
int8_t tv_t = tv->type;
ray_release(tv);
ray_graph_free(g); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain", "select: `take:` must be an integer atom or a 2-element integer range, got %s", ray_type_name(tv_t));
}
}
ray_group_emit_filter_t prev_self_emit = {0};
bool self_emit_set = false;
if (pre_top_emit_matched) {
prev_self_emit = ray_group_emit_filter_get();
pre_top_emit.target_depth = __VM ? __VM->eval_depth : 0;
ray_group_emit_filter_set(pre_top_emit);
self_emit_set = true;
}
root = ray_optimize(g, root);
ray_t* result = ray_execute(g, root);
if (n_hidden_sort > 0 && result && !RAY_IS_ERR(result)) {
if (ray_is_lazy(result)) result = ray_lazy_materialize(result);
if (result && !RAY_IS_ERR(result) && result->type == RAY_TABLE) {
int64_t rc = ray_table_ncols(result);
int64_t keep_n = rc - n_hidden_sort;
ray_t* kept = keep_n >= 0 ? ray_table_new(keep_n > 0 ? keep_n : 1) : NULL;
for (int64_t c = 0; kept && !RAY_IS_ERR(kept) && c < keep_n; c++)
kept = ray_table_add_col(kept, ray_table_col_name(result, c),
ray_table_get_col_idx(result, c));
if (kept && !RAY_IS_ERR(kept)) { ray_release(result); result = kept; }
else if (kept) ray_release(kept);
}
}
if (computed_single_key && result && !RAY_IS_ERR(result) && ray_is_lazy(result))
result = ray_lazy_materialize(result);
if (computed_single_key && result && !RAY_IS_ERR(result) && result->type == RAY_TABLE &&
ray_table_ncols(result) > 0) {
int64_t kname = derived_key_name(by_expr);
int64_t rc = ray_table_ncols(result);
for (int64_t c = 1; c < rc; c++)
if (ray_table_col_name(result, c) == kname) { kname = ray_sym_intern("key", 3); break; }
ray_table_set_col_name(result, 0, kname);
}
if (self_emit_set)
ray_group_emit_filter_set(prev_self_emit);
if (post_group_where_expr && result && !RAY_IS_ERR(result))
result = filter_group_result(result, post_group_where_expr);
ray_graph_free(g);
if (nearest_query_owned) ray_free_raw(nearest_query_owned);
if (nearest_handle_owned) ray_release(nearest_handle_owned);
if (take_range && result && !RAY_IS_ERR(result)) {
ray_t* sliced = ray_take_fn(result, take_range);
ray_release(result);
ray_release(take_range);
result = sliced;
} else if (take_range) {
ray_release(take_range);
}
if (by_expr && result && !RAY_IS_ERR(result) && result->type == RAY_TABLE) {
if (ray_is_lazy(result)) result = ray_lazy_materialize(result);
if (result && !RAY_IS_ERR(result) && result->type == RAY_TABLE) {
ray_t* key_col = ray_table_get_col_idx(result, 0);
if (key_col && key_col->type == RAY_BOOL && key_col->len >= 2) {
int64_t by_sym = -1;
if (by_expr->type == -RAY_SYM)
by_sym = by_expr->i64;
else if (by_expr->type == RAY_SYM && ray_len(by_expr) == 1)
by_sym = ((int64_t*)ray_data(by_expr))[0];
ray_t* orig_key = (by_sym >= 0) ? ray_table_get_col(tbl, by_sym) : NULL;
if (orig_key && orig_key->type == RAY_BOOL && orig_key->len > 0) {
bool first_val = ((bool*)ray_data(orig_key))[0];
bool result_first = ((bool*)ray_data(key_col))[0];
if (first_val != result_first) {
int64_t nrows_r = ray_table_nrows(result);
int64_t ncols_r = ray_table_ncols(result);
ray_t* reordered = ray_table_new((int32_t)ncols_r);
if (reordered && !RAY_IS_ERR(reordered)) {
int ok = 1;
for (int64_t c = 0; c < ncols_r && ok; c++) {
int64_t cn = ray_table_col_name(result, c);
ray_t* col = ray_table_get_col_idx(result, c);
int esz = ray_elem_size(col->type);
ray_t* new_col = ray_vec_new(col->type, nrows_r);
if (RAY_IS_ERR(new_col)) { ok = 0; break; }
if (col->type == RAY_SYM)
ray_sym_vec_adopt_domain(new_col, col);
new_col->len = nrows_r;
char* src = (char*)ray_data(col);
char* dst = (char*)ray_data(new_col);
bool has_nulls = ray_vec_may_have_nulls(col);
if (has_nulls) {
for (int64_t r = 0; r < nrows_r; r++) {
memcpy(dst + r * esz, src + (nrows_r - 1 - r) * esz, esz);
if (ray_vec_is_null(col, nrows_r - 1 - r))
ray_vec_set_null(new_col, r, true);
}
} else {
for (int64_t r = 0; r < nrows_r; r++)
memcpy(dst + r * esz, src + (nrows_r - 1 - r) * esz, esz);
}
reordered = ray_table_add_col(reordered, cn, new_col);
ray_release(new_col);
if (RAY_IS_ERR(reordered)) { ok = 0; break; }
}
if (ok) {
ray_release(result);
result = reordered;
} else if (reordered && !RAY_IS_ERR(reordered)) {
ray_release(reordered);
}
}
}
}
}
}
}
if (synth_count_col && by_expr && result && !RAY_IS_ERR(result)) {
if (ray_is_lazy(result)) result = ray_lazy_materialize(result);
if (result && !RAY_IS_ERR(result) && result->type == RAY_TABLE) {
int64_t nc = ray_table_ncols(result);
if (nc >= 1) {
ray_t* rebuilt = ray_table_new(nc - 1);
if (rebuilt && !RAY_IS_ERR(rebuilt)) {
for (int64_t c = 0; c < nc - 1; c++) {
int64_t cn = ray_table_col_name(result, c);
ray_t* col = ray_table_get_col_idx(result, c);
rebuilt = ray_table_add_col(rebuilt, cn, col);
}
ray_release(result);
result = rebuilt;
}
}
}
}
if (result && !RAY_IS_ERR(result) && n_out > 0) {
if (ray_is_lazy(result)) result = ray_lazy_materialize(result);
}
if (result && !RAY_IS_ERR(result) && result->type == RAY_TABLE && n_out > 0) {
ray_t* schema = ray_table_schema(result);
if (schema && !RAY_IS_ERR(schema) && schema->type > 0 && schema->type < RAY_TYPE_COUNT) {
int64_t ncols = schema->len;
int n_key_cols = 0;
if (by_expr) {
if (ray_is_vec(by_expr) && by_expr->type == RAY_SYM) n_key_cols = (int)ray_len(by_expr);
else n_key_cols = 1;
}
if (by_expr) {
if (parted_bydict_deferred &&
ray_is_vec(by_expr) && by_expr->type == RAY_SYM) {
const int64_t* aliases = (const int64_t*)ray_data(by_expr);
for (int64_t k = 0; k < n_key_cols && k < ncols; k++)
ray_table_set_col_name(result, k, aliases[k]);
}
for (int64_t j = 0; j < n_aggs_real && n_key_cols + j < ncols; j++)
ray_table_set_col_name(result, n_key_cols + j, agg_names[j]);
if (computed_single_key && ncols > 1) {
int64_t kname = ray_table_col_name(result, 0);
for (int64_t c = 1; c < ncols; c++)
if (ray_table_col_name(result, c) == kname) {
ray_table_set_col_name(result, 0, ray_sym_intern("key", 3));
break;
}
}
} else {
int j = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id ||
kid == nearest_id) continue;
if (n_key_cols + j < ncols)
ray_table_set_col_name(result, n_key_cols + j, kid);
j++;
}
}
}
}
if (n_compound > 0 && by_expr && result && !RAY_IS_ERR(result)) {
if (ray_is_lazy(result)) result = ray_lazy_materialize(result);
if (result && !RAY_IS_ERR(result) && result->type == RAY_TABLE) {
int nk2 = 1;
if (ray_is_vec(by_expr) && by_expr->type == RAY_SYM)
nk2 = (int)ray_len(by_expr);
int64_t hbase = (int64_t)nk2 + n_aggs_real;
int64_t rnc = ray_table_ncols(result);
ray_t* cc_hdr = NULL;
ray_t** comp_cols = (ray_t**)scratch_calloc(&cc_hdr,
(size_t)(n_compound > 0 ? n_compound : 1) * sizeof(ray_t*));
if (!comp_cols) {
for (int ci = 0; ci < n_compound; ci++)
{ ray_release(compound_rw[ci]); compound_rw[ci] = NULL; }
ray_release(result); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
ray_t* cerr = NULL;
bool layout_ok = hbase + n_hidden_aggs <= rnc;
if (layout_ok) {
if (ray_env_push_query_scope() != RAY_OK) {
cerr = ray_error("oom", NULL);
} else {
for (int hi = 0; hi < n_hidden_aggs; hi++)
ray_env_set_local(hidden_agg_names[hi],
ray_table_get_col_idx(result,
hbase + hi));
int64_t n_groups = ray_table_nrows(result);
for (int ci = 0; ci < n_compound && !cerr; ci++) {
ray_t* v = ray_eval(compound_rw[ci]);
if (!v || RAY_IS_ERR(v)) {
cerr = v ? v : ray_error("domain",
"select by: failed to evaluate aggregate expression");
} else if (ray_is_lazy(v) &&
(!(v = ray_lazy_materialize(v)) || RAY_IS_ERR(v))) {
cerr = v ? v : ray_error("domain",
"select by: failed to evaluate aggregate expression");
} else if (!(ray_is_vec(v) || v->type == RAY_LIST) ||
ray_len(v) != n_groups) {
ray_t* nm = ray_sym_str(compound_names[ci]);
cerr = ray_error("domain",
"select by: output `%.*s` did not evaluate to one value per group",
nm ? (int)ray_str_len(nm) : 1, nm ? ray_str_ptr(nm) : "?");
ray_release(v);
} else comp_cols[ci] = v;
}
ray_env_pop_scope();
}
}
for (int ci = 0; ci < n_compound; ci++)
{ ray_release(compound_rw[ci]); compound_rw[ci] = NULL; }
if (cerr) {
for (int ci = 0; ci < n_compound; ci++)
if (comp_cols[ci]) ray_release(comp_cols[ci]);
scratch_free(cc_hdr);
ray_release(result);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return cerr;
}
if (layout_ok) {
ray_t* nt = ray_table_new(rnc - n_hidden_aggs + n_compound);
for (int64_t c = 0; c < rnc && nt && !RAY_IS_ERR(nt); c++) {
if (c >= hbase && c < hbase + n_hidden_aggs) continue;
nt = ray_table_add_col(nt, ray_table_col_name(result, c),
ray_table_get_col_idx(result, c));
}
for (int ci = 0; ci < n_compound && nt && !RAY_IS_ERR(nt); ci++)
nt = ray_table_add_col(nt, compound_names[ci],
comp_cols[ci]);
if (nt && !RAY_IS_ERR(nt)) {
ray_release(result);
result = nt;
} else {
cerr = nt ? nt : ray_error("oom", NULL);
}
}
for (int ci = 0; ci < n_compound; ci++)
if (comp_cols[ci]) ray_release(comp_cols[ci]);
scratch_free(cc_hdr);
n_compound = 0;
if (cerr) {
ray_release(result);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return cerr;
}
}
}
if (n_nonaggs > 0 && by_expr && result && !RAY_IS_ERR(result)) {
if (ray_is_lazy(result)) result = ray_lazy_materialize(result);
if (result && !RAY_IS_ERR(result) && result->type == RAY_TABLE) {
int64_t n_groups = ray_table_nrows(result);
if (n_groups > 0) {
int all_broadcastable = 1;
for (int64_t ni = 0; ni < n_nonaggs && all_broadcastable; ni++) {
if (!can_atom_broadcast(nonagg_exprs[ni]))
all_broadcastable = 0;
}
if (all_broadcastable) {
for (int64_t ni = 0; ni < n_nonaggs; ni++) {
ray_t* col = atom_broadcast_vec(nonagg_exprs[ni], n_groups);
if (!col) {
ray_release(result); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
result = ray_table_add_col(result, nonagg_names[ni], col);
ray_release(col);
if (RAY_IS_ERR(result)) {
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result;
}
}
goto nonagg_done;
}
}
int64_t ks = -1;
if (by_expr->type == -RAY_SYM && !(by_expr->attrs & ATTR_QUOTED))
ks = by_expr->i64;
else if (by_expr->type == RAY_SYM && ray_len(by_expr) == 1)
ks = ((int64_t*)ray_data(by_expr))[0];
if (ks < 0) {
ray_release(result); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain", "select by: could not resolve scalar group key symbol");
}
ray_t* orig_key = ray_table_get_col(tbl, ks);
ray_t* grp_key = ray_table_get_col(result, ks);
int64_t nrows = orig_key ? ray_parted_nrows(orig_key) : 0;
if (!orig_key || !grp_key) {
ray_release(result); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain", "select by: group key column not found");
}
if (n_groups > 0 && nrows > 0) {
ray_t* scan_key = orig_key;
int scan_key_owned = 0;
if (RAY_IS_PARTED(scan_key->type) || scan_key->type == RAY_MAPCOMMON) {
scan_key = query_materialize_parted_col(scan_key);
if (!scan_key || RAY_IS_ERR(scan_key)) {
ray_release(result); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return scan_key ? scan_key : ray_error("oom", NULL);
}
scan_key_owned = 1;
}
#define RELEASE_SCAN_KEY() do { \
if (scan_key_owned && scan_key) ray_release(scan_key); \
} while (0)
int8_t okt = scan_key->type;
int8_t gkt = grp_key->type;
if (RAY_IS_PARTED(okt)) okt = (int8_t)RAY_PARTED_BASETYPE(okt);
if (RAY_IS_PARTED(gkt)) gkt = (int8_t)RAY_PARTED_BASETYPE(gkt);
#undef KEY_READ
#define KEY_READ(dst, vec, base_type, idx) do { \
const void* _d = ray_data(vec); \
switch (base_type) { \
case RAY_BOOL: \
case RAY_U8: (dst) = ((const uint8_t* )_d)[idx]; break; \
case RAY_I16: (dst) = ((const int16_t* )_d)[idx]; break; \
case RAY_I32: (dst) = ((const int32_t* )_d)[idx]; break; \
case RAY_I64: (dst) = ((const int64_t* )_d)[idx]; break; \
case RAY_F32: { uint32_t _u; \
memcpy(&_u, &((const float*)_d)[idx], 4); \
(dst) = (int64_t)_u; break; } \
case RAY_F64: { int64_t _u; \
memcpy(&_u, &((const double*)_d)[idx], 8); \
(dst) = _u; break; } \
case RAY_DATE: case RAY_TIME: \
(dst) = ((const int32_t*)_d)[idx]; break; \
case RAY_TIMESTAMP: \
(dst) = ((const int64_t*)_d)[idx]; break; \
\
case RAY_SYM: \
(dst) = ray_read_sym(_d, (idx), (base_type), \
(vec)->attrs); break; \
default: { \
\
(dst) = 0; break; \
} \
} \
} while (0)
int key_supported =
(okt == RAY_BOOL || okt == RAY_U8 ||
okt == RAY_I16 || okt == RAY_I32 || okt == RAY_I64 ||
okt == RAY_F32 || okt == RAY_F64 ||
okt == RAY_DATE || okt == RAY_TIME || okt == RAY_TIMESTAMP ||
okt == RAY_SYM || okt == RAY_STR);
if (!key_supported) {
RELEASE_SCAN_KEY();
ray_release(result); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("nyi", "non-agg scatter: unsupported group key type");
}
if (okt != gkt) {
RELEASE_SCAN_KEY();
ray_release(result); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("type", "group key type mismatch");
}
ray_t* gk_hdr = ray_alloc((size_t)n_groups * sizeof(int64_t));
ray_t* rg_hdr = ray_alloc((size_t)nrows * sizeof(int64_t));
ray_t* cnt_hdr = ray_alloc((size_t)n_groups * sizeof(int64_t));
ray_t* off_hdr = ray_alloc((size_t)n_groups * sizeof(int64_t));
ray_t* pos_hdr = ray_alloc((size_t)n_groups * sizeof(int64_t));
if (!gk_hdr || !rg_hdr || !cnt_hdr || !off_hdr || !pos_hdr) {
if (gk_hdr) ray_free(gk_hdr);
if (rg_hdr) ray_free(rg_hdr);
if (cnt_hdr) ray_free(cnt_hdr);
if (off_hdr) ray_free(off_hdr);
if (pos_hdr) ray_free(pos_hdr);
RELEASE_SCAN_KEY();
ray_release(result); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
int64_t* gk = (int64_t*)ray_data(gk_hdr);
int64_t* row_gid = (int64_t*)ray_data(rg_hdr);
int64_t* grp_cnt = (int64_t*)ray_data(cnt_hdr);
int64_t* offsets = (int64_t*)ray_data(off_hdr);
int64_t* pos = (int64_t*)ray_data(pos_hdr);
if (okt != RAY_STR)
for (int64_t gi = 0; gi < n_groups; gi++)
KEY_READ(gk[gi], grp_key, gkt, gi);
int rgid_did_mask = 0;
if (okt == RAY_STR) {
if (ray_index_kind(scan_key) == RAY_IDX_DICT) {
ray_dict_cd_t cd = ray_dict_cd_get();
if (cd.valid && cd.key_sym == ks && cd.n_groups == n_groups
&& cd.n_distinct > 0
&& ray_index_payload(scan_key->index)->u.dict.codes) {
const int32_t* scan_codes = (const int32_t*)ray_data(
ray_index_payload(scan_key->index)->u.dict.codes);
ray_t* c2g_hdr = NULL;
int32_t* code_to_gid = (int32_t*)scratch_alloc(&c2g_hdr,
(size_t)cd.n_distinct * sizeof(int32_t));
if (code_to_gid && scan_codes) {
for (int64_t i = 0; i < cd.n_distinct; i++) code_to_gid[i] = -1;
for (int64_t gi = 0; gi < n_groups; gi++) {
int32_t cc = cd.result_codes[gi];
if (cc >= 0 && cc < cd.n_distinct) code_to_gid[cc] = (int32_t)gi;
}
ray_pool_t* dg_pool = ray_pool_get();
if (dg_pool && nrows >= 200000 && ray_pool_total_workers(dg_pool) >= 2) {
dictgid_ctx_t dctx = { scan_codes, code_to_gid, cd.n_distinct, row_gid };
ray_pool_dispatch(dg_pool, dictgid_fn, &dctx, nrows);
} else {
for (int64_t r = 0; r < nrows; r++) {
int32_t cc = scan_codes[r];
row_gid[r] = (cc >= 0 && cc < cd.n_distinct) ? code_to_gid[cc] : -1;
}
}
scratch_free(c2g_hdr);
ray_dict_cd_clear();
goto str_rgid_done;
}
if (c2g_hdr) scratch_free(c2g_hdr);
}
}
const ray_str_t* gdesc = (const ray_str_t*)ray_data(grp_key);
const char* gpool = grp_key->str_pool
? (const char*)ray_data(grp_key->str_pool) : NULL;
const ray_str_t* sdesc = (const ray_str_t*)ray_data(scan_key);
const char* spool = scan_key->str_pool
? (const char*)ray_data(scan_key->str_pool) : NULL;
uint64_t cap = (uint64_t)n_groups * 2; if (cap < 32) cap = 32;
uint64_t c = 1; while (c && c < cap) c <<= 1;
if (!c) {
ray_free(gk_hdr); ray_free(rg_hdr); ray_free(cnt_hdr);
ray_free(off_hdr); ray_free(pos_hdr);
RELEASE_SCAN_KEY();
ray_release(result); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
cap = c;
uint64_t mask = cap - 1;
ray_t* slot_hdr = NULL;
int32_t* slot_gid_p1 = (int32_t*)scratch_calloc(&slot_hdr,
(size_t)cap * sizeof(int32_t));
if (!slot_gid_p1) {
if (slot_hdr) scratch_free(slot_hdr);
ray_free(gk_hdr); ray_free(rg_hdr); ray_free(cnt_hdr);
ray_free(off_hdr); ray_free(pos_hdr);
RELEASE_SCAN_KEY();
ray_release(result); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
for (int64_t gi = 0; gi < n_groups; gi++) {
uint64_t h = ray_str_t_hash(&gdesc[gi], gpool);
uint64_t s = h & mask;
while (slot_gid_p1[s] != 0) s = (s + 1) & mask;
slot_gid_p1[s] = (int32_t)(gi + 1);
}
ray_pool_t* str_pool = ray_pool_get();
if (str_pool && nrows >= 200000 &&
ray_pool_total_workers(str_pool) >= 2) {
rgid_probe_str_ctx_t spctx = {
.slot_gid_p1 = slot_gid_p1,
.mask = mask,
.gdesc = gdesc,
.gpool = gpool,
.sdesc = sdesc,
.spool = spool,
.row_gid = row_gid,
};
ray_pool_dispatch(str_pool, rgid_probe_str_fn, &spctx, nrows);
} else {
for (int64_t r = 0; r < nrows; r++) {
uint64_t h = ray_str_t_hash(&sdesc[r], spool);
uint64_t s = h & mask;
int64_t found = -1;
for (;;) {
int32_t g_p1 = slot_gid_p1[s];
if (g_p1 == 0) break;
int32_t g = g_p1 - 1;
if (ray_str_t_eq(&gdesc[g], gpool, &sdesc[r], spool)) {
found = g; break;
}
s = (s + 1) & mask;
}
row_gid[r] = found;
}
}
scratch_free(slot_hdr);
str_rgid_done: ;
} else {
uint64_t cap = (uint64_t)n_groups * 2;
if (cap < 32) cap = 32;
uint64_t c = 1;
while (c && c < cap) c <<= 1;
if (!c) {
ray_free(gk_hdr); ray_free(rg_hdr); ray_free(cnt_hdr);
ray_free(off_hdr); ray_free(pos_hdr);
RELEASE_SCAN_KEY();
ray_release(result); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
cap = c;
uint64_t mask = cap - 1;
ray_t* gk_keys_hdr = NULL;
ray_t* gk_idx_hdr = NULL;
int64_t* hk_keys = (int64_t*)scratch_alloc(&gk_keys_hdr,
(size_t)cap * sizeof(int64_t));
int32_t* hk_gid_p1 = (int32_t*)scratch_calloc(&gk_idx_hdr,
(size_t)cap * sizeof(int32_t));
if (!hk_keys || !hk_gid_p1) {
if (gk_keys_hdr) scratch_free(gk_keys_hdr);
if (gk_idx_hdr) scratch_free(gk_idx_hdr);
ray_free(gk_hdr); ray_free(rg_hdr); ray_free(cnt_hdr);
ray_free(off_hdr); ray_free(pos_hdr);
RELEASE_SCAN_KEY();
ray_release(result); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
int use_i64_gid = (n_groups >= ((int64_t)1 << 31) - 1);
ray_t* gk64_hdr = NULL;
int64_t* hk_gid64 = NULL;
if (use_i64_gid) {
hk_gid64 = (int64_t*)scratch_calloc(&gk64_hdr,
(size_t)cap * sizeof(int64_t));
if (!hk_gid64) {
scratch_free(gk_keys_hdr); scratch_free(gk_idx_hdr);
ray_free(gk_hdr); ray_free(rg_hdr); ray_free(cnt_hdr);
ray_free(off_hdr); ray_free(pos_hdr);
RELEASE_SCAN_KEY();
ray_release(result); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
}
for (int64_t gi = 0; gi < n_groups; gi++) {
int64_t k = gk[gi];
uint64_t h = (uint64_t)k * 0x9E3779B97F4A7C15ULL;
h ^= h >> 33;
uint64_t s = h & mask;
for (;;) {
int64_t cur_p1 = use_i64_gid ? hk_gid64[s]
: (int64_t)hk_gid_p1[s];
if (cur_p1 == 0) {
if (use_i64_gid) hk_gid64[s] = gi + 1;
else hk_gid_p1[s] = (int32_t)(gi + 1);
hk_keys[s] = k;
break;
}
if (hk_keys[s] == k) break;
s = (s + 1) & mask;
}
}
ray_pool_t* pool = ray_pool_get();
if (pool && nrows >= 200000 && ray_pool_total_workers(pool) >= 2) {
rgid_probe_ctx_t pctx = {
.hk_keys = hk_keys,
.hk_gid_p1 = use_i64_gid ? NULL : hk_gid_p1,
.hk_gid64 = use_i64_gid ? hk_gid64 : NULL,
.mask = mask,
.orig_key_data = ray_data(scan_key),
.okt = okt,
.okt_attrs = scan_key->attrs,
.row_gid = row_gid,
.selection = saved_selection,
.sel_flg = NULL,
.sel_offs = NULL,
.sel_idx = NULL,
.sel_n_segs = 0,
};
if (saved_selection) {
ray_rowsel_t* sm = ray_rowsel_meta(saved_selection);
pctx.sel_flg = ray_rowsel_flags(saved_selection);
pctx.sel_offs = ray_rowsel_offsets(saved_selection);
pctx.sel_idx = ray_rowsel_idx(saved_selection);
pctx.sel_n_segs = sm->n_segs;
}
ray_pool_dispatch(pool, rgid_probe_fn, &pctx, nrows);
rgid_did_mask = (saved_selection != NULL);
} else {
for (int64_t r = 0; r < nrows; r++) {
int64_t rv;
KEY_READ(rv, scan_key, okt, r);
uint64_t h = (uint64_t)rv * 0x9E3779B97F4A7C15ULL;
h ^= h >> 33;
uint64_t s = h & mask;
int64_t found = -1;
for (;;) {
int64_t cur_p1 = use_i64_gid ? hk_gid64[s]
: (int64_t)hk_gid_p1[s];
if (cur_p1 == 0) break;
if (hk_keys[s] == rv) { found = cur_p1 - 1; break; }
s = (s + 1) & mask;
}
row_gid[r] = found;
}
}
scratch_free(gk_keys_hdr);
scratch_free(gk_idx_hdr);
if (gk64_hdr) scratch_free(gk64_hdr);
}
#undef KEY_READ
if (saved_selection && !rgid_did_mask) {
ray_rowsel_t* sm = ray_rowsel_meta(saved_selection);
const uint8_t* flg = ray_rowsel_flags(saved_selection);
const uint32_t* offs = ray_rowsel_offsets(saved_selection);
const uint16_t* lidx = ray_rowsel_idx(saved_selection);
for (uint32_t seg = 0; seg < sm->n_segs; seg++) {
int64_t s_lo = (int64_t)seg * RAY_MORSEL_ELEMS;
int64_t s_hi = s_lo + RAY_MORSEL_ELEMS;
if (s_hi > nrows) s_hi = nrows;
uint8_t f = flg[seg];
if (f == RAY_SEL_NONE) {
for (int64_t r = s_lo; r < s_hi; r++) row_gid[r] = -1;
} else if (f == RAY_SEL_ALL) {
} else {
uint8_t in_seg[RAY_MORSEL_ELEMS / 8] = {0};
uint32_t off = offs[seg];
uint32_t cnt = offs[seg + 1] - off;
for (uint32_t i = 0; i < cnt; i++) {
uint16_t loc = lidx[off + i];
in_seg[loc >> 3] |= (uint8_t)(1u << (loc & 7));
}
for (int64_t r = s_lo; r < s_hi; r++) {
uint16_t loc = (uint16_t)(r - s_lo);
if (!(in_seg[loc >> 3] & (1u << (loc & 7))))
row_gid[r] = -1;
}
}
}
}
int needs_slice_idx = 0;
for (int64_t ni = 0; ni < n_nonaggs && !needs_slice_idx; ni++) {
ray_t* cd_inner = match_count_distinct(nonagg_exprs[ni]);
int simple_cd_global = (cd_inner &&
cd_inner->type == -RAY_SYM &&
!(cd_inner->attrs & ATTR_QUOTED) &&
n_groups > 50000);
if (simple_cd_global) {
ray_t* source = ray_table_get_col(tbl, cd_inner->i64);
int8_t type = source ? source->type : 0;
simple_cd_global = type == RAY_BOOL || type == RAY_U8 ||
type == RAY_I16 || type == RAY_I32 || type == RAY_I64 ||
type == RAY_F32 || type == RAY_F64 || type == RAY_DATE ||
type == RAY_TIME || type == RAY_TIMESTAMP || type == RAY_SYM;
}
if (!simple_cd_global) needs_slice_idx = 1;
}
int64_t* idx_buf = NULL;
ray_t* idx_hdr = NULL;
if (needs_slice_idx) {
if (!slice_build(row_gid, nrows, saved_selection, n_groups,
grp_cnt, offsets, pos, &idx_hdr)) {
ray_free(gk_hdr); ray_free(rg_hdr);
ray_free(cnt_hdr); ray_free(off_hdr);
ray_free(pos_hdr);
RELEASE_SCAN_KEY();
ray_release(result); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("oom", NULL);
}
idx_buf = (int64_t*)ray_data(idx_hdr);
}
ray_t* scatter_err = NULL;
for (int64_t ni = 0; ni < n_nonaggs && !scatter_err; ni++) {
ray_t* cd_inner = match_count_distinct(nonagg_exprs[ni]);
if (cd_inner) {
ray_t* col = NULL;
ray_t* src_for_global = NULL;
int src_owned = 0;
if (cd_inner->type == -RAY_SYM &&
!(cd_inner->attrs & ATTR_QUOTED)) {
src_for_global = ray_table_get_col(tbl, cd_inner->i64);
}
if (src_for_global && n_groups > 50000) {
if (RAY_IS_PARTED(src_for_global->type) ||
src_for_global->type == RAY_MAPCOMMON) {
ray_t* flat = query_materialize_parted_col(src_for_global);
if (!flat || RAY_IS_ERR(flat)) {
col = flat ? flat : ray_error("oom", NULL);
src_for_global = NULL;
} else {
src_for_global = flat;
src_owned = 1;
}
}
}
if (src_for_global) {
if (!col) {
if (n_groups <= 50000) {
col = count_distinct_per_group_buf(
cd_inner, tbl, idx_buf, offsets, grp_cnt, n_groups);
} else {
col = ray_count_distinct_per_group(
src_for_global, row_gid, nrows, n_groups);
}
}
}
if (src_owned && src_for_global) ray_release(src_for_global);
if (!col) {
col = count_distinct_per_group_buf(
cd_inner, tbl, idx_buf, offsets, grp_cnt, n_groups);
}
if (RAY_IS_ERR(col)) { scatter_err = col; break; }
result = ray_table_add_col(result, nonagg_names[ni], col);
ray_release(col);
if (RAY_IS_ERR(result)) {
scatter_err = result; result = NULL; break;
}
continue;
}
if (is_streaming_aggr_unary_call(nonagg_exprs[ni])) {
ray_t* col = NULL;
if (is_med_call(nonagg_exprs[ni])) {
col = aggr_med_per_group_buf(nonagg_exprs[ni], tbl,
idx_buf, offsets, grp_cnt, n_groups);
}
if (!col) {
col = aggr_unary_per_group_buf(
nonagg_exprs[ni], tbl,
idx_buf, offsets, grp_cnt, n_groups);
}
if (RAY_IS_ERR(col)) { scatter_err = col; break; }
result = ray_table_add_col(result, nonagg_names[ni], col);
ray_release(col);
if (RAY_IS_ERR(result)) {
scatter_err = result; result = NULL; break;
}
continue;
}
if (is_agg_expr(nonagg_exprs[ni]) ||
expr_contains_agg(nonagg_exprs[ni])) {
ray_t* per_group = nonagg_eval_per_group_buf(
nonagg_exprs[ni], tbl, idx_buf, offsets, grp_cnt, n_groups);
if (RAY_IS_ERR(per_group)) {
scatter_err = per_group; break;
}
result = ray_table_add_col(result, nonagg_names[ni], per_group);
ray_release(per_group);
if (RAY_IS_ERR(result)) {
scatter_err = result; result = NULL; break;
}
continue;
}
if (ray_env_push_query_scope() != RAY_OK) {
scatter_err = ray_error("oom", NULL); break;
}
ray_t* _aqt = bind_all_columns(tbl);
ray_t* full_val = ray_eval(nonagg_exprs[ni]);
g_active_query_table = _aqt;
ray_env_pop_scope();
if (!full_val || RAY_IS_ERR(full_val)) {
scatter_err = full_val ? full_val : ray_error("domain", "select by: failed to evaluate non-aggregate column expression");
break;
}
ray_t* list_col = ray_alloc(n_groups * sizeof(ray_t*));
if (!list_col) {
ray_release(full_val);
scatter_err = ray_error("oom", NULL); break;
}
list_col->type = RAY_LIST;
list_col->len = 0;
ray_t** list_out = (ray_t**)ray_data(list_col);
int refs_column = expr_refs_row_column(nonagg_exprs[ni], tbl);
int is_indexable =
ray_is_vec(full_val) || full_val->type == RAY_LIST;
int full_is_row_aligned =
is_indexable && full_val->len == nrows;
if (refs_column && !full_is_row_aligned) {
ray_release(full_val);
ray_release(list_col);
ray_t* per_group = nonagg_eval_per_group_buf(
nonagg_exprs[ni], tbl, idx_buf, offsets, grp_cnt, n_groups);
if (RAY_IS_ERR(per_group)) {
scatter_err = per_group; break;
}
result = ray_table_add_col(result, nonagg_names[ni], per_group);
ray_release(per_group);
if (RAY_IS_ERR(result)) {
scatter_err = result; result = NULL; break;
}
continue;
}
int gather_ok = 1;
for (int64_t gi = 0; gi < n_groups; gi++) {
ray_t* cell;
if (full_is_row_aligned) {
cell = gather_by_idx(full_val,
&idx_buf[offsets[gi]], grp_cnt[gi]);
if (!cell || RAY_IS_ERR(cell)) {
gather_ok = 0;
break;
}
} else {
ray_retain(full_val);
cell = full_val;
}
list_out[gi] = cell;
list_col->len = gi + 1;
}
ray_release(full_val);
if (!gather_ok) {
ray_release(list_col);
scatter_err = ray_error("oom", NULL); break;
}
result = ray_table_add_col(result, nonagg_names[ni], list_col);
ray_release(list_col);
if (RAY_IS_ERR(result)) {
scatter_err = result; result = NULL; break;
}
}
ray_free(gk_hdr); ray_free(rg_hdr); ray_free(cnt_hdr);
ray_free(off_hdr); ray_free(pos_hdr);
if (idx_hdr) ray_free(idx_hdr);
RELEASE_SCAN_KEY();
if (scatter_err) {
if (result) ray_release(result);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return scatter_err;
}
#undef RELEASE_SCAN_KEY
} else {
for (int64_t ni = 0; ni < n_nonaggs; ni++) {
ray_t* expr = nonagg_exprs[ni];
ray_t* empty_list = match_count_distinct(expr) ? ray_vec_new(RAY_I64, 0)
: is_streaming_aggr_unary_call(expr) ? aggr_unary_per_group_buf(expr, tbl, NULL, NULL, NULL, 0)
: can_atom_broadcast(expr) ? atom_broadcast_vec(expr, 0) : ray_list_new(0);
if (!empty_list || RAY_IS_ERR(empty_list)) {
ray_release(result); ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return empty_list ? empty_list : ray_error("oom", NULL);
}
result = ray_table_add_col(result, nonagg_names[ni], empty_list);
ray_release(empty_list);
if (RAY_IS_ERR(result)) { ray_release(tbl); scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result; }
}
}
nonagg_done: ;
}
}
if (n_dep_keys > 0 && result && !RAY_IS_ERR(result) &&
result->type == RAY_TABLE) {
if (ray_is_lazy(result))
result = ray_lazy_materialize(result);
if (result && RAY_IS_ERR(result)) {
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result;
}
if (result && result->type == RAY_TABLE) {
ray_t* base_col = ray_table_get_col(result, dep_key_base_sym);
if (!base_col || !key_type_i64_projectable(base_col->type)) {
ray_release(result);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return ray_error("domain", "dependent group key base missing");
}
int64_t n_groups = ray_table_nrows(result);
for (int64_t dk = 0; dk < n_dep_keys; dk++) {
ray_t* col = ray_vec_new(RAY_I64, n_groups);
if (!col || RAY_IS_ERR(col)) {
ray_release(result);
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return col ? col : ray_error("oom", NULL);
}
col->len = n_groups;
int64_t* out = (int64_t*)ray_data(col);
int64_t bias = dep_key_biases[dk];
for (int64_t i = 0; i < n_groups; i++)
out[i] = key_col_read_i64(base_col, i) + bias;
result = ray_table_add_col(result, dep_key_names[dk], col);
ray_release(col);
if (RAY_IS_ERR(result)) {
ray_release(tbl);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result;
}
}
}
}
ray_release(tbl);
if (by_expr && (has_sort || take_expr))
result = apply_sort_take(result, dict_elems, dict_n, asc_id, desc_id,
take_id,
take_pre.kind ? &take_pre : NULL);
if (by_sym_vec_owned) ray_release(by_sym_vec_owned);
if (saved_selection) ray_release(saved_selection);
scratch_free(sel_slots_hdr); DICT_VIEW_CLOSE(dv); return result;
}
typedef struct {
int8_t out_type;
const void* in;
void* out;
int64_t b;
int pow2;
} xbar_par_ctx_t;
static void xbar_par_fn(void* vctx, uint32_t worker_id,
int64_t start, int64_t end) {
(void)worker_id;
xbar_par_ctx_t* c = (xbar_par_ctx_t*)vctx;
int64_t b = c->b;
if (c->out_type == RAY_I64 || c->out_type == RAY_TIMESTAMP) {
const int64_t* in = (const int64_t*)c->in;
int64_t* o = (int64_t*)c->out;
if (c->pow2) {
int64_t mask = ~(b - 1);
for (int64_t i = start; i < end; i++) o[i] = in[i] & mask;
} else {
for (int64_t i = start; i < end; i++) {
int64_t a = in[i];
int64_t q = a / b;
if ((a ^ b) < 0 && q * b != a) q--;
o[i] = (int64_t)((uint64_t)q * (uint64_t)b);
}
}
} else if (c->out_type == RAY_I32 || c->out_type == RAY_DATE ||
c->out_type == RAY_TIME) {
const int32_t* in = (const int32_t*)c->in;
int32_t* o = (int32_t*)c->out;
int32_t b32 = (int32_t)b;
if (c->pow2) {
int32_t mask = (int32_t)~((uint32_t)b32 - 1);
for (int64_t i = start; i < end; i++) o[i] = in[i] & mask;
} else {
for (int64_t i = start; i < end; i++) {
int32_t a = in[i];
int64_t q = (int64_t)a / b32;
if ((a ^ b32) < 0 && q * b32 != a) q--;
o[i] = (int32_t)(q * b32);
}
}
} else {
const int16_t* in = (const int16_t*)c->in;
int16_t* o = (int16_t*)c->out;
int16_t b16 = (int16_t)b;
for (int64_t i = start; i < end; i++) {
int16_t a = in[i];
int16_t q = a / b16;
if ((a ^ b16) < 0 && q * b16 != a) q--;
o[i] = q * b16;
}
}
}
static bool xbar_bucket_fits_vec_type(int8_t out_type, int64_t b) {
if (out_type == RAY_I32 || out_type == RAY_DATE || out_type == RAY_TIME)
return b >= INT32_MIN && b <= INT32_MAX;
if (out_type == RAY_I16)
return b >= INT16_MIN && b <= INT16_MAX;
return true;
}
ray_t* ray_xbar_fn(ray_t* col, ray_t* bucket) {
if (col && ray_is_vec(col) && bucket && ray_is_atom(bucket) &&
(bucket->type == -RAY_I64 || bucket->type == -RAY_I32 ||
bucket->type == -RAY_I16) &&
(col->type == RAY_I64 || col->type == RAY_I32 ||
col->type == RAY_I16 || col->type == RAY_TIMESTAMP ||
col->type == RAY_DATE || col->type == RAY_TIME) &&
!RAY_ATOM_IS_NULL(bucket)) {
int64_t b = bucket->i64;
if (b == 0) return ray_error("domain", "xbar: bucket size must be non-zero");
if (!xbar_bucket_fits_vec_type(col->type, b))
return atomic_map_binary(ray_xbar_fn, col, bucket);
int64_t n = col->len;
ray_t* out = ray_vec_new(col->type, n);
if (!out || RAY_IS_ERR(out)) return out ? out : ray_error("oom", NULL);
out->len = n;
int8_t out_type = col->type;
int pow2 = 0;
if (out_type == RAY_I64 || out_type == RAY_TIMESTAMP) {
pow2 = (b > 0 && (b & (b - 1)) == 0);
} else if (out_type == RAY_I32 || out_type == RAY_DATE || out_type == RAY_TIME) {
int32_t b32 = (int32_t)b;
pow2 = (b32 > 0 && ((uint32_t)b32 & ((uint32_t)b32 - 1)) == 0);
}
ray_pool_t* pool = ray_pool_get();
if (pool && n >= 200000 && ray_pool_total_workers(pool) >= 2) {
xbar_par_ctx_t ctx = {
.out_type = out_type,
.in = ray_data(col),
.out = ray_data(out),
.b = b,
.pow2 = pow2,
};
ray_pool_dispatch(pool, xbar_par_fn, &ctx, n);
} else {
xbar_par_ctx_t ctx = {
.out_type = out_type,
.in = ray_data(col),
.out = ray_data(out),
.b = b,
.pow2 = pow2,
};
xbar_par_fn(&ctx, 0, 0, n);
}
if (ray_vec_may_have_nulls(col)) {
for (int64_t i = 0; i < n; i++)
if (ray_vec_is_null(col, i))
ray_vec_set_null(out, i, true);
}
return out;
}
if (is_collection(col) || is_collection(bucket))
return atomic_map_binary(ray_xbar_fn, col, bucket);
if (is_numeric(col) && is_numeric(bucket) && !is_float_op(col, bucket)) {
int64_t a = as_i64(col), b = as_i64(bucket);
if (b == 0 || RAY_ATOM_IS_NULL(col) || RAY_ATOM_IS_NULL(bucket))
return ray_error("domain", "xbar: bucket size must be non-zero and operands non-null");
int64_t q = a / b;
if ((a ^ b) < 0 && q * b != a) q--;
int64_t result = q * b;
if (col->type == -RAY_I32 && bucket->type == -RAY_I32) return make_i32((int32_t)result);
if (col->type == -RAY_I16 && bucket->type == -RAY_I16) return make_i16((int16_t)result);
return make_i64(result);
}
if (is_numeric(col) && is_numeric(bucket)) {
if (RAY_ATOM_IS_NULL(col) || RAY_ATOM_IS_NULL(bucket))
return ray_error("domain", "xbar: operands must be non-null");
double c = as_f64(col), b = as_f64(bucket);
if (b == 0.0) return ray_error("domain", "xbar: bucket size must be non-zero");
double fq = floor(c / b);
return make_f64(fq * b);
}
if (is_temporal(col) && (is_temporal(bucket) ||
(is_numeric(bucket) && bucket->type != -RAY_F64))) {
int64_t a = col->i64, b;
if (is_temporal(bucket)) {
b = bucket->i64;
if (col->type == -RAY_TIMESTAMP && bucket->type == -RAY_TIME)
b *= 1000000LL;
} else {
b = as_i64(bucket);
}
if (b == 0 || RAY_ATOM_IS_NULL(bucket)) return ray_error("domain", "xbar: bucket size must be non-zero and non-null");
int64_t q = a / b;
if ((a ^ b) < 0 && q * b != a) q--;
int64_t result = q * b;
if (col->type == -RAY_TIME) return ray_time(result);
if (col->type == -RAY_DATE) return ray_date(result);
return ray_timestamp(result);
}
return ray_error("type", "xbar: unsupported operand types, got %s and %s",
ray_type_name(col->type), ray_type_name(bucket->type));
}
static int8_t typeless_col_type(ray_t* payload) {
if (!payload) return RAY_I64;
if (ray_is_atom(payload)) return -payload->type;
if (ray_is_vec(payload)) return payload->type;
if (payload->type == RAY_LIST) {
int64_t m = ray_len(payload);
if (m == 0) return RAY_LIST;
ray_t** e = (ray_t**)ray_data(payload);
if (e[0] && !ray_is_atom(e[0])) return RAY_LIST;
int8_t t = e[0] ? (int8_t)(-e[0]->type) : RAY_I64;
if (t == RAY_I64)
for (int64_t k = 0; k < m; k++)
if (e[k] && e[k]->type == -RAY_F64) { t = RAY_F64; break; }
return t;
}
return RAY_I64;
}
static ray_t* append_atom_to_col(ray_t* col_vec, ray_t* atom) {
if (RAY_ATOM_IS_NULL(atom)) {
int64_t idx = col_vec->len;
uint8_t zero[16] = {0};
col_vec = ray_vec_append(col_vec, zero);
if (!RAY_IS_ERR(col_vec))
ray_vec_set_null(col_vec, idx, true);
return col_vec;
}
int8_t ct = col_vec->type;
int8_t at = atom->type;
bool is_int = (at == -RAY_I64 || at == -RAY_I32 ||
at == -RAY_I16 || at == -RAY_U8);
switch (ct) {
case RAY_BOOL: {
if (at != -RAY_BOOL) return ray_error("type", "insert: value type does not match bool column, got %s", ray_type_name(at));
uint8_t v = atom->b8;
return ray_vec_append(col_vec, &v);
}
case RAY_U8: {
if (!is_int) return ray_error("type", "insert: u8 column requires an integer value, got %s", ray_type_name(at));
uint8_t v = (uint8_t)as_i64(atom);
return ray_vec_append(col_vec, &v);
}
case RAY_I16: {
if (!is_int) return ray_error("type", "insert: i16 column requires an integer value, got %s", ray_type_name(at));
int16_t v = (int16_t)as_i64(atom);
return ray_vec_append(col_vec, &v);
}
case RAY_I32: {
if (!is_int) return ray_error("type", "insert: i32 column requires an integer value, got %s", ray_type_name(at));
int32_t v = (int32_t)as_i64(atom);
return ray_vec_append(col_vec, &v);
}
case RAY_I64: {
if (!is_int) return ray_error("type", "insert: i64 column requires an integer value, got %s", ray_type_name(at));
int64_t v = as_i64(atom);
return ray_vec_append(col_vec, &v);
}
case RAY_F64: {
if (at != -RAY_F64 && !is_int) return ray_error("type", "insert: f64 column requires a numeric value, got %s", ray_type_name(at));
double v = (at == -RAY_F64) ? atom->f64 : (double)as_i64(atom);
return ray_vec_append(col_vec, &v);
}
case RAY_DATE: {
if (at != -RAY_DATE) return ray_error("type", "insert: date column requires a date value, got %s", ray_type_name(at));
int32_t v = atom->i32;
return ray_vec_append(col_vec, &v);
}
case RAY_TIME: {
if (at != -RAY_TIME) return ray_error("type", "insert: time column requires a time value, got %s", ray_type_name(at));
int32_t v = atom->i32;
return ray_vec_append(col_vec, &v);
}
case RAY_TIMESTAMP: {
if (at != -RAY_TIMESTAMP) return ray_error("type", "insert: timestamp column requires a timestamp value, got %s", ray_type_name(at));
int64_t v = atom->i64;
return ray_vec_append(col_vec, &v);
}
case RAY_GUID: {
if (at != -RAY_GUID) return ray_error("type", "insert: guid column requires a guid value, got %s", ray_type_name(at));
static const uint8_t zero_guid[16] = {0};
const void* src = atom->obj ? ray_data(atom->obj) : zero_guid;
return ray_vec_append(col_vec, src);
}
case RAY_SYM: {
if (at != -RAY_SYM) return ray_error("type", "insert: sym column requires a sym value, got %s", ray_type_name(at));
int64_t v = atom->i64;
return ray_vec_append(col_vec, &v);
}
case RAY_STR: {
if (at != -RAY_STR) return ray_error("type", "insert: str column requires a str value, got %s", ray_type_name(at));
return ray_str_vec_append(col_vec, ray_str_ptr(atom), ray_str_len(atom));
}
default:
return ray_error("type", "insert: unsupported column type %s", ray_type_name(ct));
}
}
static ray_t* append_boxed_table_col(ray_t* orig_col, ray_t* payload,
bool splice_payload) {
if (!orig_col || RAY_IS_ERR(orig_col) || orig_col->type != RAY_LIST)
return ray_error("type", "insert: boxed column source must be LIST");
if (splice_payload &&
(!payload || RAY_IS_ERR(payload) || payload->type != RAY_LIST))
return ray_error("type", "insert: table payload for a LIST column must be LIST");
int64_t tail = splice_payload ? payload->len : 1;
if (tail < 0 || orig_col->len > INT64_MAX - tail)
return ray_error("oom", NULL);
ray_t* out = ray_list_new(orig_col->len + tail);
if (!out || RAY_IS_ERR(out)) return out ? out : ray_error("oom", NULL);
ray_t** dst = (ray_t**)ray_data(out);
ray_t** src = (ray_t**)ray_data(orig_col);
for (int64_t r = 0; r < orig_col->len; r++) {
dst[r] = src[r];
if (dst[r]) ray_retain(dst[r]);
}
if (splice_payload) {
ray_t** cells = (ray_t**)ray_data(payload);
for (int64_t r = 0; r < tail; r++) {
dst[orig_col->len + r] = cells[r];
if (cells[r]) ray_retain(cells[r]);
}
} else {
dst[orig_col->len] = payload;
if (payload) ray_retain(payload);
}
out->len = orig_col->len + tail;
return out;
}
static bool upsert_col_ok(ray_t* col);
static int64_t upsert_atom_int(ray_t* a);
static bool update_expr_elementwise(ray_t* e, ray_t* tbl) {
if (!e || RAY_IS_ERR(e)) return false;
if (e->type == -RAY_SYM) {
if (e->attrs & ATTR_QUOTED) return true;
return ray_table_get_col(tbl, e->i64) != NULL;
}
if (e->type < 0) return true;
if (e->type != RAY_LIST) return false;
int64_t n = ray_len(e);
if (n < 2) return false;
ray_t** el = (ray_t**)ray_data(e);
ray_t* head = el[0];
if (!head || head->type != -RAY_SYM || (head->attrs & ATTR_QUOTED)) return false;
ray_t* fn = ray_env_get(head->i64);
if (!fn || RAY_IS_ERR(fn)) return false;
if (fn->type != RAY_UNARY && fn->type != RAY_BINARY && fn->type != RAY_VARY) return false;
if (!(fn->attrs & RAY_FN_ATOMIC)) return false;
for (int64_t i = 1; i < n; i++)
if (!update_expr_elementwise(el[i], tbl)) return false;
return true;
}
static int update_where_index_rows(ray_t* tbl, ray_t* where_expr,
ray_t** rows_hdr, int64_t** rows, int64_t* n_out) {
if (!where_expr || where_expr->type != RAY_LIST || ray_len(where_expr) != 3) return 0;
ray_t** el = (ray_t**)ray_data(where_expr);
if (!el[0] || el[0]->type != -RAY_SYM) return 0;
if (el[0]->i64 != ray_sym_intern("==", 2)) return 0;
ray_t* col = NULL; ray_t* val_node = NULL;
for (int side = 1; side <= 2 && !col; side++) {
ray_t* a = el[side];
if (a && a->type == -RAY_SYM && ray_table_get_col(tbl, a->i64)) {
col = ray_table_get_col(tbl, a->i64);
val_node = el[3 - side];
}
}
if (!col || !val_node || RAY_IS_ERR(val_node)) return 0;
if (val_node->type == -RAY_SYM && ray_table_get_col(tbl, val_node->i64)) return 0;
if (!ray_is_atom(val_node)) return 0;
if (col->attrs & RAY_ATTR_HAS_NULLS) return 0;
if (ray_index_kind(col) != RAY_IDX_HASH) return 0;
if (col->type == RAY_F64 || col->type == RAY_STR || col->type == RAY_GUID) return 0;
ray_t* v = ray_eval(val_node);
if (!v) return 0;
if (RAY_IS_ERR(v)) { ray_error_free(v); return 0; }
int ok = ray_is_atom(v) && !RAY_ATOM_IS_NULL(v) &&
(col->type == RAY_SYM ? v->type == -RAY_SYM
: (v->type == -col->type || v->type == -RAY_I64 ||
v->type == -RAY_I32 || v->type == -RAY_I16 ||
v->type == -RAY_U8));
int64_t key = ok ? (col->type == RAY_SYM ? v->i64 : upsert_atom_int(v)) : 0;
ray_release(v);
if (!ok) return 0;
const int64_t* ids = NULL; int64_t n = 0;
int hit = ray_index_hash_group(col, key, &ids, &n);
if (hit < 0) return 0;
int64_t* out = (int64_t*)scratch_alloc(rows_hdr, (size_t)(n > 0 ? n : 1) * sizeof(int64_t));
if (!out) return 0;
if (n > 0) memcpy(out, ids, (size_t)n * sizeof(int64_t));
*rows = out; *n_out = n;
return 1;
}
static ray_t* update_where_mask_vec(ray_t* tbl, ray_t* where_expr) {
ray_t* mask_vec = NULL;
bool rejected = false;
ray_graph_t* g = ray_graph_new(tbl);
if (g) {
ray_op_t* pred = compile_expr_dag(g, where_expr);
if (pred) {
pred = ray_optimize(g, pred);
mask_vec = ray_execute(g, pred);
} else if (g->compile_err) {
mask_vec = graph_take_compile_err(g);
rejected = true;
}
ray_graph_free(g);
}
if (!rejected && (!mask_vec || RAY_IS_ERR(mask_vec))) {
int64_t ncols = ray_table_ncols(tbl);
ray_env_push_query_scope();
for (int64_t c = 0; c < ncols; c++)
ray_env_set_query_local(ray_table_col_name(tbl, c), ray_table_get_col_idx(tbl, c));
mask_vec = ray_eval(where_expr);
ray_env_pop_scope();
}
if (!mask_vec) return ray_error("type", "update: `where:` predicate evaluation failed");
if (RAY_IS_ERR(mask_vec)) return mask_vec;
mask_vec = where_mask_spread(mask_vec, ray_table_nrows(tbl), "update");
if (!mask_vec || RAY_IS_ERR(mask_vec)) return mask_vec ? mask_vec : ray_error("oom", NULL);
if (mask_vec->type != RAY_BOOL || mask_vec->len != ray_table_nrows(tbl)) {
int8_t mask_t = mask_vec->type;
ray_release(mask_vec);
return ray_error("type", "update: `where:` must produce a bool mask matching row count, got %s", ray_type_name(mask_t));
}
return mask_vec;
}
static ray_t* update_scalar_vec(ray_t* v, int64_t nrows) {
if (!v || RAY_IS_ERR(v) || v->type <= 0 || v->type == RAY_LIST ||
!ray_is_vec(v) || v->len != 1 || nrows == 1)
return v;
int alloc = 0;
ray_t* a = collection_elem(v, 0, &alloc);
if (!a || RAY_IS_ERR(a)) return v;
if (!alloc) ray_retain(a);
ray_release(v);
return a;
}
static void update_atom_cell(const ray_t* a, int8_t ct, size_t esz, uint8_t elem[16]) {
memset(elem, 0, 16);
if (ct == RAY_GUID) {
if (a->obj) memcpy(elem, ray_data(a->obj), 16);
} else if (ct == RAY_F64 && a->type == -RAY_I64) {
double p = (double)a->i64; memcpy(elem, &p, sizeof p);
} else if (ct == RAY_F32 && (a->type == -RAY_F32 || a->type == -RAY_F64)) {
float f = (float)a->f64; memcpy(elem, &f, sizeof f);
} else {
memcpy(elem, &a->i64, esz);
}
}
static int update_by_num_rank(int8_t t) {
switch (t) {
case RAY_BOOL: return 1;
case RAY_U8: return 2;
case RAY_I16: return 3;
case RAY_I32: return 4;
case RAY_I64: return 5;
case RAY_F32: return 6;
case RAY_F64: return 7;
default: return 0;
}
}
static ray_t* update_by_promote(ray_t* col, int8_t to) {
int64_t n = ray_len(col);
ray_t* nc = ray_vec_new(to, n);
if (!nc || RAY_IS_ERR(nc)) return nc ? nc : ray_error("oom", NULL);
nc->len = n;
for (int64_t r = 0; r < n; r++) {
int alloc = 0;
ray_t* cell = collection_elem(col, r, &alloc);
if (!cell || RAY_IS_ERR(cell)) { ray_release(nc); return cell ? cell : ray_error("oom", NULL); }
store_typed_elem(nc, r, cell);
if (alloc) ray_release(cell);
}
return nc;
}
static ray_t* update_by_store(ray_t* col, int64_t row, ray_t* cell) {
if (col->type == RAY_STR) {
const char* sp = "";
size_t sl = 0;
if (cell->type == -RAY_STR && !RAY_ATOM_IS_NULL(cell)) {
sp = ray_str_ptr(cell);
sl = ray_str_len(cell);
}
return ray_str_vec_set(col, row, sp, sl);
}
store_typed_elem(col, row, cell);
return col;
}
static int expr_refs_any_column(ray_t* expr, ray_t* tbl) {
if (!expr) return 0;
if (expr->type == -RAY_SYM && !(expr->attrs & ATTR_QUOTED)) {
if (ray_env_has_lexical_local(expr->i64)) return 0;
if (ray_table_get_col(tbl, expr->i64)) return 1;
if (ray_sym_is_dotted(expr->i64)) {
const int64_t* segs;
int nsegs = ray_sym_segs(expr->i64, &segs);
if (nsegs >= 1 && ray_table_get_col(tbl, segs[0])) return 1;
}
return 0;
}
if (expr->type == RAY_LIST) {
ray_t** elems = (ray_t**)ray_data(expr);
int64_t n = ray_len(expr);
for (int64_t i = 1; i < n; i++)
if (expr_refs_any_column(elems[i], tbl)) return 1;
}
return 0;
}
static int expr_contains_agg_over_column(ray_t* expr, ray_t* tbl) {
if (!expr || expr->type != RAY_LIST) return 0;
if (is_agg_expr(expr)) return expr_refs_any_column(expr, tbl);
ray_t** elems = (ray_t**)ray_data(expr);
int64_t n = ray_len(expr);
for (int64_t i = 0; i < n; i++)
if (expr_contains_agg_over_column(elems[i], tbl)) return 1;
return 0;
}
static bool update_dag_err_final(ray_t* out, bool if_refused) {
return if_refused && out && RAY_IS_ERR(out);
}
static ray_t* update_eval_on(ray_t* sub, ray_t* expr) {
ray_t* out = NULL;
ray_t* cerr = NULL;
bool if_refused = false;
ray_graph_t* g = ray_graph_new(sub);
if (g) {
ray_op_t* op = compile_expr_dag(g, expr);
if (op) {
op = ray_optimize(g, op);
out = ray_execute(g, op);
} else {
cerr = graph_take_compile_err(g);
}
if_refused = g->if_refused;
ray_graph_free(g);
}
if (update_dag_err_final(out, if_refused)) {
if (cerr) ray_error_free(cerr);
return out;
}
if (!out || RAY_IS_ERR(out)) {
if (out) { if (cerr) ray_error_free(cerr); cerr = out; out = NULL; }
int64_t nrows = ray_table_nrows(sub);
int per_row = expr_contains_call_named(expr, "if", 2) && expr_refs_row_column(expr, sub);
if (per_row && expr_contains_agg_over_column(expr, sub)) {
if (cerr) ray_error_free(cerr);
return ray_error("nyi", "update: an if over a column that also aggregates is not supported without the planner");
}
if (!per_row) {
int64_t ncols = ray_table_ncols(sub);
ray_env_push_query_scope();
for (int64_t c = 0; c < ncols; c++)
ray_env_set_query_local(ray_table_col_name(sub, c), ray_table_get_col_idx(sub, c));
out = ray_eval(expr);
ray_env_pop_scope();
} else {
out = eval_expr_per_row(expr, sub, nrows);
}
if ((!out || RAY_IS_ERR(out)) && cerr) {
if (out) ray_error_free(out);
return cerr;
}
}
if (cerr) ray_error_free(cerr);
if (!out) return ray_error("type", "update: failed to evaluate column update expression");
if (RAY_IS_ERR(out)) return out;
if (ray_is_lazy(out)) out = ray_lazy_materialize(out);
return update_scalar_vec(out, ray_table_nrows(sub));
}
static bool update_numeric_promo(int8_t ct, int8_t et) {
return (ct == RAY_I64 || ct == RAY_I32 || ct == RAY_F64) &&
(et == RAY_I64 || et == RAY_I32 || et == RAY_F64);
}
static ray_t* update_value_check(ray_t* col, ray_t* val, int64_t k) {
int8_t ct = col->type;
if (val->type < 0) {
int ok = (val->type == -ct) ||
(ct == RAY_F64 && val->type == -RAY_I64) ||
(ct == RAY_LIST && val->type == -RAY_SYM);
if (!ok) return ray_error("type", "update: cannot assign %s value to %s column",
ray_type_name(val->type), ray_type_name(ct));
return NULL;
}
if (!ray_is_vec(val) && val->type != RAY_LIST)
return ray_error("type", "update: cannot assign %s expression to %s column",
ray_type_name(val->type), ray_type_name(ct));
if (ray_len(val) != k)
return ray_error("length", "update: expression yields %lld values for %lld matched rows",
(long long)ray_len(val), (long long)k);
if (val->type == ct) return NULL;
if (update_numeric_promo(ct, val->type)) return NULL;
return ray_error("type", "update: expression type %s does not match %s column",
ray_type_name(val->type), ray_type_name(ct));
}
static double update_cell_f64(ray_t* v, int64_t i) {
switch (v->type) {
case RAY_F64: return ((const double*)ray_data(v))[i];
case RAY_I32: return (double)((const int32_t*)ray_data(v))[i];
default: return (double)((const int64_t*)ray_data(v))[i];
}
}
static ray_t* update_scatter(ray_t** slots, int64_t c, const int64_t* rows, int64_t k, ray_t* val) {
ray_t* col = slots[c];
int8_t ct = col->type;
if (ct == RAY_LIST) {
ray_t** cells = (ray_t**)ray_data(col);
for (int64_t i = 0; i < k; i++) {
ray_t* nv = (val->type < 0) ? val : ((ray_t**)ray_data(val))[i];
ray_t* old = cells[rows[i]];
if (nv) ray_retain(nv);
cells[rows[i]] = nv;
if (old) ray_release(old);
}
return NULL;
}
ray_index_drop(&slots[c]);
col = slots[c];
if (RAY_IS_ERR(col)) return col;
col->attrs &= (uint8_t)~RAY_ATTR_SORTED;
if (ct == RAY_STR) {
for (int64_t i = 0; i < k; i++) {
const char* sp; size_t sl;
if (val->type < 0) { sp = ray_str_ptr(val); sl = ray_str_len(val); }
else { sl = 0; sp = ray_str_vec_get(val, i, &sl); }
ray_t* res = ray_str_vec_set(col, rows[i], sp ? sp : "", sp ? sl : 0);
if (RAY_IS_ERR(res)) return res;
slots[c] = col = res;
}
return NULL;
}
size_t esz = ray_sym_elem_size(ct, col->attrs);
uint8_t* d = (uint8_t*)ray_data(col);
if (val->type < 0) {
uint8_t elem[16];
bool is_null = RAY_ATOM_IS_NULL(val);
update_atom_cell(val, ct, esz, elem);
for (int64_t i = 0; i < k; i++) {
if (is_null) ray_vec_set_null(col, rows[i], true);
else {
memcpy(d + (size_t)rows[i] * esz, elem, esz);
if (ct == RAY_SYM && val->i64 == 0) col->attrs |= RAY_ATTR_HAS_NULLS;
}
}
return NULL;
}
if (val->type != ct) {
void* p = ray_data(col);
for (int64_t i = 0; i < k; i++) {
int64_t r = rows[i];
if (ray_vec_is_null(val, i)) { ray_vec_set_null(col, r, true); continue; }
if (ct == RAY_F64) {
((double*)p)[r] = update_cell_f64(val, i);
} else if (val->type == RAY_F64) {
double x = ((const double*)ray_data(val))[i];
if (ct == RAY_I64) ((int64_t*)p)[r] = ray_cast_f64_to_i64_null(x);
else ((int32_t*)p)[r] = ray_cast_f64_to_i32_null(x);
if (ray_vec_is_null(col, r)) col->attrs |= RAY_ATTR_HAS_NULLS;
} else if (ct == RAY_I64) {
((int64_t*)p)[r] = (int64_t)((const int32_t*)ray_data(val))[i];
} else {
((int32_t*)p)[r] = (int32_t)((const int64_t*)ray_data(val))[i];
}
}
return NULL;
}
if (ct == RAY_SYM) {
int64_t* ids = (int64_t*)ray_data(col);
for (int64_t i = 0; i < k; i++) {
int64_t id = sym_cell_runtime_id(val, i);
ids[rows[i]] = id;
if (id == 0) col->attrs |= RAY_ATTR_HAS_NULLS;
}
return NULL;
}
const uint8_t* s = (const uint8_t*)ray_data(val);
bool vn = ray_vec_may_have_nulls(val);
for (int64_t i = 0; i < k; i++) {
if (vn && ray_vec_is_null(val, i)) { ray_vec_set_null(col, rows[i], true); continue; }
memcpy(d + (size_t)rows[i] * esz, s + (size_t)i * esz, esz);
}
return NULL;
}
static ray_t* update_where_inplace(ray_t* tbl, int64_t inplace_sym, ray_t* dict,
ray_t* where_expr, bool* handled) {
*handled = false;
int64_t ncols = ray_table_ncols(tbl);
if (inplace_sym < 0 || ncols <= 0) return NULL;
if (tbl->rc != 2 || tbl->mmod != 0 || (tbl->attrs & (RAY_ATTR_SLICE | RAY_ATTR_ARENA))) return NULL;
ray_t* live_cols = ((ray_t**)ray_data(tbl))[1];
if (!live_cols || live_cols->rc != 1 || live_cols->len != ncols) return NULL;
ray_t** slots = (ray_t**)ray_data(live_cols);
for (int64_t c = 0; c < ncols; c++)
if (slots[c]->rc != 1 || !upsert_col_ok(slots[c])) return NULL;
DICT_VIEW_DECL(updf);
DICT_VIEW_OPEN(dict, updf);
if (DICT_VIEW_OVERFLOW(updf)) { DICT_VIEW_CLOSE(updf); *handled = true; return ray_error("oom", NULL); }
int64_t from_id = ray_sym_intern("from", 4);
int64_t where_id = ray_sym_intern("where", 5);
int64_t by_id = ray_sym_intern("by", 2);
ray_t* ucol_hdr = NULL; ray_t* uexpr_hdr = NULL; ray_t* uval_hdr = NULL;
int64_t* ucol = (int64_t*)scratch_alloc(&ucol_hdr, (size_t)ncols * sizeof(int64_t));
ray_t** uexpr = (ray_t**)scratch_alloc(&uexpr_hdr, (size_t)ncols * sizeof(ray_t*));
ray_t** uval = (ray_t**)scratch_alloc(&uval_hdr, (size_t)ncols * sizeof(ray_t*));
if (!ucol || !uexpr || !uval) {
if (ucol_hdr) scratch_free(ucol_hdr);
if (uexpr_hdr) scratch_free(uexpr_hdr);
if (uval_hdr) scratch_free(uval_hdr);
DICT_VIEW_CLOSE(updf); *handled = true; return ray_error("oom", NULL);
}
int64_t nu = 0;
bool rowwise = true;
for (int64_t c = 0; c < ncols; c++) {
int64_t cn = ray_table_col_name(tbl, c);
for (int64_t d = 0; d + 1 < updf_n; d += 2) {
int64_t kid = updf[d]->i64;
if (kid == from_id || kid == where_id || kid == by_id) continue;
if (kid != cn) continue;
ucol[nu] = c; uexpr[nu] = updf[d + 1]; uval[nu] = NULL; nu++;
if (!update_expr_elementwise(updf[d + 1], tbl)) rowwise = false;
break;
}
}
bool adds_col = false;
for (int64_t d = 0; d + 1 < updf_n && !adds_col; d += 2) {
int64_t kid = updf[d]->i64;
if (kid == from_id || kid == where_id || kid == by_id) continue;
bool found = false;
for (int64_t i = 0; i < nu; i++)
if (ray_table_col_name(tbl, ucol[i]) == kid) { found = true; break; }
if (!found) adds_col = true;
}
if (!rowwise || adds_col) {
scratch_free(ucol_hdr); scratch_free(uexpr_hdr); scratch_free(uval_hdr);
DICT_VIEW_CLOSE(updf);
return NULL;
}
*handled = true;
{
ray_index_t* kix = ray_table_ukey_get(tbl);
if (kix) {
bool hits_key = false;
for (int64_t i = 0; i < nu && !hits_key; i++)
for (int64_t kk = 0; kk < kix->u.ukey.nk; kk++)
if (kix->u.ukey.kci[kk] == (int16_t)ucol[i]) { hits_key = true; break; }
if (hits_key) ray_table_ukey_drop(tbl);
}
}
ray_t* rows_hdr = NULL; int64_t* rows = NULL; int64_t k = 0;
ray_t* err = NULL;
if (!update_where_index_rows(tbl, where_expr, &rows_hdr, &rows, &k)) {
ray_t* mask = update_where_mask_vec(tbl, where_expr);
if (RAY_IS_ERR(mask)) err = mask;
else {
int64_t nrows = mask->len;
const uint8_t* m = (const uint8_t*)ray_data(mask);
int64_t cnt = 0;
for (int64_t r = 0; r < nrows; r++) cnt += (m[r] != 0);
rows = (int64_t*)scratch_alloc(&rows_hdr, (size_t)(cnt > 0 ? cnt : 1) * sizeof(int64_t));
if (!rows) err = ray_error("oom", NULL);
else for (int64_t r = 0; r < nrows; r++) if (m[r]) rows[k++] = r;
ray_release(mask);
}
}
if (!err && k == 0) {
if (rows_hdr) scratch_free(rows_hdr);
scratch_free(ucol_hdr); scratch_free(uexpr_hdr); scratch_free(uval_hdr);
DICT_VIEW_CLOSE(updf);
*handled = false;
return NULL;
}
ray_t* idxv = NULL; ray_t* sub = NULL;
if (!err && nu > 0) {
idxv = ray_vec_new(RAY_I64, k);
if (RAY_IS_ERR(idxv)) { err = idxv; idxv = NULL; }
else {
ray_t* r2 = ray_vec_append_raw(idxv, rows, k);
if (RAY_IS_ERR(r2)) { err = r2; } else idxv = r2;
}
if (!err) {
sub = ray_at_fn(tbl, idxv);
if (!sub || RAY_IS_ERR(sub)) { err = sub ? sub : ray_error("oom", NULL); sub = NULL; }
}
}
for (int64_t u = 0; u < nu && !err; u++) {
ray_t* v = update_eval_on(sub, uexpr[u]);
if (RAY_IS_ERR(v)) { err = v; break; }
uval[u] = v;
err = update_value_check(slots[ucol[u]], v, k);
}
for (int64_t u = 0; u < nu && !err; u++)
err = update_scatter(slots, ucol[u], rows, k, uval[u]);
for (int64_t u = 0; u < nu; u++) if (uval[u]) ray_release(uval[u]);
if (sub) ray_release(sub);
if (idxv) ray_release(idxv);
if (rows_hdr) scratch_free(rows_hdr);
scratch_free(ucol_hdr); scratch_free(uexpr_hdr); scratch_free(uval_hdr);
DICT_VIEW_CLOSE(updf);
if (err) return err;
return ray_sym(inplace_sym);
}
ray_t* ray_update_fn(ray_t** args, int64_t n) {
return ray_update(args, n);
}
ray_t* ray_update(ray_t** args, int64_t n) {
if (n < 1) return ray_error("arity", "update: expects a query dict, got %lld args", (long long)n);
ray_t* dict = args[0];
if (!dict || dict->type != RAY_DICT)
return ray_error("type", "update: query must be a dict, got %s", dict ? ray_type_name(dict->type) : "null");
ray_t* from_expr = dict_get(dict, "from");
if (!from_expr) return ray_error("domain", "update: missing `from:` clause");
int64_t inplace_sym = -1;
ray_t* tbl = ray_eval(from_expr);
if (RAY_IS_ERR(tbl)) return tbl;
if (tbl->type == -RAY_SYM) {
inplace_sym = tbl->i64;
ray_release(tbl);
tbl = ray_env_get(inplace_sym);
if (!tbl || RAY_IS_ERR(tbl)) return ray_error("domain", "update: `from:` symbol is unbound");
ray_retain(tbl);
}
if (tbl->type != RAY_TABLE) { int8_t tbl_t = tbl->type; ray_release(tbl); return ray_error("type", "update: `from:` must be a table, got %s", ray_type_name(tbl_t)); }
if (table_has_parted_columns(tbl)) {
ray_t* flat_tbl = ray_table_new(ray_table_ncols(tbl));
if (!flat_tbl || RAY_IS_ERR(flat_tbl)) { ray_release(tbl); return flat_tbl ? flat_tbl : ray_error("oom", NULL); }
int64_t nc = ray_table_ncols(tbl);
for (int64_t c = 0; c < nc; c++) {
ray_t* col = ray_table_get_col_idx(tbl, c);
ray_t* flat_col = query_materialize_parted_col(col);
if (!flat_col || RAY_IS_ERR(flat_col)) {
ray_release(flat_tbl); ray_release(tbl);
return flat_col ? flat_col : ray_error("oom", NULL);
}
flat_tbl = ray_table_add_col(flat_tbl, ray_table_col_name(tbl, c), flat_col);
ray_release(flat_col);
if (!flat_tbl || RAY_IS_ERR(flat_tbl)) { ray_release(tbl); return flat_tbl ? flat_tbl : ray_error("oom", NULL); }
}
ray_release(tbl);
tbl = flat_tbl;
}
ray_t* where_expr = dict_get(dict, "where");
ray_t* by_expr = dict_get(dict, "by");
if (by_expr && where_expr) {
ray_release(tbl);
return ray_error("nyi", "update does not support `by:` together with `where:`");
}
if (by_expr && !where_expr) {
DICT_VIEW_DECL(updv);
DICT_VIEW_OPEN(dict, updv);
if (DICT_VIEW_OVERFLOW(updv)) {
ray_release(tbl);
DICT_VIEW_CLOSE(updv); return ray_error("oom", NULL);
}
int64_t dict_n = updv_n;
ray_t** dict_elems = updv;
int64_t from_id = ray_sym_intern("from", 4);
int64_t where_id = ray_sym_intern("where", 5);
int64_t by_id = ray_sym_intern("by", 2);
int64_t by_col_name = -1;
if (by_expr->type == -RAY_SYM) {
by_col_name = by_expr->i64;
}
if (by_col_name < 0) { ray_release(tbl); DICT_VIEW_CLOSE(updv); return ray_error("type", "update by: group key must be a column name symbol"); }
ray_t* grp_col = ray_table_get_col(tbl, by_col_name);
if (!grp_col) { ray_release(tbl); DICT_VIEW_CLOSE(updv); return ray_error("domain", "update by: group key column not found"); }
int64_t nrows2 = ray_table_nrows(tbl);
ray_t* groups = NULL;
{
ray_t* gd = ray_group_indices_fn(grp_col);
if (!gd || RAY_IS_ERR(gd)) { ray_release(tbl); DICT_VIEW_CLOSE(updv); return gd ? gd : ray_error("oom", NULL); }
groups = groups_to_pair_list(gd);
ray_release(gd);
if (RAY_IS_ERR(groups)) { ray_release(tbl); DICT_VIEW_CLOSE(updv); return groups; }
}
int64_t ncols = ray_table_ncols(tbl);
int64_t ngroups = groups->len / 2;
ray_t** gdata = (ray_t**)ray_data(groups);
if (!gdata) { ray_release(groups); ray_release(tbl); DICT_VIEW_CLOSE(updv); return ray_error("oom", NULL); }
int64_t n_updates = 0;
for (int64_t d = 0; d + 1 < dict_n; d += 2) {
int64_t kid = dict_elems[d]->i64;
if (kid == from_id || kid == where_id || kid == by_id) continue;
n_updates++;
}
size_t upd_slots = (size_t)(n_updates ? n_updates : 1);
ray_t* upd_hdr = NULL;
int64_t* upd_names = (int64_t*)scratch_calloc(&upd_hdr,
upd_slots * sizeof(int64_t) +
upd_slots * sizeof(ray_t*) +
upd_slots * sizeof(uint8_t));
if (!upd_names) { ray_release(groups); ray_release(tbl); DICT_VIEW_CLOSE(updv); return ray_error("oom", NULL); }
ray_t** upd_cols = (ray_t**)(upd_names + upd_slots);
uint8_t* upd_used = (uint8_t*)(upd_cols + upd_slots);
#define UPDATE_BY_CLEANUP_COLS() do { \
for (int64_t _ui = 0; _ui < n_updates; _ui++) \
if (upd_cols[_ui]) ray_release(upd_cols[_ui]); \
scratch_free(upd_hdr); \
} while (0)
int64_t upd_i = 0;
for (int64_t d = 0; d + 1 < dict_n; d += 2) {
int64_t kid = dict_elems[d]->i64;
if (kid == from_id || kid == where_id || kid == by_id) continue;
ray_t* agg_expr = dict_elems[d + 1];
int8_t out_type = RAY_I64;
ray_t* target_col = ray_table_get_col(tbl, kid);
if (ngroups == 0 && target_col) out_type = target_col->type;
ray_t* out_col = ray_vec_new(out_type, nrows2);
if (RAY_IS_ERR(out_col)) { UPDATE_BY_CLEANUP_COLS(); ray_release(groups); ray_release(tbl); DICT_VIEW_CLOSE(updv); return out_col; }
if (ngroups == 0) {
out_col->len = nrows2;
memset(ray_data(out_col), 0,
(size_t)nrows2 * (size_t)ray_sym_elem_size(out_col->type, out_col->attrs));
}
int first_group = 1;
for (int64_t gi = 0; gi < ngroups; gi++) {
ray_t* idx_vec = gdata[gi * 2 + 1];
int64_t gsize = ray_len(idx_vec);
ray_t* sub_tbl = ray_table_new((int32_t)ncols);
if (RAY_IS_ERR(sub_tbl)) { ray_release(out_col); UPDATE_BY_CLEANUP_COLS(); ray_release(groups); ray_release(tbl); DICT_VIEW_CLOSE(updv); return sub_tbl; }
for (int64_t c = 0; c < ncols; c++) {
int64_t cn = ray_table_col_name(tbl, c);
ray_t* full_col = ray_table_get_col_idx(tbl, c);
int8_t ct = full_col->type;
ray_t* sub_col = (ct == RAY_SYM)
? ray_sym_vec_new(full_col->attrs & RAY_SYM_W_MASK, gsize)
: ray_vec_new(ct, gsize);
if (RAY_IS_ERR(sub_col)) { ray_release(sub_tbl); ray_release(out_col); UPDATE_BY_CLEANUP_COLS(); ray_release(groups); ray_release(tbl); DICT_VIEW_CLOSE(updv); return sub_col; }
if (ct == RAY_SYM)
ray_sym_vec_adopt_domain(sub_col, full_col);
sub_col->len = gsize;
int esz = ray_sym_elem_size(ct, full_col->attrs);
char* src = (char*)ray_data(full_col);
char* dst = (char*)ray_data(sub_col);
int64_t* idxs = (int64_t*)ray_data(idx_vec);
bool copied_null = false;
for (int64_t r = 0; r < gsize; r++) {
memcpy(dst + r * esz, src + idxs[r] * esz, esz);
copied_null |= ray_vec_is_null(full_col, idxs[r]);
}
if (copied_null)
sub_col->attrs |= RAY_ATTR_HAS_NULLS;
if (ct == RAY_STR)
col_propagate_str_pool(sub_col, full_col);
sub_tbl = ray_table_add_col(sub_tbl, cn, sub_col);
ray_release(sub_col);
if (RAY_IS_ERR(sub_tbl)) { ray_release(out_col); UPDATE_BY_CLEANUP_COLS(); ray_release(groups); ray_release(tbl); DICT_VIEW_CLOSE(updv); return sub_tbl; }
}
ray_t* agg_result = update_eval_on(sub_tbl, agg_expr);
ray_release(sub_tbl);
if (RAY_IS_ERR(agg_result)) { ray_release(out_col); UPDATE_BY_CLEANUP_COLS(); ray_release(groups); ray_release(tbl); DICT_VIEW_CLOSE(updv); return agg_result; }
agg_result = update_scalar_vec(agg_result, gsize);
if (first_group) {
if (ray_is_atom(agg_result)) out_type = -agg_result->type;
else if (ray_is_vec(agg_result)) out_type = agg_result->type;
ray_release(out_col);
out_col = ray_vec_new(out_type, nrows2);
if (RAY_IS_ERR(out_col)) { ray_release(agg_result); UPDATE_BY_CLEANUP_COLS(); ray_release(groups); ray_release(tbl); DICT_VIEW_CLOSE(updv); return out_col; }
out_col->len = nrows2;
memset(ray_data(out_col), 0,
(size_t)nrows2 * (size_t)ray_sym_elem_size(out_col->type, out_col->attrs));
first_group = 0;
}
{
int8_t rt = ray_is_atom(agg_result) ? (int8_t)-agg_result->type : agg_result->type;
if (rt != out_type && rt > 0) {
int rr = update_by_num_rank(rt), orr = update_by_num_rank(out_type);
if (!rr || !orr) {
ray_release(agg_result); ray_release(out_col);
UPDATE_BY_CLEANUP_COLS(); ray_release(groups); ray_release(tbl); DICT_VIEW_CLOSE(updv);
return ray_error("type", "update by: group result type %s does not match %s", ray_type_name(rt), ray_type_name(out_type));
}
if (rr > orr) {
ray_t* promoted = update_by_promote(out_col, rt);
if (RAY_IS_ERR(promoted)) { ray_release(agg_result); ray_release(out_col); UPDATE_BY_CLEANUP_COLS(); ray_release(groups); ray_release(tbl); DICT_VIEW_CLOSE(updv); return promoted; }
ray_release(out_col);
out_col = promoted;
out_type = rt;
}
}
}
int64_t* idxs = (int64_t*)ray_data(idx_vec);
ray_t* store_err = NULL;
if (ray_is_atom(agg_result)) {
for (int64_t r = 0; r < gsize && !store_err; r++) {
ray_t* nc = update_by_store(out_col, idxs[r], agg_result);
if (RAY_IS_ERR(nc)) store_err = nc; else out_col = nc;
}
} else if (ray_is_vec(agg_result) && ray_len(agg_result) == gsize) {
for (int64_t r = 0; r < gsize && !store_err; r++) {
int alloc = 0;
ray_t* cell = collection_elem(agg_result, r, &alloc);
ray_t* nc = update_by_store(out_col, idxs[r], cell);
if (RAY_IS_ERR(nc)) store_err = nc; else out_col = nc;
if (alloc) ray_release(cell);
}
} else {
int64_t got = ray_is_vec(agg_result) ? ray_len(agg_result) : -1;
ray_release(agg_result); ray_release(out_col);
UPDATE_BY_CLEANUP_COLS(); ray_release(groups); ray_release(tbl); DICT_VIEW_CLOSE(updv);
return ray_error("length", "update by: expression result length %lld does not match group size %lld", (long long)got, (long long)gsize);
}
ray_release(agg_result);
if (store_err) {
ray_release(out_col);
UPDATE_BY_CLEANUP_COLS(); ray_release(groups); ray_release(tbl); DICT_VIEW_CLOSE(updv);
return store_err;
}
}
upd_names[upd_i] = kid;
upd_cols[upd_i] = out_col;
upd_i++;
}
ray_t* result = ray_table_new((int32_t)(ncols + n_updates));
if (RAY_IS_ERR(result)) { UPDATE_BY_CLEANUP_COLS(); ray_release(groups); ray_release(tbl); DICT_VIEW_CLOSE(updv); return result; }
for (int64_t c = 0; c < ncols; c++) {
int64_t cn = ray_table_col_name(tbl, c);
int64_t ui = -1;
for (int64_t u = 0; u < n_updates; u++) {
if (upd_names[u] == cn) { ui = u; break; }
}
if (ui >= 0) {
result = ray_table_add_col(result, cn, upd_cols[ui]);
ray_release(upd_cols[ui]);
upd_cols[ui] = NULL;
upd_used[ui] = 1;
} else {
ray_t* col = ray_table_get_col_idx(tbl, c);
ray_retain(col);
result = ray_table_add_col(result, cn, col);
ray_release(col);
}
if (RAY_IS_ERR(result)) { UPDATE_BY_CLEANUP_COLS(); ray_release(groups); ray_release(tbl); DICT_VIEW_CLOSE(updv); return result; }
}
for (int64_t u = 0; u < n_updates; u++) {
if (upd_used[u]) continue;
result = ray_table_add_col(result, upd_names[u], upd_cols[u]);
ray_release(upd_cols[u]);
upd_cols[u] = NULL;
if (RAY_IS_ERR(result)) { UPDATE_BY_CLEANUP_COLS(); ray_release(groups); ray_release(tbl); DICT_VIEW_CLOSE(updv); return result; }
}
UPDATE_BY_CLEANUP_COLS();
#undef UPDATE_BY_CLEANUP_COLS
ray_release(groups);
if (inplace_sym >= 0 && result && !RAY_IS_ERR(result)) {
ray_env_set(inplace_sym, result);
ray_release(result);
ray_release(tbl);
DICT_VIEW_CLOSE(updv); return ray_sym(inplace_sym);
}
ray_release(tbl);
DICT_VIEW_CLOSE(updv); return result;
}
int64_t nrows = ray_table_nrows(tbl);
uint8_t* mask = NULL;
if (where_expr) {
bool fast_handled = false;
ray_t* fast = update_where_inplace(tbl, inplace_sym, dict, where_expr, &fast_handled);
if (fast_handled) { ray_release(tbl); return fast; }
}
if (where_expr) {
ray_t* mask_vec = NULL;
bool rejected = false;
ray_graph_t* g = ray_graph_new(tbl);
if (g) {
ray_op_t* pred = compile_expr_dag(g, where_expr);
if (pred) {
pred = ray_optimize(g, pred);
mask_vec = ray_execute(g, pred);
} else if (g->compile_err) {
mask_vec = graph_take_compile_err(g);
rejected = true;
}
ray_graph_free(g);
}
if (!rejected && (!mask_vec || RAY_IS_ERR(mask_vec))) {
int64_t ncols2 = ray_table_ncols(tbl);
ray_env_push_query_scope();
for (int64_t c = 0; c < ncols2; c++) {
int64_t cn = ray_table_col_name(tbl, c);
ray_t* col = ray_table_get_col_idx(tbl, c);
ray_env_set_query_local(cn, col);
}
mask_vec = ray_eval(where_expr);
ray_env_pop_scope();
}
if (!mask_vec || RAY_IS_ERR(mask_vec)) { ray_release(tbl); return mask_vec ? mask_vec : ray_error("type", "update: `where:` predicate evaluation failed"); }
mask_vec = where_mask_spread(mask_vec, nrows, "update");
if (!mask_vec || RAY_IS_ERR(mask_vec)) { ray_release(tbl); return mask_vec ? mask_vec : ray_error("oom", NULL); }
if (mask_vec->type != RAY_BOOL || mask_vec->len != nrows) {
int8_t mask_t = mask_vec->type;
ray_release(mask_vec);
ray_release(tbl);
return ray_error("type", "update: `where:` must produce a bool mask matching row count, got %s", ray_type_name(mask_t));
}
mask = (uint8_t*)ray_data(mask_vec);
int64_t ncols = ray_table_ncols(tbl);
DICT_VIEW_DECL(updw);
DICT_VIEW_OPEN(dict, updw);
if (DICT_VIEW_OVERFLOW(updw)) {
ray_release(mask_vec); ray_release(tbl);
DICT_VIEW_CLOSE(updw); return ray_error("oom", NULL);
}
int64_t dict_n = updw_n;
ray_t** dict_elems = updw;
int64_t from_id = ray_sym_intern("from", 4);
int64_t where_id = ray_sym_intern("where", 5);
ray_t* result = ray_table_new(ncols);
if (RAY_IS_ERR(result)) { ray_release(mask_vec); ray_release(tbl); DICT_VIEW_CLOSE(updw); return result; }
for (int64_t c = 0; c < ncols; c++) {
int64_t col_name = ray_table_col_name(tbl, c);
ray_t* orig_col = ray_table_get_col_idx(tbl, c);
ray_t* update_expr = NULL;
for (int64_t d = 0; d + 1 < dict_n; d += 2) {
int64_t kid = dict_elems[d]->i64;
if (kid == from_id || kid == where_id) continue;
if (kid == col_name) { update_expr = dict_elems[d + 1]; break; }
}
if (!update_expr) {
ray_retain(orig_col);
result = ray_table_add_col(result, col_name, orig_col);
ray_release(orig_col);
} else {
int8_t ct = orig_col->type;
ray_t* new_col = ray_vec_new(ct, nrows);
if (RAY_IS_ERR(new_col)) { ray_release(result); ray_release(mask_vec); ray_release(tbl); DICT_VIEW_CLOSE(updw); return new_col; }
ray_t* expr_vec = update_eval_on(tbl, update_expr);
if (!expr_vec || RAY_IS_ERR(expr_vec)) { ray_release(new_col); ray_release(result); ray_release(mask_vec); ray_release(tbl); DICT_VIEW_CLOSE(updw); return expr_vec ? expr_vec : ray_error("type", "update: failed to evaluate column update expression"); }
if (expr_vec->type >= 0 && ray_len(expr_vec) != nrows) {
int64_t got = ray_len(expr_vec);
ray_release(expr_vec); ray_release(new_col); ray_release(result); ray_release(mask_vec); ray_release(tbl);
DICT_VIEW_CLOSE(updw); return ray_error("length", "update: column has %lld values for %lld rows", (long long)got, (long long)nrows);
}
int8_t expr_type = (expr_vec->type < 0) ? -expr_vec->type : expr_vec->type;
if (expr_type != ct && expr_type > 0 && ray_is_vec(expr_vec)) {
int is_numeric_promo = (ct == RAY_I64 || ct == RAY_I32 || ct == RAY_F64) &&
(expr_type == RAY_I64 || expr_type == RAY_I32 || expr_type == RAY_F64);
if (!is_numeric_promo) {
ray_release(expr_vec); ray_release(new_col); ray_release(result); ray_release(mask_vec); ray_release(tbl);
DICT_VIEW_CLOSE(updw); return ray_error("type", "update: cannot assign %s expression to %s column", ray_type_name(expr_type), ray_type_name(ct));
}
int esz = ray_elem_size(ct);
memcpy(ray_data(new_col), ray_data(orig_col), (size_t)(nrows * esz));
new_col->len = nrows;
for (int64_t r = 0; r < nrows; r++) {
if (!mask[r]) continue;
if (ct == RAY_I64 && expr_type == RAY_F64)
((int64_t*)ray_data(new_col))[r] = ray_cast_f64_to_i64_null(((double*)ray_data(expr_vec))[r]);
else if (ct == RAY_I32 && expr_type == RAY_F64)
((int32_t*)ray_data(new_col))[r] = ray_cast_f64_to_i32_null(((double*)ray_data(expr_vec))[r]);
else if (ct == RAY_F64 && expr_type == RAY_I64)
((double*)ray_data(new_col))[r] = (double)((int64_t*)ray_data(expr_vec))[r];
}
for (int64_t r = 0; r < nrows; r++) {
ray_t* src = mask[r] ? expr_vec : orig_col;
if (ray_vec_is_null(src, r)) {
ray_vec_set_null(new_col, r, true);
switch (ct) {
case RAY_F64: ((double*)ray_data(new_col))[r] = NULL_F64; break;
case RAY_I64: case RAY_TIMESTAMP: ((int64_t*)ray_data(new_col))[r] = NULL_I64; break;
case RAY_I32: case RAY_DATE: case RAY_TIME:((int32_t*)ray_data(new_col))[r] = NULL_I32; break;
case RAY_I16: ((int16_t*)ray_data(new_col))[r] = NULL_I16; break;
default: break;
}
}
}
ray_release(expr_vec);
result = ray_table_add_col(result, col_name, new_col);
ray_release(new_col);
if (RAY_IS_ERR(result)) { ray_release(mask_vec); ray_release(tbl); DICT_VIEW_CLOSE(updw); return result; }
continue;
}
if (expr_vec->type < 0) {
int ok = (expr_vec->type == -ct);
if (!ok && ct == RAY_F64 && expr_vec->type == -RAY_I64) ok = 1;
if (!ok && ct == RAY_LIST && expr_vec->type == -RAY_SYM) ok = 1;
if (!ok && ct == RAY_SYM && expr_vec->type == -RAY_SYM) ok = 1;
if (!ok) {
int8_t ev_t = expr_vec->type;
ray_release(expr_vec); ray_release(new_col); ray_release(result); ray_release(mask_vec); ray_release(tbl);
DICT_VIEW_CLOSE(updw); return ray_error("type", "update: cannot assign %s value to %s column", ray_type_name(ev_t), ray_type_name(ct));
}
if (ct == RAY_LIST && expr_vec->type == -RAY_SYM) {
ray_free(new_col);
ray_t* new_list = ray_list_new((int32_t)nrows);
if (RAY_IS_ERR(new_list)) { ray_release(expr_vec); ray_release(result); ray_release(mask_vec); ray_release(tbl); DICT_VIEW_CLOSE(updw); return new_list; }
ray_t** orig_elems = (ray_t**)ray_data(orig_col);
for (int64_t r = 0; r < nrows; r++) {
ray_t* elem = mask[r] ? expr_vec : orig_elems[r];
ray_retain(elem);
new_list = ray_list_append(new_list, elem);
ray_release(elem);
if (RAY_IS_ERR(new_list)) { ray_release(expr_vec); ray_release(result); ray_release(mask_vec); ray_release(tbl); DICT_VIEW_CLOSE(updw); return new_list; }
}
ray_release(expr_vec);
result = ray_table_add_col(result, col_name, new_list);
ray_release(new_list);
if (RAY_IS_ERR(result)) { ray_release(mask_vec); ray_release(tbl); DICT_VIEW_CLOSE(updw); return result; }
continue;
}
ray_t* bcast = ray_vec_new(ct, nrows);
if (RAY_IS_ERR(bcast)) { ray_release(expr_vec); ray_release(new_col); ray_release(result); ray_release(mask_vec); ray_release(tbl); DICT_VIEW_CLOSE(updw); return bcast; }
if (ct == RAY_STR && expr_vec->type == -RAY_STR) {
const char* sp = ray_str_ptr(expr_vec);
size_t sl = ray_str_len(expr_vec);
for (int64_t r = 0; r < nrows; r++) {
bcast = ray_str_vec_append(bcast, sp, sl);
if (RAY_IS_ERR(bcast)) { ray_release(expr_vec); ray_release(new_col); ray_release(result); ray_release(mask_vec); ray_release(tbl); DICT_VIEW_CLOSE(updw); return bcast; }
}
} else {
uint8_t elem[16];
update_atom_cell(expr_vec, ct, ray_elem_size(ct), elem);
for (int64_t r = 0; r < nrows; r++) {
bcast = ray_vec_append(bcast, elem);
if (RAY_IS_ERR(bcast)) { ray_release(expr_vec); ray_release(new_col); ray_release(result); ray_release(mask_vec); ray_release(tbl); DICT_VIEW_CLOSE(updw); return bcast; }
}
}
if (RAY_ATOM_IS_NULL(expr_vec)) {
for (int64_t r = 0; r < nrows; r++)
ray_vec_set_null(bcast, r, true);
switch (ct) {
case RAY_F64: {
double* d = (double*)ray_data(bcast);
for (int64_t r = 0; r < nrows; r++) d[r] = NULL_F64;
break;
}
case RAY_I64: case RAY_TIMESTAMP: {
int64_t* d = (int64_t*)ray_data(bcast);
for (int64_t r = 0; r < nrows; r++) d[r] = NULL_I64;
break;
}
case RAY_I32: case RAY_DATE: case RAY_TIME: {
int32_t* d = (int32_t*)ray_data(bcast);
for (int64_t r = 0; r < nrows; r++) d[r] = NULL_I32;
break;
}
case RAY_I16: {
int16_t* d = (int16_t*)ray_data(bcast);
for (int64_t r = 0; r < nrows; r++) d[r] = NULL_I16;
break;
}
default: break;
}
}
ray_release(expr_vec);
expr_vec = bcast;
}
if (expr_vec->type == RAY_I64 && ct == RAY_F64) {
int64_t nr = ray_len(expr_vec);
ray_t* promoted = ray_vec_new(RAY_F64, nr);
if (RAY_IS_ERR(promoted)) { ray_release(expr_vec); ray_release(new_col); ray_release(result); ray_release(mask_vec); ray_release(tbl); DICT_VIEW_CLOSE(updw); return promoted; }
int64_t* src_data = (int64_t*)ray_data(expr_vec);
for (int64_t r = 0; r < nr; r++) {
double v = (double)src_data[r];
promoted = ray_vec_append(promoted, &v);
if (RAY_IS_ERR(promoted)) { ray_release(expr_vec); ray_release(new_col); ray_release(result); ray_release(mask_vec); ray_release(tbl); DICT_VIEW_CLOSE(updw); return promoted; }
}
double* dst = (double*)ray_data(promoted);
for (int64_t r = 0; r < nr; r++) {
if (ray_vec_is_null(expr_vec, r)) {
ray_vec_set_null(promoted, r, true);
dst[r] = NULL_F64;
}
}
ray_release(expr_vec);
expr_vec = promoted;
}
if (expr_vec->type != ct) {
int8_t ev_t = expr_vec->type;
ray_release(expr_vec); ray_release(new_col); ray_release(result); ray_release(mask_vec); ray_release(tbl);
DICT_VIEW_CLOSE(updw); return ray_error("type", "update: expression type %s does not match %s column", ray_type_name(ev_t), ray_type_name(ct));
}
if (ct == RAY_STR) {
for (int64_t r = 0; r < nrows; r++) {
ray_t* src_vec = mask[r] ? expr_vec : orig_col;
size_t slen = 0;
const char* sp = ray_str_vec_get(src_vec, r, &slen);
new_col = ray_str_vec_append(new_col, sp ? sp : "", sp ? slen : 0);
if (RAY_IS_ERR(new_col)) { ray_release(expr_vec); ray_release(result); ray_release(mask_vec); ray_release(tbl); DICT_VIEW_CLOSE(updw); return new_col; }
if (ray_vec_is_null(src_vec, r))
ray_vec_set_null(new_col, new_col->len - 1, true);
}
} else if (ct == RAY_SYM) {
for (int64_t r = 0; r < nrows; r++) {
ray_t* src_vec = mask[r] ? expr_vec : orig_col;
int64_t sym_val = sym_cell_runtime_id(src_vec, r);
new_col = ray_vec_append(new_col, &sym_val);
if (RAY_IS_ERR(new_col)) { ray_release(expr_vec); ray_release(result); ray_release(mask_vec); ray_release(tbl); DICT_VIEW_CLOSE(updw); return new_col; }
if (ray_vec_is_null(src_vec, r))
ray_vec_set_null(new_col, new_col->len - 1, true);
}
} else {
size_t elem_sz = ray_elem_size(ct);
uint8_t* orig_data = (uint8_t*)ray_data(orig_col);
uint8_t* expr_data = (uint8_t*)ray_data(expr_vec);
for (int64_t r = 0; r < nrows; r++) {
ray_t* src_vec = mask[r] ? expr_vec : orig_col;
uint8_t* base = mask[r] ? expr_data : orig_data;
new_col = ray_vec_append(new_col, base + r * elem_sz);
if (RAY_IS_ERR(new_col)) { ray_release(expr_vec); ray_release(result); ray_release(mask_vec); ray_release(tbl); DICT_VIEW_CLOSE(updw); return new_col; }
if (ray_vec_is_null(src_vec, r))
ray_vec_set_null(new_col, new_col->len - 1, true);
}
}
result = ray_table_add_col(result, col_name, new_col);
ray_release(new_col);
ray_release(expr_vec);
}
if (RAY_IS_ERR(result)) { ray_release(mask_vec); ray_release(tbl); DICT_VIEW_CLOSE(updw); return result; }
}
for (int64_t d = 0; d + 1 < dict_n; d += 2) {
int64_t kid = dict_elems[d]->i64;
if (kid == from_id || kid == where_id) continue;
bool exists = false;
for (int64_t c = 0; c < ncols; c++)
if (ray_table_col_name(tbl, c) == kid) { exists = true; break; }
if (exists) continue;
ray_t* v = update_eval_on(tbl, dict_elems[d + 1]);
if (v && !RAY_IS_ERR(v) && ray_is_atom(v)) {
ray_t* wide;
if (v->type == -RAY_STR) {
wide = broadcast_scalar(v, nrows);
} else {
ray_t* n_obj = make_i64(nrows);
wide = n_obj ? ray_take_fn(v, n_obj) : ray_error("oom", NULL);
if (n_obj) ray_release(n_obj);
}
ray_release(v);
v = wide;
}
if (v && !RAY_IS_ERR(v) && !ray_is_vec(v)) {
int8_t vt = v->type;
ray_release(v);
v = ray_error("type", "update: a new column under where: must be a typed vector, got %s",
ray_type_name(vt));
} else if (v && !RAY_IS_ERR(v) && v->len != nrows) {
int64_t got = v->len;
ray_release(v);
v = ray_error("length", "update: new column has %lld values for %lld rows",
(long long)got, (long long)nrows);
}
if (v && !RAY_IS_ERR(v)) v = ray_cow(v);
if (!v || RAY_IS_ERR(v)) {
ray_release(result); ray_release(mask_vec); ray_release(tbl); DICT_VIEW_CLOSE(updw);
return v ? v : ray_error("oom", NULL);
}
ray_t* nul = ray_typed_null((int8_t)-v->type);
for (int64_t r = 0; r < nrows && nul && !RAY_IS_ERR(nul); r++)
if (!mask[r]) store_typed_elem(v, r, nul);
if (nul && !RAY_IS_ERR(nul)) ray_release(nul);
result = ray_table_add_col(result, kid, v);
ray_release(v);
if (RAY_IS_ERR(result)) { ray_release(mask_vec); ray_release(tbl); DICT_VIEW_CLOSE(updw); return result; }
}
ray_release(mask_vec);
if (inplace_sym >= 0 && result && !RAY_IS_ERR(result)) {
ray_env_set(inplace_sym, result);
ray_release(result);
ray_release(tbl);
DICT_VIEW_CLOSE(updw); return ray_sym(inplace_sym);
}
ray_release(tbl);
DICT_VIEW_CLOSE(updw); return result;
}
int64_t ncols = ray_table_ncols(tbl);
DICT_VIEW_DECL(upda);
DICT_VIEW_OPEN(dict, upda);
if (DICT_VIEW_OVERFLOW(upda)) {
ray_release(tbl);
DICT_VIEW_CLOSE(upda); return ray_error("oom", NULL);
}
int64_t dict_n = upda_n;
ray_t** dict_elems = upda;
int64_t from_id = ray_sym_intern("from", 4);
ray_t* result = ray_table_new(ncols);
if (RAY_IS_ERR(result)) { ray_release(tbl); DICT_VIEW_CLOSE(upda); return result; }
for (int64_t c = 0; c < ncols; c++) {
int64_t col_name = ray_table_col_name(tbl, c);
ray_t* orig_col = ray_table_get_col_idx(tbl, c);
ray_t* update_expr = NULL;
for (int64_t d = 0; d + 1 < dict_n; d += 2) {
int64_t kid = dict_elems[d]->i64;
if (kid == from_id) continue;
if (kid == col_name) { update_expr = dict_elems[d + 1]; break; }
}
if (!update_expr) {
ray_retain(orig_col);
result = ray_table_add_col(result, col_name, orig_col);
ray_release(orig_col);
} else {
ray_t* expr_vec = update_eval_on(tbl, update_expr);
if (!expr_vec || RAY_IS_ERR(expr_vec)) { ray_release(result); ray_release(tbl); DICT_VIEW_CLOSE(upda); return expr_vec ? expr_vec : ray_error("type", "update: failed to evaluate column update expression"); }
if (expr_vec->type >= 0 && ray_len(expr_vec) != ray_table_nrows(tbl)) {
int64_t got = ray_len(expr_vec);
ray_release(expr_vec); ray_release(result); ray_release(tbl);
DICT_VIEW_CLOSE(upda); return ray_error("length", "update: column has %lld values for %lld rows", (long long)got, (long long)ray_table_nrows(tbl));
}
if (expr_vec->type < 0) {
int64_t nrows = ray_table_nrows(tbl);
int8_t ct = orig_col->type;
int ok = (expr_vec->type == -ct);
if (!ok && ct == RAY_F64 && expr_vec->type == -RAY_I64) ok = 1;
if (!ok && ct == RAY_LIST && expr_vec->type == -RAY_SYM) ok = 1;
if (!ok) {
int8_t ev_t = expr_vec->type;
ray_release(expr_vec); ray_release(result); ray_release(tbl);
DICT_VIEW_CLOSE(upda); return ray_error("type", "update: cannot assign %s value to %s column", ray_type_name(ev_t), ray_type_name(ct));
}
if (ct == RAY_LIST && expr_vec->type == -RAY_SYM) {
ray_t* bcast = ray_list_new((int32_t)nrows);
if (RAY_IS_ERR(bcast)) { ray_release(expr_vec); ray_release(result); ray_release(tbl); DICT_VIEW_CLOSE(upda); return bcast; }
for (int64_t r = 0; r < nrows; r++) {
ray_retain(expr_vec);
bcast = ray_list_append(bcast, expr_vec);
ray_release(expr_vec);
if (RAY_IS_ERR(bcast)) { ray_release(expr_vec); ray_release(result); ray_release(tbl); DICT_VIEW_CLOSE(upda); return bcast; }
}
ray_release(expr_vec);
expr_vec = bcast;
goto no_where_add_col;
}
ray_t* bcast = ray_vec_new(ct, nrows);
if (RAY_IS_ERR(bcast)) { ray_release(expr_vec); ray_release(result); ray_release(tbl); DICT_VIEW_CLOSE(upda); return bcast; }
if (ct == RAY_STR && expr_vec->type == -RAY_STR) {
const char* sp = ray_str_ptr(expr_vec);
size_t sl = ray_str_len(expr_vec);
for (int64_t r = 0; r < nrows; r++) {
bcast = ray_str_vec_append(bcast, sp, sl);
if (RAY_IS_ERR(bcast)) { ray_release(expr_vec); ray_release(result); ray_release(tbl); DICT_VIEW_CLOSE(upda); return bcast; }
}
} else {
uint8_t elem[16];
update_atom_cell(expr_vec, ct, ray_elem_size(ct), elem);
for (int64_t r = 0; r < nrows; r++) {
bcast = ray_vec_append(bcast, elem);
if (RAY_IS_ERR(bcast)) { ray_release(expr_vec); ray_release(result); ray_release(tbl); DICT_VIEW_CLOSE(upda); return bcast; }
}
}
if (RAY_ATOM_IS_NULL(expr_vec)) {
for (int64_t r = 0; r < nrows; r++)
ray_vec_set_null(bcast, r, true);
switch (ct) {
case RAY_F64: {
double* d = (double*)ray_data(bcast);
for (int64_t r = 0; r < nrows; r++) d[r] = NULL_F64;
break;
}
case RAY_I64: case RAY_TIMESTAMP: {
int64_t* d = (int64_t*)ray_data(bcast);
for (int64_t r = 0; r < nrows; r++) d[r] = NULL_I64;
break;
}
case RAY_I32: case RAY_DATE: case RAY_TIME: {
int32_t* d = (int32_t*)ray_data(bcast);
for (int64_t r = 0; r < nrows; r++) d[r] = NULL_I32;
break;
}
case RAY_I16: {
int16_t* d = (int16_t*)ray_data(bcast);
for (int64_t r = 0; r < nrows; r++) d[r] = NULL_I16;
break;
}
default: break;
}
}
ray_release(expr_vec);
expr_vec = bcast;
}
if (expr_vec->type == RAY_I64 && orig_col->type == RAY_F64) {
int64_t nr = ray_len(expr_vec);
ray_t* promoted = ray_vec_new(RAY_F64, nr);
if (RAY_IS_ERR(promoted)) { ray_release(expr_vec); ray_release(result); ray_release(tbl); DICT_VIEW_CLOSE(upda); return promoted; }
int64_t* src_data = (int64_t*)ray_data(expr_vec);
for (int64_t r = 0; r < nr; r++) {
double v = (double)src_data[r];
promoted = ray_vec_append(promoted, &v);
if (RAY_IS_ERR(promoted)) { ray_release(expr_vec); ray_release(result); ray_release(tbl); DICT_VIEW_CLOSE(upda); return promoted; }
}
double* dst = (double*)ray_data(promoted);
for (int64_t r = 0; r < nr; r++) {
if (ray_vec_is_null(expr_vec, r)) {
ray_vec_set_null(promoted, r, true);
dst[r] = NULL_F64;
}
}
ray_release(expr_vec);
expr_vec = promoted;
}
if (expr_vec->type > 0 && expr_vec->type != orig_col->type) {
int is_ok = 0;
if ((orig_col->type == RAY_I64 || orig_col->type == RAY_I32 || orig_col->type == RAY_F64) &&
(expr_vec->type == RAY_I64 || expr_vec->type == RAY_I32 || expr_vec->type == RAY_F64))
is_ok = 1;
if ((orig_col->type == RAY_SYM || orig_col->type == RAY_LIST) &&
(expr_vec->type == RAY_SYM || expr_vec->type == RAY_LIST))
is_ok = 1;
if (!is_ok) {
int8_t ev_t = expr_vec->type, oc_t = orig_col->type;
ray_release(expr_vec); ray_release(result); ray_release(tbl);
DICT_VIEW_CLOSE(upda); return ray_error("type", "update: expression type %s does not match %s column", ray_type_name(ev_t), ray_type_name(oc_t));
}
}
no_where_add_col:
result = ray_table_add_col(result, col_name, expr_vec);
ray_release(expr_vec);
}
if (RAY_IS_ERR(result)) { ray_release(tbl); DICT_VIEW_CLOSE(upda); return result; }
}
for (int64_t d = 0; d + 1 < dict_n; d += 2) {
int64_t kid = dict_elems[d]->i64;
if (kid == from_id) continue;
int exists = 0;
for (int64_t c = 0; c < ncols; c++) {
if (ray_table_col_name(tbl, c) == kid) { exists = 1; break; }
}
if (exists) continue;
ray_t* update_expr = dict_elems[d + 1];
ray_t* expr_vec = update_eval_on(tbl, update_expr);
if (!expr_vec || RAY_IS_ERR(expr_vec)) { ray_release(result); ray_release(tbl); DICT_VIEW_CLOSE(upda); return expr_vec ? expr_vec : ray_error("type", "update: failed to evaluate new column expression"); }
if (expr_vec->type >= 0 && ray_len(expr_vec) != ray_table_nrows(tbl)) {
int64_t got = ray_len(expr_vec);
ray_release(expr_vec); ray_release(result); ray_release(tbl);
DICT_VIEW_CLOSE(upda); return ray_error("length", "update: new column has %lld values for %lld rows", (long long)got, (long long)ray_table_nrows(tbl));
}
if (expr_vec->type < 0) {
int64_t nrows = ray_table_nrows(tbl);
int8_t ct = -expr_vec->type;
ray_t* bcast = ray_vec_new(ct, nrows);
if (RAY_IS_ERR(bcast)) { ray_release(expr_vec); ray_release(result); ray_release(tbl); DICT_VIEW_CLOSE(upda); return bcast; }
if (ct == RAY_STR) {
const char* sp = (expr_vec->type == -RAY_STR) ? ray_str_ptr(expr_vec) : "";
size_t sl = (expr_vec->type == -RAY_STR) ? ray_str_len(expr_vec) : 0;
for (int64_t r = 0; r < nrows; r++) {
bcast = ray_str_vec_append(bcast, sp, sl);
if (RAY_IS_ERR(bcast)) { ray_release(expr_vec); ray_release(result); ray_release(tbl); DICT_VIEW_CLOSE(upda); return bcast; }
}
} else {
uint8_t elem[16];
update_atom_cell(expr_vec, ct, ray_elem_size(ct), elem);
for (int64_t r = 0; r < nrows; r++) {
bcast = ray_vec_append(bcast, elem);
if (RAY_IS_ERR(bcast)) { ray_release(expr_vec); ray_release(result); ray_release(tbl); DICT_VIEW_CLOSE(upda); return bcast; }
}
}
if (RAY_ATOM_IS_NULL(expr_vec)) {
for (int64_t r = 0; r < nrows; r++)
ray_vec_set_null(bcast, r, true);
switch (ct) {
case RAY_F64: {
double* d = (double*)ray_data(bcast);
for (int64_t r = 0; r < nrows; r++) d[r] = NULL_F64;
break;
}
case RAY_I64: case RAY_TIMESTAMP: {
int64_t* d = (int64_t*)ray_data(bcast);
for (int64_t r = 0; r < nrows; r++) d[r] = NULL_I64;
break;
}
case RAY_I32: case RAY_DATE: case RAY_TIME: {
int32_t* d = (int32_t*)ray_data(bcast);
for (int64_t r = 0; r < nrows; r++) d[r] = NULL_I32;
break;
}
case RAY_I16: {
int16_t* d = (int16_t*)ray_data(bcast);
for (int64_t r = 0; r < nrows; r++) d[r] = NULL_I16;
break;
}
default: break;
}
}
ray_release(expr_vec);
expr_vec = bcast;
}
result = ray_table_add_col(result, kid, expr_vec);
ray_release(expr_vec);
if (RAY_IS_ERR(result)) { ray_release(tbl); DICT_VIEW_CLOSE(upda); return result; }
}
if (inplace_sym >= 0 && result && !RAY_IS_ERR(result)) {
ray_env_set(inplace_sym, result);
ray_release(result);
ray_release(tbl);
DICT_VIEW_CLOSE(upda); return ray_sym(inplace_sym);
}
ray_release(tbl);
DICT_VIEW_CLOSE(upda); return result;
}
static bool table_has_parted_columns(ray_t* tbl) {
if (!tbl || RAY_IS_ERR(tbl) || tbl->type != RAY_TABLE) return false;
int64_t ncols = ray_table_ncols(tbl);
for (int64_t c = 0; c < ncols; c++) {
ray_t* col = ray_table_get_col_idx(tbl, c);
if (col && (col->type == RAY_MAPCOMMON || RAY_IS_PARTED(col->type)))
return true;
}
return false;
}
static ray_t* query_materialize_parted_table(ray_t* tbl) {
int64_t nc = ray_table_ncols(tbl);
ray_t* flat_tbl = ray_table_new(nc);
if (!flat_tbl || RAY_IS_ERR(flat_tbl)) return flat_tbl ? flat_tbl : ray_error("oom", NULL);
for (int64_t c = 0; c < nc; c++) {
ray_t* flat_col = query_materialize_parted_col(ray_table_get_col_idx(tbl, c));
if (!flat_col || RAY_IS_ERR(flat_col)) {
ray_release(flat_tbl);
return flat_col ? flat_col : ray_error("oom", NULL);
}
flat_tbl = ray_table_add_col(flat_tbl, ray_table_col_name(tbl, c), flat_col);
ray_release(flat_col);
if (!flat_tbl || RAY_IS_ERR(flat_tbl)) return flat_tbl ? flat_tbl : ray_error("oom", NULL);
}
return flat_tbl;
}
typedef struct {
ray_t* mapcommon;
ray_t* key_values;
ray_t* row_counts;
int64_t ncols;
int64_t ndata;
int64_t nparts;
int64_t total_rows;
bool active_missing;
int8_t key_type;
struct ray_sym_domain_s* sym_dom;
ray_t* sym_rep;
} parted_insert_view_t;
static int byte_string_cmp(const char* a, size_t an,
const char* b, size_t bn) {
size_t n = an < bn ? an : bn;
int cmp = n ? memcmp(a, b, n) : 0;
if (cmp < 0) return -1;
if (cmp > 0) return 1;
return (an > bn) - (an < bn);
}
static int parted_key_cells_cmp(ray_t* keys, int64_t a, int64_t b,
bool* valid) {
*valid = true;
if (keys->type == RAY_DATE) {
int32_t av = ((int32_t*)ray_data(keys))[a];
int32_t bv = ((int32_t*)ray_data(keys))[b];
if (av == NULL_I32 || bv == NULL_I32) { *valid = false; return 0; }
return (av > bv) - (av < bv);
}
if (keys->type == RAY_I64) {
int64_t av = ((int64_t*)ray_data(keys))[a];
int64_t bv = ((int64_t*)ray_data(keys))[b];
if (av == NULL_I64 || bv == NULL_I64) { *valid = false; return 0; }
return (av > bv) - (av < bv);
}
if (keys->type == RAY_SYM) {
ray_t* av = ray_sym_vec_cell(keys, a);
ray_t* bv = ray_sym_vec_cell(keys, b);
if (!av || !bv || ray_str_len(av) == 0 || ray_str_len(bv) == 0) {
*valid = false;
return 0;
}
return byte_string_cmp(ray_str_ptr(av), ray_str_len(av),
ray_str_ptr(bv), ray_str_len(bv));
}
*valid = false;
return 0;
}
static int parted_key_atom_cmp(ray_t* key, ray_t* keys, int64_t cell,
bool* valid) {
*valid = true;
if (keys->type == RAY_DATE) {
int32_t cv = ((int32_t*)ray_data(keys))[cell];
if (cv == NULL_I32 || RAY_ATOM_IS_NULL(key)) { *valid = false; return 0; }
return (key->i32 > cv) - (key->i32 < cv);
}
if (keys->type == RAY_I64) {
int64_t cv = ((int64_t*)ray_data(keys))[cell];
if (cv == NULL_I64 || RAY_ATOM_IS_NULL(key)) { *valid = false; return 0; }
return (key->i64 > cv) - (key->i64 < cv);
}
if (keys->type == RAY_SYM) {
ray_t* ka = ray_sym_str(key->i64);
ray_t* cv = ray_sym_vec_cell(keys, cell);
if (!ka || !cv || ray_str_len(ka) == 0 || ray_str_len(cv) == 0) {
*valid = false;
return 0;
}
return byte_string_cmp(ray_str_ptr(ka), ray_str_len(ka),
ray_str_ptr(cv), ray_str_len(cv));
}
*valid = false;
return 0;
}
static bool parted_segment_attrs_valid(int8_t base, uint8_t attrs) {
uint8_t allowed = base == RAY_LIST
? 0
: (RAY_ATTR_HAS_INDEX | RAY_ATTR_SORTED);
if (base == RAY_SYM) allowed |= RAY_SYM_W_MASK;
if (base == RAY_I32 || base == RAY_I64)
allowed |= RAY_ATTR_HAS_LINK;
if (base == RAY_I16 || base == RAY_I32 || base == RAY_I64 ||
base == RAY_F32 || base == RAY_F64 || base == RAY_DATE ||
base == RAY_TIME || base == RAY_TIMESTAMP || base == RAY_GUID ||
base == RAY_SYM || base == RAY_STR)
allowed |= RAY_ATTR_HAS_NULLS;
return (attrs & (uint8_t)~allowed) == 0;
}
static ray_t* validate_parted_insert_target(ray_t* tbl,
parted_insert_view_t* out) {
memset(out, 0, sizeof(*out));
if (tbl->len != 2 || tbl->attrs != 0)
return ray_error("corrupt", "insert: parted target has non-canonical table metadata");
ray_t** table_slots = (ray_t**)ray_data(tbl);
ray_t* schema = table_slots[0];
ray_t* columns = table_slots[1];
if (!schema || RAY_IS_ERR(schema) || schema->type != RAY_I64 ||
schema->attrs != 0 || schema->len < 0 ||
!columns || RAY_IS_ERR(columns) || columns->type != RAY_LIST ||
columns->attrs != 0 || columns->len != schema->len)
return ray_error("corrupt", "insert: parted target has a malformed table container");
int64_t ncols = ray_table_ncols(tbl);
if (ncols < 2)
return ray_error("type", "insert: parted target must have a MAPCOMMON column and at least one physical column");
ray_t* mc = ray_table_get_col_idx(tbl, 0);
if (!mc || mc->type != RAY_MAPCOMMON || mc->len != 2 ||
mc->attrs > RAY_MC_I64)
return ray_error("type", "insert: three-argument table form requires a canonical .db.parted.get table");
ray_t** mp = (ray_t**)ray_data(mc);
ray_t* keys = mp[0];
ray_t* counts = mp[1];
int8_t key_type = mc->attrs == RAY_MC_DATE ? RAY_DATE
: mc->attrs == RAY_MC_I64 ? RAY_I64
: RAY_SYM;
uint8_t key_attrs = key_type == RAY_SYM ? RAY_SYM_W64 : 0;
if (!keys || RAY_IS_ERR(keys) || keys->type != key_type ||
keys->attrs != key_attrs ||
!counts || RAY_IS_ERR(counts) || counts->type != RAY_I64 ||
counts->attrs != 0 ||
keys->len <= 0 || counts->len != keys->len)
return ray_error("corrupt", "insert: malformed MAPCOMMON partition metadata");
if (key_type == RAY_SYM &&
ray_sym_vec_domain(keys) != ray_sym_runtime_domain())
return ray_error("corrupt", "insert: MAPCOMMON symbol keys must use the runtime domain");
for (int64_t c = 0; c < ncols; c++) {
int64_t name = ray_table_col_name(tbl, c);
if (name < 0)
return ray_error("corrupt", "insert: parted target has an invalid column name at index %lld", (long long)c);
for (int64_t d = c + 1; d < ncols; d++)
if (ray_table_col_name(tbl, d) == name)
return ray_error("corrupt", "insert: parted target has duplicate column names");
}
int64_t nparts = keys->len;
int64_t* rc = (int64_t*)ray_data(counts);
int64_t total = 0;
for (int64_t p = 0; p < nparts; p++) {
if (rc[p] < 0 || rc[p] == NULL_I64 || total > INT64_MAX - rc[p])
return ray_error("corrupt", "insert: invalid MAPCOMMON row count at partition %lld", (long long)p);
total += rc[p];
if (p > 0) {
bool valid = false;
int cmp = parted_key_cells_cmp(keys, p - 1, p, &valid);
if (!valid || cmp >= 0)
return ray_error("corrupt", "insert: MAPCOMMON partition keys are not strictly increasing");
} else if (key_type == RAY_DATE) {
if (((int32_t*)ray_data(keys))[0] == NULL_I32)
return ray_error("corrupt", "insert: MAPCOMMON contains a null partition key");
} else if (key_type == RAY_I64) {
if (((int64_t*)ray_data(keys))[0] == NULL_I64)
return ray_error("corrupt", "insert: MAPCOMMON contains a null partition key");
} else {
ray_t* first = ray_sym_vec_cell(keys, 0);
if (!first || ray_str_len(first) == 0)
return ray_error("corrupt", "insert: MAPCOMMON contains an invalid partition key");
}
}
struct ray_sym_domain_s* common_dom = NULL;
ray_t* common_rep = NULL;
bool active_missing = false;
for (int64_t c = 1; c < ncols; c++) {
ray_t* wrapper = ray_table_get_col_idx(tbl, c);
if (!wrapper || !RAY_IS_PARTED(wrapper->type) ||
wrapper->len != nparts || wrapper->attrs != 0)
return ray_error("corrupt", "insert: physical column %lld is not a canonical PARTED wrapper", (long long)(c - 1));
int8_t base = (int8_t)RAY_PARTED_BASETYPE(wrapper->type);
if (base != RAY_LIST && (base < RAY_BOOL || base > RAY_STR))
return ray_error("type", "insert: unsupported parted base type in physical column %lld", (long long)(c - 1));
ray_t** segs = (ray_t**)ray_data(wrapper);
if (!segs[0])
return ray_error("corrupt", "insert: physical column %lld has no first-partition segment", (long long)(c - 1));
for (int64_t p = 0; p < nparts; p++) {
ray_t* seg = segs[p];
if (!seg) {
if (p != nparts - 1)
return ray_error("corrupt", "insert: physical column %lld is missing historical partition %lld",
(long long)(c - 1), (long long)p);
active_missing = true;
continue;
}
if (RAY_IS_ERR(seg) || seg->type != base ||
!parted_segment_attrs_valid(base, seg->attrs) ||
seg->len != rc[p])
return ray_error("corrupt", "insert: segment metadata/length/type mismatch in physical column %lld partition %lld",
(long long)(c - 1), (long long)p);
if (seg->attrs & RAY_ATTR_HAS_INDEX) {
ray_t* index = seg->index;
if (!index || (uintptr_t)index <= 31 ||
RAY_IS_ERR(index) || index->type != RAY_INDEX)
return ray_error("corrupt", "insert: physical column %lld partition %lld has malformed index metadata",
(long long)(c - 1), (long long)p);
}
if (base == RAY_SYM) {
struct ray_sym_domain_s* dom = ray_sym_vec_domain(seg);
if (dom == ray_sym_runtime_domain() || !ray_sym_domain_path(dom))
return ray_error("corrupt", "insert: parted SYM segments must use a FILE domain");
if (!common_dom) { common_dom = dom; common_rep = seg; }
else if (common_dom != dom)
return ray_error("corrupt", "insert: parted SYM segments do not share one FILE domain");
}
}
}
if (common_dom) {
ray_t* empty = ray_sym_domain_str(common_dom, 0);
if (ray_sym_domain_count(common_dom) <= 0 || !empty || ray_str_len(empty) != 0)
return ray_error("corrupt", "insert: parted FILE symbol domain lacks the reserved empty symbol");
}
out->mapcommon = mc;
out->key_values = keys;
out->row_counts = counts;
out->ncols = ncols;
out->ndata = ncols - 1;
out->nparts = nparts;
out->total_rows = total;
out->active_missing = active_missing;
out->key_type = key_type;
out->sym_dom = common_dom;
out->sym_rep = common_rep;
return NULL;
}
static ray_t* decide_parted_insert_partition(parted_insert_view_t* v,
ray_t* key,
bool* new_partition) {
if (!key || RAY_IS_ERR(key) || RAY_IS_NULL(key) ||
key->type != -v->key_type || RAY_ATOM_IS_NULL(key))
return ray_error("type", "insert: partition key must be a non-null %s atom, got %s",
ray_type_name(-v->key_type),
key ? ray_type_name(key->type) : "null");
bool valid = false;
int cmp = parted_key_atom_cmp(key, v->key_values, v->nparts - 1, &valid);
if (!valid)
return ray_error("domain", "insert: partition key is not representable in the target key domain");
if (cmp < 0)
return ray_error("domain", "insert: partition key is historical/out of order; only the last or a later partition may grow");
*new_partition = cmp > 0;
return NULL;
}
static ray_t* parted_values_append(ray_t* vals, ray_t* value, bool owned) {
if (!value || RAY_IS_ERR(value)) {
if (owned && value && !RAY_IS_ERR(value)) ray_release(value);
ray_release(vals);
return value && RAY_IS_ERR(value) ? value
: ray_error("type", "insert: physical payload contains a missing value");
}
ray_t* next = ray_list_append(vals, value);
if (owned) ray_release(value);
if (!next || RAY_IS_ERR(next)) {
ray_release(vals);
return next ? next : ray_error("oom", NULL);
}
return next;
}
static ray_t* normalize_parted_insert_rows(ray_t* tbl,
parted_insert_view_t* v,
ray_t* rows) {
if (!rows || RAY_IS_ERR(rows) ||
(rows->type != RAY_LIST && rows->type != RAY_TABLE &&
rows->type != RAY_DICT))
return ray_error("type", "insert: parted rows must be a list, table or dict, got %s",
rows ? ray_type_name(rows->type) : "null");
ray_t* vals = ray_list_new(v->ndata);
if (!vals || RAY_IS_ERR(vals)) return vals ? vals : ray_error("oom", NULL);
if (rows->type == RAY_LIST) {
if (rows->len != v->ndata) {
ray_release(vals);
return ray_error("domain", "insert: parted row list has %lld values, expected %lld physical columns",
(long long)rows->len, (long long)v->ndata);
}
ray_t** items = (ray_t**)ray_data(rows);
for (int64_t c = 0; c < v->ndata; c++) {
ray_t* wrapper = ray_table_get_col_idx(tbl, c + 1);
if (RAY_PARTED_BASETYPE(wrapper->type) == RAY_LIST) {
ray_t* one = ray_list_new(1);
if (!one || RAY_IS_ERR(one)) {
ray_release(vals);
return one ? one : ray_error("oom", NULL);
}
one = ray_list_append(one, items[c]);
if (!one || RAY_IS_ERR(one)) {
ray_release(vals);
return one ? one : ray_error("oom", NULL);
}
vals = parted_values_append(vals, one, true);
} else {
vals = parted_values_append(vals, items[c], false);
}
if (!vals || RAY_IS_ERR(vals)) return vals;
}
return vals;
}
if (rows->type == RAY_TABLE) {
int64_t src_n = ray_table_ncols(rows);
if (src_n != v->ndata) {
ray_release(vals);
return ray_error("domain", "insert: parted row table has %lld columns, expected exactly %lld physical columns",
(long long)src_n, (long long)v->ndata);
}
for (int64_t sc = 0; sc < src_n; sc++) {
int64_t name = ray_table_col_name(rows, sc);
int64_t src_matches = 0, target_matches = 0;
for (int64_t i = 0; i < src_n; i++)
if (ray_table_col_name(rows, i) == name) src_matches++;
for (int64_t i = 1; i < v->ncols; i++)
if (ray_table_col_name(tbl, i) == name) target_matches++;
if (src_matches != 1 || target_matches != 1) {
ray_release(vals);
return ray_error("value", "insert: parted row table schema must exactly and uniquely match physical columns");
}
}
for (int64_t c = 1; c < v->ncols; c++) {
ray_t* col = ray_table_get_col(rows, ray_table_col_name(tbl, c));
vals = parted_values_append(vals, col, false);
if (!vals || RAY_IS_ERR(vals)) return vals;
}
return vals;
}
ray_t* keys = ray_dict_keys(rows);
ray_t* dvals = ray_dict_vals(rows);
if (!keys || keys->type != RAY_SYM || !dvals ||
(dvals->type != RAY_LIST && !ray_is_vec(dvals)) ||
keys->len != v->ndata || dvals->len != keys->len) {
ray_release(vals);
return ray_error("type", "insert: parted row dict must have one symbol key/value per physical column");
}
for (int64_t d = 0; d < keys->len; d++) {
int64_t name = sym_cell_runtime_id(keys, d);
int64_t dict_matches = 0, target_matches = 0;
for (int64_t i = 0; i < keys->len; i++)
if (sym_cell_runtime_id(keys, i) == name) dict_matches++;
for (int64_t i = 1; i < v->ncols; i++)
if (ray_table_col_name(tbl, i) == name) target_matches++;
if (name < 0 || dict_matches != 1 || target_matches != 1) {
ray_release(vals);
return ray_error("value", "insert: parted row dict schema must exactly and uniquely match physical columns");
}
}
for (int64_t c = 1; c < v->ncols; c++) {
int64_t want = ray_table_col_name(tbl, c);
int64_t found = -1;
for (int64_t d = 0; d < keys->len; d++)
if (sym_cell_runtime_id(keys, d) == want) { found = d; break; }
if (found < 0) {
ray_release(vals);
return ray_error("value", "insert: parted row dict is missing a physical column");
}
if (dvals->type == RAY_LIST) {
ray_t* item = ((ray_t**)ray_data(dvals))[found];
ray_t* wrapper = ray_table_get_col_idx(tbl, c);
if (RAY_PARTED_BASETYPE(wrapper->type) == RAY_LIST &&
item && item->type == RAY_LIST) {
ray_t* one = ray_list_new(1);
if (!one || RAY_IS_ERR(one)) {
ray_release(vals);
return one ? one : ray_error("oom", NULL);
}
one = ray_list_append(one, item);
if (!one || RAY_IS_ERR(one)) {
ray_release(vals);
return one ? one : ray_error("oom", NULL);
}
vals = parted_values_append(vals, one, true);
} else {
vals = parted_values_append(vals, item, false);
}
} else {
int owned = 0;
ray_t* item = collection_elem(dvals, found, &owned);
vals = parted_values_append(vals, item, owned != 0);
}
if (!vals || RAY_IS_ERR(vals)) return vals;
}
return vals;
}
static bool parted_value_is_scalar(ray_t* v) {
return v && !RAY_IS_ERR(v) &&
(RAY_IS_NULL(v) || (v->type != RAY_LIST && !ray_is_vec(v)));
}
static bool parted_source_vec_compatible(int8_t dst, int8_t src) {
return src == dst;
}
static ray_t* validate_parted_payload_atom(int8_t dst, ray_t* atom,
int64_t physical_col) {
if (!atom || RAY_IS_ERR(atom) ||
(!RAY_IS_NULL(atom) && atom->type >= 0))
return ray_error("type", "insert: physical column %lld payload cells must be atoms",
(long long)physical_col);
if (RAY_IS_NULL(atom)) {
if (dst == RAY_BOOL || dst == RAY_U8)
return ray_error("type", "insert: physical column %lld (%s) has no null representation",
(long long)physical_col, ray_type_name(dst));
return NULL;
}
if (atom->type != -dst)
return ray_error("type", "insert: value type must exactly match physical column %lld (%s), got %s",
(long long)physical_col, ray_type_name(dst),
ray_type_name(atom->type));
if (dst == RAY_GUID &&
(!atom->obj || RAY_IS_ERR(atom->obj) ||
atom->obj->type != RAY_U8 || atom->obj->len != 16))
return ray_error("corrupt", "insert: GUID payload atom must own exactly 16 U8 bytes");
if (RAY_ATOM_IS_NULL(atom)) {
if (dst == RAY_BOOL || dst == RAY_U8)
return ray_error("type", "insert: physical column %lld (%s) has no null representation",
(long long)physical_col, ray_type_name(dst));
return NULL;
}
if (dst == RAY_SYM && !ray_sym_str(atom->i64))
return ray_error("domain", "insert: SYM payload atom has an invalid runtime id");
if ((dst == RAY_F32 && !isfinite((float)atom->f64)) ||
(dst == RAY_F64 && !isfinite(atom->f64)))
return ray_error("domain", "insert: non-finite float payload must use the typed-null representation");
if (dst >= RAY_BOOL && dst <= RAY_STR) return NULL;
return ray_error("type", "insert: unsupported physical column %lld (%s), got %s",
(long long)physical_col, ray_type_name(dst),
ray_type_name(atom->type));
}
static ray_t* validate_parted_payloads(ray_t* tbl,
parted_insert_view_t* v,
ray_t* vals,
int64_t* out_batch) {
ray_t** pv = (ray_t**)ray_data(vals);
bool have_collection = false;
int64_t batch = 1;
for (int64_t c = 0; c < v->ndata; c++) {
ray_t* payload = pv[c];
if (parted_value_is_scalar(payload)) continue;
if (!payload || RAY_IS_ERR(payload) ||
(payload->type != RAY_LIST && !ray_is_vec(payload)))
return ray_error("type", "insert: physical column %lld payload must be an atom or an exactly typed vector",
(long long)c);
if (payload->len < 0)
return ray_error("corrupt", "insert: physical column %lld payload has a negative vector length",
(long long)c);
if (!have_collection) { batch = payload->len; have_collection = true; }
else if (payload->len != batch)
return ray_error("length", "insert: all non-scalar physical payload columns must have the same length");
}
for (int64_t c = 0; c < v->ndata; c++) {
ray_t* wrapper = ray_table_get_col_idx(tbl, c + 1);
int8_t dst = (int8_t)RAY_PARTED_BASETYPE(wrapper->type);
ray_t* payload = pv[c];
if (dst == RAY_LIST) {
if (!parted_value_is_scalar(payload) && payload->type != RAY_LIST)
return ray_error("type", "insert: physical column %lld LIST payload must contain boxed rows",
(long long)c);
continue;
}
if (!parted_value_is_scalar(payload) &&
!parted_source_vec_compatible(dst, payload->type))
return ray_error("type", "insert: physical column %lld vector type must exactly match %s",
(long long)c, ray_type_name(dst));
if (!parted_value_is_scalar(payload)) {
ray_t* owner = (payload->attrs & RAY_ATTR_SLICE)
? payload->slice_parent : payload;
if ((dst == RAY_BOOL || dst == RAY_U8) &&
(!owner || ray_vec_may_have_nulls(owner)))
return ray_error("type", "insert: physical column %lld (%s) has no null representation",
(long long)c, ray_type_name(dst));
if (dst == RAY_SYM) {
for (int64_t r = 0; r < payload->len; r++)
if (!ray_sym_vec_cell(payload, r))
return ray_error("domain", "insert: physical column %lld SYM vector contains an invalid domain position",
(long long)c);
} else if (dst == RAY_F32) {
const float* d = (const float*)ray_data(payload);
for (int64_t r = 0; r < payload->len; r++)
if (!isfinite(d[r]) && !isnan(d[r]))
return ray_error("domain", "insert: physical column %lld F32 vector contains infinity",
(long long)c);
} else if (dst == RAY_F64) {
const double* d = (const double*)ray_data(payload);
for (int64_t r = 0; r < payload->len; r++)
if (!isfinite(d[r]) && !isnan(d[r]))
return ray_error("domain", "insert: physical column %lld F64 vector contains infinity",
(long long)c);
}
continue;
}
ray_t* err = validate_parted_payload_atom(dst, payload, c);
if (err) return err;
}
*out_batch = batch;
return NULL;
}
static ray_t* append_parted_atom(ray_t* vec, int8_t dst, ray_t* atom) {
if (RAY_IS_NULL(atom) || RAY_ATOM_IS_NULL(atom)) {
if (dst == RAY_STR)
return ray_str_vec_append(vec, "", 0);
uint8_t zero[16] = {0};
ray_t* next = ray_vec_append(vec, zero);
if (!next || RAY_IS_ERR(next)) return next;
if (dst != RAY_BOOL && dst != RAY_U8 && dst != RAY_SYM)
ray_vec_set_null(next, next->len - 1, true);
return next;
}
switch (dst) {
case RAY_BOOL: { uint8_t x = atom->b8; return ray_vec_append(vec, &x); }
case RAY_U8: { uint8_t x = atom->u8; return ray_vec_append(vec, &x); }
case RAY_I16: { int16_t x = atom->i16; return ray_vec_append(vec, &x); }
case RAY_I32: { int32_t x = atom->i32; return ray_vec_append(vec, &x); }
case RAY_I64: { int64_t x = atom->i64; return ray_vec_append(vec, &x); }
case RAY_F32: { float x = (float)atom->f64; return ray_vec_append(vec, &x); }
case RAY_F64: { double x = atom->f64; return ray_vec_append(vec, &x); }
case RAY_DATE: { int32_t x = atom->i32; return ray_vec_append(vec, &x); }
case RAY_TIME: { int32_t x = atom->i32; return ray_vec_append(vec, &x); }
case RAY_TIMESTAMP: { int64_t x = atom->i64; return ray_vec_append(vec, &x); }
case RAY_GUID: return ray_vec_append(vec, ray_data(atom->obj));
case RAY_STR: return ray_str_vec_append(vec, ray_str_ptr(atom), ray_str_len(atom));
default: return ray_error("type", "insert: unsupported parted append type %s", ray_type_name(dst));
}
}
static void finalize_parted_segment_nulls(ray_t* vec) {
par_finalize_nulls(vec);
if (vec->type != RAY_GUID) return;
const uint8_t* d = (const uint8_t*)ray_data(vec);
for (int64_t r = 0; r < vec->len; r++) {
bool all_zero = true;
for (int b = 0; b < 16; b++) {
if (d[(size_t)r * 16u + (size_t)b] != 0) {
all_zero = false;
break;
}
}
if (all_zero) {
vec->attrs |= RAY_ATTR_HAS_NULLS;
return;
}
}
}
static ray_t* build_parted_null_prefix(int8_t base, int64_t len) {
ray_t* prefix = ray_vec_new(base, len);
if (!prefix || RAY_IS_ERR(prefix))
return prefix ? prefix : ray_error("oom", NULL);
for (int64_t r = 0; r < len; r++) {
ray_t* next = append_parted_atom(prefix, base, RAY_NULL_OBJ);
if (!next || RAY_IS_ERR(next)) {
ray_release(prefix);
return next ? next : ray_error("oom", NULL);
}
prefix = next;
}
return prefix;
}
static ray_t* build_parted_nonsym_segment(int8_t base,
ray_t* old_seg,
int64_t old_len,
ray_t* metadata_seg,
ray_t* payload,
int64_t batch) {
if (!old_seg && old_len > 0 && (base == RAY_BOOL || base == RAY_U8))
return ray_error("type", "insert: cannot backfill a missing active %s segment because the type has no null representation",
ray_type_name(base));
ray_t* owned_prefix = NULL;
ray_t* prefix = old_seg;
if (!prefix && old_len > 0) {
owned_prefix = build_parted_null_prefix(base, old_len);
if (!owned_prefix || RAY_IS_ERR(owned_prefix))
return owned_prefix ? owned_prefix : ray_error("oom", NULL);
prefix = owned_prefix;
}
ray_t* result = NULL;
if (parted_value_is_scalar(payload)) {
ray_t* tail = ray_vec_new(base, batch);
if (!tail || RAY_IS_ERR(tail))
result = tail ? tail : ray_error("oom", NULL);
for (int64_t r = 0; r < batch; r++) {
if (!tail || RAY_IS_ERR(tail)) break;
ray_t* next = append_parted_atom(tail, base, payload);
if (!next || RAY_IS_ERR(next)) {
ray_release(tail);
tail = NULL;
result = next ? next : ray_error("oom", NULL);
break;
}
tail = next;
}
if (tail && !RAY_IS_ERR(tail)) {
if (prefix) {
result = ray_vec_concat(prefix, tail);
ray_release(tail);
} else {
result = tail;
}
}
} else if (prefix) {
result = ray_vec_concat(prefix, payload);
} else {
ray_t* empty = ray_vec_new(base, 0);
if (!empty || RAY_IS_ERR(empty)) {
result = empty ? empty : ray_error("oom", NULL);
} else {
result = ray_vec_concat(empty, payload);
ray_release(empty);
}
}
if (owned_prefix) ray_release(owned_prefix);
if (!result || RAY_IS_ERR(result)) return result ? result : ray_error("oom", NULL);
finalize_parted_segment_nulls(result);
if ((base == RAY_I32 || base == RAY_I64) && metadata_seg &&
(metadata_seg->attrs & RAY_ATTR_HAS_LINK)) {
result->attrs |= RAY_ATTR_HAS_LINK;
result->link_target = metadata_seg->link_target;
}
return result;
}
static ray_t* build_parted_list_segment(ray_t* old_seg, int64_t old_len,
ray_t* payload, int64_t batch) {
if (old_len > INT64_MAX - batch) return ray_error("oom", NULL);
ray_t* out = ray_list_new(old_len + batch);
if (!out || RAY_IS_ERR(out)) return out ? out : ray_error("oom", NULL);
ray_t** dst = (ray_t**)ray_data(out);
if (old_seg) {
ray_t** src = (ray_t**)ray_data(old_seg);
for (int64_t r = 0; r < old_len; r++) {
dst[r] = src[r];
if (dst[r]) ray_retain(dst[r]);
}
} else {
memset(dst, 0, (size_t)old_len * sizeof(ray_t*));
}
if (payload->type == RAY_LIST) {
ray_t** cells = (ray_t**)ray_data(payload);
for (int64_t r = 0; r < batch; r++) {
dst[old_len + r] = cells[r];
if (cells[r]) ray_retain(cells[r]);
}
} else {
for (int64_t r = 0; r < batch; r++) {
dst[old_len + r] = payload;
ray_retain(payload);
}
}
out->len = old_len + batch;
return out;
}
static ray_t* build_parted_sym_skeleton(parted_insert_view_t* v,
ray_t* old_seg,
int64_t old_len,
int64_t batch) {
if (!v->sym_dom || !v->sym_rep)
return ray_error("corrupt", "insert: no shared FILE domain representative for SYM column");
if (old_len > INT64_MAX - batch) return ray_error("oom", NULL);
int64_t total = old_len + batch;
ray_t* out = ray_sym_vec_new(RAY_SYM_W64, total);
if (!out || RAY_IS_ERR(out)) return out ? out : ray_error("oom", NULL);
ray_sym_vec_adopt_domain(out, v->sym_rep);
if (total) memset(ray_data(out), 0, (size_t)total * sizeof(int64_t));
if (old_seg) {
int64_t domain_n = ray_sym_domain_count(v->sym_dom);
for (int64_t r = 0; r < old_len; r++) {
int64_t pos = ray_read_sym(ray_data(old_seg), r,
old_seg->type, old_seg->attrs);
if (pos < 0 || pos >= domain_n) {
ray_release(out);
return ray_error("corrupt", "insert: active SYM segment contains an out-of-domain position");
}
((int64_t*)ray_data(out))[r] = pos;
}
}
out->len = total;
return out;
}
static ray_t* parted_wrapper_metadata(ray_t* wrapper, int64_t active) {
ray_t** segs = (ray_t**)ray_data(wrapper);
if (active < wrapper->len && segs[active]) return segs[active];
int64_t start = active < wrapper->len ? active - 1 : wrapper->len - 1;
for (int64_t p = start; p >= 0; p--)
if (segs[p]) return segs[p];
return NULL;
}
static ray_t* build_parted_mapcommon(parted_insert_view_t* v,
ray_t* key,
bool new_partition,
int64_t batch) {
if (new_partition && v->nparts == INT64_MAX)
return ray_error("oom", NULL);
int64_t new_n = v->nparts + (new_partition ? 1 : 0);
ray_t* keys = ray_vec_new(v->key_type, new_n);
ray_t* counts = ray_vec_new(RAY_I64, new_n);
if (!keys || RAY_IS_ERR(keys) || !counts || RAY_IS_ERR(counts)) {
ray_t* ret = keys && RAY_IS_ERR(keys) ? keys
: counts && RAY_IS_ERR(counts) ? counts : NULL;
if (keys && keys != ret) {
if (RAY_IS_ERR(keys)) ray_error_free(keys);
else ray_release(keys);
}
if (counts && counts != ret) {
if (RAY_IS_ERR(counts)) ray_error_free(counts);
else ray_release(counts);
}
return ret ? ret : ray_error("oom", NULL);
}
for (int64_t p = 0; p < v->nparts; p++) {
ray_t* next;
if (v->key_type == RAY_DATE) {
int32_t x = ((int32_t*)ray_data(v->key_values))[p];
next = ray_vec_append(keys, &x);
} else {
int64_t x = v->key_type == RAY_SYM
? ray_read_sym(ray_data(v->key_values), p,
RAY_SYM, v->key_values->attrs)
: ((int64_t*)ray_data(v->key_values))[p];
next = ray_vec_append(keys, &x);
}
if (!next || RAY_IS_ERR(next)) {
ray_release(keys); ray_release(counts);
return next ? next : ray_error("oom", NULL);
}
keys = next;
int64_t rc = ((int64_t*)ray_data(v->row_counts))[p];
if (!new_partition && p == v->nparts - 1) rc += batch;
next = ray_vec_append(counts, &rc);
if (!next || RAY_IS_ERR(next)) {
ray_release(keys); ray_release(counts);
return next ? next : ray_error("oom", NULL);
}
counts = next;
}
if (new_partition) {
ray_t* next;
if (v->key_type == RAY_DATE) {
int32_t x = key->i32;
next = ray_vec_append(keys, &x);
} else {
int64_t x = key->i64;
next = ray_vec_append(keys, &x);
}
if (!next || RAY_IS_ERR(next)) {
ray_release(keys); ray_release(counts);
return next ? next : ray_error("oom", NULL);
}
keys = next;
next = ray_vec_append(counts, &batch);
if (!next || RAY_IS_ERR(next)) {
ray_release(keys); ray_release(counts);
return next ? next : ray_error("oom", NULL);
}
counts = next;
}
ray_t* mc = ray_alloc(2 * sizeof(ray_t*));
if (!mc || RAY_IS_ERR(mc)) {
ray_release(keys); ray_release(counts);
return mc ? mc : ray_error("oom", NULL);
}
mc->type = RAY_MAPCOMMON;
mc->len = 2;
mc->attrs = v->mapcommon->attrs;
memset(mc->aux, 0, 16);
((ray_t**)ray_data(mc))[0] = keys;
((ray_t**)ray_data(mc))[1] = counts;
return mc;
}
static bool parted_sym_payload_bytes(ray_t* payload, int64_t row,
const char** out_s, size_t* out_n) {
ray_t* atom = NULL;
if (parted_value_is_scalar(payload)) {
atom = payload;
} else if (payload->type == RAY_LIST) {
atom = ((ray_t**)ray_data(payload))[row];
} else if (payload->type == RAY_SYM) {
ray_t* s = ray_sym_vec_cell(payload, row);
if (!s) return false;
*out_s = ray_str_ptr(s);
*out_n = ray_str_len(s);
return true;
} else {
return false;
}
if (RAY_IS_NULL(atom)) {
*out_s = ""; *out_n = 0; return true;
}
if (!atom || atom->type != -RAY_SYM) return false;
ray_t* s = ray_sym_str(atom->i64);
if (!s) return false;
*out_s = ray_str_ptr(s);
*out_n = ray_str_len(s);
return true;
}
static ray_t* intern_parted_payload_symbols(ray_t* tbl,
parted_insert_view_t* v,
ray_t* vals,
int64_t batch) {
ray_t** pv = (ray_t**)ray_data(vals);
for (int64_t c = 0; c < v->ndata; c++) {
ray_t* wrapper = ray_table_get_col_idx(tbl, c + 1);
if (RAY_PARTED_BASETYPE(wrapper->type) != RAY_SYM) continue;
ray_t* payload = pv[c];
int64_t n = parted_value_is_scalar(payload) ? (batch ? 1 : 0) : batch;
for (int64_t r = 0; r < n; r++) {
const char* s = NULL; size_t slen = 0;
if (!parted_sym_payload_bytes(payload, r, &s, &slen))
return ray_error("corrupt", "insert: could not resolve a SYM payload cell");
if (ray_sym_domain_intern(v->sym_dom, s, slen) < 0)
return ray_error("oom", "insert: could not extend the parted FILE symbol domain");
}
}
return NULL;
}
static ray_t* fill_parted_payload_symbols(ray_t* tbl,
parted_insert_view_t* v,
ray_t* vals,
ray_t* staged,
int64_t old_len,
int64_t batch) {
ray_t** pv = (ray_t**)ray_data(vals);
ray_t** sv = (ray_t**)ray_data(staged);
for (int64_t c = 0; c < v->ndata; c++) {
ray_t* wrapper = ray_table_get_col_idx(tbl, c + 1);
if (RAY_PARTED_BASETYPE(wrapper->type) != RAY_SYM) continue;
ray_t* payload = pv[c];
ray_t* seg = sv[c];
for (int64_t r = 0; r < batch; r++) {
const char* s = NULL; size_t slen = 0;
int64_t src_r = parted_value_is_scalar(payload) ? 0 : r;
if (!parted_sym_payload_bytes(payload, src_r, &s, &slen))
return ray_error("corrupt", "insert: could not resolve a staged SYM payload cell");
int64_t pos = ray_sym_domain_find(v->sym_dom, s, slen);
if (pos < 0)
return ray_error("corrupt", "insert: staged SYM is absent after domain intern");
ray_write_sym(ray_data(seg), old_len + r, (uint64_t)pos,
RAY_SYM, seg->attrs);
}
}
return NULL;
}
static ray_t* insert_parted_rows(ray_t* tbl, ray_t* key, ray_t* rows) {
parted_insert_view_t v;
ray_t* err = validate_parted_insert_target(tbl, &v);
if (err) return err;
bool new_partition = false;
err = decide_parted_insert_partition(&v, key, &new_partition);
if (err) return err;
if (new_partition && v.active_missing)
return ray_error("domain", "insert: cannot advance the partition key while the current partition has missing physical segments");
ray_t* vals = normalize_parted_insert_rows(tbl, &v, rows);
if (!vals || RAY_IS_ERR(vals)) return vals ? vals : ray_error("oom", NULL);
int64_t batch = 0;
err = validate_parted_payloads(tbl, &v, vals, &batch);
if (err) { ray_release(vals); return err; }
if (batch == 0) {
ray_release(vals);
if (v.active_missing)
return ray_error("domain", "insert: an empty batch cannot materialize missing active physical segments");
ray_retain(tbl);
return tbl;
}
if (v.total_rows > INT64_MAX - batch) {
ray_release(vals);
return ray_error("oom", NULL);
}
int64_t active = new_partition ? v.nparts : v.nparts - 1;
int64_t old_len = new_partition ? 0
: ((int64_t*)ray_data(v.row_counts))[v.nparts - 1];
if (old_len > INT64_MAX - batch) {
ray_release(vals);
return ray_error("oom", NULL);
}
if (new_partition &&
(v.nparts == INT64_MAX ||
(uint64_t)v.nparts + 1u > SIZE_MAX / sizeof(ray_t*))) {
ray_release(vals);
return ray_error("oom", NULL);
}
ray_t* staged = ray_list_new(v.ndata);
if (!staged || RAY_IS_ERR(staged)) {
ray_release(vals);
return staged ? staged : ray_error("oom", NULL);
}
ray_t** pv = (ray_t**)ray_data(vals);
for (int64_t c = 0; c < v.ndata; c++) {
ray_t* wrapper = ray_table_get_col_idx(tbl, c + 1);
ray_t** segs = (ray_t**)ray_data(wrapper);
int8_t base = (int8_t)RAY_PARTED_BASETYPE(wrapper->type);
ray_t* old_seg = new_partition ? NULL : segs[v.nparts - 1];
ray_t* new_seg = base == RAY_LIST
? build_parted_list_segment(old_seg, old_len, pv[c], batch)
: base == RAY_SYM
? build_parted_sym_skeleton(&v, old_seg, old_len, batch)
: build_parted_nonsym_segment(base, old_seg, old_len,
parted_wrapper_metadata(wrapper, active),
pv[c], batch);
if (!new_seg || RAY_IS_ERR(new_seg)) {
ray_release(staged); ray_release(vals);
return new_seg ? new_seg : ray_error("oom", NULL);
}
ray_t* next = ray_list_append(staged, new_seg);
ray_release(new_seg);
if (!next || RAY_IS_ERR(next)) {
ray_release(staged); ray_release(vals);
return next ? next : ray_error("oom", NULL);
}
staged = next;
}
ray_t* mc = build_parted_mapcommon(&v, key, new_partition, batch);
if (!mc || RAY_IS_ERR(mc)) {
ray_release(staged); ray_release(vals);
return mc ? mc : ray_error("oom", NULL);
}
ray_t* result = ray_table_new(v.ncols);
if (!result || RAY_IS_ERR(result)) {
ray_release(mc); ray_release(staged); ray_release(vals);
return result ? result : ray_error("oom", NULL);
}
result = ray_table_add_col(result, ray_table_col_name(tbl, 0), mc);
ray_release(mc);
if (!result || RAY_IS_ERR(result)) {
ray_release(staged); ray_release(vals);
return result ? result : ray_error("oom", NULL);
}
int64_t new_nparts = v.nparts + (new_partition ? 1 : 0);
if ((uint64_t)new_nparts > SIZE_MAX / sizeof(ray_t*)) {
ray_release(result); ray_release(staged); ray_release(vals);
return ray_error("oom", NULL);
}
ray_t** staged_segs = (ray_t**)ray_data(staged);
for (int64_t c = 0; c < v.ndata; c++) {
ray_t* old_wrapper = ray_table_get_col_idx(tbl, c + 1);
ray_t** old_segs = (ray_t**)ray_data(old_wrapper);
ray_t* wrapper = ray_alloc((size_t)new_nparts * sizeof(ray_t*));
if (!wrapper || RAY_IS_ERR(wrapper)) {
ray_release(result); ray_release(staged); ray_release(vals);
return wrapper ? wrapper : ray_error("oom", NULL);
}
wrapper->type = old_wrapper->type;
wrapper->len = new_nparts;
wrapper->attrs = 0;
memset(wrapper->aux, 0, 16);
ray_t** ws = (ray_t**)ray_data(wrapper);
memset(ws, 0, (size_t)new_nparts * sizeof(ray_t*));
for (int64_t p = 0; p < v.nparts; p++) {
if (!new_partition && p == v.nparts - 1) continue;
ws[p] = old_segs[p];
if (ws[p]) ray_retain(ws[p]);
}
ws[active] = staged_segs[c];
ray_retain(ws[active]);
result = ray_table_add_col(result, ray_table_col_name(tbl, c + 1), wrapper);
ray_release(wrapper);
if (!result || RAY_IS_ERR(result)) {
ray_release(staged); ray_release(vals);
return result ? result : ray_error("oom", NULL);
}
}
err = intern_parted_payload_symbols(tbl, &v, vals, batch);
if (!err)
err = fill_parted_payload_symbols(tbl, &v, vals, staged,
old_len, batch);
ray_release(staged);
ray_release(vals);
if (err) { ray_release(result); return err; }
return result;
}
static void ukey_extend_after_append(ray_t* tbl, int64_t nrows0);
ray_t* ray_insert_fn(ray_t** args, int64_t n) {
return ray_insert(args, n);
}
ray_t* ray_insert(ray_t** args, int64_t n) {
if (n < 2) return ray_error("arity", "insert: expects a target and a value, got %lld args", (long long)n);
int64_t inplace_sym = -1;
ray_t* tbl_raw = args[0];
ray_t* tbl;
int already_eval = (tbl_raw && tbl_raw->type == RAY_TABLE);
if (already_eval) {
tbl = tbl_raw;
ray_retain(tbl);
} else {
tbl = ray_eval(tbl_raw);
if (!tbl || RAY_IS_ERR(tbl)) return tbl ? tbl : ray_error("type", "insert: target evaluation failed");
if (tbl->type == -RAY_SYM) {
inplace_sym = tbl->i64;
ray_release(tbl);
tbl = ray_env_get(inplace_sym);
if (!tbl || RAY_IS_ERR(tbl)) return ray_error("domain", "insert: target symbol is unbound");
ray_retain(tbl);
}
}
if (tbl->type != RAY_TABLE) {
if (already_eval) { int8_t tbl_t = tbl->type; ray_release(tbl); return ray_error("type", "insert: target must be a table, got %s", ray_type_name(tbl_t)); }
if (tbl->attrs & RAY_ATTR_ARENA) { ray_release(tbl); return ray_error("type", "insert: cannot insert into an arena-backed value"); }
if (tbl->attrs & RAY_ATTR_SLICE) {
if (tbl->type == RAY_LIST) { ray_release(tbl); return ray_error("type", "insert: cannot insert into a list slice"); }
ray_t* empty = ray_vec_new(tbl->type, 0);
if (!empty || RAY_IS_ERR(empty)) {
ray_release(tbl);
return empty ? empty : ray_error("oom", NULL);
}
ray_t* mat = ray_vec_concat(tbl, empty);
ray_release(empty);
ray_release(tbl);
if (!mat || RAY_IS_ERR(mat)) return mat ? mat : ray_error("oom", NULL);
tbl = mat;
}
bool is_target_list = (tbl->type == RAY_LIST);
bool is_target_vec = ray_is_vec(tbl);
if (!is_target_list && !is_target_vec) {
int8_t tbl_t = tbl->type;
ray_release(tbl);
return ray_error("type", "insert: target must be a table, list or vector, got %s", ray_type_name(tbl_t));
}
if (n != 2 && n != 3) {
ray_release(tbl);
return ray_error("domain", "insert: list/vector insert takes 2 or 3 args, got %lld", (long long)n);
}
ray_t* result = NULL;
int8_t tt = tbl->type;
if (n == 2) {
ray_t* val = ray_eval(args[1]);
if (!val || RAY_IS_ERR(val)) {
ray_release(tbl);
return val ? val : ray_error("type", "insert: value evaluation failed");
}
if (is_target_list) {
tbl = ray_list_append(tbl, val);
result = tbl;
} else if (val->type == -tt) {
int64_t new_idx = tbl->len;
if (tt == RAY_STR) {
tbl = ray_str_vec_append(tbl, ray_str_ptr(val), ray_str_len(val));
} else if (tt == RAY_SYM) {
int64_t s = val->i64;
tbl = ray_vec_append(tbl, &s);
} else if (tt == RAY_GUID) {
static const uint8_t zero_guid[16] = {0};
const void* src = val->obj ? ray_data(val->obj) : zero_guid;
tbl = ray_vec_append(tbl, src);
} else {
tbl = ray_vec_append(tbl, &val->u8);
}
if (tbl && !RAY_IS_ERR(tbl) && RAY_ATOM_IS_NULL(val))
ray_vec_set_null(tbl, new_idx, true);
result = tbl;
} else if (val->type == tt) {
result = ray_vec_concat(tbl, val);
ray_release(tbl);
} else {
int8_t val_t = val->type;
ray_release(tbl);
ray_release(val);
return ray_error("type", "insert: appended value type does not match %s vector, got %s",
ray_type_name(tt), ray_type_name(val_t));
}
ray_release(val);
} else {
ray_t* idx_arg = ray_eval(args[1]);
if (!idx_arg || RAY_IS_ERR(idx_arg)) {
ray_release(tbl);
return idx_arg ? idx_arg : ray_error("type", "insert: index evaluation failed");
}
ray_t* val = ray_eval(args[2]);
if (!val || RAY_IS_ERR(val)) {
ray_release(tbl);
ray_release(idx_arg);
return val ? val : ray_error("type", "insert: value evaluation failed");
}
if (is_target_list) {
if (idx_arg->type == -RAY_I64) {
tbl = ray_list_insert_at(tbl, idx_arg->i64, val);
result = tbl;
} else if (idx_arg->type == RAY_I64) {
if (val->type != RAY_LIST) {
int8_t val_t = val->type;
ray_release(tbl); ray_release(idx_arg); ray_release(val);
return ray_error("type", "insert: multi-index list insert requires a list of values, got %s", ray_type_name(val_t));
}
result = ray_list_insert_many(tbl, idx_arg, val);
ray_release(tbl);
} else {
int8_t idx_t = idx_arg->type;
ray_release(tbl); ray_release(idx_arg); ray_release(val);
return ray_error("type", "insert: list index must be an i64 atom or vector, got %s", ray_type_name(idx_t));
}
} else {
if (idx_arg->type == -RAY_I64) {
int64_t i = idx_arg->i64;
if (val->type == -tt) {
if (tt == RAY_STR) {
result = ray_str_vec_insert_at(tbl, i,
ray_str_ptr(val), ray_str_len(val));
ray_release(tbl);
} else if (tt == RAY_SYM) {
int64_t s = val->i64;
tbl = ray_vec_insert_at(tbl, i, &s);
result = tbl;
} else if (tt == RAY_GUID) {
static const uint8_t zero_guid[16] = {0};
const void* src = val->obj ? ray_data(val->obj) : zero_guid;
tbl = ray_vec_insert_at(tbl, i, src);
result = tbl;
} else {
tbl = ray_vec_insert_at(tbl, i, &val->u8);
result = tbl;
}
if (result && !RAY_IS_ERR(result) && RAY_ATOM_IS_NULL(val))
ray_vec_set_null(result, i, true);
} else if (val->type == tt) {
result = ray_vec_insert_vec_at(tbl, i, val);
ray_release(tbl);
} else {
int8_t val_t = val->type;
ray_release(tbl); ray_release(idx_arg); ray_release(val);
return ray_error("type", "insert: value type does not match %s vector, got %s",
ray_type_name(tt), ray_type_name(val_t));
}
} else if (idx_arg->type == RAY_I64) {
if (tt == RAY_STR) {
ray_release(tbl); ray_release(idx_arg); ray_release(val);
return ray_error("type", "insert: multi-index insert not supported on str vectors");
}
if (val->type != tt && val->type != -tt) {
int8_t val_t = val->type;
ray_release(tbl); ray_release(idx_arg); ray_release(val);
return ray_error("type", "insert: value type does not match %s vector, got %s",
ray_type_name(tt), ray_type_name(val_t));
}
result = ray_vec_insert_many(tbl, idx_arg, val);
ray_release(tbl);
} else {
int8_t idx_t = idx_arg->type;
ray_release(tbl); ray_release(idx_arg); ray_release(val);
return ray_error("type", "insert: index must be an i64 atom or vector, got %s", ray_type_name(idx_t));
}
}
ray_release(idx_arg);
ray_release(val);
}
if (inplace_sym >= 0 && result && !RAY_IS_ERR(result)) {
ray_env_set(inplace_sym, result);
ray_release(result);
return ray_sym(inplace_sym);
}
return result;
}
if (n == 3) {
if (!table_has_parted_columns(tbl)) {
ray_release(tbl);
return ray_error("type", "insert: three-argument table form requires a canonical .db.parted.get table");
}
if (inplace_sym >= 0 && ray_sym_is_reserved(inplace_sym) &&
!ray_sym_is_ipc_hook(inplace_sym)) {
ray_release(tbl);
return ray_error("reserved", "insert: cannot rebind a reserved target");
}
ray_t* partition_key = already_eval
? (ray_retain(args[1]), args[1])
: ray_eval(args[1]);
if (!partition_key || RAY_IS_ERR(partition_key)) {
ray_release(tbl);
return partition_key ? partition_key
: ray_error("type", "insert: partition key evaluation failed");
}
ray_t* rows = already_eval
? (ray_retain(args[2]), args[2])
: ray_eval(args[2]);
if (!rows || RAY_IS_ERR(rows)) {
ray_release(tbl); ray_release(partition_key);
return rows ? rows : ray_error("type", "insert: rows evaluation failed");
}
ray_t* success = NULL;
if (inplace_sym >= 0) {
success = ray_sym(inplace_sym);
if (!success || RAY_IS_ERR(success)) {
ray_release(rows);
ray_release(partition_key);
ray_release(tbl);
return success ? success : ray_error("oom", NULL);
}
}
ray_t* result = insert_parted_rows(tbl, partition_key, rows);
ray_release(rows);
ray_release(partition_key);
if (inplace_sym >= 0 && result && !RAY_IS_ERR(result)) {
ray_err_t bind_err = result == tbl ? RAY_OK
: ray_env_set(inplace_sym, result);
ray_release(result);
ray_release(tbl);
if (bind_err != RAY_OK) {
ray_release(success);
return ray_error(ray_err_code_str(bind_err),
"insert: could not rebind the parted target");
}
return success;
}
if (success) ray_release(success);
ray_release(tbl);
return result;
}
if (n != 2) {
ray_release(tbl);
return ray_error("arity", "insert: table insert takes 2 args, or 3 for a parted live tail, got %lld",
(long long)n);
}
if (table_has_parted_columns(tbl)) {
ray_release(tbl);
return ray_error("arity", "insert: parted table insert requires target, partition key and physical rows");
}
ray_t* row = already_eval ? (ray_retain(args[1]), args[1]) : ray_eval(args[1]);
if (!row || RAY_IS_ERR(row)) { ray_release(tbl); return row ? row : ray_error("type", "insert: row evaluation failed"); }
if (tbl->type != RAY_TABLE) { int8_t tbl_t = tbl->type; ray_release(tbl); ray_release(row); return ray_error("type", "insert: target must be a table, got %s", ray_type_name(tbl_t)); }
int64_t ncols = ray_table_ncols(tbl);
ray_t* row_orig = row;
if (!is_list(row) && row->type != RAY_TABLE && row->type != RAY_DICT) { int8_t row_t = row->type; ray_release(tbl); ray_release(row); return ray_error("type", "insert: row must be a list, table or dict, got %s", ray_type_name(row_t)); }
ray_t* tbl_row_list = NULL;
if (row->type == RAY_TABLE) {
int64_t src_ncols = ray_table_ncols(row);
if (src_ncols != ncols) { ray_release(tbl); ray_release(row); return ray_error("domain", "insert: row table has %lld columns, target has %lld", (long long)src_ncols, (long long)ncols); }
tbl_row_list = ray_alloc(ncols * sizeof(ray_t*));
if (!tbl_row_list) { ray_release(tbl); ray_release(row_orig); return ray_error("oom", NULL); }
tbl_row_list->type = RAY_LIST;
tbl_row_list->len = ncols;
ray_t** trl = (ray_t**)ray_data(tbl_row_list);
for (int64_t c = 0; c < ncols; c++) {
int64_t col_name = ray_table_col_name(tbl, c);
ray_t* src_col = ray_table_get_col(row, col_name);
if (!src_col) src_col = ray_table_get_col_idx(row, c);
if (!src_col) {
tbl_row_list->len = 0;
ray_free(tbl_row_list);
ray_release(tbl); ray_release(row_orig);
return ray_error("domain", "insert: row table missing a target column");
}
trl[c] = src_col;
ray_retain(src_col);
}
row = tbl_row_list;
}
ray_t* dict_vals = NULL;
if (row->type == RAY_DICT) {
ray_t* dkeys = ray_dict_keys(row);
ray_t* dvals = ray_dict_vals(row);
if (!dkeys || dkeys->type != RAY_SYM || !dvals) {
ray_release(tbl); ray_release(row_orig);
return ray_error("type", "insert: dict row keys must be symbols");
}
int64_t dict_len = dkeys->len;
dict_vals = ray_alloc(ncols * sizeof(ray_t*));
if (!dict_vals) { ray_release(tbl); ray_release(row_orig); return ray_error("oom", NULL); }
dict_vals->type = RAY_LIST;
dict_vals->len = ncols;
ray_t** dv = (ray_t**)ray_data(dict_vals);
for (int64_t c = 0; c < ncols; c++) {
int64_t col_name = ray_table_col_name(tbl, c);
dv[c] = NULL;
for (int64_t d = 0; d < dict_len; d++) {
int64_t dk = sym_cell_runtime_id(dkeys, d);
if (dk != col_name) continue;
if (dvals->type == RAY_LIST) {
dv[c] = ((ray_t**)ray_data(dvals))[d];
if (dv[c]) ray_retain(dv[c]);
} else {
int alloc = 0;
dv[c] = collection_elem(dvals, d, &alloc);
if (!alloc && dv[c]) ray_retain(dv[c]);
}
break;
}
}
for (int64_t d = 0; d < dict_len; d++) {
int64_t dk = sym_cell_runtime_id(dkeys, d);
int found_in_tbl = 0;
for (int64_t c = 0; c < ncols; c++) {
if (ray_table_col_name(tbl, c) == dk) { found_in_tbl = 1; break; }
}
if (!found_in_tbl) {
for (int64_t c = 0; c < ncols; c++) if (dv[c]) ray_release(dv[c]);
dict_vals->len = 0;
ray_free(dict_vals);
ray_release(tbl); ray_release(row_orig);
return ray_error("value", NULL);
}
}
row = dict_vals;
}
if (ray_len(row) != ncols) {
if (dict_vals) {
for (int64_t c = 0; c < ncols; c++) ray_release(((ray_t**)ray_data(dict_vals))[c]);
dict_vals->len = 0;
ray_free(dict_vals);
}
ray_release(tbl); ray_release(row_orig);
return ray_error("domain", "insert: row has %lld values, target has %lld columns", (long long)ray_len(row), (long long)ncols);
}
ray_t** row_elems = (ray_t**)ray_data(row);
int64_t nrows = ray_table_nrows(tbl);
bool inplace_ok = inplace_sym >= 0 && tbl->rc == 2 && tbl->mmod == 0 &&
!(tbl->attrs & (RAY_ATTR_SLICE | RAY_ATTR_ARENA));
ray_t* live_cols = NULL;
if (inplace_ok) {
live_cols = ((ray_t**)ray_data(tbl))[1];
if (!live_cols || live_cols->rc != 1 || live_cols->len != ncols)
inplace_ok = false;
}
for (int64_t c = 0; inplace_ok && c < ncols; c++) {
ray_t* col = ((ray_t**)ray_data(live_cols))[c];
if (!col || RAY_IS_ERR(col) || col->rc != 1 ||
(col->type == RAY_LIST && ray_len(col) == 0) ||
RAY_IS_PARTED(col->type) || col->type == RAY_MAPCOMMON ||
col->mmod != 0 ||
(col->attrs & (RAY_ATTR_SLICE | RAY_ATTR_ARENA))) {
inplace_ok = false;
break;
}
if (col->type == RAY_SYM &&
((col->attrs & RAY_SYM_W_MASK) != RAY_SYM_W64 ||
col->sym_domain != ray_sym_runtime_domain())) {
inplace_ok = false;
break;
}
}
if (inplace_ok) {
ray_t** slots = (ray_t**)ray_data(live_cols);
ray_t* err = NULL;
for (int64_t c = 0; c < ncols && !err; c++) {
ray_t* col = slots[c];
ray_t* pay = row_elems[c];
int8_t ct = col->type;
ray_t* res;
if (ct == RAY_LIST) {
if (tbl_row_list) {
if (!pay || RAY_IS_ERR(pay) || pay->type != RAY_LIST) {
res = ray_error("type", "insert: table payload for a LIST column must be LIST");
} else {
ray_t** cells = (ray_t**)ray_data(pay);
int64_t m = pay->len;
res = col;
for (int64_t k = 0; k < m && !RAY_IS_ERR(res); k++) {
res = ray_list_append(res, cells[k]);
if (!RAY_IS_ERR(res)) { slots[c] = res; col = res; }
}
}
} else {
res = ray_list_append(col, pay);
}
} else if (!pay) {
ray_t* null_atom = ray_typed_null(-ct);
res = append_atom_to_col(col, null_atom);
ray_release(null_atom);
} else if (ray_is_atom(pay)) {
res = append_atom_to_col(col, pay);
} else if (pay->type == ct && ct == RAY_STR) {
bool pay_nulls = ray_vec_may_have_nulls(pay);
res = col;
int64_t m = ray_len(pay);
for (int64_t k = 0; k < m && !RAY_IS_ERR(res); k++) {
int64_t at = res->len;
if (pay_nulls && ray_vec_is_null(pay, k)) {
res = ray_str_vec_append(res, "", 0);
if (!RAY_IS_ERR(res)) ray_vec_set_null(res, at, true);
} else {
size_t slen = 0;
const char* sp = ray_str_vec_get(pay, k, &slen);
res = ray_str_vec_append(res, sp ? sp : "", sp ? slen : 0);
}
if (!RAY_IS_ERR(res)) { slots[c] = res; col = res; }
}
} else if (pay->type == ct && ct == RAY_SYM &&
((pay->attrs & RAY_SYM_W_MASK) != RAY_SYM_W64 ||
pay->sym_domain != ray_sym_runtime_domain())) {
bool pay_nulls = ray_vec_may_have_nulls(pay);
res = col;
int64_t m = ray_len(pay);
for (int64_t k = 0; k < m && !RAY_IS_ERR(res); k++) {
int64_t sym_val = sym_cell_runtime_id(pay, k);
int64_t at = res->len;
res = ray_vec_append(res, &sym_val);
if (!RAY_IS_ERR(res)) {
slots[c] = res;
col = res;
if (pay_nulls && ray_vec_is_null(pay, k))
ray_vec_set_null(res, at, true);
}
}
} else if (pay->type == ct) {
res = ray_vec_append_raw(col, ray_data(pay), ray_len(pay));
if (!RAY_IS_ERR(res) && ray_vec_may_have_nulls(pay))
res->attrs |= RAY_ATTR_HAS_NULLS;
} else if (ray_is_vec(pay) || pay->type == RAY_LIST) {
res = col;
int64_t m = ray_len(pay);
for (int64_t k = 0; k < m && !RAY_IS_ERR(res); k++) {
int alloc = 0;
ray_t* e = collection_elem(pay, k, &alloc);
if (!e) {
ray_t* na = ray_typed_null(-ct);
res = append_atom_to_col(res, na);
ray_release(na);
} else {
res = append_atom_to_col(res, e);
if (alloc) ray_release(e);
}
if (!RAY_IS_ERR(res)) { slots[c] = res; col = res; }
}
} else {
res = append_atom_to_col(col, pay);
}
if (RAY_IS_ERR(res)) err = res;
else slots[c] = res;
}
if (err) {
for (int64_t c = 0; c < ncols; c++) {
ray_t* col = slots[c];
if (col->type == RAY_LIST) {
ray_t** cells = (ray_t**)ray_data(col);
for (int64_t k = nrows; k < col->len; k++)
if (cells[k]) ray_release(cells[k]);
}
col->len = nrows;
}
} else {
ukey_extend_after_append(tbl, nrows);
}
if (dict_vals) {
ray_t** dv = (ray_t**)ray_data(dict_vals);
for (int64_t c = 0; c < ncols; c++) if (dv[c]) ray_release(dv[c]);
dict_vals->len = 0;
ray_free(dict_vals);
}
if (tbl_row_list) {
ray_t** trl = (ray_t**)ray_data(tbl_row_list);
for (int64_t c = 0; c < ncols; c++) if (trl[c]) ray_release(trl[c]);
tbl_row_list->len = 0;
ray_free(tbl_row_list);
}
ray_release(tbl);
ray_release(row_orig);
return err ? err : ray_sym(inplace_sym);
}
ray_t* result = ray_table_new(ncols);
if (RAY_IS_ERR(result)) return result;
for (int64_t c = 0; c < ncols; c++) {
int64_t col_name = ray_table_col_name(tbl, c);
ray_t* orig_col = ray_table_get_col_idx(tbl, c);
int8_t ct = orig_col->type;
if (ct == RAY_LIST && ray_len(orig_col) == 0)
ct = typeless_col_type(row_elems[c]);
if (ct == RAY_LIST) {
ray_t* new_col = append_boxed_table_col(
orig_col, row_elems[c], tbl_row_list != NULL);
if (!new_col || RAY_IS_ERR(new_col)) {
ray_release(result);
return new_col ? new_col : ray_error("oom", NULL);
}
result = ray_table_add_col(result, col_name, new_col);
ray_release(new_col);
if (!result || RAY_IS_ERR(result)) return result;
continue;
}
ray_t* new_col = ray_vec_new(ct, nrows + 1);
if (RAY_IS_ERR(new_col)) { ray_release(result); return new_col; }
bool src_has_nulls = ray_vec_may_have_nulls(orig_col);
if (ct == RAY_STR) {
for (int64_t r = 0; r < nrows; r++) {
if (src_has_nulls && ray_vec_is_null(orig_col, r)) {
new_col = ray_str_vec_append(new_col, "", 0);
if (!RAY_IS_ERR(new_col))
ray_vec_set_null(new_col, new_col->len - 1, true);
} else {
size_t slen = 0;
const char* sp = ray_str_vec_get(orig_col, r, &slen);
new_col = ray_str_vec_append(new_col, sp ? sp : "", sp ? slen : 0);
}
if (RAY_IS_ERR(new_col)) { ray_release(result); return new_col; }
}
} else if (ct == RAY_SYM) {
for (int64_t r = 0; r < nrows; r++) {
int64_t sym_val = sym_cell_runtime_id(orig_col, r);
new_col = ray_vec_append(new_col, &sym_val);
if (RAY_IS_ERR(new_col)) { ray_release(result); return new_col; }
if (src_has_nulls && ray_vec_is_null(orig_col, r))
ray_vec_set_null(new_col, new_col->len - 1, true);
}
} else {
size_t elem_sz = ray_elem_size(ct);
uint8_t* src = (uint8_t*)ray_data(orig_col);
for (int64_t r = 0; r < nrows; r++) {
new_col = ray_vec_append(new_col, src + r * elem_sz);
if (RAY_IS_ERR(new_col)) { ray_release(result); return new_col; }
if (src_has_nulls && ray_vec_is_null(orig_col, r))
ray_vec_set_null(new_col, new_col->len - 1, true);
}
}
if (!row_elems[c]) {
ray_t* null_atom = ray_typed_null(-ct);
new_col = append_atom_to_col(new_col, null_atom);
ray_release(null_atom);
} else if (ray_is_atom(row_elems[c])) {
new_col = append_atom_to_col(new_col, row_elems[c]);
} else if (row_elems[c]->type == ct) {
ray_t* merged = ray_concat_fn(new_col, row_elems[c]);
ray_release(new_col);
new_col = merged;
} else if (ray_is_vec(row_elems[c]) || row_elems[c]->type == RAY_LIST) {
ray_t* payload = row_elems[c];
int64_t m = ray_len(payload);
for (int64_t k = 0; k < m; k++) {
int alloc = 0;
ray_t* e = collection_elem(payload, k, &alloc);
if (!e) {
ray_t* na = ray_typed_null(-ct);
new_col = append_atom_to_col(new_col, na);
ray_release(na);
} else {
new_col = append_atom_to_col(new_col, e);
if (alloc) ray_release(e);
}
if (RAY_IS_ERR(new_col)) break;
}
} else {
new_col = append_atom_to_col(new_col, row_elems[c]);
}
if (RAY_IS_ERR(new_col)) { ray_release(result); return new_col; }
result = ray_table_add_col(result, col_name, new_col);
ray_release(new_col);
if (RAY_IS_ERR(result)) return result;
}
if (dict_vals) {
ray_t** dv = (ray_t**)ray_data(dict_vals);
for (int64_t c = 0; c < ncols; c++) if (dv[c]) ray_release(dv[c]);
dict_vals->len = 0;
ray_free(dict_vals);
}
if (tbl_row_list) {
ray_t** trl = (ray_t**)ray_data(tbl_row_list);
for (int64_t c = 0; c < ncols; c++) if (trl[c]) ray_release(trl[c]);
tbl_row_list->len = 0;
ray_free(tbl_row_list);
}
ray_release(tbl);
ray_release(row_orig);
if (inplace_sym >= 0 && result && !RAY_IS_ERR(result)) {
ray_env_set(inplace_sym, result);
ray_release(result);
return ray_sym(inplace_sym);
}
return result;
}
static bool upsert_key_type_ok(int8_t t) {
switch (t) {
case RAY_BOOL: case RAY_U8: case RAY_I16: case RAY_I32: case RAY_I64:
case RAY_DATE: case RAY_TIME: case RAY_TIMESTAMP:
case RAY_F64: case RAY_SYM: case RAY_STR: case RAY_GUID:
return true;
default:
return false;
}
}
static bool upsert_col_ok(ray_t* col) {
if (!col || RAY_IS_ERR(col)) return false;
if (col->type == RAY_LIST && ray_len(col) == 0) return false;
if (RAY_IS_PARTED(col->type) || col->type == RAY_MAPCOMMON) return false;
if (col->mmod != 0) return false;
if (col->attrs & (RAY_ATTR_SLICE | RAY_ATTR_ARENA)) return false;
if (col->type == RAY_SYM &&
((col->attrs & RAY_SYM_W_MASK) != RAY_SYM_W64 ||
col->sym_domain != ray_sym_runtime_domain()))
return false;
return true;
}
static int64_t upsert_int_cell(const ray_t* col, int64_t r) {
const void* d = ray_data((ray_t*)col);
switch (col->type) {
case RAY_BOOL: case RAY_U8: return (int64_t)((const uint8_t*)d)[r];
case RAY_I16: return (int64_t)((const int16_t*)d)[r];
case RAY_I32: case RAY_DATE: case RAY_TIME:
return (int64_t)((const int32_t*)d)[r];
default: return ((const int64_t*)d)[r];
}
}
static int64_t upsert_atom_int(ray_t* a) {
return a->type == -RAY_BOOL ? (int64_t)a->b8 : elem_as_i64(a);
}
static inline __attribute__((always_inline)) uint64_t upsert_hash_cell(ray_t* col, int64_t r) {
switch (col->type) {
case RAY_F64: return ray_hash_f64(((const double*)ray_data(col))[r]);
case RAY_SYM: return ray_hash_i64(ray_read_sym(ray_data(col), r, col->type, col->attrs));
case RAY_STR: {
size_t n = 0;
const char* s = ray_str_vec_get(col, r, &n);
return ray_hash_bytes(s ? s : "", n);
}
case RAY_GUID: return ray_hash_bytes((const uint8_t*)ray_data(col) + r * 16, 16);
default: return ray_hash_i64(upsert_int_cell(col, r));
}
}
static uint64_t upsert_hash_atom(int8_t ct, ray_t* a) {
static const uint8_t zero_guid[16] = {0};
switch (ct) {
case RAY_F64: return ray_hash_f64(as_f64(a));
case RAY_SYM: return ray_hash_i64(a->i64);
case RAY_STR: {
const char* s = ray_str_ptr(a);
return ray_hash_bytes(s ? s : "", ray_str_len(a));
}
case RAY_GUID: return ray_hash_bytes(a->obj ? ray_data(a->obj) : zero_guid, 16);
default: return ray_hash_i64(upsert_atom_int(a));
}
}
static bool upsert_cell_eq_atom(ray_t* col, int64_t r, ray_t* a) {
static const uint8_t zero_guid[16] = {0};
switch (col->type) {
case RAY_F64: return ((const double*)ray_data(col))[r] == as_f64(a);
case RAY_SYM: return ray_read_sym(ray_data(col), r, col->type, col->attrs) == a->i64;
case RAY_STR: {
size_t n = 0;
const char* s = ray_str_vec_get(col, r, &n);
size_t m = ray_str_len(a);
const char* p = ray_str_ptr(a);
return n == m && (m == 0 || (s && p && memcmp(s, p, m) == 0));
}
case RAY_GUID:
return memcmp((const uint8_t*)ray_data(col) + r * 16,
a->obj ? ray_data(a->obj) : zero_guid, 16) == 0;
default: return upsert_int_cell(col, r) == upsert_atom_int(a);
}
}
#define UPSERT_HASH_SEED 0x9E3779B97F4A7C15ULL
static uint64_t upsert_hash_row(ray_t** slots, const int64_t* kci, int64_t nk, int64_t r) {
uint64_t h = UPSERT_HASH_SEED;
for (int64_t k = 0; k < nk; k++)
h = ray_hash_combine(h, upsert_hash_cell(slots[kci[k]], r));
return h;
}
static uint64_t upsert_hash_atoms(ray_t** slots, const int64_t* kci, int64_t nk, ray_t** cells) {
uint64_t h = UPSERT_HASH_SEED;
for (int64_t k = 0; k < nk; k++)
h = ray_hash_combine(h, upsert_hash_atom(slots[kci[k]]->type, cells[kci[k]]));
return h;
}
static bool upsert_row_eq_atoms(ray_t** slots, const int64_t* kci, int64_t nk, int64_t r, ray_t** cells) {
for (int64_t k = 0; k < nk; k++)
if (!upsert_cell_eq_atom(slots[kci[k]], r, cells[kci[k]])) return false;
return true;
}
typedef struct {
ray_t* hdr;
int64_t* slot;
uint64_t mask;
} upsert_map_t;
#define UKEY_SLOT_EMPTY ((int64_t)0)
#define UKEY_SLOT_TOMB ((int64_t)-1)
#define UKEY_PACK(h, row) ((int64_t)(((uint64_t)(uint32_t)(h) << 32) | (uint64_t)((row) + 1)))
#define UKEY_ROW(e) ((int64_t)((uint64_t)(e) & 0xFFFFFFFFULL) - 1)
#define UKEY_H32(e) ((uint32_t)((uint64_t)(e) >> 32))
#define UKEY_MAX_ROWS ((int64_t)0xFFFFFFFDLL)
#define UKEY_MAX_CAP ((uint64_t)1 << 32)
static bool upsert_map_init(upsert_map_t* mp, int64_t entries) {
uint64_t cap = 16;
while (cap < (uint64_t)entries * 2) cap <<= 1;
mp->slot = (int64_t*)scratch_alloc(&mp->hdr, cap * sizeof(int64_t));
if (!mp->slot) return false;
memset(mp->slot, 0, cap * sizeof(int64_t));
mp->mask = cap - 1;
return true;
}
static void upsert_map_put(upsert_map_t* mp, uint64_t h, int64_t row) {
uint64_t s = h & mp->mask;
while (mp->slot[s] != UKEY_SLOT_EMPTY) s = (s + 1) & mp->mask;
mp->slot[s] = UKEY_PACK(h, row);
}
static void ukey_extend_after_append(ray_t* tbl, int64_t nrows0) {
ray_index_t* ix = ray_table_ukey_get(tbl);
if (!ix) return;
int64_t n = ray_table_nrows(tbl);
if (ix->u.ukey.nrows != nrows0 || n < nrows0 ||
(uint64_t)(n + ix->u.ukey.n_tomb) * 2 > ix->u.ukey.mask + 1) {
ray_table_ukey_drop(tbl);
return;
}
ray_t* cols = ((ray_t**)ray_data(tbl))[1];
if (!cols || RAY_IS_ERR(cols)) { ray_table_ukey_drop(tbl); return; }
ray_t** slots = (ray_t**)ray_data(cols);
int64_t kci[RAY_UKEY_MAX_COLS];
int64_t nk = ix->u.ukey.nk;
for (int64_t k = 0; k < nk; k++) kci[k] = ix->u.ukey.kci[k];
upsert_map_t map = { NULL, (int64_t*)ray_data(ix->u.ukey.slots), ix->u.ukey.mask };
for (int64_t r = nrows0; r < n; r++)
upsert_map_put(&map, upsert_hash_row(slots, kci, nk, r), r);
ix->u.ukey.nrows = n;
}
static bool ukey_describes(ray_index_t* ix, const int64_t* kci, int64_t nk,
int64_t nrows0) {
if (ix->u.ukey.nrows != nrows0 || ix->u.ukey.nk != nk) return false;
for (int64_t k = 0; k < nk; k++)
if (ix->u.ukey.kci[k] != (int16_t)kci[k]) return false;
return true;
}
static bool ukey_has_room(ray_index_t* ix, int64_t nrows0, int64_t m) {
return (uint64_t)(nrows0 + m + ix->u.ukey.n_tomb) * 2 <= ix->u.ukey.mask + 1;
}
static bool ukey_grow(ray_index_t* ix, int64_t entries) {
uint64_t old_cap = ix->u.ukey.mask + 1;
uint64_t cap = 16;
while (cap < (uint64_t)entries * 2) {
if (cap >= UKEY_MAX_CAP) return false;
cap <<= 1;
}
if (cap < old_cap) cap = old_cap;
if (cap > UKEY_MAX_CAP) return false;
ray_t* fresh = ray_vec_new(RAY_I64, (int64_t)cap);
if (!fresh || RAY_IS_ERR(fresh)) { if (fresh) ray_release(fresh); return false; }
fresh->len = (int64_t)cap;
int64_t* dst = (int64_t*)ray_data(fresh);
memset(dst, 0, (size_t)cap * sizeof(int64_t));
const int64_t* src = (const int64_t*)ray_data(ix->u.ukey.slots);
uint64_t mask = cap - 1;
uint64_t start = 0;
while (start < old_cap && src[start] != UKEY_SLOT_EMPTY) start++;
for (uint64_t i = 0; i < old_cap; i++) {
int64_t e = src[(start + i) & (old_cap - 1)];
if (e == UKEY_SLOT_EMPTY || e == UKEY_SLOT_TOMB) continue;
uint64_t t = (uint64_t)UKEY_H32(e) & mask;
while (dst[t] != UKEY_SLOT_EMPTY) t = (t + 1) & mask;
dst[t] = e;
}
ray_release(ix->u.ukey.slots);
ix->u.ukey.slots = fresh;
ix->u.ukey.mask = mask;
ix->u.ukey.n_tomb = 0;
return true;
}
static int64_t upsert_map_find(upsert_map_t* mp, uint64_t h, ray_t** slots,
const int64_t* kci, int64_t nk, ray_t** cells) {
uint64_t s = h & mp->mask;
for (;;) {
int64_t e = mp->slot[s];
if (e == UKEY_SLOT_EMPTY) return -1;
if (e != UKEY_SLOT_TOMB && UKEY_H32(e) == (uint32_t)h &&
upsert_row_eq_atoms(slots, kci, nk, UKEY_ROW(e), cells)) return UKEY_ROW(e);
s = (s + 1) & mp->mask;
}
}
static int64_t upsert_find_row(ray_t** slots, const int64_t* kci, int64_t nk,
ray_t** cells, int64_t nrows) {
if (nk == 1) {
ray_t* col = slots[kci[0]];
ray_t* a = cells[kci[0]];
int8_t t = col->type;
if (t == RAY_SYM || (t != RAY_F64 && t != RAY_STR && t != RAY_GUID)) {
int64_t key = (t == RAY_SYM) ? a->i64 : upsert_atom_int(a);
int64_t hit = ray_index_find_row(col, key);
if (hit >= -1) return hit;
if (t == RAY_I64 || t == RAY_TIMESTAMP || t == RAY_SYM) {
const int64_t* d = (const int64_t*)ray_data(col);
for (int64_t r = 0; r < nrows; r++) if (d[r] == key) return r;
return -1;
}
if (t == RAY_I32 || t == RAY_DATE || t == RAY_TIME) {
const int32_t* d = (const int32_t*)ray_data(col);
for (int64_t r = 0; r < nrows; r++) if ((int64_t)d[r] == key) return r;
return -1;
}
}
}
for (int64_t r = 0; r < nrows; r++)
if (upsert_row_eq_atoms(slots, kci, nk, r, cells)) return r;
return -1;
}
static ray_t* upsert_cell_fits(int8_t ct, ray_t* cell) {
if (ct == RAY_LIST) return NULL;
if (!ray_is_atom(cell))
return ray_error("type", "upsert: %s column requires an atom value, got %s",
ray_type_name(ct), ray_type_name(cell->type));
if (RAY_ATOM_IS_NULL(cell)) return NULL;
int8_t at = cell->type;
bool is_int = (at == -RAY_I64 || at == -RAY_I32 || at == -RAY_I16 || at == -RAY_U8);
bool ok;
switch (ct) {
case RAY_BOOL: ok = (at == -RAY_BOOL); break;
case RAY_U8: case RAY_I16: case RAY_I32: case RAY_I64:
ok = is_int; break;
case RAY_F64: ok = (at == -RAY_F64 || is_int); break;
case RAY_DATE: ok = (at == -RAY_DATE); break;
case RAY_TIME: ok = (at == -RAY_TIME); break;
case RAY_TIMESTAMP: ok = (at == -RAY_TIMESTAMP); break;
case RAY_GUID: ok = (at == -RAY_GUID); break;
case RAY_SYM: ok = (at == -RAY_SYM); break;
case RAY_STR: ok = (at == -RAY_STR); break;
default:
return ray_error("type", "upsert: unsupported column type %s", ray_type_name(ct));
}
if (ok) return NULL;
return ray_error("type", "upsert: value type %s does not match %s column",
ray_type_name(at), ray_type_name(ct));
}
static ray_t* upsert_write_cell(ray_t** slots, int64_t c, int64_t row, ray_t* cell) {
ray_t* col = slots[c];
if (col->type == RAY_LIST) {
ray_t** cells = (ray_t**)ray_data(col);
ray_t* old = cells[row];
ray_retain(cell);
cells[row] = cell;
if (old) ray_release(old);
return NULL;
}
ray_index_drop(&slots[c]);
col = slots[c];
if (RAY_IS_ERR(col)) return col;
col->attrs &= (uint8_t)~RAY_ATTR_SORTED;
ray_t* res;
if (col->type == RAY_STR) {
res = RAY_ATOM_IS_NULL(cell)
? ray_str_vec_set(col, row, "", 0)
: ray_str_vec_set(col, row, ray_str_ptr(cell), ray_str_len(cell));
if (RAY_IS_ERR(res)) return res;
slots[c] = res;
return NULL;
}
int64_t tail = col->len;
res = append_atom_to_col(col, cell);
if (RAY_IS_ERR(res)) return res;
slots[c] = res;
size_t esz = ray_sym_elem_size(res->type, res->attrs);
uint8_t* d = (uint8_t*)ray_data(res);
memcpy(d + (size_t)row * esz, d + (size_t)tail * esz, esz);
res->len = tail;
return NULL;
}
static ray_t* upsert_append_row(ray_t** slots, int64_t ncols, ray_t** cells) {
for (int64_t c = 0; c < ncols; c++) {
ray_t* col = slots[c];
ray_t* cell = cells[c];
ray_t* res;
if (col->type == RAY_LIST) {
res = ray_list_append(col, cell);
} else if (!cell) {
ray_t* na = ray_typed_null(-col->type);
res = append_atom_to_col(col, na);
ray_release(na);
} else {
res = append_atom_to_col(col, cell);
}
if (RAY_IS_ERR(res)) return res;
slots[c] = res;
}
return NULL;
}
static void upsert_rollback_appends(ray_t** slots, int64_t ncols, int64_t nrows0) {
for (int64_t c = 0; c < ncols; c++) {
ray_t* col = slots[c];
if (col->type == RAY_LIST) {
ray_t** cells = (ray_t**)ray_data(col);
for (int64_t k = nrows0; k < col->len; k++)
if (cells[k]) ray_release(cells[k]);
}
col->len = nrows0;
}
}
static bool upsert_vec_payload_ok(ray_t* tbl, ray_t** src_cols, int64_t ncols, int64_t m) {
for (int64_t c = 0; c < ncols; c++) {
ray_t* sc = src_cols[c];
ray_t* tc = ray_table_get_col_idx(tbl, c);
if (!sc || RAY_IS_ERR(sc) || sc->type != tc->type) return false;
if (sc->attrs & RAY_ATTR_SLICE) return false;
if (sc->len != m) return false;
switch (tc->type) {
case RAY_BOOL: case RAY_U8: case RAY_I16: case RAY_I32: case RAY_I64:
case RAY_F64: case RAY_DATE: case RAY_TIME: case RAY_TIMESTAMP:
case RAY_GUID:
break;
case RAY_SYM:
if (ray_sym_vec_domain(sc) != ray_sym_runtime_domain()) return false;
break;
default:
return false;
}
}
return true;
}
static inline __attribute__((always_inline)) bool
upsert_cell_eq_cell(ray_t* a, int64_t ra, ray_t* b, int64_t rb) {
switch (a->type) {
case RAY_F64:
return ((const double*)ray_data(a))[ra] == ((const double*)ray_data(b))[rb];
case RAY_SYM:
return ray_read_sym(ray_data(a), ra, RAY_SYM, a->attrs) ==
ray_read_sym(ray_data(b), rb, RAY_SYM, b->attrs);
case RAY_GUID:
return memcmp((const uint8_t*)ray_data(a) + ra * 16,
(const uint8_t*)ray_data(b) + rb * 16, 16) == 0;
default:
return upsert_int_cell(a, ra) == upsert_int_cell(b, rb);
}
}
static inline __attribute__((always_inline)) int64_t
upsert_map_find_src(upsert_map_t* mp, uint64_t h, ray_t** slots,
const int64_t* kci, int64_t nk, ray_t** src,
int64_t r, int64_t nrows0, const int64_t* app) {
uint64_t s = h & mp->mask;
for (;;) {
int64_t e = mp->slot[s];
if (e == UKEY_SLOT_EMPTY) return -1;
if (e != UKEY_SLOT_TOMB && UKEY_H32(e) == (uint32_t)h) {
int64_t row = UKEY_ROW(e);
bool eq = true;
for (int64_t k = 0; k < nk && eq; k++) {
ray_t* sc = src[kci[k]];
eq = row < nrows0
? upsert_cell_eq_cell(slots[kci[k]], row, sc, r)
: upsert_cell_eq_cell(sc, app[row - nrows0], sc, r);
}
if (eq) return row;
}
s = (s + 1) & mp->mask;
}
}
#define UPSERT_PF_MIN_BYTES ((size_t)768 << 10)
static inline __attribute__((always_inline)) void
upsert_vec_move_impl(ray_t* dst, const int64_t* di, int64_t dbase,
ray_t* src, const int64_t* si, int64_t n, bool pf) {
void* d = ray_data(dst);
const void* s = ray_data(src);
#define UPSERT_MOVE(T) do { \
for (int64_t i = 0; i < n; i++) { \
if (pf && i + 16 < n && di[i + 16] >= 0) \
__builtin_prefetch(&((T*)d)[di[i + 16]], 1); \
int64_t o = di ? di[i] : dbase + i; \
if (o < 0) continue; \
((T*)d)[o] = ((const T*)s)[si ? si[i] : i]; \
} } while (0)
switch (dst->type) {
case RAY_BOOL: case RAY_U8: UPSERT_MOVE(uint8_t); break;
case RAY_I16: UPSERT_MOVE(int16_t); break;
case RAY_I32: case RAY_DATE: case RAY_TIME: UPSERT_MOVE(int32_t); break;
case RAY_I64: case RAY_TIMESTAMP: UPSERT_MOVE(int64_t); break;
case RAY_F64: UPSERT_MOVE(double); break;
case RAY_GUID:
for (int64_t i = 0; i < n; i++) {
int64_t o = di ? di[i] : dbase + i;
if (o < 0) continue;
memcpy((uint8_t*)d + o * 16, (const uint8_t*)s + (si ? si[i] : i) * 16, 16);
}
break;
case RAY_SYM: {
bool any_null = false;
for (int64_t i = 0; i < n; i++) {
int64_t o = di ? di[i] : dbase + i;
if (o < 0) continue;
int64_t id = ray_read_sym(s, si ? si[i] : i, RAY_SYM, src->attrs);
((int64_t*)d)[o] = id;
any_null |= (id == 0);
}
if (any_null) dst->attrs |= RAY_ATTR_HAS_NULLS;
return;
}
default: return;
}
#undef UPSERT_MOVE
if (!ray_vec_may_have_nulls(src)) return;
for (int64_t i = 0; i < n; i++) {
int64_t o = di ? di[i] : dbase + i;
if (o >= 0 && ray_vec_is_null(src, si ? si[i] : i)) {
dst->attrs |= RAY_ATTR_HAS_NULLS;
return;
}
}
}
static void upsert_vec_move(ray_t* dst, const int64_t* di, int64_t dbase,
ray_t* src, const int64_t* si, int64_t n) {
upsert_vec_move_impl(dst, di, dbase, src, si, n, false);
}
static void upsert_vec_scatter_ahead(ray_t* dst, const int64_t* di, ray_t* src, int64_t n) {
upsert_vec_move_impl(dst, di, 0, src, NULL, n, true);
}
static inline __attribute__((always_inline)) void
upsert_resolve_impl(upsert_map_t* map, ray_t** slots, const int64_t* kci, int64_t nk,
ray_t** src, int64_t m, int64_t nrows0, int64_t* dst, int64_t* app,
int64_t* napp_out, int64_t* nhit_out, bool ahead) {
enum { PF_SLOT = 16, PF_ROW = 8 };
const char* kbase[RAY_UKEY_MAX_COLS];
size_t kesz[RAY_UKEY_MAX_COLS];
int64_t npf = 0;
if (ahead) {
npf = nk < RAY_UKEY_MAX_COLS ? nk : RAY_UKEY_MAX_COLS;
for (int64_t k = 0; k < npf; k++) {
ray_t* kc = slots[kci[k]];
kbase[k] = (const char*)ray_data(kc);
kesz[k] = ray_sym_elem_size(kc->type, kc->attrs);
}
for (int64_t r = 0; r < m; r++) {
uint64_t h = UPSERT_HASH_SEED;
for (int64_t k = 0; k < nk; k++)
h = ray_hash_combine(h, upsert_hash_cell(src[kci[k]], r));
dst[r] = (int64_t)h;
}
}
int64_t napp = 0, nhit = 0;
for (int64_t r = 0; r < m; r++) {
uint64_t h;
if (ahead) {
if (r + PF_SLOT < m)
__builtin_prefetch(&map->slot[(uint64_t)dst[r + PF_SLOT] & map->mask]);
if (r + PF_ROW < m) {
int64_t e = map->slot[(uint64_t)dst[r + PF_ROW] & map->mask];
if (e != UKEY_SLOT_EMPTY && e != UKEY_SLOT_TOMB &&
UKEY_ROW(e) < nrows0)
for (int64_t k = 0; k < npf; k++)
__builtin_prefetch(kbase[k] + (size_t)UKEY_ROW(e) * kesz[k]);
}
h = (uint64_t)dst[r];
} else {
h = UPSERT_HASH_SEED;
for (int64_t k = 0; k < nk; k++)
h = ray_hash_combine(h, upsert_hash_cell(src[kci[k]], r));
}
int64_t hit = upsert_map_find_src(map, h, slots, kci, nk, src, r, nrows0, app);
if (hit < 0) {
upsert_map_put(map, h, nrows0 + napp);
app[napp++] = r;
dst[r] = -1;
} else {
dst[r] = hit;
nhit++;
}
}
*napp_out = napp;
*nhit_out = nhit;
}
__attribute__((noinline))
static void upsert_resolve_ahead(upsert_map_t* map, ray_t** slots, const int64_t* kci, int64_t nk,
ray_t** src, int64_t m, int64_t nrows0, int64_t* dst, int64_t* app,
int64_t* napp, int64_t* nhit) {
upsert_resolve_impl(map, slots, kci, nk, src, m, nrows0, dst, app, napp, nhit, true);
}
static ray_t* upsert_apply_vectors(ray_t** slots, int64_t ncols,
const int64_t* kci, int64_t nk,
ray_t** src, int64_t m, int64_t nrows0,
upsert_map_t* map, int64_t* nrows) {
ray_t* dst_hdr = NULL; ray_t* app_hdr = NULL; ray_t* buf_hdr = NULL;
int64_t* dst = (int64_t*)scratch_alloc(&dst_hdr, (size_t)m * sizeof(int64_t));
int64_t* app = dst ? (int64_t*)scratch_alloc(&app_hdr, (size_t)m * sizeof(int64_t)) : NULL;
ray_t* err = NULL;
if (!dst || !app) { err = ray_error("oom", NULL); goto out; }
size_t probe_bytes = (size_t)(map->mask + 1) * sizeof(int64_t);
for (int64_t k = 0; k < nk && k < RAY_UKEY_MAX_COLS; k++) {
ray_t* kc = slots[kci[k]];
probe_bytes += (size_t)nrows0 * ray_sym_elem_size(kc->type, kc->attrs);
}
int64_t napp = 0, nhit = 0;
if (probe_bytes >= UPSERT_PF_MIN_BYTES)
upsert_resolve_ahead(map, slots, kci, nk, src, m, nrows0, dst, app, &napp, &nhit);
else
upsert_resolve_impl(map, slots, kci, nk, src, m, nrows0, dst, app, &napp, &nhit, false);
if (napp > 0) {
void* buf = scratch_alloc(&buf_hdr, (size_t)napp * 16);
if (!buf) { err = ray_error("oom", NULL); goto out; }
memset(buf, 0, (size_t)napp * 16);
for (int64_t c = 0; c < ncols; c++) {
int64_t tail = slots[c]->len;
ray_t* res = ray_vec_append_raw(slots[c], buf, napp);
if (RAY_IS_ERR(res)) { err = res; goto out; }
slots[c] = res;
upsert_vec_move(res, NULL, tail, src[c], app, napp);
}
}
if (nhit > 0) {
for (int64_t c = 0; c < ncols; c++) {
bool is_key = false;
for (int64_t k = 0; k < nk; k++) if (kci[k] == c) { is_key = true; break; }
if (is_key) continue;
ray_index_drop(&slots[c]);
if (RAY_IS_ERR(slots[c])) { err = slots[c]; goto out; }
slots[c]->attrs &= (uint8_t)~RAY_ATTR_SORTED;
ray_t* col = slots[c];
if ((size_t)col->len * ray_sym_elem_size(col->type, col->attrs) >= UPSERT_PF_MIN_BYTES)
upsert_vec_scatter_ahead(col, dst, src[c], m);
else
upsert_vec_move(col, dst, 0, src[c], NULL, m);
}
}
*nrows = nrows0 + napp;
out:
if (buf_hdr) scratch_free(buf_hdr);
if (app_hdr) scratch_free(app_hdr);
if (dst_hdr) scratch_free(dst_hdr);
return err;
}
static ray_t* upsert_apply(ray_t* tbl, int64_t inplace_sym,
const int64_t* kci, int64_t nk,
ray_t** src_cols, ray_t** atoms, int64_t m,
bool* handled) {
*handled = false;
int64_t ncols = ray_table_ncols(tbl);
int64_t nrows0 = ray_table_nrows(tbl);
if (ncols <= 0 || m <= 0) return NULL;
for (int64_t c = 0; c < ncols; c++)
if (!upsert_col_ok(ray_table_get_col_idx(tbl, c))) return NULL;
for (int64_t k = 0; k < nk; k++) {
ray_t* kc = ray_table_get_col_idx(tbl, kci[k]);
if (!upsert_key_type_ok(kc->type)) return NULL;
}
bool inplace = inplace_sym >= 0 && tbl->rc == 2 && tbl->mmod == 0 &&
!(tbl->attrs & (RAY_ATTR_SLICE | RAY_ATTR_ARENA));
ray_t* live_cols = NULL;
if (inplace) {
live_cols = ((ray_t**)ray_data(tbl))[1];
if (!live_cols || live_cols->rc != 1 || live_cols->len != ncols)
inplace = false;
}
for (int64_t c = 0; inplace && c < ncols; c++)
if (((ray_t**)ray_data(live_cols))[c]->rc != 1) inplace = false;
bool vec_path = src_cols && m > 1 && upsert_vec_payload_ok(tbl, src_cols, ncols, m);
ray_t* cells_hdr = NULL; ray_t* owned_hdr = NULL;
size_t ncell = vec_path ? 1 : (size_t)m * (size_t)ncols;
ray_t** cells = (ray_t**)scratch_alloc(&cells_hdr, ncell * sizeof(ray_t*));
uint8_t* owned = cells ? (uint8_t*)scratch_alloc(&owned_hdr, ncell) : NULL;
if (!cells || !owned) {
if (cells_hdr) scratch_free(cells_hdr);
if (owned_hdr) scratch_free(owned_hdr);
*handled = true;
return ray_error("oom", NULL);
}
memset(cells, 0, ncell * sizeof(ray_t*));
memset(owned, 0, ncell);
ray_t* err = NULL;
upsert_map_t map = { NULL, NULL, 0 };
ray_index_t* ukey = NULL;
ray_t* work = NULL;
if (vec_path) {
} else if (src_cols) {
for (int64_t c = 0; c < ncols && !err; c++) {
ray_t* sc = src_cols[c];
if (!sc) continue;
if (!is_collection(sc)) {
err = ray_error("type", "upsert: multi-row value for column %lld must be a list or vector, got %s",
(long long)c, ray_type_name(sc->type));
break;
}
if (ray_len(sc) != m) {
err = ray_error("length", "upsert: column %lld has %lld values, key has %lld",
(long long)c, (long long)ray_len(sc), (long long)m);
break;
}
for (int64_t r = 0; r < m; r++) {
int alloc = 0;
ray_t* e = collection_elem(sc, r, &alloc);
if (e && RAY_IS_ERR(e)) { err = e; break; }
cells[(size_t)r * ncols + c] = e;
owned[(size_t)r * ncols + c] = (uint8_t)(alloc && e);
}
}
} else {
for (int64_t c = 0; c < ncols; c++) cells[c] = atoms[c];
}
for (int64_t r = 0; r < m && !err && !vec_path; r++) {
ray_t** rc = cells + (size_t)r * ncols;
for (int64_t k = 0; k < nk && !err; k++) {
ray_t* kc = ray_table_get_col_idx(tbl, kci[k]);
ray_t* a = rc[kci[k]];
if (!a) {
err = ray_error("domain", "upsert: key value missing");
} else if (kc->type == RAY_STR && a->type != -RAY_STR) {
err = ray_error("type", "upsert: key column is str but key value is %s", ray_type_name(a->type));
} else if (kc->type == RAY_SYM && a->type != -RAY_SYM) {
err = ray_error("type", "upsert: key column is sym but key value is %s", ray_type_name(a->type));
}
}
for (int64_t c = 0; c < ncols && !err; c++)
if (rc[c]) err = upsert_cell_fits(ray_table_get_col_idx(tbl, c)->type, rc[c]);
}
ray_t** slots = NULL;
if (!err) {
if (inplace) {
slots = (ray_t**)ray_data(live_cols);
} else {
work = ray_table_new(ncols);
if (RAY_IS_ERR(work)) { err = work; work = NULL; }
for (int64_t c = 0; c < ncols && !err; c++) {
ray_t* orig = ray_table_get_col_idx(tbl, c);
ray_retain(orig);
ray_t* cp = ray_cow(orig);
if (!cp || RAY_IS_ERR(cp)) {
ray_release(orig);
err = cp ? cp : ray_error("oom", NULL);
break;
}
work = ray_table_add_col(work, ray_table_col_name(tbl, c), cp);
ray_release(cp);
if (RAY_IS_ERR(work)) { err = work; work = NULL; }
}
if (!err) slots = (ray_t**)ray_data(((ray_t**)ray_data(work))[1]);
}
}
if (!err) {
if (inplace && nk > RAY_UKEY_MAX_COLS) ray_table_ukey_drop(tbl);
if (inplace && nk <= RAY_UKEY_MAX_COLS) {
ukey = ray_table_ukey_get(tbl);
if (ukey && (!ukey_describes(ukey, kci, nk, nrows0) ||
(!ukey_has_room(ukey, nrows0, m) &&
!ukey_grow(ukey, nrows0 + m)))) {
ray_table_ukey_drop(tbl);
ukey = NULL;
}
if (!ukey) {
if (nrows0 + m <= UKEY_MAX_ROWS) {
ray_t* idx = ray_index_build_ukey(kci, nk, nrows0 + m);
if (idx && !RAY_IS_ERR(idx)) {
if (ray_table_ukey_attach(tbl, idx)) ukey = ray_table_ukey_get(tbl);
else ray_release(idx);
}
if (ukey) {
map.slot = (int64_t*)ray_data(ukey->u.ukey.slots);
map.mask = ukey->u.ukey.mask;
for (int64_t r = 0; r < nrows0 && !err; r++)
upsert_map_put(&map, upsert_hash_row(slots, kci, nk, r), r);
ukey->u.ukey.nrows = nrows0;
}
}
} else {
map.slot = (int64_t*)ray_data(ukey->u.ukey.slots);
map.mask = ukey->u.ukey.mask;
}
}
if (!ukey && !err && m > 1) {
if (!upsert_map_init(&map, nrows0 + m)) err = ray_error("oom", NULL);
for (int64_t r = 0; r < nrows0 && !err; r++)
upsert_map_put(&map, upsert_hash_row(slots, kci, nk, r), r);
}
}
int64_t nrows = nrows0;
if (vec_path && !err)
err = upsert_apply_vectors(slots, ncols, kci, nk, src_cols, m, nrows0, &map, &nrows);
for (int64_t r = 0; r < m && !err && !vec_path; r++) {
ray_t** rc = cells + (size_t)r * ncols;
uint64_t h = 0;
int64_t hit;
if (map.slot) {
h = upsert_hash_atoms(slots, kci, nk, rc);
hit = upsert_map_find(&map, h, slots, kci, nk, rc);
} else {
hit = upsert_find_row(slots, kci, nk, rc, nrows);
}
if (hit >= 0) {
for (int64_t c = 0; c < ncols && !err; c++) {
if (!rc[c]) continue;
bool is_key = false;
for (int64_t k = 0; k < nk; k++) if (kci[k] == c) { is_key = true; break; }
if (is_key) continue;
err = upsert_write_cell(slots, c, hit, rc[c]);
}
} else {
err = upsert_append_row(slots, ncols, rc);
if (!err) {
if (map.slot) upsert_map_put(&map, h, nrows);
nrows++;
}
}
}
if (err && slots) upsert_rollback_appends(slots, ncols, nrows0);
if (ukey) {
if (err) { ray_table_ukey_drop(tbl); ukey = NULL; }
else ukey->u.ukey.nrows = nrows;
}
if (map.hdr) scratch_free(map.hdr);
for (size_t i = 0; i < ncell; i++) if (owned[i] && cells[i]) ray_release(cells[i]);
scratch_free(cells_hdr);
scratch_free(owned_hdr);
*handled = true;
if (err) {
if (work) ray_release(work);
return err;
}
if (inplace) return ray_sym(inplace_sym);
if (inplace_sym >= 0) {
ray_env_set(inplace_sym, work);
ray_release(work);
return ray_sym(inplace_sym);
}
return work;
}
ray_t* ray_upsert_fn(ray_t** args, int64_t n) {
return ray_upsert(args, n);
}
ray_t* ray_upsert(ray_t** args, int64_t n) {
if (n < 3) return ray_error("arity", "upsert: expects a target, key and row, got %lld args", (long long)n);
int64_t inplace_sym = -1;
ray_t* tbl_raw = args[0];
int already_eval = (tbl_raw && tbl_raw->type == RAY_TABLE);
ray_t* tbl;
if (already_eval) {
tbl = tbl_raw;
ray_retain(tbl);
} else {
tbl = ray_eval(tbl_raw);
if (!tbl || RAY_IS_ERR(tbl)) return tbl ? tbl : ray_error("type", "upsert: target evaluation failed");
if (tbl->type == -RAY_SYM) {
inplace_sym = tbl->i64;
ray_release(tbl);
tbl = ray_env_get(inplace_sym);
if (!tbl || RAY_IS_ERR(tbl)) return ray_error("domain", "upsert: target symbol is unbound");
ray_retain(tbl);
}
}
if (tbl->type == RAY_TABLE && table_has_parted_columns(tbl)) {
ray_release(tbl);
return ray_error("nyi", "upsert: parted tables are not supported; use insert with an explicit partition key");
}
ray_t* key_sym = already_eval ? (ray_retain(args[1]), args[1]) : ray_eval(args[1]);
if (!key_sym || RAY_IS_ERR(key_sym)) { ray_release(tbl); return key_sym ? key_sym : ray_error("type", "upsert: key evaluation failed"); }
ray_t* row = already_eval ? (ray_retain(args[2]), args[2]) : ray_eval(args[2]);
if (!row || RAY_IS_ERR(row)) { ray_release(tbl); ray_release(key_sym); return row ? row : ray_error("type", "upsert: row evaluation failed"); }
if (tbl->type != RAY_TABLE) { int8_t tbl_t = tbl->type; ray_release(tbl); ray_release(key_sym); ray_release(row); return ray_error("type", "upsert: target must be a table, got %s", ray_type_name(tbl_t)); }
if (!is_list(row) && row->type != RAY_TABLE && row->type != RAY_DICT) { int8_t row_t = row->type; ray_release(tbl); ray_release(key_sym); ray_release(row); return ray_error("type", "upsert: row must be a list, table or dict, got %s", ray_type_name(row_t)); }
int64_t ncols = ray_table_ncols(tbl);
if (row->type == RAY_TABLE) {
int64_t src_nrows = ray_table_nrows(row);
int64_t src_ncols = ray_table_ncols(row);
if (src_nrows == 0) {
ray_release(key_sym); ray_release(row);
return tbl;
}
for (int64_t sc = 0; sc < src_ncols; sc++) {
int64_t scn = ray_table_col_name(row, sc);
int64_t tbl_matches = 0, src_matches = 0;
for (int64_t i = 0; i < ncols; i++) if (ray_table_col_name(tbl, i) == scn) tbl_matches++;
for (int64_t i = 0; i < src_ncols; i++) if (ray_table_col_name(row, i) == scn) src_matches++;
if (tbl_matches != 1 || src_matches != 1) {
ray_release(tbl); ray_release(key_sym); ray_release(row);
return ray_error("value", NULL);
}
}
ray_t* keynames_hdr = NULL;
int64_t* key_names = NULL;
int64_t n_key = 0;
if (key_sym->type == -RAY_SYM) {
key_names = (int64_t*)scratch_alloc(&keynames_hdr, sizeof(int64_t));
if (!key_names) { ray_release(tbl); ray_release(key_sym); ray_release(row); return ray_error("oom", NULL); }
key_names[n_key++] = key_sym->i64;
} else if (key_sym->type == -RAY_I64) {
int64_t k = key_sym->i64;
if (k <= 0 || k > ncols) {
ray_release(tbl); ray_release(key_sym); ray_release(row);
return ray_error("domain", "upsert: key count must be 1..ncols, got %lld", (long long)k);
}
key_names = (int64_t*)scratch_alloc(&keynames_hdr, (size_t)k * sizeof(int64_t));
if (!key_names) { ray_release(tbl); ray_release(key_sym); ray_release(row); return ray_error("oom", NULL); }
for (int64_t i = 0; i < k; i++)
key_names[n_key++] = ray_table_col_name(tbl, i);
} else {
int8_t ks_t = key_sym->type;
ray_release(tbl); ray_release(key_sym); ray_release(row);
return ray_error("type", "upsert: key must be a symbol or integer, got %s", ray_type_name(ks_t));
}
for (int64_t k = 0; k < n_key; k++) {
int found = 0;
for (int64_t i = 0; i < src_ncols; i++)
if (ray_table_col_name(row, i) == key_names[k]) { found = 1; break; }
if (!found) {
scratch_free(keynames_hdr);
ray_release(tbl); ray_release(key_sym); ray_release(row);
return ray_error("value", NULL);
}
}
ray_t* tkci_hdr = NULL;
int64_t* tkci = (int64_t*)scratch_alloc(&tkci_hdr, (size_t)n_key * sizeof(int64_t));
if (!tkci) { scratch_free(keynames_hdr); ray_release(tbl); ray_release(key_sym); ray_release(row); return ray_error("oom", NULL); }
for (int64_t k = 0; k < n_key; k++) {
tkci[k] = -1;
for (int64_t c = 0; c < ncols; c++)
if (ray_table_col_name(tbl, c) == key_names[k]) { tkci[k] = c; break; }
if (tkci[k] < 0) {
scratch_free(tkci_hdr); scratch_free(keynames_hdr);
ray_release(tbl); ray_release(key_sym); ray_release(row);
return ray_error("domain", "upsert: key column not found in target table");
}
}
scratch_free(keynames_hdr);
ray_t* srcs_hdr = NULL;
ray_t** src_cols = (ray_t**)scratch_alloc(&srcs_hdr, (size_t)ncols * sizeof(ray_t*));
if (!src_cols) { scratch_free(tkci_hdr); ray_release(tbl); ray_release(key_sym); ray_release(row); return ray_error("oom", NULL); }
for (int64_t c = 0; c < ncols; c++) {
int64_t cn = ray_table_col_name(tbl, c);
src_cols[c] = ray_table_get_col(row, cn);
}
bool fast_handled = false;
ray_t* fast = upsert_apply(tbl, inplace_sym, tkci, n_key, src_cols, NULL, src_nrows, &fast_handled);
scratch_free(tkci_hdr);
if (fast_handled) {
scratch_free(srcs_hdr);
ray_release(tbl); ray_release(key_sym); ray_release(row);
return fast;
}
ray_t* cur_tbl = tbl;
ray_retain(cur_tbl);
for (int64_t r = 0; r < src_nrows; r++) {
ray_t* single = ray_alloc(ncols * sizeof(ray_t*));
if (!single) { scratch_free(srcs_hdr); ray_release(cur_tbl); ray_release(tbl); ray_release(key_sym); ray_release(row); return ray_error("oom", NULL); }
single->type = RAY_LIST;
single->len = ncols;
ray_t** sr = (ray_t**)ray_data(single);
for (int64_t c = 0; c < ncols; c++) {
int alloc = 0;
sr[c] = src_cols[c] ? collection_elem(src_cols[c], r, &alloc) : NULL;
if (!alloc && sr[c]) ray_retain(sr[c]);
}
ray_t* upsert_args[3] = { cur_tbl, key_sym, single };
ray_t* new_tbl = ray_upsert_fn(upsert_args, 3);
for (int64_t c = 0; c < ncols; c++) if (sr[c]) ray_release(sr[c]);
single->len = 0;
ray_free(single);
ray_release(cur_tbl);
if (RAY_IS_ERR(new_tbl)) { scratch_free(srcs_hdr); ray_release(tbl); ray_release(key_sym); ray_release(row); return new_tbl; }
cur_tbl = new_tbl;
}
scratch_free(srcs_hdr);
ray_release(tbl);
ray_release(key_sym);
ray_release(row);
if (inplace_sym >= 0 && cur_tbl && !RAY_IS_ERR(cur_tbl)) {
ray_env_set(inplace_sym, cur_tbl);
ray_release(cur_tbl);
return ray_sym(inplace_sym);
}
return cur_tbl;
}
ray_t* dict_row_list = NULL;
if (row->type == RAY_DICT) {
ray_t* dkeys = ray_dict_keys(row);
ray_t* dvals = ray_dict_vals(row);
if (!dkeys || dkeys->type != RAY_SYM || !dvals) {
ray_release(tbl); ray_release(key_sym); ray_release(row);
return ray_error("type", "upsert: dict row keys must be symbols");
}
int64_t n_pairs = dkeys->len;
if (n_pairs != ncols) {
ray_release(tbl); ray_release(key_sym); ray_release(row);
return ray_error("value", NULL);
}
for (int64_t c = 0; c < ncols; c++) {
int64_t cn = ray_table_col_name(tbl, c);
int64_t tbl_matches = 0, dict_matches = 0;
for (int64_t i = 0; i < ncols; i++)
if (ray_table_col_name(tbl, i) == cn) tbl_matches++;
for (int64_t d = 0; d < n_pairs; d++) {
int64_t dk = sym_cell_runtime_id(dkeys, d);
if (dk == cn) dict_matches++;
}
if (tbl_matches != 1 || dict_matches != 1) {
ray_release(tbl); ray_release(key_sym); ray_release(row);
return ray_error("value", NULL);
}
}
dict_row_list = ray_alloc(ncols * sizeof(ray_t*));
if (!dict_row_list) { ray_release(tbl); ray_release(key_sym); ray_release(row); return ray_error("oom", NULL); }
dict_row_list->type = RAY_LIST;
dict_row_list->len = ncols;
ray_t** drl = (ray_t**)ray_data(dict_row_list);
for (int64_t c = 0; c < ncols; c++) {
int64_t col_name = ray_table_col_name(tbl, c);
drl[c] = NULL;
for (int64_t d = 0; d < n_pairs; d++) {
int64_t dk = sym_cell_runtime_id(dkeys, d);
if (dk != col_name) continue;
if (dvals->type == RAY_LIST) {
drl[c] = ((ray_t**)ray_data(dvals))[d];
if (drl[c]) ray_retain(drl[c]);
} else {
int alloc = 0;
drl[c] = collection_elem(dvals, d, &alloc);
if (!alloc && drl[c]) ray_retain(drl[c]);
}
break;
}
}
ray_release(row);
row = dict_row_list;
}
if (ray_len(row) != ncols) { ray_release(tbl); ray_release(key_sym); ray_release(row); return ray_error("domain", "upsert: row has %lld values, target has %lld columns", (long long)ray_len(row), (long long)ncols); }
ray_t** row_elems = (ray_t**)ray_data(row);
int64_t nrows = ray_table_nrows(tbl);
int64_t n_key_cols = 1;
ray_t* kci_hdr = NULL;
int64_t* key_col_indices = NULL;
if (key_sym->type == -RAY_SYM) {
key_col_indices = (int64_t*)scratch_alloc(&kci_hdr, sizeof(int64_t));
if (!key_col_indices) { ray_release(tbl); ray_release(key_sym); ray_release(row); return ray_error("oom", NULL); }
key_col_indices[0] = -1;
for (int64_t c = 0; c < ncols; c++) {
if (ray_table_col_name(tbl, c) == key_sym->i64) {
key_col_indices[0] = c;
break;
}
}
if (key_col_indices[0] < 0) { scratch_free(kci_hdr); ray_release(tbl); ray_release(key_sym); ray_release(row); return ray_error("domain", "upsert: key column not found in target table"); }
} else if (key_sym->type == -RAY_I64) {
n_key_cols = key_sym->i64;
if (n_key_cols <= 0 || n_key_cols > ncols) { ray_release(tbl); ray_release(key_sym); ray_release(row); return ray_error("domain", "upsert: key count must be 1..ncols, got %lld", (long long)n_key_cols); }
key_col_indices = (int64_t*)scratch_alloc(&kci_hdr, (size_t)n_key_cols * sizeof(int64_t));
if (!key_col_indices) { ray_release(tbl); ray_release(key_sym); ray_release(row); return ray_error("oom", NULL); }
for (int64_t k = 0; k < n_key_cols; k++) key_col_indices[k] = k;
} else {
int8_t ks_t = key_sym->type;
ray_release(tbl); ray_release(key_sym); ray_release(row);
return ray_error("type", "upsert: key must be a symbol or integer, got %s", ray_type_name(ks_t));
}
ray_t* key_elem = row_elems[key_col_indices[0]];
if (!key_elem) { scratch_free(kci_hdr); ray_release(tbl); ray_release(key_sym); ray_release(row); return ray_error("domain", "upsert: key value missing"); }
{
bool multi = ray_is_vec(key_elem) || key_elem->type == RAY_LIST;
bool fast_handled = false;
ray_t* fast = multi
? upsert_apply(tbl, inplace_sym, key_col_indices, n_key_cols, row_elems, NULL, ray_len(key_elem), &fast_handled)
: upsert_apply(tbl, inplace_sym, key_col_indices, n_key_cols, NULL, row_elems, 1, &fast_handled);
if (fast_handled) {
scratch_free(kci_hdr);
ray_release(tbl); ray_release(key_sym); ray_release(row);
return fast;
}
}
if (ray_is_vec(key_elem) || key_elem->type == RAY_LIST) {
int64_t new_nrows = ray_len(key_elem);
ray_t* cur_tbl = tbl;
ray_retain(cur_tbl);
for (int64_t r = 0; r < new_nrows; r++) {
ray_t* single_row = ray_alloc(ncols * sizeof(ray_t*));
if (!single_row) { scratch_free(kci_hdr); ray_release(cur_tbl); ray_release(tbl); ray_release(key_sym); ray_release(row); return ray_error("oom", NULL); }
single_row->type = RAY_LIST;
single_row->len = ncols;
ray_t** sr = (ray_t**)ray_data(single_row);
for (int64_t c = 0; c < ncols; c++) {
int alloc = 0;
sr[c] = collection_elem(row_elems[c], r, &alloc);
if (!alloc && sr[c]) ray_retain(sr[c]);
}
ray_t* upsert_args[3] = { cur_tbl, key_sym, single_row };
ray_t* new_tbl = ray_upsert_fn(upsert_args, 3);
for (int64_t c = 0; c < ncols; c++) if (sr[c]) ray_release(sr[c]);
single_row->len = 0;
ray_free(single_row);
ray_release(cur_tbl);
if (RAY_IS_ERR(new_tbl)) { scratch_free(kci_hdr); ray_release(tbl); ray_release(key_sym); ray_release(row); return new_tbl; }
cur_tbl = new_tbl;
}
scratch_free(kci_hdr);
ray_release(tbl);
ray_release(key_sym);
ray_release(row);
if (inplace_sym >= 0 && cur_tbl && !RAY_IS_ERR(cur_tbl)) {
ray_env_set(inplace_sym, cur_tbl);
ray_release(cur_tbl);
return ray_sym(inplace_sym);
}
return cur_tbl;
}
ray_t* kw_hdr = NULL;
int64_t* key_sym_want = (int64_t*)scratch_alloc(&kw_hdr, (size_t)n_key_cols * sizeof(int64_t));
if (!key_sym_want) { scratch_free(kci_hdr); ray_release(tbl); ray_release(key_sym); ray_release(row); return ray_error("oom", NULL); }
for (int64_t k = 0; k < n_key_cols; k++) {
int64_t kci = key_col_indices[k];
ray_t* key_col = ray_table_get_col_idx(tbl, kci);
ray_t* key_atom = row_elems[kci];
int8_t kt = key_col->type;
if (kt == RAY_STR && key_atom->type != -RAY_STR) {
int8_t ka_t = key_atom->type;
scratch_free(kw_hdr); scratch_free(kci_hdr);
ray_release(tbl); ray_release(key_sym); ray_release(row);
return ray_error("type", "upsert: key column is str but key value is %s", ray_type_name(ka_t));
}
if (kt == RAY_SYM && key_atom->type != -RAY_SYM) {
int8_t ka_t = key_atom->type;
scratch_free(kw_hdr); scratch_free(kci_hdr);
ray_release(tbl); ray_release(key_sym); ray_release(row);
return ray_error("type", "upsert: key column is sym but key value is %s", ray_type_name(ka_t));
}
key_sym_want[k] = -1;
if (kt == RAY_SYM) {
if (ray_sym_vec_domain(key_col) == ray_sym_runtime_domain()) {
key_sym_want[k] = key_atom->i64;
} else {
ray_t* ks = ray_sym_str(key_atom->i64);
key_sym_want[k] = ks
? ray_sym_vec_lookup(key_col, ray_str_ptr(ks), ray_str_len(ks))
: -1;
}
}
}
int64_t match_row = -1;
for (int64_t r = 0; r < nrows; r++) {
int match = 1;
for (int64_t k = 0; k < n_key_cols && match; k++) {
int64_t kci = key_col_indices[k];
ray_t* key_col = ray_table_get_col_idx(tbl, kci);
ray_t* key_atom = row_elems[kci];
int8_t kt = key_col->type;
if (kt == RAY_F64) {
double needle = (key_atom->type == -RAY_F64) ? key_atom->f64 : (double)key_atom->i64;
if (((double*)ray_data(key_col))[r] != needle) match = 0;
} else if (kt == RAY_SYM) {
if (ray_read_sym(ray_data(key_col), r, key_col->type, key_col->attrs) != key_sym_want[k]) match = 0;
} else if (kt == RAY_STR) {
const char* ns = ray_str_ptr(key_atom);
size_t nl = ray_str_len(key_atom);
size_t rl = 0;
const char* rs = ray_str_vec_get(key_col, r, &rl);
if (rl != nl || (nl > 0 && (!rs || !ns || memcmp(rs, ns, nl) != 0))) match = 0;
} else {
int64_t needle = elem_as_i64(key_atom);
int64_t existing = (kt == RAY_I64 || kt == RAY_TIMESTAMP) ?
((int64_t*)ray_data(key_col))[r] :
(kt == RAY_I32 || kt == RAY_DATE || kt == RAY_TIME) ?
(int64_t)((int32_t*)ray_data(key_col))[r] :
(kt == RAY_BOOL) ? (int64_t)((uint8_t*)ray_data(key_col))[r] :
((int64_t*)ray_data(key_col))[r];
if (existing != needle) match = 0;
}
}
if (match) { match_row = r; break; }
}
scratch_free(kw_hdr);
scratch_free(kci_hdr);
if (match_row < 0) {
ray_t* insert_args[2] = { tbl, row };
ray_t* result = ray_insert_fn(insert_args, 2);
ray_release(tbl);
ray_release(key_sym);
ray_release(row);
if (inplace_sym >= 0 && result && !RAY_IS_ERR(result)) {
ray_env_set(inplace_sym, result);
ray_release(result);
return ray_sym(inplace_sym);
}
return result;
}
ray_t* result = ray_table_new(ncols);
if (RAY_IS_ERR(result)) { ray_release(tbl); ray_release(key_sym); ray_release(row); return result; }
for (int64_t c = 0; c < ncols; c++) {
int64_t col_name = ray_table_col_name(tbl, c);
ray_t* orig_col = ray_table_get_col_idx(tbl, c);
int8_t ct = orig_col->type;
ray_t* new_col = ray_vec_new(ct, nrows);
if (RAY_IS_ERR(new_col)) { ray_release(result); ray_release(tbl); ray_release(key_sym); ray_release(row); return new_col; }
int has_new_val = (row_elems[c] != NULL);
bool src_has_nulls = ray_vec_may_have_nulls(orig_col);
if (ct == RAY_STR) {
for (int64_t r = 0; r < nrows; r++) {
if (r == match_row && has_new_val) {
new_col = append_atom_to_col(new_col, row_elems[c]);
} else if (src_has_nulls && ray_vec_is_null(orig_col, r)) {
new_col = ray_str_vec_append(new_col, "", 0);
if (!RAY_IS_ERR(new_col))
ray_vec_set_null(new_col, new_col->len - 1, true);
} else {
size_t slen = 0;
const char* sp = ray_str_vec_get(orig_col, r, &slen);
new_col = ray_str_vec_append(new_col, sp ? sp : "", sp ? slen : 0);
}
if (RAY_IS_ERR(new_col)) { ray_release(result); ray_release(tbl); ray_release(key_sym); ray_release(row); return new_col; }
}
} else if (ct == RAY_SYM) {
for (int64_t r = 0; r < nrows; r++) {
if (r == match_row && has_new_val) {
new_col = append_atom_to_col(new_col, row_elems[c]);
} else {
int64_t sym_val = sym_cell_runtime_id(orig_col, r);
new_col = ray_vec_append(new_col, &sym_val);
if (!RAY_IS_ERR(new_col) && src_has_nulls && ray_vec_is_null(orig_col, r))
ray_vec_set_null(new_col, new_col->len - 1, true);
}
if (RAY_IS_ERR(new_col)) { ray_release(result); ray_release(tbl); ray_release(key_sym); ray_release(row); return new_col; }
}
} else {
size_t elem_sz = ray_elem_size(ct);
uint8_t* src = (uint8_t*)ray_data(orig_col);
for (int64_t r = 0; r < nrows; r++) {
if (r == match_row && has_new_val) {
new_col = append_atom_to_col(new_col, row_elems[c]);
} else {
new_col = ray_vec_append(new_col, src + r * elem_sz);
if (!RAY_IS_ERR(new_col) && src_has_nulls && ray_vec_is_null(orig_col, r))
ray_vec_set_null(new_col, new_col->len - 1, true);
}
if (RAY_IS_ERR(new_col)) { ray_release(result); ray_release(tbl); ray_release(key_sym); ray_release(row); return new_col; }
}
}
result = ray_table_add_col(result, col_name, new_col);
ray_release(new_col);
if (RAY_IS_ERR(result)) { ray_release(tbl); ray_release(key_sym); ray_release(row); return result; }
}
ray_release(tbl);
ray_release(key_sym);
ray_release(row);
if (inplace_sym >= 0 && result && !RAY_IS_ERR(result)) {
ray_env_set(inplace_sym, result);
ray_release(result);
return ray_sym(inplace_sym);
}
return result;
}
static ray_t* join_impl(ray_t** args, int64_t n, uint8_t join_type) {
if (n < 3) return ray_error("arity", "join: expects left table, right table and keys, got %lld args", (long long)n);
ray_t* left_tbl = args[0];
ray_t* right_tbl = args[1];
ray_t* keys = args[2];
if (left_tbl->type != RAY_TABLE && args[1]->type == RAY_TABLE && args[2]->type == RAY_TABLE) {
keys = args[0];
left_tbl = args[1];
right_tbl = args[2];
}
if (left_tbl->type != RAY_TABLE || right_tbl->type != RAY_TABLE)
return ray_error("type", "join: both operands must be tables, got %s and %s",
ray_type_name(left_tbl->type), ray_type_name(right_tbl->type));
ray_t* _bxk = NULL;
keys = unbox_vec_arg(keys, &_bxk);
if (RAY_IS_ERR(keys)) return keys;
if (!is_list(keys))
{ if (_bxk) ray_release(_bxk); return ray_error("type", "join: keys must be a symbol list, got %s", ray_type_name(keys->type)); }
int64_t nk = ray_len(keys);
if (nk == 0) { if (_bxk) ray_release(_bxk); return ray_error("domain", "join: requires at least 1 key, got %lld", (long long)nk); }
ray_t** key_elems = (ray_t**)ray_data(keys);
ray_graph_t* g = ray_graph_new(left_tbl);
if (!g) { if (_bxk) ray_release(_bxk); return ray_error("oom", NULL); }
ray_op_t* left_node = ray_const_table(g, left_tbl);
ray_op_t* right_node = ray_const_table(g, right_tbl);
ray_t* keyops_hdr = NULL;
ray_op_t** keyops = (ray_op_t**)scratch_alloc(&keyops_hdr, (size_t)nk * 2 * sizeof(ray_op_t*));
if (!keyops) { ray_graph_free(g); if (_bxk) ray_release(_bxk); return ray_error("oom", NULL); }
ray_op_t** lk = keyops;
ray_op_t** rk = keyops + nk;
for (int64_t i = 0; i < nk; i++) {
if (key_elems[i]->type != -RAY_SYM) {
int8_t ke_t = key_elems[i]->type;
scratch_free(keyops_hdr);
ray_graph_free(g); if (_bxk) ray_release(_bxk);
return ray_error("type", "join: key must be a symbol name, got %s", ray_type_name(ke_t));
}
int64_t key_name = key_elems[i]->i64;
ray_t* left_key_col = ray_table_get_col(left_tbl, key_name);
ray_t* right_key_col = ray_table_get_col(right_tbl, key_name);
if (!left_key_col || !right_key_col) {
ray_t* key_name_str = ray_sym_str(key_name);
const char* side = !left_key_col ? "left" : "right";
scratch_free(keyops_hdr);
ray_graph_free(g); if (_bxk) ray_release(_bxk);
if (key_name_str) {
ray_t* err = ray_error("domain", "join: key column '%.*s' not found in %s table",
(int)ray_str_len(key_name_str), ray_str_ptr(key_name_str), side);
return err;
}
return ray_error("domain", "join: key column not found in %s table", side);
}
int8_t left_key_type = left_key_col->type;
int8_t right_key_type = right_key_col->type;
if (RAY_IS_PARTED(left_key_type)) left_key_type = (int8_t)RAY_PARTED_BASETYPE(left_key_type);
if (RAY_IS_PARTED(right_key_type)) right_key_type = (int8_t)RAY_PARTED_BASETYPE(right_key_type);
if ((left_key_type == RAY_STR) != (right_key_type == RAY_STR)) {
scratch_free(keyops_hdr);
ray_graph_free(g); if (_bxk) ray_release(_bxk);
return ray_error("type", "join: string key columns must have string type on both sides");
}
ray_t* name_str = ray_sym_str(key_elems[i]->i64);
if (!name_str) { scratch_free(keyops_hdr); ray_graph_free(g); if (_bxk) ray_release(_bxk); return ray_error("domain", "join: unknown key symbol"); }
lk[i] = ray_scan(g, ray_str_ptr(name_str));
rk[i] = ray_scan(g, ray_str_ptr(name_str));
if (!lk[i] || !rk[i]) { scratch_free(keyops_hdr); ray_graph_free(g); if (_bxk) ray_release(_bxk); return ray_error("domain", "join: key column not found"); }
}
if (_bxk) ray_release(_bxk);
ray_op_t* jn = ray_join(g, left_node, lk, right_node, rk,
(uint32_t)nk, join_type);
scratch_free(keyops_hdr);
if (!jn) { ray_graph_free(g); return ray_error("oom", NULL); }
jn = ray_optimize(g, jn);
ray_t* result = ray_execute(g, jn);
ray_graph_free(g);
return result;
}
ray_t* ray_left_join_fn(ray_t** args, int64_t n) { return join_impl(args, n, 1); }
ray_t* ray_inner_join_fn(ray_t** args, int64_t n) { return join_impl(args, n, 0); }
ray_t* ray_full_join_fn(ray_t** args, int64_t n) { return join_impl(args, n, 2); }
static ray_t* antijoin_impl(ray_t** args, int64_t n) {
if (n < 3) return ray_error("arity", "antijoin: expects left table, right table and keys, got %lld args", (long long)n);
ray_t* left_tbl = args[0];
ray_t* right_tbl = args[1];
ray_t* keys = args[2];
if (left_tbl->type != RAY_TABLE && args[1]->type == RAY_TABLE && args[2]->type == RAY_TABLE) {
keys = args[0];
left_tbl = args[1];
right_tbl = args[2];
}
if (left_tbl->type != RAY_TABLE || right_tbl->type != RAY_TABLE)
return ray_error("type", "antijoin: both operands must be tables, got %s and %s",
ray_type_name(left_tbl->type), ray_type_name(right_tbl->type));
ray_t* _bxk = NULL;
keys = unbox_vec_arg(keys, &_bxk);
if (RAY_IS_ERR(keys)) return keys;
if (!is_list(keys))
{ if (_bxk) ray_release(_bxk); return ray_error("type", "antijoin: keys must be a symbol list, got %s", ray_type_name(keys->type)); }
int64_t nk = ray_len(keys);
if (nk == 0) { if (_bxk) ray_release(_bxk); return ray_error("domain", "antijoin: requires at least 1 key, got %lld", (long long)nk); }
ray_t** key_elems = (ray_t**)ray_data(keys);
ray_graph_t* g = ray_graph_new(left_tbl);
if (!g) { if (_bxk) ray_release(_bxk); return ray_error("oom", NULL); }
ray_op_t* left_node = ray_const_table(g, left_tbl);
ray_op_t* right_node = ray_const_table(g, right_tbl);
ray_t* keyops_hdr = NULL;
ray_op_t** keyops = (ray_op_t**)scratch_alloc(&keyops_hdr, (size_t)nk * 2 * sizeof(ray_op_t*));
if (!keyops) { ray_graph_free(g); if (_bxk) ray_release(_bxk); return ray_error("oom", NULL); }
ray_op_t** lk = keyops;
ray_op_t** rk = keyops + nk;
for (int64_t i = 0; i < nk; i++) {
if (key_elems[i]->type != -RAY_SYM) {
int8_t ke_t = key_elems[i]->type;
scratch_free(keyops_hdr);
ray_graph_free(g); if (_bxk) ray_release(_bxk);
return ray_error("type", "antijoin: key must be a symbol name, got %s", ray_type_name(ke_t));
}
int64_t key_name = key_elems[i]->i64;
ray_t* left_key_col = ray_table_get_col(left_tbl, key_name);
ray_t* right_key_col = ray_table_get_col(right_tbl, key_name);
if (!left_key_col || !right_key_col) {
ray_t* key_name_str = ray_sym_str(key_name);
const char* side = !left_key_col ? "left" : "right";
scratch_free(keyops_hdr);
ray_graph_free(g); if (_bxk) ray_release(_bxk);
if (key_name_str)
return ray_error("domain", "antijoin: key column '%.*s' not found in %s table",
(int)ray_str_len(key_name_str), ray_str_ptr(key_name_str), side);
return ray_error("domain", "antijoin: key column not found in %s table", side);
}
int8_t left_key_type = left_key_col->type;
int8_t right_key_type = right_key_col->type;
if (RAY_IS_PARTED(left_key_type)) left_key_type = (int8_t)RAY_PARTED_BASETYPE(left_key_type);
if (RAY_IS_PARTED(right_key_type)) right_key_type = (int8_t)RAY_PARTED_BASETYPE(right_key_type);
if ((left_key_type == RAY_STR) != (right_key_type == RAY_STR)) {
scratch_free(keyops_hdr);
ray_graph_free(g); if (_bxk) ray_release(_bxk);
return ray_error("type", "antijoin: string key columns must have string type on both sides");
}
ray_t* name_str = ray_sym_str(key_elems[i]->i64);
if (!name_str) { scratch_free(keyops_hdr); ray_graph_free(g); if (_bxk) ray_release(_bxk); return ray_error("domain", "antijoin: unknown key symbol"); }
lk[i] = ray_scan(g, ray_str_ptr(name_str));
rk[i] = ray_scan(g, ray_str_ptr(name_str));
if (!lk[i] || !rk[i]) { scratch_free(keyops_hdr); ray_graph_free(g); if (_bxk) ray_release(_bxk); return ray_error("domain", "antijoin: key column not found"); }
}
if (_bxk) ray_release(_bxk);
ray_op_t* jn = ray_antijoin(g, left_node, lk, right_node, rk, (uint32_t)nk);
scratch_free(keyops_hdr);
if (!jn) { ray_graph_free(g); return ray_error("oom", NULL); }
jn = ray_optimize(g, jn);
ray_t* result = ray_execute(g, jn);
ray_graph_free(g);
return result;
}
ray_t* ray_anti_join_fn(ray_t** args, int64_t n) { return antijoin_impl(args, n); }
typedef struct {
int64_t cnt;
int64_t sum_i;
double sum_f;
int64_t sum_sq_i;
double sum_sq_f;
int64_t extreme_i;
double extreme_f;
int64_t prod_i;
double prod_f;
} wj_acc_t;
typedef struct {
int64_t left_nrows;
int64_t right_nrows;
int64_t n_eq;
int64_t n_agg;
int mode;
const int64_t* lo_arr;
const int64_t* hi_arr;
const int64_t** left_eq_arr;
const int64_t* right_sort;
const int64_t* rt_time_i;
const void** eq_data;
const int8_t* eq_type;
const uint8_t* eq_attrs;
const uint8_t* agg_raw;
const uint16_t* agg_ops;
const int8_t* agg_result_types;
const int* agg_is_float;
const int64_t** sorted_i;
const double** sorted_f;
const uint8_t** sorted_nn;
void** result_data;
uint8_t** result_null;
wj_acc_t* acc_base;
int64_t* target_base;
} wj_scan_ctx_t;
static inline int wj_eq_cmp(const wj_scan_ctx_t* c, int64_t ri,
const int64_t* target_eq, int64_t n_eq) {
for (int64_t e = 0; e < n_eq; e++) {
int64_t rv = read_col_i64(c->eq_data[e], ri, c->eq_type[e], c->eq_attrs[e]);
if (rv < target_eq[e]) return -1;
if (rv > target_eq[e]) return 1;
}
return 0;
}
static void wj_scan_fn(void* ctx_, uint32_t worker_id, int64_t start, int64_t end) {
wj_scan_ctx_t* c = (wj_scan_ctx_t*)ctx_;
int64_t n_eq = c->n_eq;
int64_t n_agg = c->n_agg;
wj_acc_t* acc = c->acc_base + (size_t)worker_id * n_agg;
int64_t* target_eq = c->target_base + (size_t)worker_id * n_eq;
int64_t rn = c->right_nrows;
const int64_t* right_sort = c->right_sort;
const int64_t* rt_time_i = c->rt_time_i;
for (int64_t lr = start; lr < end; lr++) {
int64_t lo = c->lo_arr[lr];
int64_t hi = c->hi_arr[lr];
for (int64_t e = 0; e < n_eq; e++)
target_eq[e] = c->left_eq_arr[e][lr];
int64_t ps = 0, ps_hi = rn;
while (ps < ps_hi) {
int64_t m = (ps + ps_hi) >> 1;
if (wj_eq_cmp(c, right_sort[m], target_eq, n_eq) < 0) ps = m + 1;
else ps_hi = m;
}
int64_t pe = ps, pe_hi = rn;
while (pe < pe_hi) {
int64_t m = (pe + pe_hi) >> 1;
if (wj_eq_cmp(c, right_sort[m], target_eq, n_eq) <= 0) pe = m + 1;
else pe_hi = m;
}
int64_t ub = ps, ub_hi = pe;
while (ub < ub_hi) {
int64_t m = (ub + ub_hi) >> 1;
if (rt_time_i[right_sort[m]] <= hi) ub = m + 1; else ub_hi = m;
}
int64_t lb;
if (c->mode == 1) {
lb = ps; int64_t lbh = pe;
while (lb < lbh) {
int64_t m = (lb + lbh) >> 1;
if (rt_time_i[right_sort[m]] < lo) lb = m + 1; else lbh = m;
}
} else {
int64_t r = ps, rh = pe;
while (r < rh) {
int64_t m = (r + rh) >> 1;
if (rt_time_i[right_sort[m]] <= lo) r = m + 1; else rh = m;
}
lb = (r > ps) ? r - 1 : ps;
}
if (lb > ub) lb = ub;
memset(acc, 0, (size_t)n_agg * sizeof(wj_acc_t));
for (int64_t a = 0; a < n_agg; a++) {
if (c->agg_ops[a] == OP_PROD) { acc[a].prod_i = 1; acc[a].prod_f = 1.0; }
}
for (int64_t a = 0; a < n_agg; a++) {
if (c->agg_raw[a]) continue;
wj_acc_t* A = &acc[a];
uint16_t op = c->agg_ops[a];
if (op == OP_COUNT) { A->cnt += (ub - lb); continue; }
const uint8_t* nn = c->sorted_nn[a];
if (c->agg_is_float[a]) {
const double* ss = c->sorted_f[a];
switch (op) {
case OP_SUM: case OP_AVG: {
double sum = 0; int64_t cnt = 0;
if (nn) { for (int64_t k = lb; k < ub; k++) if (nn[k]) { sum += ss[k]; cnt++; } }
else { for (int64_t k = lb; k < ub; k++) sum += ss[k]; cnt = ub - lb; }
A->sum_f = sum; A->cnt = cnt; break;
}
case OP_VAR: case OP_VAR_POP:
case OP_STDDEV: case OP_STDDEV_POP: {
double sum = 0, sum2 = 0; int64_t cnt = 0;
if (nn) {
for (int64_t k = lb; k < ub; k++)
if (nn[k]) { double v = ss[k]; sum += v; sum2 += v * v; cnt++; }
} else {
for (int64_t k = lb; k < ub; k++) { double v = ss[k]; sum += v; sum2 += v * v; }
cnt = ub - lb;
}
A->sum_f = sum; A->sum_sq_f = sum2; A->cnt = cnt; break;
}
case OP_PROD: {
double p = 1.0; int64_t cnt = 0;
if (nn) { for (int64_t k = lb; k < ub; k++) if (nn[k]) { p *= ss[k]; cnt++; } }
else { for (int64_t k = lb; k < ub; k++) p *= ss[k]; cnt = ub - lb; }
A->prod_f = p; A->cnt = cnt; break;
}
case OP_MIN: {
int64_t k = lb;
if (nn) {
double best = 0; int64_t cnt = 0;
for (; k < ub; k++) if (nn[k]) { best = ss[k]; cnt = 1; k++; break; }
for (; k < ub; k++) if (nn[k]) { double v = ss[k]; if (v < best) best = v; cnt++; }
A->extreme_f = best; A->cnt = cnt;
} else if (k < ub) {
double best = ss[k++];
for (; k < ub; k++) { double v = ss[k]; if (v < best) best = v; }
A->extreme_f = best; A->cnt = ub - lb;
}
break;
}
case OP_MAX: {
int64_t k = lb;
if (nn) {
double best = 0; int64_t cnt = 0;
for (; k < ub; k++) if (nn[k]) { best = ss[k]; cnt = 1; k++; break; }
for (; k < ub; k++) if (nn[k]) { double v = ss[k]; if (v > best) best = v; cnt++; }
A->extreme_f = best; A->cnt = cnt;
} else if (k < ub) {
double best = ss[k++];
for (; k < ub; k++) { double v = ss[k]; if (v > best) best = v; }
A->extreme_f = best; A->cnt = ub - lb;
}
break;
}
case OP_FIRST: {
if (nn) {
int64_t cnt = 0;
for (int64_t k = lb; k < ub; k++) if (nn[k]) {
if (cnt == 0) A->extreme_f = ss[k];
cnt++;
}
A->cnt = cnt;
} else if (lb < ub) {
A->extreme_f = ss[lb]; A->cnt = ub - lb;
}
break;
}
case OP_LAST: {
if (nn) {
int64_t cnt = 0, last_k = -1;
for (int64_t k = lb; k < ub; k++) if (nn[k]) { last_k = k; cnt++; }
if (last_k >= 0) A->extreme_f = ss[last_k];
A->cnt = cnt;
} else if (lb < ub) {
A->extreme_f = ss[ub - 1]; A->cnt = ub - lb;
}
break;
}
default: break;
}
} else {
const int64_t* ss = c->sorted_i[a];
switch (op) {
case OP_SUM: case OP_AVG: {
int64_t sum = 0; int64_t cnt = 0;
if (nn) { for (int64_t k = lb; k < ub; k++) if (nn[k]) { sum += ss[k]; cnt++; } }
else { for (int64_t k = lb; k < ub; k++) sum += ss[k]; cnt = ub - lb; }
A->sum_i = sum; A->cnt = cnt; break;
}
case OP_VAR: case OP_VAR_POP:
case OP_STDDEV: case OP_STDDEV_POP: {
int64_t sum = 0, sum2 = 0; int64_t cnt = 0;
if (nn) {
for (int64_t k = lb; k < ub; k++)
if (nn[k]) { int64_t v = ss[k]; sum += v; sum2 += v * v; cnt++; }
} else {
for (int64_t k = lb; k < ub; k++) { int64_t v = ss[k]; sum += v; sum2 += v * v; }
cnt = ub - lb;
}
A->sum_i = sum; A->sum_sq_i = sum2; A->cnt = cnt; break;
}
case OP_PROD: {
int64_t p = 1; int64_t cnt = 0;
if (nn) { for (int64_t k = lb; k < ub; k++) if (nn[k]) { p *= ss[k]; cnt++; } }
else { for (int64_t k = lb; k < ub; k++) p *= ss[k]; cnt = ub - lb; }
A->prod_i = p; A->cnt = cnt; break;
}
case OP_MIN: {
int64_t k = lb;
if (nn) {
int64_t best = 0, cnt = 0;
for (; k < ub; k++) if (nn[k]) { best = ss[k]; cnt = 1; k++; break; }
for (; k < ub; k++) if (nn[k]) { int64_t v = ss[k]; if (v < best) best = v; cnt++; }
A->extreme_i = best; A->cnt = cnt;
} else if (k < ub) {
int64_t best = ss[k++];
for (; k < ub; k++) { int64_t v = ss[k]; if (v < best) best = v; }
A->extreme_i = best; A->cnt = ub - lb;
}
break;
}
case OP_MAX: {
int64_t k = lb;
if (nn) {
int64_t best = 0, cnt = 0;
for (; k < ub; k++) if (nn[k]) { best = ss[k]; cnt = 1; k++; break; }
for (; k < ub; k++) if (nn[k]) { int64_t v = ss[k]; if (v > best) best = v; cnt++; }
A->extreme_i = best; A->cnt = cnt;
} else if (k < ub) {
int64_t best = ss[k++];
for (; k < ub; k++) { int64_t v = ss[k]; if (v > best) best = v; }
A->extreme_i = best; A->cnt = ub - lb;
}
break;
}
case OP_FIRST: {
if (nn) {
int64_t cnt = 0;
for (int64_t k = lb; k < ub; k++) if (nn[k]) {
if (cnt == 0) A->extreme_i = ss[k];
cnt++;
}
A->cnt = cnt;
} else if (lb < ub) {
A->extreme_i = ss[lb]; A->cnt = ub - lb;
}
break;
}
case OP_LAST: {
if (nn) {
int64_t cnt = 0, last_k = -1;
for (int64_t k = lb; k < ub; k++) if (nn[k]) { last_k = k; cnt++; }
if (last_k >= 0) A->extreme_i = ss[last_k];
A->cnt = cnt;
} else if (lb < ub) {
A->extreme_i = ss[ub - 1]; A->cnt = ub - lb;
}
break;
}
default: break;
}
}
}
for (int64_t a = 0; a < n_agg; a++) {
wj_acc_t* A = &acc[a];
int8_t rty = c->agg_result_types[a];
bool null_out = false;
int64_t out_i = 0;
double out_f = 0.0;
if (c->agg_raw[a]) {
null_out = true;
} else {
switch (c->agg_ops[a]) {
case OP_COUNT: out_i = A->cnt; break;
case OP_SUM:
if (c->agg_is_float[a]) out_f = A->sum_f; else out_i = A->sum_i;
break;
case OP_PROD:
if (A->cnt == 0) null_out = true;
else if (c->agg_is_float[a]) out_f = A->prod_f; else out_i = A->prod_i;
break;
case OP_MIN: case OP_MAX: case OP_FIRST: case OP_LAST:
if (A->cnt == 0) null_out = true;
else if (c->agg_is_float[a]) out_f = A->extreme_f; else out_i = A->extreme_i;
break;
case OP_AVG:
if (A->cnt == 0) null_out = true;
else out_f = c->agg_is_float[a]
? A->sum_f / (double)A->cnt
: (double)A->sum_i / (double)A->cnt;
break;
case OP_VAR: case OP_VAR_POP:
case OP_STDDEV: case OP_STDDEV_POP: {
bool sample = (c->agg_ops[a] == OP_VAR || c->agg_ops[a] == OP_STDDEV);
bool insuf = sample ? (A->cnt <= 1) : (A->cnt <= 0);
if (insuf) { null_out = true; break; }
double mean, var_pop;
if (c->agg_is_float[a]) {
mean = A->sum_f / (double)A->cnt;
var_pop = A->sum_sq_f / (double)A->cnt - mean * mean;
} else {
mean = (double)A->sum_i / (double)A->cnt;
var_pop = (double)A->sum_sq_i / (double)A->cnt - mean * mean;
}
if (var_pop < 0) var_pop = 0;
if (c->agg_ops[a] == OP_VAR_POP) out_f = var_pop;
else if (c->agg_ops[a] == OP_VAR) out_f = var_pop * A->cnt / (A->cnt - 1);
else if (c->agg_ops[a] == OP_STDDEV_POP) out_f = sqrt(var_pop);
else out_f = sqrt(var_pop * A->cnt / (A->cnt - 1));
break;
}
default: null_out = true; break;
}
}
if (!null_out && rty == RAY_F64) {
out_f = ray_f64_fin(out_f);
if (out_f != out_f) null_out = true;
}
c->result_null[a][lr] = null_out ? 1 : 0;
if (null_out) continue;
void* rd = c->result_data[a];
if (rty == RAY_F64) ((double*)rd)[lr] = out_f;
else if (rty == RAY_F32) ((float*)rd)[lr] = (float)out_f;
else if (rty == RAY_I64 || rty == RAY_TIMESTAMP)
((int64_t*)rd)[lr] = out_i;
else if (rty == RAY_I32 || rty == RAY_DATE || rty == RAY_TIME)
((int32_t*)rd)[lr] = (int32_t)out_i;
else if (rty == RAY_I16) ((int16_t*)rd)[lr] = (int16_t)out_i;
else ((uint8_t*)rd)[lr] = (uint8_t)out_i;
}
}
}
static int8_t join_i64_eq_key_type(ray_t* col) {
if (!col) return 0;
int8_t t = col->type;
if (RAY_IS_PARTED(t)) t = (int8_t)RAY_PARTED_BASETYPE(t);
return t;
}
static bool join_i64_eq_key_supported(int8_t t) {
switch (t) {
case RAY_BOOL:
case RAY_U8:
case RAY_I16:
case RAY_I32:
case RAY_I64:
case RAY_DATE:
case RAY_TIME:
case RAY_TIMESTAMP:
case RAY_SYM:
return true;
default:
return false;
}
}
static ray_t* window_join_impl(ray_t** args, int64_t n, int mode) {
if (n < 4) return ray_error("domain", "window-join: expects keys, intervals, left and right tables, got %lld args", (long long)n);
ray_t* eargs[5];
for (int i = 0; i < 4 && i < (int)n; i++) {
eargs[i] = ray_eval(args[i]);
if (!eargs[i] || RAY_IS_ERR(eargs[i])) {
for (int j = 0; j < i; j++) ray_release(eargs[j]);
return eargs[i] ? eargs[i] : ray_error("type", "window-join: argument evaluation failed");
}
}
eargs[4] = (n >= 5) ? args[4] : NULL;
if (n >= 5 && ray_is_vec(eargs[0]) && eargs[0]->type == RAY_SYM &&
eargs[2]->type == RAY_TABLE && eargs[3]->type == RAY_TABLE) {
ray_t* keys_vec = eargs[0];
ray_t* intervals = eargs[1];
ray_t* left_tbl = eargs[2];
ray_t* right_tbl = eargs[3];
ray_t* agg_dict = eargs[4];
int64_t nkeys = ray_len(keys_vec);
if (nkeys < 2) return ray_error("domain", "window-join: keys must contain at least one equality key plus a time key, got %lld", (long long)nkeys);
int64_t time_key = sym_cell_runtime_id(keys_vec, nkeys - 1);
int64_t n_eq = nkeys - 1;
int64_t left_nrows = ray_table_nrows(left_tbl);
int64_t right_nrows = ray_table_nrows(right_tbl);
ray_t* left_time = ray_table_get_col(left_tbl, time_key);
ray_t* right_time = ray_table_get_col(right_tbl, time_key);
if (!left_time || !right_time) return ray_error("domain", "window-join: time key column not found in both tables");
ray_t* eq_hdr = NULL;
ray_t** left_eq = (ray_t**)scratch_calloc(&eq_hdr,
(size_t)n_eq * (5 * sizeof(void*) + 2 * sizeof(uint8_t)));
if (!left_eq) { for (int i = 0; i < 4; i++) ray_release(eargs[i]); return ray_error("oom", NULL); }
ray_t** right_eq = (ray_t**)(left_eq + n_eq);
const void** eq_data = (const void**)(right_eq + n_eq);
ray_t** left_eq_hdr = (ray_t**)(eq_data + n_eq);
int64_t** left_eq_arr = (int64_t**)(left_eq_hdr + n_eq);
int8_t* eq_type = (int8_t*)(left_eq_arr + n_eq);
uint8_t* eq_attrs = (uint8_t*)(eq_type + n_eq);
for (int64_t e = 0; e < n_eq; e++) {
int64_t eq_key = sym_cell_runtime_id(keys_vec, e);
left_eq[e] = ray_table_get_col(left_tbl, eq_key);
right_eq[e] = ray_table_get_col(right_tbl, eq_key);
if (!left_eq[e] || !right_eq[e]) {
scratch_free(eq_hdr);
for (int i = 0; i < 4; i++) ray_release(eargs[i]);
return ray_error("domain", "window-join: equality key column not found in both tables");
}
int8_t lct = join_i64_eq_key_type(left_eq[e]);
int8_t rct = join_i64_eq_key_type(right_eq[e]);
if (!join_i64_eq_key_supported(lct) ||
!join_i64_eq_key_supported(rct)) {
scratch_free(eq_hdr);
for (int i = 0; i < 4; i++) ray_release(eargs[i]);
return ray_error("nyi", "window-join: equality key column types %s and %s are not supported",
ray_type_name(lct), ray_type_name(rct));
}
}
ray_t* dkeys = NULL, *dvals = NULL;
ray_t** lvals = NULL;
int64_t adn = 0;
if (agg_dict && agg_dict->type == RAY_DICT) {
dkeys = ray_dict_keys(agg_dict);
dvals = ray_dict_vals(agg_dict);
if (dkeys && dkeys->type == RAY_SYM) adn = dkeys->len;
lvals = (dvals && dvals->type == RAY_LIST) ? (ray_t**)ray_data(dvals) : NULL;
}
ray_t* agg_hdr = NULL;
int64_t* agg_names = NULL;
int64_t* agg_src_ids = NULL;
ray_t** agg_src_vecs = NULL;
ray_t** agg_result_vecs = NULL;
int64_t** sorted_i = NULL;
double** sorted_f = NULL;
uint8_t** sorted_nn = NULL;
ray_t** sorted_i_hdr = NULL;
ray_t** sorted_f_hdr = NULL;
ray_t** sorted_nn_hdr = NULL;
ray_t** null_stage_hdr = NULL;
uint8_t** null_stage = NULL;
void** result_data = NULL;
int* agg_is_float = NULL;
uint16_t* agg_ops = NULL;
int8_t* agg_types = NULL;
int8_t* agg_result_types = NULL;
uint8_t* agg_raw = NULL;
if (adn > 0) {
agg_names = (int64_t*)scratch_calloc(&agg_hdr,
(size_t)adn * (13 * sizeof(void*) + sizeof(int) + sizeof(uint16_t) + 3 * sizeof(uint8_t)));
if (!agg_names) {
scratch_free(eq_hdr);
for (int i = 0; i < 4; i++) ray_release(eargs[i]);
return ray_error("oom", NULL);
}
agg_src_ids = (int64_t*)(agg_names + adn);
agg_src_vecs = (ray_t**)(agg_src_ids + adn);
agg_result_vecs = (ray_t**)(agg_src_vecs + adn);
sorted_i = (int64_t**)(agg_result_vecs + adn);
sorted_f = (double**)(sorted_i + adn);
sorted_nn = (uint8_t**)(sorted_f + adn);
sorted_i_hdr = (ray_t**)(sorted_nn + adn);
sorted_f_hdr = (ray_t**)(sorted_i_hdr + adn);
sorted_nn_hdr = (ray_t**)(sorted_f_hdr + adn);
null_stage_hdr = (ray_t**)(sorted_nn_hdr + adn);
null_stage = (uint8_t**)(null_stage_hdr + adn);
result_data = (void**)(null_stage + adn);
agg_is_float = (int*)(result_data + adn);
agg_ops = (uint16_t*)(agg_is_float + adn);
agg_types = (int8_t*)(agg_ops + adn);
agg_result_types = (int8_t*)(agg_types + adn);
agg_raw = (uint8_t*)(agg_result_types + adn);
}
int64_t n_agg = 0;
{
for (int64_t di = 0; di < adn; di++) {
int64_t kname_id = sym_cell_runtime_id(dkeys, di);
ray_t* expr = lvals ? lvals[di] : NULL;
if (!expr) continue;
if (expr->type == RAY_LIST && expr->len >= 2) {
ray_t** ae = (ray_t**)ray_data(expr);
if (!(ae[0]->type == -RAY_SYM && !(ae[0]->attrs & ATTR_QUOTED))) continue;
if (!(ae[1]->type == -RAY_SYM && !(ae[1]->attrs & ATTR_QUOTED))) continue;
agg_names[n_agg] = kname_id;
agg_ops[n_agg] = resolve_agg_opcode(ae[0]->i64);
agg_src_ids[n_agg] = ae[1]->i64;
agg_raw[n_agg] = 0;
n_agg++;
continue;
}
if (expr->type == -RAY_SYM && !(expr->attrs & ATTR_QUOTED)) {
agg_names[n_agg] = kname_id;
agg_ops[n_agg] = OP_MIN;
agg_src_ids[n_agg] = expr->i64;
agg_raw[n_agg] = 1;
n_agg++;
continue;
}
}
}
for (int64_t a = 0; a < n_agg; a++) {
if (agg_raw[a]) {
agg_src_vecs[a] = NULL;
agg_types[a] = RAY_I64;
agg_is_float[a] = 0;
agg_result_types[a] = RAY_I64;
agg_result_vecs[a] = ray_vec_new(RAY_I64, left_nrows);
if (RAY_IS_ERR(agg_result_vecs[a])) {
ray_t* err = agg_result_vecs[a];
for (int64_t b = 0; b < a; b++) ray_release(agg_result_vecs[b]);
for (int i = 0; i < 4; i++) ray_release(eargs[i]);
scratch_free(agg_hdr); scratch_free(eq_hdr);
return err;
}
continue;
}
if (agg_ops[a] == 0) {
for (int64_t b = 0; b < a; b++) ray_release(agg_result_vecs[b]);
for (int i = 0; i < 4; i++) ray_release(eargs[i]);
scratch_free(agg_hdr); scratch_free(eq_hdr);
return ray_error("domain", "window-join: unsupported aggregation op");
}
ray_t* src = ray_table_get_col(right_tbl, agg_src_ids[a]);
if (!src) {
for (int64_t b = 0; b < a; b++) ray_release(agg_result_vecs[b]);
for (int i = 0; i < 4; i++) ray_release(eargs[i]);
scratch_free(agg_hdr); scratch_free(eq_hdr);
return ray_error("domain", "window-join: aggregation source column not found");
}
int8_t t = src->type;
if (agg_ops[a] != OP_COUNT) {
switch (t) {
case RAY_I64: case RAY_I32: case RAY_I16: case RAY_U8:
case RAY_F64: case RAY_F32: case RAY_BOOL:
case RAY_DATE: case RAY_TIME: case RAY_TIMESTAMP:
break;
default:
for (int64_t b = 0; b < a; b++) ray_release(agg_result_vecs[b]);
for (int i = 0; i < 4; i++) ray_release(eargs[i]);
scratch_free(agg_hdr); scratch_free(eq_hdr);
return ray_error("type", "window-join: aggregation source must be numeric, got %s", ray_type_name(t));
}
}
agg_src_vecs[a] = src;
agg_types[a] = t;
agg_is_float[a] = (t == RAY_F64 || t == RAY_F32);
int8_t rt;
switch (agg_ops[a]) {
case OP_COUNT: rt = RAY_I64; break;
case OP_AVG:
case OP_VAR: case OP_VAR_POP:
case OP_STDDEV: case OP_STDDEV_POP:
case OP_MEDIAN:
case OP_QUANTILE: rt = RAY_F64; break;
case OP_SUM: case OP_PROD:
rt = agg_is_float[a] ? RAY_F64 : RAY_I64; break;
default: rt = t; break;
}
agg_result_types[a] = rt;
agg_result_vecs[a] = ray_vec_new(rt, left_nrows);
if (RAY_IS_ERR(agg_result_vecs[a])) {
ray_t* err = agg_result_vecs[a];
for (int64_t b = 0; b < a; b++) ray_release(agg_result_vecs[b]);
for (int i = 0; i < 4; i++) ray_release(eargs[i]);
scratch_free(agg_hdr); scratch_free(eq_hdr);
return err;
}
}
ray_t* rs_hdr = NULL, *rt_hdr = NULL, *tmp_hdr = NULL;
int64_t* right_sort = NULL;
int64_t* rt_time_i = NULL;
int64_t* tmp_sort = NULL;
for (int64_t e = 0; e < n_eq; e++) {
eq_data[e] = ray_data(right_eq[e]);
eq_type[e] = right_eq[e]->type;
eq_attrs[e] = right_eq[e]->attrs;
}
if (right_nrows > 0) {
right_sort = (int64_t*)scratch_alloc(&rs_hdr, (size_t)right_nrows * sizeof(int64_t));
rt_time_i = (int64_t*)scratch_alloc(&rt_hdr, (size_t)right_nrows * sizeof(int64_t));
tmp_sort = (int64_t*)scratch_alloc(&tmp_hdr, (size_t)right_nrows * sizeof(int64_t));
if (!right_sort || !rt_time_i || !tmp_sort) {
if (rs_hdr) scratch_free(rs_hdr);
if (rt_hdr) scratch_free(rt_hdr);
if (tmp_hdr) scratch_free(tmp_hdr);
for (int64_t a = 0; a < n_agg; a++) ray_release(agg_result_vecs[a]);
for (int i = 0; i < 4; i++) ray_release(eargs[i]);
scratch_free(agg_hdr); scratch_free(eq_hdr);
return ray_error("oom", NULL);
}
int8_t rt_type = right_time->type;
uint8_t rt_attrs = right_time->attrs;
const void* rt_data = ray_data(right_time);
for (int64_t rr = 0; rr < right_nrows; rr++) {
right_sort[rr] = rr;
rt_time_i[rr] = read_col_i64(rt_data, rr, rt_type, rt_attrs);
}
for (int64_t width = 1; width < right_nrows; width *= 2) {
for (int64_t lo = 0; lo < right_nrows; lo += 2 * width) {
int64_t mid = lo + width;
int64_t hi = lo + 2 * width;
if (mid > right_nrows) mid = right_nrows;
if (hi > right_nrows) hi = right_nrows;
int64_t a = lo, b = mid, t = lo;
while (a < mid && b < hi) {
int64_t ai = right_sort[a], bi = right_sort[b];
int cmp = 0;
for (int64_t e = 0; e < n_eq && cmp == 0; e++) {
int64_t va = read_col_i64(eq_data[e], ai, eq_type[e], eq_attrs[e]);
int64_t vb = read_col_i64(eq_data[e], bi, eq_type[e], eq_attrs[e]);
if (va < vb) cmp = -1;
else if (va > vb) cmp = 1;
}
if (cmp == 0) {
if (rt_time_i[ai] < rt_time_i[bi]) cmp = -1;
else if (rt_time_i[ai] > rt_time_i[bi]) cmp = 1;
}
tmp_sort[t++] = (cmp <= 0) ? right_sort[a++] : right_sort[b++];
}
while (a < mid) tmp_sort[t++] = right_sort[a++];
while (b < hi) tmp_sort[t++] = right_sort[b++];
for (int64_t c = lo; c < hi; c++) right_sort[c] = tmp_sort[c];
}
}
scratch_free(tmp_hdr);
tmp_hdr = NULL;
tmp_sort = NULL;
}
for (int64_t a = 0; a < n_agg; a++) {
if (agg_raw[a] || agg_ops[a] == OP_COUNT) continue;
ray_t* src = agg_src_vecs[a];
if (!src || right_nrows == 0) continue;
bool has_nulls = ray_vec_may_have_nulls(src);
if (has_nulls) {
sorted_nn[a] = (uint8_t*)scratch_alloc(&sorted_nn_hdr[a],
(size_t)right_nrows);
if (!sorted_nn[a]) { goto wj_preload_oom; }
}
if (agg_is_float[a]) {
sorted_f[a] = (double*)scratch_alloc(&sorted_f_hdr[a],
(size_t)right_nrows * sizeof(double));
if (!sorted_f[a]) { goto wj_preload_oom; }
int8_t t = agg_types[a];
const void* sd = ray_data(src);
for (int64_t k = 0; k < right_nrows; k++) {
int64_t rr = right_sort[k];
double v = (t == RAY_F32)
? (double)((const float*)sd)[rr]
: ((const double*)sd)[rr];
sorted_f[a][k] = v;
if (has_nulls) sorted_nn[a][k] = ray_vec_is_null(src, rr) ? 0 : 1;
}
} else {
sorted_i[a] = (int64_t*)scratch_alloc(&sorted_i_hdr[a],
(size_t)right_nrows * sizeof(int64_t));
if (!sorted_i[a]) { goto wj_preload_oom; }
const void* sd = ray_data(src);
int8_t t = agg_types[a];
uint8_t at = src->attrs;
for (int64_t k = 0; k < right_nrows; k++) {
int64_t rr = right_sort[k];
sorted_i[a][k] = read_col_i64(sd, rr, t, at);
if (has_nulls) sorted_nn[a][k] = ray_vec_is_null(src, rr) ? 0 : 1;
}
}
}
#define WJ_CLEANUP_TEMP() do { \
if (rs_hdr) scratch_free(rs_hdr); \
if (rt_hdr) scratch_free(rt_hdr); \
if (tmp_hdr) scratch_free(tmp_hdr); \
for (int64_t _a = 0; _a < n_agg; _a++) { \
if (sorted_i_hdr[_a]) scratch_free(sorted_i_hdr[_a]); \
if (sorted_f_hdr[_a]) scratch_free(sorted_f_hdr[_a]); \
if (sorted_nn_hdr[_a]) scratch_free(sorted_nn_hdr[_a]); \
} \
} while (0)
if (0) {
wj_preload_oom:
WJ_CLEANUP_TEMP();
for (int64_t a = 0; a < n_agg; a++) ray_release(agg_result_vecs[a]);
for (int i = 0; i < 4; i++) ray_release(eargs[i]);
scratch_free(agg_hdr); scratch_free(eq_hdr);
return ray_error("oom", NULL);
}
ray_t* lo_hdr = NULL, *hi_hdr = NULL;
int64_t* lo_arr = (int64_t*)scratch_alloc(&lo_hdr, (size_t)left_nrows * sizeof(int64_t));
int64_t* hi_arr = (int64_t*)scratch_alloc(&hi_hdr, (size_t)left_nrows * sizeof(int64_t));
if ((!lo_arr || !hi_arr) && left_nrows > 0) {
if (lo_hdr) scratch_free(lo_hdr);
if (hi_hdr) scratch_free(hi_hdr);
WJ_CLEANUP_TEMP();
for (int64_t a = 0; a < n_agg; a++) ray_release(agg_result_vecs[a]);
for (int i = 0; i < 4; i++) ray_release(eargs[i]);
scratch_free(agg_hdr); scratch_free(eq_hdr);
return ray_error("oom", NULL);
}
{
int alloc_lo_v = 0, alloc_hi_v = 0;
ray_t* lo_vec = (ray_len(intervals) >= 2)
? collection_elem(intervals, 0, &alloc_lo_v) : NULL;
ray_t* hi_vec = (ray_len(intervals) >= 2)
? collection_elem(intervals, 1, &alloc_hi_v) : NULL;
if (!lo_vec || !hi_vec || RAY_IS_ERR(lo_vec) || RAY_IS_ERR(hi_vec) ||
!ray_is_vec(lo_vec) || !ray_is_vec(hi_vec) ||
ray_len(lo_vec) != left_nrows || ray_len(hi_vec) != left_nrows) {
if (alloc_lo_v && lo_vec) ray_release(lo_vec);
if (alloc_hi_v && hi_vec) ray_release(hi_vec);
if (lo_hdr) scratch_free(lo_hdr);
if (hi_hdr) scratch_free(hi_hdr);
WJ_CLEANUP_TEMP();
for (int64_t a = 0; a < n_agg; a++) ray_release(agg_result_vecs[a]);
for (int i = 0; i < 4; i++) ray_release(eargs[i]);
scratch_free(agg_hdr); scratch_free(eq_hdr);
return ray_error("domain", "window-join: intervals must be [lo hi] vectors matching left row count");
}
const void* lo_d = ray_data(lo_vec);
const void* hi_d = ray_data(hi_vec);
int8_t lo_t = lo_vec->type, hi_t = hi_vec->type;
uint8_t lo_a = lo_vec->attrs, hi_a = hi_vec->attrs;
for (int64_t lr = 0; lr < left_nrows; lr++) {
lo_arr[lr] = read_col_i64(lo_d, lr, lo_t, lo_a);
hi_arr[lr] = read_col_i64(hi_d, lr, hi_t, hi_a);
}
if (alloc_lo_v) ray_release(lo_vec);
if (alloc_hi_v) ray_release(hi_vec);
}
for (int64_t e = 0; e < n_eq; e++) {
left_eq_arr[e] = (int64_t*)scratch_alloc(&left_eq_hdr[e],
(size_t)left_nrows * sizeof(int64_t));
if (!left_eq_arr[e] && left_nrows > 0) {
if (lo_hdr) scratch_free(lo_hdr);
if (hi_hdr) scratch_free(hi_hdr);
for (int64_t f = 0; f < e; f++)
if (left_eq_hdr[f]) scratch_free(left_eq_hdr[f]);
WJ_CLEANUP_TEMP();
for (int64_t a = 0; a < n_agg; a++) ray_release(agg_result_vecs[a]);
for (int i = 0; i < 4; i++) ray_release(eargs[i]);
scratch_free(agg_hdr); scratch_free(eq_hdr);
return ray_error("oom", NULL);
}
const void* sd = ray_data(left_eq[e]);
int8_t t = left_eq[e]->type;
uint8_t at = left_eq[e]->attrs;
for (int64_t lr = 0; lr < left_nrows; lr++)
left_eq_arr[e][lr] = read_col_i64(sd, lr, t, at);
}
for (int64_t a = 0; a < n_agg; a++) {
agg_result_vecs[a]->len = left_nrows;
null_stage[a] = (uint8_t*)scratch_alloc(&null_stage_hdr[a], (size_t)left_nrows);
if (!null_stage[a] && left_nrows > 0) {
if (lo_hdr) scratch_free(lo_hdr);
if (hi_hdr) scratch_free(hi_hdr);
for (int64_t f = 0; f < n_eq; f++) if (left_eq_hdr[f]) scratch_free(left_eq_hdr[f]);
for (int64_t b = 0; b < a; b++) if (null_stage_hdr[b]) scratch_free(null_stage_hdr[b]);
WJ_CLEANUP_TEMP();
for (int64_t b = 0; b < n_agg; b++) ray_release(agg_result_vecs[b]);
for (int i = 0; i < 4; i++) ray_release(eargs[i]);
scratch_free(agg_hdr); scratch_free(eq_hdr);
return ray_error("oom", NULL);
}
memset(null_stage[a], 0, (size_t)left_nrows);
}
ray_pool_t* pool = ray_pool_get();
int64_t nw = (pool && left_nrows >= 2048) ? ray_pool_total_workers(pool) : 1;
ray_t* wscr_hdr = NULL;
wj_acc_t* acc_base = (wj_acc_t*)scratch_alloc(&wscr_hdr,
(size_t)nw * ((size_t)n_agg * sizeof(wj_acc_t) + (size_t)n_eq * sizeof(int64_t)));
if (!acc_base) {
if (lo_hdr) scratch_free(lo_hdr);
if (hi_hdr) scratch_free(hi_hdr);
for (int64_t f = 0; f < n_eq; f++) if (left_eq_hdr[f]) scratch_free(left_eq_hdr[f]);
for (int64_t b = 0; b < n_agg; b++) if (null_stage_hdr[b]) scratch_free(null_stage_hdr[b]);
WJ_CLEANUP_TEMP();
for (int64_t b = 0; b < n_agg; b++) ray_release(agg_result_vecs[b]);
for (int i = 0; i < 4; i++) ray_release(eargs[i]);
scratch_free(agg_hdr); scratch_free(eq_hdr);
return ray_error("oom", NULL);
}
int64_t* target_base = (int64_t*)(acc_base + (size_t)nw * n_agg);
for (int64_t a = 0; a < n_agg; a++)
result_data[a] = ray_data(agg_result_vecs[a]);
wj_scan_ctx_t wctx;
memset(&wctx, 0, sizeof(wctx));
wctx.left_nrows = left_nrows;
wctx.right_nrows = right_nrows;
wctx.n_eq = n_eq;
wctx.n_agg = n_agg;
wctx.mode = mode;
wctx.lo_arr = lo_arr;
wctx.hi_arr = hi_arr;
wctx.right_sort = right_sort;
wctx.rt_time_i = rt_time_i;
wctx.left_eq_arr = (const int64_t**)left_eq_arr;
wctx.eq_data = eq_data;
wctx.eq_type = eq_type;
wctx.eq_attrs = eq_attrs;
wctx.agg_raw = agg_raw;
wctx.agg_ops = agg_ops;
wctx.agg_result_types = agg_result_types;
wctx.agg_is_float = agg_is_float;
wctx.sorted_i = (const int64_t**)sorted_i;
wctx.sorted_f = (const double**)sorted_f;
wctx.sorted_nn = (const uint8_t**)sorted_nn;
wctx.result_data = result_data;
wctx.result_null = null_stage;
wctx.acc_base = acc_base;
wctx.target_base = target_base;
if (pool && left_nrows >= 2048) {
ray_pool_dispatch(pool, wj_scan_fn, &wctx, left_nrows);
} else {
wj_scan_fn(&wctx, 0, 0, left_nrows);
}
for (int64_t a = 0; a < n_agg; a++) {
ray_t* rv = agg_result_vecs[a];
const uint8_t* stage = null_stage[a];
for (int64_t lr = 0; lr < left_nrows; lr++)
if (stage[lr]) ray_vec_set_null(rv, lr, true);
}
scratch_free(wscr_hdr);
if (lo_hdr) scratch_free(lo_hdr);
if (hi_hdr) scratch_free(hi_hdr);
for (int64_t e = 0; e < n_eq; e++)
if (left_eq_hdr[e]) scratch_free(left_eq_hdr[e]);
for (int64_t a = 0; a < n_agg; a++)
if (null_stage_hdr[a]) scratch_free(null_stage_hdr[a]);
WJ_CLEANUP_TEMP();
#undef WJ_CLEANUP_TEMP
int64_t ncols = ray_table_ncols(left_tbl);
ray_t* result = ray_table_new(ncols + n_agg);
for (int64_t c = 0; c < ncols; c++) {
int64_t cn = ray_table_col_name(left_tbl, c);
ray_t* cv = ray_table_get_col_idx(left_tbl, c);
ray_retain(cv);
result = ray_table_add_col(result, cn, cv);
ray_release(cv);
}
for (int64_t a = 0; a < n_agg; a++) {
result = ray_table_add_col(result, agg_names[a], agg_result_vecs[a]);
ray_release(agg_result_vecs[a]);
}
for (int i = 0; i < 4; i++) ray_release(eargs[i]);
scratch_free(agg_hdr);
scratch_free(eq_hdr);
return result;
}
ray_t* left_tbl = eargs[0];
ray_t* right_tbl = eargs[1];
ray_t* eq_keys = eargs[2];
ray_t* time_sym = eargs[3];
if (left_tbl->type != RAY_TABLE || right_tbl->type != RAY_TABLE)
return ray_error("type", "window-join: both operands must be tables, got %s and %s",
ray_type_name(left_tbl->type), ray_type_name(right_tbl->type));
if (time_sym->type != -RAY_SYM)
return ray_error("type", "window-join: time key must be a symbol, got %s", ray_type_name(time_sym->type));
int64_t n_eq = 0;
ray_t** eq_elems = NULL;
ray_t* _bxeq = NULL;
eq_keys = unbox_vec_arg(eq_keys, &_bxeq);
if (is_list(eq_keys)) {
n_eq = ray_len(eq_keys);
eq_elems = (ray_t**)ray_data(eq_keys);
}
ray_graph_t* g = ray_graph_new(left_tbl);
if (!g) { if (_bxeq) ray_release(_bxeq); return ray_error("oom", NULL); }
ray_op_t* left_node = ray_const_table(g, left_tbl);
ray_op_t* right_node = ray_const_table(g, right_tbl);
ray_t* tname = ray_sym_str(time_sym->i64);
if (!tname) { ray_graph_free(g); if (_bxeq) ray_release(_bxeq); return ray_error("domain", "window-join: unknown time key symbol"); }
ray_op_t* time_op = ray_scan(g, ray_str_ptr(tname));
if (!time_op) { ray_graph_free(g); if (_bxeq) ray_release(_bxeq); return ray_error("domain", "window-join: time key column not found"); }
ray_t* eqops_hdr = NULL;
ray_op_t** eq_ops = (ray_op_t**)scratch_alloc(&eqops_hdr,
(size_t)(n_eq ? n_eq : 1) * sizeof(ray_op_t*));
if (!eq_ops) { ray_graph_free(g); if (_bxeq) ray_release(_bxeq); return ray_error("oom", NULL); }
for (int64_t i = 0; i < n_eq; i++) {
if (eq_elems[i]->type != -RAY_SYM) {
int8_t et = eq_elems[i]->type;
scratch_free(eqops_hdr);
ray_graph_free(g);
if (_bxeq) ray_release(_bxeq);
return ray_error("type", "window-join: equality key must be a symbol, got %s", ray_type_name(et));
}
ray_t* nm = ray_sym_str(eq_elems[i]->i64);
if (!nm) { scratch_free(eqops_hdr); ray_graph_free(g); if (_bxeq) ray_release(_bxeq); return ray_error("domain", "window-join: unknown equality key symbol"); }
eq_ops[i] = ray_scan(g, ray_str_ptr(nm));
if (!eq_ops[i]) { scratch_free(eqops_hdr); ray_graph_free(g); if (_bxeq) ray_release(_bxeq); return ray_error("domain", "window-join: equality key column not found"); }
ray_t* lcol = ray_table_get_col(left_tbl, eq_elems[i]->i64);
ray_t* rcol = ray_table_get_col(right_tbl, eq_elems[i]->i64);
int8_t lct = join_i64_eq_key_type(lcol);
int8_t rct = join_i64_eq_key_type(rcol);
if (!join_i64_eq_key_supported(lct) ||
!join_i64_eq_key_supported(rct)) {
scratch_free(eqops_hdr);
ray_graph_free(g);
if (_bxeq) ray_release(_bxeq);
return ray_error("nyi", "window-join: equality key column types %s and %s are not supported",
ray_type_name(lct), ray_type_name(rct));
}
}
if (_bxeq) ray_release(_bxeq);
ray_op_t* jn = ray_asof_join(g, left_node, right_node,
time_op, eq_ops, (uint32_t)n_eq, 1);
scratch_free(eqops_hdr);
if (!jn) { ray_graph_free(g); return ray_error("oom", NULL); }
jn = ray_optimize(g, jn);
ray_t* result = ray_execute(g, jn);
ray_graph_free(g);
return result;
}
ray_t* ray_window_join_fn(ray_t** args, int64_t n) {
return window_join_impl(args, n, 0);
}
ray_t* ray_window_join1_fn(ray_t** args, int64_t n) {
return window_join_impl(args, n, 1);
}
static _Atomic(int64_t) ray_asof_perpart_runs_ctr = 0;
int64_t ray_asof_perpart_runs(void) {
return atomic_load_explicit(&ray_asof_perpart_runs_ctr,
memory_order_relaxed);
}
static _Atomic(int64_t) ray_sort_perpart_runs_ctr = 0;
int64_t ray_sort_perpart_runs(void) {
return atomic_load_explicit(&ray_sort_perpart_runs_ctr,
memory_order_relaxed);
}
static ray_t* ray_asof_join_core(ray_t* keys_vec, ray_t* left_tbl, ray_t* right_tbl) {
if (left_tbl->type != RAY_TABLE || right_tbl->type != RAY_TABLE)
return ray_error("type", "asof-join: both operands must be tables, got %s and %s",
ray_type_name(left_tbl->type), ray_type_name(right_tbl->type));
ray_t* _bxk = NULL;
keys_vec = unbox_vec_arg(keys_vec, &_bxk);
if (!is_list(keys_vec) || ray_len(keys_vec) < 1) {
if (_bxk) ray_release(_bxk);
return ray_error("domain", "asof-join: keys must be a non-empty symbol list");
}
ray_t** kelems = (ray_t**)ray_data(keys_vec);
int64_t nkeys = ray_len(keys_vec);
ray_t* time_sym = kelems[nkeys - 1];
if (time_sym->type != -RAY_SYM) {
int8_t ts_t = time_sym->type;
if (_bxk) ray_release(_bxk);
return ray_error("type", "asof-join: time key must be a symbol, got %s", ray_type_name(ts_t));
}
int64_t n_eq = nkeys - 1;
ray_t** eq_syms = kelems;
ray_graph_t* g = ray_graph_new(left_tbl);
if (!g) { if (_bxk) ray_release(_bxk); return ray_error("oom", NULL); }
ray_op_t* left_node = ray_const_table(g, left_tbl);
ray_op_t* right_node = ray_const_table(g, right_tbl);
ray_t* tname = ray_sym_str(time_sym->i64);
if (!tname) { ray_graph_free(g); if (_bxk) ray_release(_bxk); return ray_error("domain", "asof-join: unknown time key symbol"); }
ray_op_t* time_op = ray_scan(g, ray_str_ptr(tname));
if (!time_op) { ray_graph_free(g); if (_bxk) ray_release(_bxk); return ray_error("domain", "asof-join: time key column not found"); }
ray_t* eqops_hdr = NULL;
ray_op_t** eq_ops = (ray_op_t**)scratch_alloc(&eqops_hdr,
(size_t)(n_eq ? n_eq : 1) * sizeof(ray_op_t*));
if (!eq_ops) { ray_graph_free(g); if (_bxk) ray_release(_bxk); return ray_error("oom", NULL); }
for (int64_t i = 0; i < n_eq; i++) {
if (eq_syms[i]->type != -RAY_SYM) {
int8_t es_t = eq_syms[i]->type;
scratch_free(eqops_hdr);
ray_graph_free(g); if (_bxk) ray_release(_bxk);
return ray_error("type", "asof-join: equality key must be a symbol, got %s", ray_type_name(es_t));
}
ray_t* nm = ray_sym_str(eq_syms[i]->i64);
if (!nm) { scratch_free(eqops_hdr); ray_graph_free(g); if (_bxk) ray_release(_bxk); return ray_error("domain", "asof-join: unknown equality key symbol"); }
eq_ops[i] = ray_scan(g, ray_str_ptr(nm));
if (!eq_ops[i]) { scratch_free(eqops_hdr); ray_graph_free(g); if (_bxk) ray_release(_bxk); return ray_error("domain", "asof-join: equality key column not found"); }
ray_t* lcol = ray_table_get_col(left_tbl, eq_syms[i]->i64);
ray_t* rcol = ray_table_get_col(right_tbl, eq_syms[i]->i64);
int8_t lct = join_i64_eq_key_type(lcol);
int8_t rct = join_i64_eq_key_type(rcol);
if (!join_i64_eq_key_supported(lct) ||
!join_i64_eq_key_supported(rct)) {
scratch_free(eqops_hdr);
ray_graph_free(g); if (_bxk) ray_release(_bxk);
return ray_error("nyi", "asof-join: equality key column types %s and %s are not supported",
ray_type_name(lct), ray_type_name(rct));
}
}
if (_bxk) ray_release(_bxk);
ray_op_t* jn = ray_asof_join(g, left_node, right_node,
time_op, eq_ops, (uint32_t)n_eq, 1);
scratch_free(eqops_hdr);
if (!jn) { ray_graph_free(g); return ray_error("oom", NULL); }
jn = ray_optimize(g, jn);
ray_t* result = ray_execute(g, jn);
ray_graph_free(g);
return result;
}
static int64_t asof_parted_seg_count(ray_t* tbl) {
if (!tbl || tbl->type != RAY_TABLE) return -1;
int64_t ncols = ray_table_ncols(tbl);
for (int64_t c = 0; c < ncols; c++) {
ray_t* col = ray_table_get_col_idx(tbl, c);
if (!col) continue;
if (RAY_IS_PARTED(col->type)) return col->len;
if (col->type == RAY_MAPCOMMON) {
ray_t** mc = (ray_t**)ray_data(col);
return (col->len >= 1 && mc[0]) ? mc[0]->len : -1;
}
}
return -1;
}
static ray_t* asof_parted_select_operand(ray_t* arg_ast, ray_t** out_dict) {
*out_dict = NULL;
if (!arg_ast || arg_ast->type != RAY_LIST || ray_len(arg_ast) < 2) return NULL;
ray_t** e = (ray_t**)ray_data(arg_ast);
ray_t* head = e[0];
if (!head || head->type != -RAY_SYM || (head->attrs & ATTR_QUOTED)) return NULL;
if (head->i64 != ray_sym_intern("select", 6)) return NULL;
ray_t* dict = e[1];
if (!dict || dict->type != RAY_DICT) return NULL;
ray_t* from_expr = dict_get(dict, "from");
if (!from_expr) return NULL;
ray_t* tbl = ray_eval(from_expr);
if (!tbl || RAY_IS_ERR(tbl)) { if (tbl) ray_release(tbl); return NULL; }
if (tbl->type != RAY_TABLE || asof_parted_seg_count(tbl) < 0) {
ray_release(tbl);
return NULL;
}
ray_t* keys = ray_dict_keys(dict);
if (!keys || keys->type != RAY_SYM) { ray_release(tbl); return NULL; }
for (int64_t i = 0; i < keys->len; i++) {
ray_t* s = ray_sym_vec_cell(keys, i);
if (!s) { ray_release(tbl); return NULL; }
int64_t sl = ray_str_len(s);
const char* sp = ray_str_ptr(s);
bool blocked = (sl == 2 && memcmp(sp, "by", 2) == 0) ||
(sl == 4 && memcmp(sp, "take", 4) == 0) ||
(sl == 3 && memcmp(sp, "asc", 3) == 0) ||
(sl == 4 && memcmp(sp, "desc", 4) == 0) ||
(sl == 7 && memcmp(sp, "nearest", 7) == 0);
if (blocked) { ray_release(tbl); return NULL; }
}
*out_dict = dict;
return tbl;
}
static ray_t* asof_eval_select_segment(ray_t* dict, ray_t* parted_tbl, int32_t seg) {
ray_t* seg_tbl = build_segment_table(parted_tbl, seg);
if (!seg_tbl || RAY_IS_ERR(seg_tbl)) return seg_tbl;
ray_t* keys = ray_dict_keys(dict);
ray_t* vals = ray_dict_vals(dict);
if (!keys || keys->type != RAY_SYM || !vals || vals->type != RAY_LIST) {
ray_release(seg_tbl);
return ray_error("type", "asof stream: malformed select clause dict");
}
int64_t from_idx = -1, nk = keys->len;
for (int64_t i = 0; i < nk; i++) {
ray_t* s = ray_sym_vec_cell(keys, i);
if (s && ray_str_len(s) == 4 && memcmp(ray_str_ptr(s), "from", 4) == 0) {
from_idx = i; break;
}
}
if (from_idx < 0) { ray_release(seg_tbl); return ray_error("domain", "asof stream: select missing from:"); }
ray_t* nvals = ray_list_new(vals->len);
if (!nvals || RAY_IS_ERR(nvals)) { ray_release(seg_tbl); return nvals ? nvals : ray_error("oom", NULL); }
nvals->len = vals->len;
ray_t** src = (ray_t**)ray_data(vals);
for (int64_t i = 0; i < vals->len; i++)
nvals = ray_list_set(nvals, i, (i == from_idx) ? seg_tbl : src[i]);
ray_release(seg_tbl);
ray_retain(keys);
ray_t* ndict = ray_dict_new(keys, nvals);
if (!ndict || RAY_IS_ERR(ndict)) return ndict ? ndict : ray_error("oom", NULL);
ray_t* r = ray_select_impl(&ndict, 1, true);
ray_release(ndict);
return r;
}
static int sort_stream_cmp_boundary(ray_t** ca, int64_t ra,
ray_t** cb, int64_t rb, int64_t n_keys) {
for (int64_t k = 0; k < n_keys; k++) {
ray_t* a = ca[k];
ray_t* b = cb[k];
if (a->type == RAY_SYM) {
int64_t ia = ray_read_sym(ray_data(a), ra, RAY_SYM, a->attrs);
int64_t ib = ray_read_sym(ray_data(b), rb, RAY_SYM, b->attrs);
ray_t* sa = ray_sym_domain_str(ray_sym_vec_domain(a), ia);
ray_t* sb = ray_sym_domain_str(ray_sym_vec_domain(b), ib);
uint32_t la = sa ? (uint32_t)ray_str_len(sa) : 0;
uint32_t lb = sb ? (uint32_t)ray_str_len(sb) : 0;
uint32_t ml = la < lb ? la : lb;
int cmp = ml ? memcmp(ray_str_ptr(sa), ray_str_ptr(sb), ml) : 0;
if (cmp == 0) cmp = (la > lb) - (la < lb);
if (cmp) return cmp;
} else {
int64_t va = read_col_i64(ray_data(a), ra, a->type, a->attrs);
int64_t vb = read_col_i64(ray_data(b), rb, b->type, b->attrs);
if (va != vb) return va < vb ? -1 : 1;
}
}
return 0;
}
static ray_t* try_stream_parted_order_by(
ray_t* dict, ray_t* tbl, ray_t** dict_elems, int64_t dict_n,
int64_t from_id, int64_t where_id, int64_t by_id, int64_t take_id,
int64_t asc_id, int64_t desc_id, int64_t nearest_id) {
int64_t nseg = asof_parted_seg_count(tbl);
if (nseg < 1) return NULL;
bool has_asc = false, has_desc = false;
int64_t n_keys = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == by_id || kid == take_id || kid == nearest_id) return NULL;
if (kid != asc_id && kid != desc_id) continue;
if (kid == asc_id) has_asc = true;
if (kid == desc_id) has_desc = true;
ray_t* v = dict_elems[i + 1];
if (!v) return NULL;
if (v->type == -RAY_SYM && !(v->attrs & ATTR_QUOTED)) n_keys += 1;
else if (v->type == RAY_SYM) n_keys += ray_len(v);
else return NULL;
}
if (n_keys < 1) return NULL;
if (has_asc && has_desc) return NULL;
uint8_t descending = has_desc ? 1 : 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id ||
kid == nearest_id) continue;
ray_t* v = dict_elems[i + 1];
if (!v || v->type != -RAY_SYM || (v->attrs & ATTR_QUOTED)) return NULL;
if (!ray_table_get_col(tbl, v->i64)) return NULL;
}
ray_t* hdr = NULL;
int64_t* key_syms = (int64_t*)scratch_alloc(&hdr,
(size_t)n_keys * (sizeof(int64_t) + 3 * sizeof(ray_t*)));
if (!key_syms) return NULL;
ray_t** kcols = (ray_t**)(key_syms + n_keys);
ray_t** segkeys = (ray_t**)(kcols + n_keys);
ray_t** prevkeys = (ray_t**)(segkeys + n_keys);
int64_t ki = 0;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid != asc_id && kid != desc_id) continue;
ray_t* v = dict_elems[i + 1];
if (v->type == -RAY_SYM) key_syms[ki++] = v->i64;
else for (int64_t c = 0; c < ray_len(v); c++)
key_syms[ki++] = sym_cell_runtime_id(v, c);
}
for (int64_t k = 0; k < n_keys; k++) {
ray_t* col = ray_table_get_col(tbl, key_syms[k]);
if (!col || !RAY_IS_PARTED(col->type) || col->len != nseg) {
scratch_free(hdr); return NULL;
}
int8_t bt = (int8_t)RAY_PARTED_BASETYPE(col->type);
if (bt != RAY_BOOL && bt != RAY_U8 && bt != RAY_I16 && bt != RAY_I32 &&
bt != RAY_I64 && bt != RAY_DATE && bt != RAY_TIME &&
bt != RAY_TIMESTAMP && bt != RAY_SYM) {
scratch_free(hdr); return NULL;
}
kcols[k] = col;
}
int64_t prev_last = -1;
bool have_prev = false;
for (int64_t s = 0; s < nseg; s++) {
int64_t seg_rows = -1;
for (int64_t k = 0; k < n_keys; k++) {
ray_t** segs = (ray_t**)ray_data(kcols[k]);
ray_t* sv = segs[s];
if (!sv) { scratch_free(hdr); return NULL; }
segkeys[k] = sv;
if (seg_rows < 0) seg_rows = sv->len;
else if (sv->len != seg_rows) { scratch_free(hdr); return NULL; }
}
if (seg_rows == 0) continue;
if (!ray_key_cols_sorted(segkeys, n_keys, descending, seg_rows)) {
scratch_free(hdr); return NULL;
}
if (have_prev) {
int cmp = sort_stream_cmp_boundary(prevkeys, prev_last,
segkeys, 0, n_keys);
if (descending ? (cmp < 0) : (cmp > 0)) {
scratch_free(hdr); return NULL;
}
}
for (int64_t k = 0; k < n_keys; k++) prevkeys[k] = segkeys[k];
prev_last = seg_rows - 1;
have_prev = true;
}
ray_t* okeys = ray_dict_keys(dict);
ray_t* ovals = ray_dict_vals(dict);
if (!okeys || okeys->type != RAY_SYM || !ovals || ovals->type != RAY_LIST) {
scratch_free(hdr); return NULL;
}
int64_t onk = okeys->len;
int64_t keep = 0;
for (int64_t i = 0; i < onk; i++) {
int64_t kid = sym_cell_runtime_id(okeys, i);
if (kid != asc_id && kid != desc_id) keep++;
}
ray_t* nkeys = ray_vec_new(RAY_SYM, keep ? keep : 1);
ray_t* nvals = ray_list_new(keep ? keep : 1);
if (!nkeys || RAY_IS_ERR(nkeys) || !nvals || RAY_IS_ERR(nvals)) {
if (nkeys && !RAY_IS_ERR(nkeys)) ray_release(nkeys);
if (nvals && !RAY_IS_ERR(nvals)) ray_release(nvals);
scratch_free(hdr); return NULL;
}
nkeys->len = keep; nvals->len = keep;
int64_t* nkd = (int64_t*)ray_data(nkeys);
ray_t** ovd = (ray_t**)ray_data(ovals);
int64_t w = 0;
for (int64_t i = 0; i < onk; i++) {
int64_t kid = sym_cell_runtime_id(okeys, i);
if (kid == asc_id || kid == desc_id) continue;
nkd[w] = kid;
nvals = ray_list_set(nvals, w, ovd[i]);
w++;
}
ray_t* sdict = ray_dict_new(nkeys, nvals);
if (!sdict || RAY_IS_ERR(sdict)) { scratch_free(hdr); return sdict ? sdict : ray_error("oom", NULL); }
ray_t* accum = NULL;
for (int64_t s = 0; s < nseg; s++) {
ray_t* seg_res = asof_eval_select_segment(sdict, tbl, (int32_t)s);
atomic_fetch_add_explicit(&ray_sort_perpart_runs_ctr, 1,
memory_order_relaxed);
if (!seg_res || RAY_IS_ERR(seg_res)) {
if (accum) ray_release(accum);
ray_release(sdict); scratch_free(hdr);
return seg_res ? seg_res : ray_error("oom", NULL);
}
ray_t* merged = ray_result_merge(accum, seg_res);
ray_release(seg_res);
if (accum) ray_release(accum);
accum = merged;
if (!accum || RAY_IS_ERR(accum)) {
ray_release(sdict); scratch_free(hdr);
return accum ? accum : ray_error("oom", NULL);
}
}
ray_release(sdict);
scratch_free(hdr);
return accum;
}
static ray_t* try_stream_parted_order_by_topk(
ray_t* dict, ray_t* tbl, ray_t** dict_elems, int64_t dict_n,
int64_t from_id, int64_t where_id, int64_t by_id, int64_t take_id,
int64_t asc_id, int64_t desc_id, int64_t nearest_id) {
int64_t nseg = asof_parted_seg_count(tbl);
if (nseg < 1) return NULL;
bool has_sort = false, has_take = false;
ray_t* take_v = NULL;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == by_id || kid == nearest_id) return NULL;
if (kid == asc_id || kid == desc_id) has_sort = true;
if (kid == take_id) { has_take = true; take_v = dict_elems[i + 1]; }
}
if (!has_sort || !has_take || !take_v) return NULL;
ray_t* kv = ray_eval(take_v);
if (!kv || RAY_IS_ERR(kv) || !ray_is_atom(kv) ||
(kv->type != -RAY_I64 && kv->type != -RAY_I32)) {
if (kv) ray_release(kv);
return NULL;
}
int64_t k = (kv->type == -RAY_I64) ? kv->i64 : kv->i32;
ray_release(kv);
if (k < 0) return NULL;
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid == from_id || kid == where_id || kid == by_id ||
kid == take_id || kid == asc_id || kid == desc_id ||
kid == nearest_id) continue;
ray_t* v = dict_elems[i + 1];
if (!v || v->type != -RAY_SYM || (v->attrs & ATTR_QUOTED)) return NULL;
if (!ray_table_get_col(tbl, v->i64)) return NULL;
}
for (int64_t i = 0; i + 1 < dict_n; i += 2) {
int64_t kid = dict_elems[i]->i64;
if (kid != asc_id && kid != desc_id) continue;
ray_t* v = dict_elems[i + 1];
if (!v) return NULL;
int64_t nk = (v->type == -RAY_SYM && !(v->attrs & ATTR_QUOTED)) ? 1
: (v->type == RAY_SYM ? ray_len(v) : -1);
if (nk < 0) return NULL;
for (int64_t c = 0; c < nk; c++) {
int64_t sk = (v->type == -RAY_SYM) ? v->i64
: sym_cell_runtime_id(v, c);
bool found = false;
for (int64_t j = 0; j + 1 < dict_n && !found; j += 2) {
int64_t okid = dict_elems[j]->i64;
if (okid == from_id || okid == where_id || okid == by_id ||
okid == take_id || okid == asc_id || okid == desc_id ||
okid == nearest_id) continue;
if (okid == sk) found = true;
}
if (!found) return NULL;
}
}
ray_t* accum = NULL;
for (int64_t s = 0; s < nseg; s++) {
ray_t* seg_res = asof_eval_select_segment(dict, tbl, (int32_t)s);
atomic_fetch_add_explicit(&ray_sort_perpart_runs_ctr, 1,
memory_order_relaxed);
if (!seg_res || RAY_IS_ERR(seg_res)) {
if (accum) ray_release(accum);
return seg_res ? seg_res : ray_error("oom", NULL);
}
ray_t* merged = ray_result_merge(accum, seg_res);
ray_release(seg_res);
if (accum) ray_release(accum);
accum = merged;
if (!accum || RAY_IS_ERR(accum))
return accum ? accum : ray_error("oom", NULL);
}
return apply_sort_take(accum, dict_elems, dict_n, asc_id, desc_id, take_id, NULL);
}
static ray_t* asof_parted_date_keys(ray_t* tbl, int64_t* out_n) {
*out_n = 0;
if (!tbl || tbl->type != RAY_TABLE) return NULL;
int64_t ncols = ray_table_ncols(tbl);
for (int64_t c = 0; c < ncols; c++) {
ray_t* col = ray_table_get_col_idx(tbl, c);
if (col && col->type == RAY_MAPCOMMON) {
ray_t** mc = (ray_t**)ray_data(col);
if (col->len >= 1 && mc[0] && mc[0]->type == RAY_DATE) {
*out_n = mc[0]->len;
return mc[0];
}
return NULL;
}
}
return NULL;
}
static ray_t* asof_empty_right(ray_t* dict, ray_t* parted_tbl) {
ray_t* seg0 = asof_eval_select_segment(dict, parted_tbl, 0);
if (!seg0 || RAY_IS_ERR(seg0)) return seg0;
int64_t nc = ray_table_ncols(seg0);
ray_t* out = ray_table_new(nc);
if (!out || RAY_IS_ERR(out)) { ray_release(seg0); return out ? out : ray_error("oom", NULL); }
for (int64_t c = 0; c < nc; c++) {
ray_t* col = ray_table_get_col_idx(seg0, c);
int64_t nm = ray_table_col_name(seg0, c);
int8_t ct = col ? col->type : RAY_I64;
ray_t* ev = ray_vec_new(ct, 0);
if (!ev || RAY_IS_ERR(ev)) { ray_release(seg0); ray_release(out); return ev ? ev : ray_error("oom", NULL); }
ev->len = 0;
if (ct == RAY_SYM && col) ray_sym_vec_adopt_domain(ev, col);
out = ray_table_add_col(out, nm, ev);
ray_release(ev);
if (!out || RAY_IS_ERR(out)) { ray_release(seg0); return out ? out : ray_error("oom", NULL); }
}
ray_release(seg0);
return out;
}
static bool asof_cols_aligned(ray_t* a, ray_t* b) {
if (!a || !b || a->type != RAY_TABLE || b->type != RAY_TABLE) return false;
int64_t na = ray_table_ncols(a), nb = ray_table_ncols(b);
if (na != nb) return false;
for (int64_t c = 0; c < na; c++)
if (ray_table_col_name(a, c) != ray_table_col_name(b, c)) return false;
return true;
}
static ray_t* asof_carry_recompute(ray_t* qtab, ray_t* template,
const int64_t* eq_names, int64_t n_eq) {
int64_t ncols = ray_table_ncols(template);
bool has_by = (n_eq > 0);
ray_t* by_vec = NULL;
if (has_by) {
by_vec = ray_vec_new(RAY_SYM, n_eq);
if (!by_vec || RAY_IS_ERR(by_vec)) return by_vec ? by_vec : ray_error("oom", NULL);
by_vec->len = n_eq;
for (int64_t i = 0; i < n_eq; i++) ((int64_t*)ray_data(by_vec))[i] = eq_names[i];
}
int64_t n_proj = ncols - n_eq;
if (n_proj < 0) n_proj = 0;
int64_t nslot = n_proj + (has_by ? 2 : 1);
ray_t* keys = ray_vec_new(RAY_SYM, nslot);
if (!keys || RAY_IS_ERR(keys)) { if (by_vec) ray_release(by_vec); return keys ? keys : ray_error("oom", NULL); }
keys->len = nslot;
ray_t* vals = ray_list_new(nslot);
if (!vals || RAY_IS_ERR(vals)) { if (by_vec) ray_release(by_vec); ray_release(keys); return vals ? vals : ray_error("oom", NULL); }
vals->len = nslot;
int64_t* kids = (int64_t*)ray_data(keys);
int64_t last_sym = ray_sym_intern("last", 4);
int64_t s = 0;
ray_t* build_err = NULL;
for (int64_t c = 0; c < ncols && !build_err; c++) {
int64_t nm = ray_table_col_name(template, c);
bool is_eq = false;
for (int64_t k = 0; k < n_eq; k++) if (eq_names[k] == nm) { is_eq = true; break; }
if (is_eq) continue;
if (s >= n_proj) { build_err = ray_error("schema", "asof carry: projection overflow"); break; }
ray_t* head = ray_sym(last_sym);
ray_t* cref = ray_sym(nm);
ray_t* agg = ray_list_new(2);
if (!head || RAY_IS_ERR(head) || !cref || RAY_IS_ERR(cref) || !agg || RAY_IS_ERR(agg)) {
if (head && !RAY_IS_ERR(head)) ray_release(head);
if (cref && !RAY_IS_ERR(cref)) ray_release(cref);
build_err = (agg && RAY_IS_ERR(agg)) ? agg : ray_error("oom", NULL);
if (agg && !RAY_IS_ERR(agg)) ray_release(agg);
break;
}
agg->len = 2;
agg = ray_list_set(agg, 0, head); ray_release(head);
agg = ray_list_set(agg, 1, cref); ray_release(cref);
kids[s] = nm;
vals = ray_list_set(vals, s, agg); ray_release(agg);
s++;
}
if (!build_err) {
if (has_by) {
kids[s] = ray_sym_intern("by", 2);
vals = ray_list_set(vals, s, by_vec);
s++;
}
kids[s] = ray_sym_intern("from", 4);
vals = ray_list_set(vals, s, qtab);
}
if (by_vec) ray_release(by_vec);
if (build_err) { ray_release(keys); ray_release(vals); return build_err; }
ray_t* dict = ray_dict_new(keys, vals);
if (!dict || RAY_IS_ERR(dict)) return dict ? dict : ray_error("oom", NULL);
ray_t* raw = ray_select_impl(&dict, 1, true);
ray_release(dict);
if (!raw || RAY_IS_ERR(raw)) return raw ? raw : ray_error("type", NULL);
ray_t* out = ray_table_new(ncols);
if (!out || RAY_IS_ERR(out)) { ray_release(raw); return out ? out : ray_error("oom", NULL); }
for (int64_t c = 0; c < ncols; c++) {
int64_t nm = ray_table_col_name(template, c);
ray_t* col = ray_table_get_col(raw, nm);
if (!col) { ray_release(raw); ray_release(out); return ray_error("schema", "asof carry: column %lld missing from group-last", (long long)nm); }
out = ray_table_add_col(out, nm, col);
if (!out || RAY_IS_ERR(out)) { ray_release(raw); return out ? out : ray_error("oom", NULL); }
}
ray_release(raw);
return out;
}
ray_t* ray_asof_join_fn(ray_t** args, int64_t n) {
if (n < 3) return ray_error("arity", "asof-join: expects keys, left table and right table, got %lld args", (long long)n);
ray_t* keys_vec = ray_eval(args[0]);
if (!keys_vec || RAY_IS_ERR(keys_vec))
return keys_vec ? keys_vec : ray_error("type", "asof-join: key evaluation failed");
ray_t* ldict = NULL, *rdict = NULL;
ray_t* lparted = asof_parted_select_operand(args[1], &ldict);
ray_t* rparted = asof_parted_select_operand(args[2], &rdict);
int64_t l_ndates = 0, r_ndates = 0;
ray_t* l_dates = lparted ? asof_parted_date_keys(lparted, &l_ndates) : NULL;
ray_t* r_dates = rparted ? asof_parted_date_keys(rparted, &r_ndates) : NULL;
if (lparted && rparted && l_dates && r_dates && l_ndates > 0 && r_ndates > 0) {
const int32_t* ld = (const int32_t*)ray_data(l_dates);
const int32_t* rd = (const int32_t*)ray_data(r_dates);
int64_t i = 0, j = 0;
ray_t* accum = NULL;
ray_t* err = NULL;
ray_t* empty_r = NULL;
ray_t* carry = NULL;
ray_t* eqn_kbx = NULL;
ray_t* eqn_kv = unbox_vec_arg(keys_vec, &eqn_kbx);
int64_t n_eq_c = (is_list(eqn_kv) && ray_len(eqn_kv) >= 1) ? ray_len(eqn_kv) - 1 : 0;
ray_t* eqn_hdr = NULL;
int64_t* eq_names = (int64_t*)scratch_alloc(&eqn_hdr,
(size_t)(n_eq_c ? n_eq_c : 1) * sizeof(int64_t));
if (!eq_names) {
if (eqn_kbx) ray_release(eqn_kbx);
ray_release(lparted); ray_release(rparted); ray_release(keys_vec);
return ray_error("oom", NULL);
}
int64_t time_name = -1;
{
ray_t** ke = (ray_t**)ray_data(eqn_kv);
for (int64_t k = 0; k < n_eq_c; k++) {
if (ke[k]->type != -RAY_SYM) {
int8_t kt = ke[k]->type;
if (eqn_kbx) ray_release(eqn_kbx);
scratch_free(eqn_hdr);
ray_release(lparted); ray_release(rparted); ray_release(keys_vec);
return ray_error("type", "asof-join: equality key must be a symbol, got %s", ray_type_name(kt));
}
eq_names[k] = ke[k]->i64;
}
if (is_list(eqn_kv) && ray_len(eqn_kv) >= 1) {
ray_t* tk = ke[ray_len(eqn_kv) - 1];
if (tk->type != -RAY_SYM) {
int8_t tt = tk->type;
if (eqn_kbx) ray_release(eqn_kbx);
scratch_free(eqn_hdr);
ray_release(lparted); ray_release(rparted); ray_release(keys_vec);
return ray_error("type", "asof-join: time key must be a symbol, got %s", ray_type_name(tt));
}
time_name = tk->i64;
}
}
if (eqn_kbx) ray_release(eqn_kbx);
while (i < l_ndates || j < r_ndates) {
int32_t d;
int64_t lseg = -1, rseg = -1;
if (i < l_ndates && (j >= r_ndates || ld[i] <= rd[j])) {
d = ld[i];
lseg = i;
if (j < r_ndates && rd[j] == d) rseg = j;
} else {
d = rd[j];
rseg = j;
}
if (i < l_ndates && ld[i] == d) i++;
if (j < r_ndates && rd[j] == d) j++;
atomic_fetch_add_explicit(&ray_asof_perpart_runs_ctr, 1, memory_order_relaxed);
ray_t* day_r_seg = NULL;
if (rseg >= 0) {
day_r_seg = asof_eval_select_segment(rdict, rparted, (int32_t)rseg);
if (!day_r_seg || RAY_IS_ERR(day_r_seg)) { err = day_r_seg ? day_r_seg : ray_error("type", NULL); break; }
}
ray_t* qtab = NULL;
if (day_r_seg && carry) {
if (!asof_cols_aligned(carry, day_r_seg)) {
err = ray_error("schema", "asof stream: carry/quote column order mismatch");
ray_release(day_r_seg); break;
}
qtab = ray_result_merge(carry, day_r_seg);
if (!qtab || RAY_IS_ERR(qtab)) { err = qtab ? qtab : ray_error("oom", NULL); qtab = NULL; ray_release(day_r_seg); break; }
}
else if (day_r_seg) { qtab = day_r_seg; ray_retain(qtab); }
else if (carry) { qtab = carry; ray_retain(qtab); }
if (qtab && time_name >= 0) {
ray_t* sk = ray_sym(time_name);
if (!sk || RAY_IS_ERR(sk)) { err = sk ? sk : ray_error("oom", NULL); ray_release(qtab); if (day_r_seg) ray_release(day_r_seg); break; }
ray_t* qs = sort_table_by_keys(qtab, sk, 0);
ray_release(sk);
ray_release(qtab);
qtab = qs;
if (!qtab || RAY_IS_ERR(qtab)) { err = qtab ? qtab : ray_error("type", NULL); qtab = NULL; if (day_r_seg) ray_release(day_r_seg); break; }
}
if (lseg >= 0) {
ray_t* day_l = asof_eval_select_segment(ldict, lparted, (int32_t)lseg);
if (!day_l || RAY_IS_ERR(day_l)) { err = day_l ? day_l : ray_error("type", NULL); if (qtab) ray_release(qtab); if (day_r_seg) ray_release(day_r_seg); break; }
ray_t* rt;
if (qtab) { rt = qtab; ray_retain(rt); }
else {
if (!empty_r) {
empty_r = asof_empty_right(rdict, rparted);
if (!empty_r || RAY_IS_ERR(empty_r)) { err = empty_r ? empty_r : ray_error("type", NULL); empty_r = NULL; ray_release(day_l); if (day_r_seg) ray_release(day_r_seg); break; }
}
rt = empty_r; ray_retain(rt);
}
ray_t* part = ray_asof_join_core(keys_vec, day_l, rt);
ray_release(day_l); ray_release(rt);
if (!part || RAY_IS_ERR(part)) { err = part ? part : ray_error("type", NULL); if (qtab) ray_release(qtab); if (day_r_seg) ray_release(day_r_seg); break; }
if (accum && !asof_cols_aligned(accum, part)) {
err = ray_error("schema", "asof stream: per-day output column order mismatch");
ray_release(part); if (qtab) ray_release(qtab); if (day_r_seg) ray_release(day_r_seg); break;
}
ray_t* merged = ray_result_merge(accum, part);
ray_release(part);
if (accum) ray_release(accum);
accum = merged;
if (!accum || RAY_IS_ERR(accum)) { err = accum; accum = NULL; if (qtab) ray_release(qtab); if (day_r_seg) ray_release(day_r_seg); break; }
}
if (rseg >= 0) {
ray_t* nc = asof_carry_recompute(qtab, day_r_seg, eq_names, n_eq_c);
if (!nc || RAY_IS_ERR(nc)) { err = nc ? nc : ray_error("type", NULL); if (qtab) ray_release(qtab); ray_release(day_r_seg); break; }
if (carry) ray_release(carry);
carry = nc;
}
if (qtab) ray_release(qtab);
if (day_r_seg) ray_release(day_r_seg);
}
scratch_free(eqn_hdr);
if (carry) ray_release(carry);
if (empty_r) ray_release(empty_r);
ray_release(lparted); ray_release(rparted); ray_release(keys_vec);
if (err) { if (accum) ray_release(accum); return err; }
return accum ? accum : ray_error("domain", "asof stream: no partitions produced");
}
if (lparted) ray_release(lparted);
if (rparted) ray_release(rparted);
ray_t* left = ray_eval(args[1]);
if (!left || RAY_IS_ERR(left)) { ray_release(keys_vec); return left ? left : ray_error("type", "asof-join: left evaluation failed"); }
ray_t* right = ray_eval(args[2]);
if (!right || RAY_IS_ERR(right)) { ray_release(left); ray_release(keys_vec); return right ? right : ray_error("type", "asof-join: right evaluation failed"); }
ray_t* r = ray_asof_join_core(keys_vec, left, right);
ray_release(left); ray_release(right); ray_release(keys_vec);
return r;
}
static ray_t* delete_compact_cols(ray_t** slots, int64_t ncols, int64_t nrows,
const int64_t* rows, int64_t k) {
for (int64_t c = 0; c < ncols; c++) {
if (slots[c]->attrs & RAY_ATTR_HAS_INDEX) {
ray_t* drop_r = ray_index_drop(&slots[c]);
if (RAY_IS_ERR(drop_r)) return drop_r;
}
ray_t* col = slots[c];
size_t esz = (col->type == RAY_LIST) ? sizeof(ray_t*)
: ray_sym_elem_size(col->type, col->attrs);
uint8_t* d = (uint8_t*)ray_data(col);
if (col->type == RAY_LIST) {
ray_t** cells = (ray_t**)d;
for (int64_t i = 0; i < k; i++)
if (cells[rows[i]]) ray_release(cells[rows[i]]);
}
int64_t dst = rows[0];
for (int64_t i = 0; i < k; i++) {
int64_t src = rows[i] + 1;
int64_t end = (i + 1 < k) ? rows[i + 1] : nrows;
int64_t cnt = end - src;
if (cnt > 0) {
memmove(d + (size_t)dst * esz, d + (size_t)src * esz, (size_t)cnt * esz);
dst += cnt;
}
}
col->len = nrows - k;
}
return NULL;
}
static void ukey_patch_after_delete(ray_t* tbl, const int64_t* rows, int64_t k,
int64_t nrows0) {
ray_index_t* ix = ray_table_ukey_get(tbl);
if (!ix) return;
int64_t left = nrows0 - k;
if (ix->u.ukey.nrows != nrows0 || k * 2 >= nrows0 ||
(uint64_t)(nrows0 + ix->u.ukey.n_tomb) * 2 > ix->u.ukey.mask + 1) {
ray_table_ukey_drop(tbl);
return;
}
ray_t* nr_hdr = NULL;
int64_t* newrow = (int64_t*)scratch_alloc(&nr_hdr, (size_t)nrows0 * sizeof(int64_t));
if (!newrow) { ray_table_ukey_drop(tbl); return; }
for (int64_t r = 0, next = 0, ri = 0; r < nrows0; r++) {
if (ri < k && rows[ri] == r) { newrow[r] = -1; ri++; }
else newrow[r] = next++;
}
int64_t* slot = (int64_t*)ray_data(ix->u.ukey.slots);
uint64_t cap = ix->u.ukey.mask + 1;
int64_t tomb = 0;
for (uint64_t s = 0; s < cap; s++) {
int64_t e = slot[s];
if (e == UKEY_SLOT_EMPTY || e == UKEY_SLOT_TOMB) continue;
int64_t nr = newrow[UKEY_ROW(e)];
if (nr < 0) { slot[s] = UKEY_SLOT_TOMB; tomb++; }
else slot[s] = UKEY_PACK(UKEY_H32(e), nr);
}
scratch_free(nr_hdr);
ix->u.ukey.n_tomb += tomb;
ix->u.ukey.nrows = left;
}
ray_t* ray_delete_fn(ray_t** args, int64_t n) {
return ray_delete(args, n);
}
ray_t* ray_delete(ray_t** args, int64_t n) {
if (n < 1) return ray_error("arity", "delete: expects a query dict, got %lld args", (long long)n);
ray_t* dict = args[0];
if (!dict || dict->type != RAY_DICT)
return ray_error("type", "delete: query must be a dict, got %s", dict ? ray_type_name(dict->type) : "null");
ray_t* from_expr = dict_get(dict, "from");
if (!from_expr) return ray_error("domain", "delete: missing `from:` clause");
ray_t* where_expr = dict_get(dict, "where");
if (!where_expr) return ray_error("domain", "delete: missing `where:` clause");
if (dict_get(dict, "by")) return ray_error("domain", "delete: `by:` is not supported");
int64_t inplace_sym = -1;
ray_t* tbl = ray_eval(from_expr);
if (RAY_IS_ERR(tbl)) return tbl;
if (tbl->type == -RAY_SYM) {
inplace_sym = tbl->i64;
ray_release(tbl);
tbl = ray_env_get(inplace_sym);
if (!tbl || RAY_IS_ERR(tbl)) return ray_error("domain", "delete: `from:` symbol is unbound");
ray_retain(tbl);
}
if (tbl->type != RAY_TABLE) {
int8_t t = tbl->type; ray_release(tbl);
return ray_error("type", "delete: `from:` must be a table, got %s", ray_type_name(t));
}
if (table_has_parted_columns(tbl)) {
ray_t* flat = ray_table_new(ray_table_ncols(tbl));
if (!flat || RAY_IS_ERR(flat)) { ray_release(tbl); return flat ? flat : ray_error("oom", NULL); }
int64_t nc = ray_table_ncols(tbl);
for (int64_t c = 0; c < nc; c++) {
ray_t* fc = query_materialize_parted_col(ray_table_get_col_idx(tbl, c));
if (!fc || RAY_IS_ERR(fc)) { ray_release(flat); ray_release(tbl); return fc ? fc : ray_error("oom", NULL); }
flat = ray_table_add_col(flat, ray_table_col_name(tbl, c), fc);
ray_release(fc);
if (!flat || RAY_IS_ERR(flat)) { ray_release(tbl); return flat ? flat : ray_error("oom", NULL); }
}
ray_release(tbl);
tbl = flat;
}
int64_t ncols = ray_table_ncols(tbl);
int64_t nrows0 = ray_table_nrows(tbl);
if (ncols <= 0) { ray_release(tbl); return ray_error("domain", "delete: table has no columns"); }
for (int64_t c = 0; c < ncols; c++)
if (ray_table_get_col_idx(tbl, c)->attrs & RAY_ATTR_SLICE) {
ray_release(tbl);
return ray_error("domain", "delete: column %lld is a slice view", (long long)c);
}
bool inplace = inplace_sym >= 0 && tbl->rc == 2 && tbl->mmod == 0 &&
!(tbl->attrs & (RAY_ATTR_SLICE | RAY_ATTR_ARENA));
ray_t* live_cols = NULL;
if (inplace) {
live_cols = ((ray_t**)ray_data(tbl))[1];
if (!live_cols || live_cols->rc != 1 || live_cols->len != ncols) inplace = false;
}
for (int64_t c = 0; inplace && c < ncols; c++) {
ray_t* col = ((ray_t**)ray_data(live_cols))[c];
if (col->rc != 1 || !upsert_col_ok(col)) inplace = false;
}
ray_t* rows_hdr = NULL; int64_t* rows = NULL; int64_t k = 0;
ray_t* err = NULL;
if (!update_where_index_rows(tbl, where_expr, &rows_hdr, &rows, &k)) {
ray_t* mask = update_where_mask_vec(tbl, where_expr);
if (RAY_IS_ERR(mask)) { ray_release(tbl); return mask; }
int64_t mlen = mask->len;
const uint8_t* m = (const uint8_t*)ray_data(mask);
int64_t cnt = 0;
for (int64_t r = 0; r < mlen; r++) cnt += (m[r] != 0);
rows = (int64_t*)scratch_alloc(&rows_hdr, (size_t)(cnt > 0 ? cnt : 1) * sizeof(int64_t));
if (!rows) err = ray_error("oom", NULL);
else for (int64_t r = 0; r < mlen; r++) if (m[r]) rows[k++] = r;
ray_release(mask);
if (err) { ray_release(tbl); return err; }
}
if (k == 0) {
if (rows_hdr) scratch_free(rows_hdr);
if (inplace_sym >= 0) { ray_release(tbl); return ray_sym(inplace_sym); }
return tbl;
}
if (inplace) {
ray_t** slots = (ray_t**)ray_data(live_cols);
ray_t* cerr = delete_compact_cols(slots, ncols, nrows0, rows, k);
if (cerr) { scratch_free(rows_hdr); ray_release(tbl); return cerr; }
ukey_patch_after_delete(tbl, rows, k, nrows0);
scratch_free(rows_hdr);
ray_release(tbl);
return ray_sym(inplace_sym);
}
ray_t* work = ray_table_new(ncols);
if (!work || RAY_IS_ERR(work)) {
scratch_free(rows_hdr); ray_release(tbl);
return work ? work : ray_error("oom", NULL);
}
for (int64_t c = 0; c < ncols && !err; c++) {
ray_t* orig = ray_table_get_col_idx(tbl, c);
ray_retain(orig);
ray_t* cp = ray_cow(orig);
if (!cp || RAY_IS_ERR(cp)) { ray_release(orig); err = cp ? cp : ray_error("oom", NULL); break; }
work = ray_table_add_col(work, ray_table_col_name(tbl, c), cp);
ray_release(cp);
if (!work || RAY_IS_ERR(work)) { err = work ? work : ray_error("oom", NULL); work = NULL; }
}
if (!err) {
ray_t** wslots = (ray_t**)ray_data(((ray_t**)ray_data(work))[1]);
err = delete_compact_cols(wslots, ncols, nrows0, rows, k);
}
scratch_free(rows_hdr);
ray_release(tbl);
if (err) { if (work) ray_release(work); return err; }
if (inplace_sym >= 0) {
ray_env_set(inplace_sym, work);
ray_release(work);
return ray_sym(inplace_sym);
}
return work;
}