shape_jit/ffi/join.rs
1// Heap allocation audit (PR-9 V8 Gap Closure):
2// Category A (NaN-boxed returns): 0 sites
3// Category B (intermediate/consumed): 0 sites
4// Category C (heap islands): 0 sites
5// (JOIN helpers only operate on NaN-boxed numbers and tags, no heap allocations)
6//!
7//! JOIN FFI Functions for JIT
8//!
9//! External C functions for JOIN operations called from JIT-compiled code.
10//! JOIN operations are typically executed at the query level, not within
11//! tight expression loops, so these functions delegate to the runtime.
12
13use super::super::nan_boxing::*;
14
15// ============================================================================
16// JOIN Condition Helpers
17// ============================================================================
18
19/// Check if two values match for JOIN condition (equality)
20/// Args: left_value, right_value
21/// Returns: 1 (true) or 0 (false)
22#[unsafe(no_mangle)]
23pub extern "C" fn jit_join_values_equal(left: u64, right: u64) -> u64 {
24 // Handle NULL comparisons
25 if left == TAG_NULL || right == TAG_NULL {
26 return box_number(0.0); // NULL != anything
27 }
28
29 // Compare numbers
30 if is_number(left) && is_number(right) {
31 let l = unbox_number(left);
32 let r = unbox_number(right);
33
34 // NaN handling
35 if l.is_nan() && r.is_nan() {
36 return box_number(1.0); // NaN == NaN for JOIN purposes
37 }
38 if l.is_nan() || r.is_nan() {
39 return box_number(0.0);
40 }
41
42 // Float comparison with epsilon
43 if (l - r).abs() < f64::EPSILON {
44 return box_number(1.0);
45 }
46 return box_number(0.0);
47 }
48
49 // For pointers (strings, objects), compare bit patterns
50 // This is a simplified comparison - full string/object comparison
51 // would require dereferencing
52 if left == right {
53 box_number(1.0)
54 } else {
55 box_number(0.0)
56 }
57}
58
59/// Temporal JOIN match check
60/// Args: left_timestamp, right_timestamp, tolerance_ms
61/// Returns: 1 (true) if within tolerance, 0 (false) otherwise
62#[unsafe(no_mangle)]
63pub extern "C" fn jit_temporal_match(
64 left_timestamp: u64,
65 right_timestamp: u64,
66 tolerance_ms: u64,
67) -> u64 {
68 let l_ts = if is_number(left_timestamp) {
69 unbox_number(left_timestamp)
70 } else {
71 return box_number(0.0);
72 };
73
74 let r_ts = if is_number(right_timestamp) {
75 unbox_number(right_timestamp)
76 } else {
77 return box_number(0.0);
78 };
79
80 let tolerance = if is_number(tolerance_ms) {
81 unbox_number(tolerance_ms)
82 } else {
83 return box_number(0.0);
84 };
85
86 let diff = (l_ts - r_ts).abs();
87
88 if diff <= tolerance {
89 box_number(1.0)
90 } else {
91 box_number(0.0)
92 }
93}
94
95/// Check if a value is NULL (for LEFT/RIGHT/FULL JOIN null handling)
96#[unsafe(no_mangle)]
97pub extern "C" fn jit_join_is_null(value: u64) -> u64 {
98 if value == TAG_NULL {
99 box_number(1.0)
100 } else {
101 box_number(0.0)
102 }
103}
104
105/// Return NULL value (for outer join null filling)
106#[unsafe(no_mangle)]
107pub extern "C" fn jit_join_null() -> u64 {
108 TAG_NULL
109}
110
111/// Coalesce: return first non-NULL value
112/// Args: left, right
113#[unsafe(no_mangle)]
114pub extern "C" fn jit_join_coalesce(left: u64, right: u64) -> u64 {
115 if left != TAG_NULL { left } else { right }
116}