OceanBase源码解读:SQL代码生成器入口 ob_code_generator.cpp
SQL 经过解析、语义分析、改写和优化后,得到逻辑执行计划。代码生成阶段把它翻译成物理执行计划。ob_code_generator.cpp 是编排入口,ob_static_engine_cg.cpp 负责把逻辑算子递归转换为物理算子规约(ObOpSpec)。
关键数据结构
ObCodeGenerator:外层门面,持有最小集群版本和参数存储。ObStaticEngineCG:静态执行引擎生成器,后序遍历逻辑算子树生成物理算子。ObOpSpec:物理算子规约,包含类型、子节点、filters、output、calc_exprs、向量化开关等。ObExpr:运行时表达式对象,与ObRawExpr一一对应。
ObCodeGenerator::generate 主流程
int ObCodeGenerator::generate(const ObLogPlan &log_plan,
ObPhysicalPlan &phy_plan)
{
int ret = OB_SUCCESS;
int64_t batch_size = 0;
const uint64_t cur_cluster_version = CLUSTER_CURRENT_VERSION;
OZ(detect_batch_size(log_plan, batch_size));
if (OB_SUCC(ret) && batch_size > 0) {
log_plan.get_optimizer_context().set_batch_size(batch_size);
phy_plan.set_batch_size(batch_size);
}
if (OB_FAIL(ret)) {
} else if (OB_FAIL(generate_exprs(log_plan, phy_plan, cur_cluster_version))) {
LOG_WARN("fail to get all raw exprs", K(ret));
} else if (OB_FAIL(generate_operators(log_plan, phy_plan, cur_cluster_version))) {
LOG_WARN("fail to generate plan", K(ret));
}
return ret;
}
入口按“探测批次 → 生成表达式 → 生成算子”推进,任何失败都通过 OB_FAIL 瀑布返回。
向量化批次探测
bool has_registered_vec_op = false;
if (OB_ISNULL(log_plan.get_plan_root())) {
ret = OB_ERR_UNEXPECTED;
} else if (OB_FAIL(ObStaticEngineCG::exist_registered_vec_op(
*log_plan.get_plan_root(), true,
has_registered_vec_op))) {
LOG_WARN("check vectorized operator failed", K(ret));
}
bool rowsets_enabled = tenant_config.is_valid()
&& tenant_config->_rowsets_enabled;
if (GET_MIN_CLUSTER_VERSION() >= CLUSTER_VERSION_4_3_3_0 && !vectorize) {
batch_size = (rowsets_enabled && has_registered_vec_op) ? 1 : 0;
} else if (vectorize) {
OZ(expr_cg.detect_batch_size(flattened_exprs, batch_size,
rowsets_max_rows,
tenant_config->_rowsets_target_maxsize,
scan_cardinality,
lob_rowsets_max_rows));
OZ(opt_params->get_integer_opt_param(
ObOptParamHint::ROWSETS_MAX_ROWS, batch_size));
}
只有存在已注册向量化算子且开启 _rowsets_enabled 时才启用向量化;否则退化到 batch_size=0 或 1,保证稳定性。
ObStaticEngineCG 后序遍历生成算子
int ObStaticEngineCG::postorder_generate_op(ObLogicalOperator &op,
ObOpSpec *&spec, ...)
{
int ret = OB_SUCCESS;
const int64_t child_num = op.get_num_of_child();
ObSEArray<ObOpSpec *, 2> children;
for (int64_t i = 0; OB_SUCC(ret) && i < child_num; i++) {
ObLogicalOperator *child_op = op.get_child(i);
ObOpSpec *child_spec = NULL;
if (OB_FAIL(SMART_CALL(postorder_generate_op(*child_op, child_spec, ...)))) {
LOG_WARN("generate child op failed", K(ret), K(op.get_name()));
} else if (OB_FAIL(children.push_back(child_spec))) {
LOG_WARN("array push back failed", K(ret));
}
}
ObPhyOperatorType type = PHY_INVALID;
if (OB_FAIL(get_phy_op_type(op, type, in_root_job,
plan_use_rich_format && !op_disable_vectorize))) {
LOG_WARN("get phy op type failed", K(ret));
} else if (OB_FAIL(phy_plan_->alloc_op_spec_for_cg(
&op, schema_guard, plan_use_rich_format,
type, children.count(), spec,
op.get_op_id()))) {
LOG_WARN("allocate operator spec failed", K(ret));
} else {
for (int64_t i = 0; i < children.count() && OB_SUCC(ret); i++) {
if (OB_FAIL(spec->set_child(i, children.at(i)))) { ... }
}
}
...
}
先递归生成子节点,再根据逻辑算子类型选择物理算子类型并分配 ObOpSpec,最后挂载子节点。
公共属性与 calc_exprs
int ObStaticEngineCG::generate_spec_basic(ObLogicalOperator &op,
ObOpSpec &spec, ...)
{
int ret = OB_SUCCESS;
if (0 == spec.rows_) {
spec.rows_ = ceil(op.get_card());
}
spec.cost_ = op.get_cost();
spec.width_ = op.get_width();
spec.plan_depth_ = op.get_plan_depth();
OZ(generate_rt_exprs(op.get_startup_exprs(), spec.startup_filters_));
OZ(generate_rt_exprs(op.get_filter_exprs(), spec.filters_));
OZ(generate_rt_exprs(op.get_output_exprs(), spec.output_));
ObSEArray<ObRawExpr *, 16> child_outputs;
for (int64_t i = 0; OB_SUCC(ret) && i < op.get_num_of_child(); i++) {
ObLogicalOperator *child_op = op.get_child(i);
if (OB_FAIL(append(child_outputs, child_op->get_output_exprs()))) { ... }
}
OZ(generate_calc_exprs(child_outputs, cur_op_exprs_, spec.calc_exprs_,
op.get_type(), check_eval_once, need_flatten_gen_col),
op.get_op_id(), op.get_name(), K(op.get_type()));
...
}
公共字段填充后,generate_calc_exprs 用当前算子依赖的表达式减去子节点已产出的输出列,得到本算子必须额外计算的 calc_exprs_,避免重复求值。
算子特化:LIMIT 示例
int ObStaticEngineCG::generate_spec(ObLogLimit &op,
ObLimitSpec &spec,
const bool in_root_job)
{
int ret = OB_SUCCESS;
UNUSED(in_root_job);
spec.calc_found_rows_ = op.get_is_calc_found_rows();
spec.is_top_limit_ = op.is_top_limit();
spec.is_fetch_with_ties_ = op.is_fetch_with_ties();
if (NULL != op.get_limit_expr()) {
CK(op.get_limit_expr()->get_result_type().is_integer_type());
OZ(generate_rt_expr(*op.get_limit_expr(), spec.limit_expr_));
OZ(mark_expr_self_produced(op.get_limit_expr()));
}
if (NULL != op.get_offset_expr()) {
CK(op.get_offset_expr()->get_result_type().is_integer_type());
OZ(generate_rt_expr(*op.get_offset_expr(), spec.offset_expr_));
OZ(mark_expr_self_produced(op.get_offset_expr()));
}
if (OB_SUCC(ret) && op.is_fetch_with_ties()) {
OZ(spec.sort_columns_.init(op.get_ties_ordering().count()));
FOREACH_CNT_X(it, op.get_ties_ordering(), OB_SUCC(ret)) {
CK(NULL != it->expr_);
ObExpr *e = NULL;
OZ(generate_rt_expr(*it->expr_, e));
OZ(spec.sort_columns_.push_back(e));
}
}
return ret;
}
LIMIT 的映射很典型:把逻辑层 limit/offset/ties 表达式转换为 ObLimitSpec 的运行时表达式。普通版与向量化版共用同一套逻辑。
小结
ObCodeGenerator是代码生成阶段的编排门面。ObStaticEngineCG后序遍历逻辑算子树,映射为物理ObOpSpec树。- 公共逻辑与算子特化分离,新增算子只需注册工厂并实现
generate_spec特化。 - 向量化批次探测保证只有合适路径才启用向量化,避免不成熟算子破坏稳定性。