Skip to content

Promote ConstantValue to Core with Field-free value accessors - #114666

Open
yakov-olkhovskiy wants to merge 3 commits into
masterfrom
valueref-promote-constantvalue
Open

Promote ConstantValue to Core with Field-free value accessors#114666
yakov-olkhovskiy wants to merge 3 commits into
masterfrom
valueref-promote-constantvalue

Conversation

@yakov-olkhovskiy

Copy link
Copy Markdown
Member

Continue removing DB::Field from constant handling by promoting the owning single-constant type to a shared primitive with Field-free value accessors.

What

  • Move ConstantValue from Analyzer/ to Core/. It bundles {size-1 ColumnConst, DataTypePtr}. It carries a DataTypePtr, and DataTypes depends on Columns, so Core is the correct layer (this also removes a lower layer having to reach up into Analyzer for the type). It stays deliberately distinct from ColumnWithTypeAndName (size-1 const invariant, no name, scalar accessors).
  • Add value accessors that read row 0 of the size-1 column without materializing a Field: isNull, getUInt, getInt, getFloat64, getBool, getDataAt. ConstantNode gains matching delegators, and ConstantNode::getValue now delegates to a single transitional ConstantValue::getField.
  • evaluateConstantExpressionAsColumn now returns ConstantValue instead of std::pair<ColumnPtr, DataTypePtr>. Callers updated: numbers/primes/generateSeries/values table functions, ActionsVisitor, InterpreterSelectQuery (LIMIT/OFFSET), prometheus/timeSeries selectors, and the gtest. The two getStringConstArgument helpers now read the value directly via isNull/getDataAt.

Behavior

No user-visible change: the value is the same size-1 const column with the same exact type, just bundled and readable without a Field. ConstantValue's Field constructor and getField remain as the only Field entry/exit while the analyzer still folds constants into Fields; a later phase removes them.

Verification

gtest_convert_column_to_type, gtest_evaluate_constant_expression, and a numbers/generate_series/values/LIMIT (integer + fractional)/IN stateless spot-check.

Related: #113051

Changelog category (leave one):

  • Not for changelog (changelog entry is not required)

Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):

Not for changelog: internal refactor toward removing DB::Field; no user-visible behavior change.

Continue removing `DB::Field` from constant handling by making the owning
single-constant type (`{size-1 ColumnConst, DataTypePtr}`) a shared primitive
instead of an Analyzer-local helper passed around as a raw pair.

- Move `ConstantValue` from `Analyzer/` to `Core/`. It carries a `DataTypePtr`,
  and `DataTypes` depends on `Columns`, so `Core` is the correct home (it also
  fixes a lower layer having to reach up into `Analyzer` for the type).
- Add value accessors that read row 0 of the size-1 column without materializing
  a `Field`: `isNull`, `getUInt`, `getInt`, `getFloat64`, `getBool`, `getDataAt`.
  `ConstantNode` gains matching delegators, and its `getValue` now delegates to a
  single transitional `ConstantValue::getField`.
- `evaluateConstantExpressionAsColumn` now returns a `ConstantValue` instead of a
  `std::pair<ColumnPtr, DataTypePtr>`; all callers are updated (table functions,
  `ActionsVisitor`, `InterpreterSelectQuery`, prometheus/timeSeries selectors,
  gtest). The two `getStringConstArgument` helpers read the value directly via
  `isNull`/`getDataAt`.

No user-visible behavior change: the value is the same size-1 const column with
the same exact type, just bundled. `ConstantValue`'s `Field` constructor and
`getField` remain as the sole `Field` entry/exit while the analyzer still folds
constants into `Field`s (a later phase removes them).

Verified: `gtest_convert_column_to_type`, `gtest_evaluate_constant_expression`,
and a `numbers`/`generate_series`/`values`/`LIMIT`/`IN` stateless spot-check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@clickhouse-gh

clickhouse-gh Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Workflow [PR], commit [000cc7f]


AI Review

Summary

This PR promotes ConstantValue into Core, replaces raw {ColumnPtr, DataTypePtr} constant-expression results with the owning wrapper, and adds Field-free scalar accessors for the updated call sites. In the current 000cc7f7 diff I did not find remaining correctness or compatibility issues; the earlier compile-time regression and the non-literal Field-bridge regression are both fixed in the current head.

Missing context / blind spots
  • ⚠️ Only the Praktika Build profile diff report was available on the PR while reviewing. Full stateless/integration CI would close the remaining runtime-regression gap for the updated values / table-function / time-series call sites.
Final Verdict

No new findings.

@clickhouse-gh clickhouse-gh Bot added the pr-not-for-changelog This PR should not be mentioned in the changelog label Aug 13, 2026
Comment thread src/Interpreters/evaluateConstantExpression.h Outdated
@clickhouse-gh

clickhouse-gh Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Build profile diff (arm_release)

Comparing dc79add25 with master c3b0aa7b2 (stripped binary size, per-symbol sizes and ThinLTO time; compile times per translation unit against the most recent warmup build that recompiled it).

✅ No significant changes.

Binary sizes
Binary Master PR Δ
programs/clickhouse-stripped 692.29 MiB 689.28 MiB -3.01 MiB (-0.43%)

Only the stripped binary is compared: the official master build keeps debug symbols while PR builds strip them, so the other binaries differ by construction.

Compile time of recompiled translation units

183 translation units recompiled, 1601 s compile time in total, 183 of them have a recent master baseline.

Job report

Address review: `evaluateConstantExpression.h` is included by ~100 translation
units (several via other public headers). Including `Core/ConstantValue.h` there
pulled `Columns/ColumnConst.h` + `DataTypes/IDataType.h` (and their transitive
closure) into every includer - a repo-wide compile-time regression for what is a
local change.

The header only *names* `ConstantValue` (the
`EvaluateConstantExpressionColumnResult` alias and the two function return
declarations, including the `std::optional<...>` one), none of which need a
complete type. Forward-declare `class ConstantValue;` in the header and keep the
full include in `evaluateConstantExpression.cpp` and in the caller TUs that
receive the value by value (table functions, `ActionsVisitor`,
`InterpreterSelectQuery`, prometheus/timeSeries selectors, gtest).

No behavior change. Verified by a full rebuild (all includers compile with the
incomplete type) plus the constant-expression / convert gtests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread src/Interpreters/evaluateConstantExpression.cpp Outdated
Address review: `materializeToField` is the compatibility bridge that preserves
the pre-refactor `Field` semantics for NON-literal constants, and it historically
read the value via `(*result_column)[0]` (the column's `operator[]`). The
ConstantValue refactor switched it to `ConstantValue::getField()`, which uses
`IColumn::get`. For `ColumnConst`, `operator[]` and `get` dispatch to the data
column's separate `operator[]`/`get` virtuals - not guaranteed identical for
wrapped columns - so a bridge meant to stay byte-for-byte compatible must use the
same call it always did.

Read the value here via `(*column_result->getColumn())[0]` again.
`ConstantValue::getField()` (which uses `get`) stays for `ConstantNode::getValue`,
matching that caller's own historical `get` path.

Add `EvaluateConstantExpression.NonLiteralFieldBridge`: on the analyzer path a
non-literal constant reaches this bridge; it checks value preservation, the
`Bool`->`UInt64` canonicalization, and a `Nullable` unwrap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-not-for-changelog This PR should not be mentioned in the changelog

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant