开发者概览

Velox 函数开发

qianmoQqianmoQ· 更新于 2026-10-02· 阅读 6 分钟· 0 次阅读

登录后可跨设备保存划线和私人笔记登录

在 Velox 中,有两个文件夹 prestosql 和 sparksql 存放了大部分 SQL 函数,分别对应 presto 和 spark。Gluten 会先要求 Velox 注册 prestosql 函数,再注册 sparksql 函数。因此,如果 prestosql 和 sparksql 的某个函数签名相同,则 sparksql 函数会覆盖对应的 prestosql 函数。如果所需函数在这两个文件夹中都不存在(例外情况是某些在外部定义的通用函数,例如 cast),我们就需要在 sparksql 文件夹中实现缺失的函数。某个 prestosql 函数可能与对应的 Spark 函数在语义上存在差异,即使它们具有相同的函数名和函数签名。如果出现这种情况,我们也需要在 sparksql 文件夹中进行实现,通常以 prestosql 的原始实现为基础。

有一些 Spark 函数在某些特殊情况下,其行为会根据 ANSI 是开启还是关闭而有所不同。目前,Gluten 不支持 ANSI 模式。因此,在 Velox 中实现 Spark 内置函数时,只需考虑 ANSI 关闭的情况。

以 BitwiseAndFunction 为例:

template <typename T>
struct BitwiseAndFunction {
  template <typename TInput>
  // For void return type, it indicates null result will never be obtained for non-null input.
  // For bool return type, it indicates null result can be obtained for non-null input (false for null).
  FOLLY_ALWAYS_INLINE void call(TInput& result, TInput a, TInput b) {
    result = a & b;
  }
};

它和 call 函数一样是模板化的,以支持多种类型。在上面的实现中,当输入为 null 时结果为 null。如果你需要对 null 输入有不同的行为(例如,null 输入返回非 null 结果),请使用 callNullable。另请参阅 Velox 文档中的 callNullFree,它用于在任一输入包含 null 时进行快速求值。

下面的代码会为所有整数类型注册已实现的函数。实际调用该函数时使用的是指定的名称 bitwise_and。

registerBinaryIntegral<BitwiseAndFunction>({prefix + "bitwise_and"});

复杂类型的函数实现方式类似,参见 ArrayAverageFunction 中的 velox/functions/prestosql/ArrayFunctions.h。

Gluten 函数覆盖层

向上游 Velox 提交一个函数可能需要很长时间。为了避免被这一过程阻塞,Gluten 在 cpp/velox/operators/functions/overlay 中提供了函数覆盖层(function overlay),用于在 Gluten 侧托管和维护与 Velox 兼容的函数实现。该覆盖层由 gluten::registerFunctionOverlay() 在 gluten::registerAllFunctions() 末尾、所有 Velox presto/spark 函数注册完毕之后进行注册。由于在 Velox 的注册表中,同名同签名的后注册函数会替换先注册的函数,因此覆盖层函数会优先于 Velox 的实现。

在以下情况使用覆盖层:

  • Velox 中缺少某个 Spark 函数。先在覆盖层中实现它,让 Gluten 立即可以将其下推执行,之后再按自己的节奏将其提交到 Velox。
  • 某个 Velox sparksql 函数与 Spark 存在语义差异。在上游修复合入之前,将修正后的实现放入覆盖层以覆盖原函数。round 函数(overlay/Round.h)就是这样一个覆盖的例子。

添加一个函数的步骤:

  1. 在 cpp/velox/operators/functions/overlay/ 下的文件中实现它,使用 Velox 的函数编写 API(simple function、vector function、聚合函数或窗口函数)。
  2. 在 overlay/RegisterFunctionOverlay.cc 中注册它,使用 Gluten 的 Substrait 计划转换所产生的函数名。
  3. 如果新增了 .cc 文件,将其列入 cpp/velox/CMakeLists.txt。
  4. 在 cpp/velox/tests/SparkFunctionTest.cc 中添加 C++ 单元测试,并在适用的情况下添加 Scala 侧的查询测试。
  5. 对于全新的函数,还需要添加 Scala 侧的表达式映射(ExpressionNames.scala / ExpressionMappings.scala),以便计划器将其下推执行。

覆盖层是一个暂存区,而不是永久的分支:一旦某个函数被上游 Velox 接受,就在升级 Velox 版本的同一个 PR 中将其从覆盖层移除。

参考资料:

Velox 官方开发者指南:

评论

登录后参与评论

正在加载评论…