text:
/*-------------------------------------------------------------------------
 *
 * pathnode.c
 *	  Routines to manipulate pathlists and create path nodes
 *
 * Portions Copyright (c) 2005-2008, Greenplum inc
 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.133 2006/10/04 00:29:55 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <math.h>

#include "catalog/pg_operator.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "optimizer/clauses.h"              /* contain_mutable_functions() */
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/tlist.h"
#include "parser/parse_expr.h"
#include "parser/parse_oper.h"
#include "parser/parsetree.h"
#include "utils/memutils.h"
#include "utils/selfuncs.h"
#include "utils/syscache.h"

#include "cdb/cdbpath.h"        /* cdb_join_motion() etc */

static void     cdb_set_cheapest_dedup(PlannerInfo *root, RelOptInfo *rel);
static bool     cdb_is_path_deduped(Path *path);
static bool     cdb_subpath_tried_postjoin_dedup(Path *path, Relids subqrelids);
static UniquePath  *create_limit1_path(PlannerInfo *root, Path *subpath);
static UniquePath  *make_limit1_path(Path *subpath);
static UniquePath  *make_unique_path(Path *subpath);
static List *translate_sub_tlist(List *tlist, int relid);
static bool query_is_distinct_for(Query *query, List *colnos);
static bool hash_safe_tlist(List *tlist);

#define FLATCOPY(newnode, node, nodetype)  \
	( (newnode) = makeNode(nodetype), \
	  memcpy((newnode), (node), sizeof(nodetype)) )

/*
 * pathnode_copy_node
 *    Returns a flat copy of the given Path node.
 */
Path *
pathnode_copy_node(const Path *s)
{
    MemoryContext   oldcontext;
    void           *t;

    if (!s)
        return NULL;

	/* Allocate in same context as parent rel for GEQO safety. */
	oldcontext = MemoryContextSwitchTo(GetMemoryChunkContext(s->parent));

    switch (s->type)
	{
	    case T_Path:
            FLATCOPY(t, s, Path);
            break;
	    case T_IndexPath:
            FLATCOPY(t, s, IndexPath);
            break;
	    case T_BitmapHeapPath:
            FLATCOPY(t, s, BitmapHeapPath);
            break;
		case T_BitmapAppendOnlyPath:
			FLATCOPY(t, s, BitmapAppendOnlyPath);
			break;
	    case T_BitmapAndPath:
            FLATCOPY(t, s, BitmapAndPath);
            break;
	    case T_BitmapOrPath:
            FLATCOPY(t, s, BitmapOrPath);
            break;
	    case T_TidPath:
            FLATCOPY(t, s, TidPath);
            break;
	    case T_AppendPath:
            FLATCOPY(t, s, AppendPath);
            break;
	    case T_ResultPath:
            FLATCOPY(t, s, ResultPath);
            break;
	    case T_MaterialPath:
            FLATCOPY(t, s, MaterialPath);
            break;
	    case T_UniquePath:
            FLATCOPY(t, s, UniquePath);
            break;
	    case T_NestPath:
            FLATCOPY(t, s, NestPath);
            break;
	    case T_MergePath:
            FLATCOPY(t, s, MergePath);
            break;
	    case T_HashPath:
            FLATCOPY(t, s, HashPath);
            break;
	    case T_CdbMotionPath:
            FLATCOPY(t, s, CdbMotionPath);
            break;
        default:
            t = NULL;           /* keep compiler quiet */
		    elog(ERROR, "unrecognized pathnode: %d", (int)s->type);
	}
    MemoryContextSwitchTo(oldcontext);
    return (Path *)t;
}                               /* pathnode_copy_node */

/*
 * pathnode_walk_node
 *    Calls a 'walker' function for the given Path node; or returns
 *    CdbVisit_Walk if 'path' is NULL.
 *
 *    If 'walker' returns CdbVisit_Walk, then this function calls
 *    pathnode_walk_kids() to visit the node's children, and returns
 *    the result.
 *
 *    If 'walker' returns CdbVisit_Skip, then this function immediately
 *    returns CdbVisit_Walk and does not visit the node's children.
 *
 *    If 'walker' returns CdbVisit_Stop or another value, then this function
 *    immediately returns that value and does not visit the node's children.
 *
 * pathnode_walk_list
 *    Calls pathnode_walk_node() for each Path node in the given List.
 *
 *    Quits if the result of pathnode_walk_node() is CdbVisit_Stop or another
 *    value other than CdbVisit_Walk, and returns that result without visiting
 *    any more nodes.
 *
 *    Returns CdbVisit_Walk if all of the subtrees return CdbVisit_Walk, or
 *    if the list is empty.
 *
 *    Note that this function never returns CdbVisit_Skip to its caller.
 *    Only the 'walker' can return CdbVisit_Skip.
 *
 * pathnode_walk_kids
 *    Calls pathnode_walk_node() for each child of the given Path node.
 *
 *    Quits if the result of pathnode_walk_node() is CdbVisit_Stop or another
 *    value other than CdbVisit_Walk, and returns that result without visiting
 *    any more nodes.
 *
 *    Returns CdbVisit_Walk if all of the children return CdbVisit_Walk, or
 *    if there are no children.
 *
 *    Note that this function never returns CdbVisit_Skip to its caller.
 *    Only the 'walker' can return CdbVisit_Skip.
 *
 * NB: All CdbVisitOpt values other than CdbVisit_Walk or CdbVisit_Skip are
 * treated as equivalent to CdbVisit_Stop.  Thus the walker can break out
 * of a traversal and at the same time return a smidgen of information to the
 * caller, perhaps to indicate the reason for termination.  For convenience,
 * a couple of alternative stopping codes are predefined for walkers to use at
 * their discretion: CdbVisit_Failure and CdbVisit_Success.
 */
// inline
CdbVisitOpt
pathnode_walk_node(Path            *path,
			       CdbVisitOpt    (*walker)(Path *path, void *context),
			       void            *context)
{
    CdbVisitOpt     whatnext;

    if (path == NULL)
        whatnext = CdbVisit_Walk;
    else
    {
        whatnext = walker(path, context);
        if (whatnext == CdbVisit_Walk)
            whatnext = pathnode_walk_kids(path, walker, context);
        else if (whatnext == CdbVisit_Skip)
            whatnext = CdbVisit_Walk;
    }
    Assert(whatnext != CdbVisit_Skip);
    return whatnext;
}	                            /* pathnode_walk_node */

CdbVisitOpt
pathnode_walk_list(List            *pathlist,
			       CdbVisitOpt    (*walker)(Path *path, void *context),
			       void            *context)
{
    ListCell       *cell;
    Path           *path;
    CdbVisitOpt     v = CdbVisit_Walk;

	foreach(cell, pathlist)
	{
        path = (Path *)lfirst(cell);
        v = pathnode_walk_node(path, walker, context);
        if (v != CdbVisit_Walk) /* stop */
            break;
	}
	return v;
}	                            /* pathnode_walk_list */

CdbVisitOpt
pathnode_walk_kids(Path            *path,
			       CdbVisitOpt    (*walker)(Path *path, void *context),
			       void            *context)
{
    CdbVisitOpt v;

    Assert(path != NULL);

    switch (path->pathtype)
    {
            case T_SeqScan:
            case T_ExternalScan:
            case T_AppendOnlyScan:
            case T_ParquetScan:
            case T_IndexScan:
            case T_TidScan:
            case T_SubqueryScan:
            case T_FunctionScan:
            case T_ValuesScan:
            case T_CteScan:
            case T_TableFunctionScan:
                    return CdbVisit_Walk;
            case T_BitmapHeapScan:
                    v = pathnode_walk_node(((BitmapHeapPath *)path)->bitmapqual, walker, context);
                    break;
            case T_BitmapAnd:
                    v = pathnode_walk_list(((BitmapAndPath *)path)->bitmapquals, walker, context);
                    break;
            case T_BitmapOr:
                    v = pathnode_walk_list(((BitmapOrPath *)path)->bitmapquals, walker, context);
                    break;
            case T_HashJoin:
            case T_MergeJoin:
                    v = pathnode_walk_node(((JoinPath *)path)->outerjoinpath, walker, context);
                    if (v != CdbVisit_Walk)     /* stop */
                            break;
                    v = pathnode_walk_node(((JoinPath *)path)->innerjoinpath, walker, context);
                    break;
            case T_NestLoop:
                    {
                            NestPath   *nestpath = (NestPath *)path;

                            v = pathnode_walk_node(nestpath->jpath.outerjoinpath, walker, context);
                            if (v != CdbVisit_Walk)     /* stop */
                                    break;
                            v = pathnode_walk_node(nestpath->jpath.innerjoinpath, walker, context);
                            if (v != CdbVisit_Walk)     /* stop */
                                    break;
                    }
                    break;
            case T_Append:
                    v = pathnode_walk_list(((AppendPath *)path)->subpaths, walker, context);
                    break;
            case T_Result:
                    v = pathnode_walk_node(((ResultPath *)path)->subpath, walker, context);
                    break;
            case T_Material:
                    v = pathnode_walk_node(((MaterialPath *)path)->subpath, walker, context);
                    break;
            case T_Unique:
                    v = pathnode_walk_node(((UniquePath *)path)->subpath, walker, context);
                    break;
            case T_Motion:
                    v = pathnode_walk_node(((CdbMotionPath *)path)->subpath, walker, context);
                    break;
            default:
                    v = CdbVisit_Walk;  /* keep compiler quiet */
                    elog(ERROR, "unrecognized path type: %d", (int)path->pathtype);
    }
    return v;
}	                            /* pathnode_walk_kids */

/*****************************************************************************
 *		MISC. PATH UTILITIES
 *****************************************************************************/

/*
 * compare_path_costs
 *	  Return -1, 0, or +1 according as path1 is cheaper, the same cost,
 *	  or more expensive than path2 for the specified criterion.
 */
int
compare_path_costs(Path *path1, Path *path2, CostSelector criterion)
{
	if (criterion == STARTUP_COST)
	{
		if (path1->startup_cost < path2->startup_cost)
			return -1;
		if (path1->startup_cost > path2->startup_cost)
			return +1;

		/*
		 * If paths have the same startup cost (not at all unlikely), order
		 * them by total cost.
		 */
		if (path1->total_cost < path2->total_cost)
			return -1;
		if (path1->total_cost > path2->total_cost)
			return +1;
	}
	else
	{
		if (path1->total_cost < path2->total_cost)
			return -1;
		if (path1->total_cost > path2->total_cost)
			return +1;

		/*
		 * If paths have the same total cost, order them by startup cost.
		 */
		if (path1->startup_cost < path2->startup_cost)
			return -1;
		if (path1->startup_cost > path2->startup_cost)
			return +1;
	}
	return 0;
}

/*
 * compare_fuzzy_path_costs
 *	  Return -1, 0, or +1 according as path1 is cheaper, the same cost,
 *	  or more expensive than path2 for the specified criterion.
 *
 * This differs from compare_path_costs in that we consider the costs the
 * same if they agree to within a "fuzz factor".  This is used by add_path
 * to avoid keeping both of a pair of paths that really have insignificantly
 * different cost.
 */
static int
compare_fuzzy_path_costs(Path *path1, Path *path2, CostSelector criterion)
{
	/*
	 * We use a fuzz factor of 1% of the smaller cost.
	 *
	 * XXX does this percentage need to be user-configurable?
	 */
	if (criterion == STARTUP_COST)
	{
		if (path1->startup_cost > path2->startup_cost * 1.01)
			return +1;
		if (path2->startup_cost > path1->startup_cost * 1.01)
			return -1;

		/*
		 * If paths have the same startup cost (not at all unlikely), order
		 * them by total cost.
		 */
		if (path1->total_cost > path2->total_cost * 1.01)
			return +1;
		if (path2->total_cost > path1->total_cost * 1.01)
			return -1;
	}
	else
	{
		if (path1->total_cost > path2->total_cost * 1.01)
			return +1;
		if (path2->total_cost > path1->total_cost * 1.01)
			return -1;

		/*
		 * If paths have the same total cost, order them by startup cost.
		 */
		if (path1->startup_cost > path2->startup_cost * 1.01)
			return +1;
		if (path2->startup_cost > path1->startup_cost * 1.01)
			return -1;
	}
	return 0;
}

/*
 * compare_path_fractional_costs
 *	  Return -1, 0, or +1 according as path1 is cheaper, the same cost,
 *	  or more expensive than path2 for fetching the specified fraction
 *	  of the total tuples.
 *
 * If fraction is <= 0 or > 1, we interpret it as 1, ie, we select the
 * path with the cheaper total_cost.
 */
