|
| 1 | +/* ------------------------------------------------------------------------- |
| 2 | + * |
| 3 | + * nodeCustomTempScan.c |
| 4 | + *Implements strategy which allows to build and execute partial paths for |
| 5 | + *a query which contains tempoorary table scans. |
| 6 | + * |
| 7 | + * Copyright (c) 2017-2024, Postgres Professional |
| 8 | + * |
| 9 | + * IDENTIFICATION |
| 10 | + *contrib/tempscan/nodeCustomTempScan.c |
| 11 | + * |
| 12 | + * ------------------------------------------------------------------------- |
| 13 | + */ |
| 14 | +#include"postgres.h" |
| 15 | + |
| 16 | +#include"nodes/extensible.h" |
| 17 | +#include"optimizer/cost.h" |
| 18 | +#include"optimizer/pathnode.h" |
| 19 | +#include"optimizer/paths.h" |
| 20 | +#include"utils/guc.h" |
| 21 | +#include"utils/lsyscache.h" |
| 22 | +#include"utils/rel.h" |
| 23 | + |
| 24 | +PG_MODULE_MAGIC; |
| 25 | + |
| 26 | +#defineMODULENAME"tempscan" |
| 27 | +#defineNODENAME"nodeCustomTempScan" |
| 28 | + |
| 29 | +staticPlan*create_partial_tempscan_plan(PlannerInfo*root, |
| 30 | +RelOptInfo*rel, |
| 31 | +CustomPath*best_path, |
| 32 | +List*tlist, |
| 33 | +List*scan_clauses, |
| 34 | +List*custom_plans); |
| 35 | +staticNode*create_tempscan_state(CustomScan*cscan); |
| 36 | +staticvoidBeginTempScan(CustomScanState*node,EState*estate,inteflags); |
| 37 | +staticTupleTableSlot*ExecTempScan(CustomScanState*node); |
| 38 | +staticvoidEndTempScan(CustomScanState*node); |
| 39 | +staticvoidReScanTempScan(CustomScanState*node); |
| 40 | + |
| 41 | +staticCustomPathMethodspath_methods= |
| 42 | +{ |
| 43 | +.CustomName=NODENAME, |
| 44 | +.PlanCustomPath=create_partial_tempscan_plan, |
| 45 | +.ReparameterizeCustomPathByChild=NULL |
| 46 | +}; |
| 47 | + |
| 48 | +staticCustomScanMethodsplan_methods= |
| 49 | +{ |
| 50 | +.CustomName=NODENAME, |
| 51 | +.CreateCustomScanState=create_tempscan_state |
| 52 | +}; |
| 53 | + |
| 54 | +staticCustomExecMethodsexec_methods= |
| 55 | +{ |
| 56 | +.CustomName=NODENAME, |
| 57 | + |
| 58 | +.BeginCustomScan=BeginTempScan, |
| 59 | +.ExecCustomScan=ExecTempScan, |
| 60 | +.EndCustomScan=EndTempScan, |
| 61 | +.ReScanCustomScan=ReScanTempScan, |
| 62 | +.MarkPosCustomScan=NULL, |
| 63 | +.RestrPosCustomScan=NULL, |
| 64 | +.EstimateDSMCustomScan=NULL, |
| 65 | +.InitializeDSMCustomScan=NULL, |
| 66 | +.ReInitializeDSMCustomScan=NULL, |
| 67 | +.InitializeWorkerCustomScan=NULL, |
| 68 | +.ShutdownCustomScan=NULL, |
| 69 | +.ExplainCustomScan=NULL |
| 70 | +}; |
| 71 | + |
| 72 | +staticset_rel_pathlist_hook_typeset_rel_pathlist_hook_next=NULL; |
| 73 | +staticbooltempscan_enable= false; |
| 74 | + |
| 75 | +void_PG_init(void); |
| 76 | + |
| 77 | +/* |
| 78 | + * The input path shouldn't be a part of the relation pathlist. |
| 79 | + */ |
| 80 | +staticCustomPath* |
| 81 | +create_partial_tempscan_path(PlannerInfo*root,RelOptInfo*rel, |
| 82 | +Path*path) |
| 83 | +{ |
| 84 | +CustomPath*cpath; |
| 85 | +Path*pathnode; |
| 86 | + |
| 87 | +cpath=makeNode(CustomPath); |
| 88 | +pathnode=&cpath->path; |
| 89 | + |
| 90 | +pathnode->pathtype=T_CustomScan; |
| 91 | +pathnode->parent=rel; |
| 92 | +pathnode->pathtarget=rel->reltarget; |
| 93 | +pathnode->rows=rel->rows; |
| 94 | + |
| 95 | +/* XXX: Just for now */ |
| 96 | +pathnode->param_info=NULL; |
| 97 | + |
| 98 | +pathnode->parallel_safe= true; |
| 99 | +pathnode->parallel_aware= false; |
| 100 | +pathnode->parallel_workers=path->parallel_workers; |
| 101 | + |
| 102 | +/* DEBUGGING purposes only */ |
| 103 | +pathnode->startup_cost=path->startup_cost /disable_cost; |
| 104 | +pathnode->total_cost=path->total_cost /disable_cost; |
| 105 | + |
| 106 | +cpath->custom_paths=list_make1(path); |
| 107 | +cpath->custom_private=NIL; |
| 108 | +cpath->custom_restrictinfo=NIL; |
| 109 | +cpath->methods=&path_methods; |
| 110 | + |
| 111 | +returncpath; |
| 112 | +} |
| 113 | + |
| 114 | +staticPlan* |
| 115 | +create_partial_tempscan_plan(PlannerInfo*root,RelOptInfo*rel, |
| 116 | +CustomPath*best_path,List*tlist, |
| 117 | +List*scan_clauses,List*custom_plans) |
| 118 | +{ |
| 119 | +CustomScan*cscan=makeNode(CustomScan); |
| 120 | + |
| 121 | +Assert(list_length(custom_plans)==1); |
| 122 | +Assert(best_path->path.parallel_safe= true&& |
| 123 | +best_path->path.parallel_workers>0); |
| 124 | + |
| 125 | + |
| 126 | +cscan->scan.plan.targetlist=cscan->custom_scan_tlist=tlist; |
| 127 | +cscan->scan.scanrelid=0; |
| 128 | +cscan->custom_exprs=NIL; |
| 129 | +cscan->custom_plans=custom_plans; |
| 130 | +cscan->methods=&plan_methods; |
| 131 | +cscan->flags=best_path->flags; |
| 132 | +cscan->custom_private=best_path->custom_private; |
| 133 | + |
| 134 | +return&cscan->scan.plan; |
| 135 | +} |
| 136 | + |
| 137 | +staticNode* |
| 138 | +create_tempscan_state(CustomScan*cscan) |
| 139 | +{ |
| 140 | +CustomScanState*cstate=makeNode(CustomScanState); |
| 141 | + |
| 142 | +cstate->methods=&exec_methods; |
| 143 | + |
| 144 | +return (Node*)cstate; |
| 145 | +} |
| 146 | + |
| 147 | +staticvoid |
| 148 | +BeginTempScan(CustomScanState*node,EState*estate,inteflags) |
| 149 | +{ |
| 150 | +CustomScan*cscan= (CustomScan*)node->ss.ps.plan; |
| 151 | +Plan*subplan; |
| 152 | +PlanState*pstate; |
| 153 | + |
| 154 | +Assert(list_length(cscan->custom_plans)==1); |
| 155 | + |
| 156 | +subplan= (Plan*)linitial(cscan->custom_plans); |
| 157 | +pstate=ExecInitNode(subplan,estate,eflags); |
| 158 | +node->custom_ps=lappend(node->custom_ps, (void*)pstate); |
| 159 | +} |
| 160 | + |
| 161 | +staticTupleTableSlot* |
| 162 | +ExecTempScan(CustomScanState*node) |
| 163 | +{ |
| 164 | +Assert(list_length(node->custom_ps)==1); |
| 165 | + |
| 166 | +returnExecProcNode((PlanState*)linitial(node->custom_ps)); |
| 167 | +} |
| 168 | + |
| 169 | +staticvoid |
| 170 | +EndTempScan(CustomScanState*node) |
| 171 | +{ |
| 172 | +ExecClearTuple(node->ss.ss_ScanTupleSlot); |
| 173 | +ExecEndNode((PlanState*)linitial(node->custom_ps)); |
| 174 | +} |
| 175 | + |
| 176 | +staticvoid |
| 177 | +ReScanTempScan(CustomScanState*node) |
| 178 | +{ |
| 179 | +PlanState*child; |
| 180 | + |
| 181 | +ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); |
| 182 | + |
| 183 | +child= (PlanState*)linitial(node->custom_ps); |
| 184 | + |
| 185 | +if (node->ss.ps.chgParam!=NULL) |
| 186 | +UpdateChangedParamSet(child,node->ss.ps.chgParam); |
| 187 | + |
| 188 | +ExecReScan(child); |
| 189 | +} |
| 190 | + |
| 191 | +/* |
| 192 | + * Try to add partial paths to the scan of a temporary table. |
| 193 | + * |
| 194 | + * In contrast to the hook on a JOIN paths creation, here we already at the end |
| 195 | + * of paths creation procedure, right before insertion of a gather node. |
| 196 | + * So, we can discover pathlist and choose any base path we can and want to use |
| 197 | + * in parallel scan. |
| 198 | + * |
| 199 | + * TODO: add inner strategy for temp table scan (parallel_workers == 0, |
| 200 | + * parallel_safe == true). Right now it looks a bit more difficult to implement. |
| 201 | + */ |
| 202 | +staticvoid |
| 203 | +try_partial_tempscan(PlannerInfo*root,RelOptInfo*rel,Indexrti, |
| 204 | +RangeTblEntry*rte) |
| 205 | +{ |
| 206 | +intparallel_workers; |
| 207 | +Path*path; |
| 208 | + |
| 209 | +/* |
| 210 | + * Some extension intercept this hook earlier. Allow it to do a work |
| 211 | + * before us. |
| 212 | + */ |
| 213 | +if (set_rel_pathlist_hook_next) |
| 214 | +(*set_rel_pathlist_hook_next)(root,rel,rti,rte); |
| 215 | + |
| 216 | +if (rel->consider_parallel) |
| 217 | +return; |
| 218 | + |
| 219 | +if (rte->rtekind!=RTE_RELATION|| |
| 220 | +get_rel_persistence(rte->relid)!=RELPERSISTENCE_TEMP) |
| 221 | +return; |
| 222 | + |
| 223 | +parallel_workers=compute_parallel_worker(rel,rel->pages,-1, |
| 224 | +max_parallel_workers_per_gather); |
| 225 | + |
| 226 | +/* If any limit was set to zero, the user doesn't want a parallel scan. */ |
| 227 | +if (parallel_workers <=0) |
| 228 | +return; |
| 229 | + |
| 230 | +/* HACK */ |
| 231 | +rel->consider_parallel= true; |
| 232 | + |
| 233 | +path=create_seqscan_path(root,rel,NULL,parallel_workers); |
| 234 | +if (path) |
| 235 | +{ |
| 236 | +/* Add an unordered partial path based on a parallel sequential scan. */ |
| 237 | +add_partial_path(rel, (Path*) |
| 238 | +create_partial_tempscan_path(root,rel,path)); |
| 239 | +} |
| 240 | + |
| 241 | +if (!bms_equal(rel->relids,root->all_query_rels)) |
| 242 | +rel->consider_parallel= false; |
| 243 | +Assert(IsA(linitial(rel->partial_pathlist),CustomPath)); |
| 244 | +} |
| 245 | + |
| 246 | +void |
| 247 | +_PG_init(void) |
| 248 | +{ |
| 249 | +DefineCustomBoolVariable("tempscan.enable", |
| 250 | +"Enable feature of the parallel temporary table scan.", |
| 251 | +"Right now no any other purpose except debugging", |
| 252 | +&tempscan_enable, |
| 253 | + false, |
| 254 | +PGC_SUSET, |
| 255 | +0, |
| 256 | +NULL, |
| 257 | +NULL, |
| 258 | +NULL |
| 259 | +); |
| 260 | + |
| 261 | +set_rel_pathlist_hook_next=set_rel_pathlist_hook; |
| 262 | +set_rel_pathlist_hook=try_partial_tempscan; |
| 263 | + |
| 264 | +MarkGUCPrefixReserved(MODULENAME); |
| 265 | +} |