Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/ops/query.c
Original file line number Diff line number Diff line change
Expand Up @@ -14876,6 +14876,21 @@ static ray_t* window_join_impl(ray_t** args, int64_t n, int mode) {
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");
}
/* STR equality-key columns are silently mismatched: window-join
* sorts and probes eq cells through read_col_i64, which has no
* RAY_STR case (it reads one raw byte per row), so once there are
* enough distinct keys the byte collisions cross-contaminate
* groups and the aggregates are WRONG. Decline both sides — the
* base equi-join has a STR-aware kernel, these window kernels do
* not. */
int8_t lct = left_eq[e]->type, rct = right_eq[e]->type;
if (RAY_IS_PARTED(lct)) lct = (int8_t)RAY_PARTED_BASETYPE(lct);
if (RAY_IS_PARTED(rct)) rct = (int8_t)RAY_PARTED_BASETYPE(rct);
if (lct == RAY_STR || rct == RAY_STR) {
scratch_free(eq_hdr);
for (int i = 0; i < 4; i++) ray_release(eargs[i]);
return ray_error("nyi", "window-join: string equality key columns are not supported");
}
}

/* Parse every (name, (op src)) pair from the agg dict. The dict's
Expand Down Expand Up @@ -15436,6 +15451,18 @@ static ray_t* window_join_impl(ray_t** args, int64_t n, int mode) {
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 = lcol ? lcol->type : (int8_t)0;
int8_t rct = rcol ? rcol->type : (int8_t)0;
if (RAY_IS_PARTED(lct)) lct = (int8_t)RAY_PARTED_BASETYPE(lct);
if (RAY_IS_PARTED(rct)) rct = (int8_t)RAY_PARTED_BASETYPE(rct);
if (lct == RAY_STR || rct == RAY_STR) {
scratch_free(eqops_hdr);
ray_graph_free(g);
if (_bxeq) ray_release(_bxeq);
return ray_error("nyi", "window-join: string equality key columns are not supported");
}
}

if (_bxeq) ray_release(_bxeq);
Expand Down Expand Up @@ -15546,6 +15573,23 @@ static ray_t* ray_asof_join_core(ray_t* keys_vec, ray_t* left_tbl, ray_t* right_
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"); }
/* STR equality-key columns are silently mismatched: the asof kernel
* reads eq cells through read_col_i64 (asof_eq_lread), which has no
* RAY_STR case, so string keys compare as raw bytes and yield WRONG
* results (a key that should match can null out). The base equi-join
* has a separate STR-aware kernel; asof does not, so decline both
* sides rather than return corrupt data. */
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 = lcol ? lcol->type : (int8_t)0;
int8_t rct = rcol ? rcol->type : (int8_t)0;
if (RAY_IS_PARTED(lct)) lct = (int8_t)RAY_PARTED_BASETYPE(lct);
if (RAY_IS_PARTED(rct)) rct = (int8_t)RAY_PARTED_BASETYPE(rct);
if (lct == RAY_STR || rct == RAY_STR) {
scratch_free(eqops_hdr);
ray_graph_free(g); if (_bxk) ray_release(_bxk);
return ray_error("nyi", "asof-join: string equality key columns are not supported");
}
}

if (_bxk) ray_release(_bxk);
Expand Down
33 changes: 33 additions & 0 deletions test/rfl/join/str_key_nyi.rfl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
;; str_key_nyi.rfl — STR equality-key columns are unsupported in the asof and
;; window join kernels and must be DECLINED, not silently mis-answered.
;;
;; Root cause: both kernels read equality-key cells through read_col_i64
;; (asof via asof_eq_lread; window when it sorts/probes the right side), and
;; read_col_i64 has no RAY_STR case — a STR column falls into the byte-wide
;; default. asof then mismatches outright (a key that should match nulls
;; out); window only reads one byte per row, so small key sets coincide by
;; luck but enough distinct keys collide and cross-contaminate groups, so the
;; aggregates are WRONG. The base equi-join (inner/left/anti) uses a separate
;; STR-aware kernel and stays correct, so the fix is scoped to asof + window:
;; reject a STR eq-key on either side with `nyi` instead of returning corrupt
;; data. window-join1 shares the window kernel; the 4-argument compatibility
;; window forms fall through to the asof executor, so they need the same guard.