int
compare_fractional_path_costs(Path *path1, Path *path2,
							  double fraction)
{
	Cost		cost1,
				cost2;

	if (fraction <= 0.0 || fraction >= 1.0)
		return compare_path_costs(path1, path2, TOTAL_COST);
	cost1 = path1->startup_cost +
		fraction * (path1->total_cost - path1->startup_cost);
	cost2 = path2->startup_cost +
		fraction * (path2->total_cost - path2->startup_cost);
	if (cost1 < cost2)
		return -1;
	if (cost1 > cost2)
		return +1;
	return 0;
}

/*
 * set_cheapest
 *	  Find the minimum-cost paths from among a relation's paths,
 *	  and save them in the rel's cheapest-path fields.
 *
 * This is normally called only after we've finished constructing the path
 * list for the rel node.
 *
 * If we find two paths of identical costs, try to keep the better-sorted one.
 * The paths might have unrelated sort orderings, in which case we can only
 * guess which might be better to keep, but if one is superior then we
 * definitely should keep it.
 */
void
set_cheapest(PlannerInfo *root, RelOptInfo *parent_rel)
{
	ListCell   *p;
	Path	   *cheapest_startup_path;
	Path	   *cheapest_total_path;
    CdbRelDedupInfo    *dedup_info = parent_rel->dedup_info;

	Assert(IsA(parent_rel, RelOptInfo));

    /* CDB: Add paths incorporating subquery duplicate suppression. */
    if (dedup_info &&
        dedup_info->later_dedup_pathlist)
    {
        cdb_set_cheapest_dedup(root, parent_rel);

        /* If main pathlist is empty, put the later_dedup paths there. */
        if (!parent_rel->pathlist &&
            dedup_info->later_dedup_pathlist)
        {
            parent_rel->pathlist = dedup_info->later_dedup_pathlist;
            parent_rel->cheapest_startup_path = dedup_info->cheapest_startup_path;
            parent_rel->cheapest_total_path = dedup_info->cheapest_total_path;
            dedup_info->later_dedup_pathlist = NIL;
            dedup_info->cheapest_startup_path = NULL;
            dedup_info->cheapest_total_path = NULL;
            Assert(parent_rel->cheapest_total_path);
            return;
        }
    }

    /* CDB: Empty pathlist is possible if user set some enable_xxx = off. */
    if (!parent_rel->pathlist)
    {
        parent_rel->cheapest_startup_path = parent_rel->cheapest_total_path = NULL;
        return;
    }

    cheapest_startup_path = cheapest_total_path = (Path *) linitial(parent_rel->pathlist);

	for_each_cell(p, lnext(list_head(parent_rel->pathlist)))
	{
		Path	   *path = (Path *) lfirst(p);
		int			cmp;

		cmp = compare_path_costs(cheapest_startup_path, path, STARTUP_COST);
		if (cmp > 0 ||
			(cmp == 0 &&
			 compare_pathkeys(cheapest_startup_path->pathkeys,
							  path->pathkeys) == PATHKEYS_BETTER2))
			cheapest_startup_path = path;

		cmp = compare_path_costs(cheapest_total_path, path, TOTAL_COST);
		if (cmp > 0 ||
			(cmp == 0 &&
			 compare_pathkeys(cheapest_total_path->pathkeys,
							  path->pathkeys) == PATHKEYS_BETTER2))
			cheapest_total_path = path;
	}

	parent_rel->cheapest_startup_path = cheapest_startup_path;
	parent_rel->cheapest_total_path = cheapest_total_path;

}                               /* set_cheapest */

/*
 * cdb_set_cheapest_dedup
 *
 * Called by set_cheapest() to augment a RelOptInfo with paths that
 * provide for subquery duplicate suppression.
 */
static void
cdb_set_cheapest_dedup(PlannerInfo *root, RelOptInfo *rel)
{
    CdbRelDedupInfo    *dedup = rel->dedup_info;
    ListCell           *cell;
	List               *save_pathlist;
	Path               *save_cheapest_startup;
	Path               *save_cheapest_total;
    Path               *subpath;
    UniquePath         *upath = NULL;

    /*
     * If rel has only 1 row, joining with it cannot produce duplicates.
     *
     * Some paths could have been added to the later_dedup_pathlist before
     * the discovery that at most one row can satisfy the predicates.
     * Transfer any such to the main pathlist.
     */
    if (rel->onerow)
    {
        /* Seize the later_dedup_pathlist. */
        save_pathlist = dedup->later_dedup_pathlist;
        dedup->later_dedup_pathlist = NIL;

        /* Add its paths to the main pathlist. */
        foreach(cell, save_pathlist)
            add_path(root, rel, (Path *)lfirst(cell));

        /* Verify that the paths weren't added to the wrong list. */
        Insist(!dedup->later_dedup_pathlist);
        return;
    }

    /*
     * Pick out the lowest-cost paths in later_dedup_pathlist.
     *
     * The choice is made by a recursive call to set_cheapest(), for which we
     * momentarily hijack the main pathlist fields in RelOptInfo.
     */
    save_pathlist = rel->pathlist;
    save_cheapest_startup = rel->cheapest_startup_path;
    save_cheapest_total = rel->cheapest_total_path;
    rel->pathlist = dedup->later_dedup_pathlist;
    rel->cheapest_startup_path = NULL;
    rel->cheapest_total_path = NULL;
    dedup->later_dedup_pathlist = NIL;  /* to stop the recursion */

    set_cheapest(root, rel);

    dedup->later_dedup_pathlist = rel->pathlist;
    dedup->cheapest_startup_path = rel->cheapest_startup_path;
    dedup->cheapest_total_path = rel->cheapest_total_path;
    rel->pathlist = save_pathlist;
    rel->cheapest_startup_path = save_cheapest_startup;
    rel->cheapest_total_path = save_cheapest_total;

    /*
     * If rel is the final result of a pulled-up uncorrelated "= ANY" subquery,
     * consider a path that yields the distinct rows of the subquery.
     *
     * For an uncorrelated "= ANY" subquery, we'll consider plans in which
     * we remove duplicates from the rel containing the subquery result,
     * before that is joined with any other rels.  An "=" predicate (which
     * was created by convert_IN_to_join()) ensures that at most one row of
     * the duplicate-free subquery result can join with any one row of the
     * subquery's parent query.
     *
     * (Formerly in PostgreSQL this pre-join duplicate suppression was invoked
     * via special jointype codes: JOIN_UNIQUE_OUTER and JOIN_UNIQUE_INNER.)
     *
     * CDB TODO: We wouldn't have to do anything special if we knew the
     * subquery result rows are distinct because of a UNIQUE constraint.
     *
     * CDB TODO: Correlated subquery could use JOIN_UNIQUE if all outer
     * refs can be pulled up above the UniquePath operator, or if umethod
     * is UNIQUE_PATH_NOOP.
     *
     * CDB TODO: Avoid sorting when the subpath is ordered on the
     * sub_targetlist columns.
     */
    if (dedup->join_unique_ininfo)
    {
    	Assert(dedup->join_unique_ininfo->sub_targetlist);
    	/* Top off the subpath with DISTINCT ON the result columns. */
       	upath = create_unique_exprlist_path(root,
    			dedup->cheapest_total_path,
    			dedup->join_unique_ininfo->sub_targetlist);

        /* Add to rel's main pathlist. */
        add_path(root, rel, (Path *)upath);
    }

    /*
     * Consider post-join duplicate removal.
     *
     * Post-join duplicate removal is done by inserting a Unique operator that
     * is DISTINCT ON the unique row identifiers of those relids that do *not*
     * derive from flattened subqueries.
     *
     * If there is at least one subquery whose required inputs are all present
     * in this rel, and no subquery having some inputs present and some inputs
     * absent, then cdb_make_rel_dedup_info() has set the try_postjoin_dedup
     * flag and filled in the prejoin_dedup_subqrelids field giving the relids
     * of those subqueries' own tables.  Those subqueries are candidates for
     * late duplicate removal.
     *
     * NB: "Required inputs" means the sublink's lefthand relids, the
     * subquery's own tables (the sublink's righthand relids), and the
     * relids of outer references.
     */
    else if (dedup->try_postjoin_dedup)
    {
        Relids  distinct_relids = NULL;

        Assert(dedup->prejoin_dedup_subqrelids);

        /* hide later_dedup_pathlist while we walk it, so add_path can't upd. */
        save_pathlist = dedup->later_dedup_pathlist;
        dedup->later_dedup_pathlist = NIL;

        foreach(cell, save_pathlist)
        {
            subpath = (Path *)lfirst(cell);
            Assert(!subpath->subq_complete);

            /*
             * When one subplan of a join is the source of all of the relids in
             * postjoin_dedup_allrelids, we can assume post-join duplicate
             * removal has already been considered upstream.  We could keep on
             * considering it after every subsequent join, to insert the Unique
             * op at the point where it is cheapest.  But we do not attempt
             * that, because at present the join result size estimates are
             * untrustworthy.
             *
             * CDB TODO: Could remove this after fixing the join selectivity.
             */
            if (cdb_subpath_tried_postjoin_dedup(subpath,
                                                 dedup->prejoin_dedup_subqrelids))
                continue;

            /*
             * Build set of tables whose row ids will be the DISTINCT ON keys.
             * The bitmapset object will be shared; don't free it!
             */
            if (!distinct_relids)
                distinct_relids =
                    bms_difference(rel->relids, dedup->prejoin_dedup_subqrelids);

            /*
             * Top off the subpath with DISTINCT ON the non-subquery row ids.
             * Add to rel's main pathlist.
             */
            upath = create_unique_rowid_path(root, subpath, distinct_relids);
            add_path(root, rel, (Path *)upath);
        }

        /* Verify that our new paths haven't gone into the wrong pathlist. */
        Insist(!dedup->later_dedup_pathlist);
        dedup->later_dedup_pathlist = save_pathlist;
    }

    /*
     * Don't consider plans that further delay duplicate suppression
     * after all subqueries have been fully evaluated.  (If a later join
     * happens to reduce the amount of data, duplicate removal could be
     * cheaper afterward and it would be better to wait.  However, we
     * don't currently trust the join result size estimates that much.)
     *
     * CDB TODO: If current query level has GROUP BY with only MIN/MAX aggs,
     * DISTINCT or LIMIT 1, retain these paths... but move them into the rel's
     * main pathlist since no extra step will be needed downstream for dedup.
     */
    if (dedup->no_more_subqueries)
        dedup->later_dedup_pathlist = NIL;

}                             /* cdb_set_cheapest_dedup */

/*
 * cdb_is_path_deduped
 *
 * Returns true if there is no flattened subquery whose tables are all present,
 * or if the given path takes care of all the duplicate suppression for such
 * subqueries, so that they require no further action downstream.
 *
 * Returns false if some flattened subquery has all tables present but still
 * needs duplicate suppression to be done downstream.
 *
 * Not affected by subqueries which have some but not all tables present.
 */
typedef struct CdbIsPathDedupedCtx
{
    Relids          all_subqrelids;
    Relids          deduped_relids;
    bool            deduped_unshared;
} CdbIsPathDedupedCtx;

