1996-07-09 06:22:35 +00:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-13 23:22:53 +00:00
|
|
|
* createplan.c
|
1997-09-07 05:04:48 +00:00
|
|
|
* Routines to create the desired plan for processing a query
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
|
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
1999-08-18 04:15:16 +00:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.73 1999/08/18 04:15:16 tgl Exp $
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
1996-10-31 10:59:42 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
#include "postgres.h"
|
1996-07-09 06:22:35 +00:00
|
|
|
|
|
|
|
#include "nodes/makefuncs.h"
|
1999-07-16 05:00:38 +00:00
|
|
|
#include "nodes/nodeFuncs.h"
|
1996-07-09 06:22:35 +00:00
|
|
|
#include "optimizer/clauses.h"
|
1999-07-16 05:00:38 +00:00
|
|
|
#include "optimizer/cost.h"
|
|
|
|
#include "optimizer/internal.h"
|
1996-07-09 06:22:35 +00:00
|
|
|
#include "optimizer/planmain.h"
|
1999-07-16 05:00:38 +00:00
|
|
|
#include "optimizer/restrictinfo.h"
|
1996-07-09 06:22:35 +00:00
|
|
|
#include "optimizer/tlist.h"
|
1999-07-16 05:00:38 +00:00
|
|
|
#include "utils/lsyscache.h"
|
|
|
|
#include "utils/syscache.h"
|
1996-07-09 06:22:35 +00:00
|
|
|
|
|
|
|
|
1997-09-08 21:56:23 +00:00
|
|
|
static List *switch_outer(List *clauses);
|
1999-08-16 23:07:20 +00:00
|
|
|
static int set_tlist_sort_info(List *tlist, List *pathkeys);
|
1997-09-08 21:56:23 +00:00
|
|
|
static Scan *create_scan_node(Path *best_path, List *tlist);
|
1999-02-12 17:25:05 +00:00
|
|
|
static Join *create_join_node(JoinPath *best_path, List *tlist);
|
1998-09-01 04:40:42 +00:00
|
|
|
static SeqScan *create_seqscan_node(Path *best_path, List *tlist,
|
1997-09-08 21:56:23 +00:00
|
|
|
List *scan_clauses);
|
1998-09-01 04:40:42 +00:00
|
|
|
static IndexScan *create_indexscan_node(IndexPath *best_path, List *tlist,
|
1997-09-08 21:56:23 +00:00
|
|
|
List *scan_clauses);
|
1999-05-25 22:43:53 +00:00
|
|
|
static NestLoop *create_nestloop_node(NestPath *best_path, List *tlist,
|
1997-09-08 21:56:23 +00:00
|
|
|
List *clauses, Plan *outer_node, List *outer_tlist,
|
|
|
|
Plan *inner_node, List *inner_tlist);
|
1998-09-01 04:40:42 +00:00
|
|
|
static MergeJoin *create_mergejoin_node(MergePath *best_path, List *tlist,
|
1997-09-08 21:56:23 +00:00
|
|
|
List *clauses, Plan *outer_node, List *outer_tlist,
|
|
|
|
Plan *inner_node, List *inner_tlist);
|
1998-09-01 04:40:42 +00:00
|
|
|
static HashJoin *create_hashjoin_node(HashPath *best_path, List *tlist,
|
1997-09-08 21:56:23 +00:00
|
|
|
List *clauses, Plan *outer_node, List *outer_tlist,
|
|
|
|
Plan *inner_node, List *inner_tlist);
|
1999-08-09 01:01:42 +00:00
|
|
|
static List *fix_indxqual_references(List *indexquals, IndexPath *index_path);
|
1999-08-12 04:32:54 +00:00
|
|
|
static List *fix_indxqual_sublist(List *indexqual, IndexPath *index_path,
|
|
|
|
Form_pg_index index);
|
|
|
|
static Node *fix_indxqual_operand(Node *node, IndexPath *index_path,
|
|
|
|
Form_pg_index index);
|
1999-08-16 02:17:58 +00:00
|
|
|
static Noname *make_noname(List *tlist, List *pathkeys, Plan *plan_node);
|
1998-09-01 04:40:42 +00:00
|
|
|
static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
|
1999-04-30 04:04:27 +00:00
|
|
|
List *indxid, List *indxqual, List *indxqualorig);
|
1998-09-01 04:40:42 +00:00
|
|
|
static NestLoop *make_nestloop(List *qptlist, List *qpqual, Plan *lefttree,
|
1997-09-08 21:56:23 +00:00
|
|
|
Plan *righttree);
|
1998-09-01 04:40:42 +00:00
|
|
|
static HashJoin *make_hashjoin(List *tlist, List *qpqual,
|
1997-09-08 21:56:23 +00:00
|
|
|
List *hashclauses, Plan *lefttree, Plan *righttree);
|
|
|
|
static Hash *make_hash(List *tlist, Var *hashkey, Plan *lefttree);
|
1998-09-01 04:40:42 +00:00
|
|
|
static MergeJoin *make_mergejoin(List *tlist, List *qpqual,
|
1999-03-01 00:10:44 +00:00
|
|
|
List *mergeclauses, Plan *righttree, Plan *lefttree);
|
1999-02-09 17:03:14 +00:00
|
|
|
static Material *make_material(List *tlist, Oid nonameid, Plan *lefttree,
|
1997-09-07 05:04:48 +00:00
|
|
|
int keycount);
|
1999-04-30 04:04:27 +00:00
|
|
|
static void copy_costsize(Plan *dest, Plan *src);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
/*
|
1999-02-13 23:22:53 +00:00
|
|
|
* create_plan
|
1997-09-07 05:04:48 +00:00
|
|
|
* Creates the access plan for a query by tracing backwards through the
|
1999-02-13 23:22:53 +00:00
|
|
|
* desired chain of pathnodes, starting at the node 'best_path'. For
|
1997-09-07 05:04:48 +00:00
|
|
|
* every pathnode found:
|
|
|
|
* (1) Create a corresponding plan node containing appropriate id,
|
|
|
|
* target list, and qualification information.
|
|
|
|
* (2) Modify ALL clauses so that attributes are referenced using
|
|
|
|
* relative values.
|
|
|
|
* (3) Target lists are not modified, but will be in another routine.
|
|
|
|
*
|
1999-02-13 23:22:53 +00:00
|
|
|
* best_path is the best access path
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
1997-09-07 05:04:48 +00:00
|
|
|
* Returns the optimal(?) access plan.
|
1996-07-09 06:22:35 +00:00
|
|
|
*/
|
1998-02-26 04:46:47 +00:00
|
|
|
Plan *
|
1997-09-08 21:56:23 +00:00
|
|
|
create_plan(Path *best_path)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
List *tlist;
|
|
|
|
Plan *plan_node = (Plan *) NULL;
|
1998-09-01 04:40:42 +00:00
|
|
|
RelOptInfo *parent_rel;
|
1997-09-08 02:41:22 +00:00
|
|
|
int size;
|
|
|
|
int width;
|
|
|
|
int pages;
|
|
|
|
int tuples;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
parent_rel = best_path->parent;
|
|
|
|
tlist = get_actual_tlist(parent_rel->targetlist);
|
|
|
|
size = parent_rel->size;
|
|
|
|
width = parent_rel->width;
|
|
|
|
pages = parent_rel->pages;
|
|
|
|
tuples = parent_rel->tuples;
|
|
|
|
|
|
|
|
switch (best_path->pathtype)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
case T_IndexScan:
|
|
|
|
case T_SeqScan:
|
|
|
|
plan_node = (Plan *) create_scan_node(best_path, tlist);
|
|
|
|
break;
|
|
|
|
case T_HashJoin:
|
|
|
|
case T_MergeJoin:
|
|
|
|
case T_NestLoop:
|
1999-02-12 17:25:05 +00:00
|
|
|
plan_node = (Plan *) create_join_node((JoinPath *) best_path, tlist);
|
1997-09-08 02:41:22 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* do nothing */
|
|
|
|
break;
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
plan_node->plan_size = size;
|
|
|
|
plan_node->plan_width = width;
|
|
|
|
if (pages == 0)
|
|
|
|
pages = 1;
|
|
|
|
plan_node->plan_tupperpage = tuples / pages;
|
|
|
|
|
1999-05-25 16:15:34 +00:00
|
|
|
#ifdef NOT_USED /* fix xfunc */
|
1997-09-07 05:04:48 +00:00
|
|
|
/* sort clauses by cost/(1-selectivity) -- JMH 2/26/92 */
|
|
|
|
if (XfuncMode != XFUNC_OFF)
|
|
|
|
{
|
|
|
|
set_qpqual((Plan) plan_node,
|
|
|
|
lisp_qsort(get_qpqual((Plan) plan_node),
|
|
|
|
xfunc_clause_compare));
|
|
|
|
if (XfuncMode != XFUNC_NOR)
|
|
|
|
/* sort the disjuncts within each clause by cost -- JMH 3/4/92 */
|
|
|
|
xfunc_disjunct_sort(plan_node->qpqual);
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
#endif
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1998-09-01 03:29:17 +00:00
|
|
|
return plan_node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
/*
|
1999-02-13 23:22:53 +00:00
|
|
|
* create_scan_node
|
|
|
|
* Create a scan path for the parent relation of 'best_path'.
|
1997-09-07 05:04:48 +00:00
|
|
|
*
|
1999-02-13 23:22:53 +00:00
|
|
|
* tlist is the targetlist for the base relation scanned by 'best_path'
|
1997-09-07 05:04:48 +00:00
|
|
|
*
|
|
|
|
* Returns the scan node.
|
1996-07-09 06:22:35 +00:00
|
|
|
*/
|
1997-09-08 02:41:22 +00:00
|
|
|
static Scan *
|
1997-09-08 21:56:23 +00:00
|
|
|
create_scan_node(Path *best_path, List *tlist)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
|
|
|
|
1997-09-08 02:41:22 +00:00
|
|
|
Scan *node = NULL;
|
|
|
|
List *scan_clauses;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
/*
|
1999-07-27 03:51:11 +00:00
|
|
|
* Extract the relevant restriction clauses from the parent relation;
|
|
|
|
* the executor must apply all these restrictions during the scan.
|
|
|
|
* Fix regproc ids in the restriction clauses.
|
1997-09-07 05:04:48 +00:00
|
|
|
*/
|
1999-07-27 03:51:11 +00:00
|
|
|
scan_clauses = fix_opids(get_actual_clauses(best_path->parent->restrictinfo));
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
switch (best_path->pathtype)
|
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
case T_SeqScan:
|
|
|
|
node = (Scan *) create_seqscan_node(best_path, tlist, scan_clauses);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_IndexScan:
|
|
|
|
node = (Scan *) create_indexscan_node((IndexPath *) best_path,
|
|
|
|
tlist,
|
|
|
|
scan_clauses);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
1998-01-07 21:07:04 +00:00
|
|
|
elog(ERROR, "create_scan_node: unknown node type",
|
1997-09-08 02:41:22 +00:00
|
|
|
best_path->pathtype);
|
|
|
|
break;
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
/*
|
1999-05-25 16:15:34 +00:00
|
|
|
* create_join_node
|
1999-02-13 23:22:53 +00:00
|
|
|
* Create a join path for 'best_path' and(recursively) paths for its
|
1997-09-07 05:04:48 +00:00
|
|
|
* inner and outer paths.
|
|
|
|
*
|
|
|
|
* 'tlist' is the targetlist for the join relation corresponding to
|
1999-02-13 23:22:53 +00:00
|
|
|
* 'best_path'
|
1997-09-07 05:04:48 +00:00
|
|
|
*
|
|
|
|
* Returns the join node.
|
1996-07-09 06:22:35 +00:00
|
|
|
*/
|
1997-09-08 02:41:22 +00:00
|
|
|
static Join *
|
1999-02-12 17:25:05 +00:00
|
|
|
create_join_node(JoinPath *best_path, List *tlist)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
Plan *outer_node;
|
|
|
|
List *outer_tlist;
|
|
|
|
Plan *inner_node;
|
|
|
|
List *inner_tlist;
|
|
|
|
List *clauses;
|
|
|
|
Join *retval = NULL;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
outer_node = create_plan((Path *) best_path->outerjoinpath);
|
|
|
|
outer_tlist = outer_node->targetlist;
|
|
|
|
|
|
|
|
inner_node = create_plan((Path *) best_path->innerjoinpath);
|
|
|
|
inner_tlist = inner_node->targetlist;
|
|
|
|
|
1999-02-03 20:15:53 +00:00
|
|
|
clauses = get_actual_clauses(best_path->pathinfo);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
switch (best_path->path.pathtype)
|
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
case T_MergeJoin:
|
|
|
|
retval = (Join *) create_mergejoin_node((MergePath *) best_path,
|
|
|
|
tlist,
|
|
|
|
clauses,
|
|
|
|
outer_node,
|
|
|
|
outer_tlist,
|
|
|
|
inner_node,
|
|
|
|
inner_tlist);
|
|
|
|
break;
|
|
|
|
case T_HashJoin:
|
|
|
|
retval = (Join *) create_hashjoin_node((HashPath *) best_path,
|
|
|
|
tlist,
|
|
|
|
clauses,
|
|
|
|
outer_node,
|
|
|
|
outer_tlist,
|
|
|
|
inner_node,
|
|
|
|
inner_tlist);
|
|
|
|
break;
|
|
|
|
case T_NestLoop:
|
1999-02-12 06:43:53 +00:00
|
|
|
retval = (Join *) create_nestloop_node((NestPath *) best_path,
|
1997-09-08 02:41:22 +00:00
|
|
|
tlist,
|
|
|
|
clauses,
|
|
|
|
outer_node,
|
|
|
|
outer_tlist,
|
|
|
|
inner_node,
|
|
|
|
inner_tlist);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* do nothing */
|
1998-01-07 21:07:04 +00:00
|
|
|
elog(ERROR, "create_join_node: unknown node type",
|
1997-09-08 02:41:22 +00:00
|
|
|
best_path->path.pathtype);
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
1996-07-09 06:22:35 +00:00
|
|
|
|
1999-07-27 03:51:11 +00:00
|
|
|
#ifdef NOT_USED
|
1997-09-07 05:04:48 +00:00
|
|
|
/*
|
|
|
|
* * Expensive function pullups may have pulled local predicates *
|
1999-05-25 16:15:34 +00:00
|
|
|
* into this path node. Put them in the qpqual of the plan node. *
|
1997-09-08 02:41:22 +00:00
|
|
|
* JMH, 6/15/92
|
1997-09-07 05:04:48 +00:00
|
|
|
*/
|
1999-02-03 20:15:53 +00:00
|
|
|
if (get_loc_restrictinfo(best_path) != NIL)
|
1997-09-07 05:04:48 +00:00
|
|
|
set_qpqual((Plan) retval,
|
|
|
|
nconc(get_qpqual((Plan) retval),
|
|
|
|
fix_opids(get_actual_clauses
|
1999-02-03 20:15:53 +00:00
|
|
|
(get_loc_restrictinfo(best_path)))));
|
1996-07-09 06:22:35 +00:00
|
|
|
#endif
|
|
|
|
|
1998-09-01 03:29:17 +00:00
|
|
|
return retval;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
1997-09-07 05:04:48 +00:00
|
|
|
* BASE-RELATION SCAN METHODS
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
/*
|
1999-02-13 23:22:53 +00:00
|
|
|
* create_seqscan_node
|
|
|
|
* Returns a seqscan node for the base relation scanned by 'best_path'
|
|
|
|
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
|
1996-07-09 06:22:35 +00:00
|
|
|
*/
|
|
|
|
static SeqScan *
|
1997-09-08 21:56:23 +00:00
|
|
|
create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
SeqScan *scan_node = (SeqScan *) NULL;
|
|
|
|
Index scan_relid = -1;
|
|
|
|
List *temp;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
temp = best_path->parent->relids;
|
1999-07-27 03:51:11 +00:00
|
|
|
/* there should be exactly one base rel involved... */
|
|
|
|
Assert(length(temp) == 1);
|
|
|
|
scan_relid = (Index) lfirsti(temp);
|
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
scan_node = make_seqscan(tlist,
|
|
|
|
scan_clauses,
|
|
|
|
scan_relid,
|
|
|
|
(Plan *) NULL);
|
|
|
|
|
|
|
|
scan_node->plan.cost = best_path->path_cost;
|
|
|
|
|
1998-09-01 03:29:17 +00:00
|
|
|
return scan_node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
/*
|
1999-02-13 23:22:53 +00:00
|
|
|
* create_indexscan_node
|
|
|
|
* Returns a indexscan node for the base relation scanned by 'best_path'
|
|
|
|
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
|
1999-07-24 23:21:14 +00:00
|
|
|
*
|
1999-07-30 00:44:23 +00:00
|
|
|
* The indexqual of the path contains a sublist of implicitly-ANDed qual
|
|
|
|
* conditions for each scan of the index(es); if there is more than one
|
|
|
|
* scan then the retrieved tuple sets are ORed together. The indexqual
|
|
|
|
* and indexid lists must have the same length, ie, the number of scans
|
|
|
|
* that will occur. Note it is possible for a qual condition sublist
|
|
|
|
* to be empty --- then no index restrictions will be applied during that
|
|
|
|
* scan.
|
1996-07-09 06:22:35 +00:00
|
|
|
*/
|
|
|
|
static IndexScan *
|
1997-09-08 21:56:23 +00:00
|
|
|
create_indexscan_node(IndexPath *best_path,
|
|
|
|
List *tlist,
|
|
|
|
List *scan_clauses)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1999-07-24 23:21:14 +00:00
|
|
|
List *indxqual = best_path->indexqual;
|
|
|
|
List *qpqual;
|
|
|
|
List *fixed_indxqual;
|
1997-09-08 02:41:22 +00:00
|
|
|
List *ixid;
|
1999-07-24 23:21:14 +00:00
|
|
|
IndexScan *scan_node;
|
|
|
|
bool lossy = false;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-07-27 03:51:11 +00:00
|
|
|
/* there should be exactly one base rel involved... */
|
|
|
|
Assert(length(best_path->path.parent->relids) == 1);
|
|
|
|
|
1999-07-30 00:44:23 +00:00
|
|
|
/* check and see if any of the indices are lossy */
|
1997-09-07 05:04:48 +00:00
|
|
|
foreach(ixid, best_path->indexid)
|
|
|
|
{
|
1999-07-24 23:21:14 +00:00
|
|
|
HeapTuple indexTuple;
|
|
|
|
Form_pg_index index;
|
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
indexTuple = SearchSysCacheTuple(INDEXRELID,
|
|
|
|
ObjectIdGetDatum(lfirsti(ixid)),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(indexTuple))
|
1999-05-10 00:46:32 +00:00
|
|
|
elog(ERROR, "create_plan: index %u not found", lfirsti(ixid));
|
1998-09-01 03:29:17 +00:00
|
|
|
index = (Form_pg_index) GETSTRUCT(indexTuple);
|
1997-09-07 05:04:48 +00:00
|
|
|
if (index->indislossy)
|
1999-07-24 23:21:14 +00:00
|
|
|
{
|
|
|
|
lossy = true;
|
|
|
|
break;
|
|
|
|
}
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1999-07-24 23:21:14 +00:00
|
|
|
* The qpqual list must contain all restrictions not automatically
|
1997-09-07 05:04:48 +00:00
|
|
|
* handled by the index. Note that for non-lossy indices, the
|
1999-08-12 04:32:54 +00:00
|
|
|
* predicates in the indxqual are checked fully by the index, while
|
|
|
|
* for lossy indices the indxqual predicates need to be double-checked
|
1997-09-07 05:04:48 +00:00
|
|
|
* after the index fetches the best-guess tuples.
|
1999-07-24 23:21:14 +00:00
|
|
|
*
|
1999-07-27 03:51:11 +00:00
|
|
|
* Since the indexquals were generated from the restriction clauses
|
|
|
|
* given by scan_clauses, there will normally be some duplications
|
|
|
|
* between the lists. Get rid of the duplicates, then add back if lossy.
|
1997-09-07 05:04:48 +00:00
|
|
|
*/
|
1999-07-24 23:21:14 +00:00
|
|
|
if (length(indxqual) > 1)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
1999-07-24 23:21:14 +00:00
|
|
|
/*
|
|
|
|
* Build an expression representation of the indexqual, expanding
|
|
|
|
* the implicit OR and AND semantics of the first- and second-level
|
|
|
|
* lists. XXX Is it really necessary to do a deep copy here?
|
|
|
|
*/
|
|
|
|
List *orclauses = NIL;
|
|
|
|
List *orclause;
|
|
|
|
Expr *indxqual_expr;
|
|
|
|
|
|
|
|
foreach(orclause, indxqual)
|
|
|
|
{
|
|
|
|
orclauses = lappend(orclauses,
|
|
|
|
make_ands_explicit((List *) copyObject(lfirst(orclause))));
|
|
|
|
}
|
|
|
|
indxqual_expr = make_orclause(orclauses);
|
|
|
|
|
|
|
|
/* this set_difference is almost certainly a waste of time... */
|
1997-09-07 05:04:48 +00:00
|
|
|
qpqual = set_difference(scan_clauses,
|
1999-07-24 23:21:14 +00:00
|
|
|
lcons(indxqual_expr, NIL));
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
if (lossy)
|
1999-07-24 23:21:14 +00:00
|
|
|
qpqual = lappend(qpqual, indxqual_expr);
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
1999-07-24 23:21:14 +00:00
|
|
|
else if (indxqual != NIL)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
1999-07-24 23:21:14 +00:00
|
|
|
/* Here, we can simply treat the first sublist as an independent
|
|
|
|
* set of qual expressions, since there is no top-level OR behavior.
|
|
|
|
*/
|
1997-09-07 05:04:48 +00:00
|
|
|
qpqual = set_difference(scan_clauses, lfirst(indxqual));
|
|
|
|
if (lossy)
|
1999-07-24 23:21:14 +00:00
|
|
|
qpqual = nconc(qpqual, (List *) copyObject(lfirst(indxqual)));
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
1999-07-24 23:21:14 +00:00
|
|
|
else
|
|
|
|
qpqual = NIL;
|
|
|
|
|
1999-08-12 04:32:54 +00:00
|
|
|
/* The executor needs a copy with the indexkey on the left of each clause
|
|
|
|
* and with index attrs substituted for table ones.
|
|
|
|
*/
|
|
|
|
fixed_indxqual = fix_indxqual_references(indxqual, best_path);
|
|
|
|
|
1999-07-24 23:21:14 +00:00
|
|
|
/*
|
1999-08-12 04:32:54 +00:00
|
|
|
* Fix opids in the completed indxquals.
|
1999-07-27 03:51:11 +00:00
|
|
|
* XXX this ought to only happen at final exit from the planner...
|
1999-07-24 23:21:14 +00:00
|
|
|
*/
|
|
|
|
indxqual = fix_opids(indxqual);
|
1999-08-12 04:32:54 +00:00
|
|
|
fixed_indxqual = fix_opids(fixed_indxqual);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-02-03 21:18:02 +00:00
|
|
|
scan_node = make_indexscan(tlist,
|
1999-05-25 16:15:34 +00:00
|
|
|
qpqual,
|
|
|
|
lfirsti(best_path->path.parent->relids),
|
|
|
|
best_path->indexid,
|
|
|
|
fixed_indxqual,
|
|
|
|
indxqual);
|
1999-04-30 04:04:27 +00:00
|
|
|
|
|
|
|
scan_node->scan.plan.cost = best_path->path.path_cost;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1998-09-01 03:29:17 +00:00
|
|
|
return scan_node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
1997-09-07 05:04:48 +00:00
|
|
|
* JOIN METHODS
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
static NestLoop *
|
1999-05-25 22:43:53 +00:00
|
|
|
create_nestloop_node(NestPath *best_path,
|
1997-09-08 21:56:23 +00:00
|
|
|
List *tlist,
|
|
|
|
List *clauses,
|
|
|
|
Plan *outer_node,
|
|
|
|
List *outer_tlist,
|
|
|
|
Plan *inner_node,
|
|
|
|
List *inner_tlist)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1999-08-10 02:58:56 +00:00
|
|
|
NestLoop *join_node;
|
1996-07-09 06:22:35 +00:00
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
if (IsA(inner_node, IndexScan))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* An index is being used to reduce the number of tuples scanned
|
1999-08-10 02:58:56 +00:00
|
|
|
* in the inner relation. If there are join clauses being used
|
|
|
|
* with the index, we must update their outer-rel var nodes to
|
|
|
|
* refer to the outer relation.
|
1997-09-07 05:04:48 +00:00
|
|
|
*
|
1999-08-10 02:58:56 +00:00
|
|
|
* We can also remove those join clauses from the list of clauses
|
|
|
|
* that have to be checked as qpquals at the join node, but only
|
|
|
|
* if there's just one indexscan in the inner path (otherwise,
|
|
|
|
* several different sets of clauses are being ORed together).
|
|
|
|
*
|
|
|
|
* Note: if the index is lossy, the same clauses may also be getting
|
|
|
|
* checked as qpquals in the indexscan. We can still remove them
|
|
|
|
* from the nestloop's qpquals, but we gotta update the outer-rel
|
|
|
|
* vars in the indexscan's qpquals too...
|
1997-09-07 05:04:48 +00:00
|
|
|
*/
|
1999-08-10 02:58:56 +00:00
|
|
|
IndexScan *innerscan = (IndexScan *) inner_node;
|
|
|
|
List *indxqualorig = innerscan->indxqualorig;
|
|
|
|
|
|
|
|
/* No work needed if indxqual refers only to its own relation... */
|
|
|
|
if (NumRelids((Node *) indxqualorig) > 1)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
1999-08-10 02:58:56 +00:00
|
|
|
/* Remove redundant tests from my clauses, if possible.
|
|
|
|
* Note we must compare against indxqualorig not the "fixed"
|
1999-08-12 04:32:54 +00:00
|
|
|
* indxqual (which has index attnos instead of relation attnos,
|
|
|
|
* and may have been commuted as well).
|
1999-08-10 02:58:56 +00:00
|
|
|
*/
|
|
|
|
if (length(indxqualorig) == 1) /* single indexscan? */
|
|
|
|
clauses = set_difference(clauses, lfirst(indxqualorig));
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-08-09 01:01:42 +00:00
|
|
|
/* only refs to outer vars get changed in the inner indexqual */
|
1999-08-10 02:58:56 +00:00
|
|
|
innerscan->indxqualorig = join_references(indxqualorig,
|
|
|
|
outer_tlist,
|
|
|
|
NIL);
|
|
|
|
innerscan->indxqual = join_references(innerscan->indxqual,
|
|
|
|
outer_tlist,
|
|
|
|
NIL);
|
1999-08-12 04:32:54 +00:00
|
|
|
/* fix the inner qpqual too, if it has join clauses */
|
1999-08-10 02:58:56 +00:00
|
|
|
if (NumRelids((Node *) inner_node->qual) > 1)
|
|
|
|
inner_node->qual = join_references(inner_node->qual,
|
|
|
|
outer_tlist,
|
|
|
|
NIL);
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
1997-03-18 18:41:37 +00:00
|
|
|
}
|
1997-09-07 05:04:48 +00:00
|
|
|
else if (IsA_Join(inner_node))
|
1997-03-18 18:41:37 +00:00
|
|
|
{
|
1999-08-09 01:01:42 +00:00
|
|
|
/* Materialize the inner join for speed reasons */
|
1999-02-09 17:03:14 +00:00
|
|
|
inner_node = (Plan *) make_noname(inner_tlist,
|
1999-05-25 16:15:34 +00:00
|
|
|
NIL,
|
1999-08-16 02:17:58 +00:00
|
|
|
inner_node);
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
join_node = make_nestloop(tlist,
|
|
|
|
join_references(clauses,
|
|
|
|
outer_tlist,
|
|
|
|
inner_tlist),
|
|
|
|
outer_node,
|
|
|
|
inner_node);
|
|
|
|
|
|
|
|
join_node->join.cost = best_path->path.path_cost;
|
|
|
|
|
1998-09-01 03:29:17 +00:00
|
|
|
return join_node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static MergeJoin *
|
1997-09-08 21:56:23 +00:00
|
|
|
create_mergejoin_node(MergePath *best_path,
|
|
|
|
List *tlist,
|
|
|
|
List *clauses,
|
|
|
|
Plan *outer_node,
|
|
|
|
List *outer_tlist,
|
|
|
|
Plan *inner_node,
|
|
|
|
List *inner_tlist)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
List *qpqual,
|
|
|
|
*mergeclauses;
|
|
|
|
MergeJoin *join_node;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
/*
|
1999-08-10 02:58:56 +00:00
|
|
|
* Remove the mergeclauses from the list of join qual clauses,
|
|
|
|
* leaving the list of quals that must be checked as qpquals.
|
|
|
|
* Set those clauses to contain INNER/OUTER var references.
|
1997-09-07 05:04:48 +00:00
|
|
|
*/
|
|
|
|
qpqual = join_references(set_difference(clauses,
|
|
|
|
best_path->path_mergeclauses),
|
|
|
|
outer_tlist,
|
|
|
|
inner_tlist);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now set the references in the mergeclauses and rearrange them so
|
|
|
|
* that the outer variable is always on the left.
|
|
|
|
*/
|
|
|
|
mergeclauses = switch_outer(join_references(best_path->path_mergeclauses,
|
|
|
|
outer_tlist,
|
|
|
|
inner_tlist));
|
|
|
|
|
|
|
|
/*
|
1999-08-16 02:17:58 +00:00
|
|
|
* Create explicit sort nodes for the outer and inner join paths if
|
1997-09-07 05:04:48 +00:00
|
|
|
* necessary. The sort cost was already accounted for in the path.
|
|
|
|
*/
|
|
|
|
if (best_path->outersortkeys)
|
1999-04-30 04:04:27 +00:00
|
|
|
outer_node = (Plan *) make_noname(outer_tlist,
|
|
|
|
best_path->outersortkeys,
|
1999-08-16 02:17:58 +00:00
|
|
|
outer_node);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
if (best_path->innersortkeys)
|
1999-04-30 04:04:27 +00:00
|
|
|
inner_node = (Plan *) make_noname(inner_tlist,
|
|
|
|
best_path->innersortkeys,
|
1999-08-16 02:17:58 +00:00
|
|
|
inner_node);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1998-08-04 16:44:31 +00:00
|
|
|
join_node = make_mergejoin(tlist,
|
1997-09-07 05:04:48 +00:00
|
|
|
qpqual,
|
|
|
|
mergeclauses,
|
|
|
|
inner_node,
|
|
|
|
outer_node);
|
|
|
|
|
|
|
|
join_node->join.cost = best_path->jpath.path.path_cost;
|
|
|
|
|
1998-09-01 03:29:17 +00:00
|
|
|
return join_node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static HashJoin *
|
1997-09-08 21:56:23 +00:00
|
|
|
create_hashjoin_node(HashPath *best_path,
|
|
|
|
List *tlist,
|
|
|
|
List *clauses,
|
|
|
|
Plan *outer_node,
|
|
|
|
List *outer_tlist,
|
|
|
|
Plan *inner_node,
|
|
|
|
List *inner_tlist)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
List *qpqual;
|
|
|
|
List *hashclauses;
|
|
|
|
HashJoin *join_node;
|
|
|
|
Hash *hash_node;
|
|
|
|
Var *innerhashkey;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
/*
|
1999-08-10 02:58:56 +00:00
|
|
|
* NOTE: there will always be exactly one hashclause in the list
|
|
|
|
* best_path->path_hashclauses (cf. hash_inner_and_outer()).
|
1999-08-16 02:17:58 +00:00
|
|
|
* We represent it as a list anyway, for convenience with routines
|
1999-08-10 02:58:56 +00:00
|
|
|
* that want to work on lists of clauses.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove the hashclauses from the list of join qual clauses,
|
|
|
|
* leaving the list of quals that must be checked as qpquals.
|
|
|
|
* Set those clauses to contain INNER/OUTER var references.
|
1997-09-07 05:04:48 +00:00
|
|
|
*/
|
1999-02-03 21:18:02 +00:00
|
|
|
qpqual = join_references(set_difference(clauses,
|
1999-05-25 16:15:34 +00:00
|
|
|
best_path->path_hashclauses),
|
|
|
|
outer_tlist,
|
|
|
|
inner_tlist);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now set the references in the hashclauses and rearrange them so
|
|
|
|
* that the outer variable is always on the left.
|
|
|
|
*/
|
1999-02-03 21:18:02 +00:00
|
|
|
hashclauses = switch_outer(join_references(best_path->path_hashclauses,
|
1999-05-25 16:15:34 +00:00
|
|
|
outer_tlist,
|
|
|
|
inner_tlist));
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-08-10 02:58:56 +00:00
|
|
|
/* Now the righthand op of the sole hashclause is the inner hash key. */
|
1997-09-07 05:04:48 +00:00
|
|
|
innerhashkey = get_rightop(lfirst(hashclauses));
|
|
|
|
|
1999-08-10 02:58:56 +00:00
|
|
|
/*
|
|
|
|
* Build the hash node and hash join node.
|
|
|
|
*/
|
1997-09-07 05:04:48 +00:00
|
|
|
hash_node = make_hash(inner_tlist, innerhashkey, inner_node);
|
|
|
|
join_node = make_hashjoin(tlist,
|
|
|
|
qpqual,
|
|
|
|
hashclauses,
|
|
|
|
outer_node,
|
|
|
|
(Plan *) hash_node);
|
1999-04-30 04:04:27 +00:00
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
join_node->join.cost = best_path->jpath.path.path_cost;
|
|
|
|
|
1998-09-01 03:29:17 +00:00
|
|
|
return join_node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
1997-09-07 05:04:48 +00:00
|
|
|
* SUPPORTING ROUTINES
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
|
1999-05-06 01:30:58 +00:00
|
|
|
/*
|
|
|
|
* fix_indxqual_references
|
1999-08-12 04:32:54 +00:00
|
|
|
* Adjust indexqual clauses to refer to index attributes instead of the
|
|
|
|
* attributes of the original relation. Also, commute clauses if needed
|
|
|
|
* to put the indexkey on the left. (Someday the executor might not need
|
|
|
|
* that, but for now it does.)
|
1999-05-06 01:30:58 +00:00
|
|
|
*
|
1999-08-09 01:01:42 +00:00
|
|
|
* This code used to be entirely bogus for multi-index scans. Now it keeps
|
|
|
|
* track of which index applies to each subgroup of index qual clauses...
|
|
|
|
*
|
|
|
|
* Returns a modified copy of the indexqual list --- the original is not
|
|
|
|
* changed.
|
1999-05-06 01:30:58 +00:00
|
|
|
*/
|
|
|
|
|
1999-08-09 01:01:42 +00:00
|
|
|
static List *
|
|
|
|
fix_indxqual_references(List *indexquals, IndexPath *index_path)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1999-08-09 01:01:42 +00:00
|
|
|
List *fixed_quals = NIL;
|
|
|
|
List *indexids = index_path->indexid;
|
|
|
|
List *i;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-08-09 01:01:42 +00:00
|
|
|
foreach(i, indexquals)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
1999-08-09 01:01:42 +00:00
|
|
|
List *indexqual = lfirst(i);
|
|
|
|
Oid indexid = lfirsti(indexids);
|
|
|
|
HeapTuple indexTuple;
|
|
|
|
Form_pg_index index;
|
1999-05-25 16:15:34 +00:00
|
|
|
|
1999-08-09 01:01:42 +00:00
|
|
|
indexTuple = SearchSysCacheTuple(INDEXRELID,
|
|
|
|
ObjectIdGetDatum(indexid),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!HeapTupleIsValid(indexTuple))
|
|
|
|
elog(ERROR, "fix_indxqual_references: index %u not found",
|
|
|
|
indexid);
|
|
|
|
index = (Form_pg_index) GETSTRUCT(indexTuple);
|
1996-07-09 06:22:35 +00:00
|
|
|
|
1999-08-09 01:01:42 +00:00
|
|
|
fixed_quals = lappend(fixed_quals,
|
1999-08-12 04:32:54 +00:00
|
|
|
fix_indxqual_sublist(indexqual,
|
|
|
|
index_path,
|
|
|
|
index));
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-08-09 01:01:42 +00:00
|
|
|
indexids = lnext(indexids);
|
|
|
|
}
|
|
|
|
return fixed_quals;
|
|
|
|
}
|
1999-05-25 16:15:34 +00:00
|
|
|
|
1999-08-09 01:01:42 +00:00
|
|
|
/*
|
|
|
|
* Fix the sublist of indexquals to be used in a particular scan.
|
|
|
|
*
|
1999-08-12 04:32:54 +00:00
|
|
|
* For each qual clause, commute if needed to put the indexkey operand on the
|
|
|
|
* left, and then change its varno. We do not need to change the other side
|
|
|
|
* of the clause.
|
1999-08-09 01:01:42 +00:00
|
|
|
*/
|
|
|
|
static List *
|
1999-08-12 04:32:54 +00:00
|
|
|
fix_indxqual_sublist(List *indexqual, IndexPath *index_path,
|
|
|
|
Form_pg_index index)
|
1999-08-09 01:01:42 +00:00
|
|
|
{
|
|
|
|
List *fixed_qual = NIL;
|
|
|
|
List *i;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-08-09 01:01:42 +00:00
|
|
|
foreach(i, indexqual)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
1999-08-12 04:32:54 +00:00
|
|
|
Expr *clause = (Expr *) lfirst(i);
|
|
|
|
int relid;
|
|
|
|
AttrNumber attno;
|
|
|
|
Datum constval;
|
|
|
|
int flag;
|
1999-08-09 01:01:42 +00:00
|
|
|
Expr *newclause;
|
|
|
|
|
1999-08-12 04:32:54 +00:00
|
|
|
if (!is_opclause((Node *) clause) ||
|
|
|
|
length(clause->args) != 2)
|
|
|
|
elog(ERROR, "fix_indxqual_sublist: indexqual clause is not binary opclause");
|
|
|
|
|
|
|
|
/* Which side is the indexkey on?
|
|
|
|
*
|
|
|
|
* get_relattval sets flag&SEL_RIGHT if the indexkey is on the LEFT.
|
|
|
|
*/
|
|
|
|
get_relattval((Node *) clause,
|
|
|
|
lfirsti(index_path->path.parent->relids),
|
|
|
|
&relid, &attno, &constval, &flag);
|
|
|
|
|
|
|
|
/* Copy enough structure to allow commuting and replacing an operand
|
|
|
|
* without changing original clause.
|
|
|
|
*/
|
|
|
|
newclause = make_clause(clause->opType, clause->oper,
|
|
|
|
listCopy(clause->args));
|
1999-08-09 01:01:42 +00:00
|
|
|
|
1999-08-12 04:32:54 +00:00
|
|
|
/* If the indexkey is on the right, commute the clause. */
|
|
|
|
if ((flag & SEL_RIGHT) == 0)
|
|
|
|
CommuteClause(newclause);
|
1999-08-09 01:01:42 +00:00
|
|
|
|
1999-08-12 04:32:54 +00:00
|
|
|
/* Now, change the indexkey operand as needed. */
|
|
|
|
lfirst(newclause->args) = fix_indxqual_operand(lfirst(newclause->args),
|
|
|
|
index_path,
|
|
|
|
index);
|
1999-08-09 01:01:42 +00:00
|
|
|
|
|
|
|
fixed_qual = lappend(fixed_qual, newclause);
|
|
|
|
}
|
|
|
|
return fixed_qual;
|
|
|
|
}
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-08-09 01:01:42 +00:00
|
|
|
static Node *
|
1999-08-12 04:32:54 +00:00
|
|
|
fix_indxqual_operand(Node *node, IndexPath *index_path,
|
|
|
|
Form_pg_index index)
|
1999-08-09 01:01:42 +00:00
|
|
|
{
|
|
|
|
if (IsA(node, Var))
|
|
|
|
{
|
|
|
|
if (((Var *) node)->varno == lfirsti(index_path->path.parent->relids))
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
1999-08-09 01:01:42 +00:00
|
|
|
int varatt = ((Var *) node)->varattno;
|
|
|
|
int pos;
|
1999-05-25 16:15:34 +00:00
|
|
|
|
1999-08-09 01:01:42 +00:00
|
|
|
for (pos = 0; pos < INDEX_MAX_KEYS; pos++)
|
|
|
|
{
|
|
|
|
if (index->indkey[pos] == varatt)
|
|
|
|
{
|
|
|
|
Node *newnode = copyObject(node);
|
|
|
|
((Var *) newnode)->varattno = pos + 1;
|
|
|
|
return newnode;
|
|
|
|
}
|
|
|
|
}
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
1999-08-09 01:01:42 +00:00
|
|
|
/*
|
1999-08-12 04:32:54 +00:00
|
|
|
* Oops, this Var isn't the indexkey!
|
1999-08-09 01:01:42 +00:00
|
|
|
*/
|
1999-08-12 04:32:54 +00:00
|
|
|
elog(ERROR, "fix_indxqual_operand: var is not index attribute");
|
1999-07-29 02:48:05 +00:00
|
|
|
}
|
|
|
|
|
1999-08-09 01:01:42 +00:00
|
|
|
/*
|
1999-08-12 04:32:54 +00:00
|
|
|
* Else, it must be a func expression representing a functional index.
|
|
|
|
*
|
|
|
|
* Currently, there is no need for us to do anything here for
|
1999-08-09 01:01:42 +00:00
|
|
|
* functional indexes. If nodeIndexscan.c sees a func clause as the left
|
|
|
|
* or right-hand toplevel operand of an indexqual, it assumes that that is
|
|
|
|
* a reference to the functional index's value and makes the appropriate
|
|
|
|
* substitution. (It would be cleaner to make the substitution here, I
|
|
|
|
* think --- suspect this issue if a join clause involving a function call
|
|
|
|
* misbehaves...)
|
|
|
|
*/
|
1999-07-29 02:48:05 +00:00
|
|
|
|
1999-08-09 01:01:42 +00:00
|
|
|
/* return the unmodified node */
|
|
|
|
return node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
/*
|
1999-02-13 23:22:53 +00:00
|
|
|
* switch_outer
|
1999-08-16 02:17:58 +00:00
|
|
|
* Given a list of merge or hash joinclauses, rearrange the elements within
|
|
|
|
* the clauses so the outer join variable is on the left and the inner is
|
|
|
|
* on the right. The original list is not touched; a modified list
|
1999-03-01 00:10:44 +00:00
|
|
|
* is returned.
|
1996-07-09 06:22:35 +00:00
|
|
|
*/
|
1997-09-08 02:41:22 +00:00
|
|
|
static List *
|
1997-09-08 21:56:23 +00:00
|
|
|
switch_outer(List *clauses)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
List *t_list = NIL;
|
1999-03-01 00:10:44 +00:00
|
|
|
List *i;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
foreach(i, clauses)
|
1997-04-22 03:32:38 +00:00
|
|
|
{
|
1999-08-12 04:32:54 +00:00
|
|
|
Expr *clause = (Expr *) lfirst(i);
|
1999-08-16 02:17:58 +00:00
|
|
|
Var *op;
|
1999-08-09 01:01:42 +00:00
|
|
|
|
1999-05-25 16:15:34 +00:00
|
|
|
Assert(is_opclause((Node *) clause));
|
1999-08-16 02:17:58 +00:00
|
|
|
op = get_rightop(clause);
|
|
|
|
Assert(op && IsA(op, Var));
|
|
|
|
if (var_is_outer(op))
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
1999-05-25 16:15:34 +00:00
|
|
|
/*
|
|
|
|
* Duplicate just enough of the structure to allow commuting
|
1999-03-01 00:10:44 +00:00
|
|
|
* the clause without changing the original list. Could use
|
|
|
|
* copyObject, but a complete deep copy is overkill.
|
|
|
|
*/
|
1999-08-09 01:01:42 +00:00
|
|
|
Expr *temp;
|
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
temp = make_clause(clause->opType, clause->oper,
|
1999-08-12 04:32:54 +00:00
|
|
|
listCopy(clause->args));
|
1999-03-01 00:10:44 +00:00
|
|
|
/* Commute it --- note this modifies the temp node in-place. */
|
1999-08-12 04:32:54 +00:00
|
|
|
CommuteClause(temp);
|
1997-09-07 05:04:48 +00:00
|
|
|
t_list = lappend(t_list, temp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
t_list = lappend(t_list, clause);
|
|
|
|
}
|
1998-09-01 03:29:17 +00:00
|
|
|
return t_list;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
1999-03-01 00:10:44 +00:00
|
|
|
/*
|
1999-08-16 02:17:58 +00:00
|
|
|
* set_tlist_sort_info
|
|
|
|
* Sets the reskey and reskeyop fields of resdom nodes in a target list
|
|
|
|
* for a sort node.
|
1997-09-07 05:04:48 +00:00
|
|
|
*
|
1999-08-16 23:07:20 +00:00
|
|
|
* 'tlist' is the target list (which is modified in-place).
|
|
|
|
* tlist's reskey fields must be clear to start with.
|
1999-08-16 02:17:58 +00:00
|
|
|
* 'pathkeys' is the desired pathkeys for the sort. NIL means no sort.
|
1997-09-07 05:04:48 +00:00
|
|
|
*
|
1999-08-16 23:07:20 +00:00
|
|
|
* Returns the number of sort keys assigned (which might be less than
|
|
|
|
* length(pathkeys)!)
|
1996-07-09 06:22:35 +00:00
|
|
|
*/
|
1999-08-16 23:07:20 +00:00
|
|
|
static int
|
1999-08-16 02:17:58 +00:00
|
|
|
set_tlist_sort_info(List *tlist, List *pathkeys)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1999-08-16 23:07:20 +00:00
|
|
|
int keysassigned = 0;
|
1999-03-01 00:10:44 +00:00
|
|
|
List *i;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
foreach(i, pathkeys)
|
|
|
|
{
|
1999-08-16 02:17:58 +00:00
|
|
|
List *keysublist = (List *) lfirst(i);
|
1999-08-16 23:07:20 +00:00
|
|
|
PathKeyItem *pathkey = NULL;
|
|
|
|
Resdom *resdom = NULL;
|
|
|
|
List *j;
|
1999-05-25 16:15:34 +00:00
|
|
|
|
1999-08-16 02:17:58 +00:00
|
|
|
/*
|
|
|
|
* We can sort by any one of the sort key items listed in this
|
1999-08-16 23:07:20 +00:00
|
|
|
* sublist. For now, we take the first one that corresponds to
|
|
|
|
* an available Var in the tlist.
|
|
|
|
*
|
|
|
|
* XXX if we have a choice, is there any way of figuring out which
|
|
|
|
* might be cheapest to execute? (For example, int4lt is likely
|
|
|
|
* much cheaper to execute than numericlt, but both might appear in
|
|
|
|
* the same pathkey sublist...) Not clear that we ever will have
|
|
|
|
* a choice in practice, so it may not matter.
|
1999-08-16 02:17:58 +00:00
|
|
|
*/
|
1999-08-16 23:07:20 +00:00
|
|
|
foreach(j, keysublist)
|
|
|
|
{
|
|
|
|
pathkey = lfirst(j);
|
|
|
|
Assert(IsA(pathkey, PathKeyItem));
|
|
|
|
resdom = tlist_member((Var *) pathkey->key, tlist);
|
|
|
|
if (resdom)
|
|
|
|
break;
|
|
|
|
}
|
1999-08-16 02:17:58 +00:00
|
|
|
if (!resdom)
|
|
|
|
elog(ERROR, "set_tlist_sort_info: cannot find tlist item to sort");
|
1999-08-16 23:07:20 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The resdom might be already marked as a sort key, if the pathkeys
|
|
|
|
* contain duplicate entries. (This can happen in scenarios where
|
|
|
|
* multiple mergejoinable clauses mention the same var, for example.)
|
|
|
|
* In that case the current pathkey is essentially a no-op, because
|
|
|
|
* only one value can be seen within any subgroup where it would be
|
|
|
|
* consulted. We can ignore it.
|
|
|
|
*/
|
|
|
|
if (resdom->reskey == 0)
|
|
|
|
{
|
|
|
|
/* OK, mark it as a sort key and set the sort operator regproc */
|
|
|
|
resdom->reskey = ++keysassigned;
|
|
|
|
resdom->reskeyop = get_opcode(pathkey->sortop);
|
|
|
|
}
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
1999-08-16 23:07:20 +00:00
|
|
|
|
|
|
|
return keysassigned;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
1999-04-30 04:04:27 +00:00
|
|
|
/*
|
|
|
|
* Copy cost and size info from a lower plan node to an inserted node.
|
|
|
|
* This is not critical, since the decisions have already been made,
|
|
|
|
* but it helps produce more reasonable-looking EXPLAIN output.
|
|
|
|
*/
|
|
|
|
static void
|
1999-05-25 16:15:34 +00:00
|
|
|
copy_costsize(Plan *dest, Plan *src)
|
1999-04-30 04:04:27 +00:00
|
|
|
{
|
|
|
|
if (src)
|
|
|
|
{
|
|
|
|
dest->cost = src->cost;
|
|
|
|
dest->plan_size = src->plan_size;
|
|
|
|
dest->plan_width = src->plan_width;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dest->cost = 0;
|
|
|
|
dest->plan_size = 0;
|
|
|
|
dest->plan_width = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1996-07-09 06:22:35 +00:00
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
|
1997-09-07 05:04:48 +00:00
|
|
|
/*
|
1999-02-13 23:22:53 +00:00
|
|
|
* make_noname
|
1999-08-18 04:15:16 +00:00
|
|
|
* Create plan node to sort or materialize relations into noname.
|
1997-09-07 05:04:48 +00:00
|
|
|
*
|
1999-08-16 02:17:58 +00:00
|
|
|
* 'tlist' is the target list of the scan to be sorted or materialized
|
|
|
|
* 'pathkeys' is the list of pathkeys by which the result is to be sorted
|
|
|
|
* (NIL implies no sort needed, just materialize it)
|
|
|
|
* 'plan_node' is the node which yields input tuples
|
1996-07-09 06:22:35 +00:00
|
|
|
*/
|
1999-02-09 17:03:14 +00:00
|
|
|
static Noname *
|
|
|
|
make_noname(List *tlist,
|
1999-05-25 16:15:34 +00:00
|
|
|
List *pathkeys,
|
1999-08-16 02:17:58 +00:00
|
|
|
Plan *plan_node)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1999-02-09 17:03:14 +00:00
|
|
|
List *noname_tlist;
|
1999-08-16 23:07:20 +00:00
|
|
|
int numsortkeys;
|
1999-08-18 04:15:16 +00:00
|
|
|
Plan *retval;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-08-16 02:17:58 +00:00
|
|
|
/* Create a new target list for the noname, with sort keys set. */
|
1999-08-16 23:07:20 +00:00
|
|
|
noname_tlist = new_unsorted_tlist(tlist);
|
|
|
|
numsortkeys = set_tlist_sort_info(noname_tlist, pathkeys);
|
1999-08-16 02:17:58 +00:00
|
|
|
|
1999-08-16 23:07:20 +00:00
|
|
|
if (numsortkeys > 0)
|
1997-09-07 05:04:48 +00:00
|
|
|
{
|
1999-08-16 02:17:58 +00:00
|
|
|
/* need to sort */
|
1999-08-18 04:15:16 +00:00
|
|
|
retval = (Plan *) make_sort(noname_tlist,
|
|
|
|
_NONAME_RELATION_ID_,
|
|
|
|
plan_node,
|
|
|
|
numsortkeys);
|
1999-08-16 02:17:58 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* no sort */
|
1999-08-18 04:15:16 +00:00
|
|
|
retval = (Plan *) make_material(noname_tlist,
|
|
|
|
_NONAME_RELATION_ID_,
|
|
|
|
plan_node,
|
|
|
|
0);
|
1997-09-07 05:04:48 +00:00
|
|
|
}
|
1999-08-16 23:07:20 +00:00
|
|
|
|
1999-08-18 04:15:16 +00:00
|
|
|
return (Noname *) retval;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-09-08 02:41:22 +00:00
|
|
|
SeqScan *
|
1997-09-08 21:56:23 +00:00
|
|
|
make_seqscan(List *qptlist,
|
|
|
|
List *qpqual,
|
1997-09-07 05:04:48 +00:00
|
|
|
Index scanrelid,
|
1997-09-08 21:56:23 +00:00
|
|
|
Plan *lefttree)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
SeqScan *node = makeNode(SeqScan);
|
|
|
|
Plan *plan = &node->plan;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-04-30 04:04:27 +00:00
|
|
|
copy_costsize(plan, lefttree);
|
1997-09-07 05:04:48 +00:00
|
|
|
plan->state = (EState *) NULL;
|
|
|
|
plan->targetlist = qptlist;
|
|
|
|
plan->qual = qpqual;
|
|
|
|
plan->lefttree = lefttree;
|
|
|
|
plan->righttree = NULL;
|
|
|
|
node->scanrelid = scanrelid;
|
|
|
|
node->scanstate = (CommonScanState *) NULL;
|
|
|
|
|
1998-09-01 03:29:17 +00:00
|
|
|
return node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static IndexScan *
|
1997-09-08 21:56:23 +00:00
|
|
|
make_indexscan(List *qptlist,
|
|
|
|
List *qpqual,
|
1997-09-07 05:04:48 +00:00
|
|
|
Index scanrelid,
|
1997-09-08 21:56:23 +00:00
|
|
|
List *indxid,
|
1997-12-18 12:21:02 +00:00
|
|
|
List *indxqual,
|
1999-04-30 04:04:27 +00:00
|
|
|
List *indxqualorig)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
IndexScan *node = makeNode(IndexScan);
|
|
|
|
Plan *plan = &node->scan.plan;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-04-30 04:04:27 +00:00
|
|
|
copy_costsize(plan, NULL);
|
1997-09-07 05:04:48 +00:00
|
|
|
plan->state = (EState *) NULL;
|
|
|
|
plan->targetlist = qptlist;
|
|
|
|
plan->qual = qpqual;
|
|
|
|
plan->lefttree = NULL;
|
|
|
|
plan->righttree = NULL;
|
|
|
|
node->scan.scanrelid = scanrelid;
|
|
|
|
node->indxid = indxid;
|
|
|
|
node->indxqual = indxqual;
|
1998-11-22 10:48:45 +00:00
|
|
|
node->indxqualorig = indxqualorig;
|
1999-08-09 06:20:27 +00:00
|
|
|
node->indxorderdir = NoMovementScanDirection;
|
1997-09-07 05:04:48 +00:00
|
|
|
node->scan.scanstate = (CommonScanState *) NULL;
|
|
|
|
|
1998-09-01 03:29:17 +00:00
|
|
|
return node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static NestLoop *
|
1997-09-08 21:56:23 +00:00
|
|
|
make_nestloop(List *qptlist,
|
|
|
|
List *qpqual,
|
|
|
|
Plan *lefttree,
|
|
|
|
Plan *righttree)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
NestLoop *node = makeNode(NestLoop);
|
|
|
|
Plan *plan = &node->join;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-05-25 16:15:34 +00:00
|
|
|
/*
|
|
|
|
* this cost estimate is entirely bogus... hopefully it will be
|
|
|
|
* overwritten by caller.
|
1999-04-30 04:04:27 +00:00
|
|
|
*/
|
1998-02-26 04:46:47 +00:00
|
|
|
plan->cost = (lefttree ? lefttree->cost : 0) +
|
|
|
|
(righttree ? righttree->cost : 0);
|
1997-09-07 05:04:48 +00:00
|
|
|
plan->state = (EState *) NULL;
|
|
|
|
plan->targetlist = qptlist;
|
|
|
|
plan->qual = qpqual;
|
|
|
|
plan->lefttree = lefttree;
|
|
|
|
plan->righttree = righttree;
|
|
|
|
node->nlstate = (NestLoopState *) NULL;
|
|
|
|
|
1998-09-01 03:29:17 +00:00
|
|
|
return node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static HashJoin *
|
1997-09-08 21:56:23 +00:00
|
|
|
make_hashjoin(List *tlist,
|
|
|
|
List *qpqual,
|
|
|
|
List *hashclauses,
|
|
|
|
Plan *lefttree,
|
|
|
|
Plan *righttree)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
HashJoin *node = makeNode(HashJoin);
|
|
|
|
Plan *plan = &node->join;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-05-25 16:15:34 +00:00
|
|
|
/*
|
|
|
|
* this cost estimate is entirely bogus... hopefully it will be
|
|
|
|
* overwritten by caller.
|
1999-04-30 04:04:27 +00:00
|
|
|
*/
|
1998-02-26 04:46:47 +00:00
|
|
|
plan->cost = (lefttree ? lefttree->cost : 0) +
|
|
|
|
(righttree ? righttree->cost : 0);
|
1997-09-07 05:04:48 +00:00
|
|
|
plan->state = (EState *) NULL;
|
|
|
|
plan->targetlist = tlist;
|
|
|
|
plan->qual = qpqual;
|
|
|
|
plan->lefttree = lefttree;
|
|
|
|
plan->righttree = righttree;
|
|
|
|
node->hashclauses = hashclauses;
|
|
|
|
node->hashdone = false;
|
|
|
|
|
1998-09-01 03:29:17 +00:00
|
|
|
return node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
1997-09-08 02:41:22 +00:00
|
|
|
static Hash *
|
1997-09-08 21:56:23 +00:00
|
|
|
make_hash(List *tlist, Var *hashkey, Plan *lefttree)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
Hash *node = makeNode(Hash);
|
|
|
|
Plan *plan = &node->plan;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-04-30 04:04:27 +00:00
|
|
|
copy_costsize(plan, lefttree);
|
1997-09-07 05:04:48 +00:00
|
|
|
plan->state = (EState *) NULL;
|
|
|
|
plan->targetlist = tlist;
|
|
|
|
plan->qual = NULL;
|
|
|
|
plan->lefttree = lefttree;
|
|
|
|
plan->righttree = NULL;
|
|
|
|
node->hashkey = hashkey;
|
|
|
|
|
1998-09-01 03:29:17 +00:00
|
|
|
return node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static MergeJoin *
|
1998-08-04 16:44:31 +00:00
|
|
|
make_mergejoin(List *tlist,
|
1997-09-08 21:56:23 +00:00
|
|
|
List *qpqual,
|
|
|
|
List *mergeclauses,
|
|
|
|
Plan *righttree,
|
|
|
|
Plan *lefttree)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
MergeJoin *node = makeNode(MergeJoin);
|
|
|
|
Plan *plan = &node->join;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-05-25 16:15:34 +00:00
|
|
|
/*
|
|
|
|
* this cost estimate is entirely bogus... hopefully it will be
|
|
|
|
* overwritten by caller.
|
1999-04-30 04:04:27 +00:00
|
|
|
*/
|
1998-02-26 04:46:47 +00:00
|
|
|
plan->cost = (lefttree ? lefttree->cost : 0) +
|
|
|
|
(righttree ? righttree->cost : 0);
|
1997-09-07 05:04:48 +00:00
|
|
|
plan->state = (EState *) NULL;
|
|
|
|
plan->targetlist = tlist;
|
|
|
|
plan->qual = qpqual;
|
|
|
|
plan->lefttree = lefttree;
|
|
|
|
plan->righttree = righttree;
|
|
|
|
node->mergeclauses = mergeclauses;
|
|
|
|
|
1998-09-01 03:29:17 +00:00
|
|
|
return node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
1998-02-26 04:46:47 +00:00
|
|
|
Sort *
|
1999-02-09 17:03:14 +00:00
|
|
|
make_sort(List *tlist, Oid nonameid, Plan *lefttree, int keycount)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
Sort *node = makeNode(Sort);
|
|
|
|
Plan *plan = &node->plan;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-04-30 04:04:27 +00:00
|
|
|
copy_costsize(plan, lefttree);
|
1999-05-01 19:47:42 +00:00
|
|
|
plan->cost += cost_sort(NULL, plan->plan_size, plan->plan_width);
|
1997-09-07 05:04:48 +00:00
|
|
|
plan->state = (EState *) NULL;
|
|
|
|
plan->targetlist = tlist;
|
|
|
|
plan->qual = NIL;
|
|
|
|
plan->lefttree = lefttree;
|
|
|
|
plan->righttree = NULL;
|
1999-02-09 17:03:14 +00:00
|
|
|
node->nonameid = nonameid;
|
1997-09-07 05:04:48 +00:00
|
|
|
node->keycount = keycount;
|
|
|
|
|
1998-09-01 03:29:17 +00:00
|
|
|
return node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static Material *
|
1997-09-08 21:56:23 +00:00
|
|
|
make_material(List *tlist,
|
1999-02-09 17:03:14 +00:00
|
|
|
Oid nonameid,
|
1997-09-08 21:56:23 +00:00
|
|
|
Plan *lefttree,
|
1997-09-07 05:04:48 +00:00
|
|
|
int keycount)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
Material *node = makeNode(Material);
|
|
|
|
Plan *plan = &node->plan;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-04-30 04:04:27 +00:00
|
|
|
copy_costsize(plan, lefttree);
|
1997-09-07 05:04:48 +00:00
|
|
|
plan->state = (EState *) NULL;
|
|
|
|
plan->targetlist = tlist;
|
|
|
|
plan->qual = NIL;
|
|
|
|
plan->lefttree = lefttree;
|
|
|
|
plan->righttree = NULL;
|
1999-02-09 17:03:14 +00:00
|
|
|
node->nonameid = nonameid;
|
1997-09-07 05:04:48 +00:00
|
|
|
node->keycount = keycount;
|
|
|
|
|
1998-09-01 03:29:17 +00:00
|
|
|
return node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
1998-02-26 04:46:47 +00:00
|
|
|
Agg *
|
1998-01-15 19:00:16 +00:00
|
|
|
make_agg(List *tlist, Plan *lefttree)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
Agg *node = makeNode(Agg);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-05-25 16:15:34 +00:00
|
|
|
copy_costsize(&node->plan, lefttree);
|
1997-09-07 05:04:48 +00:00
|
|
|
node->plan.state = (EState *) NULL;
|
|
|
|
node->plan.qual = NULL;
|
|
|
|
node->plan.targetlist = tlist;
|
1997-12-18 12:30:44 +00:00
|
|
|
node->plan.lefttree = lefttree;
|
1997-09-07 05:04:48 +00:00
|
|
|
node->plan.righttree = (Plan *) NULL;
|
1998-01-15 19:00:16 +00:00
|
|
|
node->aggs = NIL;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1998-09-01 03:29:17 +00:00
|
|
|
return node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
1998-02-26 04:46:47 +00:00
|
|
|
Group *
|
1997-09-08 21:56:23 +00:00
|
|
|
make_group(List *tlist,
|
1997-09-07 05:04:48 +00:00
|
|
|
bool tuplePerGroup,
|
|
|
|
int ngrp,
|
1997-09-08 20:59:27 +00:00
|
|
|
AttrNumber *grpColIdx,
|
1997-09-08 21:56:23 +00:00
|
|
|
Sort *lefttree)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
Group *node = makeNode(Group);
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-05-25 16:15:34 +00:00
|
|
|
copy_costsize(&node->plan, (Plan *) lefttree);
|
1997-09-07 05:04:48 +00:00
|
|
|
node->plan.state = (EState *) NULL;
|
|
|
|
node->plan.qual = NULL;
|
|
|
|
node->plan.targetlist = tlist;
|
|
|
|
node->plan.lefttree = (Plan *) lefttree;
|
|
|
|
node->plan.righttree = (Plan *) NULL;
|
|
|
|
node->tuplePerGroup = tuplePerGroup;
|
|
|
|
node->numCols = ngrp;
|
|
|
|
node->grpColIdx = grpColIdx;
|
|
|
|
|
1998-09-01 03:29:17 +00:00
|
|
|
return node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
1997-09-07 05:04:48 +00:00
|
|
|
* A unique node always has a SORT node in the lefttree.
|
1996-07-09 06:22:35 +00:00
|
|
|
*
|
1997-09-07 05:04:48 +00:00
|
|
|
* the uniqueAttr argument must be a null-terminated string,
|
|
|
|
* either the name of the attribute to select unique on
|
1996-07-09 06:22:35 +00:00
|
|
|
* or "*"
|
|
|
|
*/
|
|
|
|
|
1998-02-26 04:46:47 +00:00
|
|
|
Unique *
|
1997-09-08 21:56:23 +00:00
|
|
|
make_unique(List *tlist, Plan *lefttree, char *uniqueAttr)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
Unique *node = makeNode(Unique);
|
|
|
|
Plan *plan = &node->plan;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
1999-04-30 04:04:27 +00:00
|
|
|
copy_costsize(plan, lefttree);
|
1997-09-07 05:04:48 +00:00
|
|
|
plan->state = (EState *) NULL;
|
|
|
|
plan->targetlist = tlist;
|
|
|
|
plan->qual = NIL;
|
|
|
|
plan->lefttree = lefttree;
|
|
|
|
plan->righttree = NULL;
|
1999-02-09 17:03:14 +00:00
|
|
|
node->nonameid = _NONAME_RELATION_ID_;
|
1997-09-07 05:04:48 +00:00
|
|
|
node->keycount = 0;
|
|
|
|
if (strcmp(uniqueAttr, "*") == 0)
|
|
|
|
node->uniqueAttr = NULL;
|
|
|
|
else
|
|
|
|
node->uniqueAttr = pstrdup(uniqueAttr);
|
1998-09-01 03:29:17 +00:00
|
|
|
return node;
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
|
|
|
|
1997-12-18 12:54:45 +00:00
|
|
|
#ifdef NOT_USED
|
1998-02-26 04:46:47 +00:00
|
|
|
List *
|
1997-09-08 21:56:23 +00:00
|
|
|
generate_fjoin(List *tlist)
|
1996-07-09 06:22:35 +00:00
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
List tlistP;
|
|
|
|
List newTlist = NIL;
|
|
|
|
List fjoinList = NIL;
|
|
|
|
int nIters = 0;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Break the target list into elements with Iter nodes, and those
|
|
|
|
* without them.
|
|
|
|
*/
|
|
|
|
foreach(tlistP, tlist)
|
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
List tlistElem;
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
tlistElem = lfirst(tlistP);
|
|
|
|
if (IsA(lsecond(tlistElem), Iter))
|
|
|
|
{
|
|
|
|
nIters++;
|
|
|
|
fjoinList = lappend(fjoinList, tlistElem);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
newTlist = lappend(newTlist, tlistElem);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if we have an Iter node then we need to flatten.
|
|
|
|
*/
|
|
|
|
if (nIters > 0)
|
|
|
|
{
|
1997-09-08 02:41:22 +00:00
|
|
|
List *inner;
|
|
|
|
List *tempList;
|
|
|
|
Fjoin *fjoinNode;
|
|
|
|
DatumPtr results = (DatumPtr) palloc(nIters * sizeof(Datum));
|
|
|
|
BoolPtr alwaysDone = (BoolPtr) palloc(nIters * sizeof(bool));
|
1997-09-07 05:04:48 +00:00
|
|
|
|
|
|
|
inner = lfirst(fjoinList);
|
|
|
|
fjoinList = lnext(fjoinList);
|
|
|
|
fjoinNode = (Fjoin) MakeFjoin(false,
|
|
|
|
nIters,
|
|
|
|
inner,
|
|
|
|
results,
|
|
|
|
alwaysDone);
|
1999-02-15 02:04:58 +00:00
|
|
|
tempList = lcons(fjoinNode, fjoinList);
|
1997-09-07 05:04:48 +00:00
|
|
|
newTlist = lappend(newTlist, tempList);
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
1997-09-07 05:04:48 +00:00
|
|
|
return newTlist;
|
|
|
|
return tlist; /* do nothing for now - ay 10/94 */
|
1996-07-09 06:22:35 +00:00
|
|
|
}
|
1997-12-18 12:54:45 +00:00
|
|
|
|
1998-02-26 04:46:47 +00:00
|
|
|
#endif
|