NVIDIA / NVIDIA/cudf

[FEA] AST should support `CAST_TO_DECIMAL{32,64,128}` (cast-to-fixed-point in `compute_column` / `compute_column_jit`)

Open
#22,507 1 comment 0 reactions 0 assignees View on GitHub
feature request libcudf
Dominant language
C++
Stars
9.8k
Forks
1.1k
Avg merge
3d 6m
Merged PRs (30d)
278

Description

## Feature request

`cudf::ast` exposes three cast operators today:

```82:84:/home/matal/main/cudf/cpp/include/cudf/ast/ast_operator.hpp
CAST_TO_INT64, ///< Cast value to int64_t
CAST_TO_UINT64, ///< Cast value to uint64_t
CAST_TO_FLOAT64 ///< Cast value to double
```

Notably absent: a cast targeting a fixed-point/decimal output. There is no way to express *"cast this `int64`/`float64`/`decimal_X` operand to `decimal64:-S`"* inside a `compute_column` / `compute_column_jit` expression today.

## Why this matters

Most SQL frontends emit cast-to-decimal as a routine planning operation. In every case the consumer ends up materializing an extra intermediate column whose only purpose is to hold the rescaled values, because the AST evaluator can't perform the cast itself.

The shape of expression that's blocked today:

```
column_a [int32] < literal[0.5, decimal64:-2] * column_b [decimal64:-2]
```

…requires the AST to cast `column_a` to a decimal before comparing, and it can't.

## Repro: what fails today

```cpp
#include
#include

// `column_a` is INT32, `column_b` is DECIMAL64:-2.
// Goal: column_a < column_b (with column_a rescaled to decimal64:-2).
auto col_a = cudf::ast::column_reference(0);
auto col_b = cudf::ast::column_reference(1);
auto lt = cudf::ast::operation(cudf::ast::ast_operator::LESS, col_a, col_b);
auto out = cudf::compute_column(table, lt);
// → CUDF failure: "An AST expression was provided non-matching operand types."
```

The only way to make this work today is to materialize the cast before the AST:

```cpp
auto col_a_dec = cudf::cast(table.column(0), cudf::data_type{cudf::type_id::DECIMAL64, -2});
auto table2 = cudf::table_view{{col_a_dec->view(), table.column(1)}};
// ...then run the AST against table2.
```

## Design challenge: target scale

Unlike `CAST_TO_INT64` etc., a cast-to-decimal needs a runtime parameter: the **target scale**. The existing AST cast operators are constant in the enum because their target type is fully determined. Decimals aren't.

One viable shape: a new expression node parallel to `cudf::ast::operation` / `cudf::ast::literal`, e.g.

```cpp
namespace cudf::ast {

class cast : public expression {
public:
cast(expression const& operand, cudf::data_type target_type);
// target_type carries scale for decimal targets, ignored otherwise
...
};

} // namespace cudf::ast
```

## Workarounds today (and their cost)

| Workaround | Cost |
|---|---|
| Pre-materialize the cast outside the AST (cudf-polars #20060, GQE) | Extra kernel + device buffer + planner branch per cast site |
| Cast both sides up to FLOAT64 inside the AST via `CAST_TO_FLOAT64` | Lossy past ~15.9 mantissa digits; reverts comparison to float semantics; defeats the purpose of using decimal in the first place |

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.