Postgresql源码(134)优化器针对volatile函数的排序优化分析

相关
《Postgresql源码(133)优化器动态规划生成连接路径的实例分析》

上一篇对路径的生成进行了分析,通过make_one_rel最终拿到了一个带着路径的RelOptInfo。本篇针对带volatile函数的排序场景继续分析subquery_planner的后续流程。

subquery_plannergrouping_plannerquery_plannermake_one_rel   <<< 上一篇// 后续流程         <<< 本篇

总结速查

一句话总结:带有volatile的投影列会被SORT算子忽略,达到先排序在投影计算volatile的效果。

  • grouping_planner→make_one_rel层层生成path,每个path都会带pathtarget(不一定是SQL中最后需要的target列表),一般都是层层继承上来的。
  • make_one_rel生成的最终path中,会忽略volatile函数列,交给外层grouping_planner函数处理,所以生成的path中的pathtarget都是看不到volatile函数列的。
  • 这里一个关键逻辑就path中的pathtargetmake_sort_input_target计算出来列表的是不是一样的
    • 如果是一样的就不加投影节点,等后面加sort时(create_ordered_paths)先加sort在加投影,计算顺序就是先排序,在拿排序阶段投影(计算random函数)
    • 如果不一样就直接加投影节点,后面sort会加到投影上面,计算顺序就是先投影(计算random函数),再排序。
  • path中的pathtarget会忽略volatile函数。
  • make_sort_input_target中的volatile函数正常也会被忽略掉(实例3),除非volatile函数就是排序列(实例4)。

最终效果是,投影列有volatile函数的SQL(函数非排序列),sort节点会忽略这类函数的执行,sort结束后,在投影节点使用sort的结果集来计算这类函数。

实例3:
在这里插入图片描述

1 实例:简单join

drop table student;
create table student(sno int primary key, sname varchar(10), ssex int);
insert into student values(1, 'stu1', 0);
insert into student values(2, 'stu2', 1);
insert into student values(3, 'stu3', 1);
insert into student values(4, 'stu4', 0);drop table course;
create table course(cno int primary key, cname varchar(10), tno int);
insert into course values(20, 'meth', 10);
insert into course values(21, 'english', 11);drop table teacher;
create table teacher(tno int primary key, tname varchar(10), tsex int);
insert into teacher values(10, 'te1', 1);
insert into teacher values(11, 'te2', 0);drop table score;
create table score (sno int, cno int, degree int);
create index idx_score_sno on score(sno);
insert into score values (1, 20, 100);
insert into score values (1, 21, 89);
insert into score values (2, 20, 99);
insert into score values (2, 21, 90);
insert into score values (3, 20, 87);
insert into score values (3, 21, 20);
insert into score values (4, 20, 60);
insert into score values (4, 21, 70);explain 
SELECT STUDENT.sname, COURSE.cname, SCORE.degree
FROM STUDENT
LEFT JOIN SCORE ON STUDENT.sno = SCORE.sno
LEFT JOIN COURSE ON SCORE.cno = COURSE.cno;QUERY PLAN
------------------------------------------------------------------------------Hash Left Join  (cost=69.50..110.65 rows=2040 width=80)Hash Cond: (score.cno = course.cno)->  Hash Right Join  (cost=34.75..70.53 rows=2040 width=46)Hash Cond: (score.sno = student.sno)->  Seq Scan on score  (cost=0.00..30.40 rows=2040 width=12)->  Hash  (cost=21.00..21.00 rows=1100 width=42)->  Seq Scan on student  (cost=0.00..21.00 rows=1100 width=42)->  Hash  (cost=21.00..21.00 rows=1100 width=42)->  Seq Scan on course  (cost=0.00..21.00 rows=1100 width=42)

1.1 subquery_planner→grouping_planner

