Skip to content

Commit 8fdf3ca

Browse files
DX-105463: [C++][Gandiva] Add TimestampIR wrapper for next_day (#135)
next_day(timestamp, utf8) was registered in the C++ function registry for timestamp inputs but was not included in any TimestampIR table, so no _us/_ns IR wrappers were generated. With the relaxed DataTypeEquals (ignoring TimeUnit), calls like next_day(timestamp[us], 'MO') pass validation and are routed to Gandiva, but BuildFunctionCall silently falls through to the precompiled millis function, which interprets microseconds as milliseconds — producing dates ~51,000 years in the future (e.g. +53425-02-28 for a 2021 input). This adds a BuildNextDayWrapper that scales the timestamp input to millis via FloorDiv and calls the precompiled next_day_from_timestamp(context, millis, day_str, day_len) function. Pattern follows the cast-from-timestamp wrapper shape with the additional (context, string, length) args. No remainder recombination is needed: next_day returns date64 (midnight of the next weekday), so sub-millisecond input precision is not meaningful in the result. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 28932a0 commit 8fdf3ca

2 files changed

Lines changed: 48 additions & 0 deletions

File tree

cpp/src/gandiva/timestamp_ir.cc

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ static std::unordered_set<std::string> BuildAllFunctionNames() {
189189
names.insert(std::string("to_utc_timezone_timestamp") + sfx);
190190
names.insert(std::string("from_utc_timezone_timestamp") + sfx);
191191
names.insert(std::string("castVARCHAR_timestamp_int64") + sfx);
192+
names.insert(std::string("next_day_from_timestamp") + sfx);
192193
}
193194
return names;
194195
}
@@ -462,6 +463,40 @@ Status TimestampIR::BuildTimezoneWrapper(const std::string& function_name,
462463
return Status::OK();
463464
}
464465

466+
Status TimestampIR::BuildNextDayWrapper(const std::string& function_name,
467+
const std::string& precompiled_millis_fn,
468+
arrow::TimeUnit::type time_unit) {
469+
// fn(context, ts, day_str, day_len) -> date64
470+
// next_day returns the date of the next weekday (e.g. 'MO' for Monday) at midnight,
471+
// so sub-ms precision is not relevant in the result — just scale the input to millis.
472+
auto precompiled_fn = module()->getFunction(precompiled_millis_fn);
473+
if (!precompiled_fn) {
474+
return Status::Invalid("Precompiled function not found: ", precompiled_millis_fn);
475+
}
476+
477+
auto i64 = types()->i64_type();
478+
auto i32 = types()->i32_type();
479+
auto i8ptr = llvm::PointerType::get(*context(), 0);
480+
auto function = BuildFunction(
481+
function_name, i64,
482+
{{"ctx", i64}, {"ts", i64}, {"day", i8ptr}, {"day_len", i32}});
483+
auto entry = llvm::BasicBlock::Create(*context(), "entry", function);
484+
ir_builder()->SetInsertPoint(entry);
485+
486+
auto arg_iter = function->arg_begin();
487+
auto ctx = &arg_iter[0];
488+
auto ts = &arg_iter[1];
489+
auto day = &arg_iter[2];
490+
auto day_len = &arg_iter[3];
491+
492+
int64_t upm = UnitsPerMilli(time_unit);
493+
auto millis = FloorDiv(ts, llvm::ConstantInt::get(i64, upm));
494+
auto result = ir_builder()->CreateCall(precompiled_fn, {ctx, millis, day, day_len});
495+
496+
ir_builder()->CreateRet(result);
497+
return Status::OK();
498+
}
499+
465500
Status TimestampIR::BuildCastVARCHARWrapper(const std::string& function_name,
466501
const std::string& precompiled_millis_fn,
467502
arrow::TimeUnit::type time_unit) {
@@ -775,6 +810,13 @@ std::pair<llvm::Value*, llvm::Value*> TimestampIR::FloorDivRem(
775810
ts_ir->BuildCastVARCHARWrapper(ir_name, "castVARCHAR_timestamp_int64",
776811
unit));
777812
}
813+
814+
// next_day(timestamp, utf8): scale to millis, return date64
815+
{
816+
std::string ir_name = std::string("next_day_from_timestamp") + sfx;
817+
try_build(ir_name,
818+
ts_ir->BuildNextDayWrapper(ir_name, "next_day_from_timestamp", unit));
819+
}
778820
}
779821

780822
// Validate that the set of functions we tried to build matches AllFunctionNames().

cpp/src/gandiva/timestamp_ir.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ class TimestampIR : public FunctionIRBuilder {
113113
const std::string& precompiled_fn,
114114
arrow::TimeUnit::type unit);
115115

116+
// next_day: scale ts to millis, call precompiled, return date64
117+
// fn(context, ts, day_str, day_len) -> int64
118+
Status BuildNextDayWrapper(const std::string& fn,
119+
const std::string& precompiled_fn,
120+
arrow::TimeUnit::type unit);
121+
116122
// Floor division: ts / divisor rounded toward negative infinity.
117123
// C/LLVM SDiv truncates toward zero, which gives wrong millis for negative
118124
// timestamps with non-zero sub-ms components (e.g., SDiv(-456, 1000) = 0

0 commit comments

Comments
 (0)