11#include "postgres.h"
22#include "optimizer/paths.h"
33#include "nodes_common.h"
4+ #include "pickyappend.h"
45
56
7+ static void
8+ free_child_scan_common_array (ChildScanCommon * cur_plans ,int n )
9+ {
10+ int i ;
11+
12+ if (!cur_plans )
13+ return ;
14+
15+ /* We shouldn't free inner objects e.g. Plans here */
16+ for (i = 0 ;i < n ;i ++ )
17+ pfree (cur_plans [i ]);
18+
19+ pfree (cur_plans );
20+ }
21+
22+ static void
23+ transform_plans_into_states (PickyAppendState * scan_state ,
24+ ChildScanCommon * selected_plans ,int n ,
25+ EState * estate )
26+ {
27+ int i ;
28+
29+ for (i = 0 ;i < n ;i ++ )
30+ {
31+ ChildScanCommon child = selected_plans [i ];
32+ PreservedPlanState * pps ;
33+ bool pps_found ;
34+
35+ pps = (PreservedPlanState * )hash_search (scan_state -> plan_state_table ,
36+ (const void * )& child -> relid ,
37+ HASH_ENTER ,& pps_found );
38+
39+ /* Create new node since this plan hasn't been used yet */
40+ if (!pps_found )
41+ {
42+ pps -> ps = ExecInitNode (child -> content .plan ,estate ,0 );
43+ /* Explain and clear_plan_states rely on this list */
44+ scan_state -> css .custom_ps = lappend (scan_state -> css .custom_ps ,pps -> ps );
45+ }
46+
47+ /* Node with params will be ReScanned */
48+ if (scan_state -> css .ss .ps .chgParam )
49+ UpdateChangedParamSet (pps -> ps ,scan_state -> css .ss .ps .chgParam );
50+
51+ /*
52+ * We should ReScan this node manually since
53+ * ExecProcNode won't do this for us in this case.
54+ */
55+ if (!pps -> ps -> chgParam )
56+ ExecReScan (pps -> ps );
57+
58+ child -> content .plan_state = pps -> ps ;
59+ }
60+ }
61+
62+ static ChildScanCommon *
63+ select_required_plans (HTAB * children_table ,Oid * parts ,int nparts ,int * nres )
64+ {
65+ int allocated = 10 ;
66+ int used = 0 ;
67+ ChildScanCommon * result = palloc (10 * sizeof (ChildScanCommon ));
68+ int i ;
69+
70+ for (i = 0 ;i < nparts ;i ++ )
71+ {
72+ ChildScanCommon child_copy ;
73+ ChildScanCommon child = hash_search (children_table ,
74+ (const void * )& parts [i ],
75+ HASH_FIND ,NULL );
76+ if (!child )
77+ continue ;
78+
79+ if (allocated <=used )
80+ {
81+ allocated *=2 ;
82+ result = repalloc (result ,allocated * sizeof (ChildScanCommon ));
83+ }
84+
85+ child_copy = palloc (sizeof (ChildScanCommonData ));
86+ memcpy (child_copy ,child ,sizeof (ChildScanCommonData ));
87+
88+ result [used ++ ]= child_copy ;
89+ }
90+
91+ * nres = used ;
92+ return result ;
93+ }
94+
95+ /* Transform partition ranges into plain array of partition Oids */
96+ static Oid *
97+ get_partition_oids (List * ranges ,int * n ,PartRelationInfo * prel )
98+ {
99+ ListCell * range_cell ;
100+ int allocated = 10 ;
101+ int used = 0 ;
102+ Oid * result = palloc (allocated * sizeof (Oid ));
103+ Oid * children = dsm_array_get_pointer (& prel -> children );
104+
105+ foreach (range_cell ,ranges )
106+ {
107+ int i ;
108+ int a = irange_lower (lfirst_irange (range_cell ));
109+ int b = irange_upper (lfirst_irange (range_cell ));
110+
111+ for (i = a ;i <=b ;i ++ )
112+ {
113+ if (allocated <=used )
114+ {
115+ allocated *=2 ;
116+ result = repalloc (result ,allocated * sizeof (Oid ));
117+ }
118+
119+ Assert (i < prel -> children_count );
120+ result [used ++ ]= children [i ];
121+ }
122+ }
123+
124+ * n = used ;
125+ return result ;
126+ }
127+
6128/* Compare plans by 'original_order' */
7129static int
8130cmp_child_scan_common_by_orig_order (const void * ap ,
@@ -19,8 +141,255 @@ cmp_child_scan_common_by_orig_order(const void *ap,
19141return 0 ;
20142}
21143
144+ static void
145+ pack_pickyappend_private (CustomScan * cscan ,PickyAppendPath * path )
146+ {
147+ ChildScanCommon * children = path -> children ;
148+ int nchildren = path -> nchildren ;
149+ List * custom_private = NIL ;
150+ List * custom_oids = NIL ;
151+ int i ;
152+
153+ for (i = 0 ;i < nchildren ;i ++ )
154+ {
155+ /* We've already filled 'custom_paths' in create_pickyappend_path */
156+ custom_oids = lappend_oid (custom_oids ,children [i ]-> relid );
157+ pfree (children [i ]);
158+ }
159+
160+ /* Save main table and partition relids */
161+ custom_private = list_make2 (list_make1_oid (path -> relid ),custom_oids );
162+
163+ cscan -> custom_private = custom_private ;
164+ }
165+
166+ static void
167+ unpack_pickyappend_private (PickyAppendState * scan_state ,CustomScan * cscan )
168+ {
169+ ListCell * oid_cell ;
170+ ListCell * plan_cell ;
171+ List * custom_oids = (List * )lsecond (cscan -> custom_private );
172+ int nchildren = list_length (custom_oids );
173+ HTAB * children_table = scan_state -> children_table ;
174+ HASHCTL * children_table_config = & scan_state -> children_table_config ;
175+ int i ;
176+
177+ memset (children_table_config ,0 ,sizeof (HASHCTL ));
178+ children_table_config -> keysize = sizeof (Oid );
179+ children_table_config -> entrysize = sizeof (ChildScanCommonData );
180+
181+ children_table = hash_create ("Plan storage" ,nchildren ,
182+ children_table_config ,
183+ HASH_ELEM |HASH_BLOBS );
184+
185+ i = 0 ;
186+ forboth (oid_cell ,custom_oids ,plan_cell ,cscan -> custom_plans )
187+ {
188+ bool child_found ;
189+ Oid cur_oid = lfirst_oid (oid_cell );
190+
191+ ChildScanCommon child = hash_search (children_table ,
192+ (const void * )& cur_oid ,
193+ HASH_ENTER ,& child_found );
194+
195+ Assert (!child_found );/* there should be no collisions */
196+
197+ child -> content .plan = (Plan * )lfirst (plan_cell );
198+ child -> original_order = i ++ ;/* will be used in EXPLAIN */
199+ }
200+
201+ scan_state -> children_table = children_table ;
202+ scan_state -> relid = linitial_oid (linitial (cscan -> custom_private ));
203+ }
204+
205+ Path *
206+ create_append_path_common (PlannerInfo * root ,
207+ AppendPath * inner_append ,
208+ ParamPathInfo * param_info ,
209+ List * picky_clauses ,
210+ CustomPathMethods * path_methods )
211+ {
212+ RelOptInfo * innerrel = inner_append -> path .parent ;
213+ ListCell * lc ;
214+ int i ;
215+
216+ RangeTblEntry * inner_entry = root -> simple_rte_array [innerrel -> relid ];
217+
218+ PickyAppendPath * result ;
219+
220+ result = palloc0 (sizeof (PickyAppendPath ));
221+ NodeSetTag (result ,T_CustomPath );
222+
223+ result -> cpath .path .pathtype = T_CustomScan ;
224+ result -> cpath .path .parent = innerrel ;
225+ result -> cpath .path .param_info = param_info ;
226+ result -> cpath .path .pathkeys = NIL ;
227+ #if PG_VERSION_NUM >=90600
228+ result -> cpath .path .pathtarget = inner_append -> path .pathtarget ;
229+ #endif
230+ result -> cpath .path .rows = inner_append -> path .rows ;
231+ result -> cpath .flags = 0 ;
232+ result -> cpath .methods = path_methods ;
233+
234+ /* TODO: real costs */
235+ result -> cpath .path .startup_cost = 0 ;
236+ result -> cpath .path .total_cost = 0 ;
237+
238+ /* Set 'partitioned column'-related clauses */
239+ result -> cpath .custom_private = picky_clauses ;
240+ result -> cpath .custom_paths = NIL ;
241+
242+ Assert (inner_entry -> relid != 0 );
243+ result -> relid = inner_entry -> relid ;
244+
245+ result -> nchildren = list_length (inner_append -> subpaths );
246+ result -> children = palloc (result -> nchildren * sizeof (ChildScanCommon ));
247+ i = 0 ;
248+ foreach (lc ,inner_append -> subpaths )
249+ {
250+ Path * path = lfirst (lc );
251+ Index relindex = path -> parent -> relid ;
252+ ChildScanCommon child = palloc (sizeof (ChildScanCommonData ));
253+
254+ child -> content .path = path ;
255+ child -> relid = root -> simple_rte_array [relindex ]-> relid ;
256+ Assert (child -> relid != InvalidOid );
257+
258+ result -> cpath .custom_paths = lappend (result -> cpath .custom_paths ,
259+ child -> content .path );
260+ result -> children [i ]= child ;
261+
262+ i ++ ;
263+ }
264+
265+ return & result -> cpath .path ;
266+ }
267+
268+ Plan *
269+ create_append_plan_common (PlannerInfo * root ,RelOptInfo * rel ,
270+ CustomPath * best_path ,List * tlist ,
271+ List * clauses ,List * custom_plans ,
272+ CustomScanMethods * scan_methods )
273+ {
274+ PickyAppendPath * gpath = (PickyAppendPath * )best_path ;
275+ CustomScan * cscan ;
276+
277+ cscan = makeNode (CustomScan );
278+ cscan -> scan .plan .qual = NIL ;
279+ cscan -> scan .plan .targetlist = tlist ;
280+ cscan -> custom_scan_tlist = tlist ;
281+ cscan -> scan .scanrelid = 0 ;
282+
283+ cscan -> custom_exprs = gpath -> cpath .custom_private ;
284+ cscan -> custom_plans = custom_plans ;
285+
286+ cscan -> methods = scan_methods ;
287+
288+ pack_pickyappend_private (cscan ,gpath );
289+
290+ return & cscan -> scan .plan ;
291+ }
292+
293+ Node *
294+ create_append_scan_state_common (CustomScan * node ,
295+ CustomExecMethods * exec_methods ,
296+ uint32 size )
297+ {
298+ PickyAppendState * scan_state = palloc0 (size );
299+
300+ NodeSetTag (scan_state ,T_CustomScanState );
301+ scan_state -> css .flags = node -> flags ;
302+ scan_state -> css .methods = exec_methods ;
303+ scan_state -> custom_exprs = node -> custom_exprs ;
304+
305+ unpack_pickyappend_private (scan_state ,node );
306+
307+ /* Fill in relation info using main table's relid */
308+ scan_state -> prel = get_pathman_relation_info (scan_state -> relid ,NULL );
309+ Assert (scan_state -> prel );
310+
311+ scan_state -> cur_plans = NULL ;
312+ scan_state -> ncur_plans = 0 ;
313+ scan_state -> running_idx = 0 ;
314+
315+ return (Node * )scan_state ;
316+ }
317+
318+ void
319+ begin_append_common (CustomScanState * node ,EState * estate ,int eflags )
320+ {
321+ PickyAppendState * scan_state = (PickyAppendState * )node ;
322+ HTAB * plan_state_table = scan_state -> plan_state_table ;
323+ HASHCTL * plan_state_table_config = & scan_state -> plan_state_table_config ;
324+
325+ memset (plan_state_table_config ,0 ,sizeof (HASHCTL ));
326+ plan_state_table_config -> keysize = sizeof (Oid );
327+ plan_state_table_config -> entrysize = sizeof (PreservedPlanState );
328+
329+ plan_state_table = hash_create ("PlanState storage" ,128 ,
330+ plan_state_table_config ,
331+ HASH_ELEM |HASH_BLOBS );
332+
333+ scan_state -> plan_state_table = plan_state_table ;
334+ scan_state -> custom_expr_states = (List * )ExecInitExpr ((Expr * )scan_state -> custom_exprs ,
335+ (PlanState * )scan_state );
336+ }
337+
338+ void
339+ end_append_common (CustomScanState * node )
340+ {
341+ PickyAppendState * scan_state = (PickyAppendState * )node ;
342+
343+ clear_plan_states (& scan_state -> css );
344+ hash_destroy (scan_state -> plan_state_table );
345+ hash_destroy (scan_state -> children_table );
346+ }
347+
348+ void
349+ rescan_append_common (CustomScanState * node )
350+ {
351+ PickyAppendState * scan_state = (PickyAppendState * )node ;
352+ ExprContext * econtext = node -> ss .ps .ps_ExprContext ;
353+ PartRelationInfo * prel = scan_state -> prel ;
354+ List * ranges ;
355+ ListCell * lc ;
356+ Oid * parts ;
357+ int nparts ;
358+
359+ ranges = list_make1_int (make_irange (0 ,prel -> children_count - 1 , false));
360+
361+ foreach (lc ,scan_state -> custom_exprs )
362+ {
363+ WrapperNode * wn ;
364+ WalkerContext wcxt ;
365+
366+ wcxt .econtext = econtext ;
367+ wn = walk_expr_tree (& wcxt , (Expr * )lfirst (lc ),prel );
368+
369+ ranges = irange_list_intersect (ranges ,wn -> rangeset );
370+ }
371+
372+ /* Get Oids of the required partitions */
373+ parts = get_partition_oids (ranges ,& nparts ,prel );
374+
375+ /* Select new plans for this run using 'parts' */
376+ free_child_scan_common_array (scan_state -> cur_plans ,scan_state -> ncur_plans );
377+ scan_state -> cur_plans = select_required_plans (scan_state -> children_table ,
378+ parts ,nparts ,
379+ & scan_state -> ncur_plans );
380+ pfree (parts );
381+
382+ /* Transform selected plans into executable plan states */
383+ transform_plans_into_states (scan_state ,
384+ scan_state -> cur_plans ,
385+ scan_state -> ncur_plans ,
386+ scan_state -> css .ss .ps .state );
387+
388+ scan_state -> running_idx = 0 ;
389+ }
390+
22391void
23- explain_common (CustomScanState * node ,HTAB * children_table ,ExplainState * es )
392+ explain_append_common (CustomScanState * node ,HTAB * children_table ,ExplainState * es )
24393{
25394/* Construct excess PlanStates */
26395if (!es -> analyze )