static CdbVisitOpt
cdb_is_path_deduped_walker(Path *path, void* context)
{
    CdbIsPathDedupedCtx    *ctx = (CdbIsPathDedupedCtx *)context;
    RelOptInfo             *rel = path->parent;
    Relids                  deduped;

    /* Skip path unless rel includes all relids of some flattened subquery. */
    if (!rel->dedup_info ||
        !rel->dedup_info->prejoin_dedup_subqrelids)
        return CdbVisit_Skip;       /* no relids to add; don't visit children */

    /* Dedup usually wraps up all subqueries whose tables are all present. */
    deduped = rel->dedup_info->prejoin_dedup_subqrelids;

    /* Already satisfied? */
    if (path->subq_complete)
        goto put_deduped_result;

    /* If rel has only 1 row, joining with it cannot produce duplicates. */
    if (rel->onerow)
        goto put_deduped_result;

    switch (path->pathtype)
    {
        /*
         * Unique op is used only for subquery duplicate suppression, at present.
         * Subqueries whose relids are covered by a Unique op don't require
         * further duplicate suppression downstream.
         */
        case T_Unique:
            goto put_deduped_result;

        /*
         * Join with jointype JOIN_IN will suppress duplicates for all
         * subqueries whose relids are covered by the join's inner rel.
         */
        case T_HashJoin:
	    case T_MergeJoin:
	    case T_NestLoop:
        {
            JoinPath   *joinpath = (JoinPath *)path;
            RelOptInfo *inner_rel = joinpath->innerjoinpath->parent;
            CdbVisitOpt status;

            /* Visit the outer subpath. */
            status = pathnode_walk_node(joinpath->outerjoinpath,
                                        cdb_is_path_deduped_walker,
                                        ctx);
            if (status != CdbVisit_Walk)
                return status;

            /* Subqueries on inner side of JOIN_IN can't cause duplicates. */
            if (joinpath->jointype == JOIN_IN)
            {
                if (!inner_rel->dedup_info ||
                    !inner_rel->dedup_info->prejoin_dedup_subqrelids)
                    return CdbVisit_Walk;
                deduped = inner_rel->dedup_info->prejoin_dedup_subqrelids;
                goto put_deduped_result;
            }

            /* Visit the inner subpath. */
            return pathnode_walk_node(joinpath->innerjoinpath,
                                      cdb_is_path_deduped_walker,
                                      ctx);
        }

        default:
            break;
    }
    return CdbVisit_Walk;       /* onward to visit children */

    /*
     * Add this Path node's deduped relids to set.
     * To reduce allocations, try to return an existing bitmapset.
     */
put_deduped_result:
    if (!ctx->deduped_relids)
    {
        ctx->deduped_relids = deduped;
        ctx->deduped_unshared = false;
    }
    else if (ctx->deduped_unshared)
        ctx->deduped_relids = bms_add_members(ctx->deduped_relids, deduped);
    else
    {
        ctx->deduped_relids = bms_union(ctx->deduped_relids, deduped);
        ctx->deduped_unshared = true;
    }

    /*
     * All found?  Then stop.
     */
    if (bms_equal(ctx->deduped_relids, ctx->all_subqrelids))
        return CdbVisit_Success;

    /* Done with this branch; don't visit children, but proceed to next. */
    return CdbVisit_Skip;
}                               /* cdb_is_path_deduped_walker */

static bool
cdb_is_path_deduped(Path *path)
{
    CdbIsPathDedupedCtx context;
    CdbVisitOpt         status;

    if (!path->parent->dedup_info ||
        !path->parent->dedup_info->prejoin_dedup_subqrelids)
        return true;

    context.all_subqrelids = path->parent->dedup_info->prejoin_dedup_subqrelids;
    context.deduped_relids = NULL;
    context.deduped_unshared = false;

    status = pathnode_walk_node(path, cdb_is_path_deduped_walker, &context);

    if (context.deduped_unshared)
        bms_free(context.deduped_relids);

    return status == CdbVisit_Success;
}                               /* cdb_is_path_deduped */

/*
 * cdb_subpath_tried_postjoin_dedup
 *
 * Returns true if the given Path has a subpath in which post-join duplicate
 * suppression can be assumed to have been considered already for exactly the
 * given set of subquery relids.
 */
typedef struct CdbSubpathTriedPostjoinDedupCtx
{
    RelOptInfo     *given_rel;
    Relids          subqrelids;
} CdbSubpathTriedPostjoinDedupCtx;

static CdbVisitOpt
cdb_subpath_tried_postjoin_dedup_walker(Path *path, void* context)
{
    CdbSubpathTriedPostjoinDedupCtx *ctx = (CdbSubpathTriedPostjoinDedupCtx *)context;
    RelOptInfo *rel = path->parent;

    /* Descend thru parent_rel's Path nodes to top Path node of an input rel. */
    if (rel == ctx->given_rel)
        return CdbVisit_Walk;

    /* If none of the given relids come from this input rel, advance to next. */
    if (!bms_overlap(ctx->subqrelids, rel->relids))
        return CdbVisit_Skip;

    /* Succeed if rel is marked for post-join dedup of given subqueries. */
    if (rel->dedup_info &&
        rel->dedup_info->try_postjoin_dedup &&
        bms_equal(rel->dedup_info->prejoin_dedup_subqrelids, ctx->subqrelids))
        return CdbVisit_Success;

    return CdbVisit_Failure;
}                               /* cdb_subpath_tried_postjoin_dedup_walker */

static bool
cdb_subpath_tried_postjoin_dedup(Path *path, Relids subqrelids)
{
    CdbSubpathTriedPostjoinDedupCtx context;
    CdbVisitOpt             status;

    context.given_rel = path->parent;
    context.subqrelids = subqrelids;

    status = pathnode_walk_kids(path, cdb_subpath_tried_postjoin_dedup_walker, &context);

    return (status == CdbVisit_Success);
}                               /* cdb_subpath_tried_postjoin_dedup */

/*
 * add_path
 *	  Consider a potential implementation path for the specified parent rel,
 *	  and add it to the rel's pathlist if it is worthy of consideration.
 *	  A path is worthy if it has either a better sort order (better pathkeys)
 *	  or cheaper cost (on either dimension) than any of the existing old paths.
 *
 *	  We also remove from the rel's pathlist any old paths that are dominated
 *	  by new_path --- that is, new_path is both cheaper and at least as well
 *	  ordered.
 *
 *	  The pathlist is kept sorted by TOTAL_COST metric, with cheaper paths
 *	  at the front.  No code depends on that for correctness; it's simply
 *	  a speed hack within this routine.  Doing it that way makes it more
 *	  likely that we will reject an inferior path after a few comparisons,
 *	  rather than many comparisons.
 *
 *	  NOTE: discarded Path objects are immediately pfree'd to reduce planner
 *	  memory consumption.  We dare not try to free the substructure of a Path,
 *	  since much of it may be shared with other Paths or the query tree itself;
 *	  but just recycling discarded Path nodes is a very useful savings in
 *	  a large join tree.  We can recycle the List nodes of pathlist, too.
 *
 *    NB: The Path that is passed to add_path() must be considered invalid
 *    upon return, and not touched again by the caller, because we free it
 *    if we already know of a better path.  Likewise, a Path that is passed
 *    to add_path() must not be shared as a subpath of any other Path of the
 *    same join level.  Use pathnode_copy_node() to make a copy of the top
 *    Path node before calling add_path(); then it'll be ok to share the copy.
 *
 *	  BUT: we do not pfree IndexPath objects, since they may be referenced as
 *	  children of BitmapHeapPaths as well as being paths in their own right.
 *
 * 'parent_rel' is the relation entry to which the path corresponds.
 * 'new_path' is a potential path for parent_rel.
 *
 * Returns nothing, but modifies parent_rel->pathlist.
 */
void add_path(PlannerInfo *root, RelOptInfo *parent_rel, Path *new_path)
{
	bool		accept_new = true;		/* unless we find a superior old path */
	ListCell   *insert_after = NULL;	/* where to insert new item */
	ListCell   *p1_prev = NULL;
	ListCell   *p1;
    List      **which_pathlist;
    List       *pathlist;

    if (!new_path)
        return;

    Assert(cdbpathlocus_is_valid(new_path->locus));

   /*
     * CDB: Can we limit the rel to 1 row without affecting the query result?
     *
     * If rel's targetlist is empty, then only the rel's row count affects
     * downstream processing.  And if the rel consists only of subquery tables
     * which have not been joined with tables of the current query level, then
     * all that matters is whether the number of result rows is zero or nonzero.
     * We'll insert LIMIT 1 on top of every path.
     *
     * This situation won't come up often.  Flattened uncorrelated EXISTS
     * subqueries would be a typical example, if not for the fact that they
     * are pulled out for separate optimization as InitPlans.
     */
    if (!parent_rel->reltargetlist &&
        !parent_rel->onerow &&
        !bms_overlap(parent_rel->relids, root->currlevel_relids))
        new_path = (Path *)create_limit1_path(root, new_path);

    /*
     * CDB: Note whether duplicate suppression has been completed for all
     * flattened subqueries whose tables are all present.
     *
     * Paths that await later addition of duplicate suppression are kept in a
     * separate pathlist.
     *
     * NB: We set subq_complete false if all tables of a not-deduped subquery
     * are present, even if the not-deduped subquery is joined with a deduped
     * one.  The earlier deduping can't help the later one, except perhaps by
     * reducing the number of rows; so don't bother to keep flags about it.
     */
    Assert(!new_path->subq_complete);
    if (!parent_rel->dedup_info ||
        cdb_is_path_deduped(new_path))
    {
        new_path->subq_complete = true;
        which_pathlist = &parent_rel->pathlist;
        pathlist = *which_pathlist;
    }
    else
    {
        new_path->subq_complete = false;
        which_pathlist = &parent_rel->dedup_info->later_dedup_pathlist;
        pathlist = *which_pathlist;
    }

    /*
	 * Loop to check proposed new path against old paths.  Note it is possible
	 * for more than one old path to be tossed out because new_path dominates
	 * it.
	 */
	p1 = list_head(pathlist);		    /* cannot use foreach here */
	while (p1 != NULL)
	{
		Path	   *old_path = (Path *) lfirst(p1);
		bool		remove_old = false; /* unless new proves superior */
		int			costcmp;

		/*
		 * As of Postgres 8.0, we use fuzzy cost comparison to avoid wasting
		 * cycles keeping paths that are really not significantly different in
		 * cost.
		 */
		costcmp = compare_fuzzy_path_costs(new_path, old_path, TOTAL_COST);

		/*
		 * If the two paths compare differently for startup and total cost,
		 * then we want to keep both, and we can skip the (much slower)
		 * comparison of pathkeys.	If they compare the same, proceed with the
		 * pathkeys comparison.  Note: this test relies on the fact that
		 * compare_fuzzy_path_costs will only return 0 if both costs are
		 * effectively equal (and, therefore, there's no need to call it twice
		 * in that case).
		 */
		if (costcmp == 0 ||
			costcmp == compare_fuzzy_path_costs(new_path, old_path,
												STARTUP_COST))
		{
            /* Still a tie?  See which path has better pathkeys. */
            switch (compare_pathkeys(new_path->pathkeys, old_path->pathkeys))
			{
				case PATHKEYS_EQUAL:
					if (costcmp < 0)
						remove_old = true;		/* new dominates old */
					else if (costcmp > 0)
						accept_new = false;		/* old dominates new */
					else
					{
						/*
						 * Same pathkeys, and fuzzily the same cost, so keep
						 * just one --- but we'll do an exact cost comparison
						 * to decide which.
						 */
						if (compare_path_costs(new_path, old_path,
											   TOTAL_COST) < 0)
							remove_old = true;	/* new dominates old */
						else
							accept_new = false; /* old equals or dominates new */
					}
					break;
				case PATHKEYS_BETTER1:
					if (costcmp <= 0)
						remove_old = true;		/* new dominates old */
					break;
				case PATHKEYS_BETTER2:
					if (costcmp >= 0)
						accept_new = false;		/* old dominates new */
					break;
				case PATHKEYS_DIFFERENT:
					/* keep both paths, since they have different ordering */
					break;
			}
		}

		/*
		 * Remove current element from pathlist if dominated by new.
		 */
		if (remove_old)
		{
			pathlist = list_delete_cell(pathlist, p1, p1_prev);

			/*
			 * Delete the data pointed-to by the deleted cell, if possible
			 */
			if (!IsA(old_path, IndexPath))
			          pfree(old_path);

			/* Advance list pointer */
			if (p1_prev)
				p1 = lnext(p1_prev);
			else
				p1 = list_head(pathlist);
		}
		else
		{
			/* new belongs after this old path if it has cost >= old's */
			if (costcmp >= 0)
				insert_after = p1;
			/* Advance list pointers */
			p1_prev = p1;
			p1 = lnext(p1);
		}

		/*
		 * If we found an old path that dominates new_path, we can quit
		 * scanning the pathlist; we will not add new_path, and we assume
		 * new_path cannot dominate any other elements of the pathlist.
		 */
		if (!accept_new)
			break;
	}

	if (accept_new)
	{
		/* Accept the new path: insert it at proper place in pathlist */
		if (insert_after)
			lappend_cell(pathlist, insert_after, new_path);
		else
			pathlist = lcons(new_path, pathlist);
	}
	else
	{
		/* Reject and recycle the new path */
		if (!IsA(new_path, IndexPath))
			pfree(new_path);
	}

    /* Put back the updated pathlist ptr. */
    *which_pathlist = pathlist;
}                               /* add_path */

/*****************************************************************************
 *		PATH NODE CREATION ROUTINES
 *****************************************************************************/

/*
 * create_seqscan_path
 *	  Creates a path corresponding to a sequential scan, returning the
 *	  pathnode.
 */
