mirror of
https://github.com/postgres/postgres.git
synced 2025-04-29 13:56:47 +03:00
1211 lines
30 KiB
C
1211 lines
30 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* createplan.c--
|
|
* Routines to create the desired plan for processing a query
|
|
*
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.41 1999/02/10 03:52:44 momjian Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "postgres.h"
|
|
|
|
#include <utils/syscache.h>
|
|
#include <catalog/pg_index.h>
|
|
|
|
#include "nodes/execnodes.h"
|
|
#include "nodes/plannodes.h"
|
|
#include "nodes/relation.h"
|
|
#include "nodes/primnodes.h"
|
|
#include "nodes/nodeFuncs.h"
|
|
|
|
#include "nodes/makefuncs.h"
|
|
|
|
#include "utils/lsyscache.h"
|
|
#include "utils/palloc.h"
|
|
#include "utils/builtins.h"
|
|
|
|
#include "optimizer/restrictinfo.h"
|
|
#include "optimizer/clauses.h"
|
|
#include "optimizer/planmain.h"
|
|
#include "optimizer/tlist.h"
|
|
#include "optimizer/planner.h"
|
|
#include "optimizer/xfunc.h"
|
|
#include "optimizer/internal.h"
|
|
|
|
|
|
#define NONAME_SORT 1
|
|
#define NONAME_MATERIAL 2
|
|
|
|
static List *switch_outer(List *clauses);
|
|
static Scan *create_scan_node(Path *best_path, List *tlist);
|
|
static Join *create_join_node(JoinPath *best_path, List *tlist);
|
|
static SeqScan *create_seqscan_node(Path *best_path, List *tlist,
|
|
List *scan_clauses);
|
|
static IndexScan *create_indexscan_node(IndexPath *best_path, List *tlist,
|
|
List *scan_clauses);
|
|
static NestLoop *create_nestloop_node(JoinPath *best_path, List *tlist,
|
|
List *clauses, Plan *outer_node, List *outer_tlist,
|
|
Plan *inner_node, List *inner_tlist);
|
|
static MergeJoin *create_mergejoin_node(MergePath *best_path, List *tlist,
|
|
List *clauses, Plan *outer_node, List *outer_tlist,
|
|
Plan *inner_node, List *inner_tlist);
|
|
static HashJoin *create_hashjoin_node(HashPath *best_path, List *tlist,
|
|
List *clauses, Plan *outer_node, List *outer_tlist,
|
|
Plan *inner_node, List *inner_tlist);
|
|
static Node *fix_indxqual_references(Node *clause, Path *index_path);
|
|
static Noname *make_noname(List *tlist, List *pathkeys, Oid *operators,
|
|
Plan *plan_node, int nonametype);
|
|
static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
|
|
List *indxid, List *indxqual, List *indxqualorig, Cost cost);
|
|
static NestLoop *make_nestloop(List *qptlist, List *qpqual, Plan *lefttree,
|
|
Plan *righttree);
|
|
static HashJoin *make_hashjoin(List *tlist, List *qpqual,
|
|
List *hashclauses, Plan *lefttree, Plan *righttree);
|
|
static Hash *make_hash(List *tlist, Var *hashkey, Plan *lefttree);
|
|
static MergeJoin *make_mergejoin(List *tlist, List *qpqual,
|
|
List *mergeclauses, Oid opcode, Oid *rightorder,
|
|
Oid *leftorder, Plan *righttree, Plan *lefttree);
|
|
static Material *make_material(List *tlist, Oid nonameid, Plan *lefttree,
|
|
int keycount);
|
|
|
|
/*
|
|
* create_plan--
|
|
* Creates the access plan for a query by tracing backwards through the
|
|
* desired chain of pathnodes, starting at the node 'best-path'. For
|
|
* 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.
|
|
*
|
|
* best-path is the best access path
|
|
*
|
|
* Returns the optimal(?) access plan.
|
|
*/
|
|
Plan *
|
|
create_plan(Path *best_path)
|
|
{
|
|
List *tlist;
|
|
Plan *plan_node = (Plan *) NULL;
|
|
RelOptInfo *parent_rel;
|
|
int size;
|
|
int width;
|
|
int pages;
|
|
int tuples;
|
|
|
|
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)
|
|
{
|
|
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:
|
|
plan_node = (Plan *) create_join_node((JoinPath *) best_path, tlist);
|
|
break;
|
|
default:
|
|
/* do nothing */
|
|
break;
|
|
}
|
|
|
|
plan_node->plan_size = size;
|
|
plan_node->plan_width = width;
|
|
if (pages == 0)
|
|
pages = 1;
|
|
plan_node->plan_tupperpage = tuples / pages;
|
|
|
|
#if 0 /* fix xfunc */
|
|
/* 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);
|
|
}
|
|
#endif
|
|
|
|
return plan_node;
|
|
}
|
|
|
|
/*
|
|
* create_scan_node--
|
|
* Create a scan path for the parent relation of 'best-path'.
|
|
*
|
|
* tlist is the targetlist for the base relation scanned by 'best-path'
|
|
*
|
|
* Returns the scan node.
|
|
*/
|
|
static Scan *
|
|
create_scan_node(Path *best_path, List *tlist)
|
|
{
|
|
|
|
Scan *node = NULL;
|
|
List *scan_clauses;
|
|
|
|
/*
|
|
* Extract the relevant clauses from the parent relation and replace
|
|
* the operator OIDs with the corresponding regproc ids.
|
|
*
|
|
* now that local predicate clauses are copied into paths in
|
|
* find_rel_paths() and then (possibly) pulled up in
|
|
* xfunc_trypullup(), we get the relevant clauses from the path
|
|
* itself, not its parent relation. --- JMH, 6/15/92
|
|
*/
|
|
scan_clauses = fix_opids(get_actual_clauses(best_path->loc_restrictinfo));
|
|
|
|
switch (best_path->pathtype)
|
|
{
|
|
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:
|
|
elog(ERROR, "create_scan_node: unknown node type",
|
|
best_path->pathtype);
|
|
break;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
* create_join_node --
|
|
* Create a join path for 'best-path' and(recursively) paths for its
|
|
* inner and outer paths.
|
|
*
|
|
* 'tlist' is the targetlist for the join relation corresponding to
|
|
* 'best-path'
|
|
*
|
|
* Returns the join node.
|
|
*/
|
|
static Join *
|
|
create_join_node(JoinPath *best_path, List *tlist)
|
|
{
|
|
Plan *outer_node;
|
|
List *outer_tlist;
|
|
Plan *inner_node;
|
|
List *inner_tlist;
|
|
List *clauses;
|
|
Join *retval = NULL;
|
|
|
|
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;
|
|
|
|
clauses = get_actual_clauses(best_path->pathinfo);
|
|
|
|
switch (best_path->path.pathtype)
|
|
{
|
|
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:
|
|
retval = (Join *) create_nestloop_node((JoinPath *) best_path,
|
|
tlist,
|
|
clauses,
|
|
outer_node,
|
|
outer_tlist,
|
|
inner_node,
|
|
inner_tlist);
|
|
break;
|
|
default:
|
|
/* do nothing */
|
|
elog(ERROR, "create_join_node: unknown node type",
|
|
best_path->path.pathtype);
|
|
}
|
|
|
|
#if 0
|
|
|
|
/*
|
|
* * Expensive function pullups may have pulled local predicates *
|
|
* into this path node. Put them in the qpqual of the plan node. * --
|
|
* JMH, 6/15/92
|
|
*/
|
|
if (get_loc_restrictinfo(best_path) != NIL)
|
|
set_qpqual((Plan) retval,
|
|
nconc(get_qpqual((Plan) retval),
|
|
fix_opids(get_actual_clauses
|
|
(get_loc_restrictinfo(best_path)))));
|
|
#endif
|
|
|
|
return retval;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* BASE-RELATION SCAN METHODS
|
|
*
|
|
*****************************************************************************/
|
|
|
|
|
|
/*
|
|
* create_seqscan_node--
|
|
* Returns a seqscan node for the base relation scanned by 'best-path'
|
|
* with restriction clauses 'scan-clauses' and targetlist 'tlist'.
|
|
*/
|
|
static SeqScan *
|
|
create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
|
|
{
|
|
SeqScan *scan_node = (SeqScan *) NULL;
|
|
Index scan_relid = -1;
|
|
List *temp;
|
|
|
|
temp = best_path->parent->relids;
|
|
if (temp == NULL)
|
|
elog(ERROR, "scanrelid is empty");
|
|
else
|
|
scan_relid = (Index) lfirsti(temp); /* ??? who takes care of
|
|
* lnext? - ay */
|
|
scan_node = make_seqscan(tlist,
|
|
scan_clauses,
|
|
scan_relid,
|
|
(Plan *) NULL);
|
|
|
|
scan_node->plan.cost = best_path->path_cost;
|
|
|
|
return scan_node;
|
|
}
|
|
|
|
/*
|
|
* create_indexscan_node--
|
|
* Returns a indexscan node for the base relation scanned by 'best-path'
|
|
* with restriction clauses 'scan-clauses' and targetlist 'tlist'.
|
|
*/
|
|
static IndexScan *
|
|
create_indexscan_node(IndexPath *best_path,
|
|
List *tlist,
|
|
List *scan_clauses)
|
|
{
|
|
|
|
/*
|
|
* Extract the(first if conjunct, only if disjunct) clause from the
|
|
* restrictinfo list.
|
|
*/
|
|
Expr *index_clause = (Expr *) NULL;
|
|
List *indxqual = NIL;
|
|
List *qpqual = NIL;
|
|
List *fixed_indxqual = NIL;
|
|
List *ixid;
|
|
IndexScan *scan_node = (IndexScan *) NULL;
|
|
bool lossy = FALSE;
|
|
HeapTuple indexTuple;
|
|
Form_pg_index index;
|
|
|
|
/*
|
|
* If an 'or' clause is to be used with this index, the indxqual field
|
|
* will contain a list of the 'or' clause arguments, e.g., the
|
|
* clause(OR a b c) will generate: ((a) (b) (c)). Otherwise, the
|
|
* indxqual will simply contain one conjunctive qualification: ((a)).
|
|
*/
|
|
if (best_path->indexqual != NULL)
|
|
/* added call to fix_opids, JMH 6/23/92 */
|
|
index_clause = (Expr *)
|
|
lfirst(fix_opids(get_actual_clauses(best_path->indexqual)));
|
|
|
|
if (or_clause((Node *) index_clause))
|
|
{
|
|
List *temp = NIL;
|
|
|
|
foreach(temp, index_clause->args)
|
|
indxqual = lappend(indxqual, lcons(lfirst(temp), NIL));
|
|
}
|
|
else
|
|
{
|
|
indxqual = lcons(get_actual_clauses(best_path->indexqual),
|
|
NIL);
|
|
}
|
|
|
|
/* check and see if any indices are lossy */
|
|
foreach(ixid, best_path->indexid)
|
|
{
|
|
indexTuple = SearchSysCacheTuple(INDEXRELID,
|
|
ObjectIdGetDatum(lfirsti(ixid)),
|
|
0, 0, 0);
|
|
if (!HeapTupleIsValid(indexTuple))
|
|
elog(ERROR, "create_plan: index %d not found",
|
|
lfirsti(ixid));
|
|
index = (Form_pg_index) GETSTRUCT(indexTuple);
|
|
if (index->indislossy)
|
|
lossy = TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* The qpqual field contains all restrictions not automatically
|
|
* handled by the index. Note that for non-lossy indices, the
|
|
* predicates in the indxqual are handled by the index, while for
|
|
* lossy indices the indxqual predicates need to be double-checked
|
|
* after the index fetches the best-guess tuples.
|
|
*/
|
|
if (or_clause((Node *) index_clause))
|
|
{
|
|
qpqual = set_difference(scan_clauses,
|
|
lcons(index_clause, NIL));
|
|
|
|
if (lossy)
|
|
qpqual = nconc(qpqual,
|
|
lcons((List *) copyObject(index_clause), NIL));
|
|
}
|
|
else
|
|
{
|
|
qpqual = set_difference(scan_clauses, lfirst(indxqual));
|
|
if (lossy)
|
|
qpqual = nconc(qpqual,
|
|
(List *) copyObject(lfirst(indxqual)));
|
|
}
|
|
|
|
fixed_indxqual = (List *) fix_indxqual_references((Node *) indxqual, (Path *) best_path);
|
|
|
|
scan_node = make_indexscan(tlist,
|
|
qpqual,
|
|
lfirsti(best_path->path.parent->relids),
|
|
best_path->indexid,
|
|
fixed_indxqual,
|
|
indxqual,
|
|
best_path->path.path_cost);
|
|
|
|
return scan_node;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* JOIN METHODS
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static NestLoop *
|
|
create_nestloop_node(JoinPath *best_path,
|
|
List *tlist,
|
|
List *clauses,
|
|
Plan *outer_node,
|
|
List *outer_tlist,
|
|
Plan *inner_node,
|
|
List *inner_tlist)
|
|
{
|
|
NestLoop *join_node = (NestLoop *) NULL;
|
|
|
|
if (IsA(inner_node, IndexScan))
|
|
{
|
|
|
|
/*
|
|
* An index is being used to reduce the number of tuples scanned
|
|
* in the inner relation. There will never be more than one index
|
|
* used in the inner scan path, so we need only consider the first
|
|
* set of qualifications in indxqual.
|
|
*
|
|
* But there may be more than one clause in this "first set" in the
|
|
* case of multi-column indices. - vadim 03/18/97
|
|
*/
|
|
|
|
List *inner_indxqual = lfirst(((IndexScan *) inner_node)->indxqual);
|
|
List *inner_qual;
|
|
bool found = false;
|
|
|
|
foreach(inner_qual, inner_indxqual)
|
|
{
|
|
if (!qual_clause_p((Node *) lfirst(inner_qual)))
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we have in fact found a join index qualification, remove
|
|
* these index clauses from the nestloop's join clauses and reset
|
|
* the inner(index) scan's qualification so that the var nodes
|
|
* refer to the proper outer join relation attributes.
|
|
*
|
|
* XXX Re-moving index clauses doesn't work properly: 1.
|
|
* fix_indxqual_references may change varattno-s in
|
|
* inner_indxqual; 2. clauses may be commuted I havn't time to fix
|
|
* it at the moment. - vadim 04/24/97
|
|
*/
|
|
if (found)
|
|
{
|
|
List *new_inner_qual = NIL;
|
|
|
|
clauses = set_difference(clauses, inner_indxqual); /* XXX */
|
|
new_inner_qual = index_outerjoin_references(inner_indxqual,
|
|
outer_node->targetlist,
|
|
((Scan *) inner_node)->scanrelid);
|
|
((IndexScan *) inner_node)->indxqual = lcons(new_inner_qual, NIL);
|
|
}
|
|
}
|
|
else if (IsA_Join(inner_node))
|
|
{
|
|
inner_node = (Plan *) make_noname(inner_tlist,
|
|
NIL,
|
|
NULL,
|
|
inner_node,
|
|
NONAME_MATERIAL);
|
|
}
|
|
|
|
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;
|
|
|
|
return join_node;
|
|
}
|
|
|
|
static MergeJoin *
|
|
create_mergejoin_node(MergePath *best_path,
|
|
List *tlist,
|
|
List *clauses,
|
|
Plan *outer_node,
|
|
List *outer_tlist,
|
|
Plan *inner_node,
|
|
List *inner_tlist)
|
|
{
|
|
List *qpqual,
|
|
*mergeclauses;
|
|
RegProcedure opcode;
|
|
Oid *outer_order,
|
|
*inner_order;
|
|
MergeJoin *join_node;
|
|
|
|
|
|
/*
|
|
* Separate the mergeclauses from the other join qualification clauses
|
|
* and set those clauses to contain references to lower attributes.
|
|
*/
|
|
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));
|
|
|
|
opcode = get_opcode((best_path->jpath.path.path_order->ord.merge)->join_operator);
|
|
|
|
outer_order = (Oid *) palloc(sizeof(Oid) * 2);
|
|
outer_order[0] = (best_path->jpath.path.path_order->ord.merge)->left_operator;
|
|
outer_order[1] = 0;
|
|
|
|
inner_order = (Oid *) palloc(sizeof(Oid) * 2);
|
|
inner_order[0] = (best_path->jpath.path.path_order->ord.merge)->right_operator;
|
|
inner_order[1] = 0;
|
|
|
|
/*
|
|
* Create explicit sort paths for the outer and inner join paths if
|
|
* necessary. The sort cost was already accounted for in the path.
|
|
*/
|
|
if (best_path->outersortkeys)
|
|
{
|
|
Noname *sorted_outer_node = make_noname(outer_tlist,
|
|
best_path->outersortkeys,
|
|
outer_order,
|
|
outer_node,
|
|
NONAME_SORT);
|
|
|
|
sorted_outer_node->plan.cost = outer_node->cost;
|
|
outer_node = (Plan *) sorted_outer_node;
|
|
}
|
|
|
|
if (best_path->innersortkeys)
|
|
{
|
|
Noname *sorted_inner_node = make_noname(inner_tlist,
|
|
best_path->innersortkeys,
|
|
inner_order,
|
|
inner_node,
|
|
NONAME_SORT);
|
|
|
|
sorted_inner_node->plan.cost = outer_node->cost;
|
|
inner_node = (Plan *) sorted_inner_node;
|
|
}
|
|
|
|
join_node = make_mergejoin(tlist,
|
|
qpqual,
|
|
mergeclauses,
|
|
opcode,
|
|
inner_order,
|
|
outer_order,
|
|
inner_node,
|
|
outer_node);
|
|
|
|
join_node->join.cost = best_path->jpath.path.path_cost;
|
|
|
|
return join_node;
|
|
}
|
|
|
|
/*
|
|
* create_hashjoin_node-- XXX HASH
|
|
*
|
|
* Returns a new hashjoin node.
|
|
*
|
|
* XXX hash join ops are totally bogus -- how the hell do we choose
|
|
* these?? at runtime? what about a hash index?
|
|
*/
|
|
static HashJoin *
|
|
create_hashjoin_node(HashPath *best_path,
|
|
List *tlist,
|
|
List *clauses,
|
|
Plan *outer_node,
|
|
List *outer_tlist,
|
|
Plan *inner_node,
|
|
List *inner_tlist)
|
|
{
|
|
List *qpqual;
|
|
List *hashclauses;
|
|
HashJoin *join_node;
|
|
Hash *hash_node;
|
|
Var *innerhashkey;
|
|
|
|
/*
|
|
* Separate the hashclauses from the other join qualification clauses
|
|
* and set those clauses to contain references to lower attributes.
|
|
*/
|
|
qpqual = join_references(set_difference(clauses,
|
|
best_path->path_hashclauses),
|
|
outer_tlist,
|
|
inner_tlist);
|
|
|
|
/*
|
|
* Now set the references in the hashclauses and rearrange them so
|
|
* that the outer variable is always on the left.
|
|
*/
|
|
hashclauses = switch_outer(join_references(best_path->path_hashclauses,
|
|
outer_tlist,
|
|
inner_tlist));
|
|
|
|
innerhashkey = get_rightop(lfirst(hashclauses));
|
|
|
|
hash_node = make_hash(inner_tlist, innerhashkey, inner_node);
|
|
join_node = make_hashjoin(tlist,
|
|
qpqual,
|
|
hashclauses,
|
|
outer_node,
|
|
(Plan *) hash_node);
|
|
join_node->join.cost = best_path->jpath.path.path_cost;
|
|
|
|
return join_node;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* SUPPORTING ROUTINES
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static Node *
|
|
fix_indxqual_references(Node *clause, Path *index_path)
|
|
{
|
|
Node *newclause;
|
|
|
|
if (IsA(clause, Var))
|
|
{
|
|
if (lfirsti(index_path->parent->relids) == ((Var *) clause)->varno)
|
|
{
|
|
int pos = 0;
|
|
int varatt = ((Var *) clause)->varattno;
|
|
int *indexkeys = ((IndexPath *) index_path)->indexkeys;
|
|
|
|
if (indexkeys)
|
|
{
|
|
while (indexkeys[pos] != 0)
|
|
{
|
|
if (varatt == indexkeys[pos])
|
|
break;
|
|
pos++;
|
|
}
|
|
}
|
|
newclause = copyObject((Node *) clause);
|
|
((Var *) newclause)->varattno = pos + 1;
|
|
return newclause;
|
|
}
|
|
else
|
|
return clause;
|
|
}
|
|
else if (IsA(clause, Const))
|
|
return clause;
|
|
else if (IsA(clause, Param))
|
|
{
|
|
/* Function parameter used as index scan arg. DZ - 27-8-1996 */
|
|
return clause;
|
|
}
|
|
else if (is_opclause(clause) &&
|
|
is_funcclause((Node *) get_leftop((Expr *) clause)) &&
|
|
((Func *) ((Expr *) get_leftop((Expr *) clause))->oper)->funcisindex)
|
|
{
|
|
Var *newvar = makeVar((Index) lfirsti(index_path->parent->relids),
|
|
1, /* func indices have one key */
|
|
((Func *) ((Expr *) clause)->oper)->functype,
|
|
-1,
|
|
0,
|
|
(Index) lfirsti(index_path->parent->relids),
|
|
0);
|
|
|
|
return ((Node *) make_opclause((Oper *) ((Expr *) clause)->oper,
|
|
newvar,
|
|
get_rightop((Expr *) clause)));
|
|
|
|
}
|
|
else if (IsA(clause, Expr))
|
|
{
|
|
Expr *expr = (Expr *) clause;
|
|
List *new_subclauses = NIL;
|
|
Node *subclause = NULL;
|
|
List *i = NIL;
|
|
|
|
foreach(i, expr->args)
|
|
{
|
|
subclause = lfirst(i);
|
|
if (subclause)
|
|
new_subclauses = lappend(new_subclauses,
|
|
fix_indxqual_references(subclause,
|
|
index_path));
|
|
|
|
}
|
|
|
|
/*
|
|
* XXX new_subclauses should be a list of the form: ( (var var)
|
|
* (var const) ...) ?
|
|
*/
|
|
if (new_subclauses)
|
|
{
|
|
return (Node *)
|
|
make_clause(expr->opType, expr->oper, new_subclauses);
|
|
}
|
|
else
|
|
return clause;
|
|
}
|
|
else if (IsA(clause, CaseExpr))
|
|
{
|
|
elog(NOTICE,"optimizer: fix_indxqual_references sees CaseExpr");
|
|
return clause;
|
|
}
|
|
else
|
|
{
|
|
List *oldclauses = (List *) clause;
|
|
List *new_subclauses = NIL;
|
|
Node *subclause = NULL;
|
|
List *i = NIL;
|
|
|
|
foreach(i, oldclauses)
|
|
{
|
|
subclause = lfirst(i);
|
|
if (subclause)
|
|
new_subclauses = lappend(new_subclauses,
|
|
fix_indxqual_references(subclause,
|
|
index_path));
|
|
|
|
}
|
|
|
|
/*
|
|
* XXX new_subclauses should be a list of the form: ( (var var)
|
|
* (var const) ...) ?
|
|
*/
|
|
if (new_subclauses)
|
|
return (Node *) new_subclauses;
|
|
else
|
|
return clause;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* switch_outer--
|
|
* Given a list of merge clauses, rearranges the elements within the
|
|
* clauses so the outer join variable is on the left and the inner is on
|
|
* the right.
|
|
*
|
|
* Returns the rearranged list ?
|
|
*
|
|
* XXX Shouldn't the operator be commuted?!
|
|
*/
|
|
static List *
|
|
switch_outer(List *clauses)
|
|
{
|
|
List *t_list = NIL;
|
|
Expr *temp = NULL;
|
|
List *i = NIL;
|
|
Expr *clause;
|
|
Node *op;
|
|
|
|
foreach(i, clauses)
|
|
{
|
|
clause = lfirst(i);
|
|
op = (Node *) get_rightop(clause);
|
|
if (IsA(op, ArrayRef))
|
|
op = ((ArrayRef *) op)->refexpr;
|
|
Assert(IsA(op, Var));
|
|
if (var_is_outer((Var *) op))
|
|
{
|
|
temp = make_clause(clause->opType, clause->oper,
|
|
lcons(get_rightop(clause),
|
|
lcons(get_leftop(clause),
|
|
NIL)));
|
|
t_list = lappend(t_list, temp);
|
|
}
|
|
else
|
|
t_list = lappend(t_list, clause);
|
|
}
|
|
return t_list;
|
|
}
|
|
|
|
/*
|
|
* set-noname-tlist-operators--
|
|
* Sets the key and keyop fields of resdom nodes in a target list.
|
|
*
|
|
* 'tlist' is the target list
|
|
* 'pathkeys' is a list of N keys in the form((key1) (key2)...(keyn)),
|
|
* corresponding to vars in the target list that are to
|
|
* be sorted or hashed
|
|
* 'operators' is the corresponding list of N sort or hash operators
|
|
* 'keyno' is the first key number
|
|
* XXX - keyno ? doesn't exist - jeff
|
|
*
|
|
* Returns the modified target list.
|
|
*/
|
|
static List *
|
|
set_noname_tlist_operators(List *tlist, List *pathkeys, Oid *operators)
|
|
{
|
|
Node *pathkey = NULL;
|
|
int keyno = 1;
|
|
Resdom *resdom = (Resdom *) NULL;
|
|
List *i = NIL;
|
|
|
|
foreach(i, pathkeys)
|
|
{
|
|
pathkey = lfirst((List *) lfirst(i));
|
|
resdom = tlist_member((Var *) pathkey, tlist);
|
|
if (resdom)
|
|
{
|
|
/*
|
|
* Order the resdom pathkey and replace the operator OID for each
|
|
* key with the regproc OID.
|
|
*
|
|
* XXX Note that the optimizer only generates merge joins with 1
|
|
* operator (see create_mergejoin_node) - ay 2/95
|
|
*/
|
|
resdom->reskey = keyno;
|
|
resdom->reskeyop = get_opcode(operators[0]);
|
|
}
|
|
keyno += 1;
|
|
}
|
|
return tlist;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
|
|
/*
|
|
* make_noname--
|
|
* Create plan nodes to sort or materialize relations into noname. The
|
|
* result returned for a sort will look like (SEQSCAN(SORT(plan-node)))
|
|
* or (SEQSCAN(MATERIAL(plan-node)))
|
|
*
|
|
* 'tlist' is the target list of the scan to be sorted or hashed
|
|
* 'pathkeys' is the list of keys which the sort or hash will be done on
|
|
* 'operators' is the operators with which the sort or hash is to be done
|
|
* (a list of operator OIDs)
|
|
* 'plan-node' is the node which yields tuples for the sort
|
|
* 'nonametype' indicates which operation(sort or hash) to perform
|
|
*/
|
|
static Noname *
|
|
make_noname(List *tlist,
|
|
List *pathkeys,
|
|
Oid *operators,
|
|
Plan *plan_node,
|
|
int nonametype)
|
|
{
|
|
List *noname_tlist;
|
|
Noname *retval = NULL;
|
|
|
|
/* Create a new target list for the noname, with keys set. */
|
|
noname_tlist = set_noname_tlist_operators(new_unsorted_tlist(tlist),
|
|
pathkeys,
|
|
operators);
|
|
switch (nonametype)
|
|
{
|
|
case NONAME_SORT:
|
|
retval = (Noname *) make_seqscan(tlist,
|
|
NIL,
|
|
_NONAME_RELATION_ID_,
|
|
(Plan *) make_sort(noname_tlist,
|
|
_NONAME_RELATION_ID_,
|
|
plan_node,
|
|
length(pathkeys)));
|
|
break;
|
|
|
|
case NONAME_MATERIAL:
|
|
retval = (Noname *) make_seqscan(tlist,
|
|
NIL,
|
|
_NONAME_RELATION_ID_,
|
|
(Plan *) make_material(noname_tlist,
|
|
_NONAME_RELATION_ID_,
|
|
plan_node,
|
|
length(pathkeys)));
|
|
break;
|
|
|
|
default:
|
|
elog(ERROR, "make_noname: unknown noname type %d", nonametype);
|
|
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
|
|
SeqScan *
|
|
make_seqscan(List *qptlist,
|
|
List *qpqual,
|
|
Index scanrelid,
|
|
Plan *lefttree)
|
|
{
|
|
SeqScan *node = makeNode(SeqScan);
|
|
Plan *plan = &node->plan;
|
|
|
|
plan->cost = (lefttree ? lefttree->cost : 0);
|
|
plan->state = (EState *) NULL;
|
|
plan->targetlist = qptlist;
|
|
plan->qual = qpqual;
|
|
plan->lefttree = lefttree;
|
|
plan->righttree = NULL;
|
|
node->scanrelid = scanrelid;
|
|
node->scanstate = (CommonScanState *) NULL;
|
|
|
|
return node;
|
|
}
|
|
|
|
static IndexScan *
|
|
make_indexscan(List *qptlist,
|
|
List *qpqual,
|
|
Index scanrelid,
|
|
List *indxid,
|
|
List *indxqual,
|
|
List *indxqualorig,
|
|
Cost cost)
|
|
{
|
|
IndexScan *node = makeNode(IndexScan);
|
|
Plan *plan = &node->scan.plan;
|
|
|
|
plan->cost = cost;
|
|
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;
|
|
node->indxqualorig = indxqualorig;
|
|
node->scan.scanstate = (CommonScanState *) NULL;
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static NestLoop *
|
|
make_nestloop(List *qptlist,
|
|
List *qpqual,
|
|
Plan *lefttree,
|
|
Plan *righttree)
|
|
{
|
|
NestLoop *node = makeNode(NestLoop);
|
|
Plan *plan = &node->join;
|
|
|
|
plan->cost = (lefttree ? lefttree->cost : 0) +
|
|
(righttree ? righttree->cost : 0);
|
|
plan->state = (EState *) NULL;
|
|
plan->targetlist = qptlist;
|
|
plan->qual = qpqual;
|
|
plan->lefttree = lefttree;
|
|
plan->righttree = righttree;
|
|
node->nlstate = (NestLoopState *) NULL;
|
|
|
|
return node;
|
|
}
|
|
|
|
static HashJoin *
|
|
make_hashjoin(List *tlist,
|
|
List *qpqual,
|
|
List *hashclauses,
|
|
Plan *lefttree,
|
|
Plan *righttree)
|
|
{
|
|
HashJoin *node = makeNode(HashJoin);
|
|
Plan *plan = &node->join;
|
|
|
|
plan->cost = (lefttree ? lefttree->cost : 0) +
|
|
(righttree ? righttree->cost : 0);
|
|
plan->cost = 0.0;
|
|
plan->state = (EState *) NULL;
|
|
plan->targetlist = tlist;
|
|
plan->qual = qpqual;
|
|
plan->lefttree = lefttree;
|
|
plan->righttree = righttree;
|
|
node->hashclauses = hashclauses;
|
|
node->hashjointable = NULL;
|
|
node->hashjointablekey = 0;
|
|
node->hashjointablesize = 0;
|
|
node->hashdone = false;
|
|
|
|
return node;
|
|
}
|
|
|
|
static Hash *
|
|
make_hash(List *tlist, Var *hashkey, Plan *lefttree)
|
|
{
|
|
Hash *node = makeNode(Hash);
|
|
Plan *plan = &node->plan;
|
|
|
|
plan->cost = (lefttree ? lefttree->cost : 0);
|
|
plan->cost = 0.0;
|
|
plan->state = (EState *) NULL;
|
|
plan->targetlist = tlist;
|
|
plan->qual = NULL;
|
|
plan->lefttree = lefttree;
|
|
plan->righttree = NULL;
|
|
node->hashkey = hashkey;
|
|
node->hashtable = NULL;
|
|
node->hashtablekey = 0;
|
|
node->hashtablesize = 0;
|
|
|
|
return node;
|
|
}
|
|
|
|
static MergeJoin *
|
|
make_mergejoin(List *tlist,
|
|
List *qpqual,
|
|
List *mergeclauses,
|
|
Oid opcode,
|
|
Oid *rightorder,
|
|
Oid *leftorder,
|
|
Plan *righttree,
|
|
Plan *lefttree)
|
|
{
|
|
MergeJoin *node = makeNode(MergeJoin);
|
|
Plan *plan = &node->join;
|
|
|
|
plan->cost = (lefttree ? lefttree->cost : 0) +
|
|
(righttree ? righttree->cost : 0);
|
|
plan->state = (EState *) NULL;
|
|
plan->targetlist = tlist;
|
|
plan->qual = qpqual;
|
|
plan->lefttree = lefttree;
|
|
plan->righttree = righttree;
|
|
node->mergeclauses = mergeclauses;
|
|
node->mergejoinop = opcode;
|
|
node->mergerightorder = rightorder;
|
|
node->mergeleftorder = leftorder;
|
|
|
|
return node;
|
|
}
|
|
|
|
Sort *
|
|
make_sort(List *tlist, Oid nonameid, Plan *lefttree, int keycount)
|
|
{
|
|
Sort *node = makeNode(Sort);
|
|
Plan *plan = &node->plan;
|
|
|
|
plan->cost = (lefttree ? lefttree->cost : 0);
|
|
plan->state = (EState *) NULL;
|
|
plan->targetlist = tlist;
|
|
plan->qual = NIL;
|
|
plan->lefttree = lefttree;
|
|
plan->righttree = NULL;
|
|
node->nonameid = nonameid;
|
|
node->keycount = keycount;
|
|
|
|
return node;
|
|
}
|
|
|
|
static Material *
|
|
make_material(List *tlist,
|
|
Oid nonameid,
|
|
Plan *lefttree,
|
|
int keycount)
|
|
{
|
|
Material *node = makeNode(Material);
|
|
Plan *plan = &node->plan;
|
|
|
|
plan->cost = (lefttree ? lefttree->cost : 0);
|
|
plan->state = (EState *) NULL;
|
|
plan->targetlist = tlist;
|
|
plan->qual = NIL;
|
|
plan->lefttree = lefttree;
|
|
plan->righttree = NULL;
|
|
node->nonameid = nonameid;
|
|
node->keycount = keycount;
|
|
|
|
return node;
|
|
}
|
|
|
|
Agg *
|
|
make_agg(List *tlist, Plan *lefttree)
|
|
{
|
|
Agg *node = makeNode(Agg);
|
|
|
|
node->plan.cost = (lefttree ? lefttree->cost : 0);
|
|
node->plan.state = (EState *) NULL;
|
|
node->plan.qual = NULL;
|
|
node->plan.targetlist = tlist;
|
|
node->plan.lefttree = lefttree;
|
|
node->plan.righttree = (Plan *) NULL;
|
|
node->aggs = NIL;
|
|
|
|
return node;
|
|
}
|
|
|
|
Group *
|
|
make_group(List *tlist,
|
|
bool tuplePerGroup,
|
|
int ngrp,
|
|
AttrNumber *grpColIdx,
|
|
Sort *lefttree)
|
|
{
|
|
Group *node = makeNode(Group);
|
|
|
|
node->plan.cost = (lefttree ? lefttree->plan.cost : 0);
|
|
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;
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
* A unique node always has a SORT node in the lefttree.
|
|
*
|
|
* the uniqueAttr argument must be a null-terminated string,
|
|
* either the name of the attribute to select unique on
|
|
* or "*"
|
|
*/
|
|
|
|
Unique *
|
|
make_unique(List *tlist, Plan *lefttree, char *uniqueAttr)
|
|
{
|
|
Unique *node = makeNode(Unique);
|
|
Plan *plan = &node->plan;
|
|
|
|
plan->cost = (lefttree ? lefttree->cost : 0);
|
|
plan->state = (EState *) NULL;
|
|
plan->targetlist = tlist;
|
|
plan->qual = NIL;
|
|
plan->lefttree = lefttree;
|
|
plan->righttree = NULL;
|
|
node->nonameid = _NONAME_RELATION_ID_;
|
|
node->keycount = 0;
|
|
if (strcmp(uniqueAttr, "*") == 0)
|
|
node->uniqueAttr = NULL;
|
|
else
|
|
node->uniqueAttr = pstrdup(uniqueAttr);
|
|
return node;
|
|
}
|
|
|
|
#ifdef NOT_USED
|
|
List *
|
|
generate_fjoin(List *tlist)
|
|
{
|
|
List tlistP;
|
|
List newTlist = NIL;
|
|
List fjoinList = NIL;
|
|
int nIters = 0;
|
|
|
|
/*
|
|
* Break the target list into elements with Iter nodes, and those
|
|
* without them.
|
|
*/
|
|
foreach(tlistP, tlist)
|
|
{
|
|
List tlistElem;
|
|
|
|
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)
|
|
{
|
|
List *inner;
|
|
List *tempList;
|
|
Fjoin *fjoinNode;
|
|
DatumPtr results = (DatumPtr) palloc(nIters * sizeof(Datum));
|
|
BoolPtr alwaysDone = (BoolPtr) palloc(nIters * sizeof(bool));
|
|
|
|
inner = lfirst(fjoinList);
|
|
fjoinList = lnext(fjoinList);
|
|
fjoinNode = (Fjoin) MakeFjoin(false,
|
|
nIters,
|
|
inner,
|
|
results,
|
|
alwaysDone);
|
|
tempList = lcons(fjoinNode, NIL);
|
|
tempList = nconc(tempList, fjoinList);
|
|
newTlist = lappend(newTlist, tempList);
|
|
}
|
|
return newTlist;
|
|
return tlist; /* do nothing for now - ay 10/94 */
|
|
}
|
|
|
|
#endif
|