MCPcopy Create free account
hub / github.com/ClickHouse/ClickHouse / makePipe

Method makePipe

src/Processors/QueryPlan/ReadFromSystemNumbersStep.cpp:461–691  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

459}
460
461Pipe ReadFromSystemNumbersStep::makePipe()
462{
463 auto & numbers_storage = storage->as<StorageSystemNumbers &>();
464
465 /// For non-multithreaded variants (`system.numbers` / `numbers(...)`), keep a single stream to preserve ordering.
466 if (!numbers_storage.multithreaded)
467 num_streams = 1;
468
469 Pipe pipe;
470 const auto header = NumbersSource::createHeader(numbers_storage.column_name);
471
472 auto add_null_source = [&] { NumbersLikeUtils::addNullSource(pipe, header); };
473
474 /// Pushdown rationale:
475 /// - Filter pushdown:
476 /// - `numbers` is a value-domain source. For bounded sources (`numbers(limit)` / `numbers(offset, limit[, step])`),
477 /// storage arguments define the table domain (with step and possible wrap), and extracted ranges are intersected
478 /// with that domain to preserve semantics while reducing generation.
479 /// Example: `SELECT number FROM numbers(10, 10) WHERE number >= 15` should read only `[15, 19]`.
480 /// - For unbounded sources (`system.numbers(_mt)` / `numbers(_mt)()`), if no useful bounds are extracted
481 /// and query LIMIT/OFFSET cannot be safely pushed down, we keep the fast unbounded generator.
482 /// Otherwise we use ranged generation from extracted ranges/bounds (or from pushed query LIMIT/OFFSET).
483 /// Example: `SELECT number FROM system.numbers WHERE number BETWEEN 100 AND 130`.
484 /// - LIMIT/OFFSET pushdown:
485 /// - Safe only when extracted ranges are exact. With conservative bounds, pre-filter LIMIT may stop too early.
486 /// Example: `SELECT number FROM system.numbers WHERE number % 3 = 1 AND number < 100 LIMIT 5`.
487 /// Here `number < 100` gives only a conservative bound, so pushing LIMIT first could return too few rows
488 /// (e.g. from `[0, 1, 2, 3, 4]` we keep only `[1, 4]` instead of `[1, 4, 7, 10, 13]`).
489 ///
490 /// Storage-level LIMIT 0 (`numbers(..., 0, ...)`) is an empty table regardless of the WHERE clause.
491 if (numbers_storage.limit.has_value() && (numbers_storage.limit.value() == 0))
492 {
493 add_null_source();
494 return pipe;
495 }
496
497 chassert(numbers_storage.step != UInt64{0});
498
499 /// Descending series (e.g. `generate_series(10, 0, -1)`): compute exact output count
500 /// from domain size and step, then use `SimpleSteppedNumbersSource` with a wrapping step.
501 /// The step stored in storage is always the positive absolute value; the wrapping trick
502 /// (UInt64(0) - step) makes unsigned addition equivalent to subtraction.
503 /// TODO: Support filter pushdown for negative step as well.
504 if (numbers_storage.descending)
505 {
506 chassert(numbers_storage.limit.has_value());
507 UInt128 domain_size = *numbers_storage.limit;
508 UInt128 count128 = (domain_size + numbers_storage.step - 1) / numbers_storage.step;
509
510 chassert(count128 <= std::numeric_limits<UInt64>::max());
511
512 UInt64 total_count = static_cast<UInt64>(count128);
513
514 /// We cannot push down LIMIT if we have a filter. Consider:
515 /// SELECT * FROM generate_series(10, 0, -1) WHERE generate_series < 3 LIMIT 1
516 /// Pushing LIMIT 1 would generate only {10}, which the filter discards, returning
517 /// empty instead of the correct {2}.
518 if (limit.has_value() && !filter_actions_dag)

Callers

nothing calls this directly

Calls 15

addNullSourceFunction · 0.85
checkLimitsFunction · 0.85
extractRangesFunction · 0.85
isAlwaysFalseFunction · 0.85
isUniverseFunction · 0.85
steppedRangeFromRangeFunction · 0.85
FieldRefClass · 0.85
sizeOfRangesFunction · 0.85
shrinkRangesFunction · 0.85
maxFunction · 0.50
minFunction · 0.50
RangeClass · 0.50

Tested by

no test coverage detected