Path *
create_seqscan_path(PlannerInfo *root, RelOptInfo *rel)
{
	Path	   *pathnode = makeNode(Path);

	pathnode->pathtype = T_SeqScan;
	pathnode->parent = rel;
	pathnode->pathkeys = NIL;	/* seqscan has unordered result */

    pathnode->locus = cdbpathlocus_from_baserel(root, rel);
    pathnode->motionHazard = false;
	pathnode->rescannable = true;

	cost_seqscan(pathnode, root, rel);

	return pathnode;
}

/*
 * Create a path for scanning an append-only table
 */
AppendOnlyPath *
create_appendonly_path(PlannerInfo *root, RelOptInfo *rel)
{
	AppendOnlyPath	   *pathnode = makeNode(AppendOnlyPath);

	pathnode->path.pathtype = T_AppendOnlyScan;
	pathnode->path.parent = rel;
	pathnode->path.pathkeys = NIL;	/* seqscan has unordered result */

    pathnode->path.locus = cdbpathlocus_from_baserel(root, rel);
    pathnode->path.motionHazard = false;
	pathnode->path.rescannable = true;

	cost_appendonlyscan(pathnode, root, rel);

	return pathnode;
}

/*
 * Create a path for scanning a parquet table
 */
ParquetPath *
create_parquet_path(PlannerInfo *root, RelOptInfo *rel)
{
	ParquetPath	   *pathnode = makeNode(ParquetPath);

	pathnode->path.pathtype = T_ParquetScan;
	pathnode->path.parent = rel;
	pathnode->path.pathkeys = NIL;	/* seqscan has unordered result */

    pathnode->path.locus = cdbpathlocus_from_baserel(root, rel);
    pathnode->path.motionHazard = false;
	pathnode->path.rescannable = true;

	cost_parquetscan(pathnode, root, rel);
	return pathnode;
}

/*
* Create a path for scanning an external table
 */
ExternalPath *
create_external_path(PlannerInfo *root, RelOptInfo *rel)
{
	ExternalPath   *pathnode = makeNode(ExternalPath);

	pathnode->path.pathtype = T_ExternalScan;
	pathnode->path.parent = rel;
	pathnode->path.pathkeys = NIL;	/* external scan has unordered result */

    pathnode->path.locus = cdbpathlocus_from_baserel(root, rel);
    pathnode->path.motionHazard = false;
	pathnode->path.rescannable = rel->isrescannable;

	cost_externalscan(pathnode, root, rel);

	return pathnode;
}

/*
 * create_index_path
 *	  Creates a path node for an index scan.
 *
 * 'index' is a usable index.
 * 'clause_groups' is a list of lists of RestrictInfo nodes
 *			to be used as index qual conditions in the scan.
 * 'pathkeys' describes the ordering of the path.
 * 'indexscandir' is ForwardScanDirection or BackwardScanDirection
 *			for an ordered index, or NoMovementScanDirection for
 *			an unordered index.
 * 'outer_rel' is the outer relation if this is a join inner indexscan path.
 *			(pathkeys and indexscandir are ignored if so.)	NULL if not.
 *
 * Returns the new path node.
 */
IndexPath *
create_index_path(PlannerInfo *root,
				  IndexOptInfo *index,
				  List *clause_groups,
				  List *pathkeys,
				  ScanDirection indexscandir,
				  RelOptInfo *outer_rel)
{
	IndexPath  *pathnode = makeNode(IndexPath);
	RelOptInfo *rel = index->rel;
	List	   *indexquals,
			   *allclauses;

	/*
	 * For a join inner scan, there's no point in marking the path with any
	 * pathkeys, since it will only ever be used as the inner path of a
	 * nestloop, and so its ordering does not matter.  For the same reason we
	 * don't really care what order it's scanned in.  (We could expect the
	 * caller to supply the correct values, but it's easier to force it here.)
	 */
	if (outer_rel != NULL)
	{
		pathkeys = NIL;
		indexscandir = NoMovementScanDirection;
	}

	pathnode->path.pathtype = T_IndexScan;
	pathnode->path.parent = rel;
	pathnode->path.pathkeys = pathkeys;

	/* Convert clauses to indexquals the executor can handle */
	indexquals = expand_indexqual_conditions(index, clause_groups);

	/* Flatten the clause-groups list to produce indexclauses list */
	allclauses = flatten_clausegroups_list(clause_groups);

	/* Fill in the pathnode */
	pathnode->indexinfo = index;
	pathnode->indexclauses = allclauses;
	pathnode->indexquals = indexquals;

	pathnode->isjoininner = (outer_rel != NULL);
	pathnode->indexscandir = indexscandir;

	if (outer_rel != NULL)
	{
		/*
		 * We must compute the estimated number of output rows for the
		 * indexscan.  This is less than rel->rows because of the additional
		 * selectivity of the join clauses.  Since clause_groups may contain
		 * both restriction and join clauses, we have to do a set union to get
		 * the full set of clauses that must be considered to compute the
		 * correct selectivity.  (Without the union operation, we might have
		 * some restriction clauses appearing twice, which'd mislead
		 * clauselist_selectivity into double-counting their selectivity.
		 * However, since RestrictInfo nodes aren't copied when linking them
		 * into different lists, it should be sufficient to use pointer
		 * comparison to remove duplicates.)
		 *
		 * Always assume the join type is JOIN_INNER; even if some of the join
		 * clauses come from other contexts, that's not our problem.
		 */
		allclauses = list_union_ptr(rel->baserestrictinfo, allclauses);
		pathnode->rows = rel->tuples *
			clauselist_selectivity(root,
								   allclauses,
								   rel->relid,	/* do not use 0! */
								   JOIN_INNER,
								   false /* use_damping */);
		/* Like costsize.c, force estimate to be at least one row */
		pathnode->rows = clamp_row_est(pathnode->rows);
	}
	else
	{
		/*
		 * The number of rows is the same as the parent rel's estimate, since
		 * this isn't a join inner indexscan.
		 */
		pathnode->rows = rel->rows;
	}

	/* Distribution is same as the base table. */
	pathnode->path.locus = cdbpathlocus_from_baserel(root, rel);
	pathnode->path.motionHazard = false;
	pathnode->path.rescannable = true;

	cost_index(pathnode, root, index, indexquals, outer_rel);

	return pathnode;
}

/*
 * create_bitmap_heap_path
 *	  Creates a path node for a bitmap scan.
 *
 * 'bitmapqual' is a tree of IndexPath, BitmapAndPath, and BitmapOrPath nodes.
 *
 * If this is a join inner indexscan path, 'outer_rel' is the outer relation,
 * and all the component IndexPaths should have been costed accordingly.
 */
BitmapHeapPath *
create_bitmap_heap_path(PlannerInfo *root,
						RelOptInfo *rel,
						Path *bitmapqual,
						RelOptInfo *outer_rel)
{
	BitmapHeapPath *pathnode = makeNode(BitmapHeapPath);

	pathnode->path.pathtype = T_BitmapHeapScan;
	pathnode->path.parent = rel;
	pathnode->path.pathkeys = NIL;		/* always unordered */

    /* Distribution is same as the base table. */
    pathnode->path.locus = cdbpathlocus_from_baserel(root, rel);
    pathnode->path.motionHazard = false;
    pathnode->path.rescannable = true;

	pathnode->bitmapqual = bitmapqual;
	pathnode->isjoininner = (outer_rel != NULL);

	if (pathnode->isjoininner)
	{
		/*
		 * We must compute the estimated number of output rows for the
		 * indexscan.  This is less than rel->rows because of the additional
		 * selectivity of the join clauses.  We make use of the selectivity
		 * estimated for the bitmap to do this; this isn't really quite right
		 * since there may be restriction conditions not included in the
		 * bitmap ...
		 */
		Cost		indexTotalCost;
		Selectivity indexSelectivity;

		cost_bitmap_tree_node(bitmapqual, &indexTotalCost, &indexSelectivity);
		pathnode->rows = rel->tuples * indexSelectivity;
		if (pathnode->rows > rel->rows)
			pathnode->rows = rel->rows;
		/* Like costsize.c, force estimate to be at least one row */
		pathnode->rows = clamp_row_est(pathnode->rows);
	}
	else
	{
		/*
		 * The number of rows is the same as the parent rel's estimate, since
		 * this isn't a join inner indexscan.
		 */
		pathnode->rows = rel->rows;
	}

	cost_bitmap_heap_scan(&pathnode->path, root, rel, bitmapqual, outer_rel);

	return pathnode;
}

/*
 * create_bitmap_and_path
 *	  Creates a path node representing a BitmapAnd.
 */
BitmapAndPath *
create_bitmap_and_path(PlannerInfo *root,
					   RelOptInfo *rel,
					   List *bitmapquals)
{
	BitmapAndPath *pathnode = makeNode(BitmapAndPath);

	pathnode->path.pathtype = T_BitmapAnd;
	pathnode->path.parent = rel;
	pathnode->path.pathkeys = NIL;		/* always unordered */

	pathnode->bitmapquals = bitmapquals;

	/* this sets bitmapselectivity as well as the regular cost fields: */
	cost_bitmap_and_node(pathnode, root);

	return pathnode;
}

/*
 * create_bitmap_or_path
 *	  Creates a path node representing a BitmapOr.
 */
BitmapOrPath *
create_bitmap_or_path(PlannerInfo *root,
					  RelOptInfo *rel,
					  List *bitmapquals)
{
	BitmapOrPath *pathnode = makeNode(BitmapOrPath);

	pathnode->path.pathtype = T_BitmapOr;
	pathnode->path.parent = rel;
	pathnode->path.pathkeys = NIL;		/* always unordered */

	pathnode->bitmapquals = bitmapquals;

	/* this sets bitmapselectivity as well as the regular cost fields: */
	cost_bitmap_or_node(pathnode, root);

	return pathnode;
}

/*
 * create_tidscan_path
 *	  Creates a path corresponding to a scan by TID, returning the pathnode.
 */
TidPath *
create_tidscan_path(PlannerInfo *root, RelOptInfo *rel, List *tidquals)
{
	TidPath    *pathnode = makeNode(TidPath);

	pathnode->path.pathtype = T_TidScan;
	pathnode->path.parent = rel;
	pathnode->path.pathkeys = NIL;

	pathnode->tidquals = tidquals;

    /* Distribution is same as the base table. */
    pathnode->path.locus = cdbpathlocus_from_baserel(root, rel);
    pathnode->path.motionHazard = false;
    pathnode->path.rescannable = true;

	cost_tidscan(&pathnode->path, root, rel, tidquals);

	return pathnode;
}

/*
 * create_append_path
 *	  Creates a path corresponding to an Append plan, returning the
 *	  pathnode.
 */
AppendPath *
create_append_path(PlannerInfo *root, RelOptInfo *rel, List *subpaths)
{
	AppendPath *pathnode = makeNode(AppendPath);
	ListCell   *l;

	pathnode->path.pathtype = T_Append;
	pathnode->path.parent = rel;
	pathnode->path.pathkeys = NIL;		/* result is always considered
										 * unsorted */
	pathnode->subpaths = NIL;

    pathnode->path.motionHazard = false;
    pathnode->path.rescannable = true;

	pathnode->path.startup_cost = 0;
	pathnode->path.total_cost = 0;

    /* If no subpath, any worker can execute this Append.  Result has 0 rows. */
    if (!subpaths)
        CdbPathLocus_MakeGeneral(&pathnode->path.locus);

    else
	{
		bool fIsNotPartitioned = false;

		/*
		 * Do a first pass over the children to determine if
		 * there's any child which is not partitioned, i.e. a bottleneck or replicated
		 */
		foreach(l, subpaths)
		{
			Path *subpath = (Path *) lfirst(l);

			if (CdbPathLocus_IsBottleneck(subpath->locus) ||
				CdbPathLocus_IsReplicated(subpath->locus))
			{
				fIsNotPartitioned = true;
				break;
			}
		}

		foreach(l, subpaths)
		{
			Path	       *subpath = (Path *) lfirst(l);
			CdbPathLocus    projectedlocus;

			/*
			 * In case any of the children is not partitioned convert all children
			 * to have singleQE locus
			 */
			if (fIsNotPartitioned &&
				!CdbPathLocus_IsSingleQE(subpath->locus))
			{
				CdbPathLocus    singleQE;
				CdbPathLocus_MakeSingleQE(&singleQE);

				subpath = cdbpath_create_motion_path(root, subpath, NIL, false, singleQE);
			}

			/* Transform subpath locus into the appendrel's space for comparison. */
			if (subpath->parent == rel ||
				subpath->parent->reloptkind != RELOPT_OTHER_MEMBER_REL)
				projectedlocus = subpath->locus;
			else
				projectedlocus =
					cdbpathlocus_pull_above_projection(root,
													   subpath->locus,
													   subpath->parent->relids,
													   subpath->parent->reltargetlist,
													   rel->reltargetlist,
													   rel->relid);

			if (l == list_head(subpaths))	/* first node? */
				pathnode->path.startup_cost = subpath->startup_cost;
			pathnode->path.total_cost += subpath->total_cost;

			/*
			 * CDB: If all the scans are distributed alike, set
			 * the result locus to match.  Otherwise, if all are partitioned,
			 * set it to strewn.  A mixture of partitioned and non-partitioned
			 * scans should not occur after above correction;
			 *
			 * CDB TODO: When the scans are not all partitioned alike, and the
			 * result is joined with another rel, consider pushing the join
			 * below the Append so that child tables that are properly
			 * distributed can be joined in place.
			 */
			if (l == list_head(subpaths))
				pathnode->path.locus = projectedlocus;
			else if (cdbpathlocus_compare(CdbPathLocus_Comparison_Equal,
										  pathnode->path.locus, projectedlocus))
			{}
			else if (CdbPathLocus_IsPartitioned(pathnode->path.locus) &&
					 CdbPathLocus_IsPartitioned(projectedlocus))
				CdbPathLocus_MakeStrewn(&pathnode->path.locus);
			else
				ereport(ERROR, (errcode(ERRCODE_GP_FEATURE_NOT_SUPPORTED),
								errmsg_internal("Cannot append paths with "
												"incompatible distribution")));

			if (subpath->motionHazard)
				pathnode->path.motionHazard = true;

			if ( !subpath->rescannable )
				pathnode->path.rescannable = false;

			pathnode->subpaths = lappend(pathnode->subpaths, subpath);
		}

		/*
		 * CDB: If there is exactly one subpath, its ordering is preserved.
		 * Child rel's pathkey exprs are already expressed in terms of the
		 * columns of the parent appendrel.  See find_usable_indexes().
		 */
		if (list_length(subpaths) == 1)
			pathnode->path.pathkeys = ((Path *)linitial(subpaths))->pathkeys;
	}
	return pathnode;
}

/*
 * create_result_path
 *	  Creates a path representing a Result-and-nothing-else plan.
 *	  This is only used for the case of a query with an empty jointree.
 */
ResultPath *
create_result_path(RelOptInfo *rel, Path *subpath, List *quals)
{
	ResultPath *pathnode = makeNode(ResultPath);

	pathnode->path.pathtype = T_Result;
	pathnode->path.parent = NULL;
	pathnode->path.pathkeys = NIL;
	pathnode->quals = quals;

	/* Ideally should define cost_result(), but I'm too lazy */
	pathnode->path.startup_cost = 0;
	pathnode->path.total_cost = cpu_tuple_cost;

	pathnode->subpath = subpath;
	pathnode->quals = quals;

	/* Ideally should define cost_result(), but I'm too lazy */
	if (subpath)
	{
		pathnode->path.startup_cost = subpath->startup_cost;
		pathnode->path.total_cost = subpath->total_cost;

        pathnode->path.locus = subpath->locus;
        pathnode->path.motionHazard = subpath->motionHazard;
		pathnode->path.rescannable = subpath->rescannable;
	}
	else
	{
		pathnode->path.startup_cost = 0;
		pathnode->path.total_cost = cpu_tuple_cost;

        CdbPathLocus_MakeGeneral(&pathnode->path.locus);
        pathnode->path.motionHazard = false;
        pathnode->path.rescannable = true;
	}

	/*
	 * In theory we should include the qual eval cost as well, but at present
	 * that doesn't accomplish much except duplicate work that will be done
	 * again in make_result; since this is only used for degenerate cases,
	 * nothing interesting will be done with the path cost values...
	 */

	return pathnode;
}

/*
 * create_material_path
 *	  Creates a path corresponding to a Material plan, returning the
 *	  pathnode.
 */
MaterialPath *
create_material_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath)
{
	MaterialPath *pathnode = makeNode(MaterialPath);

	pathnode->path.pathtype = T_Material;
	pathnode->path.parent = rel;

	pathnode->path.pathkeys = subpath->pathkeys;

    pathnode->path.locus = subpath->locus;
    pathnode->path.motionHazard = subpath->motionHazard;
    pathnode->cdb_strict = false;
    pathnode->path.rescannable = true; /* Independent of sub-path */

	pathnode->subpath = subpath;

	cost_material(&pathnode->path,
				  root,
				  subpath->total_cost,
				  cdbpath_rows(root, subpath),
				  rel->width);

	return pathnode;
}

/*
 * create_unique_path
 *	  Creates a path representing elimination of distinct rows from the
 *	  input data.
 */
static UniquePath *
create_unique_path(PlannerInfo *root,
                   Path        *subpath,
                   List        *distinct_on_exprs,
                   Relids       distinct_on_rowid_relids)
{
	UniquePath *pathnode;
	Path		sort_path;		/* dummy for result of cost_sort */
	Path		agg_path;		/* dummy for result of cost_agg */
    RelOptInfo *rel = subpath->parent;
	int			numCols;
    double      subpath_rows;
    bool        hashable = false;

    subpath_rows = cdbpath_rows(root, subpath);

    /* Allocate and partially initialize a UniquePath node. */
    pathnode = make_unique_path(subpath);

    /* Share caller's expr list and relids. */
    pathnode->distinct_on_exprs = distinct_on_exprs;
    pathnode->distinct_on_rowid_relids = distinct_on_rowid_relids;

	/*
	 * Treat the output as always unsorted, since we don't necessarily have
	 * pathkeys to represent it.
	 */
	pathnode->path.pathkeys = NIL;

	/*
	 * If we know the targetlist, try to estimate number of result rows;
	 * otherwise punt.
	 */
	if (distinct_on_exprs)
	{
		pathnode->rows = estimate_num_groups(root, distinct_on_exprs, subpath_rows);
		numCols = list_length(distinct_on_exprs);
	}
	else
	{
		pathnode->rows = subpath_rows;
		numCols = 1;
	}

	/*
	 * Estimate cost for sort+unique implementation
	 */
	cost_sort(&sort_path, root, NIL,
			  subpath->total_cost,
			  subpath_rows,
			  rel->width);

	/*
	 * Charge one cpu_operator_cost per comparison per input tuple. We assume
	 * all columns get compared at most of the tuples.	(XXX probably this is
	 * an overestimate.)  This should agree with make_unique.
	 */
	sort_path.total_cost += cpu_operator_cost * subpath_rows * numCols;

	/*
	 * Is it safe to use a hashed implementation?  If so, estimate and compare
	 * costs.  We only try this if we know the targetlist for sure (else we
	 * can't be sure about the datatypes involved).
     *
     * CDB: For dedup, the distinct_on_exprs list isn't built until later,
     * but we know the data types will be hashable.
	 */
	pathnode->umethod = UNIQUE_PATH_SORT;
	pathnode->path.startup_cost = sort_path.startup_cost;
	pathnode->path.total_cost = sort_path.total_cost;

    if (distinct_on_exprs && hash_safe_tlist(distinct_on_exprs))
        hashable = true;
    if (distinct_on_rowid_relids)
        hashable = true;
    if (hashable
    		&& (root->config->enable_hashagg
    				|| root->config->mpp_trying_fallback_plan
    		)
    )
	{
		/*
		 * Estimate the overhead per hashtable entry at 64 bytes (same as in
		 * planner.c).
		 */
		int			hashentrysize = rel->width + 64;

        /*
         * CDB TODO: Hybrid hashed aggregation is not limited by work_mem.
         */
        if (hashentrysize * pathnode->rows <= global_work_mem(root))
		{
			cost_agg(&agg_path, root,
					 AGG_HASHED, 0,
					 numCols, pathnode->rows,
					 subpath->startup_cost,
					 subpath->total_cost,
					 rel->rows, 0.0, 0.0, hashentrysize, false);
			if (agg_path.total_cost < sort_path.total_cost ||
                (!root->config->enable_sort && !root->config->mpp_trying_fallback_plan))
            {
                pathnode->umethod = UNIQUE_PATH_HASH;
		        pathnode->path.startup_cost = agg_path.startup_cost;
		        pathnode->path.total_cost = agg_path.total_cost;
            }
		}
	}

	/* see MPP-1140 */
	if (pathnode->umethod == UNIQUE_PATH_HASH)
	{
		/* hybrid hash agg is not rescannable, and may present a motion hazard */
		pathnode->path.motionHazard = subpath->motionHazard;
		pathnode->path.rescannable = false;
	}
	else
	{
		/* sort or plain implies materialization and breaks deadlock cycle.
		 *  (NB: Must not reset motionHazard when sort is eliminated due to
		 *  existing ordering; but Unique sort is never optimized away at present.)
		 */
		pathnode->path.motionHazard = subpath->motionHazard;

		/* Same reasoning applies to rescanablilty.  If no actual sort is placed
		 * in the plan, then rescannable is set correctly to the subpath value.
		 * If sort intervenes, it should be set to true.  We depend
		 * on the above claim that sort will always intervene.
		 */
		pathnode->path.rescannable = true;
	}

	return pathnode;
}                               /* create_unique_path */

/*
 * create_unique_exprlist_path
 *	  Creates a path representing elimination of distinct rows from the
 *	  input data.
 *
 * Returns a UniquePath node representing a "DISTINCT ON e1,...,en" operator,
 * where e1,...,en are the expressions in the 'distinct_on_exprs' list.
 *
 * NB: The returned node shares the 'distinct_on_exprs' list given by the
 * caller; the list and its members must not be changed or freed during the
 * node's lifetime.
 *
 * If a row's duplicates might occur in more than one partition, a Motion
 * operator will be needed to bring them together.  Since this path might
 * not be chosen, we won't take the time to create a CdbMotionPath node here.
 * Just estimate what the cost would be, and assign a dummy locus; leave
 * the real work for create_plan().
 */
UniquePath *
create_unique_exprlist_path(PlannerInfo    *root,
                            Path           *subpath,
                            List           *distinct_on_exprs)
{
	UniquePath *uniquepath;
    RelOptInfo *rel = subpath->parent;

    Assert(distinct_on_exprs);

	/*
	 * If the input is a subquery whose output must be unique already, then we
	 * don't need to do anything.  The test for uniqueness has to consider
	 * exactly which columns we are extracting; for example "SELECT DISTINCT
	 * x,y" doesn't guarantee that x alone is distinct. So we cannot check for
	 * this optimization unless we found our own targetlist above, and it
	 * consists only of simple Vars referencing subquery outputs.  (Possibly
	 * we could do something with expressions in the subquery outputs, too,
	 * but for now keep it simple.)
	 */
	if (rel->rtekind == RTE_SUBQUERY)
	{
		RangeTblEntry *rte = rt_fetch(rel->relid, root->parse->rtable);
		List	   *sub_tlist_colnos;

		sub_tlist_colnos = translate_sub_tlist(distinct_on_exprs, rel->relid);

		if (sub_tlist_colnos &&
			query_is_distinct_for(rte->subquery, sub_tlist_colnos))
		{
            uniquepath = make_unique_path(subpath);
			uniquepath->umethod = UNIQUE_PATH_NOOP;
			uniquepath->rows = cdbpath_rows(root, subpath);
            uniquepath->distinct_on_exprs = distinct_on_exprs;  /* share list */
			return uniquepath;
		}
	}

    /* Repartition first if duplicates might be on different QEs. */
    if (!CdbPathLocus_IsBottleneck(subpath->locus) &&
        !cdbpathlocus_is_hashed_on_exprs(subpath->locus, distinct_on_exprs))
    {
        CdbPathLocus    locus;

        locus = cdbpathlocus_from_exprs(root, distinct_on_exprs);
        subpath = cdbpath_create_motion_path(root, subpath, NIL, false, locus);
        Insist(subpath);
    }

    /* Create the UniquePath node.  (Might return NULL if enable_sort = off.) */
    uniquepath = create_unique_path(root, subpath, distinct_on_exprs, NULL);

	return uniquepath;
}                               /* create_unique_exprlist_path */

