Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1af3cd8
test(cudf): Add reproducers for GPU timezone gaps
dan13bauer Jun 22, 2026
5ddd99b
feat(cudf): Add timezone support to GPU expression evaluation
dan13bauer Jun 22, 2026
37c8b33
test(cudf): Add reproducers for GPU timezone null and offset gaps
dan13bauer Jun 23, 2026
739a9d5
fix(cudf): Propagate nulls and bound offsets in GPU timezone path
dan13bauer Jun 23, 2026
0bf6bdc
test(cudf): Add reproducers for more GPU timezone parity gaps
dan13bauer Jun 23, 2026
d413034
fix(cudf): Close GPU timezone parity gaps for format and parsing
dan13bauer Jun 23, 2026
57eceec
test(cudf): Add pre-1970 coverage and tidy the extraction test
dan13bauer Jun 23, 2026
5142afa
refactor(cudf): Rename timezone helpers and apply style nits
dan13bauer Jun 23, 2026
3c0bf51
Merge branch 'main' into cudf-timezone-support
dan13bauer Jun 23, 2026
0b1351c
test(cudf): Cover from_iso8601 DST session-zone conversions
dan13bauer Jun 24, 2026
d319e3c
fix(cudf): Convert session-zone instants with a tzdb offset table
dan13bauer Jun 24, 2026
1417819
refactor(cudf): Build from_iso8601 regex once in the constructor
dan13bauer Jun 24, 2026
850adba
feat(cudf): Honor session timezone in join and scan filters
dan13bauer Jun 25, 2026
39c6118
fix(cudf): Reject now() when the session timezone is unusable
dan13bauer Jun 26, 2026
c2b0fab
test(cudf): Cover session timezone in the filter precompute
dan13bauer Jun 26, 2026
a51a9c4
Merge branch 'main' into cudf-timezone-support
dan13bauer Jul 15, 2026
b4c0342
refactor(cudf): rename CudfExpressionContext to CudfDateTimeContext
dan13bauer Jul 17, 2026
a3798ea
fix(cudf): use checked arithmetic for from_unixtime offset
dan13bauer Jul 17, 2026
1dcb49f
fix(cudf): preserve parsed offset in parse_datetime
dan13bauer Jul 17, 2026
ddc5e62
feat(cudf): per-row multi-zone timezone functions
dan13bauer Jul 17, 2026
474fead
fix(cudf): from_iso8601_timestamp matches CPU parse/throw contract
dan13bauer Jul 17, 2026
cd8a779
Merge branch 'main' into cudf-timezone-support
dan13bauer Jul 17, 2026
0e3eaab
Merge branch 'main' into cudf-timezone-support
dan13bauer Jul 21, 2026
b0d5900
feat(cudf): timezone-aware date_trunc(timestamp) on GPU
dan13bauer Jul 21, 2026
f471946
refactor(cudf): extract shared TimestampWithTimeZoneColumn helpers
dan13bauer Jul 22, 2026
1a33267
feat(cudf): timezone-aware date_trunc(timestamp with time zone) on GPU
dan13bauer Jul 22, 2026
3510306
feat(cudf): timezone-aware date_add(unit, value, timestamp) on GPU
dan13bauer Jul 22, 2026
4b1cb73
feat(cudf): GPU date_add(unit, value, timestamp with time zone)
dan13bauer Jul 23, 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
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,16 @@ CudfHiveDataSource::CudfHiveDataSource(
}

auto const remainingFilterType = getTableRowType();
// The connector exposes the session timezone directly rather than through a
// QueryConfig. sessionStartTimeMs is unused for filter pushdown (no now() /
// current_timestamp here), so it is left at 0.
const velox::cudf_velox::CudfDateTimeContext context{
connectorQueryCtx_->sessionTimezone(),
connectorQueryCtx_->adjustTimestampToTimezone(),
0,
};
cudfExpressionEvaluator_ = velox::cudf_velox::createCudfExpression(
remainingFilterExprSet_->exprs()[0], remainingFilterType);
remainingFilterExprSet_->exprs()[0], remainingFilterType, context);
// TODO(kn): Get column names and subfields from remaining filter and add to
// readColumnNames_
}
Expand Down
53 changes: 12 additions & 41 deletions velox/experimental/cudf/exec/CudfFilterProject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@
#include "velox/experimental/cudf/CudfNoDefaults.h"
#include "velox/experimental/cudf/exec/CudfFilterProject.h"
#include "velox/experimental/cudf/exec/GpuResources.h"
#include "velox/experimental/cudf/exec/Validation.h"
#include "velox/experimental/cudf/exec/VeloxCudfInterop.h"
#include "velox/experimental/cudf/expression/DateTruncFunction.h"
#include "velox/experimental/cudf/vector/CudfVector.h"

#include "velox/common/memory/Memory.h"
Expand Down Expand Up @@ -53,28 +51,6 @@ void debugPrintTree(
}
}

bool isTimezoneSensitiveDateTrunc(
const std::shared_ptr<velox::exec::Expr>& expr) {
const auto dateTruncName =
CudfConfig::getInstance().functionNamePrefix + "date_trunc";
return expr->name() == dateTruncName &&
DateTruncFunction::isTimezoneSensitive(expr);
}

bool containsTimezoneSensitiveDateTrunc(
const std::shared_ptr<velox::exec::Expr>& expr) {
if (isTimezoneSensitiveDateTrunc(expr)) {
return true;
}

for (const auto& input : expr->inputs()) {
if (containsTimezoneSensitiveDateTrunc(input)) {
return true;
}
}
return false;
}

bool checkAddIdentityProjection(
const core::TypedExprPtr& projection,
const RowTypePtr& inputType,
Expand Down Expand Up @@ -145,19 +121,7 @@ bool canBeEvaluatedByCudf(
std::unique_ptr<exec::ExprSet> exprSet = exec::makeExprSetFromFlag(
std::move(exprsCopy), &precompileCtx, lazyDereference);

const core::QueryConfig defaultQueryConfig = core::QueryConfig({});
const core::QueryConfig& queryConfig =
queryCtx ? queryCtx->queryConfig() : defaultQueryConfig;
const bool adjustTimestampToTimezone =
queryConfig.adjustTimestampToTimezone();

for (const auto& e : exprSet->exprs()) {
if (adjustTimestampToTimezone && containsTimezoneSensitiveDateTrunc(e)) {
LOG_FALLBACK(
"date_trunc(timestamp) requires CPU evaluation when "
"adjust_timestamp_to_session_timezone is enabled");
return false;
}
if (!canBeEvaluatedByCudf(e)) {
return false;
}
Expand Down Expand Up @@ -233,6 +197,12 @@ void CudfFilterProject::initialize() {
const auto inputType = project_ ? project_->sources()[0]->outputType()
: filter_->sources()[0]->outputType();

// Capture the session timezone so timezone-aware GPU functions (date/time
// extraction, the TIMESTAMP WITH TIME ZONE family, temporal casts) match the
// CPU path.
const auto exprContext =
contextFromConfig(operatorCtx_->driverCtx()->queryConfig());

// convert to AST
if (CudfConfig::getInstance().debugEnabled) {
int i = 0;
Expand All @@ -243,21 +213,22 @@ void CudfFilterProject::initialize() {
}
if (hasFilter_) {
// First expr is Filter, rest are Project
filterEvaluator_ = createCudfExpression(expr->exprs()[0], inputType);
filterEvaluator_ =
createCudfExpression(expr->exprs()[0], inputType, exprContext);
std::transform(
expr->exprs().begin() + 1,
expr->exprs().end(),
std::back_inserter(projectEvaluators_),
[inputType](const auto& expr) {
return createCudfExpression(expr, inputType);
[&](const auto& expr) {
return createCudfExpression(expr, inputType, exprContext);
});
} else {
std::transform(
expr->exprs().begin(),
expr->exprs().end(),
std::back_inserter(projectEvaluators_),
[inputType](const auto& expr) {
return createCudfExpression(expr, inputType);
[&](const auto& expr) {
return createCudfExpression(expr, inputType, exprContext);
});
}

Expand Down
16 changes: 13 additions & 3 deletions velox/experimental/cudf/exec/CudfHashJoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

#include "velox/common/testutil/TestValue.h"
#include "velox/core/PlanNode.h"
#include "velox/exec/Driver.h"
#include "velox/exec/Task.h" // NOLINT(misc-unused-headers)
#include "velox/type/TypeUtil.h"

Expand Down Expand Up @@ -490,9 +491,16 @@ void CudfHashJoinProbe::initialize() {
// Create a reusable evaluator for the filter column. This is expensive to
// build, and the expression + input schema are stable for the lifetime of
// the operator instance.
// Resolve the session timezone once so timezone-sensitive CudfFunctions in
// the join filter receive it at construction.
const auto context =
contextFromConfig(operatorCtx_->driverCtx()->queryConfig());

std::vector<velox::RowTypePtr> filterRowTypes{probeType_, buildType_};
filterEvaluator_ = createCudfExpression(
exprs.exprs()[0], facebook::velox::type::concatRowTypes(filterRowTypes));
exprs.exprs()[0],
facebook::velox::type::concatRowTypes(filterRowTypes),
context);

// Check if the filter expression spans both join sides (e.g., switch
// expressions referencing columns from both probe and build). If so, we
Expand Down Expand Up @@ -521,7 +529,8 @@ void CudfHashJoinProbe::initialize() {
buildType_,
probeType_,
rightPrecomputeInstructions_,
leftPrecomputeInstructions_);
leftPrecomputeInstructions_,
context);
} else {
createAstTree(
exprs.exprs()[0],
Expand All @@ -530,7 +539,8 @@ void CudfHashJoinProbe::initialize() {
probeType_,
buildType_,
leftPrecomputeInstructions_,
rightPrecomputeInstructions_);
rightPrecomputeInstructions_,
context);
}
}
}
Expand Down
9 changes: 8 additions & 1 deletion velox/experimental/cudf/exec/CudfNestedLoopJoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "velox/experimental/cudf/expression/AstExpressionUtils.h"
#include "velox/experimental/cudf/expression/PrecomputeInstruction.h"

#include "velox/exec/Driver.h"
#include "velox/exec/Task.h"

#include <cudf/ast/expressions.hpp>
Expand Down Expand Up @@ -294,6 +295,11 @@ void CudfNestedLoopJoinProbe::initialize() {
exec::ExprSet exprs({joinNode_->joinCondition()}, operatorCtx_->execCtx());
VELOX_CHECK_EQ(exprs.exprs().size(), 1);

// Resolve the session timezone once so timezone-sensitive CudfFunctions built
// on the precompute path receive it at construction.
const auto context =
contextFromConfig(operatorCtx_->driverCtx()->queryConfig());

// Convert Velox expression to cuDF AST expression tree.
// The AST will be passed to cudf::conditional_inner_join() for GPU
// evaluation.
Expand All @@ -304,7 +310,8 @@ void CudfNestedLoopJoinProbe::initialize() {
probeType_,
buildType_,
leftPrecomputeInstructions_,
rightPrecomputeInstructions_);
rightPrecomputeInstructions_,
context);

// Set hasFilter_ only after the AST has been fully built so that a throw
// from createAstTree() does not leave the operator marked as having a filter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,7 @@ class ArrayAccessFunction : public CudfFunction {

ColumnOrView eval(
std::vector<ColumnOrView>& inputColumns,
[[maybe_unused]] cudf::size_type numRows,
rmm::cuda_stream_view stream,
rmm::device_async_resource_ref mr) const override {
// Case 1: constant array, variable index.
Expand Down
35 changes: 23 additions & 12 deletions velox/experimental/cudf/expression/AstExpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,11 @@ cudf::ast::expression const& createAstTree(
cudf::ast::tree& tree,
std::vector<std::unique_ptr<cudf::scalar>>& scalars,
const RowTypePtr& inputRowSchema,
std::vector<PrecomputeInstruction>& precomputeInstructions) {
AstContext context{
tree, scalars, {inputRowSchema}, {precomputeInstructions}, expr};
return context.pushExprToTree(expr);
std::vector<PrecomputeInstruction>& precomputeInstructions,
const CudfDateTimeContext& context) {
AstContext astContext{
tree, scalars, {inputRowSchema}, {precomputeInstructions}, expr, context};
return astContext.pushExprToTree(expr);
}

cudf::ast::expression const& createAstTree(
Expand All @@ -48,22 +49,30 @@ cudf::ast::expression const& createAstTree(
const RowTypePtr& leftRowSchema,
const RowTypePtr& rightRowSchema,
std::vector<PrecomputeInstruction>& leftPrecomputeInstructions,
std::vector<PrecomputeInstruction>& rightPrecomputeInstructions) {
AstContext context{
std::vector<PrecomputeInstruction>& rightPrecomputeInstructions,
const CudfDateTimeContext& context) {
AstContext astContext{
tree,
scalars,
{leftRowSchema, rightRowSchema},
{leftPrecomputeInstructions, rightPrecomputeInstructions},
expr};
return context.pushExprToTree(expr);
expr,
context};
return astContext.pushExprToTree(expr);
}

ASTExpression::ASTExpression(
std::shared_ptr<velox::exec::Expr> expr,
const RowTypePtr& inputRowSchema)
const RowTypePtr& inputRowSchema,
const CudfDateTimeContext& context)
: expr_(expr), inputRowSchema_(inputRowSchema) {
createAstTree(
expr, cudfTree_, scalars_, inputRowSchema, precomputeInstructions_);
expr,
cudfTree_,
scalars_,
inputRowSchema,
precomputeInstructions_,
context);
}

void ASTExpression::close() {
Expand Down Expand Up @@ -134,8 +143,10 @@ void registerAstEvaluator(int priority) {
[](std::shared_ptr<velox::exec::Expr> expr) {
return ASTExpression::canEvaluate(expr);
},
[](std::shared_ptr<velox::exec::Expr> expr, const RowTypePtr& row) {
return std::make_shared<ASTExpression>(std::move(expr), row);
[](std::shared_ptr<velox::exec::Expr> expr,
const RowTypePtr& row,
const CudfDateTimeContext& context) {
return std::make_shared<ASTExpression>(std::move(expr), row, context);
},
/*overwrite=*/false);
}
Expand Down
9 changes: 6 additions & 3 deletions velox/experimental/cudf/expression/AstExpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ cudf::ast::expression const& createAstTree(
cudf::ast::tree& tree,
std::vector<std::unique_ptr<cudf::scalar>>& scalars,
const RowTypePtr& inputRowSchema,
std::vector<PrecomputeInstruction>& precomputeInstructions);
std::vector<PrecomputeInstruction>& precomputeInstructions,
const CudfDateTimeContext& context);

cudf::ast::expression const& createAstTree(
const std::shared_ptr<velox::exec::Expr>& expr,
Expand All @@ -38,7 +39,8 @@ cudf::ast::expression const& createAstTree(
const RowTypePtr& leftRowSchema,
const RowTypePtr& rightRowSchema,
std::vector<PrecomputeInstruction>& leftPrecomputeInstructions,
std::vector<PrecomputeInstruction>& rightPrecomputeInstructions);
std::vector<PrecomputeInstruction>& rightPrecomputeInstructions,
const CudfDateTimeContext& context);

// Evaluates the expression tree
class ASTExpression : public CudfExpression {
Expand All @@ -48,7 +50,8 @@ class ASTExpression : public CudfExpression {
// precompute instructions and stores them
ASTExpression(
std::shared_ptr<velox::exec::Expr> expr,
const RowTypePtr& inputRowSchema);
const RowTypePtr& inputRowSchema,
const CudfDateTimeContext& context);

// Evaluates the expression tree for the given input columns
ColumnOrView eval(
Expand Down
6 changes: 5 additions & 1 deletion velox/experimental/cudf/expression/AstExpressionUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,10 @@ struct AstContext {
precomputeInstructions;
const std::shared_ptr<velox::exec::Expr>
rootExpr; // Track the root expression
// Query-scoped context threaded into timezone-sensitive functions built on
// the precompute path (e.g. date_format or a VARCHAR->TIMESTAMP cast inside a
// join condition).
CudfDateTimeContext context;
bool allowPureAstOnly;

cudf::ast::expression const& pushExprToTree(
Expand Down Expand Up @@ -580,7 +584,7 @@ cudf::ast::expression const& AstContext::pushExprToTree(
if (sideIdx < 0) {
sideIdx = 0; // Default to left side if no fields found
}
auto node = createCudfExpression(expr, inputRowSchema[sideIdx]);
auto node = createCudfExpression(expr, inputRowSchema[sideIdx], context);
return addPrecomputeInstructionOnSide(sideIdx, 0, name, "", node);
}
VELOX_FAIL("Unsupported expression: {}", name);
Expand Down
4 changes: 4 additions & 0 deletions velox/experimental/cudf/expression/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,21 @@ add_library(
PrestoFunctions.cpp
prestosql/DateAddFunction.cpp
prestosql/DatePlusIntervalFunction.cpp
prestosql/TimezoneFunctions.cpp
SparkFunctions.cpp
sparksql/DateAddFunction.cpp
sparksql/HashFunction.cpp
sparksql/SubStringFunction.cpp
SubfieldFiltersToAst.cpp
TimestampWithTimeZoneColumn.cpp
TimezoneConversion.cpp
)

target_link_libraries(
velox_cudf_expression
PUBLIC cudf::cudf
PRIVATE arrow velox_common_base velox_cudf_vector velox_exception
velox_presto_types
)

target_compile_options(velox_cudf_expression PRIVATE -Wno-missing-field-initializers)
Loading
Loading