HornOptimizationContext.java
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.doris.horn;
import org.apache.doris.nereids.CascadesContext;
import org.apache.doris.nereids.hint.LeadingHint;
import org.apache.doris.nereids.trees.expressions.Slot;
import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
import org.apache.horn4j.thrift.TEngineType;
import org.apache.horn4j.thrift.TExplainLevel;
import org.apache.horn4j.thrift.TQueryOptionConfig;
import org.apache.horn4j.thrift.TTable;
import org.apache.horn4j.thrift.TTableBatch;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Horn optimization context shared across the input-translation, JNI, and
* output-rebuild phases.
*/
public class HornOptimizationContext {
private final CascadesContext cascadesContext;
/** Table metadata + statistics. */
private TTableBatch tableBatch = new TTableBatch();
/** Horn optimizer configuration. */
private TQueryOptionConfig queryOptionConfig;
/**
* forward 每个 LogicalOlapScan 的裁剪后分区集,按 relationId 索引(Horn 只透传 relationId,
* 不进 thrift)。backward DorisPhysicalPlanBuilder.buildScan 用它重建 scan 分区。
*/
private final Map<Long, List<Long>> scanPrunedPartitions = new HashMap<>();
/**
* backward 阶段 horn scalar_unique_id → 反译出的 Doris Slot 全局映射,保证上下游 plan 节点的
* slot 引用一致({@link org.apache.doris.horn.horn2doris.DorisPhysicalPlanBuilder} 写入、
* {@link org.apache.doris.horn.horn2doris.DorisExpressionBuilder} 反译 ColumnRef 时按 uid 查)。
*/
private final Map<Long, Slot> scalarUidToSlot = new HashMap<>();
/**
* backward 阶段 horn agg fn 的 scalar_unique_id → 反译出的 Doris AggregateFunction,专给两阶段
* agg 用:local agg put、global agg 按 uid get 回同一实例(Doris 用引用关联 phase 间的同一 fn)。
*/
private final Map<Long, AggregateFunction> scalarUidToAggFunc = new HashMap<>();
public HornOptimizationContext(CascadesContext cascadesContext) {
this.cascadesContext = cascadesContext;
this.queryOptionConfig = buildDefaultQueryOptionConfig();
applyLeadingHint(this.queryOptionConfig);
}
/**
* 把 SQL 的 {@code LEADING(...)} hint 转发给 horn,让内部 {@code XformLeadingJoin} 按指定顺序重排 join。
*/
private void applyLeadingHint(TQueryOptionConfig config) {
if (!cascadesContext.isLeadingJoin()) {
return;
}
LeadingHint leading = (LeadingHint) cascadesContext.getHintMap().get("Leading");
if (leading != null && !leading.getTablelist().isEmpty()) {
config.setJoin_leading_string(new ArrayList<>(leading.getTablelist()));
}
}
public CascadesContext getCascadesContext() {
return cascadesContext;
}
public TTableBatch getTableBatch() {
return tableBatch;
}
public void setTableBatch(TTableBatch tableBatch) {
this.tableBatch = tableBatch;
}
public void putScanPrunedPartitions(long relationId, List<Long> prunedPartitionIds) {
scanPrunedPartitions.put(relationId, prunedPartitionIds);
}
public List<Long> getScanPrunedPartitions(long relationId) {
return scanPrunedPartitions.get(relationId);
}
public void addTable(TTable ttable) {
if (tableBatch.getTables() == null) {
tableBatch.setTables(new ArrayList<>());
}
tableBatch.getTables().add(ttable);
}
public Map<Long, Slot> getScalarUidToSlot() {
return scalarUidToSlot;
}
public Map<Long, AggregateFunction> getScalarUidToAggFunc() {
return scalarUidToAggFunc;
}
public TQueryOptionConfig getQueryOptionConfig() {
return queryOptionConfig;
}
public TExplainLevel getExplainLevel() {
return TExplainLevel.EXTRACTED_PLAN;
}
private TQueryOptionConfig buildDefaultQueryOptionConfig() {
TQueryOptionConfig config = new TQueryOptionConfig();
config.setHorn_dphyper_enable(true);
config.setHorn_statistics_default_selectivity(0.4);
config.setHorn_statistics_damping_factor_filter(0.75);
config.setHorn_statistics_damping_factor_groupby(0.75);
config.setHorn_statistics_damping_factor_join(0.1);
// 默认允许无统计的表也走 horn(用 default selectivity 估算)——否则缺 stats 的表
// 会大量 fallback Nereids。严格 stats 后续由 session var 控制。
config.setEnable_horn_no_statistics_support(true);
config.setLocal_time_zone(cascadesContext.getConnectContext()
.getSessionVariable().getTimeZone());
config.setMt_dop(1);
config.setEngine_type(TEngineType.kDoris);
boolean detailInfo = cascadesContext.getConnectContext()
.getSessionVariable().enableHornDetailInfo;
config.setEnable_horn_cascade_log(detailInfo);
config.setEnable_horn_cascade_detail_log(detailInfo);
config.setEnable_horn_stats_log(detailInfo);
config.setHorn_print_memo_after_optimization(detailInfo); // detail 模式下 dump memo
config.setEnable_horn_profile(cascadesContext.getConnectContext()
.getSessionVariable().enableHornProfile);
config.setExplain_level(getExplainLevel());
return config;
}
}