*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 1479,1484 **** _outPlannerInfo(StringInfo str, PlannerInfo *node)
--- 1479,1485 ----
  	WRITE_NODE_FIELD(group_pathkeys);
  	WRITE_NODE_FIELD(distinct_pathkeys);
  	WRITE_NODE_FIELD(sort_pathkeys);
+ 	WRITE_NODE_FIELD(window_pathkeys);
  	WRITE_FLOAT_FIELD(total_table_pages, "%.0f");
  	WRITE_FLOAT_FIELD(tuple_fraction, "%.4f");
  	WRITE_BOOL_FIELD(hasJoinRTEs);
*** a/src/backend/optimizer/plan/planmain.c
--- b/src/backend/optimizer/plan/planmain.c
***************
*** 233,238 **** query_planner(PlannerInfo *root, List *tlist,
--- 233,239 ----
  	 */
  	root->query_pathkeys = canonicalize_pathkeys(root, root->query_pathkeys);
  	root->group_pathkeys = canonicalize_pathkeys(root, root->group_pathkeys);
+ 	root->window_pathkeys = canonicalize_pathkeys(root, root->window_pathkeys);
  	root->distinct_pathkeys = canonicalize_pathkeys(root, root->distinct_pathkeys);
  	root->sort_pathkeys = canonicalize_pathkeys(root, root->sort_pathkeys);
  
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
***************
*** 902,907 **** grouping_planner(PlannerInfo *root, double tuple_fraction)
--- 902,948 ----
  		else
  			root->group_pathkeys = NIL;
  
+ 		/*
+ 		 * Currently we don't relocate each Window node based on
+ 		 * cost estimation; it'd be better to think about the order
+ 		 * of each node execution. But for now we only think about
+ 		 * the bottom node pathkeys. This should be fixed.
+ 		 */
+ 		if (parse->windowList)
+ 		{
+ 			ListCell   *l;
+ 
+ 			foreach(l, parse->windowList)
+ 			{
+ 				List	   *partition_pathkeys = NIL;
+ 				List	   *order_pathkeys = NIL;
+ 				WindowClause *wc = (WindowClause *) lfirst(l);
+ 
+ 				if (wc->partitionClause &&
+ 					grouping_is_sortable(wc->partitionClause))
+ 					partition_pathkeys =
+ 						make_pathkeys_for_sortclauses(root,
+ 													  wc->partitionClause,
+ 													  tlist,
+ 													  false);
+ 
+ 				if (wc->orderClause &&
+ 					grouping_is_sortable(wc->orderClause))
+ 					order_pathkeys =
+ 						make_pathkeys_for_sortclauses(root,
+ 													  wc->orderClause,
+ 													  tlist,
+ 													  false);
+ 
+ 				root->window_pathkeys = list_concat(partition_pathkeys, order_pathkeys);
+ 				/*
+ 				 * Window node may be stacked more than one, but
+ 				 * what is effective to query_planner() is only the bottom pathkeys.
+ 				 */
+ 				break;
+ 			}
+ 		}
+ 
  		if (parse->distinctClause &&
  			grouping_is_sortable(parse->distinctClause))
  			root->distinct_pathkeys =
***************
*** 954,959 **** grouping_planner(PlannerInfo *root, double tuple_fraction)
--- 995,1002 ----
  		 */
  		if (root->group_pathkeys)
  			root->query_pathkeys = root->group_pathkeys;
+ 		else if (root->window_pathkeys)
+ 			root->query_pathkeys = root->window_pathkeys;
  		else if (list_length(root->distinct_pathkeys) >
  				 list_length(root->sort_pathkeys))
  			root->query_pathkeys = root->distinct_pathkeys;
***************
*** 1277,1283 **** grouping_planner(PlannerInfo *root, double tuple_fraction)
  				continue;
  
  			/*
! 			 * Currently, Window Partitioning strategy is only by Sort. 
  			 * So just join partitionClause and orderClause
  			 * to match Grouping. Hashing algorithm will be considered later.
  			 */
--- 1320,1326 ----
  				continue;
  
  			/*
! 			 * Currently, Window partitioning is only by Sort. 
  			 * So just join partitionClause and orderClause
  			 * to match Grouping. Hashing algorithm will be considered later.
  			 */
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
***************
*** 174,179 **** typedef struct PlannerInfo
--- 174,181 ----
  	List	   *distinct_pathkeys;	/* distinctClause pathkeys, if any */
  	List	   *sort_pathkeys;		/* sortClause pathkeys, if any */
  
+ 	List	   *window_pathkeys;	/* pathkeys of bottom Window, if any */
+ 
  	List	   *initial_rels;	/* RelOptInfos we are now trying to join */
  
  	MemoryContext planner_cxt;	/* context holding PlannerInfo */
