参数说明
| 参数 | 类型 | 含义 |
|---|---|---|
| input_rel | RelOptInfo* | 输入集合------即当前 stage 的上层关系(UpperRelation)的输入。例如对于 UPPERREL_GROUP_AGG 阶段,input_rel 就是 UPPERREL_PARTIAL_GROUP_AGG(或 UPPERREL_GROUP_AGG 本身,取决于 PG 版本)。它包含了聚合操作需要处理的所有行/元组信息。 |
| output_rel | RelOptInfo* | 输出集合------即当前 stage 要构建的目标上层关系。TimescaleDB 在这里往 output_rel->pathlist 中添加自定义的 Path(如 gapfill path、chunkwise aggregation path、skip-scan path 等)。 |
timescaledb_create_upper_paths_hook 各段分类
① 链式调用 prev hook(第 1648-1649 行)
if (prev_create_upper_paths_hook != NULL)
prev_create_upper_paths_hook(root, stage, input_rel, output_rel, extra);
语义必须。 这是 PostgreSQL 的 hook 链式调用惯例。如果跳过,其他扩展注册的 create_upper_paths_hook 就不会被执行。
②replace_hypertable_modify_paths(第 1659-1662 行)
if (output_rel->pathlist != NIL)
output_rel->pathlist = replace_hypertable_modify_paths(root, output_rel->pathlist, input_rel);
语义必须。 看 replace_hypertable_modify_paths 的逻辑(第 1586-1637 行):当 output_rel 的 pathlist 包含 ModifyTablePath 且操作是 INSERT/UPDATE/DELETE
时,它会用 TimescaleDB 自定义的 HypertableModifyPath 替换掉。
这个自定义 Path 的作用是将 DML 路由到正确的 chunk(见函数上方注释第 1531-1583 行的图解:插入 ChunkDispatch + HypertableModify
节点)。如果不做这个替换,INSERT/UPDATE/DELETE 会直接写入 hypertable 本身而不是分散到各个 chunk,结果完全错误。
③ts_plan_process_partialize_agg(第 1664-1670 行)
if (parse->hasAggs && stage == UPPERREL_GROUP_AGG)
partials_found = ts_plan_process_partialize_agg(root, output_rel);
条件语义必须。 只有当用户 SQL 中显式调用了 _timescaledb_functions.partialize_agg() 时才必须执行。如果 query 里没有这个函数,它直接返回 false 什么都不改。
这个函数响应的是用户显式调用了 _timescaledb_functions.partialize_agg() 这个函数。看它的行为:
-
扫描 query 的 targetList,查找是否有 partialize_agg(...) 包裹的聚合函数调用
-
如果有,修改 AggPath 的 aggsplit 标志,把普通聚合(AGGSPLIT_SIMPLE)改成部分聚合(AGGSPLIT_INITIAL_SERIAL),或者把 finalize 阶段改成 combine
阶段(AGGSPLIT_FINAL_DESERIAL →AGGSPLITOP_COMBINE \| ... \| AGGSPLITOP_SKIPFINAL)
- 如果没有 combine 路径,还二次遍历 targetList 做实际修改(TS_FIX_AGGSPLIT_SIMPLE)
这直接影响查询结果的计算方式:
-
不用 partialize_agg() →正常算出最终聚合结果
-
用了 partialize_agg(avg(temp)) →只算到部分聚合阶段,返回的是中间序列化状态而不是最终值
所以这不是可选的性能优化,而是对用户 SQL 中显式语义表达的实现。
如果你不打算支持 partialize_agg 这个函数,可以整个跳过这段。
④ts_preprocess_first_last_aggregates(第 1676 行)
if (parse->hasAggs)
ts_preprocess_first_last_aggregates(root, root->processed_tlist);
纯优化。 这是为 FIRST()/LAST() 聚合生成子查询 + index scan 路径,替代全表扫描聚合。不调用结果一样正确,只是慢一些。
⑤ts_plan_add_hashagg(第 1679-1680 行)
if (!partials_found)
ts_plan_add_hashagg(root, input_rel, output_rel);
纯优化。 添加 hash aggregation 路径作为备选,供规划器按成本选择。不走这条路径就用标准 sort-based agg,结果正确。
⑥TSL hook 分发(第 1683-1685 行)
if (ts_cm_functions->create_upper_paths_hook != NULL)
ts_cm_functions->create_upper_paths_hook(...);
| TSL 功能 | 是否语义必须 |
|---|---|
| gapfill(plan_add_gapfill) ---给 time_bucket() 等 gapfill 函数添加自定义 Path | 纯优化 |
| chunkwise aggregation(tsl_pushdown_partial_agg) ---chunkwise 聚合下推,把部分聚合推到每个 chunk 上减少数据传输 | 纯优化 |
| window gapfill adjustment ---调整 gapfill 的窗口函数 targetlist | 纯优化 |
| skip-scan(tsl_skip_scan_paths_add) ---给 SKIP SCAN 添加自定义 Path | 纯优化 |
这些 hook 做的事情都是往 output_rel->pathlist 里追加额外的 Path 供规划器选最优成本路径。如果不调用:
-
查询结果不变(语义一致)
-
只是少了 gapfill、chunkwise agg、skip-scan 这几条优化路径,规划器会走标准的 PostgreSQL 聚合/DISTINCT 路径
-
性能可能差一些,但功能不受影响