| 1595 | } |
| 1596 | |
| 1597 | void addBuildSubqueriesForSetsStepIfNeeded( |
| 1598 | QueryPlan & query_plan, |
| 1599 | const SelectQueryOptions & select_query_options, |
| 1600 | const PlannerContextPtr & planner_context, |
| 1601 | const UsefulSets & useful_sets) |
| 1602 | { |
| 1603 | auto subqueries = planner_context->getPreparedSets().getSubqueries(); |
| 1604 | |
| 1605 | auto predicate = [&useful_sets](const auto & set) { return !useful_sets.contains(set); }; |
| 1606 | auto it = std::remove_if(subqueries.begin(), subqueries.end(), std::move(predicate)); |
| 1607 | subqueries.erase(it, subqueries.end()); |
| 1608 | |
| 1609 | for (auto & subquery : subqueries) |
| 1610 | { |
| 1611 | auto query_tree = subquery->detachQueryTree(); |
| 1612 | auto subquery_options = select_query_options.subquery(); |
| 1613 | /// Sets may use Materialized CTEs, so we need to materialize them in order to correctly build set from subquery. |
| 1614 | /// Normally CTEs are materialized before building sets and running the main query, but in case when |
| 1615 | /// set is built for primary key analysis, CTEs are not materialized yet. |
| 1616 | /// |
| 1617 | /// To build the set correctly, we need to add a DelayedMaterializingCTEsStep for CTEs in the set subquery plan. |
| 1618 | /// This is done by forceMaterializeCTE() method, which would lead collectMaterializedCTEs() to return non-empty result. |
| 1619 | /// |
| 1620 | /// As a result: |
| 1621 | /// 1. If set is built during primary key analysis FutureSetFromSubquery::buildSetInplace(), we will build plans for used CTEs materialization. |
| 1622 | /// Later, when the main query plan is optimized, DelayedMaterializingCTEsStep for these CTEs will not add materialization plans again. |
| 1623 | /// 2. If set is built during main query execution, we will build plans for used CTEs materialization to be run before the set is built |
| 1624 | /// and before the main query is executed. |
| 1625 | subquery_options.forceMaterializeCTE(); |
| 1626 | /// I don't know if this is a good decision, |
| 1627 | /// but for now it is done in the same way as in old analyzer. |
| 1628 | /// This would not ignore limits for subqueries (affects mutations only). |
| 1629 | /// See test_build_sets_from_multiple_threads-analyzer. |
| 1630 | subquery_options.ignore_limits = false; |
| 1631 | Planner subquery_planner( |
| 1632 | query_tree, |
| 1633 | subquery_options, |
| 1634 | std::make_shared<GlobalPlannerContext>( |
| 1635 | nullptr, nullptr, nullptr, collectFiltersForAnalysis(query_tree, subquery_options, nullptr))); |
| 1636 | subquery_planner.buildQueryPlanIfNeeded(); |
| 1637 | |
| 1638 | auto subquery_plan = std::move(subquery_planner).extractQueryPlan(); |
| 1639 | /// Contexts should be copied into the root query plan, because some functions may |
| 1640 | /// be created using them while this subquery plan will be destroyed after |
| 1641 | /// FutureSetFromSubquery::buildSetInplace(). Otherwise, function execution may fail |
| 1642 | /// with a "Context has expired" exception. |
| 1643 | for (const auto & context : subquery_plan.getInterpretersContexts()) |
| 1644 | query_plan.addInterpreterContext(context); |
| 1645 | subquery->setQueryPlan(std::make_unique<QueryPlan>(std::move(subquery_plan))); |
| 1646 | } |
| 1647 | |
| 1648 | if (!subqueries.empty()) |
| 1649 | { |
| 1650 | const auto & settings = planner_context->getQueryContext()->getSettingsRef(); |
| 1651 | SizeLimits network_transfer_limits(settings[Setting::max_rows_to_transfer], settings[Setting::max_bytes_to_transfer], settings[Setting::transfer_overflow_mode]); |
| 1652 | auto prepared_sets_cache = planner_context->getQueryContext()->getPreparedSetsCache(); |
| 1653 | |
| 1654 | auto step = std::make_unique<DelayedCreatingSetsStep>( |
no test coverage detected