/*
 * create_unique_rowid_path
 *
 * CDB: Used when a subquery predicate (such as "x IN (SELECT ...)") has
 * been flattened into a join with the tables of the current query.  A row
 * of the cross-product of the current query's tables might join with more
 * than one row from the subquery and thus be duplicated.  Removing such
 * duplicates after the join is called deduping.
 *
 * Returns a UniquePath node representing a "DISTINCT ON r1,...,rn" operator,
 * where (r1,...,rn) represents a unique identifier for each row of the cross
 * product of the tables specified by the 'distinct_relids' parameter.
 *
 * NB: The returned node shares the given 'distinct_relids' bitmapset object;
 * so the caller must not free or modify it during the node's lifetime.
 *
 * If a row's duplicates might occur in more than one partition, a Motion
 * operator will be needed to bring them together.  Since this path might
 * not be chosen, we won't take the time to create a CdbMotionPath node here.
 * Just estimate what the cost would be, and assign a dummy locus; leave
 * the real work for create_plan().
 */
UniquePath *
create_unique_rowid_path(PlannerInfo *root,
                         Path        *subpath,
                         Relids       distinct_relids)
{
	UniquePath *uniquepath;

    Assert(!bms_is_empty(distinct_relids));

    /* Create the UniquePath node.  (Might return NULL if enable_sort = off.) */
    uniquepath = create_unique_path(root, subpath, NIL, distinct_relids);
    if (!uniquepath)
        return NULL;

    /* Add repartitioning cost if duplicates might be on different QEs. */
    if (!CdbPathLocus_IsBottleneck(subpath->locus) &&
        !cdbpathlocus_is_hashed_on_relids(subpath->locus, distinct_relids))
    {
        CdbMotionPath   motionpath;     /* dummy for cost estimate */
        Cost            repartition_cost;

        /* Tell create_unique_plan() to insert Motion operator atop subpath. */
        uniquepath->must_repartition = true;

        /* Set a fake locus.  Repartitioning key won't be built until later. */
        CdbPathLocus_MakeStrewn(&uniquepath->path.locus);

        /* Estimate repartitioning cost. */
        memset(&motionpath, 0, sizeof(motionpath));
        motionpath.path.type = T_CdbMotionPath;
        motionpath.path.parent = subpath->parent;
        motionpath.path.locus = uniquepath->path.locus;
        motionpath.subpath = subpath;
        cdbpath_cost_motion(root, &motionpath);

        /* Add MotionPath cost to UniquePath cost. */
        repartition_cost = motionpath.path.total_cost - subpath->total_cost;
        uniquepath->path.total_cost += repartition_cost;
    }

	return uniquepath;
}                               /* create_unique_rowid_path */

/*
 * create_limit1_path
 *
 * CDB: Add operators on top of the path to ensure uniqueness by
 * discarding all but the first row of the given subpath's result.
 *
 * If a row's duplicates might occur in more than one partition, a Motion
 * operator will be inserted to bring them together.
 */
UniquePath *
create_limit1_path(PlannerInfo *root, Path *subpath)
{
    UniquePath     *uniquepath;
    CdbPathLocus    singleQE;

    /* Add LIMIT 1 operator. */
    uniquepath = make_limit1_path(subpath);

    /* Add Motion operator to gather to a single qExec, then LIMIT 1 again. */
    if (CdbPathLocus_IsPartitioned(subpath->locus))
    {
        CdbPathLocus_MakeSingleQE(&singleQE);
        subpath = cdbpath_create_motion_path(root, (Path *)uniquepath, NIL,
                                             false, singleQE);

        /* Add another LIMIT 1 on top. */
        uniquepath = make_limit1_path(subpath);

		uniquepath->path.motionHazard = subpath->motionHazard;
    }
	return uniquepath;
}                               /* create_limit1_path */

/*
 * make_limit1_path
 *
 * CDB: Returns a UniquePath which ensures uniqueness by producing at most
 * one row of the given subpath's result.
 */
UniquePath *
make_limit1_path(Path *subpath)
{
    UniquePath *uniquepath = make_unique_path(subpath);

    uniquepath->umethod = UNIQUE_PATH_LIMIT1;
    uniquepath->rows = 1;

    /* Cost to get a single row is same as startup cost (from subpath). */
    uniquepath->path.total_cost = uniquepath->path.startup_cost;

    return uniquepath;
}                               /* make_limit1_path */

/*
 * make_unique_path
 *    Allocates and partially initializes a UniquePath node to be completed
 *    by the caller.
 */
UniquePath *
make_unique_path(Path *subpath)
{
	UniquePath     *pathnode;
    MemoryContext   oldcontext;

	/* Allocate in same context as parent rel in case GEQO is ever used. */
	oldcontext = MemoryContextSwitchTo(GetMemoryChunkContext(subpath->parent));

    /* Allocate the UniquePath node. */
	pathnode = makeNode(UniquePath);

    /* Copy Path header from subpath. */
    pathnode->path = *subpath;

    /* Fix up the Path header. */
    pathnode->path.type = T_UniquePath;
    pathnode->path.pathtype = T_Unique;

    /* Initialize added UniquePath fields. */
    pathnode->subpath = subpath;
    pathnode->distinct_on_exprs = NIL;
    pathnode->distinct_on_rowid_relids = NULL;
    pathnode->must_repartition = false;

	/* Restore caller's allocation context. */
	MemoryContextSwitchTo(oldcontext);

    return pathnode;
}                               /* make_unique_path */

/*
 * translate_sub_tlist - get subquery column numbers represented by tlist
 *
 * The given targetlist should contain only Vars referencing the given relid.
 * Extract their varattnos (ie, the column numbers of the subquery) and return
 * as an integer List.
 *
 * If any of the tlist items is not a simple Var, we cannot determine whether
 * the subquery's uniqueness condition (if any) matches ours, so punt and
 * return NIL.
 */
static List *
translate_sub_tlist(List *tlist, int relid)
{
	List	   *result = NIL;
	ListCell   *l;

	foreach(l, tlist)
	{
		Var		   *var = (Var *) lfirst(l);

		if (!var || !IsA(var, Var) ||
			(int)var->varno != relid)
			return NIL;			/* punt */

		result = lappend_int(result, var->varattno);
	}
	return result;
}

/*
 * query_is_distinct_for - does query never return duplicates of the
 *		specified columns?
 *
 * colnos is an integer list of output column numbers (resno's).  We are
 * interested in whether rows consisting of just these columns are certain
 * to be distinct.
 */
static bool
query_is_distinct_for(Query *query, List *colnos)
{
	ListCell   *l;

	/*
	 * DISTINCT (including DISTINCT ON) guarantees uniqueness if all the
	 * columns in the DISTINCT clause appear in colnos.
	 */
	if (query->distinctClause)
	{
		foreach(l, query->distinctClause)
		{
			SortClause *scl = (SortClause *) lfirst(l);
			TargetEntry *tle = get_sortgroupclause_tle(scl,
													   query->targetList);

			if (!list_member_int(colnos, tle->resno))
				break;			/* exit early if no match */
		}
		if (l == NULL)			/* had matches for all? */
			return true;
	}

	/*
	 * Similarly, GROUP BY guarantees uniqueness if all the grouped columns
	 * appear in colnos.
	 */
	if (query->groupClause)
	{
		List *grouptles =
			get_sortgroupclauses_tles(query->groupClause, query->targetList);

		foreach(l, grouptles)
		{
			TargetEntry *tle = (TargetEntry *)lfirst(l);

			if (!list_member_int(colnos, tle->resno))
				break;			/* exit early if no match */
		}
		if (l == NULL)			/* had matches for all? */
			return true;
	}
	else
	{
		/*
		 * If we have no GROUP BY, but do have aggregates or HAVING, then the
		 * result is at most one row so it's surely unique.
		 */
		if (query->hasAggs || query->havingQual)
			return true;
	}

	/*
	 * UNION, INTERSECT, EXCEPT guarantee uniqueness of the whole output row,
	 * except with ALL
	 */
	if (query->setOperations)
	{
		SetOperationStmt *topop = (SetOperationStmt *) query->setOperations;

		Assert(IsA(topop, SetOperationStmt));
		Assert(topop->op != SETOP_NONE);

		if (!topop->all)
		{
			/* We're good if all the nonjunk output columns are in colnos */
			foreach(l, query->targetList)
			{
				TargetEntry *tle = (TargetEntry *) lfirst(l);

				if (!tle->resjunk &&
					!list_member_int(colnos, tle->resno))
					break;		/* exit early if no match */
			}
			if (l == NULL)		/* had matches for all? */
				return true;
		}
	}

	/*
	 * XXX Are there any other cases in which we can easily see the result
	 * must be distinct?
	 */

	return false;
}

/*
 * hash_safe_tlist - can datatypes of given tlist be hashed?
 *
 * We assume hashed aggregation will work if the datatype's equality operator
 * is marked hashjoinable.
 *
 * XXX this probably should be somewhere else.	See also hash_safe_grouping
 * in plan/planner.c.
 */
static bool
hash_safe_tlist(List *tlist)
{
	ListCell   *tl;

	foreach(tl, tlist)
	{
		Node	   *expr = (Node *) lfirst(tl);
		Operator	optup;
		bool		oprcanhash;

		optup = equality_oper(exprType(expr), true);
		if (!optup)
			return false;
		oprcanhash = ((Form_pg_operator) GETSTRUCT(optup))->oprcanhash;
		ReleaseOperator(optup);
		if (!oprcanhash)
			return false;
	}
	return true;
}

/*
 * create_subqueryscan_path
 *	  Creates a path corresponding to a sequential scan of a subquery,
 *	  returning the pathnode.
 */
Path *
create_subqueryscan_path(PlannerInfo *root, RelOptInfo *rel, List *pathkeys)
{
	Path	   *pathnode = makeNode(Path);

	pathnode->pathtype = T_SubqueryScan;
	pathnode->parent = rel;
	pathnode->pathkeys = pathkeys;

    pathnode->locus = cdbpathlocus_from_subquery(root, rel->subplan, rel->relid);
    pathnode->motionHazard = true;          /* better safe than sorry */
    pathnode->rescannable = false;

	cost_subqueryscan(pathnode, rel);

	return pathnode;
}

/*
 * create_functionscan_path
 *	  Creates a path corresponding to a sequential scan of a function,
 *	  returning the pathnode.
 */
Path *
create_functionscan_path(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
	Path	   *pathnode = makeNode(Path);

	pathnode->pathtype = T_FunctionScan;
	pathnode->parent = rel;
	pathnode->pathkeys = NIL;	/* for now, assume unordered result */

    /*
     * CDB: If expression contains mutable functions, evaluate it on entry db.
     * Otherwise let it be evaluated in the same slice as its parent operator.
     */
    Assert(rte->rtekind == RTE_FUNCTION);
    if (contain_mutable_functions(rte->funcexpr))
        CdbPathLocus_MakeEntry(&pathnode->locus);
    else
        CdbPathLocus_MakeGeneral(&pathnode->locus);

    pathnode->motionHazard = false;

	/* For now, be conservative. */
	pathnode->rescannable = false;

	cost_functionscan(pathnode, root, rel);

	return pathnode;
}

/*
 * create_tablefunction_path
 *	  Creates a path corresponding to a sequential scan of a table function,
 *	  returning the pathnode.
 */
Path *
create_tablefunction_path(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
	Path	   *pathnode = makeNode(Path);

	Assert(rte->rtekind == RTE_TABLEFUNCTION);

	/* Setup the basics of the TableFunction path */
	pathnode->pathtype	   = T_TableFunctionScan;
	pathnode->parent	   = rel;
	pathnode->pathkeys	   = NIL;		/* no way to specify output ordering */
	pathnode->motionHazard = true;      /* better safe than sorry */
	pathnode->rescannable  = false;     /* better safe than sorry */

	/*
	 * Inherit the locus of the input subquery.  This is necessary to handle the
	 * case of a General locus, e.g. if all the data has been concentrated to a
	 * single segment then the output will all be on that segment, otherwise the
	 * output must be declared as randomly distributed because we do not know
	 * what relationship, if any, there is between the input data and the output
	 * data.
	 */
	pathnode->locus = cdbpathlocus_from_subquery(root, rel->subplan, rel->relid);

	/* Mark the output as random if the input is partitioned */
	if (CdbPathLocus_IsPartitioned(pathnode->locus))
		CdbPathLocus_MakeStrewn(&pathnode->locus);

	cost_tablefunction(pathnode, root, rel);

	return pathnode;
}

/*
 * create_valuesscan_path
 *	  Creates a path corresponding to a scan of a VALUES list,
 *	  returning the pathnode.
 */
