OceanBase源码解读:SQL代码生成器入口 ob_code_generator.cpp

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=01,保证稳定性。

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 特化。
  • 向量化批次探测保证只有合适路径才启用向量化,避免不成熟算子破坏稳定性。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注