|
| 1 | +/* ------------------------------------------------------------------------ |
| 2 | + * |
| 3 | + * hooks.c |
| 4 | + *definitions of rel_pathlist and join_pathlist hooks |
| 5 | + * |
| 6 | + * Copyright (c) 2016, Postgres Professional |
| 7 | + * |
| 8 | + * ------------------------------------------------------------------------ |
| 9 | + */ |
| 10 | +#include"postgres.h" |
| 11 | +#include"optimizer/cost.h" |
| 12 | +#include"optimizer/restrictinfo.h" |
| 13 | +#include"hooks.h" |
| 14 | +#include"utils.h" |
| 15 | +#include"pathman.h" |
| 16 | +#include"runtimeappend.h" |
| 17 | + |
| 18 | + |
| 19 | +set_join_pathlist_hook_typeset_join_pathlist_next=NULL; |
| 20 | +set_rel_pathlist_hook_typeset_rel_pathlist_hook_next=NULL; |
| 21 | + |
| 22 | + |
| 23 | +void |
| 24 | +pathman_join_pathlist_hook(PlannerInfo*root, |
| 25 | +RelOptInfo*joinrel, |
| 26 | +RelOptInfo*outerrel, |
| 27 | +RelOptInfo*innerrel, |
| 28 | +JoinTypejointype, |
| 29 | +JoinPathExtraData*extra) |
| 30 | +{ |
| 31 | +JoinCostWorkspaceworkspace; |
| 32 | +Path*outer, |
| 33 | +*inner; |
| 34 | +Relidsinner_required; |
| 35 | +RangeTblEntry*inner_entry=root->simple_rte_array[innerrel->relid]; |
| 36 | +PartRelationInfo*inner_prel; |
| 37 | +NestPath*nest_path; |
| 38 | +List*pathkeys=NIL; |
| 39 | +List*joinrestrictclauses=extra->restrictlist; |
| 40 | +List*joinclauses, |
| 41 | +*otherclauses; |
| 42 | +ListCell*lc; |
| 43 | +doubleparamsel; |
| 44 | + |
| 45 | +if (set_join_pathlist_next) |
| 46 | +set_join_pathlist_next(root,joinrel,outerrel, |
| 47 | +innerrel,jointype,extra); |
| 48 | + |
| 49 | +if (jointype==JOIN_UNIQUE_OUTER|| |
| 50 | +jointype==JOIN_UNIQUE_INNER) |
| 51 | +{ |
| 52 | +jointype=JOIN_INNER; |
| 53 | +} |
| 54 | + |
| 55 | +if (jointype==JOIN_FULL|| !pg_pathman_enable_runtimeappend) |
| 56 | +return; |
| 57 | + |
| 58 | +if (innerrel->reloptkind!=RELOPT_BASEREL|| |
| 59 | +!inner_entry->inh|| |
| 60 | +!(inner_prel=get_pathman_relation_info(inner_entry->relid,NULL))) |
| 61 | +{ |
| 62 | +return;/* Obviously not our case */ |
| 63 | +} |
| 64 | + |
| 65 | +/* Extract join clauses which will separate partitions */ |
| 66 | +if (IS_OUTER_JOIN(extra->sjinfo->jointype)) |
| 67 | +{ |
| 68 | +extract_actual_join_clauses(joinrestrictclauses, |
| 69 | +&joinclauses,&otherclauses); |
| 70 | +} |
| 71 | +else |
| 72 | +{ |
| 73 | +/* We can treat all clauses alike for an inner join */ |
| 74 | +joinclauses=extract_actual_clauses(joinrestrictclauses, false); |
| 75 | +otherclauses=NIL; |
| 76 | +} |
| 77 | + |
| 78 | +paramsel=1.0; |
| 79 | +foreach (lc,joinclauses) |
| 80 | +{ |
| 81 | +WrapperNode*wrap; |
| 82 | + |
| 83 | +wrap=walk_expr_tree(NULL, (Expr*)lfirst(lc),inner_prel); |
| 84 | +paramsel *=wrap->paramsel; |
| 85 | +} |
| 86 | + |
| 87 | +foreach (lc,innerrel->pathlist) |
| 88 | +{ |
| 89 | +AppendPath*cur_inner_path= (AppendPath*)lfirst(lc); |
| 90 | + |
| 91 | +if (!IsA(cur_inner_path,AppendPath)) |
| 92 | +continue; |
| 93 | + |
| 94 | +outer=outerrel->cheapest_total_path; |
| 95 | + |
| 96 | +inner_required=bms_union(PATH_REQ_OUTER((Path*)cur_inner_path), |
| 97 | +bms_make_singleton(outerrel->relid)); |
| 98 | + |
| 99 | +inner=create_runtimeappend_path(root,cur_inner_path, |
| 100 | +get_appendrel_parampathinfo(innerrel, |
| 101 | +inner_required), |
| 102 | +joinclauses,paramsel); |
| 103 | + |
| 104 | +initial_cost_nestloop(root,&workspace,jointype, |
| 105 | +outer,inner, |
| 106 | +extra->sjinfo,&extra->semifactors); |
| 107 | + |
| 108 | +pathkeys=build_join_pathkeys(root,joinrel,jointype,outer->pathkeys); |
| 109 | + |
| 110 | +nest_path=create_nestloop_path(root,joinrel,jointype,&workspace, |
| 111 | +extra->sjinfo,&extra->semifactors, |
| 112 | +outer,inner,extra->restrictlist, |
| 113 | +pathkeys, |
| 114 | +calc_nestloop_required_outer(outer,inner)); |
| 115 | + |
| 116 | +add_path(joinrel, (Path*)nest_path); |
| 117 | +} |
| 118 | +} |
| 119 | + |
| 120 | +/* |
| 121 | + * Main hook. All the magic goes here |
| 122 | + */ |
| 123 | +void |
| 124 | +pathman_rel_pathlist_hook(PlannerInfo*root,RelOptInfo*rel,Indexrti,RangeTblEntry*rte) |
| 125 | +{ |
| 126 | +PartRelationInfo*prel=NULL; |
| 127 | +RangeTblEntry**new_rte_array; |
| 128 | +RelOptInfo**new_rel_array; |
| 129 | +boolfound; |
| 130 | +intlen; |
| 131 | +intfirst_child_relid=0; |
| 132 | + |
| 133 | +/* Invoke original hook if needed */ |
| 134 | +if (set_rel_pathlist_hook_next!=NULL) |
| 135 | +set_rel_pathlist_hook_next(root,rel,rti,rte); |
| 136 | + |
| 137 | +if (!pg_pathman_enable) |
| 138 | +return; |
| 139 | + |
| 140 | +/* This works only for SELECT queries */ |
| 141 | +if (root->parse->commandType!=CMD_SELECT|| !inheritance_disabled) |
| 142 | +return; |
| 143 | + |
| 144 | +/* Lookup partitioning information for parent relation */ |
| 145 | +prel=get_pathman_relation_info(rte->relid,&found); |
| 146 | + |
| 147 | +if (prel!=NULL&&found) |
| 148 | +{ |
| 149 | +ListCell*lc; |
| 150 | +inti; |
| 151 | +Oid*dsm_arr; |
| 152 | +List*ranges, |
| 153 | +*wrappers; |
| 154 | +PathKey*pathkeyAsc=NULL, |
| 155 | +*pathkeyDesc=NULL; |
| 156 | +doubleparamsel=1.0; |
| 157 | + |
| 158 | +if (prel->parttype==PT_RANGE) |
| 159 | +{ |
| 160 | +/* |
| 161 | + * Get pathkeys for ascending and descending sort by partition |
| 162 | + * column |
| 163 | + */ |
| 164 | +List*pathkeys; |
| 165 | +Var*var; |
| 166 | +Oidvartypeid, |
| 167 | +varcollid; |
| 168 | +int32type_mod; |
| 169 | +TypeCacheEntry*tce; |
| 170 | + |
| 171 | +/* Make Var from patition column */ |
| 172 | +get_rte_attribute_type(rte,prel->attnum, |
| 173 | +&vartypeid,&type_mod,&varcollid); |
| 174 | +var=makeVar(rti,prel->attnum,vartypeid,type_mod,varcollid,0); |
| 175 | +var->location=-1; |
| 176 | + |
| 177 | +/* Determine operator type */ |
| 178 | +tce=lookup_type_cache(var->vartype,TYPECACHE_LT_OPR |TYPECACHE_GT_OPR); |
| 179 | + |
| 180 | +/* Make pathkeys */ |
| 181 | +pathkeys=build_expression_pathkey(root, (Expr*)var,NULL, |
| 182 | +tce->lt_opr,NULL, false); |
| 183 | +if (pathkeys) |
| 184 | +pathkeyAsc= (PathKey*)linitial(pathkeys); |
| 185 | +pathkeys=build_expression_pathkey(root, (Expr*)var,NULL, |
| 186 | +tce->gt_opr,NULL, false); |
| 187 | +if (pathkeys) |
| 188 | +pathkeyDesc= (PathKey*)linitial(pathkeys); |
| 189 | +} |
| 190 | + |
| 191 | +rte->inh= true; |
| 192 | +dsm_arr= (Oid*)dsm_array_get_pointer(&prel->children); |
| 193 | +ranges=list_make1_int(make_irange(0,prel->children_count-1, false)); |
| 194 | + |
| 195 | +/* Make wrappers over restrictions and collect final rangeset */ |
| 196 | +wrappers=NIL; |
| 197 | +foreach(lc,rel->baserestrictinfo) |
| 198 | +{ |
| 199 | +WrapperNode*wrap; |
| 200 | + |
| 201 | +RestrictInfo*rinfo= (RestrictInfo*)lfirst(lc); |
| 202 | + |
| 203 | +wrap=walk_expr_tree(NULL,rinfo->clause,prel); |
| 204 | +paramsel *=wrap->paramsel; |
| 205 | +wrappers=lappend(wrappers,wrap); |
| 206 | +ranges=irange_list_intersect(ranges,wrap->rangeset); |
| 207 | +} |
| 208 | + |
| 209 | +/* |
| 210 | + * Expand simple_rte_array and simple_rel_array |
| 211 | + */ |
| 212 | + |
| 213 | +if (ranges) |
| 214 | +{ |
| 215 | +len=irange_list_length(ranges); |
| 216 | + |
| 217 | +/* Expand simple_rel_array and simple_rte_array */ |
| 218 | +new_rel_array= (RelOptInfo**) |
| 219 | +palloc0((root->simple_rel_array_size+len)*sizeof(RelOptInfo*)); |
| 220 | + |
| 221 | +/* simple_rte_array is an array equivalent of the rtable list */ |
| 222 | +new_rte_array= (RangeTblEntry**) |
| 223 | +palloc0((root->simple_rel_array_size+len)*sizeof(RangeTblEntry*)); |
| 224 | + |
| 225 | +/* Copy relations to the new arrays */ |
| 226 | +for (i=0;i<root->simple_rel_array_size;i++) |
| 227 | + { |
| 228 | +new_rel_array[i]=root->simple_rel_array[i]; |
| 229 | +new_rte_array[i]=root->simple_rte_array[i]; |
| 230 | + } |
| 231 | + |
| 232 | +/* Free old arrays */ |
| 233 | +pfree(root->simple_rel_array); |
| 234 | +pfree(root->simple_rte_array); |
| 235 | + |
| 236 | +root->simple_rel_array_size+=len; |
| 237 | +root->simple_rel_array=new_rel_array; |
| 238 | +root->simple_rte_array=new_rte_array; |
| 239 | +} |
| 240 | + |
| 241 | +/* |
| 242 | + * Iterate all indexes in rangeset and append corresponding child |
| 243 | + * relations. |
| 244 | + */ |
| 245 | +foreach(lc,ranges) |
| 246 | +{ |
| 247 | +IndexRangeirange=lfirst_irange(lc); |
| 248 | +OidchildOid; |
| 249 | + |
| 250 | +for (i=irange_lower(irange);i <=irange_upper(irange);i++) |
| 251 | +{ |
| 252 | +intidx; |
| 253 | + |
| 254 | +childOid=dsm_arr[i]; |
| 255 | +idx=append_child_relation(root,rel,rti,rte,i,childOid,wrappers); |
| 256 | + |
| 257 | +if (!first_child_relid) |
| 258 | +first_child_relid=idx; |
| 259 | +} |
| 260 | +} |
| 261 | + |
| 262 | +/* Clear old path list */ |
| 263 | +list_free(rel->pathlist); |
| 264 | + |
| 265 | +rel->pathlist=NIL; |
| 266 | +set_append_rel_pathlist(root,rel,rti,rte,pathkeyAsc,pathkeyDesc); |
| 267 | +set_append_rel_size(root,rel,rti,rte); |
| 268 | + |
| 269 | +if (!pg_pathman_enable_runtimeappend) |
| 270 | +return; |
| 271 | + |
| 272 | +foreach (lc,rel->pathlist) |
| 273 | +{ |
| 274 | +AppendPath*cur_path= (AppendPath*)lfirst(lc); |
| 275 | +Relidsinner_required=PATH_REQ_OUTER((Path*)cur_path); |
| 276 | +ParamPathInfo*ppi=get_appendrel_parampathinfo(rel,inner_required); |
| 277 | +Path*inner_path; |
| 278 | +ListCell*subpath_cell; |
| 279 | +List*runtime_quals=NIL; |
| 280 | + |
| 281 | +if (!IsA(cur_path,AppendPath)|| |
| 282 | +rel->has_eclass_joins|| |
| 283 | +rel->joininfo) |
| 284 | +{ |
| 285 | +continue; |
| 286 | +} |
| 287 | + |
| 288 | +foreach (subpath_cell,cur_path->subpaths) |
| 289 | +{ |
| 290 | +Path*subpath= (Path*)lfirst(subpath_cell); |
| 291 | +RelOptInfo*child_rel=subpath->parent; |
| 292 | +List*quals; |
| 293 | +ListCell*qual_cell; |
| 294 | +ReplaceVarsContextrepl_var_cxt; |
| 295 | + |
| 296 | +repl_var_cxt.child=subpath->parent; |
| 297 | +repl_var_cxt.parent=rel; |
| 298 | +repl_var_cxt.sublevels_up=0; |
| 299 | + |
| 300 | +quals=extract_actual_clauses(child_rel->baserestrictinfo, false); |
| 301 | + |
| 302 | +/* Do not proceed if there's a rel containing quals without params */ |
| 303 | +if (!clause_contains_params((Node*)quals)) |
| 304 | +{ |
| 305 | +runtime_quals=NIL;/* skip this path */ |
| 306 | +break; |
| 307 | +} |
| 308 | + |
| 309 | +/* Replace child Vars with a parent rel's Var */ |
| 310 | +quals= (List*)replace_child_vars_with_parent_var((Node*)quals, |
| 311 | +&repl_var_cxt); |
| 312 | + |
| 313 | +/* Combine unique quals for RuntimeAppend */ |
| 314 | +foreach (qual_cell,quals) |
| 315 | +runtime_quals=list_append_unique(runtime_quals, |
| 316 | + (Node*)lfirst(qual_cell)); |
| 317 | +} |
| 318 | + |
| 319 | +/* |
| 320 | + * Dismiss RuntimeAppend if there |
| 321 | + * are no parameterized quals |
| 322 | + */ |
| 323 | +if (runtime_quals==NIL) |
| 324 | +continue; |
| 325 | + |
| 326 | +inner_path=create_runtimeappend_path(root,cur_path, |
| 327 | +ppi,runtime_quals, |
| 328 | +paramsel); |
| 329 | + |
| 330 | +add_path(rel,inner_path); |
| 331 | +} |
| 332 | +} |
| 333 | +} |