Path *
create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
	Path	   *pathnode = makeNode(Path);

	pathnode->pathtype = T_ValuesScan;
	pathnode->parent = rel;
	pathnode->pathkeys = NIL;	/* result is always unordered */

    /*
     * CDB: If VALUES list contains mutable functions, evaluate it on entry db.
     * Otherwise let it be evaluated in the same slice as its parent operator.
     */
    Assert(rte->rtekind == RTE_VALUES);
    if (contain_mutable_functions((Node *)rte->values_lists))
        CdbPathLocus_MakeEntry(&pathnode->locus);
    else
        CdbPathLocus_MakeGeneral(&pathnode->locus);

    pathnode->motionHazard = false;
	pathnode->rescannable = true;

	cost_valuesscan(pathnode, root, rel);

	return pathnode;
}

/*
 * create_ctescan_path
 *	  Creates a path corresponding to a scan of a CTE,
 *	  returning the pathnode.
 */
Path *
create_ctescan_path(PlannerInfo *root, RelOptInfo *rel, List *pathkeys)
{
	Path *pathnode = makeNode(Path);

	pathnode->pathtype = T_CteScan;
	pathnode->parent = rel;
	pathnode->pathkeys = pathkeys;

	pathnode->locus = cdbpathlocus_from_subquery(root, rel->subplan, rel->relid);

	/*
	 * We can't extract these two values from the subplan, so we simple set
	 * them to their worst case here.
	 */
	pathnode->motionHazard = true;
	pathnode->rescannable = false;

	cost_ctescan(pathnode, root, rel);

	return pathnode;
}

/*
 * cdb_jointype_to_join_in
 *    Returns JOIN_IN if the jointype should be changed from JOIN_INNER to
 *    JOIN_IN so as to produce at most one matching inner row per outer row.
 *    Else returns the given jointype.
 *
 * CDB TODO: Allow outer joins to use the JOIN_IN technique.
 * CDB TODO: Occasionally, symmetric JOIN_IN might be useful (aka 'match join').
 */
static inline JoinType
cdb_jointype_to_join_in(RelOptInfo *joinrel, JoinType jointype, Path *inner_path)
{
    CdbRelDedupInfo    *dedup = joinrel->dedup_info;

    if (dedup &&
        dedup->spent_subqrelids &&
        bms_is_subset(inner_path->parent->relids, dedup->spent_subqrelids) &&
        !IsA(inner_path, UniquePath) &&
        jointype == JOIN_INNER)
    {
        jointype = JOIN_IN;
    }
    return jointype;
}                               /* cdb_jointype_to_join_in */

static bool
path_contains_inner_index(Path *path)
{
    if (IsA(path, IndexPath) &&
        ((IndexPath *)path)->isjoininner)
		return true;
    else if (IsA(path, BitmapHeapPath) &&
             ((BitmapHeapPath *)path)->isjoininner)
		return true;
	else if (IsA(path, BitmapAppendOnlyPath) &&
			 ((BitmapAppendOnlyPath *)path)->isjoininner)
		return true;
	else if (IsA(path, AppendPath))
	{
		/* MPP-2377: Append paths may conceal inner-index scans, if
		 * any of the subpaths are indexpaths or bitmapheap-paths we
		 * have to do more checking */
		ListCell   *l;

		/* scan the subpaths of the Append */
		foreach(l, ((AppendPath *)path)->subpaths)
		{
			Path *subpath = (Path *)lfirst(l);

			if (path_contains_inner_index(subpath))
				return true;
		}
	}

	return false;
}

/*
 * create_nestloop_path
 *	  Creates a pathnode corresponding to a nestloop join between two
 *	  relations.
 *
 * 'joinrel' is the join relation.
 * 'jointype' is the type of join required
 * 'outer_path' is the outer path
 * 'inner_path' is the inner path
 * 'restrict_clauses' are the RestrictInfo nodes to apply at the join
 * 'pathkeys' are the path keys of the new join path
 *
 * Returns the resulting path node.
 */
NestPath *
create_nestloop_path(PlannerInfo *root,
					 RelOptInfo *joinrel,
					 JoinType jointype,
					 Path *outer_path,
					 Path *inner_path,
					 List *restrict_clauses,
                     List *mergeclause_list,    /*CDB*/
					 List *pathkeys)
{
	NestPath       *pathnode;
    CdbPathLocus    join_locus;
    bool            inner_must_be_local = false;

    /* CDB: Change jointype to JOIN_IN from JOIN_INNER (if eligible). */
    if (joinrel->dedup_info)
        jointype = cdb_jointype_to_join_in(joinrel, jointype, inner_path);

    /* CDB: Inner indexpath must execute in the same backend as the
     * nested join to receive input values from the outer rel.
     */
	inner_must_be_local = path_contains_inner_index(inner_path);

    /* Add motion nodes above subpaths and decide where to join. */
    join_locus = cdbpath_motion_for_join(root,
                                         jointype,
                                         &outer_path,       /* INOUT */
                                         &inner_path,       /* INOUT */
                                         mergeclause_list,
                                         pathkeys,
                                         NIL,
                                         false,
                                         inner_must_be_local);
    if (CdbPathLocus_IsNull(join_locus))
        return NULL;

    /* Outer might not be ordered anymore after motion. */
    if (!outer_path->pathkeys)
        pathkeys = NIL;

    /*
     * If outer has at most one row, NJ will make at most one pass over inner.
     * Else materialize inner rel after motion so NJ can loop over results.
     * Skip if an intervening Unique operator will materialize.
     */
    if (!outer_path->parent->onerow)
    {
        if (!inner_path->rescannable)
		{
			/* NLs potentially rescan the inner; if our inner path
			 * isn't rescannable we have to add a materialize node */
			inner_path = (Path *)create_material_path(root, inner_path->parent, inner_path);

			/* If we have motion on the outer, to avoid a deadlock; we
			 * need to set cdb_strict. In order for materialize to
			 * fully fetch the underlying (required to avoid our
			 * deadlock hazard) we must set cdb_strict! */
			if (inner_path->motionHazard && outer_path->motionHazard)
			{
				((MaterialPath *)inner_path)->cdb_strict = true;
				inner_path->motionHazard = false;
			}
		}
    }

    pathnode = makeNode(NestPath);
	pathnode->jpath.path.pathtype = T_NestLoop;
	pathnode->jpath.path.parent = joinrel;
	pathnode->jpath.jointype = jointype;
	pathnode->jpath.outerjoinpath = outer_path;
	pathnode->jpath.innerjoinpath = inner_path;
	pathnode->jpath.joinrestrictinfo = restrict_clauses;
	pathnode->jpath.path.pathkeys = pathkeys;

    pathnode->jpath.path.locus = join_locus;
    pathnode->jpath.path.motionHazard = outer_path->motionHazard || inner_path->motionHazard;

	/* we're only as rescannable as our child plans */
    pathnode->jpath.path.rescannable = outer_path->rescannable && inner_path->rescannable;

	cost_nestloop(pathnode, root);

	return pathnode;
}

/*
 * create_mergejoin_path
 *	  Creates a pathnode corresponding to a mergejoin join between
 *	  two relations
 *
 * 'joinrel' is the join relation
 * 'jointype' is the type of join required
 * 'outer_path' is the outer path
 * 'inner_path' is the inner path
 * 'restrict_clauses' are the RestrictInfo nodes to apply at the join
 * 'pathkeys' are the path keys of the new join path
 * 'mergeclauses' are the RestrictInfo nodes to use as merge clauses
 *		(this should be a subset of the restrict_clauses list)
 * 'allmergeclauses' are the RestrictInfo nodes that are of the form
 *      required of merge clauses (equijoin between outer and inner rel).
 *      Consists of the ones to be used for merging ('mergeclauses') plus
 *      any others in 'restrict_clauses' that are to be applied after the
 *      merge.  We use them for motion planning.  (CDB)
 * 'outersortkeys' are the sort varkeys for the outer relation
 *      or NIL to use existing ordering
 * 'innersortkeys' are the sort varkeys for the inner relation
 *      or NIL to use existing ordering
 */
MergePath *
create_mergejoin_path(PlannerInfo *root,
					  RelOptInfo *joinrel,
					  JoinType jointype,
					  Path *outer_path,
					  Path *inner_path,
					  List *restrict_clauses,
					  List *pathkeys,
					  List *mergeclauses,
                      List *allmergeclauses,    /*CDB*/
					  List *outersortkeys,
					  List *innersortkeys)
{
    MergePath      *pathnode;
    CdbPathLocus    join_locus;
    List           *outermotionkeys;
    List           *innermotionkeys;

    /* CDB: Change jointype to JOIN_IN from JOIN_INNER (if eligible). */
    if (joinrel->dedup_info)
        jointype = cdb_jointype_to_join_in(joinrel, jointype, inner_path);

    /*
     * Do subpaths have useful ordering?
     */
    if (outersortkeys == NIL)           /* must preserve existing ordering */
        outermotionkeys = outer_path->pathkeys;
    else if (pathkeys_contained_in(outersortkeys, outer_path->pathkeys))
        outermotionkeys = outersortkeys;/* lucky coincidence, already ordered */
    else                                /* existing order useless; must sort */
        outermotionkeys = NIL;

    if (innersortkeys == NIL)
        innermotionkeys = inner_path->pathkeys;
    else if (pathkeys_contained_in(innersortkeys, inner_path->pathkeys))
        innermotionkeys = innersortkeys;
    else
        innermotionkeys = NIL;

    /*
     * Add motion nodes above subpaths and decide where to join.
     */
    join_locus = cdbpath_motion_for_join(root,
                                         jointype,
                                         &outer_path,       /* INOUT */
                                         &inner_path,       /* INOUT */
                                         allmergeclauses,
                                         outermotionkeys,
                                         innermotionkeys,
                                         outersortkeys == NIL,
                                         innersortkeys == NIL);
    if (CdbPathLocus_IsNull(join_locus))
        return NULL;

	/*
	 * Sort is not needed if subpath is already well enough ordered and a
     * disordering motion node (with pathkeys == NIL) hasn't been added.
	 */
	if (outermotionkeys &&
		outer_path->pathkeys)
		outersortkeys = NIL;
	if (innermotionkeys &&
		inner_path->pathkeys)
		innersortkeys = NIL;

    /* If user doesn't want sort, but this MJ requires a sort, fail. */
    if (!root->config->enable_sort &&
        !root->config->mpp_trying_fallback_plan)
    {
        if (outersortkeys || innersortkeys)
            return NULL;
    }

    pathnode = makeNode(MergePath);

	/*
	 * If we are not sorting the inner path, we may need a materialize node to
	 * ensure it can be marked/restored.  (Sort does support mark/restore, so
	 * no materialize is needed in that case.)
	 *
	 * Since the inner side must be ordered, and only Sorts and IndexScans can
	 * create order to begin with, you might think there's no problem --- but
	 * you'd be wrong.  Nestloop and merge joins can *preserve* the order of
	 * their inputs, so they can be selected as the input of a mergejoin, and
	 * they don't support mark/restore at present.
	 */
	if (!ExecSupportsMarkRestore(inner_path->pathtype))
	{
		/*
		 * The inner side does not support mark/restore capability.
		 * Check whether a sort node will be inserted later, and if that is not the case,
		 * add a materialize node.
		 * */
		bool need_sort = false;
		if (innersortkeys != NIL)
		{
			/* Check whether all sort keys are constants. If that is the case,
			 * no sort node is needed.
			 * The check is essentially the same as the one performed later in the
			 * make_sort_from_pathkeys() function (optimizer/plan/createplan.c),
			 * which decides whether a sort node is to be added.
			*/
			ListCell   *sortkeycell;

			foreach(sortkeycell, innersortkeys)
			{
				List	   *keysublist = (List *) lfirst(sortkeycell);

			    if (!CdbPathkeyEqualsConstant(keysublist))
			    {
			    	/* sort key is not a constant - sort will be added later */
			    	need_sort = true;
			    	break;
			    }
			}
		}
		// else: innersortkeys == NIL -> no sort node will be added

		if (!need_sort)
		{
			/* no sort node will be added - add a materialize node */
			inner_path = (Path *)
							create_material_path(root, inner_path->parent, inner_path);
		}
	}

	pathnode->jpath.path.pathtype = T_MergeJoin;
	pathnode->jpath.path.parent = joinrel;
	pathnode->jpath.jointype = jointype;
	pathnode->jpath.outerjoinpath = outer_path;
	pathnode->jpath.innerjoinpath = inner_path;
	pathnode->jpath.joinrestrictinfo = restrict_clauses;
	pathnode->jpath.path.pathkeys = pathkeys;
    pathnode->jpath.path.locus = join_locus;

	pathnode->jpath.path.motionHazard = outer_path->motionHazard || inner_path->motionHazard;
	pathnode->jpath.path.rescannable = outer_path->rescannable && inner_path->rescannable;

	pathnode->path_mergeclauses = mergeclauses;
	pathnode->outersortkeys = outersortkeys;
	pathnode->innersortkeys = innersortkeys;

	cost_mergejoin(pathnode, root);

	return pathnode;
}

/*
 * create_hashjoin_path
 *	  Creates a pathnode corresponding to a hash join between two relations.
 *
 * 'joinrel' is the join relation
 * 'jointype' is the type of join required
 * 'outer_path' is the cheapest outer path
 * 'inner_path' is the cheapest inner path
 * 'restrict_clauses' are the RestrictInfo nodes to apply at the join
 * 'hashclauses' are the RestrictInfo nodes to use as hash clauses
 *		(this should be a subset of the restrict_clauses list)
 * 'freeze_outer_path' is true if the outer_path should be used exactly as
 *      given, without adding other operators such as Motion.
 */
HashPath *
create_hashjoin_path(PlannerInfo *root,
					 RelOptInfo *joinrel,
					 JoinType jointype,
					 Path *outer_path,
					 Path *inner_path,
					 List *restrict_clauses,
                     List *mergeclause_list,    /*CDB*/
					 List *hashclauses,
                     bool  freeze_outer_path)
{
    HashPath       *pathnode;
    CdbPathLocus    join_locus;

    /* CDB: Change jointype to JOIN_IN from JOIN_INNER (if eligible). */
    if (joinrel->dedup_info)
        jointype = cdb_jointype_to_join_in(joinrel, jointype, inner_path);

    /* Add motion nodes above subpaths and decide where to join. */
    join_locus = cdbpath_motion_for_join(root,
                                         jointype,
                                         &outer_path,       /* INOUT */
                                         &inner_path,       /* INOUT */
                                         mergeclause_list,
                                         NIL,   /* don't care about ordering */
                                         NIL,
                                         freeze_outer_path,
                                         false);
    if (CdbPathLocus_IsNull(join_locus))
        return NULL;

	pathnode = makeNode(HashPath);

	pathnode->jpath.path.pathtype = T_HashJoin;
	pathnode->jpath.path.parent = joinrel;
	pathnode->jpath.jointype = jointype;
	pathnode->jpath.outerjoinpath = outer_path;
	pathnode->jpath.innerjoinpath = inner_path;
	pathnode->jpath.joinrestrictinfo = restrict_clauses;
	/* A hashjoin never has pathkeys, since its ordering is unpredictable */
	pathnode->jpath.path.pathkeys = NIL;
    pathnode->jpath.path.locus = join_locus;

	pathnode->path_hashclauses = hashclauses;

    /*
     * If hash table overflows to disk, and an ancestor node requests rescan
     * (e.g. because the HJ is in the inner subtree of a NJ), then the HJ has
     * to be redone, including rescanning the inner rel in order to rebuild
     * the hash table.
     */
    pathnode->jpath.path.rescannable = outer_path->rescannable && inner_path->rescannable;

	/* see the comment above; we may have a motion hazard on our inner ?! */
	if (pathnode->jpath.path.rescannable)
		pathnode->jpath.path.motionHazard = outer_path->motionHazard;
	else
		pathnode->jpath.path.motionHazard = outer_path->motionHazard || inner_path->motionHazard;

    cost_hashjoin(pathnode, root);

	return pathnode;
}

redpajama_set_name: RedPajamaGithub


text:
Hunger Movements in Early Victorian Literature
Want, Riots, Migration
Lesa Scholl
Published January 13, 2020 by Routledge
Published May 30, 2016 by Routledge
SAVE ~ $9.99
Hunger Movements in Early Victorian Literature: Want, Riots, Migration
Book Description Table of Contents Author(s) Reviews
In Hunger Movements in Early Victorian Literature, Lesa Scholl explores the ways in which the language of starvation interacts with narratives of emotional and intellectual want to create a dynamic, evolving notion of hunger. Scholl's interdisciplinary study emphasises literary analysis, sensory history, and political economy to interrogate the progression of hunger in Britain from the early 1830s to the late 1860s. Examining works by Charles Dickens, Harriet Martineau, George Eliot, Elizabeth Gaskell, Henry Mayhew, and Charlotte Bronte, Scholl argues for the centrality of hunger in social development and understanding. She shows how the rhetoric of hunger moves beyond critiques of physical starvation to a paradigm in which the dominant narrative of civilisation is predicated on the continual progress and evolution of literal and metaphorical taste. Her study makes a persuasive case for how hunger, as a signifier of both individual and corporate ambition, is a necessarily self-interested and increasingly violent agent of progress within the discourse of political economy that emerged in the eighteenth century and subsequently shaped nineteenth-century social and political life.
Table of Contents to come
Lesa Scholl is the Dean of Emmanuel College within the University of Queensland, Australia, and an Honorary Research Fellow with the University of Exeter, UK.
Featured Author Profiles
Head of College, Kathleen Lumley College, University of Adelaide
Learn more about Lesa Scholl »
In our age of Effective Altruisms, eating disorders, sugar regulation, and a myriad of responses to global inequality, Lesa Scholl reminds us of what hunger was during the West's modernization, and what it still may be. She explores key concepts and representations of want, moderation, excess; the dialectics of scarcity and plenty; sensory and aesthetic taste in early Victorian literature and the eclipsing of hunger by taste by the later nineteenth century. Her subtitle, Want, Riots, Migration, reminds us of the timeliness and significance of the topic and the inescapable interconnectedness of response: an historical work for our time.
Regenia Gagnier, University of Exeter and author of The Insatiability of Human Wants
"Lesa Scholl's Hunger Movements tells the story of these bleak times through the lens of early Victorian writers and the pre-eminent political, social and economic thinkers of the age. Our modern world of excess seems far removed from the starvation caused by the potato blight and the catastrophic disruptions of the Industrial Revolution, but many of the human responses and emotions still resonate.
Its revelations on want, riots and migration gave interesting new perspectives that triggered fresh exploration of some near-forgotten Victorian classics." John Algate, Brisbane writer and journalist The Australian
Interdisciplinary Literary Studies
Social & Cultural History

redpajama_set_name: RedPajamaCommonCrawl


#67770807 - Silhouette of a break dancer. Background and text on a separate..
#31467196 - Graffiti spray can crazy characters hip hop musical culture drippy..
#40404435 - Hip Arthritis, damaged joint cartilage and osteophytes.
#10737720 - The dancer. Colorful silhouette with lines and sprays on abstract..
#10737719 - The dancer. Colorful silhouette with lines and sprays on abstract..
#9374900 - illustration of breakdancer.
#49602454 - Rap music girl. Pretty Young Urban Rap Girl. Lady artwork. Doodle..
#36401476 - Angry father having a quarrel arguing with rebellious teenage..
#30351552 - Expressive collection of graffiti urban youth art individual..
#19022128 - Hip joint, detailed medical illustration Isolated on a white..
#42018693 - Rap battle, concert hip-hop music. Vector template design, flyer,..


In a post recession landscape, it can be hard to believe that only nine years ago interest only accounted for 30% of the mortgages taken out by first time buyers.1 However, back in the 1980s and 1990s many customers were attracted by the earning potential of endowments that were sold alongside interest only products. Many customers cashed these endowment policies in without changing the terms of their mortgage from interest only to repayment, which means that there are an increasing number of customers now reaching the end of their mortgage term with little means to repay, leaving brokers in a very difficult situation on how best to advise them.
Let's say you had some customers – Mr and Mrs X. Mr and Mrs X took out their mortgage in 1988, when the expected returns from endowments sold alongside mortgages were an attractive prospect for many customers. The couple took out an endowment mortgage over a 25-year term at a LTV of 95%, and four years in, cashed in their endowment and were compensated. Instead of using their endowment to pay off the remainder of their mortgage and converting to a repayment mortgage, they used the money to splash out on the latest fashionable accessory for their home – a new conservatory.
Mr and Mrs X weren't worried; after all, property prices were increasing and the conservatory would add even more value to their home. Mr X planned to use annual bonuses to pay off the outstanding mortgage but, as it always does, life got in the way and instead of paying off their mortgage, those annual bonuses went on annual holidays.
Unfortunately for Mr and Mrs X during the recession the valuation of their property was reduced by 25%. They were quickly back to 95% LTV, but were 18 years into their mortgage term with plans to retire in the next 10-15 years. Since the recession the value of their property has risen and they are back to an 85% LTV, but unless they make a significant capital repayment they will not be able to repay their mortgage before retirement.
With research conducted by the Financial Conduct Authority revealing that 10% of customers do not have a repayment plan in place to pay off the capital of their loan2, brokers are going to be faced with an ever growing number of customers like Mr and Mrs X who need new plans to repay their mortgage. With differing needs and circumstances it can be difficult for brokers to know where to begin when advising these customers. For Mr and Mrs X, their options are to downsize or extend the mortgage term, but for other customers a part repayment part interest only mortgage might be another option.
To help navigate the tricky waters of interest only mortgages and the options open to customers without the necessary plans in place to pay off the capital, we will be sharing examples of customers you might encounter as brokers and how they might be best advised. Check back for more case studies on the blog in the coming weeks, or discuss Interest Only mortgages with one of our business development team on 0113 2257722.


L'Oreal Paris are bringing out a very impressive range of 5ml mini nail polishes in April based on gel formulas. I've had a couple of samples to play with and really like them – especially the brushes which make for very simple application. There is a great deal of technical stuff surrounding the brush, but enough to say here that it delivers the right amount to the nail and I found I got pretty much a perfect application.
The 'gel' technology is great; the polishes give a very high shine finish that banishes the need for a topcoat but personally I am such a Seche Vite addict I couldn't help myself and added a coat. It definitely knocked off some of the shine. With 41 colours, it really is the new nail varnish rainbow with shade inspirations from everywhere – even Marie Antoinette's boudoir (Shade 202).
Best of all, for polish addicts everywhere, the 5ml pots at £4.99 a pop means that we might actually get to finish a bottle, and hopefully have a selection of shades.
Revlon Just Bitten Kissable Balm Stain – The Full Swatch!
Like the price and size of bottle but 41 colours with at least 8 sludgy shades to choose from is too much for me.
I'm intrigued to see the technically advanced brush, are you forbidden to photograph it?
Is this international release or just UK/Europe? I would love to get this in Canada.


text:
Hideki Kamiya Quotes
Total quotes (1)
Hideki Kamiya
Birthname
AKA Hideki Kamiya
Birthday December 19, 1970
Birthplace Matsumoto, Nagano, Japan
Nationality Japan
Occupations Video Game Producer
For Viewtiful Joe, we brought in some kids to a focus test and asked them, "What do you think of the characters?" And all the kids said, "Oh, his head's too big," or "Silvia's annoying, I just want to kill her." They were just trashing the game, so I just got pissed off and said I'm not changing anything.
— Hideki Kamiya, Hideki Kamiya Revealed
Video GamesUser Testing
Related quotes to Hideki Kamiya tagged Video Games
The only legitimate use of a computer is to play games.
— Ernest Cline, Armada
computersVideo GamesEscapism
I coded everything. And what came out of that was my first tutorial site where I was teaching people – other girls, mostly – to code. I had a 'staff member' when I was 14, also writing tutorials. That's what I was doing in my spare time.
— Madison Kanna, 'Neopets': Inside Look at Early 2000s Internet Girl Culture
NostalgiaGirls in techThe Digital Age
managing my bank account like it's neopets
viewing work as a game on neopets
and everyone is in neopets costumes that are invisible
— Mallory Whitten, God Box
NeopetsThe Digital AgeWeirdVideo Games
We all know that video games are a simplification of real life, but I think that's what's compelling about it. Because real life is complicated. Video games give us goals and you know when you failed and when you succeeded. In real life, that's pretty rare.
— Hank Green, Hank Has Questions
Video GamesReal Life
They invited Pac-Man? That cherry-chasing dot-muncher isn't even part of this game!
— Wreck-It Ralph (voice) (John C. Reilly), Wreck-It Ralph
Pac-ManVideo Games
I just want to not be me. Whether it's sleeping or playing video games or riding my bike or studying. Giving my brain up. That's what's important.
— Ned Vizzini, It's Kind of a Funny Story
TiredSleepVideo Gamesstudying
One of the best lessons children learn through video games is standing still will get them killed quicker than anything else.
— Jinx Milea, via www.finedictionary.com
Video Gamesdanger