grouping_plannercurrent_rel = query_planner(root, standard_qp_callback, &qp_extra);
  • current_rel:
    在这里插入图片描述
	final_target = create_pathtarget(root, root->processed_tlist);
  • 得到final_target
    • final_target->exprs->elements[0] : {varno = 1, varattno = 2, vartype = 1043} STUDENT.sname
    • final_target->exprs->elements[1] : {varno = 4, varattno = 2, vartype = 1043} COURSE.cname
    • final_target->exprs->elements[2] : {varno = 2, varattno = 3, vartype = 23} SCORE.degree
	if (parse->sortClause)make_sort_input_targetif (activeWindows)...if (have_grouping)...if (parse->hasTargetSRFs)...
  • apply_scanjoin_target_to_paths创建投影节点
    在这里插入图片描述
	/* Apply scan/join target. */scanjoin_target_same_exprs = list_length(scanjoin_targets) == 1&& equal(scanjoin_target->exprs, current_rel->reltarget->exprs);apply_scanjoin_target_to_paths(root, current_rel, scanjoin_targets,scanjoin_targets_contain_srfs,scanjoin_target_parallel_safe,scanjoin_target_same_exprs);
  • 继续
	if (have_grouping)...if (activeWindows)...if (parse->distinctClause)...if (parse->sortClause)create_ordered_paths
  • 创建空的最顶层节点
	final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
  • 遍历current_rel中所有的path,用add_path加入到最顶层节点中。
  • 其中limit、rowclock的场景需要特殊处理下。
	foreach(lc, current_rel->pathlist)if (parse->rowMarks)create_lockrows_pathif (limit_needed(parse))create_limit_pathadd_path(final_rel, path);

grouping_planner函数执行结束,最后拼接的final_rel在upper_rels里面记录:
在这里插入图片描述
pathlist最上层是投影节点:
在这里插入图片描述

1.2 standard_planner→subquery_planner

subquery_planner中后续处理流程:

计划生成步骤作用
root = subquery_planner优化器入口,返回PlannerInfo,里面记录了一个最终的RelOptInfo相当于一张逻辑表,每个ROI都记录了多个path,表示不同的计算路径
final_rel = fetch_upper_rel拿到最终的RelOptInfo
best_path = get_cheapest_fractional_path在RelOptInfo中选择一个最优的path
top_plan = create_plan→create_plan_recurse根据最优path生成计划

2 实例:【简单join】【排序非投影列】【投影列无函数】

drop table student;
create table student(sno int primary key, sname varchar(10), ssex int);
insert into student values(1, 'stu1', 0);
insert into student values(2, 'stu2', 1);
insert into student values(3, 'stu3', 1);
insert into student values(4, 'stu4', 0);drop table course;
create table course(cno int primary key, cname varchar(10), tno int);
insert into course values(20, 'meth', 10);
insert into course values(21, 'english', 11);drop table teacher;
create table teacher(tno int primary key, tname varchar(10), tsex int);
insert into teacher values(10, 'te1', 1);
insert into teacher values(11, 'te2', 0);drop table score;
create table score (sno int, cno int, degree int);
create index idx_score_sno on score(sno);
insert into score values (1, 20, 100);
insert into score values (1, 21, 89);
insert into score values (2, 20, 99);
insert into score values (2, 21, 90);
insert into score values (3, 20, 87);
insert into score values (3, 21, 20);
insert into score values (4, 20, 60);
insert into score values (4, 21, 70);explain verbose
SELECT STUDENT.sname, COURSE.cname, SCORE.degree
FROM STUDENT
LEFT JOIN SCORE ON STUDENT.sno = SCORE.sno
LEFT JOIN COURSE ON SCORE.cno = COURSE.cno
ORDER BY COURSE.cno;QUERY PLAN
--------------------------------------------------------------------------------------Sort  (cost=3.44..3.46 rows=8 width=19)Output: student.sname, course.cname, score.degree, course.cnoSort Key: course.cno->  Hash Left Join  (cost=2.14..3.32 rows=8 width=19)Output: student.sname, course.cname, score.degree, course.cnoInner Unique: trueHash Cond: (score.cno = course.cno)->  Hash Right Join  (cost=1.09..2.21 rows=8 width=13)Output: student.sname, score.degree, score.cnoInner Unique: trueHash Cond: (score.sno = student.sno)->  Seq Scan on public.score  (cost=0.00..1.08 rows=8 width=12)Output: score.sno, score.cno, score.degree->  Hash  (cost=1.04..1.04 rows=4 width=9)Output: student.sname, student.sno->  Seq Scan on public.student  (cost=0.00..1.04 rows=4 width=9)Output: student.sname, student.sno->  Hash  (cost=1.02..1.02 rows=2 width=10)Output: course.cname, course.cno->  Seq Scan on public.course  (cost=0.00..1.02 rows=2 width=10)Output: course.cname, course.cno

2.1 grouping_planner

grouping_plannercurrent_rel = query_planner(root, standard_qp_callback, &qp_extra);final_target = create_pathtarget(root, root->processed_tlist);if (parse->sortClause)sort_input_target = make_sort_input_target(root, final_target, &have_postponed_srfs);

make_sort_input_target函数的作用:

  • 排序列可能不在最终的投影列里面,需要特殊处理下。
  • 易变函数和成本很高的函数需要再投影列中识别出来,先排序,在计算。
    • 因为1:sort limit场景可以少算一些。
    • 因为2:易变函数每次算都可能不一样,先排序好了再算有利于结果集稳定,例如current_timestamp这种,期望是排序后给出的每一样的时间都是递增的,如果先排序在计算就能得到这种效果。

生成的final_target和sort_input_target相同,因为没看到srf函数、易变函数。

final_target同sort_input_targetVar指向列sortgrouprefs
final_target->exprs->elements[0]varno = 1, varattno = 2, vartype = 1043STUDENT.sname0
final_target->exprs->elements[1]varno = 4, varattno = 2, vartype = 1043COURSE.cname0
final_target->exprs->elements[2]varno = 2, varattno = 3, vartype = 23SCORE.degree0
final_target->exprs->elements[3]varno = 4, varattno = 1, vartype = 23COURSE.cno1

grouping_planner继续执行,开始生成排序path:

	...if (parse->sortClause)current_rel = create_ordered_paths(root,current_rel,final_target,final_target_parallel_safe,have_postponed_srfs ? -1.0 :limit_tuples);

grouping_planner→create_ordered_paths

create_ordered_paths// 创建一个排序节点ordered_rel = fetch_upper_rel(root, UPPERREL_ORDERED, NULL);// 拿到path入口,目前顶层是T_ProjectionPath,就一个节点foreach(lc, input_rel->pathlist)// 判断input_path->pathkeys是不是有序的?// 因为现在计划树是hashjoin,每一列都是无序的,所以input_path->pathkeys是空的,需要排序is_sorted = pathkeys_count_contained_in(root->sort_pathkeys, input_path->pathkeys, &presorted_keys);if (is_sorted)sorted_path = input_path;elsesorted_path = (Path *) create_sort_path(root,ordered_rel,input_path,root->sort_pathkeys,limit_tuples);
  • 输入的path顶层节点是project本来没有带pathkeys信息,这里创建一个sort节点放在上面,加入pathkey信息。
  • 但生成的sortpath没看到排序列的信息?
  • 排序信息在基类path的pathkeys中。
sorted_path = 
{ path = { type = T_SortPath, pathtype = T_Sort, parent = 0x2334030, pathtarget = 0x2333ef0, param_info = 0x0, parallel_aware = false, parallel_safe = true, parallel_workers = 0, rows = 8, startup_cost = 3.4437500000000005, total_cost = 3.4637500000000006, pathkeys = 0x232e018}, subpath = 0x2333a00}

T_PathKey每个pathkey(排序列)都对应了一个T_EquivalenceClass,T_EquivalenceClass中记录了排序的具体信息。

{ type = T_PathKey, pk_eclass = 0x232bf88, pk_opfamily = 1976, pk_strategy = 1, pk_nulls_first = false}

T_EquivalenceClass中的ec_members记录了排序列信息Var{varno = 4, varattno = 1}

{ type = T_EquivalenceClass, ec_opfamilies = 0x232ddf8,    // List{ 1976 }ec_collation = 0, ec_members = 0x232df48,  // List { EquivalenceMember }// EquivalenceMember{//   type = T_EquivalenceMember, //   em_expr = 0x232de68,  Var{varno = 4, varattno = 1}//   em_relids = 0x232de48, //   em_is_const = false, //   em_is_child = false, //   em_datatype = 23, //   em_jdomain = 0x2329158, em_parent = 0x0}ec_sources = 0x0, ec_derives = 0x0, ec_relids = 0x232df28,ec_has_const = false, ec_has_volatile = false, ec_broken = false, ec_sortref = 1, ec_min_security = 4294967295, ec_max_security = 0, ec_merged = 0x0}

生成排序节点后的计划:

  • sort节点的target是四列,虽然sql只写了三列,但有一列是排序需要的,也会加到pathtarget中。
    在这里插入图片描述

3 实例:【简单join】【排序非投影列】【投影列中有volatile函数】

drop table student;
create table student(sno int primary key, sname varchar(10), ssex int);
insert into student values(1, 'stu1', 0);
insert into student values(2, 'stu2', 1);
insert into student values(3, 'stu3', 1);
insert into student values(4, 'stu4', 0);drop table course;
create table course(cno int primary key, cname varchar(10), tno int);
insert into course values(20, 'meth', 10);
insert into course values(21, 'english', 11);drop table teacher;
create table teacher(tno int primary key, tname varchar(10), tsex int);
insert into teacher values(10, 'te1', 1);
insert into teacher values(11, 'te2', 0);drop table score;
create table score (sno int, cno int, degree int);
create index idx_score_sno on score(sno);
insert into score values (1, 20, 100);
insert into score values (1, 21, 89);
insert into score values (2, 20, 99);
insert into score values (2, 21, 90);
insert into score values (3, 20, 87);
insert into score values (3, 21, 20);
insert into score values (4, 20, 60);
insert into score values (4, 21, 70);explain verbose
SELECT STUDENT.sname, random(), SCORE.degree
FROM STUDENT
LEFT JOIN SCORE ON STUDENT.sno = SCORE.sno
LEFT JOIN COURSE ON SCORE.cno = COURSE.cno
ORDER BY COURSE.cno;QUERY PLAN
--------------------------------------------------------------------------------------------Result  (cost=3.44..3.56 rows=8 width=21)Output: student.sname, random(), score.degree, course.cno->  Sort  (cost=3.44..3.46 rows=8 width=13)Output: student.sname, score.degree, course.cnoSort Key: course.cno->  Hash Left Join  (cost=2.14..3.32 rows=8 width=13)Output: student.sname, score.degree, course.cnoInner Unique: trueHash Cond: (score.cno = course.cno)->  Hash Right Join  (cost=1.09..2.21 rows=8 width=13)Output: student.sname, score.degree, score.cnoInner Unique: trueHash Cond: (score.sno = student.sno)->  Seq Scan on public.score  (cost=0.00..1.08 rows=8 width=12)Output: score.sno, score.cno, score.degree->  Hash  (cost=1.04..1.04 rows=4 width=9)Output: student.sname, student.sno->  Seq Scan on public.student  (cost=0.00..1.04 rows=4 width=9)Output: student.sname, student.sno->  Hash  (cost=1.02..1.02 rows=2 width=4)Output: course.cno->  Seq Scan on public.course  (cost=0.00..1.02 rows=2 width=4)Output: course.cno

3.1 grouping_planner→make_one_rel生成的RelOptInfo→reltarget

make_one_rel前:

准备连接的RelOptInfo在simple_rel_array数组中,这里关注下三个RelOptInfo的reltarget:

(gdb) plist root->simple_rel_array[1]->reltarget->exprs
$67 = 2
$68 = {ptr_value = 0x3083218, int_value = 50868760, oid_value = 50868760, xid_value = 50868760}
$69 = {ptr_value = 0x30ab8b8, int_value = 51034296, oid_value = 51034296, xid_value = 51034296}
(gdb) p root->simple_rte_array[1]->relid
$70 = 16564
root→simple_rel_array[i]simple_rel_array[i]→reltarget->exprsrelid
1varno = 1, varattno = 2, vartype = 104316564 student.sname
1varno = 1, varattno = 1, vartype = 2316564 student.sno
2varno = 2, varattno = 3, vartype = 2316579 score.degree
2varno = 2, varattno = 1, vartype = 2316579 score.cno
2varno = 2, varattno = 2, vartype = 2316579 score.sno
4varno = 4, varattno = 1, vartype = 2316569 course.cno
SELECT STUDENT.sname, random(), SCORE.degree
FROM STUDENT
LEFT JOIN SCORE ON STUDENT.sno = SCORE.sno
LEFT JOIN COURSE ON SCORE.cno = COURSE.cno
ORDER BY COURSE.cno;

make_one_rel生成后:

final_rel->reltarget->exprs
1varno = 1, varattno = 2, vartype = 1043投影第1列:STUDENT.sname
2varno = 2, varattno = 3, vartype = 23投影第3列:SCORE.degree
3varno = 4, varattno = 1, vartype = 23排序列:COURSE.cno

3.2 grouping_planner→make_sort_input_target规律v函数生成排序target

final_target = create_pathtarget(root, root->processed_tlist);拿到的final_target:

final_targetVar / FuncExpr指向列sortgrouprefs
final_target->exprs->elements[0]varno = 1, varattno = 2, vartype = 1043STUDENT.sname0
final_target->exprs->elements[1]funcid = 1598, funcresulttype = 701random()0
final_target->exprs->elements[2]varno = 2, varattno = 3, vartype = 23SCORE.degree0
final_target->exprs->elements[3]varno = 4, varattno = 1, vartype = 23COURSE.cno1

make_sort_input_target拿到的sort_input_target,过滤掉了random列:

sort_input_targetVar / FuncExpr指向列sortgrouprefs
sort_input_target->exprs->elements[0]varno = 1, varattno = 2, vartype = 1043STUDENT.sname0
sort_input_target->exprs->elements[1]varno = 2, varattno = 3, vartype = 23SCORE.degree0
sort_input_target->exprs->elements[2]varno = 4, varattno = 1, vartype = 23COURSE.cno1

实例2中,apply_scanjoin_target_to_paths会先挂投影节点,后面的create_ordered_paths在创建顶层的排序节点,为什么这里的投影节点在最上层?因为有volatile函数在,需要先排序,在到投影节点上计算random函数

3.3 grouping_planner→apply_scanjoin_target_to_paths

		final_target = create_pathtarget(root, root->processed_tlist);...sort_input_target = make_sort_input_target(...);...grouping_target = sort_input_target;...scanjoin_target = grouping_target;...scanjoin_targets = list_make1(scanjoin_target);...scanjoin_target_same_exprs = list_length(scanjoin_targets) == 1&& equal(scanjoin_target->exprs, current_rel->reltarget->exprs);...// 1 确定没有SRF  list_length(scanjoin_targets) == 1// 2 这里make_one_rel出来的current_rel和上面make_sort_input_target计算出来的投影列一样,都过滤掉了v函数,剩下三列// scanjoin_target_same_exprs == truescanjoin_target_same_exprs = list_length(scanjoin_targets) == 1&& equal(scanjoin_target->exprs, current_rel->reltarget->exprs);apply_scanjoin_target_to_paths(root, current_rel, scanjoin_targets,scanjoin_targets_contain_srfs,scanjoin_target_parallel_safe,

注意:

  1. scanjoin_target->exprs:表示最终结果需要的targetlist。
  2. current_rel->reltarget->exprs:表示当前生成path中带的targetlist。
  3. 生成path的路径需要和scanjoin_target一致,所以进入下面函数判断是否生成投影节点。
  4. 如果相同,scanjoin_target_same_exprs==true,则不生成投影节点。
  5. 如果不同,scanjoin_target_same_exprs==false,则调用create_projection_path传入scanjoin_target,生成投影节点。

在apply_scanjoin_target_to_paths中:

apply_scanjoin_target_to_paths......foreach(lc, rel->pathlist){Path	   *subpath = (Path *) lfirst(lc);if (tlist_same_exprs)// scanjoin_target->sortgrouprefs = [0, 0, 1] 表示第三列是排序列// 因为现在的scanjoin_target(同sort_input_target)中只有三列,投影列1、3和排序列,参考上面sort_input_target表格。subpath->pathtarget->sortgrouprefs = scanjoin_target->sortgrouprefs;else{Path	   *newpath;newpath = (Path *) create_projection_path(root, rel, subpath,scanjoin_target);lfirst(lc) = newpath;}}

3.4 grouping_planner→create_ordered_paths

继续成成排序node:

grouping_planner...if (parse->sortClause)current_rel = create_ordered_paths(root,current_rel,final_target,final_target_parallel_safe,have_postponed_srfs ? -1.0 :limit_tuples);
  • create_ordered_paths最重要的入参就是final_target,保存了全部的列信息和排序列的位置sortgrouprefs。
  • 注意前面生成path中的reltarget已经过滤了random列,但这里没有过滤,需要全量的信息。
final_targetVar / FuncExpr指向列sortgrouprefs
final_target->exprs->elements[0]varno = 1, varattno = 2, vartype = 1043STUDENT.sname0
final_target->exprs->elements[1]funcid = 1598, funcresulttype = 701random()0
final_target->exprs->elements[2]varno = 2, varattno = 3, vartype = 23SCORE.degree0
final_target->exprs->elements[3]varno = 4, varattno = 1, vartype = 23COURSE.cno1
  1. 注意:这里create_sort_path为hashjoin节点上面加了一层sort节点,sort节点的pathtarget继承了hash节点的pathtarget,也就是三列(没有random函数列)。
  2. 注意:这里的target是上面表格中的final_target,也就是四列(带random函数)。
  3. 加了sort节点后,发现这里不相同,所以开始增加投影列apply_projection_to_path。
create_ordered_pathsordered_rel = fetch_upper_rel(root, UPPERREL_ORDERED, NULL);foreach(lc, input_rel->pathlist)is_sorted = pathkeys_count_contained_inif (is_sorted)sorted_path = input_path;elsesorted_path = (Path *) create_sort_path(...)// 生成sorted_path// {type = T_SortPath, pathtype = T_Sort, pathtarget = 三列 }if (sorted_path->pathtarget != target)sorted_path = apply_projection_to_path(root, ordered_rel, sorted_path, target);// 生成投影列// {type = T_ProjectionPath, pathtype = T_Result, pathtarget = 四列 }

最终生成PATH:

SELECT STUDENT.sname, random(), SCORE.degree
FROM STUDENT
LEFT JOIN SCORE ON STUDENT.sno = SCORE.sno
LEFT JOIN COURSE ON SCORE.cno = COURSE.cno
ORDER BY COURSE.cno;

最终效果:
在这里插入图片描述

4 实例:【简单join】【排序volatile函数】【投影列中有volatile函数】

drop table student;
create table student(sno int primary key, sname varchar(10), ssex int);
insert into student values(1, 'stu1', 0);
insert into student values(2, 'stu2', 1);
insert into student values(3, 'stu3', 1);
insert into student values(4, 'stu4', 0);drop table course;
create table course(cno int primary key, cname varchar(10), tno int);
insert into course values(20, 'meth', 10);
insert into course values(21, 'english', 11);drop table teacher;
create table teacher(tno int primary key, tname varchar(10), tsex int);
insert into teacher values(10, 'te1', 1);
insert into teacher values(11, 'te2', 0);drop table score;
create table score (sno int, cno int, degree int);
create index idx_score_sno on score(sno);
insert into score values (1, 20, 100);
insert into score values (1, 21, 89);
insert into score values (2, 20, 99);
insert into score values (2, 21, 90);
insert into score values (3, 20, 87);
insert into score values (3, 21, 20);
insert into score values (4, 20, 60);
insert into score values (4, 21, 70);explain verbose
SELECT STUDENT.sname, random(), SCORE.degree
FROM STUDENT
LEFT JOIN SCORE ON STUDENT.sno = SCORE.sno
LEFT JOIN COURSE ON SCORE.cno = COURSE.cno
ORDER BY random();QUERY PLAN
--------------------------------------------------------------------------------Sort  (cost=2.35..2.37 rows=8 width=17)Output: student.sname, (random()), score.degreeSort Key: (random())->  Hash Right Join  (cost=1.09..2.23 rows=8 width=17)Output: student.sname, random(), score.degreeInner Unique: trueHash Cond: (score.sno = student.sno)->  Seq Scan on public.score  (cost=0.00..1.08 rows=8 width=12)Output: score.sno, score.cno, score.degree->  Hash  (cost=1.04..1.04 rows=4 width=9)Output: student.sname, student.sno->  Seq Scan on public.student  (cost=0.00..1.04 rows=4 width=9)Output: student.sname, student.sno

4.1 make_one_rel结果

第一步:拿到RelOptInfo
current_rel = query_planner(root, standard_qp_callback, &qp_extra);

current_rel->reltarget中忽略了random函数:

{ type = T_PathTarget, exprs = {Var{varno = 1, varattno = 2, vartype = 1043}, // STUDENT.snameVar{varno = 2, varattno = 3, vartype = 23}    // SCORE.degree}, sortgrouprefs = 0x0 }

4.2 拿到final_target

final_target = create_pathtarget(root, root->processed_tlist);

{type = T_PathTarget, exprs = {Var{varno = 1, varattno = 2, vartype = 1043},         // STUDENT.snameFuncExpr {xpr = {type = T_FuncExpr}, funcid = 1598},  // random()Var{varno = 2, varattno = 3, vartype = 23}            // SCORE.degree}, sortgrouprefs = [0, 1, 0]
}

4.3 构造排序target:make_sort_input_target

sort_input_target = make_sort_input_target(root, final_target, &have_postponed_srfs);

{type = T_PathTarget,exprs = {Var{varno = 1, varattno = 2, vartype = 1043},         // STUDENT.snameFuncExpr {xpr = {type = T_FuncExpr}, funcid = 1598},  // random()Var{varno = 2, varattno = 3, vartype = 23}            // SCORE.degree}, sortgrouprefs = [0, 1, 0]
}

4.4 apply_scanjoin_target_to_paths增加投影

apply_scanjoin_target_to_paths执行后,增加投影节点:

{ path = {type = T_ProjectionPath, pathtype = T_Result }

4.5 create_ordered_paths后增加排序节点在最顶层

{ path = {type = T_SortPath, pathtype = T_Sort }

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/17651.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SAM遥感图像处理开源新SOTA!在GPU上实现40倍加速,不损准确性

在遥感图像处理领域&#xff0c;通过SAM捕捉复杂图像特征和细微差异&#xff0c;可以实现高精度的图像分割&#xff0c;提升遥感数据的处理效率。这种高度的准确性让SAM遥感展现出了比传统方法更优越的性能。 不仅如此&#xff0c;这种策略灵活普适的特性还能拓展遥感技术的应…

4款让人骄傲的国产软件,功能过于强大,却被误认为是外国佬研发

说到国产软件&#xff0c;许多人可能会有“流氓软件、弹屏广告多、隐藏消费套路”等负面印象。 这种偏见导致一些功能强大、用户友好的国产软件被误认为是外国人开发的。 1、格式工厂 格式工厂是一个很实用的国产格式转换工具&#xff0c;它完全免费且没有广告&#xff0c;不…

实现echarts多图联动效果

实现echarts多图联动效果 文章目录 实现echarts多图联动效果业务场景实现关键api代码示例&#xff08;vue&#xff09; 业务场景 提示&#xff1a;主要是记录一下多个echarts联动效果实现方案 这本来就是echarts本身自带的api&#xff0c;并没有多高级&#xff0c;奈何寻找的过…

使用DockerFile 编写 指令来构建镜像

文章目录 前言使用DockerFile 编写 指令来构建镜像1. 构建2. 验证 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在白嫖的话&#x…

混合A*算法详解(二)路径平滑

描述 上一篇文章混合A*算法详解&#xff08;一&#xff09;路径搜索 路径损失函数使用Voroni势能图 根据之前的文章分析&#xff0c;决定A*路径长度的有两点&#xff1a;路径长度和距离障碍物远近。Voroni图用于权衡这两者。之前我在记录二维点云的阿尔法形状算法时简单介绍过…

grafana大盘展示node_expod节点

node_expod添加lables标签 Prometheus查询 语句查询 node_exporter_build_infografna添加变量查询 正常有值 切换其他的是有值的 我的报错原因 因为有多个数据源,我选择错了,因为修改的lable标签是其他数据源,所以获取不到 查询语句 我的变量是 $app node_filesyste…

JavaScript的当前时间设置及Date的运算

作者:私语茶馆 1.场景描述 如下图,在HTML刚加载时,需要将开始时间设置为默认当前时间,结束时间设置为当前时间后7天的时间。手工填写时间时,时间段不超过30天。 这里涉及到两个技术点: 1)Input Date的当前时间设置 2)date的运算 由于是动态修改HTML,所以采用…

AI预测福彩3D采取888=3策略+和值012路一缩定乾坤测试5月28日预测第4弹

昨天的第二套方案已命中&#xff0c;第一套方案由于杀了对子&#xff0c;导致最终出错。 今天继续基于8883的大底&#xff0c;使用尽可能少的条件进行缩号&#xff0c;同时&#xff0c;同样准备两套方案&#xff0c;一套是我自己的条件进行缩号&#xff0c;另外一套是8883的大底…

freemarker ftl模板 格式、列表、图片

文章目录 前言一、freemarker实现内容替换二、ftl 模板1.word另存ftl2.编辑ftl文件2.1 了解一下常用的标记及其说明2.2 list处理2.3 红线2.4 图片 总结 前言 固定内容word生成&#xff1a;freemarker ftl模板 动态表格生成&#xff1a;https://blog.csdn.net/mr_wanter/articl…

Python爬虫入门到进阶:解锁网络数据的钥匙

Python爬虫入门到进阶&#xff1a;解锁网络数据的钥匙 一、Python爬虫基础1.1 爬虫基本概念1.2 Python爬虫必备库1.3 第一个爬虫示例 二、实战爬虫实例2.1 爬取天气数据2.2 高级技巧&#xff1a;异步爬虫 三、反爬机制与应对策略3.1 常见反爬机制3.2 应对策略 四、性能优化与安…

【经验分享】可视化的项目管理,轻松解决资源冲突和协作困难

在数字化时代&#xff0c;高效协同逐步成为提升组织效能的重要着力点&#xff0c;同时也是企业保持竞争力、实现持续发展的关键要素。一方面可以打破部门壁垒&#xff0c;促进信息流通&#xff0c;从而提升整体工作效率&#xff1b;另一方面还能帮助企业优化资源配置和管理流程…

keithely 2401 低压源表

Keithley 2401 低压源表提供精密电压和电流源以及测量功能&#xff08;1V - 20V 和 10pA - 1A&#xff09;。它既是高度稳定的直流电源&#xff0c;也是真正的仪器级 5 位万用表。电源特性包括低噪声、精度和回读。万用表功能包括高重复性和低噪声。结果是紧凑型单通道直流参数…

JavaSE:String类常用方法(巨详细)

1、引言 在学习C语言时&#xff0c;我们就已经了解了字符串&#xff0c;但是在C语言中字符串只能用字符数组或者指针来表示&#xff0c;没有特定的字符串类型&#xff0c;而在Java中&#xff0c;我们有了字符串这一特定的类型---String类。 2、String类的常用方法 2.1 字符串…

第98天:权限提升-WIN 全平台MSF 自动化CS 插件化EXP 筛选溢出漏洞

目录 思维导图 前置知识 案例一&#xff1a; Web&Win2008-人工手动&全自动msf-筛选&下载&利用 手动 全自动msf 案例二: Web&Win2019-CS 半自动-反弹&插件&利用 思维导图 前置知识 提权方式&#xff0c;这里讲的是溢出漏洞 windows权限 常…

Java处理CSV文件示例

Java处理CSV文件示例 1. 导入依赖 <dependency><groupId>org.apache.commons</groupId><artifactId>commons-csv</artifactId><version>1.10.0</version></dependency>文件示例 下面是示例文件文件数据 vscode和idea都有解析…

智慧林业云巡平台 客户端和移动端(支持语音和视频)自动定位巡护,后端离线路线监测

目前现状 无法客观、方便地掌握护林员的到位情况&#xff0c;因而无法有效地保证巡护人员按计划要求&#xff0c;按时按周期对所负责的林区开展巡护&#xff0c;使巡护工作的质量得不到保证。遇到火情、乱砍滥伐等灾情时无法及时上报处理&#xff0c;现场状况、位置等信息描述…

[IM002][Microsoft][ODBC驱动程序管理器]未发现数据源名称并且未指定默认驱动程序(0]

目录预览 一、问题描述二、原因分析三、解决方案四、参考链接 一、问题描述 使用navicat 连接sql server出现[IM002][Microsoft][ODBC驱动程序管理器]未发现数据源名称并且未指定默认驱动程序&#xff08;0] 二、原因分析 Navicat没有找到电脑安装的SQLServer驱动 三、解决方…

向传音手机学习产品市场定位与产品需求定义

2024 年第一季度全球智能手机发货量同比增长 11%&#xff0c;排在第一名的是三星&#xff0c;占比 21%&#xff0c;苹果占比 17% 排在第二位&#xff0c;小米 14%排在第三名&#xff0c;传音手机10% 排在第四位&#xff0c;OPPO为 9% 排在第五名。 「非洲之王」传音手机表现十…

【Python】 如何从日期中减去一天?

基本原理 在编程中&#xff0c;日期和时间的处理是一个常见的需求&#xff0c;尤其是在处理日志、调度任务、数据分析等场景中。Python 提供了多种方式来处理日期和时间&#xff0c;其中最常用的库是 datetime。datetime 模块包含了日期&#xff08;date&#xff09;、时间&am…

【python脚本】修改目标检测的xml标签(VOC)类别名

需求&#xff1a; 在集成多个数据集一同训练时&#xff0c;可能会存在不同数据集针对同一种目标有不同的类名&#xff0c;可以通过python脚本修改数据内的类名映射&#xff0c;实现统一数据集标签名的目的。 代码&#xff1a; # -*- coding: utf-8 -*- # Time : 2023/9/11 1…