Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
133 commits
Select commit Hold shift + click to select a range
89eed55
v2.4.0 (#327)
singaraiona Jul 11, 2026
eb66eed
feat(query): support live inserts into parted tables
singaraiona Jul 14, 2026
bbdc419
fix(core): restore total-core -c semantics
singaraiona Jul 15, 2026
9961388
fix(parse) Fix nonstring if not defined
protocolstardust Jul 16, 2026
6cac6e8
Merge pull request #330 from protocolstardust/dev
protocolstardust Jul 16, 2026
f44ab81
Merge branch 'master' into dev
singaraiona Jul 16, 2026
49d8524
fix(store): surface FlushFileBuffers failure in journal SYNC mode (#335)
belowzeroff Jul 18, 2026
cb1c6cd
fix(hnsw): reject build dims whose vector count overflows size_t (#333)
belowzeroff Jul 18, 2026
7475949
fix(store): read full link sidecar to avoid wrong-symbol truncation (…
belowzeroff Jul 18, 2026
ebadd07
fix(hnsw): reject index files whose vector count overflows size_t (#332)
belowzeroff Jul 21, 2026
fa39d9d
fix(docs): remediate F-0001 F-0005 F-0007
singaraiona Jul 22, 2026
ffd784f
fix(docs): remediate CF-0001
singaraiona Jul 22, 2026
1f27e61
fix(docs): remediate CF-0002
singaraiona Jul 22, 2026
465e58e
chore(audit): plan CF-0003 ratification
singaraiona Jul 22, 2026
8f8033c
fix(docs): remediate CF-0003
singaraiona Jul 22, 2026
3ae79f5
feat(docs): redesign website and documentation
singaraiona Jul 22, 2026
5dc8265
Merge branch 'master' into dev
singaraiona Jul 22, 2026
4d2c8f3
fix(null): avoid f64 null casts to integers (#340)
belowzeroff Jul 23, 2026
4e49626
fix(expr): avoid null truthiness casts in fallback binary ops (#339)
belowzeroff Jul 23, 2026
a05886d
ci: make Rayforce audit PR comments best-effort
singaraiona Jul 23, 2026
6ca3d8a
ci: publish Rayforce audit comments from trusted workflow
singaraiona Jul 23, 2026
04d8f3d
ci: resolve fork PRs for audit commenter
singaraiona Jul 23, 2026
6acdc2c
perf: parallelize serial stages around group-by; unify binary-agg nul…
ser-vasilich Jul 23, 2026
a53d98e
Merge branch 'master' into dev
singaraiona Jul 23, 2026
260e018
fix(aggr): preserve slice nullability in binary groups
singaraiona Jul 23, 2026
492c5db
fix(expr): avoid f64 null cast in fallback idiv integer output (#344)
belowzeroff Jul 23, 2026
d250108
fix(group): avoid f64 null read cast in dense aggs (#343)
belowzeroff Jul 23, 2026
95f0a3d
fix(ipc): preserve boxed data list args (#346)
belowzeroff Jul 24, 2026
e10d1ab
fix(group): avoid f64 null cast in DA reads (#348)
belowzeroff Jul 24, 2026
842acc0
ci: use portable march for fuzz jobs (#347)
belowzeroff Jul 24, 2026
84cea17
chore(release): merge master into dev
singaraiona Jul 24, 2026
7a5839e
fix(pivot): preserve generic missing cells as null (#350)
belowzeroff Jul 27, 2026
57628fb
fix(xbar): avoid narrow bucket truncation (#351)
belowzeroff Jul 28, 2026
d7a1f6f
Merge branch 'master' into dev
singaraiona Jul 28, 2026
c501ae0
fix(arith): reject float temporal operands (#353)
belowzeroff Jul 29, 2026
aea9bda
fix(ops): make nested (LIST) columns usable through a parted view (fi…
singaraiona Jul 30, 2026
0605f1a
fix(query): preserve temporal arithmetic semantics (#354)
belowzeroff Jul 30, 2026
282d3a1
Merge branch 'master' into dev
singaraiona Jul 30, 2026
17bf2b8
ci: skip audit comments for cancelled runs
singaraiona Jul 30, 2026
05c8078
fix(query): preserve if temporal branch types (#359)
belowzeroff Jul 31, 2026
488a296
fix(store): reject duplicate splayed column names (#360)
belowzeroff Aug 3, 2026
5684559
fix(builtins): reject malformed strings in TIMESTAMP cast (#361)
belowzeroff Aug 3, 2026
c9b9a66
fix(store): support nested column inserts and persistence (#365)
singaraiona Aug 3, 2026
4c0fa46
fix(eval): enforce restricted mode in compiled lambdas (#366)
singaraiona Aug 3, 2026
8c9300b
feat(core): add bounded poll step for embedders (#367)
singaraiona Aug 3, 2026
3c79632
Merge branch 'master' into dev
singaraiona Aug 3, 2026
9b05f60
fix(test): rely on public runtime declarations (#369)
belowzeroff Aug 4, 2026
add3498
feat: website add consumer
singaraiona Aug 4, 2026
9d255c2
Merge branch 'master' into dev
singaraiona Aug 4, 2026
a1dcfd9
feat(core): expose restricted poll mode (#372)
singaraiona Aug 4, 2026
d5d5240
fix(aggr): preserve grouped nested first and last
singaraiona Aug 4, 2026
c35fa2b
Merge branch 'master' into dev
singaraiona Aug 4, 2026
2cfc70e
fix(join): preserve nested columns (#376)
singaraiona Aug 4, 2026
ac9d0cc
Merge branch 'master' into dev
singaraiona Aug 4, 2026
2fa0fbd
fix(csv): round-trip signed and >=24h TIME values in .csv.read (#379)
belowzeroff Aug 4, 2026
959db02
fix(str): propagate null start/length in substr instead of overflowin…
belowzeroff Aug 4, 2026
2083f24
fix(builtins): clamp out-of-range float in scalar numeric casts (#380)
belowzeroff Aug 5, 2026
5419053
Merge branch 'master' into dev
singaraiona Aug 5, 2026
c731b42
fix(builtins): validate bounds in DATE string cast (#382)
belowzeroff Aug 6, 2026
93db5ce
perf(aggr): specialize reduction scans (#383)
singaraiona Aug 6, 2026
e71798a
perf(collection): parallel radix distinct for fixed-width columns (#384)
protocolstardust Aug 6, 2026
8d6466f
fix(csv): harden import cancellation and schema handling (#387)
singaraiona Aug 7, 2026
8a1cf0c
fix(temporal): guard int64 overflow in DATE/TIMESTAMP decomposition (…
belowzeroff Aug 9, 2026
7bf522a
fix(eval): materialize lazy values at compiled boundaries (#391)
singaraiona Aug 11, 2026
caa529d
fix(arith): wrap scalar integer add/sub/mul on overflow instead of UB…
belowzeroff Aug 11, 2026
a2607ac
fix(builtins): reject malformed strings in TIME cast (#389)
belowzeroff Aug 11, 2026
a14ba5c
Merge branch 'master' into dev
singaraiona Aug 11, 2026
5aa47a5
fix(builtins): validate hex digits and dash layout in GUID string cas…
belowzeroff Aug 12, 2026
d1d256e
feat(core): address Data Vault workflow gaps (#395)
singaraiona Aug 12, 2026
f1d1b71
feat(io): add binary file read and write (#396)
singaraiona Aug 12, 2026
9a4ea60
Merge branch 'master' into dev
singaraiona Aug 12, 2026
776b290
fix(store): repair legacy string hash caches (#398)
singaraiona Aug 12, 2026
afbcf69
fix(builtins): reject trailing garbage and overflow in numeric string…
belowzeroff Aug 13, 2026
31d8970
fix(query): reject STR equality keys in asof-join and window-join (#400)
belowzeroff Aug 14, 2026
95defb7
fix(store): support LIST columns in parted fill (#402)
singaraiona Aug 14, 2026
4098f8b
fix(exec): propagate str_pool in OP_HEAD/OP_TAIL flat-column copies (…
singaraiona Aug 14, 2026
55c6e22
fix(query): evaluate whole-column aggregate args once in ungrouped se…
singaraiona Aug 14, 2026
bafb435
fix(group): sign-extend narrow keys in the DA accumulate loops
singaraiona Aug 14, 2026
4c0ed09
perf(group): size filtered group-by work from the surviving rows (Cli…
singaraiona Aug 14, 2026
68ace96
perf(agg): fix radix hash-bit overlap and rehash ladders in the v2 gr…
singaraiona Aug 15, 2026
76c50e3
perf(group): prime fat-entry radix payload buffers to expected load
singaraiona Aug 15, 2026
75b6901
feat(store): decide hash-vs-zone indexes at csv->splayed conversion; …
singaraiona Aug 15, 2026
2dc9cff
perf(group): route wide-domain SYM emit-filter groups to the parallel…
singaraiona Aug 15, 2026
e9c2d64
perf(group): tighten emit-filter v2 rerouting gates
singaraiona Aug 15, 2026
07ff419
perf(agg): memset the phase-3 order map instead of a scalar -1 loop
singaraiona Aug 15, 2026
399f9dd
perf(agg): parallelize the phase-3 stable-order scatter and compaction
singaraiona Aug 15, 2026
6357a2c
perf(pool): default worker auto-sizing to physical cores, not SMT thr…
singaraiona Aug 15, 2026
212fff3
perf(mem): reuse cache for direct (large) allocations
singaraiona Aug 15, 2026
1842700
fix(mem): public drain for the direct reuse cache + watermark test co…
singaraiona Aug 15, 2026
1ae57b9
refactor(mem): derive the direct-cache budget instead of an env knob
singaraiona Aug 15, 2026
d5867b6
perf(mem): raise the direct-cache cap to 4GB for 100M-row scale
singaraiona Aug 15, 2026
5d318ec
perf(group): pipelined slot prefetch in the v2 direct-insert phase1
singaraiona Aug 15, 2026
88c95bb
test(group): pin high-cardinality radix_v2 group shapes before partit…
singaraiona Aug 15, 2026
f30b578
fix(test): cast I32 key column to ensure radix_v2 path exercises I32 …
singaraiona Aug 15, 2026
9768280
perf(group): partition-major morsel batching in radix_v2 phase1
singaraiona Aug 15, 2026
34c9be0
perf(group): sparse touched-partition histogram + u32 partition ids i…
singaraiona Aug 15, 2026
0ac9aa4
feat(group): fused grouped count-distinct kernel (unwired)
singaraiona Aug 15, 2026
9959cbf
fix(cdfuse): guard dropped dispatch tasks; strengthen fused-cd tests
singaraiona Aug 15, 2026
0c68d3b
perf(query): route single-key unfiltered count-distinct through the f…
singaraiona Aug 15, 2026
94e28b9
perf(cdfuse): pair-hash partitioning with additive per-key merge (ske…
singaraiona Aug 15, 2026
125336f
perf(cdfuse): parallel key-bucketed merge phase
singaraiona Aug 15, 2026
66dcdd2
fix(cdfuse): asymmetric pair-hash combine (k==v cancellation cliff)
singaraiona Aug 15, 2026
c0cf496
fix(cdfuse): memory admission gate, SYM + boundary coverage, self-con…
singaraiona Aug 15, 2026
f3b2cbb
perf(mem): drop the direct-cache absolute cap; watermark/16 is the bound
singaraiona Aug 15, 2026
f1aaa73
perf(query): push a positive take: into grouped DAGs and bound v2's emit
singaraiona Aug 15, 2026
5684d4c
fix(query): guard the grouped take: pushdown and trim LIST columns
singaraiona Aug 15, 2026
2dd36e9
fix(query): pin the parted take guard, drop the take_pre allocation
singaraiona Aug 15, 2026
5784c19
perf(group): packed narrow-key fast path in the fat-entry radix pipeline
singaraiona Aug 15, 2026
ab5053a
fix(group): restore packed-key hash lockstep in the fused radix path
singaraiona Aug 15, 2026
701c7b8
test(group): pin F64 packed-key exclusion; honest 9-key test comment
singaraiona Aug 15, 2026
16bd46d
perf(group): prefetched partition-major probe for COUNT/SUM/AVG near-…
singaraiona Aug 16, 2026
ead2fff
fix(group): bound the grow_cap jump and keep it off the row array
singaraiona Aug 16, 2026
f2fba93
perf(group): parallelize the top-N group compaction
singaraiona Aug 16, 2026
bd9e743
fix(group): guard the top-N pre-pass against a cancelled dispatch
singaraiona Aug 16, 2026
853b3d3
perf(temporal): parallelize the extract/truncate kernels over the pool
singaraiona Aug 16, 2026
d64eb1d
test(temporal): pin the HAS_NULLS fold with vec-gate consumers; slim …
singaraiona Aug 16, 2026
902f294
perf(group): parallelize the radix key-column emit
singaraiona Aug 16, 2026
9e3cb09
test(group): slim the key-emit fixture; correct the take-bounded rout…
singaraiona Aug 16, 2026
d315102
perf(group): fuse the per-partition top-k into radix phase 2
singaraiona Aug 16, 2026
03142f5
fix(group): honour asc: on the top-N group emit filter (#408)
singaraiona Aug 16, 2026
9d95f6f
docs(group): pin the emit-filter .desc contract; honest #408 test com…
singaraiona Aug 16, 2026
c3fb6c6
fix(group): canonicalise -0.0 F64 group keys at the key-read boundary…
singaraiona Aug 16, 2026
2ec5284
fix(group): bit-level -0.0 fold in mode_scalar_key; precise NaN-polic…
singaraiona Aug 16, 2026
d59bb43
fix(collection): reject INT64_MIN count in take/drop instead of overf…
belowzeroff Aug 16, 2026
cd84f48
fix(cal): decode dates near INT32_MAX without int32 overflow (#410)
belowzeroff Aug 16, 2026
91fe7e3
fix(query): support update over parted tables and prevent column dupl…
belowzeroff Aug 16, 2026
66a80fb
fix(arith): keep integer div exact and UB-free (#403)
belowzeroff Aug 16, 2026
9e5f0a1
perf(expr): stop rescanning operands for the integer div gate (#403 f…
singaraiona Aug 16, 2026
2b93010
test(expr): pin the div gate bound; defensive idiv_i64_small reset un…
singaraiona Aug 16, 2026
412b69d
Merge branch 'master' into dev
singaraiona Aug 16, 2026
4fca523
fix(build): clang-clean boolean accumulation in the div gate scans
singaraiona Aug 16, 2026
c303733
docs(query): neutralize a product-name mention in the update by: scat…
singaraiona Aug 16, 2026
e5d87cd
docs(test): neutralize remaining product-name mentions in update-by c…
singaraiona Aug 16, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,4 @@ crash-*
leak-*
timeout-*
oom-*
.superpowers/
12 changes: 10 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,16 @@ RAY_MARCH ?= native
DEBUG_CFLAGS = -fPIC $(WARNS) -std=$(STD) -g -O0 -march=$(RAY_MARCH) -DDEBUG \
-fsanitize=address,undefined -fno-omit-frame-pointer
RELEASE_CFLAGS = -fPIC $(WARNS) -std=$(STD) -O3 -march=$(RAY_MARCH) \
-funroll-loops -fomit-frame-pointer -fno-math-errno \
-funroll-loops -fomit-frame-pointer -fno-math-errno -falign-functions=64 \
-fassociative-math -ffp-contract=fast -fno-signed-zeros -fno-trapping-math
# -falign-functions=64: start every function on a cache line. The default
# 16-byte alignment makes hot leaf functions' cost depend on where unrelated
# edits elsewhere in the same translation unit happen to push them —
# measured as a reproducible 14% swing on ClickBench q33/q34
# (exec_group_sp_dyn_emit, ~45% of those queries) from an edit that path
# never executes. That noise floor is larger than most real optimisations,
# so it hides regressions and manufactures phantom ones. Costs a few KB of
# padding.
# -fassociative-math: license to reorder FP additions/multiplications.
# Required for autovectorization of F64 reductions (sum/avg/dot).
# Without it, scalar_sum_f64_fn at group.c:1666 is a serial latency
Expand Down Expand Up @@ -111,7 +119,7 @@ TSAN_LDFLAGS = -fsanitize=thread
HARDENED_CFLAGS = -fPIC $(WARNS) -std=$(STD) -O3 -march=$(RAY_MARCH) -g \
-fno-omit-frame-pointer -DRAY_HARDENED \
-funroll-loops -fno-math-errno -fassociative-math -ffp-contract=fast \
-fno-signed-zeros -fno-trapping-math
-fno-signed-zeros -fno-trapping-math -falign-functions=64

CFLAGS = $(DEBUG_CFLAGS)
LDFLAGS = $(DEBUG_LDFLAGS)
Expand Down
3 changes: 3 additions & 0 deletions docs/docs/storage/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ ray_t* trades = ray_read_parted("db", "trades");
!!! note "Rayfall builtin"
Use `.db.parted.get` from Rayfall to load partitioned tables: `(.db.parted.get "db" 'trades)`. See the [Rayfall Storage Builtins](#rayfall-storage) section below.

!!! warning "`update` over a partitioned table materializes in memory"
`update` on a partitioned table flattens the whole table into memory first — the parted/`MAPCOMMON` columns cannot be mutated in place — so the result is an ordinary in-memory table. It is **not** written back to the store: re-reading the root returns the original values, and the returned table loses its parted / memory-mapped identity.

### Partition Pruning

The query optimizer recognizes predicates on the `MAPCOMMON` column and eliminates entire partitions from the scan plan. This means a query filtering on a single date in a year of data only touches 1/365th of the files on disk — with zero per-row cost for the pruned partitions.
Expand Down
53 changes: 53 additions & 0 deletions src/core/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#if defined(RAY_OS_MACOS)
#include <sys/sysctl.h> /* sysctlbyname — hw.physicalcpu */
#endif
#include "mem/sys.h"

/* --------------------------------------------------------------------------
Expand Down Expand Up @@ -247,6 +251,49 @@ uint32_t ray_thread_count(void) {
return (n > 0) ? (uint32_t)n : 1;
}

/* Physical cores (SMT siblings collapsed). The worker pool's kernels are
* memory-bound; two hyperthreads sharing one core's load/store machinery
* only add contention (measured: the full ClickBench suite runs ~11%
* SLOWER with 32 SMT threads than with the 16 physical cores on a 5950X).
* Counts unique (package, core) pairs from sysfs; any read failure falls
* back to the logical count so exotic systems keep the old behavior. */
uint32_t ray_physical_core_count(void) {
#if defined(RAY_OS_MACOS)
int phys = 0;
size_t len = sizeof(phys);
if (sysctlbyname("hw.physicalcpu", &phys, &len, NULL, 0) == 0 && phys > 0)
return (uint32_t)phys;
return ray_thread_count();
#else
uint32_t logical = ray_thread_count();
/* (package_id << 16) | core_id per cpu; count distinct values. */
enum { MAX_IDS = 4096 };
uint32_t seen[MAX_IDS];
uint32_t n_seen = 0;
for (uint32_t cpu = 0; cpu < logical && cpu < MAX_IDS; cpu++) {
char path[128];
long core = -1, pkg = 0;
FILE* f;
snprintf(path, sizeof(path),
"/sys/devices/system/cpu/cpu%u/topology/core_id", cpu);
f = fopen(path, "r");
if (!f) return logical; /* no topology → fall back */
if (fscanf(f, "%ld", &core) != 1) { fclose(f); return logical; }
fclose(f);
snprintf(path, sizeof(path),
"/sys/devices/system/cpu/cpu%u/topology/physical_package_id",
cpu);
f = fopen(path, "r");
if (f) { if (fscanf(f, "%ld", &pkg) != 1) pkg = 0; fclose(f); }
uint32_t id = ((uint32_t)pkg << 16) | ((uint32_t)core & 0xFFFF);
uint32_t j = 0;
while (j < n_seen && seen[j] != id) j++;
if (j == n_seen) seen[n_seen++] = id;
}
return n_seen > 0 ? n_seen : logical;
#endif
}

/* --------------------------------------------------------------------------
* Semaphore
* -------------------------------------------------------------------------- */
Expand Down Expand Up @@ -433,6 +480,12 @@ uint32_t ray_thread_count(void) {
return (uint32_t)si.dwNumberOfProcessors;
}

/* Windows: no cheap topology read here — fall back to the logical count
* (the SMT-aware default sizing is a POSIX-side optimization). */
uint32_t ray_physical_core_count(void) {
return ray_thread_count();
}

/* --------------------------------------------------------------------------
* Semaphore
* -------------------------------------------------------------------------- */
Expand Down
3 changes: 3 additions & 0 deletions src/core/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ bool ray_vm_hugepage(void* ptr, size_t size);
ray_err_t ray_thread_create(ray_thread_t* t, ray_thread_fn fn, void* arg);
ray_err_t ray_thread_join(ray_thread_t t);
uint32_t ray_thread_count(void);
/* Physical cores (SMT siblings collapsed); falls back to the logical
* count when topology is unavailable. */
uint32_t ray_physical_core_count(void);

void ray_parallel_begin(void);
void ray_parallel_end(void);
Expand Down
8 changes: 7 additions & 1 deletion src/core/pool.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,13 @@ static ray_err_t ray_pool_create_impl(ray_pool_t* pool, uint32_t n_workers,
long v = strtol(env, NULL, 10);
n_workers = (v > 0) ? (uint32_t)v : 0;
} else {
uint32_t ncpu = ray_thread_count();
/* Physical cores, not SMT threads: the pool's kernels are
* memory-bound, and hyperthread pairs sharing one core's
* load/store machinery only contend (full ClickBench suite
* measured ~11% slower at 32 SMT threads than at the 16
* physical cores of a 5950X). ray_physical_core_count falls
* back to the logical count when topology is unreadable. */
uint32_t ncpu = ray_physical_core_count();
n_workers = (ncpu > 1) ? ncpu - 1 : 0;
}
}
Expand Down
58 changes: 44 additions & 14 deletions src/io/csv.c
Original file line number Diff line number Diff line change
Expand Up @@ -1466,14 +1466,17 @@ static int csv_hash_elem_size(int8_t t) {
* BOOL/U8/I16 where the index would dwarf the column.
*
* Returns 1 to attach, 0 to skip. */
static int csv_should_attach_hash(ray_t* v) {
if (!v || RAY_IS_ERR(v)) return 0;
int esz = csv_hash_elem_size(v->type);
/* Payload-level core of the hash-upgrade decision, shared with the
* .csv.splayed index builder (ray_splay_build_indexes) so the on-disk
* store makes the SAME hash-vs-zone decision the in-memory load does --
* a reloaded store must not query slower than a fresh .csv.read. */
int ray_csv_hash_upgrade_check(int8_t type, int64_t len,
const void* index_payload) {
const ray_index_t* ix = (const ray_index_t*)index_payload;
int esz = csv_hash_elem_size(type);
if (esz == 0) return 0;
/* Need a chunk_zone we can read for entropy estimation. */
if (!(v->attrs & RAY_ATTR_HAS_INDEX) || !v->index) return 0;
ray_index_t* ix = ray_index_payload(v->index);
if (ix->kind != RAY_IDX_CHUNK_ZONE || ix->u.chunk_zone.is_f64) return 0;
if (!ix || ix->kind != RAY_IDX_CHUNK_ZONE || ix->u.chunk_zone.is_f64)
return 0;
uint32_t n_chunks = ix->u.chunk_zone.n_chunks;
if (n_chunks < 4) return 0;
const int64_t* mins = (const int64_t*)ray_data(ix->u.chunk_zone.mins);
Expand Down Expand Up @@ -1535,7 +1538,7 @@ static int csv_should_attach_hash(ray_t* v) {
* index dwarfs the data) out of the index set while admitting
* I32 / I64 numeric IDs. Done in int64 arithmetic (we cap n
* to anything that would overflow at the row counts we accept). */
int64_t n = v->len;
int64_t n = len;
if (n <= 0) return 0;
uint64_t cap = 8;
uint64_t want = (uint64_t)(2 * n);
Expand All @@ -1547,6 +1550,13 @@ static int csv_should_attach_hash(ray_t* v) {
return 1;
}

static int csv_should_attach_hash(ray_t* v) {
if (!v || RAY_IS_ERR(v)) return 0;
if (!(v->attrs & RAY_ATTR_HAS_INDEX) || !v->index) return 0;
return ray_csv_hash_upgrade_check(v->type, v->len,
ray_index_payload(v->index));
}

/* --------------------------------------------------------------------------
* `INT` schema columns — auto narrowest integer width.
*
Expand Down Expand Up @@ -1953,9 +1963,19 @@ static ray_t* csv_materialize_rows(const char* buf, size_t file_size,
* After the chunk_zone attaches we re-walk the same columns and
* upgrade the high-entropy ones to a hash index (the chunk_zone
* stays as well — it's the entropy signal we just measured). See
* csv_should_attach_hash for the selectivity + memory cap. */
* csv_should_attach_hash for the selectivity + memory cap.
*
* Progress: same treatment as the finalize dispatch above — the
* per-column index builds run their own pool dispatches whose row
* totals are not n_rows, so letting them drive the progress pump
* resets a completed parse to arbitrary fractions (visibly
* 100% -> 50% -> ...). Suppress progress for the whole index
* phase; the parse's completed row count stays on screen. */
uint32_t idx_qmode = ray_qstats_mode();
ray_qstats_set_mode(idx_qmode & ~RAY_QS_PROGRESS);
for (int c = 0; c < ncols; c++) {
if (ray_interrupted()) {
ray_qstats_set_mode(idx_qmode);
for (int j = 0; j < ncols; j++) ray_release(col_vecs[j]);
return ray_error("cancel", "interrupted");
}
Expand All @@ -1964,6 +1984,7 @@ static ray_t* csv_materialize_rows(const char* buf, size_t file_size,
if (v->len < (1 << 16)) continue; /* < one chunk, skip */
ray_t* r = ray_index_attach_chunk_zone(&v, 16);
if (ray_interrupted()) {
ray_qstats_set_mode(idx_qmode);
for (int j = 0; j < ncols; j++) ray_release(col_vecs[j]);
return ray_error("cancel", "interrupted");
}
Expand All @@ -1972,6 +1993,7 @@ static ray_t* csv_materialize_rows(const char* buf, size_t file_size,
}
for (int c = 0; c < ncols; c++) {
if (ray_interrupted()) {
ray_qstats_set_mode(idx_qmode);
for (int j = 0; j < ncols; j++) ray_release(col_vecs[j]);
return ray_error("cancel", "interrupted");
}
Expand All @@ -1984,11 +2006,13 @@ static ray_t* csv_materialize_rows(const char* buf, size_t file_size,
* anyway, so the chunk_zone is dead weight. */
ray_t* r = ray_index_attach_hash(&v);
if (ray_interrupted()) {
ray_qstats_set_mode(idx_qmode);
for (int j = 0; j < ncols; j++) ray_release(col_vecs[j]);
return ray_error("cancel", "interrupted");
}
if (r && !RAY_IS_ERR(r)) col_vecs[c] = v;
}
ray_qstats_set_mode(idx_qmode);

ray_t* tbl = ray_table_new(ncols);
if (!tbl || RAY_IS_ERR(tbl)) {
Expand Down Expand Up @@ -2459,24 +2483,30 @@ ray_t* ray_read_csv_named_opts(const char* path, char delimiter, bool header,
* matching loop in build_table_from_cols) — unsupported types
* fall through to the unindexed path inside the consumer.
* Second pass upgrades high-entropy columns to a hash index;
* see csv_should_attach_hash. */
* see csv_should_attach_hash.
* Progress suppressed for the whole phase (same rationale as the
* finalize dispatch): the index builds' pool dispatches would
* reset the completed parse progress to arbitrary fractions. */
uint32_t idx_qmode = ray_qstats_mode();
ray_qstats_set_mode(idx_qmode & ~RAY_QS_PROGRESS);
for (int c = 0; c < ncols; c++) {
if (ray_interrupted()) goto fail_cols_cancel;
if (ray_interrupted()) { ray_qstats_set_mode(idx_qmode); goto fail_cols_cancel; }
ray_t* v = col_vecs[c];
if (!v || RAY_IS_ERR(v)) continue;
if (v->len < (1 << 16)) continue;
ray_t* r = ray_index_attach_chunk_zone(&v, 16);
if (ray_interrupted()) goto fail_cols_cancel;
if (ray_interrupted()) { ray_qstats_set_mode(idx_qmode); goto fail_cols_cancel; }
if (r && !RAY_IS_ERR(r)) col_vecs[c] = v;
}
for (int c = 0; c < ncols; c++) {
if (ray_interrupted()) goto fail_cols_cancel;
if (ray_interrupted()) { ray_qstats_set_mode(idx_qmode); goto fail_cols_cancel; }
ray_t* v = col_vecs[c];
if (!csv_should_attach_hash(v)) continue;
ray_t* r = ray_index_attach_hash(&v);
if (ray_interrupted()) goto fail_cols_cancel;
if (ray_interrupted()) { ray_qstats_set_mode(idx_qmode); goto fail_cols_cancel; }
if (r && !RAY_IS_ERR(r)) col_vecs[c] = v;
}
ray_qstats_set_mode(idx_qmode);

ray_t* tbl = ray_table_new(ncols);
if (!tbl || RAY_IS_ERR(tbl)) {
Expand Down
6 changes: 6 additions & 0 deletions src/io/csv.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,10 @@ ray_err_t ray_csv_save_splayed_named_opts(const char* path, char delimiter, bool
const char* dir, int64_t rows_per_chunk);
ray_err_t ray_write_csv(ray_t* table, const char* path);

/* Hash-vs-chunk-zone index policy for a column, from its chunk-zone entropy
* (payload-level; see csv.c). Shared by the in-memory CSV load and the
* .csv.splayed store index builder so both make the same decision. */
int ray_csv_hash_upgrade_check(int8_t type, int64_t len,
const void* index_payload);

#endif /* RAY_CSV_H */
18 changes: 10 additions & 8 deletions src/lang/cal.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,22 @@ static inline int date_leap_year(int year) {
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}

static inline int32_t date_years_by_days(int yy) {
return (int32_t)((int64_t)yy * 365 + yy / 4 - yy / 100 + yy / 400);
static inline int64_t date_years_by_days(int yy) {
return (int64_t)yy * 365 + yy / 4 - yy / 100 + yy / 400;
}

/* Decode: days-since-epoch → year/month/day */
static inline void date_to_ymd(int32_t days, int* y, int* m, int* d) {
int32_t offset = days + date_years_by_days(RAY_DATE_EPOCH - 1);
/* int64 throughout: days near INT32_MAX overflow int32 after the epoch
* offset (e.g. (as 'DATE 2147483646)), producing garbage years. */
int64_t offset = (int64_t)days + date_years_by_days(RAY_DATE_EPOCH - 1);
double approx = (double)offset / 365.2425;
int32_t years = (int32_t)(approx >= 0.0 ? approx + 0.5 : approx - 0.5);

if (date_years_by_days(years) > offset)
years -= 1;

int32_t rem = offset - date_years_by_days(years);
int64_t rem = offset - date_years_by_days(years);
int yy = years + 1;
int leap = date_leap_year(yy);
int mid = 0;
Expand All @@ -68,18 +70,18 @@ static inline void date_to_ymd(int32_t days, int* y, int* m, int* d) {

*y = yy;
*m = 1 + mid % 12;
*d = 1 + rem - (int32_t)MONTHDAYS[leap][mid];
*d = 1 + (int)(rem - (int32_t)MONTHDAYS[leap][mid]);
}

/* Encode: year/month/day → days-since-epoch */
static inline int32_t ymd_to_date(int year, int month, int day) {
int yy = (year > 0) ? year - 1 : 0;
int32_t ydays = date_years_by_days(yy);
int64_t ydays = date_years_by_days(yy);
int leap = date_leap_year(year);
int mm = (month > 0) ? month - 1 : 0;
if (mm > 12) mm = 12; /* defensive: never index past the 13-wide table */
int32_t mdays = (int32_t)MONTHDAYS[leap][mm];
return ydays - date_years_by_days(RAY_DATE_EPOCH - 1) + mdays + day - 1;
int64_t mdays = (int32_t)MONTHDAYS[leap][mm];
return (int32_t)(ydays - date_years_by_days(RAY_DATE_EPOCH - 1) + mdays + day - 1);
}

#endif /* RAY_CAL_H */
Loading
Loading