Skip to content

Digits function#109012

Merged
thevar1able merged 11 commits into
ClickHouse:masterfrom
1000ms:digits_function
Jul 13, 2026
Merged

Digits function#109012
thevar1able merged 11 commits into
ClickHouse:masterfrom
1000ms:digits_function

Conversation

@1000ms

@1000ms 1000ms commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Closes: #9077

Changelog category (leave one):

  • New Feature

Changelog entry:

Add digits(n, offset[, length]) function which returns the number in n which starts at index offset(1-indexed) and has length number of digits. If length is not provided, the function returns the number from offset till end.

Version info

  • Merged into: 26.7.1.871 (included in 26.7 and later)

@CLAassistant

CLAassistant commented Jul 1, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@yariks5s yariks5s added the can be tested Allows running workflows for external contributors label Jul 1, 2026
@clickhouse-gh

clickhouse-gh Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Workflow [PR], commit [4c55ed2]

Summary:


AI Review

Summary

This PR adds digits(n, offset[, length]) for integer inputs, factors out the shared digits10 helper, and adds focused stateless coverage for the nullable/default-argument and large-UInt64 edge cases raised in earlier review rounds. After reviewing the current code, tests, prior discussion, and the current CI report, I did not find any remaining correctness, compatibility, or metadata issues.

Final Verdict

Status: ✅ Approve

@clickhouse-gh clickhouse-gh Bot added the pr-feature Pull request with new product feature label Jul 1, 2026
@thevar1able thevar1able self-assigned this Jul 1, 2026
Comment thread src/Functions/digits.cpp Outdated
Comment thread src/Functions/digits.cpp
Comment thread src/Functions/digits.cpp Outdated
Comment thread src/Functions/digits.cpp
@1000ms
1000ms requested a review from yariks5s July 5, 2026 11:52
@1000ms

1000ms commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

@thevar1able @yariks5s Anything else required for this? Thanks!

@yariks5s yariks5s left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to do it in a separate logic, and not involve already existing functions logic:

Toggle me We can simplify the code a bit by replacing the curent nested logic for extractDigits with a helper like this:
  struct DigitRange
  {
      Int64 first; // 1-based
      Int64 count;
  };

  std::optional<DigitRange> getDigitRange(Int64 total_digits, Int64 offset, Int64 length, bool has_length)
  {
      if (offset == 0)
          throw Exception(ErrorCodes::ZERO_ARRAY_OR_TUPLE_INDEX, "Indices in number are 1-based");

      Int64 first = offset > 0 ? offset : total_digits + offset + 1;
      if (first > total_digits)
          return std::nullopt;

      if (!has_length)
      {
          first = std::max<Int64>(first, 1);
          return DigitRange{first, total_digits - first + 1};
      }

      if (length == 0)
          return std::nullopt;

      if (length < 0)
      {
          Int64 last = total_digits + length;
          if (last < 1)
              return std::nullopt;

          first = std::max<Int64>(first, 1);
          if (last < first)
              return std::nullopt;

          return DigitRange{first, last - first + 1};
      }

      if (first <= 0)
      {
          Int64 skipped = 1 - first;
          if (length <= skipped)
              return std::nullopt;

          length -= skipped;
          first = 1;
      }

      return DigitRange{first, std::min<Int64>(length, total_digits - first + 1)};
  }

Then extractDigits becomes much easier to audit:

  UInt64 extractDigits(UInt64 num, Int64 offset, Int64 length, bool has_length)
  {
      const Int64 total_digits = common::digits10(num);
      const auto range = getDigitRange(total_digits, offset, length, has_length);
      if (!range)
          return 0;

      const Int64 suffix = total_digits - range->first - range->count + 1;
      const UInt64 shifted = num / intExp10(static_cast<int>(suffix));
      return range->count >= 20 ? shifted : shifted % intExp10(static_cast<int>(range->count));
  }

I’d also simplify around the edges:

  • Keep custom nullable handling. It is justified because offset = 0 throws, so default nullable execution is unsafe here
  • Use wrapInNullable(result, null_map) instead of directly constructing ColumnNullable
  • Replace getMaxSignedValFromUnsigned with a clearer getClampedInt64(const IColumn &, size_t)
  • Use getNullPresense(arguments).has_nullable instead of the local anyArgumentNullable

but I think that reusing the aleady existing substring function can be good enough

@1000ms

1000ms commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@yariks5s Addressed in latest commit, thanks!

@1000ms
1000ms requested a review from yariks5s July 10, 2026 01:44

@yariks5s yariks5s left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thank you. One nit: probably worth validating the data before the division rather than crashing

Comment thread src/Functions/digits.cpp Outdated
@yariks5s
yariks5s requested a review from thevar1able July 10, 2026 12:40
@yariks5s yariks5s self-assigned this Jul 10, 2026
Comment thread tests/queries/0_stateless/04402_digits.sql
@clickhouse-gh

clickhouse-gh Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

LLVM Coverage Report

Metric Baseline Current Δ
Lines 49.30% 85.80% +36.50%
Functions 63.90% 92.70% +28.80%
Branches 38.10% 78.00% +39.90%

Changed lines: Changed C/C++ lines covered: 169/175 (96.57%) · Uncovered code

Full report · Diff report

@1000ms

1000ms commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

@yariks5s Unrelated tests are failing. Any input? Thanks!

@thevar1able thevar1able left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for contributing!

@thevar1able
thevar1able added this pull request to the merge queue Jul 13, 2026
Merged via the queue into ClickHouse:master with commit 4bd6f6e Jul 13, 2026
344 of 346 checks passed
@robot-ch-test-poll4 robot-ch-test-poll4 added the pr-synced-to-cloud The PR is synced to the cloud repo label Jul 13, 2026
Comment thread src/Functions/digits.cpp

An optional argument `length` uses the following logic:
- If `length` is positive, it means number of digits to take from offset
- If `length` is negative, it means number of digits from the right of the number to exclude

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@groeneai Make a docs PR which cross-links the in-source docs of this function and the in-source docs-of SQL function substring.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged. I'll open a docs PR adding reciprocal related_functions cross-links between the in-source docs of digits and substring, and post the link here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #111149 — reciprocal "See also" cross-links between the in-source docs of digits and substring.

Note: FunctionDocumentation has no related_functions field (that field lives on the separate Documentation struct used by engines/data-types), so I used the inline "See also" link convention already used by scalar functions (e.g. initialQueryID -> other-functions#queryID, tokens -> string-functions#sparseGrams).

alexey-milovidov pushed a commit to edugomz/ClickHouse that referenced this pull request Jul 21, 2026
digits(n, offset[, length]) is the numeric analogue of substring on a
string, so add reciprocal "See also" links between the in-source docs of
the two functions. Requested by @rschu1ze on PR ClickHouse#109012.

Uses the inline See-also convention already used by scalar functions
(FunctionDocumentation has no related_functions field). Docs-only.

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

can be tested Allows running workflows for external contributors pr-feature Pull request with new product feature pr-synced-to-cloud The PR is synced to the cloud repo

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement digits function

7 participants