ddn.df.join
The join layer of ddn.df: inner and left joins emitting rows in left-frame order (T28; D12; ADR-2026-10-18-05).
The table is built on the right key (the smaller probing side) and probed with the left key in sequence, so both join kinds emit rows in left-frame order and, within one left row, in right-frame row order — a stronger contract than Polars' maintain_order='none', which lets the cross-chunking harness compare join results exactly. Null keys never match; unmatched retention follows the kind (ADR-2026-10-23-07: LEFT and ANTI retain null-key left rows, INNER and SEMI drop them); the key column is coalesced into one output column; right-side name collisions get the suffix.
Copyright
Types 6
The join kinds (ADR-2026-10-23-07). Null keys never match in any kind; retention follows the kind: INNER drops unmatched (and null-key) left rows, LEFT retains every left row (null-key rows null-padded - the Polars semantics, adopted with explicit compatibility approval), SEMI keeps only matched left rows, ANTI only unmatched ones (null-key left rows included).
size_t hashsize_t rowsize_t nextThe small-right serial chain table (the original build, unchanged).
The large-right slot-partitioned CSR table (ADR-2026-10-23-03): every right row appears exactly once in hashes/rows, its slot's region [starts[slot] .. starts[slot + 1]] holding it in right-row order - the same set in the same order as the chain table's walk, with no pointers to chase and no way to cycle. Built by three deterministic phases with one writer per precomputed position.
size_t[] startssize_t[] hashessize_t[] rowsThe asof match direction (ADR-2026-10-30-03).
Functions 17
CsrTable buildCsrTable(const size_t[] rHashes, size_t rheight,
size_t capacity)Builds the CSR table (ADR-2026-10-23-03). Phase A scatters the entries into 64 hash-high-bit partitions (per-segment histograms, a partition-major/segment-minor serial prefix - the `argSortRadix` d...void probeSegments(bool csr, Tables)(Tables tables, size_t mask,
const ArrayData[][] lkeyChunks, const size_t[] lstarts,
const size_t[] lHashes, const ArrayData[][] rkeyChunks,
const size_t[] rstarts, const ColumnEq[] eqs,
JoinKind kind, size_t segLen, size_t nSegs, size_t lheight,
ref size_t[][] segLeft, ref size_t[][] segRight,
size_t[][] matchedRight = null)The probe (ADR-2026-10-19-02, ADR-2026-10-23-03): fixed left-row segments over the immutable table, each first counting its hash-equal candidates (integer compares only - the typed key equality run...size_t chunkIndexOf(const size_t[] starts, size_t row) @safe pure nothrowJoins two frames on equal-dtype key columns. The output columns are the left frame's followed by the right frame's minus the key; the key is coalesced into one column; right-side name collisions ge...bool crossRowCount(size_t lheight, size_t rheight,
out size_t count) @safe pure nothrowThe checked Cartesian row count (ADR-2026-10-30-03): the product must fit `size_t`; a null result rejects before any allocation.DataFrame crossJoinFrames(DataFrame left, DataFrame right,
string suffix = "_right")The cross join (ADR-2026-10-30-03): the left-major nested loop - every left row in order, all right rows in right order within it - with the product's row count overflow-checked before any allocati...DataFrame joinFrames(DataFrame left, DataFrame right, string leftOn,
string rightOn, JoinKind kind = JoinKind.INNER, string suffix = "_right")DataFrame joinKeys(DataFrame left, DataFrame right, string[] leftOn,
string[] rightOn, JoinKind kind = JoinKind.INNER,
string suffix = "_right")The multi-column join (ADR-2026-10-30-03): a row matches only when every key pair holds under the existing per-kind rules; any null component never matches (LEFT keeps the row null-padded, SEMI dro...size_t[] chunkStartsOf(const ArrayData[] chunks) @safe pure nothrowThe chunk-start offsets of a chunk list.size_t[] concatSegments(size_t[][] segs, size_t nSegs)Key-row equality across the two sides' chunkings: nulls never match, values compare through the preselected typed equality at the key dtype, and both rows locate by binary search over the chunk sta...void probeExistence(bool csr, Tables)(Tables tables, size_t mask,
const ArrayData[][] lkeyChunks, const size_t[] lstarts,
const size_t[] lHashes, const ArrayData[][] rkeyChunks,
const size_t[] rstarts, const ColumnEq[] eqs,
JoinKind kind, size_t segLen, size_t nSegs, size_t lheight,
size_t[][] segKeep)The existence probe of SEMI/ANTI (ADR-2026-10-23-07): fixed left-row segments over the immutable table, each filling its kept left rows - SEMI keeps rows with at least one match, ANTI the rest (nul...bool rowsEqual(const ArrayData[][] lcols,
const size_t[] lstarts, size_t lrow, const ArrayData[][] rcols,
const size_t[] rstarts, size_t rrow, const ColumnEq[] eqs) @safe pure nothrowbool rowsEqualAt(const ArrayData[][] lcols, size_t lc,
size_t li, const ArrayData[][] rcols, const size_t[] rstarts,
size_t rrow, const ColumnEq[] eqs) @safe pure nothrowsize_t chunkIndexFor(const size_t[] starts, size_t row) @safe pure nothrowThe binary search of the chunk holding a global row.DataFrame joinAsofFrames(DataFrame left, DataFrame right,
string leftOn, string rightOn,
AsofDirection direction = AsofDirection.BACKWARD,
long tolerance = long.max)The sorted asof join (ADR-2026-10-30-03): for each left row (in order, at most one right row), BACKWARD takes the last right row with `rightKey <= leftKey`, FORWARD the first with `>=`, NEAREST the...