@@ -20,12 +20,30 @@ CustomScanMethodspickyappend_plan_methods;
2020CustomExecMethods pickyappend_exec_methods ;
2121
2222
23+ /*
24+ * Element of the plan_state_table
25+ */
2326typedef struct
2427{
2528Oid relid ;/* relid of the corresponding partition */
2629PlanState * ps ;
2730}PreservedPlanState ;
2831
32+ /* Compare plans by 'original_order' */
33+ static int
34+ cmp_child_scan_common_by_orig_order (const void * ap ,
35+ const void * bp )
36+ {
37+ ChildScanCommon a = * (ChildScanCommon * )ap ;
38+ ChildScanCommon b = * (ChildScanCommon * )bp ;
39+
40+ if (a -> original_order > b -> original_order )
41+ return 1 ;
42+ else if (a -> original_order < b -> original_order )
43+ return -1 ;
44+ else
45+ return 0 ;
46+ }
2947
3048static void
3149free_child_scan_common_array (ChildScanCommon * cur_plans ,int n )
@@ -45,14 +63,11 @@ free_child_scan_common_array(ChildScanCommon *cur_plans, int n)
4563static void
4664clear_plan_states (PickyAppendState * scan_state )
4765{
48- PreservedPlanState * pps ;
49- HASH_SEQ_STATUS seqstat ;
66+ ListCell * state_cell ;
5067
51- hash_seq_init (& seqstat ,scan_state -> plan_state_table );
52-
53- while ((pps = (PreservedPlanState * )hash_seq_search (& seqstat )))
68+ foreach (state_cell ,scan_state -> css .custom_ps )
5469{
55- ExecEndNode (pps -> ps );
70+ ExecEndNode (( PlanState * ) lfirst ( state_cell ) );
5671}
5772}
5873
@@ -73,15 +88,25 @@ transform_plans_into_states(PickyAppendState *scan_state,
7388 (const void * )& child -> relid ,
7489HASH_ENTER ,& pps_found );
7590
91+ /* Create new node since this plan hasn't been used yet */
7692if (!pps_found )
7793{
7894pps -> ps = ExecInitNode (child -> content .plan ,estate ,0 );
95+ /* Explain and clear_plan_states rely on this list */
7996scan_state -> css .custom_ps = lappend (scan_state -> css .custom_ps ,pps -> ps );
8097}
81- else
98+
99+ /* Node with params will be ReScanned */
100+ if (scan_state -> css .ss .ps .chgParam )
101+ UpdateChangedParamSet (pps -> ps ,scan_state -> css .ss .ps .chgParam );
102+
103+ /*
104+ * We should ReScan this node manually since
105+ * ExecProcNode won't do this for us in this case.
106+ */
107+ if (!pps -> ps -> chgParam )
82108ExecReScan (pps -> ps );
83109
84- child -> content_type = CHILD_PLAN_STATE ;
85110child -> content .plan_state = pps -> ps ;
86111}
87112}
@@ -119,6 +144,7 @@ select_required_plans(HTAB *children_table, Oid *parts, int nparts, int *nres)
119144return result ;
120145}
121146
147+ /* Transform partition ranges into plain array of partition Oids */
122148static Oid *
123149get_partition_oids (List * ranges ,int * n ,PartRelationInfo * prel )
124150{
@@ -216,7 +242,6 @@ create_pickyappend_path(PlannerInfo *root,
216242Index relindex = path -> parent -> relid ;
217243ChildScanCommon child = palloc (sizeof (ChildScanCommonData ));
218244
219- child -> content_type = CHILD_PATH ;
220245child -> content .path = path ;
221246child -> relid = root -> simple_rte_array [relindex ]-> relid ;
222247Assert (child -> relid != InvalidOid );
@@ -305,6 +330,7 @@ save_pickyappend_private(CustomScan *cscan, PickyAppendPath *path)
305330pfree (children [i ]);
306331}
307332
333+ /* Save main table and partition relids */
308334custom_private = list_make2 (list_make1_oid (path -> relid ),custom_oids );
309335
310336cscan -> custom_private = custom_private ;
@@ -319,24 +345,30 @@ unpack_pickyappend_private(PickyAppendState *scan_state, CustomScan *cscan)
319345int nchildren = list_length (custom_oids );
320346HTAB * children_table = scan_state -> children_table ;
321347HASHCTL * children_table_config = & scan_state -> children_table_config ;
348+ int i ;
322349
323350memset (children_table_config ,0 ,sizeof (HASHCTL ));
324351children_table_config -> keysize = sizeof (Oid );
325352children_table_config -> entrysize = sizeof (ChildScanCommonData );
326353
327354children_table = hash_create ("Plan storage" ,nchildren ,
328- children_table_config ,HASH_ELEM |HASH_BLOBS );
355+ children_table_config ,
356+ HASH_ELEM |HASH_BLOBS );
329357
358+ i = 0 ;
330359forboth (oid_cell ,custom_oids ,plan_cell ,cscan -> custom_plans )
331360{
332- Oid cur_oid = lfirst_oid (oid_cell );
361+ bool child_found ;
362+ Oid cur_oid = lfirst_oid (oid_cell );
333363
334- ChildScanCommon child = hash_search (children_table ,
335- (const void * )& cur_oid ,
336- HASH_ENTER ,NULL );
364+ ChildScanCommon child = hash_search (children_table ,
365+ (const void * )& cur_oid ,
366+ HASH_ENTER ,& child_found );
367+
368+ Assert (!child_found );/* there should be no collisions */
337369
338- child -> content_type = CHILD_PLAN ;
339370child -> content .plan = (Plan * )lfirst (plan_cell );
371+ child -> original_order = i ++ ;/* will be used in EXPLAIN */
340372}
341373
342374scan_state -> children_table = children_table ;
@@ -379,7 +411,9 @@ pickyappend_create_scan_state(CustomScan *node)
379411
380412unpack_pickyappend_private (scan_state ,node );
381413
414+ /* Fill in relation info using main table's relid */
382415scan_state -> prel = get_pathman_relation_info (scan_state -> relid ,NULL );
416+ Assert (scan_state -> prel );
383417
384418scan_state -> cur_plans = NULL ;
385419scan_state -> ncur_plans = 0 ;
@@ -400,7 +434,8 @@ pickyappend_begin(CustomScanState *node, EState *estate, int eflags)
400434plan_state_table_config -> entrysize = sizeof (PreservedPlanState );
401435
402436plan_state_table = hash_create ("PlanState storage" ,128 ,
403- plan_state_table_config ,HASH_ELEM |HASH_BLOBS );
437+ plan_state_table_config ,
438+ HASH_ELEM |HASH_BLOBS );
404439
405440scan_state -> plan_state_table = plan_state_table ;
406441scan_state -> custom_expr_states = (List * )ExecInitExpr ((Expr * )scan_state -> custom_exprs ,
@@ -474,9 +509,10 @@ pickyappend_rescan(CustomScanState *node)
474509ranges = irange_list_intersect (ranges ,wn -> rangeset );
475510}
476511
512+ /* Get Oids of the required partitions */
477513parts = get_partition_oids (ranges ,& nparts ,prel );
478514
479- /* Select new plans for thispass */
515+ /* Select new plans for thisrun using 'parts' */
480516free_child_scan_common_array (scan_state -> cur_plans ,scan_state -> ncur_plans );
481517scan_state -> cur_plans = select_required_plans (scan_state -> children_table ,
482518parts ,nparts ,
@@ -490,11 +526,54 @@ pickyappend_rescan(CustomScanState *node)
490526scan_state -> css .ss .ps .state );
491527
492528scan_state -> running_idx = 0 ;
493-
494- /* elog(LOG, "nparts: %d, plans: %d", nparts, nplans); */
495529}
496530
497531void
498532pickyppend_explain (CustomScanState * node ,List * ancestors ,ExplainState * es )
499533{
534+ PickyAppendState * scan_state = (PickyAppendState * )node ;
535+
536+ /* Construct excess PlanStates */
537+ if (!es -> analyze )
538+ {
539+ int allocated = 10 ;
540+ int used = 0 ;
541+ ChildScanCommon * custom_ps = palloc (allocated * sizeof (ChildScanCommon ));
542+ ChildScanCommon child ;
543+ HASH_SEQ_STATUS seqstat ;
544+ int i ;
545+
546+ /* There can't be any nodes since we're not scanning anything */
547+ Assert (!node -> custom_ps );
548+
549+ hash_seq_init (& seqstat ,scan_state -> children_table );
550+
551+ while ((child = (ChildScanCommon )hash_seq_search (& seqstat )))
552+ {
553+ if (allocated <=used )
554+ {
555+ allocated *=2 ;
556+ custom_ps = repalloc (custom_ps ,allocated * sizeof (ChildScanCommon ));
557+ }
558+
559+ custom_ps [used ++ ]= child ;
560+ }
561+
562+ /*
563+ * We have to restore the original plan order
564+ * which has been lost within the hash table
565+ */
566+ qsort (custom_ps ,used ,sizeof (ChildScanCommon ),
567+ cmp_child_scan_common_by_orig_order );
568+
569+ /*
570+ * These PlanStates will be used by EXPLAIN,
571+ * pickyappend_end will destroy them eventually
572+ */
573+ for (i = 0 ;i < used ;i ++ )
574+ node -> custom_ps = lappend (node -> custom_ps ,
575+ ExecInitNode (custom_ps [i ]-> content .plan ,
576+ node -> ss .ps .state ,
577+ 0 ));
578+ }
500579}