TimescaleDB 函数 timescaledb_create_upper_paths_hook分析

TimescaleDB 函数 timescaledb_create_upper_paths_hook分析 参数说明参数类型含义input_relRelOptInfo*输入集合——即当前 stage 的上层关系UpperRelation的输入。例如对于 UPPERREL_GROUP_AGG 阶段input_rel 就是 UPPERREL_PARTIAL_GROUP_AGG或 UPPERREL_GROUP_AGG 本身取决于 PG 版本。它包含了聚合操作需要处理的所有行/元组信息。output_relRelOptInfo*输出集合——即当前 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() 这个函数。看它的行为1. 扫描 query 的 targetList查找是否有 partialize_agg(...) 包裹的聚合函数调用2. 如果有修改 AggPath 的 aggsplit 标志把普通聚合AGGSPLIT_SIMPLE改成部分聚合AGGSPLIT_INITIAL_SERIAL或者把 finalize 阶段改成 combine阶段AGGSPLIT_FINAL_DESERIAL →AGGSPLITOP_COMBINE \| ... \| AGGSPLITOP_SKIPFINAL3. 如果没有 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 功能是否语义必须gapfillplan_add_gapfill —给 time_bucket() 等 gapfill 函数添加自定义 Path纯优化chunkwise aggregationtsl_pushdown_partial_agg —chunkwise 聚合下推把部分聚合推到每个 chunk 上减少数据传输纯优化window gapfill adjustment —调整 gapfill 的窗口函数 targetlist纯优化skip-scantsl_skip_scan_paths_add —给 SKIP SCAN 添加自定义 Path纯优化这些 hook 做的事情都是往 output_rel-pathlist 里追加额外的 Path 供规划器选最优成本路径。如果不调用- 查询结果不变语义一致- 只是少了 gapfill、chunkwise agg、skip-scan 这几条优化路径规划器会走标准的 PostgreSQL 聚合/DISTINCT 路径- 性能可能差一些但功能不受影响