| 701 | } |
| 702 | |
| 703 | UInt64 mainQueryNodeBlockSizeByLimit(const SelectQueryInfo & select_query_info) |
| 704 | { |
| 705 | // Since we support negative limit, query node field could potentially be Int64 implying negative value. |
| 706 | // So, we have to handle to separately |
| 707 | auto const & main_query_node = select_query_info.query_tree->as<QueryNode const &>(); |
| 708 | |
| 709 | /// Constness of limit and offset is validated during query analysis stage |
| 710 | UInt64 limit_length = 0; |
| 711 | if (main_query_node.hasLimit()) |
| 712 | { |
| 713 | const auto & field = main_query_node.getLimit()->as<ConstantNode &>().getValue(); |
| 714 | |
| 715 | const bool is_uint64 = !convertFieldToType(field, DataTypeUInt64()).isNull(); |
| 716 | |
| 717 | // Negative LIMIT, skip optimization |
| 718 | if (!is_uint64) |
| 719 | return 0; |
| 720 | |
| 721 | limit_length = field.safeGet<UInt64>(); |
| 722 | } |
| 723 | |
| 724 | UInt64 limit_offset = 0; |
| 725 | if (main_query_node.hasOffset()) |
| 726 | { |
| 727 | const auto & field = main_query_node.getOffset()->as<ConstantNode &>().getValue(); |
| 728 | const bool is_uint64 = !convertFieldToType(field, DataTypeUInt64()).isNull(); |
| 729 | |
| 730 | // Negative OFFSET, skip optimization |
| 731 | if (!is_uint64) |
| 732 | return 0; |
| 733 | |
| 734 | limit_offset = field.safeGet<UInt64>(); |
| 735 | } |
| 736 | |
| 737 | /// `arrayJoin` in the projection expands one input row into several output rows after the |
| 738 | /// source has run. Capping the source to `limit + offset` rows would truncate input BEFORE |
| 739 | /// expansion, so hard consumers of `trivial_limit` (StorageLoop, system.zeros, generateRandom) |
| 740 | /// could drop output rows that the LIMIT should keep. See issue #82279 and the sibling guard |
| 741 | /// in `numbersLikeUtils::shouldPushdownLimit`. (The `ARRAY JOIN` clause is lowered to a |
| 742 | /// separate table expression in the analyzer, so it is not a single-table read and never |
| 743 | /// reaches this optimization.) |
| 744 | if (hasFunctionNode(main_query_node.getProjectionNode(), "arrayJoin")) |
| 745 | return 0; |
| 746 | |
| 747 | /** If not specified DISTINCT, WHERE, GROUP BY, HAVING, ORDER BY, JOIN, LIMIT BY, LIMIT WITH TIES |
| 748 | * but LIMIT is specified with UInt64 value, and limit + offset < max_block_size, |
| 749 | * then as the block size we will use limit + offset (not to read more from the table than requested), |
| 750 | * and also set the number of threads to 1. |
| 751 | */ |
| 752 | if (main_query_node.hasLimit() |
| 753 | && !main_query_node.isDistinct() |
| 754 | && !main_query_node.isLimitWithTies() |
| 755 | && !main_query_node.hasPrewhere() |
| 756 | && !main_query_node.hasWhere() |
| 757 | && select_query_info.filter_asts.empty() |
| 758 | && !main_query_node.hasGroupBy() |
| 759 | && !main_query_node.hasHaving() |
| 760 | && !main_query_node.hasOrderBy() |
no test coverage detected