;; ── asof-join: STR equality key → nyi (both operand orders) ──────────
(set tr (table [Sym Time Price] (list ["A" "A"] [12:00:01 12:00:04] [100 101])))(set qs (table [Sym Time Bid] (list ["A" "A"] [12:00:00 12:00:05] [99 100])))(asof-join [Sym Time] tr qs) !- nyi
(set tr (table [Sym Time Price] (list ["A" "A"] [12:00:01 12:00:04] [100 101])))(set qs (table [Sym Time Bid] (list ["A" "A"] [12:00:00 12:00:05] [99 100])))(asof-join [Sym Time] qs tr) !- nyi

;; ── window-join / window-join1: STR equality key → nyi ───────────────
(set wt (table [Sym Time Price] (list ["a" "a"] [10:00:01.000 10:00:05.000] [100 200])))(set wq (table [Sym Time Bid] (list ["a" "a" "a"] [10:00:00.000 10:00:02.000 10:00:04.000] [99 100 101])))(set wi (map-left + [-2000 2000] (at wt 'Time)))(window-join [Sym Time] wi wt wq {minBid: (min Bid)}) !- nyi
(set wt (table [Sym Time Price] (list ["a" "a"] [10:00:01.000 10:00:05.000] [100 200])))(set wq (table [Sym Time Bid] (list ["a" "a" "a"] [10:00:00.000 10:00:02.000 10:00:04.000] [99 100 101])))(set wi (map-left + [-2000 2000] (at wt 'Time)))(window-join1 [Sym Time] wi wt wq {minBid: (min Bid)}) !- nyi
(set wt (table [Sym Time Price] (list ["a" "a"] [10:00:01.000 10:00:05.000] [100 200])))(set wq (table [Sym Time Bid] (list ["a" "a" "a"] [10:00:00.000 10:00:02.000 10:00:04.000] [99 100 101])))(at (window-join wt wq [Sym] 'Time) 'Bid) !- nyi
(set wt (table [Sym Time Price] (list ["a" "a"] [10:00:01.000 10:00:05.000] [100 200])))(set wq (table [Sym Time Bid] (list ["a" "a" "a"] [10:00:00.000 10:00:02.000 10:00:04.000] [99 100 101])))(at (window-join1 wt wq [Sym] 'Time) 'Bid) !- nyi

;; ── SYM equality keys keep working and stay order-independent ────────
(set KS (as 'sym ["AAPL" "AAPL"]))(set trS (table [Sym Time Price] (list KS [12:00:01 12:00:04] [100 101])))(set qsS (table [Sym Time Bid] (list KS [12:00:00 12:00:05] [99 100])))(at (asof-join [Sym Time] trS qsS) 'Bid) -- [99 99]
(set KS (as 'sym ["AAPL" "AAPL"]))(set trS (table [Sym Time Price] (list KS [12:00:01 12:00:04] [100 101])))(set qsU (table [Sym Time Bid] (list KS [12:00:05 12:00:00] [100 99])))(at (asof-join [Sym Time] trS qsU) 'Bid) -- [99 99]
(set KW (as 'sym ["a" "a"]))(set wtS (table [Sym Time Price] (list KW [10:00:01.000 10:00:05.000] [100 200])))(set wqS (table [Sym Time Bid] (list (as 'sym ["a" "a" "a"]) [10:00:00.000 10:00:02.000 10:00:04.000] [99 100 101])))(set wiS (map-left + [-2000 2000] (at wtS 'Time)))(at (window-join [Sym Time] wiS wtS wqS {minBid: (min Bid)}) 'minBid) -- [99 100]

;; ── base equi-join must NOT over-reject: STR keys stay correct ───────
(set A (table [K V] (list ["x" "y"] [1 2])))(set B (table [K W] (list ["x" "z"] [10 20])))(at (inner-join [K] A B) 'V) -- [1]
(set A (table [K V] (list ["x" "y"] [1 2])))(set B (table [K W] (list ["x" "z"] [10 20])))(at (anti-join [K] A B) 'V) -- [2]
Loading